oleavr-rgl-a500-mini-linux-.../drivers/media/platform/sunxi-vin/vin-stat/vin_h3a.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

381 lines
9.4 KiB
C

/*
* linux-3.10/drivers/media/platform/sunxi-vin/vin-stat/vin_h3a.c
*
* Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*
* vin_3a.c
*
*/
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "vin_h3a.h"
#include "vin_ispstat.h"
/*
* h3a_setup_regs - Helper function to update h3a registers.
*/
static void h3a_setup_regs(struct isp_stat *h3a, void *priv)
{
struct vin_isp_h3a_config *conf = priv;
struct isp_dev *isp = container_of(h3a, struct isp_dev, h3a_stat);
dma_addr_t dma_addr = (dma_addr_t)(h3a->active_buf->dma_addr);
if (h3a->state == ISPSTAT_DISABLED)
return;
bsp_isp_set_statistics_addr(isp->id, dma_addr);
if (!h3a->update)
return;
h3a->update = 0;
h3a->config_counter += h3a->inc_config;
h3a->inc_config = 0;
h3a->buf_size = conf->buf_size;
}
static void h3a_enable(struct isp_stat *h3a, int enable)
{
}
static int h3a_busy(struct isp_stat *h3a)
{
return 0;
}
static u32 h3a_get_buf_size(struct vin_isp_h3a_config *conf)
{
return ISP_STAT_TOTAL_SIZE;
}
static int h3a_validate_params(struct isp_stat *aewb, void *new_conf)
{
struct vin_isp_h3a_config *user_cfg = new_conf;
u32 buf_size;
buf_size = h3a_get_buf_size(user_cfg);
if (buf_size > user_cfg->buf_size)
user_cfg->buf_size = buf_size;
else if (user_cfg->buf_size > ISP_STAT_TOTAL_SIZE)
user_cfg->buf_size = ISP_STAT_TOTAL_SIZE;
return 0;
}
static void h3a_set_params(struct isp_stat *stat, void *new_conf)
{
/*struct vin_isp_h3a_config *user_cfg = new_conf;*/
struct vin_isp_h3a_config *cur_cfg = stat->priv;
int update = 0;
/*
* check the privat field, which will write to the hardware later.
*/
if (update || !stat->configured) {
stat->inc_config++;
stat->update = 1;
cur_cfg->buf_size = h3a_get_buf_size(cur_cfg);
}
}
int isp_ae_stat_req(struct isp_dev *isp, struct isp_stat_buf *ae_buf)
{
int ret = 0;
if (isp->stat_buf->ae_buf == NULL)
return -EINVAL;
ae_buf->buf_size = ISP_STAT_AE_MEM_SIZE;
ret = copy_to_user(ae_buf->buf, isp->stat_buf->ae_buf, ae_buf->buf_size);
return ret;
}
int isp_gamma_req(struct isp_dev *isp, struct isp_stat_buf *gamma_buf)
{
int ret = 0;
if (isp->isp_tbl.isp_gamma_tbl_vaddr == NULL)
return -EINVAL;
gamma_buf->buf_size = ISP_GAMMA_MEM_SIZE;
ret = copy_to_user(gamma_buf->buf, isp->isp_tbl.isp_gamma_tbl_vaddr,
gamma_buf->buf_size);
return ret;
}
int isp_hist_stat_req(struct isp_dev *isp, struct isp_stat_buf *hist_buf)
{
int ret = 0;
if (isp->stat_buf->hist_buf == NULL)
return -EINVAL;
hist_buf->buf_size = ISP_STAT_HIST_MEM_SIZE;
ret = copy_to_user(hist_buf->buf, isp->stat_buf->hist_buf,
hist_buf->buf_size);
return ret;
}
int isp_af_stat_req(struct isp_dev *isp, struct isp_stat_buf *af_buf)
{
int ret = 0;
if (isp->stat_buf->af_buf == NULL)
return -EINVAL;
af_buf->buf_size = ISP_STAT_AF_MEM_SIZE;
ret = copy_to_user(af_buf->buf, isp->stat_buf->af_buf, af_buf->buf_size);
return ret;
}
static long h3a_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct isp_stat *stat = v4l2_get_subdevdata(sd);
vin_log(VIN_LOG_STAT, "%s cmd is 0x%x\n", __func__, cmd);
switch (cmd) {
case VIDIOC_ISP_AE_STAT_REQ:
return isp_ae_stat_req(stat->isp, arg);
case VIDIOC_ISP_AF_STAT_REQ:
return isp_af_stat_req(stat->isp, arg);
case VIDIOC_ISP_HIST_STAT_REQ:
return isp_hist_stat_req(stat->isp, arg);
case VIDIOC_ISP_GAMMA_REQ:
return isp_gamma_req(stat->isp, arg);
case VIDIOC_VIN_ISP_H3A_CFG:
return vin_isp_stat_config(stat, arg);
case VIDIOC_VIN_ISP_STAT_REQ:
return vin_isp_stat_request_statistics(stat, arg);
case VIDIOC_VIN_ISP_STAT_EN: {
unsigned int *en = arg;
return vin_isp_stat_enable(stat, !!*en);
}
}
return -ENOIOCTLCMD;
}
#ifdef CONFIG_COMPAT
struct isp_stat_buf32 {
compat_caddr_t buf;
__u32 buf_size;
};
static int get_isp_stat_buf32(struct isp_stat_buf *kp,
struct isp_stat_buf32 __user *up)
{
u32 tmp;
if (!access_ok(VERIFY_READ, up, sizeof(struct isp_stat_buf32)) ||
get_user(kp->buf_size, &up->buf_size) || get_user(tmp, &up->buf))
return -EFAULT;
kp->buf = compat_ptr(tmp);
return 0;
}
static int put_isp_stat_buf32(struct isp_stat_buf *kp,
struct isp_stat_buf32 __user *up)
{
u32 tmp = (u32) ((unsigned long)kp->buf);
if (!access_ok(VERIFY_WRITE, up, sizeof(struct isp_stat_buf32)) ||
put_user(kp->buf_size, &up->buf_size) || put_user(tmp, &up->buf))
return -EFAULT;
return 0;
}
struct vin_isp_stat_data32 {
compat_caddr_t buf;
__u32 buf_size;
__u32 frame_number;
__u32 config_counter;
};
static int get_isp_statistics_buf32(struct vin_isp_stat_data *kp,
struct vin_isp_stat_data32 __user *up)
{
u32 tmp;
if (!access_ok(VERIFY_READ, up, sizeof(struct vin_isp_stat_data32)) ||
get_user(kp->buf_size, &up->buf_size) ||
get_user(kp->frame_number, &up->frame_number) ||
get_user(kp->config_counter, &up->config_counter) ||
get_user(tmp, &up->buf))
return -EFAULT;
kp->buf = compat_ptr(tmp);
return 0;
}
static int put_isp_statistics_buf32(struct vin_isp_stat_data *kp,
struct vin_isp_stat_data32 __user *up)
{
u32 tmp = (u32) ((unsigned long)kp->buf);
if (!access_ok(VERIFY_READ, up, sizeof(struct vin_isp_stat_data32)) ||
put_user(kp->buf_size, &up->buf_size) ||
put_user(kp->frame_number, &up->frame_number) ||
put_user(kp->config_counter, &up->config_counter) ||
put_user(tmp, &up->buf))
return -EFAULT;
return 0;
}
#define VIDIOC_ISP_AE_STAT_REQ32 \
_IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct isp_stat_buf32)
#define VIDIOC_ISP_HIST_STAT_REQ32 \
_IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct isp_stat_buf32)
#define VIDIOC_ISP_AF_STAT_REQ32 \
_IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct isp_stat_buf32)
#define VIDIOC_ISP_GAMMA_REQ32 \
_IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct isp_stat_buf32)
#define VIDIOC_VIN_ISP_STAT_REQ32 \
_IOWR('V', BASE_VIDIOC_PRIVATE + 32, struct vin_isp_stat_data32)
static long h3a_compat_ioctl32(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
{
union {
struct isp_stat_buf isb;
struct vin_isp_stat_data isd;
} karg;
void __user *up = compat_ptr(arg);
int compatible_arg = 1;
long err = 0;
vin_log(VIN_LOG_STAT, "%s cmd is 0x%x\n", __func__, cmd);
switch (cmd) {
case VIDIOC_ISP_AE_STAT_REQ32:
cmd = VIDIOC_ISP_AE_STAT_REQ;
break;
case VIDIOC_ISP_HIST_STAT_REQ32:
cmd = VIDIOC_ISP_HIST_STAT_REQ;
break;
case VIDIOC_ISP_AF_STAT_REQ32:
cmd = VIDIOC_ISP_AF_STAT_REQ;
break;
case VIDIOC_ISP_GAMMA_REQ32:
cmd = VIDIOC_ISP_GAMMA_REQ;
break;
case VIDIOC_VIN_ISP_STAT_REQ32:
cmd = VIDIOC_VIN_ISP_STAT_REQ;
break;
}
switch (cmd) {
case VIDIOC_ISP_AE_STAT_REQ:
case VIDIOC_ISP_HIST_STAT_REQ:
case VIDIOC_ISP_AF_STAT_REQ:
case VIDIOC_ISP_GAMMA_REQ:
err = get_isp_stat_buf32(&karg.isb, up);
compatible_arg = 0;
break;
case VIDIOC_VIN_ISP_STAT_REQ:
err = get_isp_statistics_buf32(&karg.isd, up);
compatible_arg = 0;
break;
}
if (err)
return err;
if (compatible_arg)
err = h3a_ioctl(sd, cmd, up);
else {
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
err = h3a_ioctl(sd, cmd, &karg);
set_fs(old_fs);
}
switch (cmd) {
case VIDIOC_ISP_AE_STAT_REQ:
case VIDIOC_ISP_HIST_STAT_REQ:
case VIDIOC_ISP_AF_STAT_REQ:
case VIDIOC_ISP_GAMMA_REQ:
err = put_isp_stat_buf32(&karg.isb, up);
break;
case VIDIOC_VIN_ISP_STAT_REQ:
err = put_isp_statistics_buf32(&karg.isd, up);
break;
}
return err;
}
#endif
static const struct ispstat_ops h3a_ops = {
.validate_params = h3a_validate_params,
.set_params = h3a_set_params,
.setup_regs = h3a_setup_regs,
.enable = h3a_enable,
.busy = h3a_busy,
};
static const struct v4l2_subdev_core_ops h3a_subdev_core_ops = {
.ioctl = h3a_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = h3a_compat_ioctl32,
#endif
.subscribe_event = vin_isp_stat_subscribe_event,
.unsubscribe_event = vin_isp_stat_unsubscribe_event,
};
static const struct v4l2_subdev_video_ops h3a_subdev_video_ops = {
.s_stream = vin_isp_stat_s_stream,
};
static const struct v4l2_subdev_ops h3a_subdev_ops = {
.core = &h3a_subdev_core_ops,
.video = &h3a_subdev_video_ops,
};
int vin_isp_h3a_init(struct isp_dev *isp)
{
struct isp_stat *stat = &isp->h3a_stat;
struct vin_isp_h3a_config *cfg;
struct vin_isp_h3a_config *recover_cfg;
char name[32];
vin_print("%s\n", __func__);
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
stat->ops = &h3a_ops;
stat->priv = cfg;
stat->isp = isp;
stat->event_type = V4L2_EVENT_VIN_H3A;
recover_cfg = kzalloc(sizeof(*recover_cfg), GFP_KERNEL);
if (!recover_cfg) {
vin_err("H3A: cannot allocate memory for recover config.\n");
return -ENOMEM;
}
if (h3a_validate_params(stat, recover_cfg)) {
vin_err("H3A: recover configuration is invalid.\n");
return -EINVAL;
}
recover_cfg->buf_size = h3a_get_buf_size(recover_cfg);
stat->recover_priv = recover_cfg;
snprintf(name, sizeof(name), "h3a.%u", isp->id);
return vin_isp_stat_init(stat, &name[0], &h3a_subdev_ops);
}
void vin_isp_h3a_cleanup(struct isp_dev *isp)
{
vin_isp_stat_cleanup(&isp->h3a_stat);
vin_print("%s\n", __func__);
}