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

1436 lines
37 KiB
C

/*
* linux-3.10/drivers/media/platform/sunxi-vin/vin-isp/sunxi_isp.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.
*
*/
/*
******************************************************************************
*
* sunxi_isp.c
*
* Hawkview ISP - sunxi_isp.c module
*
* Copyright (c) 2014 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
*
* Version Author Date Description
*
* 3.0 Yang Feng 2014/12/11 ISP Tuning Tools Support
*
******************************************************************************
*/
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ctrls.h>
#include "../platform/platform_cfg.h"
#include "sunxi_isp.h"
#include "../vin-video/vin_core.h"
#include "../utility/vin_io.h"
#define ISP_MODULE_NAME "vin_isp"
extern unsigned int isp_reparse_flag;
static LIST_HEAD(isp_drv_list);
#define MIN_IN_WIDTH 32
#define MIN_IN_HEIGHT 32
#define MAX_IN_WIDTH 4095
#define MAX_IN_HEIGHT 4095
static struct isp_pix_fmt sunxi_isp_formats[] = {
{
.name = "RAW8 (BGGR)",
.fourcc = V4L2_PIX_FMT_SBGGR8,
.depth = {8},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
.infmt = ISP_BGGR,
}, {
.name = "RAW8 (GBRG)",
.fourcc = V4L2_PIX_FMT_SGBRG8,
.depth = {8},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGBRG8_1X8,
.infmt = ISP_GBRG,
}, {
.name = "RAW8 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG8,
.depth = {8},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8,
.infmt = ISP_GRBG,
}, {
.name = "RAW8 (RGGB)",
.fourcc = V4L2_PIX_FMT_SRGGB8,
.depth = {8},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SRGGB8_1X8,
.infmt = ISP_RGGB,
}, {
.name = "RAW10 (BGGR)",
.fourcc = V4L2_PIX_FMT_SBGGR10,
.depth = {10},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SBGGR10_1X10,
.infmt = ISP_BGGR,
}, {
.name = "RAW10 (GBRG)",
.fourcc = V4L2_PIX_FMT_SGBRG8,
.depth = {10},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGBRG10_1X10,
.infmt = ISP_GBRG,
}, {
.name = "RAW10 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG10,
.depth = {10},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
.infmt = ISP_GRBG,
}, {
.name = "RAW10 (RGGB)",
.fourcc = V4L2_PIX_FMT_SRGGB10,
.depth = {10},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SRGGB10_1X10,
.infmt = ISP_RGGB,
}, {
.name = "RAW12 (BGGR)",
.fourcc = V4L2_PIX_FMT_SBGGR12,
.depth = {12},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SBGGR12_1X12,
.infmt = ISP_BGGR,
}, {
.name = "RAW12 (GBRG)",
.fourcc = V4L2_PIX_FMT_SGBRG12,
.depth = {12},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGBRG12_1X12,
.infmt = ISP_GBRG,
}, {
.name = "RAW12 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG12,
.depth = {12},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
.infmt = ISP_GRBG,
}, {
.name = "RAW12 (RGGB)",
.fourcc = V4L2_PIX_FMT_SRGGB12,
.depth = {12},
.color = 0,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SRGGB12_1X12,
.infmt = ISP_RGGB,
},
};
static int isp_3d_pingpong_alloc(struct isp_dev *isp)
{
int ret = 0;
isp->isp_3d_buf.flags[0] = ISP_3D_R;
isp->isp_3d_buf.flags[1] = ISP_3D_W;
isp->isp_3d_buf.buf[0].size = isp->mf.width * isp->mf.height * 4;
isp->isp_3d_buf.buf[1].size = isp->mf.width * isp->mf.height * 4;
ret = os_mem_alloc(&isp->pdev->dev, &isp->isp_3d_buf.buf[0]);
if (ret < 0) {
vin_err("isp 3d pingpong buf0 requset add failed!\n");
return -ENOMEM;
}
ret = os_mem_alloc(&isp->pdev->dev, &isp->isp_3d_buf.buf[1]);
if (ret < 0) {
vin_err("isp 3d pingpong buf1 requset add failed!\n");
return -ENOMEM;
}
return ret;
}
static void isp_3d_pingpong_free(struct isp_dev *isp)
{
os_mem_free(&isp->pdev->dev, &isp->isp_3d_buf.buf[0]);
os_mem_free(&isp->pdev->dev, &isp->isp_3d_buf.buf[1]);
}
static int isp_3d_pingpong_update(struct isp_dev *isp,
struct isp_hw_pingpong *pingpong)
{
dma_addr_t addr;
int tmp = -1;
tmp = pingpong->flags[0];
pingpong->flags[0] = pingpong->flags[1];
pingpong->flags[1] = tmp;
addr = (dma_addr_t)pingpong->buf[pingpong->flags[ISP_3D_W]].dma_addr;
writel(addr >> 2, isp->isp_load.vir_addr + 0xc8);
addr = (dma_addr_t)pingpong->buf[pingpong->flags[ISP_3D_R]].dma_addr;
writel(addr >> 2, isp->isp_load.vir_addr + 0xcc);
return 0;
}
static int sunxi_isp_subdev_set_crop(struct v4l2_subdev *sd,
const struct v4l2_crop *crop)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
isp->isp_ob.ob_valid.width = crop->c.width;
isp->isp_ob.ob_valid.height = crop->c.height;
isp->isp_ob.ob_start.hor = crop->c.left;
isp->isp_ob.ob_start.ver = crop->c.top;
isp->isp_ob.ob_black.width = crop->c.width + crop->c.left;
isp->isp_ob.ob_black.height = crop->c.height + crop->c.top;
return 0;
}
static int sunxi_isp_subdev_s_power(struct v4l2_subdev *sd, int enable)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
if (!isp->use_isp)
return 0;
return 0;
}
static int sunxi_isp_subdev_s_stream(struct v4l2_subdev *sd, int enable)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf = &isp->mf;
struct mbus_framefmt_res *res = (void *)mf->reserved;
struct isp_size_settings *ob = &isp->isp_ob;
if (!isp->use_isp)
return 0;
switch (res->res_pix_fmt) {
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
case V4L2_PIX_FMT_SGRBG10:
case V4L2_PIX_FMT_SRGGB10:
case V4L2_PIX_FMT_SBGGR12:
case V4L2_PIX_FMT_SGBRG12:
case V4L2_PIX_FMT_SGRBG12:
case V4L2_PIX_FMT_SRGGB12:
vin_print("%s output fmt is raw, return directly\n", __func__);
return 0;
default:
break;
}
if (enable) {
isp->h3a_stat.frame_number = 0;
if (isp_3d_pingpong_alloc(isp))
return -ENOMEM;
isp_3d_pingpong_update(isp, &isp->isp_3d_buf);
isp->load_shadow = kzalloc(ISP_LOAD_REG_SIZE, GFP_KERNEL);
if (isp->load_shadow == NULL) {
vin_err("%s - kzalloc load_shadow failed\n", __func__);
return -ENOMEM;
}
bsp_isp_enable(isp->id);
bsp_isp_src0_enable(isp->id);
bsp_isp_set_input_fmt(isp->id, isp->isp_fmt->infmt);
ob->ob_valid.width = mf->width;
ob->ob_valid.height = mf->height;
ob->ob_start.hor = 0;
ob->ob_start.ver = 0;
ob->ob_black.width = mf->width + ob->ob_start.hor;
ob->ob_black.height = mf->height + ob->ob_start.ver;
bsp_isp_set_ob_zone(isp->id, &isp->isp_ob);
sunxi_isp_dump_regs(sd);
bsp_isp_set_para_ready(isp->id);
bsp_isp_set_speed_mode(isp->id, 3);
bsp_isp_channel_enable(isp->id, 0);
bsp_isp_clr_irq_status(isp->id, ISP_IRQ_EN_ALL);
bsp_isp_irq_enable(isp->id, FINISH_INT_EN | SRC0_FIFO_INT_EN
| FRAME_ERROR_INT_EN | FRAME_LOST_INT_EN);
bsp_isp_capture_start(isp->id);
} else {
bsp_isp_capture_stop(isp->id);
bsp_isp_src0_disable(isp->id);
bsp_isp_channel_disable(isp->id, 0);
bsp_isp_irq_disable(isp->id, ISP_IRQ_EN_ALL);
bsp_isp_clr_irq_status(isp->id, ISP_IRQ_EN_ALL);
kfree(isp->load_shadow);
bsp_isp_disable(isp->id);
isp_3d_pingpong_free(isp);
}
vin_print("%s on = %d, %d*%d %x %d\n", __func__, enable,
mf->width, mf->height, mf->code, mf->field);
return 0;
}
static struct isp_pix_fmt *__isp_find_format(const u32 *pixelformat,
const u32 *mbus_code,
int index)
{
struct isp_pix_fmt *fmt, *def_fmt = NULL;
unsigned int i;
int id = 0;
if (index >= (int)ARRAY_SIZE(sunxi_isp_formats))
return NULL;
for (i = 0; i < ARRAY_SIZE(sunxi_isp_formats); ++i) {
fmt = &sunxi_isp_formats[i];
if (pixelformat && fmt->fourcc == *pixelformat)
return fmt;
if (mbus_code && fmt->mbus_code == *mbus_code)
return fmt;
if (index == id)
def_fmt = fmt;
id++;
}
return def_fmt;
}
static struct v4l2_mbus_framefmt *__isp_get_format(struct isp_dev *isp,
struct v4l2_subdev_fh *fh,
enum v4l2_subdev_format_whence which)
{
if (which == V4L2_SUBDEV_FORMAT_TRY)
return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
return &isp->mf;
}
static struct isp_pix_fmt *__isp_try_format(struct v4l2_mbus_framefmt *mf)
{
struct isp_pix_fmt *isp_fmt;
isp_fmt = __isp_find_format(NULL, &mf->code, 0);
if (isp_fmt == NULL)
isp_fmt = &sunxi_isp_formats[0];
mf->width = clamp_t(u32, mf->width, MIN_IN_WIDTH, MAX_IN_WIDTH);
mf->height = clamp_t(u32, mf->height, MIN_IN_HEIGHT, MAX_IN_HEIGHT);
mf->colorspace = V4L2_COLORSPACE_JPEG;
return isp_fmt;
}
static int sunxi_isp_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
{
struct v4l2_captureparm *cp = &parms->parm.capture;
struct isp_dev *isp = v4l2_get_subdevdata(sd);
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
isp->capture_mode = cp->capturemode;
return 0;
}
static int sunxi_isp_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
{
struct v4l2_captureparm *cp = &parms->parm.capture;
struct isp_dev *isp = v4l2_get_subdevdata(sd);
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
memset(cp, 0, sizeof(struct v4l2_captureparm));
cp->capability = V4L2_CAP_TIMEPERFRAME;
cp->capturemode = isp->capture_mode;
return 0;
}
static int sunxi_isp_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
struct isp_pix_fmt *fmt;
fmt = __isp_find_format(NULL, NULL, code->index);
if (!fmt)
return -EINVAL;
code->code = fmt->mbus_code;
return 0;
}
static int sunxi_isp_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
if (fse->index != 0)
return -EINVAL;
fse->min_width = MIN_IN_WIDTH;
fse->min_height = MIN_IN_HEIGHT;
fse->max_width = MAX_IN_WIDTH;
fse->max_height = MAX_IN_HEIGHT;
return 0;
}
static int sunxi_isp_subdev_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
mf = __isp_get_format(isp, fh, fmt->which);
if (!mf)
return -EINVAL;
mutex_lock(&isp->subdev_lock);
fmt->format = *mf;
mutex_unlock(&isp->subdev_lock);
return 0;
}
static int sunxi_isp_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
struct isp_pix_fmt *isp_fmt;
vin_log(VIN_LOG_FMT, "%s %d*%d %x %d\n", __func__,
fmt->format.width, fmt->format.height,
fmt->format.code, fmt->format.field);
mf = __isp_get_format(isp, fh, fmt->which);
if (fmt->pad == ISP_PAD_SOURCE) {
if (mf) {
mutex_lock(&isp->subdev_lock);
fmt->format = *mf;
mutex_unlock(&isp->subdev_lock);
}
return 0;
}
isp_fmt = __isp_try_format(&fmt->format);
if (mf) {
mutex_lock(&isp->subdev_lock);
*mf = fmt->format;
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
isp->isp_fmt = isp_fmt;
mutex_unlock(&isp->subdev_lock);
}
return 0;
}
int sunxi_isp_subdev_init(struct v4l2_subdev *sd, u32 val)
{
int ret = 0;
struct isp_dev *isp = v4l2_get_subdevdata(sd);
if (!isp->use_isp)
return 0;
vin_print("%s, val = %d.\n", __func__, val);
if (val) {
bsp_isp_set_dma_load_addr(isp->id, (unsigned long)isp->isp_load.dma_addr);
bsp_isp_set_dma_saved_addr(isp->id, (unsigned long)isp->isp_save.dma_addr);
ret = isp_table_request(sd);
if (ret) {
vin_err("isp_table_request error at %s\n", __func__);
return ret;
}
INIT_WORK(&isp->isp_isr_bh_task, isp_isr_bh_handle);
isp_table_setting(sd);
bsp_isp_clr_para_ready(isp->id);
} else {
flush_work(&isp->isp_isr_bh_task);
isp_table_release(sd);
}
return 0;
}
static int __isp_set_load_reg(struct v4l2_subdev *sd, struct isp_table_reg_map *reg)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
if (!isp->use_isp)
return 0;
isp->load_flag = 1;
return copy_from_user(isp->load_shadow, reg->addr, reg->size);
}
static int __isp_set_table1_map(struct v4l2_subdev *sd, struct isp_table_reg_map *tbl)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
int ret;
if (!isp->use_isp)
return 0;
ret = copy_from_user(isp->isp_lut_tbl.vir_addr, tbl->addr, tbl->size);
if (ret < 0) {
vin_err("copy table mapping1 from usr error!\n");
return ret;
}
return 0;
}
static int __isp_set_table2_map(struct v4l2_subdev *sd, struct isp_table_reg_map *tbl)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
int ret;
if (!isp->use_isp)
return 0;
ret = copy_from_user(isp->isp_drc_tbl.vir_addr, tbl->addr, tbl->size);
if (ret < 0) {
vin_err("copy table mapping2 from usr error!\n");
return ret;
}
return 0;
}
static long sunxi_isp_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd,
void *arg)
{
int ret = 0;
switch (cmd) {
case VIDIOC_VIN_ISP_LOAD_REG:
ret = __isp_set_load_reg(sd, (struct isp_table_reg_map *)arg);
break;
case VIDIOC_VIN_ISP_TABLE1_MAP:
ret = __isp_set_table1_map(sd, (struct isp_table_reg_map *)arg);
break;
case VIDIOC_VIN_ISP_TABLE2_MAP:
ret = __isp_set_table2_map(sd, (struct isp_table_reg_map *)arg);
break;
default:
return -ENOIOCTLCMD;
}
return ret;
}
#ifdef CONFIG_COMPAT
struct isp_table_reg_map32 {
compat_caddr_t addr;
unsigned int size;
};
#define VIDIOC_VIN_ISP_LOAD_REG32 \
_IOWR('V', BASE_VIDIOC_PRIVATE + 70, struct isp_table_reg_map32)
#define VIDIOC_VIN_ISP_TABLE1_MAP32 \
_IOWR('V', BASE_VIDIOC_PRIVATE + 71, struct isp_table_reg_map32)
#define VIDIOC_VIN_ISP_TABLE2_MAP32 \
_IOWR('V', BASE_VIDIOC_PRIVATE + 72, struct isp_table_reg_map32)
static int get_isp_table_reg_map32(struct isp_table_reg_map *kp,
struct isp_table_reg_map32 __user *up)
{
u32 tmp;
if (!access_ok(VERIFY_READ, up, sizeof(struct isp_table_reg_map32)) ||
get_user(kp->size, &up->size) || get_user(tmp, &up->addr))
return -EFAULT;
kp->addr = compat_ptr(tmp);
return 0;
}
static int put_isp_table_reg_map32(struct isp_table_reg_map *kp,
struct isp_table_reg_map32 __user *up)
{
u32 tmp = (u32) ((unsigned long)kp->addr);
if (!access_ok(VERIFY_WRITE, up, sizeof(struct isp_table_reg_map32)) ||
put_user(kp->size, &up->size) || put_user(tmp, &up->addr))
return -EFAULT;
return 0;
}
static long isp_compat_ioctl32(struct v4l2_subdev *sd,
unsigned int cmd, unsigned long arg)
{
union {
struct isp_table_reg_map isd;
} karg;
void __user *up = compat_ptr(arg);
int compatible_arg = 1;
long err = 0;
vin_log(VIN_LOG_ISP, "%s cmd is %d\n", __func__, cmd);
switch (cmd) {
case VIDIOC_VIN_ISP_LOAD_REG32:
cmd = VIDIOC_VIN_ISP_LOAD_REG;
break;
case VIDIOC_VIN_ISP_TABLE1_MAP32:
cmd = VIDIOC_VIN_ISP_TABLE1_MAP;
break;
case VIDIOC_VIN_ISP_TABLE2_MAP32:
cmd = VIDIOC_VIN_ISP_TABLE2_MAP;
break;
}
switch (cmd) {
case VIDIOC_VIN_ISP_LOAD_REG:
case VIDIOC_VIN_ISP_TABLE1_MAP:
case VIDIOC_VIN_ISP_TABLE2_MAP:
err = get_isp_table_reg_map32(&karg.isd, up);
compatible_arg = 0;
break;
}
if (err)
return err;
if (compatible_arg)
err = sunxi_isp_subdev_ioctl(sd, cmd, up);
else {
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
err = sunxi_isp_subdev_ioctl(sd, cmd, &karg);
set_fs(old_fs);
}
switch (cmd) {
case VIDIOC_VIN_ISP_LOAD_REG:
case VIDIOC_VIN_ISP_TABLE1_MAP:
case VIDIOC_VIN_ISP_TABLE2_MAP:
err = put_isp_table_reg_map32(&karg.isd, up);
break;
}
return err;
}
#endif
static void sunxi_isp_stat_parse(struct isp_dev *isp)
{
void *va = NULL;
if (NULL == isp->h3a_stat.active_buf) {
vin_log(VIN_LOG_ISP, "stat active buf is NULL, please enable\n");
return;
}
va = isp->h3a_stat.active_buf->virt_addr;
isp->stat_buf->hist_buf = (void *) (va);
isp->stat_buf->ae_buf = (void *) (va + ISP_STAT_AE_MEM_OFS);
isp->stat_buf->af_buf = (void *) (va + ISP_STAT_AF_MEM_OFS);
isp->stat_buf->afs_buf = (void *) (va + ISP_STAT_AFS_MEM_OFS);
isp->stat_buf->awb_buf = (void *) (va + ISP_STAT_AWB_MEM_OFS);
isp->stat_buf->pltm_buf = (void *) (va + ISP_STAT_PLTM_LST_MEM_OFS);
}
void sunxi_isp_vsync_isr(struct v4l2_subdev *sd)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
struct v4l2_event event;
memset(&event, 0, sizeof(event));
event.type = V4L2_EVENT_VSYNC;
event.id = 0;
v4l2_event_queue(isp->subdev.devnode, &event);
}
void sunxi_isp_frame_sync_isr(struct v4l2_subdev *sd)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
struct v4l2_event event;
vin_isp_stat_isr_frame_sync(&isp->h3a_stat);
memset(&event, 0, sizeof(event));
event.type = V4L2_EVENT_FRAME_SYNC;
event.id = 0;
v4l2_event_queue(isp->subdev.devnode, &event);
isp->h3a_stat.frame_number++;
vin_isp_stat_isr(&isp->h3a_stat);
/* you should enable the isp stat buf first,
** when you want to get the stat buf separate.
** user can get stat buf from external ioctl.
*/
sunxi_isp_stat_parse(isp);
}
int sunxi_isp_subscribe_event(struct v4l2_subdev *sd,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
vin_log(VIN_LOG_ISP, "%s id = %d\n", __func__, sub->id);
if (sub->type == V4L2_EVENT_CTRL)
return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
else
return v4l2_event_subscribe(fh, sub, 1, NULL);
}
int sunxi_isp_unsubscribe_event(struct v4l2_subdev *sd,
struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
vin_log(VIN_LOG_ISP, "%s id = %d\n", __func__, sub->id);
return v4l2_event_unsubscribe(fh, sub);
}
static const struct v4l2_subdev_core_ops sunxi_isp_subdev_core_ops = {
.s_power = sunxi_isp_subdev_s_power,
.init = sunxi_isp_subdev_init,
.ioctl = sunxi_isp_subdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = isp_compat_ioctl32,
#endif
.subscribe_event = sunxi_isp_subscribe_event,
.unsubscribe_event = sunxi_isp_unsubscribe_event,
};
static const struct v4l2_subdev_video_ops sunxi_isp_subdev_video_ops = {
.s_parm = sunxi_isp_s_parm,
.g_parm = sunxi_isp_g_parm,
.s_crop = sunxi_isp_subdev_set_crop,
.s_stream = sunxi_isp_subdev_s_stream,
};
static const struct v4l2_subdev_pad_ops sunxi_isp_subdev_pad_ops = {
.enum_mbus_code = sunxi_isp_enum_mbus_code,
.enum_frame_size = sunxi_isp_enum_frame_size,
.get_fmt = sunxi_isp_subdev_get_fmt,
.set_fmt = sunxi_isp_subdev_set_fmt,
};
static struct v4l2_subdev_ops sunxi_isp_subdev_ops = {
.core = &sunxi_isp_subdev_core_ops,
.video = &sunxi_isp_subdev_video_ops,
.pad = &sunxi_isp_subdev_pad_ops,
};
static int __sunxi_isp_ctrl(struct isp_dev *isp, struct v4l2_ctrl *ctrl)
{
int ret = 0;
if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
return 0;
switch (ctrl->id) {
case V4L2_CID_HFLIP:
break;
case V4L2_CID_VFLIP:
break;
case V4L2_CID_ROTATE:
break;
default:
break;
}
return ret;
}
#define ctrl_to_sunxi_isp(ctrl) \
container_of(ctrl->handler, struct isp_dev, ctrls.handler)
static int sunxi_isp_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct isp_dev *isp = ctrl_to_sunxi_isp(ctrl);
unsigned long flags;
int ret;
vin_log(VIN_LOG_ISP, "id = %d, val = %d, cur.val = %d\n",
ctrl->id, ctrl->val, ctrl->cur.val);
spin_lock_irqsave(&isp->slock, flags);
ret = __sunxi_isp_ctrl(isp, ctrl);
spin_unlock_irqrestore(&isp->slock, flags);
return ret;
}
static const struct v4l2_ctrl_ops sunxi_isp_ctrl_ops = {
.s_ctrl = sunxi_isp_s_ctrl,
};
static const struct v4l2_ctrl_config ae_win_ctrls[] = {
{
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_AE_WIN_X1,
.name = "R GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_AE_WIN_Y1,
.name = "R GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_AE_WIN_X2,
.name = "R GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_AE_WIN_Y2,
.name = "R GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}
};
static const struct v4l2_ctrl_config af_win_ctrls[] = {
{
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_AF_WIN_X1,
.name = "R GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_AF_WIN_Y1,
.name = "R GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_AF_WIN_X2,
.name = "R GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_AF_WIN_Y2,
.name = "R GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}
};
static const struct v4l2_ctrl_config wb_gain_ctrls[] = {
{
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_R_GAIN,
.name = "R GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 1024,
.step = 1,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_GR_GAIN,
.name = "GR GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 1024,
.step = 1,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_GB_GAIN,
.name = "GB GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 1024,
.step = 1,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &sunxi_isp_ctrl_ops,
.id = V4L2_CID_B_GAIN,
.name = "B GAIN",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 1024,
.step = 1,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}
};
int __isp_init_subdev(struct isp_dev *isp)
{
const struct v4l2_ctrl_ops *ops = &sunxi_isp_ctrl_ops;
struct v4l2_ctrl_handler *handler = &isp->ctrls.handler;
struct v4l2_subdev *sd = &isp->subdev;
struct sunxi_isp_ctrls *ctrls = &isp->ctrls;
int i, ret;
mutex_init(&isp->subdev_lock);
v4l2_subdev_init(sd, &sunxi_isp_subdev_ops);
sd->grp_id = VIN_GRP_ID_ISP;
sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "sunxi_isp.%u", isp->id);
v4l2_set_subdevdata(sd, isp);
v4l2_ctrl_handler_init(handler, 3 + ARRAY_SIZE(ae_win_ctrls)
+ ARRAY_SIZE(af_win_ctrls) + ARRAY_SIZE(wb_gain_ctrls));
ctrls->rotate =
v4l2_ctrl_new_std(handler, ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
ctrls->hflip =
v4l2_ctrl_new_std(handler, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
ctrls->vflip =
v4l2_ctrl_new_std(handler, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
for (i = 0; i < ARRAY_SIZE(wb_gain_ctrls); i++)
ctrls->wb_gain[i] = v4l2_ctrl_new_custom(handler,
&wb_gain_ctrls[i], NULL);
v4l2_ctrl_cluster(ARRAY_SIZE(wb_gain_ctrls), &ctrls->wb_gain[0]);
for (i = 0; i < ARRAY_SIZE(ae_win_ctrls); i++)
ctrls->ae_win[i] = v4l2_ctrl_new_custom(handler,
&ae_win_ctrls[i], NULL);
v4l2_ctrl_cluster(ARRAY_SIZE(ae_win_ctrls), &ctrls->ae_win[0]);
for (i = 0; i < ARRAY_SIZE(af_win_ctrls); i++)
ctrls->af_win[i] = v4l2_ctrl_new_custom(handler,
&af_win_ctrls[i], NULL);
v4l2_ctrl_cluster(ARRAY_SIZE(af_win_ctrls), &ctrls->af_win[0]);
if (handler->error) {
return handler->error;
}
/*sd->entity->ops = &isp_media_ops;*/
isp->isp_pads[ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
isp->isp_pads[ISP_PAD_SOURCE_ST].flags = MEDIA_PAD_FL_SOURCE;
isp->isp_pads[ISP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
ret = media_entity_init(&sd->entity, ISP_PAD_NUM, isp->isp_pads, 0);
if (ret < 0)
return ret;
sd->ctrl_handler = handler;
/*sd->internal_ops = &sunxi_isp_sd_internal_ops;*/
return 0;
}
void isp_table_setting(struct v4l2_subdev *sd)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
printk("isp->use_cnt = %d\n", isp->use_cnt);
if (isp->use_cnt > 1)
return;
if (isp->is_raw && isp->use_isp) {
bsp_isp_update_lens_gamma_table(isp->id, &isp->isp_tbl);
bsp_isp_update_drc_table(isp->id, &isp->isp_tbl);
}
}
int isp_table_request(struct v4l2_subdev *sd)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
struct isp_table_addr *tbl = &isp->isp_tbl;
void *pa, *va, *dma;
int ret;
if (isp->use_cnt > 1)
return 0;
/*requeset for isp table and statistic buffer*/
if (isp->use_isp && isp->is_raw) {
isp->isp_lut_tbl.size = ISP_TABLE_MAPPING1_SIZE;
ret = os_mem_alloc(&isp->pdev->dev, &isp->isp_lut_tbl);
if (ret < 0) {
vin_err("isp lookup table request failed!\n");
return -ENOMEM;
}
pa = isp->isp_lut_tbl.phy_addr;
va = isp->isp_lut_tbl.vir_addr;
dma = isp->isp_lut_tbl.dma_addr;
tbl->isp_lsc_tbl_paddr = (void *)(pa + ISP_LSC_MEM_OFS);
tbl->isp_lsc_tbl_dma_addr = (void *)(dma + ISP_LSC_MEM_OFS);
tbl->isp_lsc_tbl_vaddr = (void *)(va + ISP_LSC_MEM_OFS);
tbl->isp_gamma_tbl_paddr = (void *)(pa + ISP_GAMMA_MEM_OFS);
tbl->isp_gamma_tbl_dma_addr = (void *)(dma + ISP_GAMMA_MEM_OFS);
tbl->isp_gamma_tbl_vaddr = (void *)(va + ISP_GAMMA_MEM_OFS);
tbl->isp_linear_tbl_paddr = (void *)(pa + ISP_LINEAR_MEM_OFS);
tbl->isp_linear_tbl_dma_addr = (void *)(dma + ISP_LINEAR_MEM_OFS);
tbl->isp_linear_tbl_vaddr = (void *)(va + ISP_LINEAR_MEM_OFS);
vin_log(VIN_LOG_ISP, "isp_lsc_tbl_vaddr = %p\n",
tbl->isp_lsc_tbl_vaddr);
vin_log(VIN_LOG_ISP, "isp_gamma_tbl_vaddr = %p\n",
tbl->isp_gamma_tbl_vaddr);
isp->isp_drc_tbl.size = ISP_TABLE_MAPPING2_SIZE;
ret = os_mem_alloc(&isp->pdev->dev, &isp->isp_drc_tbl);
if (ret < 0) {
vin_err("isp drc table request pa failed!\n");
return -ENOMEM;
}
pa = isp->isp_drc_tbl.phy_addr;
va = isp->isp_drc_tbl.vir_addr;
dma = isp->isp_drc_tbl.dma_addr;
tbl->isp_drc_tbl_paddr = (void *)(pa + ISP_DRC_MEM_OFS);
tbl->isp_drc_tbl_dma_addr = (void *)(dma + ISP_DRC_MEM_OFS);
tbl->isp_drc_tbl_vaddr = (void *)(va + ISP_DRC_MEM_OFS);
vin_log(VIN_LOG_ISP, "isp_drc_tbl_vaddr = %p\n",
tbl->isp_drc_tbl_vaddr);
}
return 0;
}
/*static void isp_table_release(struct isp_dev *isp)*/
void isp_table_release(struct v4l2_subdev *sd)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
if (isp->use_cnt == 0)
return;
/* release isp table and statistic buffer */
if (isp->use_isp && isp->is_raw) {
os_mem_free(&isp->pdev->dev, &isp->isp_lut_tbl);
os_mem_free(&isp->pdev->dev, &isp->isp_drc_tbl);
}
}
static int isp_resource_alloc(struct isp_dev *isp)
{
int ret = 0;
isp->isp_load.size = ISP_LOAD_REG_SIZE;
isp->isp_save.size = ISP_SAVED_REG_SIZE;
ret = os_mem_alloc(&isp->pdev->dev, &isp->isp_load);
if (ret < 0) {
vin_err("isp load regs requset add failed!\n");
return -ENOMEM;
}
ret = os_mem_alloc(&isp->pdev->dev, &isp->isp_save);
if (ret < 0) {
vin_err("isp save regs requset add failed!\n");
return -ENOMEM;
}
return ret;
}
static void isp_resource_free(struct isp_dev *isp)
{
os_mem_free(&isp->pdev->dev, &isp->isp_load);
os_mem_free(&isp->pdev->dev, &isp->isp_save);
}
#define ISP_REGS(n) (void __iomem *)(ISP_REGS_BASE + n)
void isp_isr_bh_handle(struct work_struct *work)
{
struct isp_dev *isp = container_of(work, struct isp_dev, isp_isr_bh_task);
if (isp->is_raw) {
mutex_lock(&isp->subdev_lock);
if (1 == isp_reparse_flag) {
vin_print("ISP reparse ini file!\n");
isp_reparse_flag = 0;
} else if (2 == isp_reparse_flag) {
vin_reg_set(ISP_REGS(0x10), (1 << 20));
} else if (3 == isp_reparse_flag) {
vin_reg_clr_set(ISP_REGS(0x10), (0xF << 16), (1 << 16));
vin_reg_set(ISP_REGS(0x10), (1 << 20));
} else if (4 == isp_reparse_flag) {
vin_reg_clr(ISP_REGS(0x10), (1 << 20));
vin_reg_clr(ISP_REGS(0x10), (0xF << 16));
}
mutex_unlock(&isp->subdev_lock);
}
}
static irqreturn_t isp_isr(int irq, void *priv)
{
struct isp_dev *isp = (struct isp_dev *)priv;
unsigned int update_flag;
unsigned long flags;
if (!isp->use_isp)
return 0;
vin_log(VIN_LOG_ISP, "isp %d interrupt, status is 0x%x!!!\n", isp->id,
bsp_isp_get_irq_status(isp->id, ISP_IRQ_EN_ALL));
spin_lock_irqsave(&isp->slock, flags);
if (bsp_isp_get_irq_status(isp->id, SRC0_FIFO_INT_EN)) {
vin_err("isp source0 fifo overflow\n");
bsp_isp_clr_irq_status(isp->id, SRC0_FIFO_INT_EN);
goto unlock;
}
if (bsp_isp_get_irq_status(isp->id, FRAME_ERROR_INT_EN)) {
vin_err("isp frame error\n");
bsp_isp_clr_irq_status(isp->id, FRAME_ERROR_INT_EN);
goto unlock;
}
if (bsp_isp_get_irq_status(isp->id, FRAME_LOST_INT_EN)) {
vin_err("isp frame lost\n");
bsp_isp_clr_irq_status(isp->id, FRAME_LOST_INT_EN);
goto unlock;
}
unlock:
spin_unlock_irqrestore(&isp->slock, flags);
if (bsp_isp_get_irq_status(isp->id, FINISH_INT_EN)) {
bsp_isp_irq_disable(isp->id, FINISH_INT_EN);
vin_log(VIN_LOG_ISP, "call tasklet schedule!\n");
bsp_isp_clr_para_ready(isp->id);
schedule_work(&isp->isp_isr_bh_task);
if (isp->load_flag) {
memcpy(isp->isp_load.vir_addr, isp->load_shadow, 0x400);
isp->load_flag = 0;
}
bsp_isp_set_ob_zone(isp->id, &isp->isp_ob);
update_flag = readl(isp->isp_load.vir_addr + 0x04);
bsp_isp_update_table(isp->id, (unsigned short)update_flag);
isp_3d_pingpong_update(isp, &isp->isp_3d_buf);
bsp_isp_set_para_ready(isp->id);
sunxi_isp_frame_sync_isr(&isp->subdev);
}
bsp_isp_clr_irq_status(isp->id, FINISH_INT_EN);
bsp_isp_irq_enable(isp->id, FINISH_INT_EN);
return IRQ_HANDLED;
}
unsigned int isp_default_reg[0x100] = {
0x00000101, 0x00000001, 0x00004111, 0x00000087,
0x03c00010, 0x00000000, 0x28000000, 0x04000000,
0x0dc11000, 0x0dc11400, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x80000000, 0x00000004, 0x00000000, 0x0136003c,
0x00000106, 0x00005040, 0x00000000, 0x00000000,
0x00000000, 0x000f0013, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x01e00280, 0x01e00280,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x0f000200, 0x01390010,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x04000804, 0x00000000, 0x00000000, 0x00000000,
0x00021010, 0x00000000, 0x00000000, 0x00000000,
0x00400010, 0x01000100, 0x00200020, 0x00000100,
0x00200020, 0x00200020, 0x04000400, 0x04000400,
0x00200020, 0x00200020, 0x00ff00ff, 0x000000ff,
0x000f0013, 0x00000000, 0x00000000, 0x00000000,
0x00080008, 0x00000000, 0x00000000, 0x00000000,
0x40070f01, 0xfcff0080, 0x1f173c2d, 0x001845c8,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x01000100, 0x01000100, 0x00000fff, 0x00000010,
0x30000000, 0x00000080, 0x0003875c, 0x00400010,
0x02000200, 0x04000400, 0x00000000, 0x00000484,
0x00000808, 0x00420077, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x02040107, 0x07680064, 0x01c206d6, 0x068701c2,
0x000007b7, 0x02010010, 0x00000008, 0x00000000,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00000000, 0x00000000, 0x00000000,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x02108421, 0x02108421,
0x02108421, 0x02108421, 0x02108421, 0x02108421,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00000000, 0x00000000,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00080008, 0x00080008,
0x00080008, 0x00080008, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
};
static int isp_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct isp_dev *isp = NULL;
int ret = 0;
if (np == NULL) {
vin_err("ISP failed to get of node\n");
return -ENODEV;
}
isp = kzalloc(sizeof(struct isp_dev), GFP_KERNEL);
if (!isp) {
ret = -ENOMEM;
goto ekzalloc;
}
isp->stat_buf = kzalloc(sizeof(struct isp_stat_to_user), GFP_KERNEL);
if (!isp->stat_buf) {
vin_err("request stat_buf struct failed!\n");
return -ENOMEM;
}
of_property_read_u32(np, "device_id", &pdev->id);
if (pdev->id < 0) {
vin_err("ISP failed to get device id\n");
ret = -EINVAL;
goto freedev;
}
isp->id = pdev->id;
isp->pdev = pdev;
isp->base = of_iomap(np, 0);
if (!isp->base) {
isp->is_empty = 1;
isp->base = kzalloc(0x300, GFP_KERNEL);
if (!isp->base) {
ret = -EIO;
goto freedev;
}
} else {
isp->is_empty = 0;
/*get irq resource */
isp->irq = irq_of_parse_and_map(np, 0);
if (isp->irq <= 0) {
vin_err("failed to get ISP IRQ resource\n");
goto unmap;
}
ret = request_irq(isp->irq, isp_isr, IRQF_DISABLED, isp->pdev->name, isp);
if (ret) {
vin_err("isp%d request irq failed\n", isp->id);
goto unmap;
}
}
list_add_tail(&isp->isp_list, &isp_drv_list);
__isp_init_subdev(isp);
spin_lock_init(&isp->slock);
if (isp_resource_alloc(isp) < 0) {
ret = -ENOMEM;
goto freeirq;
}
ret = vin_isp_h3a_init(isp);
if (ret < 0) {
vin_err("VIN H3A initialization failed\n");
goto free_res;
}
bsp_isp_init_platform(SUNXI_PLATFORM_ID);
bsp_isp_set_base_addr(isp->id, (unsigned long)isp->base);
bsp_isp_set_map_load_addr(isp->id, (unsigned long)isp->isp_load.vir_addr);
bsp_isp_set_map_saved_addr(isp->id, (unsigned long)isp->isp_save.vir_addr);
memset(isp->isp_load.vir_addr, 0, ISP_LOAD_REG_SIZE);
memset(isp->isp_save.vir_addr, 0, ISP_SAVED_REG_SIZE);
memcpy(isp->isp_load.vir_addr, &isp_default_reg[0], 0x400);
platform_set_drvdata(pdev, isp);
vin_print("isp%d probe end!\n", isp->id);
return 0;
free_res:
isp_resource_free(isp);
freeirq:
if (!isp->is_empty)
free_irq(isp->irq, isp);
unmap:
if (!isp->is_empty)
iounmap(isp->base);
else
kfree(isp->base);
list_del(&isp->isp_list);
freedev:
kfree(isp);
ekzalloc:
vin_print("isp probe err!\n");
return ret;
}
static int isp_remove(struct platform_device *pdev)
{
struct isp_dev *isp = platform_get_drvdata(pdev);
struct v4l2_subdev *sd = &isp->subdev;
platform_set_drvdata(pdev, NULL);
v4l2_ctrl_handler_free(sd->ctrl_handler);
v4l2_set_subdevdata(sd, NULL);
isp_resource_free(isp);
if (!isp->is_empty) {
free_irq(isp->irq, isp);
if (isp->base)
iounmap(isp->base);
} else {
kfree(isp->base);
}
list_del(&isp->isp_list);
kfree(isp->stat_buf);
vin_isp_h3a_cleanup(isp);
kfree(isp);
return 0;
}
static const struct of_device_id sunxi_isp_match[] = {
{.compatible = "allwinner,sunxi-isp",},
{},
};
static struct platform_driver isp_platform_driver = {
.probe = isp_probe,
.remove = isp_remove,
.driver = {
.name = ISP_MODULE_NAME,
.owner = THIS_MODULE,
.of_match_table = sunxi_isp_match,
}
};
void sunxi_isp_sensor_type(struct v4l2_subdev *sd, int use_isp, int is_raw)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
isp->use_isp = use_isp;
isp->is_raw = is_raw;
}
void sunxi_isp_dump_regs(struct v4l2_subdev *sd)
{
struct isp_dev *isp = v4l2_get_subdevdata(sd);
int i = 0;
printk("vin dump ISP regs :\n");
for (i = 0; i < 0x40; i = i + 4) {
if (i % 0x10 == 0)
printk("0x%08x: ", i);
printk("0x%08x, ", readl(isp->base + i));
if (i % 0x10 == 0xc)
printk("\n");
}
for (i = 0x40; i < 0x240; i = i + 4) {
if (i % 0x10 == 0)
printk("0x%08x: ", i);
printk("0x%08x, ", readl(isp->isp_load.vir_addr + i));
if (i % 0x10 == 0xc)
printk("\n");
}
}
struct v4l2_subdev *sunxi_isp_get_subdev(int id)
{
struct isp_dev *isp;
list_for_each_entry(isp, &isp_drv_list, isp_list) {
if (isp->id == id) {
isp->use_cnt++;
return &isp->subdev;
}
}
return NULL;
}
struct v4l2_subdev *sunxi_stat_get_subdev(int id)
{
struct isp_dev *isp;
list_for_each_entry(isp, &isp_drv_list, isp_list) {
if (isp->id == id) {
return &isp->h3a_stat.sd;
}
}
return NULL;
}
int sunxi_isp_platform_register(void)
{
return platform_driver_register(&isp_platform_driver);
}
void sunxi_isp_platform_unregister(void)
{
platform_driver_unregister(&isp_platform_driver);
vin_print("isp_exit end\n");
}