/* * linux-4.9/drivers/media/platform/sunxi-vfe/vfe.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 Camera Interface driver * Author: raymonxiu */ #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_DEVFREQ_DRAM_FREQ_WITH_SOFT_NOTIFY #include #endif #include "vfe.h" #include "bsp_common.h" #include "lib/bsp_isp_algo.h" #include "csi_cci/bsp_cci.h" #include "csi_cci/cci_helper.h" #include "config.h" #include "device/camera_cfg.h" #include "utility/sensor_info.h" #include "utility/vfe_io.h" #include "csi/sunxi_csi.h" #include "sunxi_isp.h" #include "mipi_csi/sunxi_mipi.h" #define IS_FLAG(x, y) (((x)&(y)) == y) #define CLIP_MAX(x, max) ((x) > max ? max : x) #define VFE_MAJOR_VERSION 1 #define VFE_MINOR_VERSION 0 #define VFE_RELEASE 0 #define VFE_VERSION \ KERNEL_VERSION(VFE_MAJOR_VERSION, VFE_MINOR_VERSION, VFE_RELEASE) #define VFE_MODULE_NAME "sunxi_vfe" #define VID_N_OFF 8 #define MCLK_OUT_RATE (24*1000*1000) #define MAX_FRAME_MEM (150*1024*1024) #define MIN_WIDTH (32) #define MIN_HEIGHT (32) #define MAX_WIDTH (4800) #define MAX_HEIGHT (4800) #define DUMP_CSI (1 << 0) #define DUMP_ISP (1 << 1) /* #define _REGULATOR_CHANGE_ */ struct vfe_dev *vfe_dev_gbl[2]; static char ccm[I2C_NAME_SIZE] = ""; static uint i2c_addr = 0xff; static char act_name[I2C_NAME_SIZE] = ""; static uint act_slave = 0xff; static uint define_sensor_list = 0xff; static uint vfe_i2c_dbg; static uint isp_log; static uint vips = 0xffff; static int touch_flash_flag; static int ev_cumul; unsigned int isp_reparse_flag; static unsigned int frame_cnt; static unsigned int vfe_dump; struct mutex probe_hdl_lock; struct file *fp_dbg; static char LogFileName[128] = "/system/etc/hawkview/log.bin"; module_param_string(ccm, ccm, sizeof(ccm), S_IRUGO | S_IWUSR); module_param(i2c_addr, uint, S_IRUGO | S_IWUSR); module_param_string(act_name, act_name, sizeof(act_name), S_IRUGO | S_IWUSR); module_param(act_slave, uint, S_IRUGO | S_IWUSR); module_param(define_sensor_list, uint, S_IRUGO | S_IWUSR); module_param(vfe_i2c_dbg, uint, S_IRUGO | S_IWUSR); module_param(isp_log, uint, S_IRUGO | S_IWUSR); module_param(vips, uint, S_IRUGO | S_IWUSR); static ssize_t vfe_dbg_en_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", vfe_dbg_en); } static ssize_t vfe_dbg_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int err; unsigned long val; err = kstrtoul(buf, 10, &val); if (err) { vfe_print("Invalid size\n"); return err; } if (val < 0 || val > 1) { vfe_print("Invalid value, 0~1 is expected!\n"); } else { vfe_dbg_en = val; vfe_print("vfe_dbg_en = %ld\n", val); } return count; } static ssize_t vfe_dbg_lv_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", vfe_dbg_lv); } static ssize_t vfe_dbg_lv_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int err; unsigned long val; err = kstrtoul(buf, 10, &val); if (err) { vfe_print("Invalid size\n"); return err; } if (val < 0 || val > 4) { vfe_print("Invalid value, 0~3 is expected!\n"); } else { vfe_dbg_lv = val; vfe_print("vfe_dbg_lv = %d\n", vfe_dbg_lv); } return count; } static ssize_t isp_reparse_flag_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", isp_reparse_flag); } static ssize_t isp_reparse_flag_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int err; unsigned long val; err = kstrtoul(buf, 10, &val); if (err) { vfe_print("Invalid size\n"); return err; } if (val < 0 || val > 4) { vfe_print("Invalid value, 0~1 is expected!\n"); } else { isp_reparse_flag = val; vfe_print("isp_reparse_flag = %ld\n", val); } return count; } static ssize_t vfe_dbg_dump_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", vfe_dump); } static ssize_t vfe_dbg_dump_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int err; unsigned long val; err = kstrtoul(buf, 10, &val); if (err) { vfe_print("Invalid size\n"); return err; } if (val < 0 || val > 3) { vfe_print("Invalid value, 0~3 is expected!\n"); } else { vfe_dump = val; vfe_print("vfe_dump = %ld\n", val); } return count; } static DEVICE_ATTR(vfe_dbg_en, S_IRUGO | S_IWUSR | S_IWGRP, vfe_dbg_en_show, vfe_dbg_en_store); static DEVICE_ATTR(vfe_dbg_lv, S_IRUGO | S_IWUSR | S_IWGRP, vfe_dbg_lv_show, vfe_dbg_lv_store); static DEVICE_ATTR(vfe_dump, S_IRUGO | S_IWUSR | S_IWGRP, vfe_dbg_dump_show, vfe_dbg_dump_store); static DEVICE_ATTR(isp_reparse_flag, S_IRUGO | S_IWUSR | S_IWGRP, isp_reparse_flag_show, isp_reparse_flag_store); static struct attribute *vfe_attributes[] = { &dev_attr_vfe_dbg_en.attr, &dev_attr_vfe_dbg_lv.attr, &dev_attr_vfe_dump.attr, &dev_attr_isp_reparse_flag.attr, NULL }; static struct attribute_group vfe_attribute_group = { .name = "vfe_attr", .attrs = vfe_attributes }; static struct vfe_fmt formats[] = { { .name = "planar YUV 422", .fourcc = V4L2_PIX_FMT_YUV422P, .depth = 16, .planes_cnt = 3, }, { .name = "planar YUV 420", .fourcc = V4L2_PIX_FMT_YUV420, .depth = 12, .planes_cnt = 3, }, { .name = "planar YVU 420", .fourcc = V4L2_PIX_FMT_YVU420, .depth = 12, .planes_cnt = 3, }, { .name = "planar YUV 422 UV combined", .fourcc = V4L2_PIX_FMT_NV16, .depth = 16, .planes_cnt = 2, }, { .name = "planar YUV 420 UV combined", .fourcc = V4L2_PIX_FMT_NV12, .depth = 12, .planes_cnt = 2, }, { .name = "planar YUV 422 VU combined", .fourcc = V4L2_PIX_FMT_NV61, .depth = 16, .planes_cnt = 2, }, { .name = "planar YUV 420 VU combined", .fourcc = V4L2_PIX_FMT_NV21, .depth = 12, .planes_cnt = 2, }, { .name = "MB YUV420", .fourcc = V4L2_PIX_FMT_HM12, .depth = 12, .planes_cnt = 2, }, { .name = "YUV422 YUYV", .fourcc = V4L2_PIX_FMT_YUYV, .depth = 16, .planes_cnt = 1, }, { .name = "YUV422 YVYU", .fourcc = V4L2_PIX_FMT_YVYU, .depth = 16, .planes_cnt = 1, }, { .name = "YUV422 UYVY", .fourcc = V4L2_PIX_FMT_UYVY, .depth = 16, .planes_cnt = 1, }, { .name = "YUV422 VYUY", .fourcc = V4L2_PIX_FMT_VYUY, .depth = 16, .planes_cnt = 1, }, { .name = "RAW Bayer BGGR 8bit", .fourcc = V4L2_PIX_FMT_SBGGR8, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer GBRG 8bit", .fourcc = V4L2_PIX_FMT_SGBRG8, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer GRBG 8bit", .fourcc = V4L2_PIX_FMT_SGRBG8, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer RGGB 8bit", .fourcc = V4L2_PIX_FMT_SGRBG8, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer BGGR 10bit", .fourcc = V4L2_PIX_FMT_SBGGR10, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer GBRG 10bit", .fourcc = V4L2_PIX_FMT_SGBRG10, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer GRBG 10bit", .fourcc = V4L2_PIX_FMT_SGRBG10, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer RGGB 10bit", .fourcc = V4L2_PIX_FMT_SGRBG10, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer BGGR 12bit", .fourcc = V4L2_PIX_FMT_SBGGR12, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer GBRG 12bit", .fourcc = V4L2_PIX_FMT_SGBRG12, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer GRBG 12bit", .fourcc = V4L2_PIX_FMT_SGRBG12, .depth = 8, .planes_cnt = 1, }, { .name = "RAW Bayer RGGB 12bit", .fourcc = V4L2_PIX_FMT_SGRBG12, .depth = 8, .planes_cnt = 1, }, }; u32 try_yuv422_bus[] = { MEDIA_BUS_FMT_VYUY10_2X10, MEDIA_BUS_FMT_UYVY10_2X10, MEDIA_BUS_FMT_YUYV10_2X10, MEDIA_BUS_FMT_YVYU10_2X10, MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_VYUY8_2X8, MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YVYU8_2X8, MEDIA_BUS_FMT_YUYV10_1X20, MEDIA_BUS_FMT_YVYU10_1X20, MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_VYUY8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YVYU8_1X16, MEDIA_BUS_FMT_AYUV8_1X32, }; u32 try_yuv420_bus[] = { MEDIA_BUS_FMT_VYUY10_1X20, MEDIA_BUS_FMT_UYVY10_1X20, }; u32 try_bayer_rgb_bus[] = { MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, }; u32 try_rgb565_bus[] = { MEDIA_BUS_FMT_ARGB8888_1X32, MEDIA_BUS_FMT_BGR565_2X8_BE, MEDIA_BUS_FMT_BGR565_2X8_LE, MEDIA_BUS_FMT_RGB565_2X8_BE, MEDIA_BUS_FMT_RGB565_2X8_LE, }; u32 try_rgb888_bus[] = { MEDIA_BUS_FMT_RBG888_1X24, }; struct vfe_fmt_code vfe_fmt_code[] = { { .bus_name = "bayer", .pix_name = "yuv422/yuv420", .mbus_code = try_bayer_rgb_bus, .size = ARRAY_SIZE(try_bayer_rgb_bus), }, { .bus_name = "yuv422", .pix_name = "yuv422/yuv420", .mbus_code = try_yuv422_bus, .size = ARRAY_SIZE(try_yuv422_bus), }, { .bus_name = "yuv420", .pix_name = "yuv422/yuv420", .mbus_code = try_yuv420_bus, .size = ARRAY_SIZE(try_yuv420_bus), }, { .bus_name = "rgb565", .pix_name = "rgb565", .mbus_code = try_rgb565_bus, .size = ARRAY_SIZE(try_rgb565_bus), }, { .bus_name = "rgb888", .pix_name = "rgb888/prgb888", .mbus_code = try_rgb888_bus, .size = ARRAY_SIZE(try_rgb888_bus), }, }; static int isp_resource_request(struct vfe_dev *dev) { unsigned int isp_used_flag = 0, i; void *pa_base, *va_base, *dma_base; int ret; /* requeset for isp table and statistic buffer */ for (i = 0; i < dev->dev_qty; i++) { if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw) { dev->isp_lut_tbl_buf_mm[i].size = ISP_LINEAR_LUT_LENS_GAMMA_MEM_SIZE; ret = os_mem_alloc(&dev->pdev->dev, &dev->isp_lut_tbl_buf_mm[i]); if (!ret) { pa_base = dev->isp_lut_tbl_buf_mm[i].phy_addr; va_base = dev->isp_lut_tbl_buf_mm[i].vir_addr; dma_base = dev->isp_lut_tbl_buf_mm[i].dma_addr; dev->isp_tbl_addr[i].isp_def_lut_tbl_paddr = (void *)(pa_base + ISP_LUT_MEM_OFS); dev->isp_tbl_addr[i].isp_def_lut_tbl_dma_addr = (void *)(dma_base + ISP_LUT_MEM_OFS); dev->isp_tbl_addr[i].isp_def_lut_tbl_vaddr = (void *)(va_base + ISP_LUT_MEM_OFS); dev->isp_tbl_addr[i].isp_lsc_tbl_paddr = (void *)(pa_base + ISP_LENS_MEM_OFS); dev->isp_tbl_addr[i].isp_lsc_tbl_dma_addr = (void *)(dma_base + ISP_LENS_MEM_OFS); dev->isp_tbl_addr[i].isp_lsc_tbl_vaddr = (void *)(va_base + ISP_LENS_MEM_OFS); dev->isp_tbl_addr[i].isp_gamma_tbl_paddr = (void *)(pa_base + ISP_GAMMA_MEM_OFS); dev->isp_tbl_addr[i].isp_gamma_tbl_dma_addr = (void *)(dma_base + ISP_GAMMA_MEM_OFS); dev->isp_tbl_addr[i].isp_gamma_tbl_vaddr = (void *)(va_base + ISP_GAMMA_MEM_OFS); dev->isp_tbl_addr[i].isp_linear_tbl_paddr = (void *)(pa_base + ISP_LINEAR_MEM_OFS); dev->isp_tbl_addr[i].isp_linear_tbl_dma_addr = (void *)(dma_base + ISP_LINEAR_MEM_OFS); dev->isp_tbl_addr[i].isp_linear_tbl_vaddr = (void *)(va_base + ISP_LINEAR_MEM_OFS); vfe_dbg(0, "isp_def_lut_tbl_vaddr[%d] = %p\n", i, dev->isp_tbl_addr[i].isp_def_lut_tbl_vaddr); vfe_dbg(0, "isp_lsc_tbl_vaddr[%d] = %p\n", i, dev->isp_tbl_addr[i].isp_lsc_tbl_vaddr); vfe_dbg(0, "isp_gamma_tbl_vaddr[%d] = %p\n", i, dev->isp_tbl_addr[i].isp_gamma_tbl_vaddr); } else { vfe_err("isp lut_lens_gamma table request pa failed!\n"); return -ENOMEM; } } if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw) { dev->isp_drc_tbl_buf_mm[i].size = ISP_DRC_DISC_MEM_SIZE; ret = os_mem_alloc(&dev->pdev->dev, &dev->isp_drc_tbl_buf_mm[i]); if (!ret) { pa_base = dev->isp_drc_tbl_buf_mm[i].phy_addr; va_base = dev->isp_drc_tbl_buf_mm[i].vir_addr; dma_base = dev->isp_drc_tbl_buf_mm[i].dma_addr; dev->isp_tbl_addr[i].isp_drc_tbl_paddr = (void *)(pa_base + ISP_DRC_MEM_OFS); dev->isp_tbl_addr[i].isp_drc_tbl_dma_addr = (void *)(dma_base + ISP_DRC_MEM_OFS); dev->isp_tbl_addr[i].isp_drc_tbl_vaddr = (void *)(va_base + ISP_DRC_MEM_OFS); dev->isp_tbl_addr[i].isp_disc_tbl_paddr = (void *)(pa_base + ISP_DISC_MEM_OFS); dev->isp_tbl_addr[i].isp_disc_tbl_dma_addr = (void *)(dma_base + ISP_DISC_MEM_OFS); dev->isp_tbl_addr[i].isp_disc_tbl_vaddr = (void *)(va_base + ISP_DISC_MEM_OFS); vfe_dbg(0, "isp_drc_tbl_vaddr[%d] = %p\n", i, dev->isp_tbl_addr[i].isp_drc_tbl_vaddr); } else { vfe_err("isp drc table request pa failed!\n"); return -ENOMEM; } } } for (i = 0; i < dev->dev_qty; i++) { if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw) { isp_used_flag = 1; break; } } if (isp_used_flag) { for (i = 0; i < MAX_ISP_STAT_BUF; i++) { dev->isp_stat_buf_mm[i].size = ISP_STAT_TOTAL_SIZE; ret = os_mem_alloc(&dev->pdev->dev, &dev->isp_stat_buf_mm[i]); if (!ret) { pa_base = dev->isp_stat_buf_mm[i].phy_addr; va_base = dev->isp_stat_buf_mm[i].vir_addr; dma_base = dev->isp_stat_buf_mm[i].dma_addr; INIT_LIST_HEAD(&dev->isp_stat_bq.isp_stat[i].queue); dev->isp_stat_bq.isp_stat[i].id = i; dev->isp_stat_bq.isp_stat[i].paddr = (void *)(pa_base); dev->isp_stat_bq.isp_stat[i].dma_addr = (void *)(dma_base); dev->isp_stat_bq.isp_stat[i].isp_stat_buf.stat_buf = (void *)(va_base); dev->isp_stat_bq.isp_stat[i].isp_stat_buf.buf_size = ISP_STAT_TOTAL_SIZE; dev->isp_stat_bq.isp_stat[i].isp_stat_buf.buf_status = BUF_IDLE; vfe_dbg(0, "dev->isp_stat_bq.isp_stat[i].isp_stat_buf.stat_buf[%d] = %p\n", i, dev->isp_stat_bq.isp_stat[i].isp_stat_buf.stat_buf); } else { vfe_err("isp statistic buffer request pa failed!\n"); return -ENOMEM; } } } return 0; } static int vfe_device_regulator_get(struct ccm_config *ccm_cfg); static int vfe_device_regulator_put(struct ccm_config *ccm_cfg); static int vfe_set_sensor_power_on(struct vfe_dev *dev); static int vfe_set_sensor_power_off(struct vfe_dev *dev); static void isp_resource_release(struct vfe_dev *dev) { unsigned int isp_used_flag = 0, i; /* release isp table and statistic buffer */ for (i = 0; i < dev->dev_qty; i++) { if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw) { os_mem_free(&dev->pdev->dev, &dev->isp_lut_tbl_buf_mm[i]); os_mem_free(&dev->pdev->dev, &dev->isp_drc_tbl_buf_mm[i]); } } for (i = 0; i < dev->dev_qty; i++) { if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw) { isp_used_flag = 1; break; } } if (isp_used_flag) { for (i = 0; i < MAX_ISP_STAT_BUF; i++) { os_mem_free(&dev->pdev->dev, &dev->isp_stat_buf_mm[i]); } } } static int vfe_is_generating(struct vfe_dev *dev) { int ret; unsigned long flags = 0; spin_lock_irqsave(&dev->slock, flags); ret = test_bit(0, &dev->generating); spin_unlock_irqrestore(&dev->slock, flags); return ret; } static void vfe_start_generating(struct vfe_dev *dev) { unsigned long flags = 0; spin_lock_irqsave(&dev->slock, flags); set_bit(0, &dev->generating); spin_unlock_irqrestore(&dev->slock, flags); } static void vfe_stop_generating(struct vfe_dev *dev) { unsigned long flags = 0; spin_lock_irqsave(&dev->slock, flags); dev->first_flag[dev->cur_ch] = 0; clear_bit(0, &dev->generating); spin_unlock_irqrestore(&dev->slock, flags); } static int vfe_is_opened(struct vfe_dev *dev) { int ret; mutex_lock(&dev->opened_lock); ret = test_bit(0, &dev->opened); mutex_unlock(&dev->opened_lock); return ret; } static void vfe_start_opened(struct vfe_dev *dev) { mutex_lock(&dev->opened_lock); set_bit(0, &dev->opened); mutex_unlock(&dev->opened_lock); } static void vfe_stop_opened(struct vfe_dev *dev) { mutex_lock(&dev->opened_lock); clear_bit(0, &dev->opened); mutex_unlock(&dev->opened_lock); } static void update_ccm_info(struct vfe_dev *dev, struct ccm_config *ccm_cfg) { dev->sd = ccm_cfg->sd; dev->sd_act = ccm_cfg->sd_act; dev->is_isp_used = ccm_cfg->is_isp_used; dev->is_bayer_raw = ccm_cfg->is_bayer_raw; dev->power = &ccm_cfg->power; dev->gpio = ccm_cfg->gpio; dev->flash_used = ccm_cfg->flash_used; dev->flash_type = ccm_cfg->flash_type; /* print change */ vfe_dbg(0, "ccm_cfg pt = %p\n", ccm_cfg); vfe_dbg(0, "ccm_cfg->sd = %p\n", ccm_cfg->sd); vfe_dbg(0, "module is_isp_used = %d is_bayer_raw= %d\n", dev->is_isp_used, dev->is_bayer_raw); } static void update_isp_setting(struct vfe_dev *dev) { dev->isp_3a_result_pt = &dev->isp_3a_result[dev->input]; dev->isp_gen_set_pt = dev->isp_gen_set[dev->input]; dev->isp_gen_set_pt->module_cfg.isp_platform_id = dev->platform_id; if (dev->is_bayer_raw) { mutex_init(&dev->isp_3a_result_mutex); dev->isp_gen_set_pt->module_cfg.lut_src0_table = dev->isp_tbl_addr[dev->input].isp_def_lut_tbl_vaddr; dev->isp_gen_set_pt->module_cfg.gamma_table = dev->isp_tbl_addr[dev->input].isp_gamma_tbl_vaddr; dev->isp_gen_set_pt->module_cfg.lens_table = dev->isp_tbl_addr[dev->input].isp_lsc_tbl_vaddr; dev->isp_gen_set_pt->module_cfg.linear_table = dev->isp_tbl_addr[dev->input].isp_linear_tbl_vaddr; dev->isp_gen_set_pt->module_cfg.disc_table = dev->isp_tbl_addr[dev->input].isp_disc_tbl_vaddr; if (dev->is_isp_used) bsp_isp_update_lut_lens_gamma_table(&dev->isp_tbl_addr[dev->input]); } dev->isp_gen_set_pt->module_cfg.drc_table = dev->isp_tbl_addr[dev->input].isp_drc_tbl_vaddr; if (dev->is_isp_used) bsp_isp_update_drc_table(&dev->isp_tbl_addr[dev->input]); } static inline void vfe_set_addr(struct vfe_dev *dev, struct vfe_buffer *buffer, int ch) { dma_addr_t addr_org; struct vb2_buffer *vb_buf = &buffer->vb.vb2_buf; if (dev->special_active == 1) { if (buffer == NULL || buffer->paddr == NULL) { vfe_err("%s buffer is NULL!\n", __func__); return; } } else { if (vb_buf == NULL || vb_buf->planes[0].mem_priv == NULL) { vfe_err("vb_buf->priv is NULL!\n"); return; } } if (dev->special_active == 1) addr_org = (dma_addr_t)buffer->paddr; else addr_org = vb2_dma_contig_plane_dma_addr(vb_buf, 0) - CPU_DRAM_PADDR_ORG + HW_DMA_OFFSET; if (dev->is_isp_used) { sunxi_isp_set_output_addr(dev->isp_sd, addr_org); } else { #if defined(CH_OUTPUT_IN_DIFFERENT_VIDEO) bsp_csi_set_ch_addr(dev->csi_sel, ch, addr_org); #else bsp_csi_set_addr(dev->csi_sel, addr_org); #endif } vfe_dbg(3, "csi_buf_addr_orginal=%pa\n", &addr_org); } static void vfe_init_isp_log(struct vfe_dev *dev) { if (isp_log == 1) { fp_dbg = cfg_open_file(LogFileName); dev->isp_gen_set[0]->enable_log = 1; dev->isp_gen_set[1]->enable_log = 1; if (IS_ERR(fp_dbg)) vfe_err("open log.txt error."); } else { dev->isp_gen_set[0]->enable_log = 0; dev->isp_gen_set[1]->enable_log = 0; } } static void vfe_exit_isp_log(struct vfe_dev *dev) { if (isp_log == 1) cfg_close_file(fp_dbg); } static void vfe_dump_isp_log(struct vfe_dev *dev) { /* dump isp log. */ if (isp_log == 1 && (frame_cnt % 4 == 0)) { if (cfg_write_file(fp_dbg, dev->isp_gen_set_pt->stat.hist_buf, ISP_STAT_HIST_MEM_SIZE) < 0) { vfe_err("dump isp hist faild."); return; } if (cfg_write_file(fp_dbg, dev->isp_gen_set_pt->stat.ae_buf, ISP_STAT_AE_MEM_SIZE) < 0) { vfe_err("dump isp ae faild."); } if (cfg_write_file(fp_dbg, (char *)dev->isp_gen_set_pt->awb_buf, 3*ISP_STAT_AWB_WIN_MEM_SIZE) < 0) { vfe_err("dump awb log faild."); } /* if (cfg_write_file(fp_dbg, dev->isp_gen_set_pt->stat.af_buf, ISP_STAT_AF_MEM_SIZE) < 0) { vfe_err("dump isp log faild."); } if (cfg_write_file(fp_dbg, "0123456789abcdef\n", 16) < 0) { vfe_err("/system/etc/hawkview/log.txt write test failed."); } */ } } static void isp_isr_bh_handle(struct work_struct *work) { struct actuator_ctrl_word_t vcm_ctrl; struct vfe_dev *dev = container_of(work, struct vfe_dev, isp_isr_bh_task); FUNCTION_LOG; if (vfe_dump & DUMP_ISP) { if (9 == (frame_cnt % 10)) { sunxi_isp_dump_regs(dev->isp_sd); } } if (dev->is_bayer_raw) { mutex_lock(&dev->isp_3a_result_mutex); if (isp_reparse_flag == 1) { vfe_print("ISP reparse ini file!\n"); if (read_ini_info(dev, dev->input, "/system/etc/hawkview/")) { vfe_warn("ISP reparse ini fail, please check isp config!\n"); goto ISP_REPARSE_END; } isp_param_init(dev->isp_gen_set_pt); isp_config_init(dev->isp_gen_set_pt); isp_module_init(dev->isp_gen_set_pt, dev->isp_3a_result_pt); ISP_REPARSE_END: isp_reparse_flag = 0; } if (isp_reparse_flag == 2) { vfe_reg_set((void __iomem *) (unsigned long)(ISP_REGS_BASE+0x10), (1 << 20)); } if (isp_reparse_flag == 3) { vfe_reg_clr_set((void __iomem *) (unsigned long)(ISP_REGS_BASE+0x10), (0xF << 16), (1 << 16)); vfe_reg_set((void __iomem *) (unsigned long)(ISP_REGS_BASE+0x10), (1 << 20)); } if (isp_reparse_flag == 4) { /* vfe_reg_clr_set(IO_ADDRESS(ISP_REGS_BASE+0x10), (0xF << 16), (1 << 16)); */ vfe_reg_clr((void __iomem *) (unsigned long)(ISP_REGS_BASE+0x10), (1 << 20)); vfe_reg_clr((void __iomem *) (unsigned long)(ISP_REGS_BASE+0x10), (0xF << 16)); } vfe_dump_isp_log(dev); isp_isr(dev->isp_gen_set_pt, dev->isp_3a_result_pt); if ((dev->ctrl_para.prev_focus_pos != dev->isp_3a_result_pt->real_vcm_pos || dev->isp_gen_set_pt->isp_ini_cfg.isp_test_settings.isp_test_mode != 0 || dev->isp_gen_set_pt->isp_ini_cfg.isp_test_settings.af_en == 0) && dev->sd_act) { vcm_ctrl.code = dev->isp_3a_result_pt->real_vcm_pos; vcm_ctrl.sr = 0x0; if (v4l2_subdev_call(dev->sd_act, core, ioctl, ACT_SET_CODE, &vcm_ctrl)) { vfe_warn("set vcm error!\n"); } else { dev->ctrl_para.prev_focus_pos = dev->isp_3a_result_pt->real_vcm_pos; } } mutex_unlock(&dev->isp_3a_result_mutex); } else { isp_isr(dev->isp_gen_set_pt, NULL); } FUNCTION_LOG; } int set_sensor_shutter(struct vfe_dev *dev, int shutter) { struct v4l2_control ctrl; if (shutter <= 0) return -EINVAL; ctrl.id = V4L2_CID_EXPOSURE; ctrl.value = shutter; if (v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &ctrl) != 0) { vfe_err("set sensor exposure line error!\n"); return -EPERM; } else dev->ctrl_para.prev_exp_line = shutter; return 0; } int set_sensor_gain(struct vfe_dev *dev, int gain) { struct v4l2_control ctrl; if (gain < 16) return -EINVAL; ctrl.id = V4L2_CID_GAIN; ctrl.value = gain; if (v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &ctrl) != 0) { vfe_err("set sensor gain error!\n"); return -EPERM; } else dev->ctrl_para.prev_ana_gain = gain; return 0; } int set_sensor_shutter_and_gain(struct vfe_dev *dev) { struct sensor_exp_gain exp_gain; exp_gain.exp_val = dev->isp_3a_result_pt->exp_line_num; exp_gain.gain_val = dev->isp_3a_result_pt->exp_analog_gain; if (exp_gain.gain_val < 16 || exp_gain.exp_val <= 0) return -EINVAL; if (v4l2_subdev_call(dev->sd, core, ioctl, ISP_SET_EXP_GAIN, &exp_gain) != 0) { vfe_warn("set ISP_SET_EXP_GAIN error, set V4L2_CID_EXPOSURE!\n"); return -EPERM; } else { dev->ctrl_para.prev_exp_line = exp_gain.exp_val; dev->ctrl_para.prev_ana_gain = exp_gain.gain_val; } return 0; } static int isp_s_ctrl_torch_open(struct vfe_dev *dev) { if (dev->isp_gen_set_pt->exp_settings.flash_mode == FLASH_MODE_OFF) return 0; if (((dev->isp_gen_set_pt->exp_settings.tbl_cnt > (dev->isp_gen_set_pt->exp_settings.tbl_max_ind - 25)) || dev->isp_gen_set_pt->exp_settings.flash_mode == FLASH_MODE_ON)) { vfe_dbg(0, "open flash when nigth mode\n"); io_set_flash_ctrl(dev->flash_sd, SW_CTRL_TORCH_ON); touch_flash_flag = 1; } return 0; } static int isp_s_ctrl_torch_close(struct vfe_dev *dev) { if (dev->isp_gen_set_pt->exp_settings.flash_mode == FLASH_MODE_OFF) return 0; if (touch_flash_flag == 1) { vfe_dbg(0, "close flash when nigth mode\n"); io_set_flash_ctrl(dev->flash_sd, SW_CTRL_FLASH_OFF); touch_flash_flag = 0; } return 0; } static int isp_streamoff_torch_and_flash_close(struct vfe_dev *dev) { if (dev->isp_gen_set_pt->exp_settings.flash_mode == FLASH_MODE_OFF) return 0; if (touch_flash_flag == 1 || dev->isp_gen_set_pt->exp_settings.flash_open == 1) { vfe_dbg(0, "close flash when nigth mode\n"); io_set_flash_ctrl(dev->flash_sd, SW_CTRL_FLASH_OFF); touch_flash_flag = 0; } return 0; } static int isp_set_capture_flash(struct vfe_dev *dev) { if (dev->isp_gen_set_pt->exp_settings.flash_mode == FLASH_MODE_OFF) return 0; if (dev->isp_gen_set_pt->take_pic_start_cnt == 1) { if (dev->isp_gen_set_pt->exp_settings.tbl_cnt > (dev->isp_gen_set_pt->exp_settings.tbl_max_ind - 40) || dev->isp_gen_set_pt->exp_settings.flash_mode == FLASH_MODE_ON) { vfe_dbg(0, "open torch when nigth mode\n"); io_set_flash_ctrl(dev->flash_sd, SW_CTRL_TORCH_ON); dev->isp_gen_set_pt->exp_settings.flash_open = 1; } } if (dev->isp_gen_set_pt->exp_settings.flash_open == 1 && dev->isp_gen_set_pt->take_pic_start_cnt == dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.flash_delay_frame) { vfe_dbg(0, "open flash when nigth mode\n"); dev->isp_gen_set_pt->exp_settings.exposure_lock = ISP_TRUE; ev_cumul = get_pre_ev_cumul(dev->isp_gen_set_pt, dev->isp_3a_result_pt); if (ev_cumul >= 100) { dev->isp_gen_set_pt->exp_settings.tbl_cnt = CLIP(dev->isp_gen_set_pt->exp_settings.expect_tbl_cnt, 1, dev->isp_gen_set_pt->exp_settings.tbl_max_ind); } else if (ev_cumul >= dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.flash_gain * 100 / 256 && ev_cumul < 100) { dev->isp_gen_set_pt->exp_settings.tbl_cnt = CLIP(dev->isp_gen_set_pt->exp_settings.expect_tbl_cnt, 1, dev->isp_gen_set_pt->exp_settings.tbl_max_ind); } else if (ev_cumul >= -25 && ev_cumul < dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.flash_gain * 100 / 256) { dev->isp_gen_set_pt->exp_settings.tbl_cnt = CLIP(dev->isp_gen_set_pt->exp_settings.expect_tbl_cnt + ev_cumul * dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.flash_gain / 256, 1, dev->isp_gen_set_pt->exp_settings.tbl_max_ind); } else { dev->isp_gen_set_pt->exp_settings.tbl_cnt = CLIP(dev->isp_gen_set_pt->exp_settings.expect_tbl_cnt + ev_cumul * dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.flash_gain / 256, 1, dev->isp_gen_set_pt->exp_settings.tbl_max_ind); } config_sensor_next_exposure(dev->isp_gen_set_pt, dev->isp_3a_result_pt); io_set_flash_ctrl(dev->flash_sd, SW_CTRL_FLASH_OFF); } if (dev->isp_gen_set_pt->exp_settings.flash_open == 1 && dev->isp_gen_set_pt->take_pic_start_cnt == dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.flash_delay_frame + 1) { io_set_flash_ctrl(dev->flash_sd, SW_CTRL_FLASH_ON); } if (dev->isp_gen_set_pt->take_pic_start_cnt == 7 + dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.flash_delay_frame) { vfe_dbg(0, "close flash when nigth mode\n"); io_set_flash_ctrl(dev->flash_sd, SW_CTRL_FLASH_OFF); dev->isp_gen_set_pt->exp_settings.tbl_cnt = CLIP(dev->isp_gen_set_pt->exp_settings.expect_tbl_cnt, 1, dev->isp_gen_set_pt->exp_settings.tbl_max_ind); dev->isp_gen_set_pt->exp_settings.exposure_lock = ISP_FALSE; dev->isp_gen_set_pt->exp_settings.flash_open = 0; } if (dev->isp_gen_set_pt->exp_settings.flash_open == 0 && touch_flash_flag == 1 && (dev->isp_3a_result_pt->af_status == AUTO_FOCUS_STATUS_REACHED || dev->isp_3a_result_pt->af_status == AUTO_FOCUS_STATUS_FAILED || dev->isp_3a_result_pt->af_status == AUTO_FOCUS_STATUS_FINDED)) { vfe_dbg(0, "close flash when touch nigth mode\n"); io_set_flash_ctrl(dev->flash_sd, SW_CTRL_FLASH_OFF); touch_flash_flag = 0; } return 0; } static void isp_isr_set_sensor_handle(struct work_struct *work) { struct vfe_dev *dev = container_of(work, struct vfe_dev, isp_isr_set_sensor_task); if (dev->is_bayer_raw) { mutex_lock(&dev->isp_3a_result_mutex); isp_set_capture_flash(dev); if (dev->isp_gen_set_pt->isp_ini_cfg.isp_3a_settings.adaptive_frame_rate == 1 || dev->isp_gen_set_pt->isp_ini_cfg.isp_3a_settings.force_frame_rate == 1 || dev->isp_gen_set_pt->isp_ini_cfg.isp_3a_settings.high_quality_mode_en == 1) { vfe_dbg(0, "combinate shutter = %d, gain =%d\n", dev->isp_3a_result_pt->exp_line_num, dev->isp_3a_result_pt->exp_analog_gain); if (set_sensor_shutter_and_gain(dev) != 0) { set_sensor_shutter(dev, dev->isp_3a_result_pt->exp_line_num); set_sensor_gain(dev, dev->isp_3a_result_pt->exp_analog_gain); } } else { vfe_dbg(0, "separate shutter = %d, gain =%d\n", dev->isp_3a_result_pt->exp_line_num/16, dev->isp_3a_result_pt->exp_analog_gain); set_sensor_shutter(dev, dev->isp_3a_result_pt->exp_line_num); set_sensor_gain(dev, dev->isp_3a_result_pt->exp_analog_gain); } mutex_unlock(&dev->isp_3a_result_mutex); } } static void vfe_isp_stat_parse(struct isp_gen_settings *isp_gen) { unsigned long buffer_addr = (unsigned long)isp_gen->stat.stat_buf_whole->stat_buf; isp_gen->stat.hist_buf = (void *) (buffer_addr); isp_gen->stat.ae_buf = (void *) (buffer_addr + ISP_STAT_AE_MEM_OFS); isp_gen->stat.awb_buf = (void *) (buffer_addr + ISP_STAT_AWB_MEM_OFS); isp_gen->stat.af_buf = (void *) (buffer_addr + ISP_STAT_AF_MEM_OFS); isp_gen->stat.afs_buf = (void *) (buffer_addr + ISP_STAT_AFS_MEM_OFS); isp_gen->stat.awb_win_buf = (void *) (buffer_addr + ISP_STAT_AWB_WIN_MEM_OFS); } /* * the interrupt routine */ static irqreturn_t vfe_isr(int irq, void *priv) { int i; unsigned long flags; struct vfe_buffer *buf; struct vfe_dev *dev = (struct vfe_dev *)priv; struct vfe_dmaqueue *dma_q = &dev->vidq[dev->cur_ch]; struct vfe_dmaqueue *done = NULL; int need_callback = 0, current_ch = 0; struct csi_int_status status; struct vfe_isp_stat_buf_queue *isp_stat_bq = &dev->isp_stat_bq; struct vfe_isp_stat_buf *stat_buf_pt; struct timeval timestamp; FUNCTION_LOG; if (dev->special_active == 1) { dma_q = &dev->vidq_special; done = &dev->done_special; need_callback = 0; } if (vfe_is_generating(dev) == 0) { bsp_csi_int_clear_status(dev->csi_sel, dev->cur_ch, CSI_INT_ALL); if (dev->is_isp_used) bsp_isp_clr_irq_status(ISP_IRQ_EN_ALL); return IRQ_HANDLED; } FUNCTION_LOG; spin_lock_irqsave(&dev->slock, flags); FUNCTION_LOG; #if defined(CH_OUTPUT_IN_DIFFERENT_VIDEO) for (i = 0; i < MAX_CH_NUM; i++) { bsp_csi_int_get_status(dev->csi_sel, i, &status); if (status.frame_done == 1) { current_ch = i; dma_q = &dev->vidq[current_ch]; break; } } #else bsp_csi_int_get_status(dev->csi_sel, dev->cur_ch, &status); current_ch = dev->cur_ch; dma_q = &dev->vidq[current_ch]; #endif vfe_dbg(0, "csi ch %d interrupt, dev->cur_ch is %d!\n", current_ch, dev->cur_ch); if ((status.capture_done == 0) && (status.frame_done == 0) && (status.vsync_trig == 0)) { vfe_print("enter vfe int for nothing\n"); bsp_csi_int_clear_status(dev->csi_sel, current_ch, CSI_INT_ALL); if (dev->is_isp_used) bsp_isp_clr_irq_status(ISP_IRQ_EN_ALL); spin_unlock_irqrestore(&dev->slock, flags); return IRQ_HANDLED; } if (dev->is_isp_used && dev->is_bayer_raw) { /* update_sensor_setting: */ if (status.vsync_trig) { if ((dev->capture_mode == V4L2_MODE_VIDEO) || (dev->capture_mode == V4L2_MODE_PREVIEW)) { vfe_dbg(3, "call set sensor task schedule!\n"); schedule_work(&dev->isp_isr_set_sensor_task); } bsp_csi_int_clear_status(dev->csi_sel, current_ch, CSI_INT_VSYNC_TRIG); spin_unlock_irqrestore(&dev->slock, flags); return IRQ_HANDLED; } } if (vfe_dump & DUMP_CSI) { if (5 == frame_cnt % 10) { sunxi_csi_dump_regs(dev->csi_sd); } } frame_cnt++; /* exception handle: */ if ((status.buf_0_overflow) || (status.buf_1_overflow) || (status.buf_2_overflow) || (status.hblank_overflow)) { if ((status.buf_0_overflow) || (status.buf_1_overflow) || (status.buf_2_overflow)) { bsp_csi_int_clear_status(dev->csi_sel, current_ch, CSI_INT_BUF_0_OVERFLOW | CSI_INT_BUF_1_OVERFLOW | CSI_INT_BUF_2_OVERFLOW); vfe_err("fifo overflow\n"); } if (status.hblank_overflow) { bsp_csi_int_clear_status(dev->csi_sel, current_ch, CSI_INT_HBLANK_OVERFLOW); vfe_err("hblank overflow\n"); } vfe_err("reset csi module\n"); bsp_csi_reset(dev->csi_sel); if (dev->is_isp_used) goto isp_exp_handle; else goto unlock; } isp_exp_handle: if (dev->is_isp_used) { if (bsp_isp_get_irq_status(SRC0_FIFO_INT_EN)) { vfe_err("isp source0 fifo overflow\n"); bsp_isp_clr_irq_status(SRC0_FIFO_INT_EN); goto unlock; } } vfe_dbg(3, "status vsync = %d, framedone = %d, capdone = %d\n", status.vsync_trig, status.frame_done, status.capture_done); if (dev->capture_mode == V4L2_MODE_IMAGE) { if (dev->is_isp_used) bsp_isp_irq_disable(FINISH_INT_EN); else bsp_csi_int_disable(dev->csi_sel, current_ch, CSI_INT_CAPTURE_DONE); vfe_print("ch%d capture image mode!\n", current_ch); buf = list_entry(dma_q->active.next, struct vfe_buffer, list); list_del(&buf->list); if (dev->special_active == 1) { list_add_tail(&buf->list, &done->active); need_callback = 1; } else { vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } goto unlock; } else { if (dev->is_isp_used) bsp_isp_irq_disable(FINISH_INT_EN); else bsp_csi_int_disable(dev->csi_sel, current_ch, CSI_INT_FRAME_DONE); if (dev->first_flag[current_ch] == 0) { dev->first_flag[current_ch]++; vfe_print("ch%d capture video mode!\n", current_ch); goto set_isp_stat_addr; } if (dev->first_flag[current_ch] == 1) { dev->first_flag[current_ch]++; vfe_print("ch%d capture video first frame done!\n", current_ch); } /* video buffer handle: */ if ((&dma_q->active) == dma_q->active.next->next->next) { vfe_warn("Only two buffer left for csi ch%d\n", current_ch); dev->first_flag[current_ch] = 0; goto unlock; } buf = list_entry(dma_q->active.next, struct vfe_buffer, list); /* Nobody is waiting on this buffer*/ if (!dev->special_active) { if (!waitqueue_active(&buf->vb.vb2_buf.vb2_queue->done_wq)) { vfe_warn("Nobody is waiting on buf = 0x%p, ch is %d\n", buf, current_ch); } } list_del(&buf->list); v4l2_get_timestamp(×tamp); vfe_dbg(2, "video buffer frame interval = %ld\n", timestamp.tv_sec * 1000000 + timestamp.tv_usec - (dev->sec*1000000+dev->usec)); dev->sec = timestamp.tv_sec; dev->usec = timestamp.tv_usec; buf->vb.vb2_buf.timestamp = timestamp.tv_sec*1000000000 + timestamp.tv_usec*1000; /* buf->vb.image_quality = dev->isp_3a_result_pt->image_quality.dwval; */ if (dev->special_active == 1) { list_add_tail(&buf->list, &done->active); need_callback = 1; } else { vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); } /* isp_stat_handle: */ if (dev->is_isp_used && dev->is_bayer_raw) { list_for_each_entry(stat_buf_pt, &isp_stat_bq->locked, queue) { if (stat_buf_pt->isp_stat_buf.buf_status == BUF_LOCKED) { vfe_dbg(3, "find isp stat buf locked!\n"); vfe_dbg(3, "isp_stat_bq->locked = %p\n", &isp_stat_bq->locked); vfe_dbg(3, "isp_stat_bq->locked.next = %p\n", isp_stat_bq->locked.next); vfe_dbg(3, "isp_stat_bq->isp_stat[%d].queue = %p\n", stat_buf_pt->id, &isp_stat_bq->isp_stat[stat_buf_pt->id].queue); vfe_dbg(3, "isp_stat_bq->isp_stat[%d].queue.prev = %p\n", stat_buf_pt->id, isp_stat_bq->isp_stat[stat_buf_pt->id].queue.prev); vfe_dbg(3, "isp_stat_bq->isp_stat[%d].queue.next = %p\n", stat_buf_pt->id, isp_stat_bq->isp_stat[stat_buf_pt->id].queue.next); goto set_next_output_addr; } } for (i = 0; i < MAX_ISP_STAT_BUF; i++) { stat_buf_pt = &isp_stat_bq->isp_stat[i]; if (stat_buf_pt->isp_stat_buf.buf_status == BUF_IDLE) { vfe_dbg(3, "find isp stat buf idle!\n"); list_move_tail(&stat_buf_pt->queue, &isp_stat_bq->active); stat_buf_pt->isp_stat_buf.buf_status = BUF_ACTIVE; } } vfe_dbg(3, "before list empty isp_stat_bq->active = %p\n", &isp_stat_bq->active); vfe_dbg(3, "before list empty isp_stat_bq->active.prev = %p\n", isp_stat_bq->active.prev); vfe_dbg(3, "before list empty isp_stat_bq->active.next = %p\n", isp_stat_bq->active.next); /* judge if the isp stat queue has been written to the last */ if (list_empty(&isp_stat_bq->active)) { vfe_err("No active isp stat queue to serve\n"); goto set_next_output_addr; } vfe_dbg(3, "after list empty isp_stat_bq->active = %p\n", &isp_stat_bq->active); vfe_dbg(3, "after list empty isp_stat_bq->active.prev = %p\n", isp_stat_bq->active.prev); vfe_dbg(3, "after list empty isp_stat_bq->active.next = %p\n", isp_stat_bq->active.next); /* delete the ready buffer from the actvie queue add the ready buffer to the locked queue stat_buf_pt = list_first_entry(&isp_stat_bq->active, struct vfe_isp_stat_buf, queue); */ stat_buf_pt = list_entry(isp_stat_bq->active.next, struct vfe_isp_stat_buf, queue); list_move_tail(&stat_buf_pt->queue, &isp_stat_bq->locked); stat_buf_pt->isp_stat_buf.buf_status = BUF_LOCKED; dev->isp_gen_set_pt->stat.stat_buf_whole = &isp_stat_bq->isp_stat[stat_buf_pt->id].isp_stat_buf; vfe_isp_stat_parse(dev->isp_gen_set_pt); isp_stat_bq->isp_stat[stat_buf_pt->id].isp_stat_buf.frame_number++; if ((&isp_stat_bq->active) == isp_stat_bq->active.next->next) { vfe_warn("No more isp stat free frame on next time\n"); goto set_next_output_addr; } } } set_isp_stat_addr: if (dev->is_isp_used && dev->is_bayer_raw) { /* stat_buf_pt = list_entry(isp_stat_bq->active.next->next, struct vfe_isp_stat_buf, queue); */ stat_buf_pt = list_entry(isp_stat_bq->active.next, struct vfe_isp_stat_buf, queue); bsp_isp_set_statistics_addr((unsigned long)(stat_buf_pt->dma_addr)); } set_next_output_addr: if (list_empty(&dma_q->active) || dma_q->active.next->next == (&dma_q->active)) { vfe_print("No active queue to serve\n"); goto unlock; } buf = list_entry(dma_q->active.next->next, struct vfe_buffer, list); vfe_set_addr(dev, buf, current_ch); unlock: #if defined(CH_OUTPUT_IN_DIFFERENT_VIDEO) bsp_csi_int_clear_status(dev->csi_sel, current_ch, CSI_INT_FRAME_DONE); bsp_csi_int_clear_status(dev->csi_sel, current_ch, CSI_INT_CAPTURE_DONE); if ((dev->capture_mode == V4L2_MODE_VIDEO) || (dev->capture_mode == V4L2_MODE_PREVIEW)) bsp_csi_int_enable(dev->csi_sel, current_ch, CSI_INT_FRAME_DONE); #endif spin_unlock_irqrestore(&dev->slock, flags); if (dev->special_active && need_callback && dev->vfe_buffer_process) dev->vfe_buffer_process(dev->id); if (((dev->capture_mode == V4L2_MODE_VIDEO) || (dev->capture_mode == V4L2_MODE_PREVIEW)) && dev->is_isp_used && bsp_isp_get_irq_status(FINISH_INT_EN)) { /* if(bsp_isp_get_para_ready()) */ vfe_dbg(3, "call tasklet schedule!\n"); bsp_isp_clr_para_ready(); schedule_work(&dev->isp_isr_bh_task); bsp_isp_set_para_ready(); } #if !defined(CH_OUTPUT_IN_DIFFERENT_VIDEO) if (dev->is_isp_used) { bsp_isp_clr_irq_status(FINISH_INT_EN); bsp_isp_irq_enable(FINISH_INT_EN); } else { bsp_csi_int_clear_status(dev->csi_sel, dev->cur_ch, CSI_INT_FRAME_DONE); bsp_csi_int_clear_status(dev->csi_sel, dev->cur_ch, CSI_INT_CAPTURE_DONE); if ((dev->capture_mode == V4L2_MODE_VIDEO) || (dev->capture_mode == V4L2_MODE_PREVIEW)) bsp_csi_int_enable(dev->csi_sel, dev->cur_ch, CSI_INT_FRAME_DONE); } #endif return IRQ_HANDLED; } /* * Videobuf operations */ static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { struct vfe_dev *dev = vb2_get_drv_priv(vq); unsigned int size; int buf_max_flag = 0; vfe_dbg(1, "queue_setup\n"); size = dev->buf_byte_size; if (size == 0) return -EINVAL; if (*nbuffers == 0) *nbuffers = 8; while (size * *nbuffers > MAX_FRAME_MEM) { (*nbuffers)--; buf_max_flag = 1; if (*nbuffers == 0) vfe_err("one buffer size larger than max frame memory! buffer count = %d\n,", *nbuffers); } if (buf_max_flag == 0) { if (dev->capture_mode == V4L2_MODE_IMAGE) { if (*nbuffers != 1) { *nbuffers = 1; vfe_err("buffer count is set to 1 in image capture mode\n"); } } else { if (*nbuffers < 3) { *nbuffers = 3; vfe_err("buffer count is invalid, set to 3 in video capture\n"); } } } *nplanes = 1; sizes[0] = size; alloc_devs[0] = dev->allocate_dev[dev->cur_ch]; vfe_print("%s, buffer count=%d, size=%d\n", __func__, *nbuffers, size); return 0; } static int buffer_prepare(struct vb2_buffer *vb) { struct vfe_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vvb = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); struct vfe_buffer *buf = container_of(vvb, struct vfe_buffer, vb); unsigned long size; vfe_dbg(1, "buffer_prepare\n"); if (dev->width < MIN_WIDTH || dev->width > MAX_WIDTH || dev->height < MIN_HEIGHT || dev->height > MAX_HEIGHT) return -EINVAL; size = dev->buf_byte_size; if (vb2_plane_size(vb, 0) < size) { vfe_err("%s data will not fit into plane (%lu < %lu)\n", __func__, vb2_plane_size(vb, 0), size); return -EINVAL; } vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); vb->planes[0].m.offset = vb2_dma_contig_plane_dma_addr(vb, 0); return 0; } static void buffer_queue(struct vb2_buffer *vb) { struct vfe_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vvb = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); struct vfe_buffer *buf = container_of(vvb, struct vfe_buffer, vb); struct vfe_dmaqueue *vidq = &dev->vidq[dev->cur_ch]; unsigned long flags = 0; vfe_dbg(1, "ch%d buffer_queue\n", dev->cur_ch); spin_lock_irqsave(&dev->slock, flags); list_add_tail(&buf->list, &vidq->active); spin_unlock_irqrestore(&dev->slock, flags); } static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct vfe_dev *dev = vb2_get_drv_priv(vq); vfe_dbg(1, "%s\n", __func__); if (dev->cur_ch == 0) vfe_start_generating(dev); return 0; } /* abort streaming and wait for last buffer */ static void stop_streaming(struct vb2_queue *vq) { struct vfe_dev *dev = vb2_get_drv_priv(vq); struct vfe_dmaqueue *dma_q = &dev->vidq[dev->cur_ch]; unsigned long flags = 0; vfe_dbg(1, "%s\n", __func__); if (dev->cur_ch == 0) vfe_stop_generating(dev); spin_lock_irqsave(&dev->slock, flags); /* Release all active buffers */ while (!list_empty(&dma_q->active)) { struct vfe_buffer *buf; buf = list_entry(dma_q->active.next, struct vfe_buffer, list); list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); vfe_dbg(2, "[%p/%d] done\n", buf, buf->vb.vb2_buf.index); } spin_unlock_irqrestore(&dev->slock, flags); } static void vfe_lock(struct vb2_queue *vq) { struct vfe_dev *dev = vb2_get_drv_priv(vq); mutex_lock(&dev->buf_lock); } static void vfe_unlock(struct vb2_queue *vq) { struct vfe_dev *dev = vb2_get_drv_priv(vq); mutex_unlock(&dev->buf_lock); } static const struct vb2_ops vfe_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, .wait_prepare = vfe_unlock, .wait_finish = vfe_lock, }; /* * IOCTL vidioc handling */ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct vfe_dev *dev = video_drvdata(file); strcpy(cap->driver, "sunxi-vfe"); strcpy(cap->card, "sunxi-vfe"); strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); cap->version = VFE_VERSION; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \ V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS; cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; return 0; } static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct vfe_fmt *fmt; vfe_dbg(0, "vidioc_enum_fmt_vid_cap\n"); if (f->index > ARRAY_SIZE(formats) - 1) return -EINVAL; fmt = &formats[f->index]; strlcpy(f->description, fmt->name, sizeof(f->description)); f->pixelformat = fmt->fourcc; return 0; } static int vidioc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct vfe_dev *dev = video_drvdata(file); struct v4l2_subdev_frame_size_enum fse; int ret; vfe_dbg(0, "vidioc_enum_framesizes\n"); fse.index = fsize->index; if (dev == NULL || dev->sd->ops->pad->enum_frame_size == NULL) return -EINVAL; ret = v4l2_subdev_call(dev->sd, pad, enum_frame_size, NULL, &fse); if (ret < 0) return -1; fsize->stepwise.max_width = fse.max_width; fsize->stepwise.max_height = fse.max_height; fsize->stepwise.min_width = fse.min_width; fsize->stepwise.min_height = fse.min_height; return ret; } static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vfe_dev *dev = video_drvdata(file); f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; f->fmt.pix.field = dev->fmt.field; f->fmt.pix.pixelformat = dev->fmt.bus_pix_code; return 0; } static u32 *try_fmt_from_sensor(struct vfe_dev *dev, char *bus_name, char *pix_name, struct v4l2_subdev_format *fmat) { struct vfe_fmt_code *vfe_formats, *def_fmt = NULL; int ret = 0; u32 *mbus_code; unsigned int i; for (i = 0; i < ARRAY_SIZE(vfe_fmt_code); i++) { vfe_formats = &vfe_fmt_code[i]; if (bus_name == vfe_formats->bus_name && pix_name == vfe_formats->pix_name) { def_fmt = vfe_formats; mbus_code = vfe_formats->mbus_code; for (i = 0; i < def_fmt->size; ++i) { fmat->format.code = mbus_code[i]; ret = v4l2_subdev_call(dev->sd, pad, get_fmt, NULL, fmat); if (ret >= 0) { vfe_dbg(0, "try %s bus ok when pix fmt is %s!\n", bus_name, pix_name); return &fmat->format.code; } } } } vfe_err("try %s bus error when pix fmt is %s!\n", bus_name, pix_name); return NULL; } static u32 *try_fmt_internal(struct vfe_dev *dev, struct v4l2_format *f) { enum pixel_fmt pix_fmt; enum pixel_fmt_type pix_fmt_type; struct v4l2_subdev_format fmat; struct v4l2_mbus_framefmt *ccm_fmt = &fmat.format; u32 *bus_pix_code = NULL; vfe_dbg(0, "try_fmt_internal\n"); /*judge the resolution*/ if (f->fmt.pix.width > MAX_WIDTH || f->fmt.pix.height > MAX_HEIGHT) { vfe_err("size is too large,automatically set to maximum!\n"); f->fmt.pix.width = MAX_WIDTH; f->fmt.pix.height = MAX_HEIGHT; } pix_fmt = pix_fmt_v4l2_to_common(f->fmt.pix.pixelformat); pix_fmt_type = find_pixel_fmt_type(pix_fmt); ccm_fmt->width = f->fmt.pix.width; ccm_fmt->height = f->fmt.pix.height; ccm_fmt->field = f->fmt.pix.field; /* find the expect bus format via frame format list */ if (pix_fmt_type == YUV422_PL || pix_fmt_type == YUV422_SPL || \ pix_fmt_type == YUV420_PL || pix_fmt_type == YUV420_SPL #ifdef CONFIG_ARCH_SUN3IW1P1 || pix_fmt_type == YUV420_MB #endif ) { if (dev->is_isp_used && dev->is_bayer_raw) { bus_pix_code = try_fmt_from_sensor(dev, "bayer", "yuv422/yuv420", &fmat); if (bus_pix_code == NULL) { bus_pix_code = try_fmt_from_sensor(dev, "yuv422", "yuv422/yuv420", &fmat); if (bus_pix_code == NULL) { if (pix_fmt_type == YUV420_PL || pix_fmt_type == YUV420_SPL) { bus_pix_code = try_fmt_from_sensor(dev, "yuv420", "yuv422/yuv420", &fmat); } else { return NULL; } } } } else { bus_pix_code = try_fmt_from_sensor(dev, "yuv422", "yuv422/yuv420", &fmat); if (bus_pix_code == NULL) { if (pix_fmt_type == YUV420_PL || pix_fmt_type == YUV420_SPL) { } else { return NULL; } } } } else if (pix_fmt_type == YUV422_INTLVD) { bus_pix_code = try_fmt_from_sensor(dev, "yuv422", "yuv422/yuv420", &fmat); } else if (pix_fmt_type == BAYER_RGB) { bus_pix_code = try_fmt_from_sensor(dev, "bayer", "yuv422/yuv420", &fmat); } else if (pix_fmt_type == RGB565) { bus_pix_code = try_fmt_from_sensor(dev, "rgb565", "rgb565", &fmat); } else if (pix_fmt_type == RGB888 || pix_fmt_type == PRGB888) { bus_pix_code = try_fmt_from_sensor(dev, "rgb888", "rgb888/prgb888", &fmat); } else { return NULL; } if (bus_pix_code == NULL) return NULL; f->fmt.pix.width = ccm_fmt->width; f->fmt.pix.height = ccm_fmt->height; vfe_dbg(0, "bus pixel code = %x at %s\n", *bus_pix_code, __func__); vfe_dbg(0, "pix->width = %d at %s\n", f->fmt.pix.width, __func__); vfe_dbg(0, "pix->height = %d at %s\n", f->fmt.pix.height, __func__); return bus_pix_code; } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vfe_dev *dev = video_drvdata(file); u32 *bus_pix_code; vfe_dbg(0, "vidioc_try_fmt_vid_cap\n"); bus_pix_code = try_fmt_internal(dev, f); if (!bus_pix_code) { vfe_err("pixel format (0x%08x) width %d height %d invalid at %s.\n", \ f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height, __func__); return -EINVAL; } return 0; } static int s_fmt_internal(struct vfe_dev *dev, void *priv, struct v4l2_format *f) { struct v4l2_subdev_format fmat; struct v4l2_mbus_framefmt *ccm_fmt = &fmat.format; struct v4l2_mbus_config mbus_cfg; u32 bus_pix_code; struct sensor_win_size win_cfg; struct main_channel_cfg main_cfg; struct v4l2_subdev_format csi_fmt; struct v4l2_subdev_format mipi_fmt; int ret; vfe_dbg(0, "vidioc_s_fmt_vid_cap\n"); if (vfe_is_generating(dev)) { vfe_err("%s device busy\n", __func__); return -EBUSY; } bus_pix_code = *try_fmt_internal(dev, f); if (!bus_pix_code) { vfe_err("pixel format (0x%08x) width %d height %d invalid at %s.\n", \ f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height, __func__); ret = -EINVAL; goto out; } vfe_dbg(0, "bus pixel code = %x at %s\n", bus_pix_code, __func__); vfe_dbg(0, "pix->width = %d at %s\n", f->fmt.pix.width, __func__); vfe_dbg(0, "pix->height = %d at %s\n", f->fmt.pix.height, __func__); /* get current win configs */ memset(&win_cfg, 0, sizeof(struct sensor_win_size)); ret = v4l2_subdev_call(dev->sd, core, ioctl, GET_CURRENT_WIN_CFG, &win_cfg); ret = v4l2_subdev_call(dev->sd, video, g_mbus_config, &mbus_cfg); if (ret < 0) { vfe_err("v4l2 sub device sensor g_mbus_config error!\n"); goto out; } ret = v4l2_subdev_call(dev->csi_sd, video, s_mbus_config, &mbus_cfg); if (ret < 0) { vfe_err("v4l2 sub device csi s_mbus_config error!\n"); goto out; } if (mbus_cfg.type == V4L2_MBUS_CSI2) { ret = v4l2_subdev_call(dev->mipi_sd, video, s_mbus_config, &mbus_cfg); if (ret < 0) { vfe_err("v4l2 sub device mipi s_mbus_config error!\n"); goto out; } mipi_fmt.reserved[0] = win_cfg.mipi_bps; mipi_fmt.format.code = bus_pix_code; mipi_fmt.format.field = f->fmt.pix.field; ret = v4l2_subdev_call(dev->mipi_sd, pad, set_fmt, NULL, &mipi_fmt); if (ret < 0) { vfe_err("v4l2 sub device mipi set_fmt error!\n"); goto out; } usleep_range(1000, 2000); v4l2_subdev_call(dev->mipi_sd, core, s_power, 0); bsp_mipi_csi_dphy_enable(dev->mipi_sel); v4l2_subdev_call(dev->mipi_sd, core, s_power, 1); usleep_range(10000, 12000); } /* init device */ ccm_fmt->code = bus_pix_code; ccm_fmt->width = f->fmt.pix.width; ccm_fmt->height = f->fmt.pix.height; ccm_fmt->field = f->fmt.pix.field; if (dev->capture_mode == V4L2_MODE_IMAGE) sunxi_flash_check_to_start(dev->flash_sd, SW_CTRL_FLASH_ON); else sunxi_flash_stop(dev->flash_sd); ret = v4l2_subdev_call(dev->sd, pad, set_fmt, NULL, &fmat); if (ret < 0) { vfe_err("v4l2 sub device sensor s_mbus_fmt error!\n"); goto out; } /* prepare the vfe bsp parameter assuming using single channel */ csi_fmt.format = fmat.format; csi_fmt.format.reserved[0] = win_cfg.hoffset; csi_fmt.format.reserved[1] = win_cfg.voffset; csi_fmt.reserved[0] = f->fmt.pix.pixelformat; ret = v4l2_subdev_call(dev->csi_sd, pad, set_fmt, NULL, &csi_fmt); if (ret < 0) { vfe_err("v4l2 sub device csi set_fmt error!\n"); goto out; } dev->fmt.bus_pix_code = bus_pix_code; dev->fmt.field = ccm_fmt->field; if (dev->is_isp_used) { main_cfg.pix = f->fmt.pix; main_cfg.win_cfg = win_cfg; main_cfg.bus_code = find_bus_type((enum bus_pixelcode)(bus_pix_code)); ret = v4l2_subdev_call(dev->isp_sd, core, ioctl, VIDIOC_SUNXI_ISP_MAIN_CH_CFG, &main_cfg); if (ret < 0) vfe_err("vidioc_set_main_channel error! ret = %d\n", ret); dev->isp_gen_set_pt->double_ch_flag = 0; dev->buf_byte_size = main_cfg.pix.sizeimage; vfe_print("dev->buf_byte_size = %d, double_ch_flag = %d\n", dev->buf_byte_size, dev->isp_gen_set_pt->double_ch_flag); } else { v4l2_subdev_call(dev->csi_sd, core, ioctl, VIDIOC_SUNXI_CSI_GET_FRM_SIZE, &dev->buf_byte_size); } dev->thumb_width = 0; dev->thumb_height = 0; dev->width = ccm_fmt->width; dev->height = ccm_fmt->height; dev->mbus_type = mbus_cfg.type; if (dev->is_isp_used == 1) { vfe_dbg(0, "isp_module_init start!\n"); if (dev->is_bayer_raw == 1) { if (0 == win_cfg.width_input || 0 == win_cfg.height_input) { win_cfg.width_input = win_cfg.width; win_cfg.height_input = win_cfg.height; } dev->isp_gen_set_pt->stat.pic_size.width = win_cfg.width_input; dev->isp_gen_set_pt->stat.pic_size.height = win_cfg.height_input; dev->isp_gen_set_pt->stat.hoffset = win_cfg.hoffset; dev->isp_gen_set_pt->stat.voffset = win_cfg.voffset; dev->isp_gen_set_pt->stat.hts = win_cfg.hts; dev->isp_gen_set_pt->stat.vts = win_cfg.vts; dev->isp_gen_set_pt->stat.pclk = win_cfg.pclk; dev->isp_gen_set_pt->stat.fps_fixed = win_cfg.fps_fixed; dev->isp_gen_set_pt->stat.bin_factor = win_cfg.bin_factor; dev->isp_gen_set_pt->stat.intg_min = win_cfg.intg_min; dev->isp_gen_set_pt->stat.intg_max = win_cfg.intg_max; dev->isp_gen_set_pt->stat.gain_min = win_cfg.gain_min; dev->isp_gen_set_pt->stat.gain_max = win_cfg.gain_max; if (dev->capture_mode == V4L2_MODE_IMAGE) dev->isp_gen_set_pt->sensor_mod = CAPTURE_MODE; else if (dev->capture_mode == V4L2_MODE_VIDEO) dev->isp_gen_set_pt->sensor_mod = VIDEO_MODE; else dev->isp_gen_set_pt->sensor_mod = PREVIEW_MODE; isp_module_init(dev->isp_gen_set_pt, dev->isp_3a_result_pt); dev->ctrl_para.prev_exp_line = 0; dev->ctrl_para.prev_ana_gain = 1; if (set_sensor_shutter_and_gain(dev) != 0) { set_sensor_shutter(dev, dev->isp_3a_result_pt->exp_line_num); set_sensor_gain(dev, dev->isp_3a_result_pt->exp_analog_gain); } usleep_range(50000, 60000); } else isp_module_init(dev->isp_gen_set_pt, NULL); vfe_dbg(0, "isp_module_init end!\n"); } ret = 0; out: return ret; } static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); if (dev->cur_ch > 0) return 0; return s_fmt_internal(dev, priv, f); } static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); vfe_dbg(0, "vidioc_reqbufs\n"); return vb2_reqbufs(&dev->vb_vidq[dev->cur_ch], p); } static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); return vb2_querybuf(&dev->vb_vidq[dev->cur_ch], p); } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); return vb2_qbuf(&dev->vb_vidq[dev->cur_ch], p); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); vfe_dbg(2, "vidioc dqbuf\n"); return vb2_dqbuf(&dev->vb_vidq[dev->cur_ch], p, file->f_flags & O_NONBLOCK); } #define CSI_PIN1_REG 0xf1c20890 #define CSI_PIN2_REG 0xf1c20894 static int __vfe_streamon(struct vfe_dev *dev, void *priv, enum v4l2_buf_type i) { struct vfe_dmaqueue *dma_q = &dev->vidq[dev->cur_ch]; struct vfe_isp_stat_buf_queue *isp_stat_bq = &dev->isp_stat_bq; struct vfe_buffer *buf = NULL; struct vfe_isp_stat_buf *stat_buf_pt; int ret = 0, j; #ifdef CONFIG_ARCH_SUN3IW1P1 int val; writel(0x22222222, CSI_PIN1_REG); val = readl(CSI_PIN2_REG) & 0xffff0000; val |= 0x2222; writel(val, CSI_PIN2_REG); #endif mutex_lock(&dev->stream_lock); vfe_dbg(0, "video stream on\n"); if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ret = -EINVAL; goto streamon_unlock; } if (dev->cur_ch == 0) { if (vfe_is_generating(dev)) { vfe_err("stream has been already on\n"); ret = -1; goto streamon_unlock; } bsp_csi_enable(dev->csi_sel); bsp_csi_disable(dev->csi_sel); bsp_csi_enable(dev->csi_sel); } if (dev->is_isp_used) { v4l2_subdev_call(dev->isp_sd, video, s_stream, 1); bsp_isp_enable(); } if (dev->is_isp_used && dev->is_bayer_raw) { /* initial for isp statistic buffer queue */ INIT_LIST_HEAD(&isp_stat_bq->active); INIT_LIST_HEAD(&isp_stat_bq->locked); for (j = 0; j < MAX_ISP_STAT_BUF; j++) { isp_stat_bq->isp_stat[j].isp_stat_buf.buf_status = BUF_ACTIVE; list_add_tail(&isp_stat_bq->isp_stat[j].queue, &isp_stat_bq->active); } } if (dev->special_active == 1) { dma_q = &dev->vidq_special; vfe_start_generating(dev); } else { ret = vb2_streamon(&dev->vb_vidq[dev->cur_ch], i); if (ret) goto streamon_unlock; } if (!list_empty(&dma_q->active)) { buf = list_entry(dma_q->active.next, struct vfe_buffer, list); } else { vfe_err("stream on, but no buffer now.\n"); goto streamon_unlock; } vfe_set_addr(dev, buf, dev->cur_ch); #if defined(CH_OUTPUT_IN_DIFFERENT_VIDEO) if ((dev->cur_ch == 0) && (dev->id == 0)) { int ch = 0; dma_addr_t dma_addr; for (ch = 1; ch < MAX_CH_NUM; ch++) { dev->buf_ext[ch].size = dev->buf_byte_size; ret = os_mem_alloc(&dev->pdev->dev, &dev->buf_ext[ch]); if (ret) goto streamon_unlock; dma_addr = (dma_addr_t)dev->buf_ext[ch].dma_addr; bsp_csi_set_ch_addr(dev->csi_sel, ch, dma_addr); } } #endif if (dev->is_isp_used && dev->is_bayer_raw) { stat_buf_pt = list_entry(isp_stat_bq->active.next, struct vfe_isp_stat_buf, queue); if (stat_buf_pt == NULL) { vfe_err("stat_buf_pt =null"); } else{ bsp_isp_set_statistics_addr((unsigned long)(stat_buf_pt->dma_addr)); } } if (dev->is_isp_used) { bsp_isp_set_para_ready(); bsp_isp_clr_irq_status(ISP_IRQ_EN_ALL); bsp_isp_irq_enable(FINISH_INT_EN | SRC0_FIFO_INT_EN); if (dev->is_isp_used && dev->is_bayer_raw) bsp_csi_int_enable(dev->csi_sel, dev->cur_ch, CSI_INT_VSYNC_TRIG); } else { bsp_csi_int_clear_status(dev->csi_sel, dev->cur_ch, CSI_INT_ALL); bsp_csi_int_enable(dev->csi_sel, dev->cur_ch, CSI_INT_CAPTURE_DONE | \ CSI_INT_FRAME_DONE | \ CSI_INT_BUF_0_OVERFLOW | \ CSI_INT_BUF_1_OVERFLOW | \ CSI_INT_BUF_2_OVERFLOW | \ CSI_INT_HBLANK_OVERFLOW); } #if defined(CONFIG_ARCH_SUN8IW8P1) if (dev->mbus_type == V4L2_MBUS_CSI2) bsp_mipi_csi_protocol_enable(dev->mipi_sel); usleep_range(10000, 11000); if (dev->capture_mode == V4L2_MODE_IMAGE) { if (dev->is_isp_used) bsp_isp_image_capture_start(); } else { if (dev->is_isp_used) bsp_isp_video_capture_start(); } v4l2_subdev_call(dev->csi_sd, video, s_stream, 1); #else if (dev->capture_mode == V4L2_MODE_IMAGE) { if (dev->is_isp_used) bsp_isp_image_capture_start(); } else { if (dev->is_isp_used) bsp_isp_video_capture_start(); } if (dev->cur_ch == 0) v4l2_subdev_call(dev->csi_sd, video, s_stream, 1); if (dev->mbus_type == V4L2_MBUS_CSI2) bsp_mipi_csi_protocol_enable(dev->mipi_sel); #endif streamon_unlock: mutex_unlock(&dev->stream_lock); return ret; } static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); return __vfe_streamon(dev, priv, i); } static int __vfe_streamoff(struct vfe_dev *dev, void *priv, enum v4l2_buf_type i) { struct vfe_dmaqueue *dma_q = &dev->vidq[dev->cur_ch]; struct vfe_dmaqueue *donelist = NULL; struct vfe_buffer *buffer = NULL; unsigned long flags = 0; int ret = 0; #ifdef CONFIG_ARCH_SUN3IW1P1 u32 val = 0; #endif mutex_lock(&dev->stream_lock); vfe_dbg(0, "video stream off\n"); if (dev->cur_ch == 0) { if (!vfe_is_generating(dev)) { vfe_err("stream has been already off\n"); ret = 0; goto streamoff_unlock; } isp_streamoff_torch_and_flash_close(dev); } if (dev->is_isp_used) { vfe_dbg(0, "disable isp int in streamoff\n"); bsp_isp_irq_disable(ISP_IRQ_EN_ALL); bsp_isp_clr_irq_status(ISP_IRQ_EN_ALL); } else { vfe_dbg(0, "disable csi int in streamoff\n"); bsp_csi_int_disable(dev->csi_sel, dev->cur_ch, CSI_INT_ALL); bsp_csi_int_clear_status(dev->csi_sel, dev->cur_ch, CSI_INT_ALL); } if (dev->cur_ch == 0) v4l2_subdev_call(dev->csi_sd, video, s_stream, 0); if (dev->capture_mode == V4L2_MODE_IMAGE) { if (dev->is_isp_used) bsp_isp_image_capture_stop(); vfe_dbg(0, "dev->capture_mode = %d\n", dev->capture_mode); } else { if (dev->is_isp_used) bsp_isp_video_capture_stop(); vfe_dbg(0, "dev->capture_mode = %d\n", dev->capture_mode); } if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ret = -EINVAL; goto streamoff_unlock; } if (dev->mbus_type == V4L2_MBUS_CSI2) bsp_mipi_csi_protocol_disable(dev->mipi_sel); if (dev->special_active == 1) { dma_q = &dev->vidq_special; donelist = &dev->done_special; vfe_stop_generating(dev); spin_lock_irqsave(&dev->slock, flags); while (!list_empty(&dma_q->active)) { buffer = list_first_entry(&dma_q->active, struct vfe_buffer, list); list_del(&buffer->list); list_add(&buffer->list, &donelist->active); } spin_unlock_irqrestore(&dev->slock, flags); } else { ret = vb2_streamoff(&dev->vb_vidq[dev->cur_ch], i); if (ret != 0) { vfe_err("videobu_streamoff error!\n"); goto streamoff_unlock; } } #if defined(CH_OUTPUT_IN_DIFFERENT_VIDEO) if ((dev->cur_ch == 0) && (dev->id == 0)) { int ch = 0; for (ch = 1; ch < MAX_CH_NUM; ch++) os_mem_free(&dev->pdev->dev, &dev->buf_ext[ch]); } #endif if (dev->is_isp_used) bsp_isp_disable(); if (dev->cur_ch == 0) bsp_csi_disable(dev->csi_sel); #ifdef CONFIG_ARCH_SUN3IW1P1 writel(0x7777777, CSI_PIN1_REG); val = readl(CSI_PIN2_REG) & 0xffff0000; val |= 0x7777; writel(val, CSI_PIN2_REG); #endif streamoff_unlock: mutex_unlock(&dev->stream_lock); return ret; } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); return __vfe_streamoff(dev, priv, i); } static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { struct vfe_dev *dev = video_drvdata(file); if (inp->index > dev->dev_qty-1) { vfe_err("input index(%d) > dev->dev_qty(%d)-1 invalid!\n", inp->index, dev->dev_qty); return -EINVAL; } if (dev->device_valid_flag[inp->index] == 0) { vfe_err("input index(%d) > dev->dev_qty(%d)-1 invalid!, device_valid_flag[%d] = %d\n", inp->index, dev->dev_qty, inp->index, dev->device_valid_flag[inp->index]); return -EINVAL; } inp->type = V4L2_INPUT_TYPE_CAMERA; return 0; } static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { struct vfe_dev *dev = video_drvdata(file); *i = dev->input; return 0; } static int vfe_ctrl_para_reset(struct vfe_dev *dev) { dev->ctrl_para.gsensor_rot = 0; dev->ctrl_para.prev_exp_line = 16; dev->ctrl_para.prev_ana_gain = 16; dev->ctrl_para.prev_focus_pos = 50; return 0; } static int internal_s_input(struct vfe_dev *dev, unsigned int i) { struct v4l2_control ctrl; struct sensor_item sensor_info; unsigned long core_clk; int ret; if (i > dev->dev_qty-1) { vfe_err("set input i(%d)>dev_qty(%d)-1 error!\n", i, dev->dev_qty); return -EINVAL; } if (i == dev->input) return 0; if (dev->input != -1) { /*Power down current device*/ if (dev->sd_act != NULL) v4l2_subdev_call(dev->sd_act, core, ioctl, ACT_SOFT_PWDN, 0); ret = vfe_set_sensor_power_off(dev); if (ret < 0) goto altend; } vfe_dbg(0, "input_num = %d\n", i); dev->input = i; /* Alternate the device info and select target device*/ update_ccm_info(dev, dev->ccm_cfg[i]); /* set vfe core clk rate for each sensor! */ if (get_sensor_info(dev->ccm_cfg[i]->ccm, &sensor_info) == 0) { core_clk = sensor_info.core_clk_for_sensor; } else { core_clk = CSI_CORE_CLK_RATE; } v4l2_subdev_call(dev->csi_sd, core, ioctl, VIDIOC_SUNXI_CSI_SET_CORE_CLK, &core_clk); /* alternate isp setting */ update_isp_setting(dev); if (dev->is_bayer_raw) isp_param_init(dev->isp_gen_set_pt); if (dev->flash_used == 1) sunxi_flash_info_init(dev->flash_sd); /* Initial target device */ ret = vfe_set_sensor_power_on(dev); #ifdef USE_SPECIFIC_CCI csi_cci_init_helper(dev->cci_sel); #endif if (ret != 0) { vfe_err("sensor standby off error when selecting target device!\n"); goto altend; } ret = v4l2_subdev_call(dev->sd, core, init, 0); if (ret != 0) { vfe_err("sensor initial error when selecting target device!\n"); goto altend; } if (dev->sd_act != NULL) { struct actuator_para_t vcm_para; vcm_para.active_min = dev->isp_gen_set_pt->isp_ini_cfg.isp_3a_settings.vcm_min_code; vcm_para.active_max = dev->isp_gen_set_pt->isp_ini_cfg.isp_3a_settings.vcm_max_code; vfe_dbg(0, "min/max=%d/%d\n", dev->isp_gen_set_pt->isp_ini_cfg.isp_3a_settings.vcm_min_code, dev->isp_gen_set_pt->isp_ini_cfg.isp_3a_settings.vcm_max_code); v4l2_subdev_call(dev->sd_act, core, ioctl, ACT_INIT, &vcm_para); } bsp_csi_disable(dev->csi_sel); if (dev->is_isp_used) { vfe_ctrl_para_reset(dev); bsp_isp_disable(); bsp_isp_enable(); bsp_isp_init(&dev->isp_init_para); /* Set the initial flip */ ctrl.id = V4L2_CID_VFLIP; ctrl.value = dev->ccm_cfg[i]->vflip; ret = v4l2_s_ctrl(NULL, dev->isp_sd->ctrl_handler, &ctrl); if (ret != 0) { vfe_err("isp s_ctrl V4L2_CID_VFLIP error when vidioc_s_input!input_num = %d\n", i); } ctrl.id = V4L2_CID_VFLIP_THUMB; ctrl.value = dev->ccm_cfg[i]->vflip; ret = v4l2_s_ctrl(NULL, dev->isp_sd->ctrl_handler, &ctrl); if (ret != 0) vfe_err("isp s_ctrl V4L2_CID_VFLIP_THUMB error when vidioc_s_input!input_num = %d\n", i); ctrl.id = V4L2_CID_HFLIP; ctrl.value = dev->ccm_cfg[i]->hflip; ret = v4l2_s_ctrl(NULL, dev->isp_sd->ctrl_handler, &ctrl); if (ret != 0) vfe_err("isp s_ctrl V4L2_CID_HFLIP error when vidioc_s_input!input_num = %d\n", i); ctrl.id = V4L2_CID_HFLIP_THUMB; ctrl.value = dev->ccm_cfg[i]->hflip; ret = v4l2_s_ctrl(NULL, dev->isp_sd->ctrl_handler, &ctrl); if (ret != 0) vfe_err("isp s_ctrl V4L2_CID_HFLIP error when vidioc_s_input!input_num = %d\n", i); } else { /* bsp_isp_exit(); */ /* Set the initial flip */ ctrl.id = V4L2_CID_VFLIP; ctrl.value = dev->ccm_cfg[i]->vflip; ret = v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &ctrl); if (ret != 0) vfe_err("sensor sensor_s_ctrl V4L2_CID_VFLIP error when vidioc_s_input!input_num = %d\n", i); ctrl.id = V4L2_CID_HFLIP; ctrl.value = dev->ccm_cfg[i]->hflip; ret = v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &ctrl); if (ret != 0) vfe_err("sensor sensor_s_ctrl V4L2_CID_HFLIP error when vidioc_s_input!input_num = %d\n", i); } ret = 0; altend: dev->vfe_s_input_flag = 1; return ret; } static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); if (dev->cur_ch > 0) return 0; vfe_dbg(0, "%s ,input_num = %d\n", __func__, i); return internal_s_input(dev, i); } struct vfe_command { char name[32]; int v4l2_item; int isp_item; }; static struct vfe_command vfe_power_line_frequency[] = { {"frequency disabled", V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, FREQUENCY_DISABLED,}, {"frequency 50hz", V4L2_CID_POWER_LINE_FREQUENCY_50HZ, FREQUENCY_50HZ,}, {"frequency 60hz", V4L2_CID_POWER_LINE_FREQUENCY_60HZ, FREQUENCY_60HZ,}, {"frequency auto", V4L2_CID_POWER_LINE_FREQUENCY_AUTO, FREQUENCY_AUTO,}, }; static struct vfe_command vfe_colorfx[] = { {"NONE ", V4L2_COLORFX_NONE, COLORFX_NONE,}, {"BW ", V4L2_COLORFX_BW, COLORFX_BW,}, {"SEPIA ", V4L2_COLORFX_SEPIA, COLORFX_SEPIA,}, {"NEGATIVE ", V4L2_COLORFX_NEGATIVE, COLORFX_NEGATIVE,}, {"EMBOSS ", V4L2_COLORFX_EMBOSS, COLORFX_EMBOSS,}, {"SKETCH ", V4L2_COLORFX_SKETCH, COLORFX_SKETCH,}, {"SKY_BLUE ", V4L2_COLORFX_SKY_BLUE, COLORFX_SKY_BLUE,}, {"GRASS_GREEN ", V4L2_COLORFX_GRASS_GREEN, COLORFX_GRASS_GREEN,}, {"SKIN_WHITEN ", V4L2_COLORFX_SKIN_WHITEN, COLORFX_SKIN_WHITEN,}, {"VIVID ", V4L2_COLORFX_VIVID, COLORFX_VIVID,}, {"AQUA ", V4L2_COLORFX_AQUA, COLORFX_AQUA,}, {"ART_FREEZE ", V4L2_COLORFX_ART_FREEZE, COLORFX_ART_FREEZE,}, {"SILHOUETTE ", V4L2_COLORFX_SILHOUETTE, COLORFX_SILHOUETTE,}, {"SOLARIZATION", V4L2_COLORFX_SOLARIZATION, COLORFX_SOLARIZATION,}, {"ANTIQUE ", V4L2_COLORFX_ANTIQUE, COLORFX_ANTIQUE,}, {"SET_CBCR ", V4L2_COLORFX_SET_CBCR, COLORFX_SET_CBCR,}, }; static struct vfe_command vfe_ae_mode[] = { {"EXPOSURE_AUTO", V4L2_EXPOSURE_AUTO, EXP_AUTO,}, {"EXPOSURE_MANUAL", V4L2_EXPOSURE_MANUAL, EXP_MANUAL,}, {"EXPOSURE_SHUTTER_PRIORITY", V4L2_EXPOSURE_SHUTTER_PRIORITY, EXP_AUTO,}, {"EXPOSURE_APERTURE_PRIORITY", V4L2_EXPOSURE_APERTURE_PRIORITY, EXP_AUTO,}, }; static struct vfe_command vfe_wb[] = { {"WB_MANUAL ", V4L2_WHITE_BALANCE_MANUAL, WB_MANUAL,}, {"WB_AUTO ", V4L2_WHITE_BALANCE_AUTO, WB_AUTO,}, {"WB_INCANDESCENT ", V4L2_WHITE_BALANCE_INCANDESCENT, WB_INCANDESCENT,}, {"WB_FLUORESCENT ", V4L2_WHITE_BALANCE_FLUORESCENT, WB_FLUORESCENT,}, {"WB_FLUORESCENT_H", V4L2_WHITE_BALANCE_FLUORESCENT_H, WB_FLUORESCENT_H,}, {"WB_HORIZON ", V4L2_WHITE_BALANCE_HORIZON, WB_HORIZON,}, {"WB_DAYLIGHT ", V4L2_WHITE_BALANCE_DAYLIGHT, WB_DAYLIGHT,}, {"WB_FLASH ", V4L2_WHITE_BALANCE_FLASH, WB_FLASH,}, {"WB_CLOUDY ", V4L2_WHITE_BALANCE_CLOUDY, WB_CLOUDY,}, {"WB_SHADE ", V4L2_WHITE_BALANCE_SHADE, WB_SHADE,}, }; static struct vfe_command vfe_iso[] = { {"ISO_SENSITIVITY_MANUAL", V4L2_ISO_SENSITIVITY_MANUAL, ISO_MANUAL,}, {"ISO_SENSITIVITY_AUTO", V4L2_ISO_SENSITIVITY_AUTO, ISO_AUTO,}, }; static struct vfe_command vfe_scene[] = { {"SCENE_MODE_NONE ", V4L2_SCENE_MODE_NONE, SCENE_MODE_NONE}, {"SCENE_MODE_BACKLIGHT ", V4L2_SCENE_MODE_BACKLIGHT, SCENE_MODE_BACKLIGHT,}, {"SCENE_MODE_BEACH_SNOW ", V4L2_SCENE_MODE_BEACH_SNOW, SCENE_MODE_BEACH_SNOW,}, {"SCENE_MODE_CANDLE_LIGHT", V4L2_SCENE_MODE_CANDLE_LIGHT, SCENE_MODE_CANDLE_LIGHT,}, {"SCENE_MODE_DAWN_DUSK ", V4L2_SCENE_MODE_DAWN_DUSK, SCENE_MODE_DAWN_DUSK,}, {"SCENE_MODE_FALL_COLORS ", V4L2_SCENE_MODE_FALL_COLORS, SCENE_MODE_FALL_COLORS,}, {"SCENE_MODE_FIREWORKS ", V4L2_SCENE_MODE_FIREWORKS, SCENE_MODE_FIREWORKS,}, {"SCENE_MODE_LANDSCAPE ", V4L2_SCENE_MODE_LANDSCAPE, SCENE_MODE_LANDSCAPE,}, {"SCENE_MODE_NIGHT ", V4L2_SCENE_MODE_NIGHT, SCENE_MODE_NIGHT,}, {"SCENE_MODE_PARTY_INDOOR", V4L2_SCENE_MODE_PARTY_INDOOR, SCENE_MODE_PARTY_INDOOR,}, {"SCENE_MODE_PORTRAIT ", V4L2_SCENE_MODE_PORTRAIT, SCENE_MODE_PORTRAIT,}, {"SCENE_MODE_SPORTS ", V4L2_SCENE_MODE_SPORTS, SCENE_MODE_SPORTS,}, {"SCENE_MODE_SUNSET ", V4L2_SCENE_MODE_SUNSET, SCENE_MODE_SUNSET,}, {"SCENE_MODE_TEXT ", V4L2_SCENE_MODE_TEXT, SCENE_MODE_TEXT,}, }; static struct vfe_command vfe_af_range[] = { {"AF_RANGE_AUTO", V4L2_AUTO_FOCUS_RANGE_AUTO, AF_RANGE_AUTO,}, {"AF_RANGE_NORMAL", V4L2_AUTO_FOCUS_RANGE_NORMAL, AF_RANGE_NORMAL,}, {"AF_RANGE_MACRO", V4L2_AUTO_FOCUS_RANGE_MACRO, AF_RANGE_MACRO,}, {"AF_RANGE_INFINITY", V4L2_AUTO_FOCUS_RANGE_INFINITY, AF_RANGE_INFINITY,}, }; static struct vfe_command vfe_flash_mode[] = { {"FLASH_LED_MODE_NONE ", V4L2_FLASH_LED_MODE_NONE, FLASH_MODE_OFF,}, {"FLASH_LED_MODE_FLASH ", V4L2_FLASH_LED_MODE_FLASH, FLASH_MODE_ON,}, {"FLASH_LED_MODE_TORCH ", V4L2_FLASH_LED_MODE_TORCH, FLASH_MODE_TORCH,}, {"FLASH_LED_MODE_AUTO ", V4L2_FLASH_LED_MODE_AUTO, FLASH_MODE_AUTO,}, {"FLASH_LED_MODE_RED_EYE", V4L2_FLASH_LED_MODE_RED_EYE, FLASH_MODE_RED_EYE,}, }; static struct vfe_command vfe_focus_status[] = { {"V4L2_AUTO_FOCUS_STATUS_IDLE ", V4L2_AUTO_FOCUS_STATUS_IDLE, AUTO_FOCUS_STATUS_IDLE, }, {"V4L2_AUTO_FOCUS_STATUS_BUSY ", V4L2_AUTO_FOCUS_STATUS_BUSY, AUTO_FOCUS_STATUS_BUSY, }, {"V4L2_AUTO_FOCUS_STATUS_REACHED ", V4L2_AUTO_FOCUS_STATUS_REACHED, AUTO_FOCUS_STATUS_REACHED,}, {"V4L2_AUTO_FOCUS_STATUS_BUSY ", V4L2_AUTO_FOCUS_STATUS_BUSY, V4L2_AUTO_FOCUS_STATUS_BUSY,}, {"V4L2_AUTO_FOCUS_STATUS_BUSY", V4L2_AUTO_FOCUS_STATUS_BUSY, AUTO_FOCUS_STATUS_REFOCUS,}, {"V4L2_AUTO_FOCUS_STATUS_BUSY ", V4L2_AUTO_FOCUS_STATUS_BUSY, AUTO_FOCUS_STATUS_FINDED,}, {"V4L2_AUTO_FOCUS_STATUS_FAILED", V4L2_AUTO_FOCUS_STATUS_FAILED, AUTO_FOCUS_STATUS_FAILED,}, }; enum vfe_command_tpye { VFE_POWER_LINE_FREQUENCY, VFE_COLORFX, VFE_AE_MODE, VFE_WB, VFE_ISO, VFE_SCENE, VFE_AF_RANGE, VFE_FLASH_MODE, VFE_FOCUS_STATUS, VFE_COMMAND_MAX, }; struct vfe_command_adapter { struct vfe_command *cmd_pt; int size; }; struct vfe_command_adapter vfe_cmd_adapter[] = { {&vfe_power_line_frequency[0], ARRAY_SIZE(vfe_power_line_frequency)}, {&vfe_colorfx[0], ARRAY_SIZE(vfe_colorfx)}, {&vfe_ae_mode[0], ARRAY_SIZE(vfe_ae_mode)}, {&vfe_wb[0], ARRAY_SIZE(vfe_wb)}, {&vfe_iso[0], ARRAY_SIZE(vfe_iso)}, {&vfe_scene[0], ARRAY_SIZE(vfe_scene)}, {&vfe_af_range[0], ARRAY_SIZE(vfe_af_range)}, {&vfe_flash_mode[0], ARRAY_SIZE(vfe_flash_mode)}, {&vfe_focus_status[0], ARRAY_SIZE(vfe_focus_status)}, }; enum { V4L2_TO_ISP, ISP_TO_V4L2, }; int vfe_v4l2_isp(int type, int cmd, int flag) { struct vfe_command_adapter cmd_adapter; int i; if (type >= ARRAY_SIZE(vfe_cmd_adapter)) vfe_err("vfe command tpye ERR, type = %d\n", type); cmd_adapter = vfe_cmd_adapter[type]; if (flag == V4L2_TO_ISP) { for (i = 0; i < cmd_adapter.size; i++) { if (cmd == cmd_adapter.cmd_pt[i].v4l2_item) { vfe_dbg(0, "vfe set %s, cmd = %d\n", cmd_adapter.cmd_pt[i].name, cmd); return cmd_adapter.cmd_pt[i].isp_item; } } } else if (flag == ISP_TO_V4L2) { for (i = 0; i < cmd_adapter.size; i++) { if (cmd == cmd_adapter.cmd_pt[i].isp_item) { vfe_dbg(0, "vfe get %s, cmd = %d\n", cmd_adapter.cmd_pt[i].name, cmd); return cmd_adapter.cmd_pt[i].v4l2_item; } } } vfe_err("command conver ERR, cmd = %d\n", cmd); return 0; } static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *parms) { struct vfe_dev *dev = video_drvdata(file); int ret; ret = v4l2_subdev_call(dev->sd, video, g_parm, parms); if (ret < 0) vfe_warn("v4l2 sub device g_parm fail!\n"); return ret; } static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *parms) { struct vfe_dev *dev = video_drvdata(file); int ret; if (parms->parm.capture.capturemode != V4L2_MODE_VIDEO && \ parms->parm.capture.capturemode != V4L2_MODE_IMAGE && \ parms->parm.capture.capturemode != V4L2_MODE_PREVIEW) { parms->parm.capture.capturemode = V4L2_MODE_PREVIEW; } dev->capture_mode = parms->parm.capture.capturemode; ret = v4l2_subdev_call(dev->sd, video, s_parm, parms); if (ret < 0) vfe_warn("v4l2 sub device s_parm error!\n"); ret = v4l2_subdev_call(dev->csi_sd, video, s_parm, parms); if (ret < 0) vfe_warn("v4l2 sub device s_parm error!\n"); return ret; } int isp_ae_stat_req(struct file *file, struct v4l2_fh *fh, struct isp_stat_buf *ae_buf) { struct vfe_dev *dev = video_drvdata(file); int ret = 0; ae_buf->buf_size = ISP_STAT_AE_MEM_SIZE; ret = copy_to_user(ae_buf->buf, dev->isp_gen_set_pt->stat.ae_buf, ae_buf->buf_size); return ret; } int isp_gamma_req(struct file *file, struct v4l2_fh *fh, struct isp_stat_buf *gamma_buf) { struct vfe_dev *dev = video_drvdata(file); int ret = 0; gamma_buf->buf_size = ISP_GAMMA_MEM_SIZE; ret = copy_to_user(gamma_buf->buf, dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.gamma_tbl_post, gamma_buf->buf_size); return ret; } int isp_hist_stat_req(struct file *file, struct v4l2_fh *fh, struct isp_stat_buf *hist_buf) { struct vfe_dev *dev = video_drvdata(file); int ret = 0; hist_buf->buf_size = ISP_STAT_HIST_MEM_SIZE; ret = copy_to_user(hist_buf->buf, dev->isp_gen_set_pt->stat.hist_buf, hist_buf->buf_size); return ret; } int isp_af_stat_req(struct file *file, struct v4l2_fh *fh, struct isp_stat_buf *af_buf) { struct vfe_dev *dev = video_drvdata(file); int ret = 0; af_buf->buf_size = ISP_STAT_AF_MEM_SIZE; ret = copy_to_user(af_buf->buf, dev->isp_gen_set_pt->stat.af_buf, af_buf->buf_size); return 0; } static int isp_exif_req(struct file *file, struct v4l2_fh *fh, struct isp_exif_attribute *exif_attr) { struct vfe_dev *dev = video_drvdata(file); struct sensor_exif_attribute exif; if (dev->isp_gen_set_pt && dev->is_bayer_raw) { exif_attr->fnumber = dev->isp_gen_set_pt->isp_ini_cfg.isp_3a_settings.fno; if (exif_attr->fnumber < 40) exif_attr->fnumber = 240; exif_attr->focal_length = dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.focus_length; if (dev->isp_gen_set_pt->isp_ini_cfg.isp_test_settings.isp_test_mode == ISP_TEST_ALL_ENABLE) { if (exif_attr->focal_length < 40) exif_attr->focal_length = 400; } else exif_attr->focal_length = dev->isp_3a_result_pt->real_vcm_pos; exif_attr->brightness = dev->isp_gen_set_pt->ae_lum; exif_attr->exposure_bias = dev->isp_gen_set_pt->exp_settings.exp_compensation; exif_attr->flash_fire = dev->isp_gen_set_pt->exp_settings.flash_open; exif_attr->iso_speed = (dev->isp_3a_result_pt->exp_analog_gain * dev->isp_3a_result_pt->exp_digital_gain) * 50 / 4096; exif_attr->exposure_time.numerator = 1; exif_attr->shutter_speed.numerator = 1; if (dev->isp_3a_result_pt->exp_time != 0) { exif_attr->exposure_time.denominator = 1000000/dev->isp_3a_result_pt->exp_time; exif_attr->shutter_speed.denominator = 1000000/dev->isp_3a_result_pt->exp_time; } else { exif_attr->exposure_time.denominator = 10000; exif_attr->shutter_speed.denominator = 10000; } exif_attr->reserved[0] = dev->isp_3a_result_pt->real_vcm_pos; exif_attr->reserved[1] = dev->isp_gen_set_pt->color_temp; } else { if (v4l2_subdev_call(dev->sd, core, ioctl, GET_SENSOR_EXIF, &exif) != 0) { exif_attr->fnumber = 240; exif_attr->focal_length = 180; exif_attr->brightness = 128; exif_attr->exposure_bias = dev->ctrl_para.exp_bias; exif_attr->flash_fire = 0; exif_attr->iso_speed = 200; exif_attr->exposure_time.numerator = 1; exif_attr->exposure_time.denominator = 20; exif_attr->shutter_speed.numerator = 1; exif_attr->shutter_speed.denominator = 24; } else { exif_attr->fnumber = exif.fnumber; exif_attr->focal_length = exif.focal_length; exif_attr->brightness = exif.brightness; exif_attr->exposure_bias = dev->ctrl_para.exp_bias; exif_attr->flash_fire = exif.flash_fire; exif_attr->iso_speed = exif.iso_speed; exif_attr->exposure_time.numerator = exif.exposure_time_num; exif_attr->exposure_time.denominator = exif.exposure_time_den; exif_attr->shutter_speed.numerator = exif.exposure_time_num; exif_attr->shutter_speed.denominator = exif.exposure_time_den; } } return 0; } static int __isp_auto_focus_win(struct vfe_dev *dev, struct v4l2_win_setting *af_win) { int i; struct ccm_config *ccm_curr = &dev->ccm_cfg_content[dev->input]; if (af_win->win_num == 0) { bsp_isp_s_auto_focus_win_num(dev->isp_gen_set_pt, AF_AUTO_WIN, NULL); } else if (af_win->win_num > V4L2_MAX_WIN_NUM) { return -EINVAL; } else { struct v4l2_win_coordinate *win_coor = &af_win->coor[0]; for (i = 0; i < af_win->win_num; i++) { if (ccm_curr->vflip == 1) { dev->ctrl_para.af_coor[i].y1 = -win_coor[0].y1; dev->ctrl_para.af_coor[i].y2 = -win_coor[0].y2; } else { dev->ctrl_para.af_coor[i].y1 = win_coor[i].y1; dev->ctrl_para.af_coor[i].y2 = win_coor[i].y2; } if (ccm_curr->hflip == 1) { dev->ctrl_para.af_coor[i].x1 = -win_coor[0].x1; dev->ctrl_para.af_coor[i].x2 = -win_coor[0].x2; } else { dev->ctrl_para.af_coor[i].x1 = win_coor[0].x1; dev->ctrl_para.af_coor[i].x2 = win_coor[0].x2; } } bsp_isp_s_auto_focus_win_num(dev->isp_gen_set_pt, AF_NUM_WIN, &dev->ctrl_para.af_coor[0]); } return 0; } static int __isp_auto_exp_win(struct vfe_dev *dev, struct v4l2_win_setting *ae_win) { int i; struct ccm_config *ccm_curr = &dev->ccm_cfg_content[dev->input]; if (ae_win->win_num == 0) { bsp_isp_s_auto_exposure_win_num(dev->isp_gen_set_pt, AE_AUTO_WIN, NULL); } else if (ae_win->win_num > V4L2_MAX_WIN_NUM) { return -EINVAL; } else { struct v4l2_win_coordinate *win_coor = &ae_win->coor[0]; for (i = 0; i < ae_win->win_num; i++) { if (ccm_curr->vflip == 1) { dev->ctrl_para.ae_coor[i].y1 = -win_coor[0].y1; dev->ctrl_para.ae_coor[i].y2 = -win_coor[0].y2; } else { dev->ctrl_para.ae_coor[i].y1 = win_coor[i].y1; dev->ctrl_para.ae_coor[i].y2 = win_coor[i].y2; } if (ccm_curr->hflip == 1) { dev->ctrl_para.ae_coor[i].x1 = -win_coor[0].x1; dev->ctrl_para.ae_coor[i].x2 = -win_coor[0].x2; } else { dev->ctrl_para.ae_coor[i].x1 = win_coor[0].x1; dev->ctrl_para.ae_coor[i].x2 = win_coor[0].x2; } } dev->isp_gen_set_pt->win.hist_coor.x1 = win_coor[0].x1; dev->isp_gen_set_pt->win.hist_coor.y1 = win_coor[0].y1; dev->isp_gen_set_pt->win.hist_coor.x2 = win_coor[0].x2; dev->isp_gen_set_pt->win.hist_coor.y2 = win_coor[0].y2; bsp_isp_s_auto_exposure_win_num(dev->isp_gen_set_pt, AE_SINGLE_WIN, &dev->ctrl_para.ae_coor[0]); } return 0; } int vidioc_auto_focus_win(struct file *file, struct v4l2_fh *fh, struct v4l2_win_setting *af_win) { struct vfe_dev *dev = video_drvdata(file); int ret = 0; if (dev->isp_gen_set_pt && dev->is_bayer_raw) ret = __isp_auto_focus_win(dev, af_win); else ret = v4l2_subdev_call(dev->sd, core, ioctl, SET_AUTO_FOCUS_WIN, af_win); return ret; } int vidioc_auto_exposure_win(struct file *file, struct v4l2_fh *fh, struct v4l2_win_setting *exp_win) { struct vfe_dev *dev = video_drvdata(file); int ret = 0; if (dev->isp_gen_set_pt && dev->is_bayer_raw) ret = __isp_auto_exp_win(dev, exp_win); else ret = v4l2_subdev_call(dev->sd, core, ioctl, SET_AUTO_EXPOSURE_WIN, exp_win); return ret; } int vidioc_hdr_ctrl(struct file *file, struct v4l2_fh *fh, struct isp_hdr_ctrl *hdr) { struct vfe_dev *dev = video_drvdata(file); if (dev->isp_gen_set_pt && dev->is_bayer_raw) { if (hdr->flag == HDR_CTRL_SET) { bsp_isp_s_hdr(dev->isp_gen_set_pt, (struct hdr_setting_t *)(&hdr->hdr_t)); dev->isp_3a_result_pt->image_quality.bits.hdr_cnt = 0; } else { hdr->count = dev->isp_gen_set_pt->hdr_setting.frames_count - 1; memcpy(&hdr->hdr_t, &dev->isp_gen_set_pt->hdr_setting, sizeof(struct isp_hdr_setting_t)); } return 0; } return -EINVAL; } int vidioc_set_subchannel(struct file *file, struct v4l2_fh *fh, struct v4l2_pix_format *sub) { int ret = 0; struct vfe_dev *dev = video_drvdata(file); if (!dev->is_isp_used) { vfe_err("isp must be set first when set subchannel\n"); return -1; } ret = v4l2_subdev_call(dev->isp_sd, core, ioctl, VIDIOC_SUNXI_ISP_SUB_CH_CFG, sub); if (ret < 0) { vfe_err("vidioc_set_subchannel error! ret = %d\n", ret); return ret; } dev->buf_byte_size = sub->sizeimage; dev->isp_gen_set_pt->double_ch_flag = 1; dev->thumb_width = sub->width; dev->thumb_height = sub->height; return ret; } int vidioc_set_rotchannel(struct file *file, struct v4l2_fh *fh, struct rot_channel_cfg *rot) { int ret = 0; struct vfe_dev *dev = video_drvdata(file); if (!dev->is_isp_used) { vfe_err("isp must be set first when set rotchannel\n"); return -1; } ret = v4l2_subdev_call(dev->isp_sd, core, ioctl, VIDIOC_SUNXI_ISP_ROT_CH_CFG, rot); if (ret < 0) { vfe_err("vidioc_set_rotchannel error! ret = %d\n", ret); return ret; } dev->buf_byte_size = rot->pix.sizeimage; return ret; } static long vfe_param_handler(struct file *file, void *priv, bool valid_prio, unsigned int cmd, void *param) { int ret = 0; struct v4l2_fh *fh = (struct v4l2_fh *)priv; struct isp_stat_buf *stat = (struct isp_stat_buf *)param; switch (cmd) { case VIDIOC_ISP_AE_STAT_REQ: ret = isp_ae_stat_req(file, fh, stat); break; case VIDIOC_ISP_AF_STAT_REQ: ret = isp_af_stat_req(file, fh, stat); break; case VIDIOC_ISP_HIST_STAT_REQ: ret = isp_hist_stat_req(file, fh, stat); break; case VIDIOC_ISP_EXIF_REQ: ret = isp_exif_req(file, fh, (struct isp_exif_attribute *)param); break; case VIDIOC_ISP_GAMMA_REQ: ret = isp_gamma_req(file, fh, stat); break; case VIDIOC_AUTO_FOCUS_WIN: ret = vidioc_auto_focus_win(file, fh, (struct v4l2_win_setting *)param); break; case VIDIOC_AUTO_EXPOSURE_WIN: ret = vidioc_auto_exposure_win(file, fh, (struct v4l2_win_setting *)param); break; case VIDIOC_HDR_CTRL: ret = vidioc_hdr_ctrl(file, fh, (struct isp_hdr_ctrl *)param); break; case VIDIOC_SET_SUBCHANNEL: ret = vidioc_set_subchannel(file, fh, (struct v4l2_pix_format *)param); break; case VIDIOC_SET_ROTCHANNEL: ret = vidioc_set_rotchannel(file, fh, (struct rot_channel_cfg *)param); break; default: ret = -ENOTTY; } return ret; } static ssize_t vfe_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); int ret = 0; mutex_lock(&dev->buf_lock); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); if (vfe_is_generating(dev)) { ret = vb2_read(&dev->vb_vidq[dev->cur_ch], data, count, ppos, file->f_flags & O_NONBLOCK); } else { vfe_err("csi is not generating!\n"); ret = -EINVAL; } mutex_unlock(&dev->buf_lock); return ret; } static unsigned int vfe_poll(struct file *file, struct poll_table_struct *wait) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); int ret = 0; mutex_lock(&dev->buf_lock); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); if (vfe_is_generating(dev)) { ret = vb2_poll(&dev->vb_vidq[dev->cur_ch], file, wait); } else { vfe_err("csi is not generating!\n"); ret = -EINVAL; } mutex_unlock(&dev->buf_lock); return ret; } void vfe_clk_open(struct vfe_dev *dev) { vfe_print("..........................vfe clk open!.......................\n"); v4l2_subdev_call(dev->csi_sd, core, s_power, 1); v4l2_subdev_call(dev->mipi_sd, core, s_power, 1); } void vfe_clk_close(struct vfe_dev *dev) { vfe_print("..........................vfe clk close!.......................\n"); v4l2_subdev_call(dev->csi_sd, core, s_power, 0); v4l2_subdev_call(dev->mipi_sd, core, s_power, 0); } static void vfe_suspend_trip(struct vfe_dev *dev); static void vfe_resume_trip(struct vfe_dev *dev); static int __vfe_open(struct vfe_dev *dev) { int ret; vfe_print("vfe_open\n"); if (vfe_is_opened(dev)) { vfe_err("device open busy\n"); ret = -EBUSY; goto open_end; } #ifdef CONFIG_DEVFREQ_DRAM_FREQ_WITH_SOFT_NOTIFY dramfreq_master_access(MASTER_CSI, true); #endif vfe_resume_trip(dev); #ifdef USE_SPECIFIC_CCI csi_cci_init_helper(dev->cci_sel); #endif if (dev->ccm_cfg[0]->is_isp_used || dev->ccm_cfg[1]->is_isp_used) { /* must be after ahb and core clock enable */ ret = v4l2_subdev_call(dev->isp_sd, core, init, 0); if (ret < 0) { vfe_err("ISP init error at %s\n", __func__); return ret; } ret = isp_resource_request(dev); if (ret) { vfe_err("isp_resource_request error at %s\n", __func__); return ret; } vfe_dbg(0, "tasklet init !\n"); INIT_WORK(&dev->isp_isr_bh_task, isp_isr_bh_handle); INIT_WORK(&dev->isp_isr_set_sensor_task, isp_isr_set_sensor_handle); } dev->input = -1; vfe_start_opened(dev); vfe_init_isp_log(dev); open_end: if (ret != 0) vfe_print("vfe_open busy\n"); else vfe_print("vfe_open ok\n"); return ret; } static int vfe_open(struct file *file) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); int ret = 0; mutex_lock(&dev->buf_lock); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); dev->first_flag[dev->cur_ch] = 0; if (dev->cur_ch > 0) { mutex_unlock(&dev->buf_lock); return 0; } dev->special_active = 0; ret = __vfe_open(dev); mutex_unlock(&dev->buf_lock); return ret; } static int __vfe_close(struct vfe_dev *dev) { int ret; vfe_print("vfe_close\n"); vfe_stop_generating(dev); if (dev->vfe_s_input_flag == 1) { if (dev->sd_act != NULL) v4l2_subdev_call(dev->sd_act, core, ioctl, ACT_SOFT_PWDN, 0); ret = vfe_set_sensor_power_off(dev); if (ret != 0) vfe_err("sensor power off error at device number when csi close!\n"); dev->vfe_s_input_flag = 0; } else vfe_print("vfe select input flag = %d, s_input have not be used .\n", dev->vfe_s_input_flag); /* hardware */ bsp_csi_int_disable(dev->csi_sel, dev->cur_ch, CSI_INT_ALL); v4l2_subdev_call(dev->csi_sd, video, s_stream, 0); bsp_csi_disable(dev->csi_sel); if (dev->is_isp_used) bsp_isp_disable(); if (dev->mbus_type == V4L2_MBUS_CSI2) { bsp_mipi_csi_protocol_disable(dev->mipi_sel); bsp_mipi_csi_dphy_disable(dev->mipi_sel); bsp_mipi_csi_dphy_exit(dev->mipi_sel); } if (dev->is_isp_used) bsp_isp_exit(); flush_delayed_work(&dev->probe_work); if (dev->ccm_cfg[0]->is_isp_used || dev->ccm_cfg[1]->is_isp_used) { flush_work(&dev->isp_isr_bh_task); flush_work(&dev->isp_isr_set_sensor_task); /* resource */ isp_resource_release(dev); } if (dev->is_bayer_raw) mutex_destroy(&dev->isp_3a_result_mutex); vfe_stop_opened(dev); dev->ctrl_para.prev_exp_line = 0; dev->ctrl_para.prev_ana_gain = 1; vfe_suspend_trip(dev); vfe_print("vfe_close end\n"); vfe_exit_isp_log(dev); #ifdef CONFIG_DEVFREQ_DRAM_FREQ_WITH_SOFT_NOTIFY dramfreq_master_access(MASTER_CSI, false); #endif return 0; } static int vfe_close(struct file *file) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); int ret = 0; mutex_lock(&dev->buf_lock); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); vb2_queue_release(&dev->vb_vidq[dev->cur_ch]); if (dev->cur_ch > 0) { mutex_unlock(&dev->buf_lock); return 0; } ret = __vfe_close(dev); mutex_unlock(&dev->buf_lock); return ret; } static int vfe_mmap(struct file *file, struct vm_area_struct *vma) { struct vfe_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); int ret = 0; mutex_lock(&dev->buf_lock); dev->cur_ch = (vdev->num - VID_N_OFF) < 0 ? 0 : (vdev->num - VID_N_OFF); ret = vb2_mmap(&dev->vb_vidq[dev->cur_ch], vma); mutex_unlock(&dev->buf_lock); return ret; } static int vfe_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { int ret = 0; struct vfe_dev *dev = container_of(ctrl->handler, struct vfe_dev, ctrl_handler); struct v4l2_control c; c.id = ctrl->id; if (dev->is_isp_used && dev->is_bayer_raw) { switch (ctrl->id) { case V4L2_CID_EXPOSURE: v4l2_g_ctrl(dev->sd->ctrl_handler, &c); ctrl->val = c.value; break; case V4L2_CID_GAIN: if (dev->isp_gen_set_pt->isp_ini_cfg.isp_test_settings.isp_test_mode == ISP_TEST_ALL_ENABLE || dev->isp_gen_set_pt->isp_ini_cfg.isp_test_settings.isp_test_mode == ISP_TEST_MANUAL) { ctrl->val = CLIP(CLIP(dev->isp_3a_result_pt->exp_analog_gain, 16, 255) | (CLIP(dev->isp_gen_set_pt->sharp_cfg_to_hal[1], 0, 4095) << V4L2_SHARP_LEVEL_SHIFT) | (CLIP(dev->isp_gen_set_pt->sharp_cfg_to_hal[0], 0, 63) << V4L2_SHARP_MIN_SHIFT) | (CLIP(dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.color_denoise_level, 0, 31) << V4L2_NDF_SHIFT), 0, 0xffffffff); } else { ctrl->val = CLIP(dev->isp_3a_result_pt->exp_analog_gain, 16, 255); } break; case V4L2_CID_HOR_VISUAL_ANGLE: ctrl->val = dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.hor_visual_angle; break; case V4L2_CID_VER_VISUAL_ANGLE: ctrl->val = dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.ver_visual_angle; break; case V4L2_CID_FOCUS_LENGTH: ctrl->val = dev->isp_gen_set_pt->isp_ini_cfg.isp_tunning_settings.focus_length; break; case V4L2_CID_R_GAIN: ctrl->val = dev->isp_gen_set_pt->module_cfg.wb_gain_cfg.wb_gain.r_gain; break; case V4L2_CID_G_GAIN: ctrl->val = dev->isp_gen_set_pt->module_cfg.wb_gain_cfg.wb_gain.gr_gain; break; case V4L2_CID_B_GAIN: ctrl->val = dev->isp_gen_set_pt->module_cfg.wb_gain_cfg.wb_gain.b_gain; break; case V4L2_CID_3A_LOCK: if (dev->isp_gen_set_pt->exp_settings.exposure_lock == ISP_TRUE) ctrl->val |= V4L2_LOCK_EXPOSURE; else ctrl->val &= ~V4L2_LOCK_EXPOSURE; if (dev->isp_gen_set_pt->wb_settings.white_balance_lock == ISP_TRUE) ctrl->val |= V4L2_LOCK_WHITE_BALANCE; else ctrl->val &= ~V4L2_LOCK_WHITE_BALANCE; if (dev->isp_gen_set_pt->af_settings.focus_lock == ISP_TRUE) ctrl->val |= V4L2_LOCK_FOCUS; else ctrl->val &= ~V4L2_LOCK_FOCUS; break; case V4L2_CID_AUTO_FOCUS_STATUS: /* Read-Only */ ctrl->val = vfe_v4l2_isp(VFE_FOCUS_STATUS, dev->isp_3a_result_pt->af_status, ISP_TO_V4L2); break; case V4L2_CID_SENSOR_TYPE: ctrl->val = dev->is_bayer_raw; break; default: return -EINVAL; } vfe_dbg(0, "vfe_g_volatile_ctrl: %s, last value: 0x%x\n", ctrl->name, ctrl->val); return 0; } else { switch (ctrl->id) { case V4L2_CID_SENSOR_TYPE: c.value = dev->is_bayer_raw; break; case V4L2_CID_FLASH_LED_MODE: ret = v4l2_g_ctrl(dev->flash_sd->ctrl_handler, &c); break; case V4L2_CID_AUTO_FOCUS_STATUS: ret = v4l2_g_ctrl(dev->sd->ctrl_handler, &c); if (c.value != V4L2_AUTO_FOCUS_STATUS_BUSY) sunxi_flash_stop(dev->flash_sd); break; default: ret = v4l2_g_ctrl(dev->sd->ctrl_handler, &c); break; } ctrl->val = c.value; if (ret < 0) vfe_warn("v4l2 sub device g_ctrl fail!\n"); } return ret; } static int vfe_s_ctrl(struct v4l2_ctrl *ctrl) { struct vfe_dev *dev = container_of(ctrl->handler, struct vfe_dev, ctrl_handler); int ret = 0; struct actuator_ctrl_word_t vcm_ctrl; struct v4l2_control c; c.id = ctrl->id; c.value = ctrl->val; vfe_dbg(0, "s_ctrl: %s, set value: 0x%x\n", ctrl->name, ctrl->val); if (dev->is_isp_used && dev->is_bayer_raw) { switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: bsp_isp_s_brightness(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_CONTRAST: bsp_isp_s_contrast(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_SATURATION: bsp_isp_s_saturation(dev->isp_gen_set_pt, ctrl->val * 25); break; case V4L2_CID_HUE: bsp_isp_s_hue(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_AUTO_WHITE_BALANCE: if (ctrl->val == 0) bsp_isp_s_auto_white_balance(dev->isp_gen_set_pt, WB_MANUAL); else bsp_isp_s_auto_white_balance(dev->isp_gen_set_pt, WB_AUTO); dev->ctrl_para.auto_wb = ctrl->val; break; case V4L2_CID_EXPOSURE: ret = v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &c); break; case V4L2_CID_AUTOGAIN: if (ctrl->val == 0) bsp_isp_s_exposure(dev->isp_gen_set_pt, ISO_MANUAL); else bsp_isp_s_exposure(dev->isp_gen_set_pt, ISO_AUTO); break; case V4L2_CID_GAIN: ret = v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &c); break; case V4L2_CID_POWER_LINE_FREQUENCY: bsp_isp_s_power_line_frequency(dev->isp_gen_set_pt, vfe_v4l2_isp(VFE_POWER_LINE_FREQUENCY, ctrl->val, V4L2_TO_ISP)); break; case V4L2_CID_HUE_AUTO: bsp_isp_s_hue_auto(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_WHITE_BALANCE_TEMPERATURE: bsp_isp_s_white_balance_temperature(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_SHARPNESS: bsp_isp_s_sharpness(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_CHROMA_AGC: bsp_isp_s_chroma_agc(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_COLORFX: bsp_isp_s_colorfx(dev->isp_gen_set_pt, vfe_v4l2_isp(VFE_COLORFX, ctrl->val, V4L2_TO_ISP)); break; case V4L2_CID_AUTOBRIGHTNESS: bsp_isp_s_auto_brightness(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_BAND_STOP_FILTER: bsp_isp_s_band_stop_filter(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_ILLUMINATORS_1: bsp_isp_s_illuminators_1(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_ILLUMINATORS_2: bsp_isp_s_illuminators_2(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_EXPOSURE_AUTO: bsp_isp_s_exposure_auto(dev->isp_gen_set_pt, vfe_v4l2_isp(VFE_AE_MODE, ctrl->val, V4L2_TO_ISP)); dev->ctrl_para.exp_auto_mode = ctrl->val; break; case V4L2_CID_EXPOSURE_ABSOLUTE: bsp_isp_s_exposure_absolute(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_EXPOSURE_AUTO_PRIORITY: bsp_isp_s_exposure_auto_priority(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_FOCUS_ABSOLUTE: bsp_isp_s_focus_absolute(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_FOCUS_RELATIVE: bsp_isp_s_focus_relative(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_FOCUS_AUTO: dev->isp_3a_result_pt->af_status = AUTO_FOCUS_STATUS_REFOCUS; bsp_isp_s_focus_auto(dev->isp_gen_set_pt, ctrl->val); dev->ctrl_para.auto_focus = ctrl->val; break; case V4L2_CID_AUTO_EXPOSURE_BIAS: bsp_isp_s_auto_exposure_bias(dev->isp_gen_set_pt, ctrl->qmenu_int[ctrl->val]); dev->ctrl_para.exp_bias = ctrl->qmenu_int[ctrl->val]; break; case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: bsp_isp_s_auto_n_preset_white_balance(dev->isp_gen_set_pt, vfe_v4l2_isp(VFE_WB, ctrl->val, V4L2_TO_ISP)); break; case V4L2_CID_WIDE_DYNAMIC_RANGE: bsp_isp_s_wide_dynamic_rage(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_IMAGE_STABILIZATION: bsp_isp_s_image_stabilization(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_ISO_SENSITIVITY: bsp_isp_s_iso_sensitivity(dev->isp_gen_set_pt, ctrl->qmenu_int[ctrl->val]); break; case V4L2_CID_ISO_SENSITIVITY_AUTO: bsp_isp_s_iso_sensitivity_auto(dev->isp_gen_set_pt, vfe_v4l2_isp(VFE_ISO, ctrl->val, V4L2_TO_ISP)); break; case V4L2_CID_EXPOSURE_METERING: ret = -EINVAL; break; case V4L2_CID_SCENE_MODE: bsp_isp_s_scene_mode(dev->isp_gen_set_pt, vfe_v4l2_isp(VFE_SCENE, ctrl->val, V4L2_TO_ISP)); break; case V4L2_CID_3A_LOCK: if (dev->ctrl_para.exp_auto_mode != V4L2_EXPOSURE_MANUAL) { if (IS_FLAG(ctrl->val, V4L2_LOCK_EXPOSURE)) dev->isp_gen_set_pt->exp_settings.exposure_lock = ISP_TRUE; else dev->isp_gen_set_pt->exp_settings.exposure_lock = ISP_FALSE; } if (dev->ctrl_para.auto_wb == 1) { if (IS_FLAG(ctrl->val, V4L2_LOCK_WHITE_BALANCE)) dev->isp_gen_set_pt->wb_settings.white_balance_lock = ISP_TRUE; else dev->isp_gen_set_pt->wb_settings.white_balance_lock = ISP_FALSE; } if (dev->ctrl_para.auto_focus == 1) { if (IS_FLAG(ctrl->val, V4L2_LOCK_FOCUS)) dev->isp_gen_set_pt->af_settings.focus_lock = ISP_TRUE; else dev->isp_gen_set_pt->af_settings.focus_lock = ISP_FALSE; } break; case V4L2_CID_AUTO_FOCUS_START: dev->isp_3a_result_pt->af_status = AUTO_FOCUS_STATUS_REFOCUS; bsp_isp_s_auto_focus_start(dev->isp_gen_set_pt, ctrl->val); isp_s_ctrl_torch_open(dev); break; case V4L2_CID_AUTO_FOCUS_STOP: vfe_dbg(0, "V4L2_CID_AUTO_FOCUS_STOP\n"); bsp_isp_s_auto_focus_stop(dev->isp_gen_set_pt, ctrl->val); isp_s_ctrl_torch_close(dev); break; case V4L2_CID_AUTO_FOCUS_RANGE: bsp_isp_s_auto_focus_range(dev->isp_gen_set_pt, vfe_v4l2_isp(VFE_AF_RANGE, ctrl->val, V4L2_TO_ISP)); break; case V4L2_CID_FLASH_LED_MODE: bsp_isp_s_flash_mode(dev->isp_gen_set_pt, vfe_v4l2_isp(VFE_FLASH_MODE, ctrl->val, V4L2_TO_ISP)); if (ctrl->val == V4L2_FLASH_LED_MODE_TORCH) io_set_flash_ctrl(dev->flash_sd, SW_CTRL_TORCH_ON); else if (ctrl->val == V4L2_FLASH_LED_MODE_NONE) io_set_flash_ctrl(dev->flash_sd, SW_CTRL_FLASH_OFF); break; case V4L2_CID_AUTO_FOCUS_INIT: break; case V4L2_CID_AUTO_FOCUS_RELEASE: break; case V4L2_CID_GSENSOR_ROTATION: bsp_isp_s_gsensor_rotation(dev->isp_gen_set_pt, ctrl->val); dev->ctrl_para.gsensor_rot = ctrl->val; break; case V4L2_CID_TAKE_PICTURE: bsp_isp_s_take_pic(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_R_GAIN: bsp_isp_s_r_gain(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_G_GAIN: bsp_isp_s_g_gain(dev->isp_gen_set_pt, ctrl->val); break; case V4L2_CID_B_GAIN: bsp_isp_s_b_gain(dev->isp_gen_set_pt, ctrl->val); break; default: ret = -EINVAL; break; } if (ret < 0) vfe_warn("v4l2 isp s_ctrl fail!\n"); } else { switch (ctrl->id) { case V4L2_CID_FOCUS_ABSOLUTE: vcm_ctrl.code = ctrl->val; vcm_ctrl.sr = 0x0; ret = v4l2_subdev_call(dev->sd_act, core, ioctl, ACT_SET_CODE, &vcm_ctrl); break; case V4L2_CID_FLASH_LED_MODE: ret = v4l2_s_ctrl(NULL, dev->flash_sd->ctrl_handler, &c); break; case V4L2_CID_AUTO_FOCUS_START: sunxi_flash_check_to_start(dev->flash_sd, SW_CTRL_TORCH_ON); ret = v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &c); break; case V4L2_CID_AUTO_FOCUS_STOP: sunxi_flash_stop(dev->flash_sd); ret = v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &c); break; case V4L2_CID_AUTO_EXPOSURE_BIAS: c.value = ctrl->val; ret = v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &c); break; default: ret = v4l2_s_ctrl(NULL, dev->sd->ctrl_handler, &c); break; } if (ret < 0) vfe_warn("v4l2 sensor s_ctrl fail!\n"); } return ret; } #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; } #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) static long vfe_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) { union { struct isp_stat_buf isb; } karg; void __user *up = compat_ptr(arg); int compatible_arg = 1; long err = 0; 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; } 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; } if (err) return err; if (compatible_arg) err = video_ioctl2(file, cmd, (unsigned long)up); else { mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); if (file->f_op->unlocked_ioctl) err = file->f_op->unlocked_ioctl(file, cmd, (unsigned long)&karg); else err = -ENOIOCTLCMD; 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: if (put_isp_stat_buf32(&karg.isb, up)) err = -EFAULT; break; } return err; } #endif int vfe_open_special(int id) { struct vfe_dev *dev = vfe_dev_gbl[id]; struct vfe_dmaqueue *active = &dev->vidq_special; struct vfe_dmaqueue *done = &dev->done_special; INIT_LIST_HEAD(&active->active); INIT_LIST_HEAD(&done->active); dev->special_active = 1; return __vfe_open(dev); } EXPORT_SYMBOL(vfe_open_special); int vfe_s_input_special(int id, int sel) { struct vfe_dev *dev = vfe_dev_gbl[id]; return internal_s_input(dev, sel); } EXPORT_SYMBOL(vfe_s_input_special); int vfe_close_special(int id) { struct vfe_dev *dev = vfe_dev_gbl[id]; struct vfe_dmaqueue *active = &dev->vidq_special; struct vfe_dmaqueue *done = &dev->done_special; INIT_LIST_HEAD(&active->active); INIT_LIST_HEAD(&done->active); dev->special_active = 0; return __vfe_close(dev); } EXPORT_SYMBOL(vfe_close_special); int vfe_s_fmt_special(int id, struct v4l2_format *f) { struct vfe_dev *dev = vfe_dev_gbl[id]; return s_fmt_internal(dev, NULL, f); } EXPORT_SYMBOL(vfe_s_fmt_special); int vfe_g_fmt_special(int id, struct v4l2_format *f) { struct vfe_dev *dev = vfe_dev_gbl[id]; f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; f->fmt.pix.field = dev->fmt.field; f->fmt.pix.pixelformat = dev->fmt.bus_pix_code; return 0; } EXPORT_SYMBOL(vfe_g_fmt_special); void os_mem_free_sample(int id, struct vfe_mm *mem_man) { struct vfe_dev *dev = vfe_dev_gbl[id]; struct device pdev = dev->pdev->dev; os_mem_free(&pdev, mem_man); } EXPORT_SYMBOL(os_mem_free_sample); int os_mem_alloc_sample(int id, struct vfe_mm *mem_man) { struct vfe_dev *dev = vfe_dev_gbl[id]; struct device pdev = dev->pdev->dev; return os_mem_alloc(&pdev, mem_man); } EXPORT_SYMBOL(os_mem_alloc_sample); int vfe_dqbuffer_special(int id, struct vfe_buffer **buf) { int ret = 0; unsigned long flags = 0; struct vfe_dev *dev = vfe_dev_gbl[id]; struct vfe_dmaqueue *done = &dev->done_special; spin_lock_irqsave(&dev->slock, flags); if (!list_empty(&done->active)) { *buf = list_first_entry(&done->active, struct vfe_buffer, list); list_del(&((*buf)->list)); (*buf)->state = VB2_BUF_STATE_DEQUEUED; } else { vfe_err("there is no done buf, please wait\n"); ret = -1; } spin_unlock_irqrestore(&dev->slock, flags); return ret; } EXPORT_SYMBOL(vfe_dqbuffer_special); int vfe_qbuffer_special(int id, struct vfe_buffer *buf) { struct vfe_dev *dev = vfe_dev_gbl[id]; struct vfe_dmaqueue *vidq = &dev->vidq_special; unsigned long flags = 0; if (buf == NULL) { vfe_err("buf is NULL, cannot qbuf\n"); return -1; } spin_lock_irqsave(&dev->slock, flags); list_add_tail(&buf->list, &vidq->active); buf->state = VB2_BUF_STATE_QUEUED; spin_unlock_irqrestore(&dev->slock, flags); return 0; } EXPORT_SYMBOL(vfe_qbuffer_special); int vfe_streamon_special(int id, enum v4l2_buf_type i) { struct vfe_dev *dev = vfe_dev_gbl[id]; return __vfe_streamon(dev, NULL, i); } EXPORT_SYMBOL(vfe_streamon_special); int vfe_streamoff_special(int id, enum v4l2_buf_type i) { struct vfe_dev *dev = vfe_dev_gbl[id]; return __vfe_streamoff(dev, NULL, i); } EXPORT_SYMBOL(vfe_streamoff_special); void vfe_register_buffer_done_callback(int id, void *func) { struct vfe_dev *dev = vfe_dev_gbl[id]; dev->vfe_buffer_process = func; } EXPORT_SYMBOL(vfe_register_buffer_done_callback); /* ------------------------------------------------------------------ */ /* File operations for the device */ /* ------------------------------------------------------------------*/ static const struct v4l2_ctrl_ops vfe_ctrl_ops = { .g_volatile_ctrl = vfe_g_volatile_ctrl, .s_ctrl = vfe_s_ctrl, }; static const struct v4l2_file_operations vfe_fops = { .owner = THIS_MODULE, .open = vfe_open, .release = vfe_close, .read = vfe_read, .poll = vfe_poll, .unlocked_ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl32 = vfe_compat_ioctl32, #endif .mmap = vfe_mmap, }; static const struct v4l2_ioctl_ops vfe_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, .vidioc_default = vfe_param_handler, }; static struct video_device vfe_template[] = { [0] = { .name = "vfe_0", .fops = &vfe_fops, .ioctl_ops = &vfe_ioctl_ops, .release = video_device_release, }, [1] = { .name = "vfe_1", .fops = &vfe_fops, .ioctl_ops = &vfe_ioctl_ops, .release = video_device_release, }, }; static int vfe_pin_config(struct vfe_dev *dev, int enable) { char pinctrl_names[10] = ""; #ifdef VFE_GPIO if (!IS_ERR_OR_NULL(dev->pctrl)) devm_pinctrl_put(dev->pctrl); if (enable == 1) strcpy(pinctrl_names, "default"); else strcpy(pinctrl_names, "sleep"); dev->pctrl = devm_pinctrl_get_select(&dev->pdev->dev, pinctrl_names); if (IS_ERR_OR_NULL(dev->pctrl)) vfe_err("vip%d request pinctrl handle for device [%s] failed!\n", dev->id, dev_name(&dev->pdev->dev)); return -EINVAL; usleep_range(5000, 6000); #else void __iomem *gpio_base; vfe_print("directly write pin config @ FPGA\n"); gpio_base = ioremap(GPIO_REGS_VBASE, 0x120); if (!gpio_base) { pr_info("gpio_base directly write pin config EIO\n"); return -EIO; } #ifdef FPGA_PIN /* Direct write for pin of FPGA */ writel(0x33333333, (gpio_base+0x90)); writel(0x33333333, (gpio_base+0x94)); writel(0x03333333, (gpio_base+0x98)); #else /* Direct write for pin of IC */ writel(0x22222222, (gpio_base+0x90)); writel(0x10222222, (gpio_base+0x94)); writel(0x11111111, (gpio_base+0x98)); #endif #endif return 0; } static int vfe_pin_release(struct vfe_dev *dev) { #ifdef VFE_GPIO devm_pinctrl_put(dev->pctrl); #endif return 0; } static int vfe_request_gpio(struct vfe_dev *dev) { #ifdef VFE_GPIO unsigned int i, j; for (i = 0; i < dev->dev_qty; i++) { for (j = 0; j < MAX_GPIO_NUM; j++) { os_gpio_request(&dev->ccm_cfg[i]->gpio[j], 1); /* os_gpio_set(&dev->ccm_cfg[i]->gpio[j],1); */ } } #endif return 0; } static int vfe_gpio_config(struct vfe_dev *dev, int bon) { #ifdef VFE_GPIO unsigned int i, j; struct vfe_gpio_cfg gpio_item; for (i = 0; i < dev->dev_qty; i++) { for (j = 0; j < MAX_GPIO_NUM; j++) { memcpy(&gpio_item, &dev->ccm_cfg[i]->gpio[j], sizeof(struct vfe_gpio_cfg)); if (bon == 0) gpio_item.mul_sel = GPIO_DISABLE; os_gpio_set(&gpio_item, 1); } } #endif return 0; } static void vfe_gpio_release(struct vfe_dev *dev) { #ifdef VFE_GPIO unsigned int i, j; for (i = 0; i < dev->dev_qty; i++) { for (j = 0; j < MAX_GPIO_NUM; j++) os_gpio_release(dev->ccm_cfg[i]->gpio[j].gpio, 1); } #endif } static int vfe_resource_request(struct platform_device *pdev, struct vfe_dev *dev) { int ret; struct device_node *np = pdev->dev.of_node; vfe_dbg(0, "get irq resource\n"); /*get irq resource*/ dev->irq = irq_of_parse_and_map(np, 0); if (dev->irq <= 0) { vfe_err("failed to get IRQ resource\n"); return -ENXIO; } #define INTC_EN_IRQ1 (0xf1c20400 + 0x24) #define INTC_PEND_REG1 (0xf1c20400 + 0x14) #define INTC_FF_REG1 (0xf1c20400 + 0x54) #define CSI_BASE_REG 0xf1cb0000 #define CSI_IRQ_EN (0xf1cb0000 + 0x30) #define CSI_IRQ_ST (0xf1cb0000 + 0x34) #define IRQ_EN (1<<0) #ifdef CONFIG_ARCH_SUN3IW1P1 ret = request_irq(dev->irq, vfe_isr, 0, "csi_irq", dev); if (ret) { vfe_err("failed to install irq (%d)\n", ret); return -ENXIO; } writel(CSI_INT_ALL, CSI_IRQ_EN); writel(0xff, CSI_BASE_REG); pr_info("%s: csi irq(%d) enable\n", __func__, dev->irq); #else #ifndef FPGA_VER ret = request_irq(dev->irq, vfe_isr, IRQF_SHARED, pdev->name, dev); #else ret = request_irq(dev->irq, vfe_isr, IRQF_SHARED, pdev->name, dev); #endif if (ret) { vfe_err("failed to install irq (%d)\n", ret); return -ENXIO; } vfe_dbg(0, "clock resource\n"); #endif vfe_dbg(0, "get pin resource\n"); /* request gpio */ vfe_request_gpio(dev); return 0; } static void vfe_resource_release(struct vfe_dev *dev) { vfe_gpio_release(dev); vfe_pin_release(dev); /* vfe_clk_release(dev); */ if (dev->irq > 0) free_irq(dev->irq, dev); } static int vfe_set_sensor_power_on(struct vfe_dev *dev) { int ret = 0; #ifdef _REGULATOR_CHANGE_ vfe_device_regulator_get(dev->ccm_cfg[dev->input]); dev->power = &dev->ccm_cfg[dev->input]->power; #endif #if defined(CONFIG_ARCH_SUN8IW6P1) || defined(CONFIG_ARCH_SUN8IW7P1) #else if (!IS_ERR_OR_NULL(dev->sd)) vfe_set_pmu_channel(dev->sd, IOVDD, ON); usleep_range(10000, 12000); #endif ret = v4l2_subdev_call(dev->sd, core, s_power, CSI_SUBDEV_PWR_ON); dev->vfe_sensor_power_cnt++; vfe_dbg(0, "power_on______________________________\n"); return ret; } static int vfe_set_sensor_power_off(struct vfe_dev *dev) { int ret = 0; if (dev->vfe_sensor_power_cnt > 0) { ret = v4l2_subdev_call(dev->sd, core, s_power, CSI_SUBDEV_PWR_OFF); #if defined(CONFIG_ARCH_SUN8IW6P1) || defined(CONFIG_ARCH_SUN8IW7P1) #else usleep_range(10000, 12000); if (!IS_ERR_OR_NULL(dev->sd)) vfe_set_pmu_channel(dev->sd, IOVDD, OFF); #endif dev->vfe_sensor_power_cnt--; } else { vfe_warn("Sensor is already power off!\n"); dev->vfe_sensor_power_cnt = 0; } #ifdef _REGULATOR_CHANGE_ vfe_device_regulator_put(dev->ccm_cfg[dev->input]); #endif vfe_dbg(0, "power_off______________________________\n"); return ret; } static const char *vfe_regulator_name[] = { VFE_ISP_REGULATOR, VFE_CSI_REGULATOR, }; static int vfe_get_regulator(struct vfe_dev *dev) { struct regulator *regul; unsigned int i; for (i = 0; i < ARRAY_SIZE(vfe_regulator_name); ++i) { if (strcmp(vfe_regulator_name[i], "") != 0) { regul = regulator_get(NULL, vfe_regulator_name[i]); if (IS_ERR_OR_NULL(regul)) { vfe_err("get regulator vfe system power error, i = %d!\n", i); regul = NULL; } } else regul = NULL; dev->vfe_system_power[i] = regul; } return 0; } static int vfe_enable_regulator_all(struct vfe_dev *dev) { unsigned int i, ret = -1; for (i = 0; i < ARRAY_SIZE(vfe_regulator_name); ++i) { if (dev->vfe_system_power[i] != NULL) ret = regulator_enable(dev->vfe_system_power[i]); } usleep_range(5000, 6000); return ret; } static int vfe_disable_regulator_all(struct vfe_dev *dev) { unsigned int i, ret = -1; for (i = 0; i < ARRAY_SIZE(vfe_regulator_name); ++i) { if (dev->vfe_system_power[i] != NULL) ret = regulator_disable(dev->vfe_system_power[i]); } return ret; } static int vfe_put_regulator(struct vfe_dev *dev) { unsigned int i; for (i = 0; i < ARRAY_SIZE(vfe_regulator_name); ++i) { if (dev->vfe_system_power[i] != NULL) regulator_put(dev->vfe_system_power[i]); } return 0; } static int vfe_device_regulator_get(struct ccm_config *ccm_cfg) { #ifdef VFE_PMU /*power issue*/ ccm_cfg->power.iovdd = NULL; ccm_cfg->power.avdd = NULL; ccm_cfg->power.dvdd = NULL; ccm_cfg->power.afvdd = NULL; ccm_cfg->power.flvdd = NULL; if (strcmp(ccm_cfg->iovdd_str, "")) { ccm_cfg->power.iovdd = regulator_get(NULL, ccm_cfg->iovdd_str); if (IS_ERR_OR_NULL(ccm_cfg->power.iovdd)) { vfe_err("get regulator csi_iovdd error!\n"); goto regulator_get_err; } } if (strcmp(ccm_cfg->avdd_str, "")) { ccm_cfg->power.avdd = regulator_get(NULL, ccm_cfg->avdd_str); if (IS_ERR_OR_NULL(ccm_cfg->power.avdd)) { vfe_err("get regulator csi_avdd error!\n"); goto regulator_get_err; } } if (strcmp(ccm_cfg->dvdd_str, "")) { ccm_cfg->power.dvdd = regulator_get(NULL, ccm_cfg->dvdd_str); if (IS_ERR_OR_NULL(ccm_cfg->power.dvdd)) { vfe_err("get regulator csi_dvdd error!\n"); goto regulator_get_err; } } if (strcmp(ccm_cfg->afvdd_str, "")) { ccm_cfg->power.afvdd = regulator_get(NULL, ccm_cfg->afvdd_str); if (IS_ERR_OR_NULL(ccm_cfg->power.afvdd)) { vfe_err("get regulator csi_afvdd error!\n"); goto regulator_get_err; } } if (strcmp(ccm_cfg->flvdd_str, "")) { ccm_cfg->power.flvdd = regulator_get(NULL, ccm_cfg->flvdd_str); if (IS_ERR_OR_NULL(ccm_cfg->power.flvdd)) { vfe_err("get regulator csi_flvdd error!\n"); goto regulator_get_err; } } return 0; regulator_get_err: return -1; #else return 0; #endif } static int vfe_device_regulator_put(struct ccm_config *ccm_cfg) { /*power issue*/ if (!IS_ERR_OR_NULL(ccm_cfg->power.iovdd)) regulator_put(ccm_cfg->power.iovdd); if (!IS_ERR_OR_NULL(ccm_cfg->power.avdd)) regulator_put(ccm_cfg->power.avdd); if (!IS_ERR_OR_NULL(ccm_cfg->power.dvdd)) regulator_put(ccm_cfg->power.dvdd); if (!IS_ERR_OR_NULL(ccm_cfg->power.afvdd)) regulator_put(ccm_cfg->power.afvdd); if (!IS_ERR_OR_NULL(ccm_cfg->power.flvdd)) regulator_put(ccm_cfg->power.flvdd); return 0; } static int vfe_sensor_check(struct vfe_dev *dev) { int ret = 0; struct v4l2_subdev *sd = dev->sd; vfe_print("Check sensor!\n"); vfe_set_sensor_power_on(dev); #ifdef USE_SPECIFIC_CCI csi_cci_init_helper(dev->cci_sel); #endif ret = (v4l2_subdev_call(sd, core, init, 0) < 0) ? -1 : 0; vfe_set_sensor_power_off(dev); if (vfe_i2c_dbg == 1) { vfe_print("NOTE: Sensor i2c dbg, it's always power on and register success!..................\n"); ret = 0; vfe_set_sensor_power_on(dev); } #ifdef USE_SPECIFIC_CCI csi_cci_exit_helper(dev->cci_sel); #endif return ret; } #ifdef USE_SPECIFIC_CCI static int vfe_sensor_subdev_register_check(struct vfe_dev *dev, struct v4l2_device *v4l2_dev, struct ccm_config *ccm_cfg, struct i2c_board_info *sensor_i2c_board) { int ret; ccm_cfg->sd = NULL; ccm_cfg->sd = cci_bus_match(ccm_cfg->ccm, dev->cci_sel, sensor_i2c_board->addr);/* ccm_cfg->i2c_addr >> 1);*/ if (ccm_cfg->sd) { ret = v4l2_device_register_subdev(&dev->v4l2_dev, ccm_cfg->sd); vfe_print("v4l2_device_register_subdev return %d\n", ret); if (ret < 0) ccm_cfg->sd = NULL; } update_ccm_info(dev, ccm_cfg); if (IS_ERR_OR_NULL(ccm_cfg->sd)) { vfe_err("Error registering v4l2 subdevice No such device!\n"); return -ENODEV; } else vfe_print("registered sensor subdev is OK!\n"); /* Subdev register is OK, check sensor init! */ return vfe_sensor_check(dev); } static int vfe_sensor_subdev_unregister(struct v4l2_device *v4l2_dev, struct ccm_config *ccm_cfg, struct i2c_board_info *sensor_i2c_board) { struct cci_driver *cci_driv = v4l2_get_subdevdata(ccm_cfg->sd); if (IS_ERR_OR_NULL(cci_driv)) return -ENODEV; vfe_print("vfe sensor subdev unregister!\n"); v4l2_device_unregister_subdev(ccm_cfg->sd); cci_bus_match_cancel(cci_driv); return 0; } static int vfe_actuator_subdev_register(struct vfe_dev *dev, struct ccm_config *ccm_cfg, struct i2c_board_info *act_i2c_board) { ccm_cfg->sd_act = NULL; ccm_cfg->sd_act = cci_bus_match(ccm_cfg->act_name, dev->cci_sel, act_i2c_board->addr);/* ccm_cfg->i2c_addr >> 1); */ /* reg_sd_act: */ if (!ccm_cfg->sd_act) { vfe_err("Error registering v4l2 act subdevice!\n"); return -EINVAL; } else vfe_print("registered actuator device succeed! act_name is %s\n", ccm_cfg->act_name); ccm_cfg->act_ctrl = (struct actuator_ctrl_t *)container_of(ccm_cfg->sd_act, struct actuator_ctrl_t, sdev); /* pr_info("ccm_cfg->act_ctrl=%x\n",(unsigned int )ccm_cfg->act_ctrl); */ return 0; } #else /* NOT defind USE_SPECIFIC_CCI */ static int vfe_sensor_subdev_register_check(struct vfe_dev *dev, struct v4l2_device *v4l2_dev, struct ccm_config *ccm_cfg, struct i2c_board_info *sensor_i2c_board) { struct i2c_adapter *i2c_adap = i2c_get_adapter(ccm_cfg->twi_id); if (i2c_adap == NULL) { vfe_err("request i2c adapter failed!\n"); return -EFAULT; } ccm_cfg->sd = v4l2_i2c_new_subdev_board(v4l2_dev, i2c_adap, sensor_i2c_board, NULL); if (IS_ERR_OR_NULL(ccm_cfg->sd)) { i2c_put_adapter(i2c_adap); vfe_err("Error registering v4l2 subdevice No such device!\n"); return -ENODEV; } else vfe_print("registered sensor subdev is OK!\n"); update_ccm_info(dev, ccm_cfg); /* Subdev register is OK, check sensor init! */ return vfe_sensor_check(dev); } static int vfe_sensor_subdev_unregister(struct v4l2_device *v4l2_dev, struct ccm_config *ccm_cfg, struct i2c_board_info *sensor_i2c_board) { struct i2c_client *client = v4l2_get_subdevdata(ccm_cfg->sd); struct i2c_adapter *adapter; if (!client) return -ENODEV; vfe_print("vfe sensor subdev unregister!\n"); v4l2_device_unregister_subdev(ccm_cfg->sd); adapter = client->adapter; i2c_unregister_device(client); if (adapter) i2c_put_adapter(adapter); return 0; } static int vfe_actuator_subdev_register(struct vfe_dev *dev, struct ccm_config *ccm_cfg, struct i2c_board_info *act_i2c_board) { struct v4l2_device *v4l2_dev = &dev->v4l2_dev; struct i2c_adapter *i2c_adap_act = i2c_get_adapter(ccm_cfg->twi_id);/* must use the same twi_channel with sensor */ if (i2c_adap_act == NULL) { vfe_err("request act i2c adapter failed\n"); return -EINVAL; } ccm_cfg->sd_act = NULL; act_i2c_board->addr = (unsigned short)(ccm_cfg->act_slave>>1); strcpy(act_i2c_board->type, ccm_cfg->act_name); ccm_cfg->sd_act = v4l2_i2c_new_subdev_board(v4l2_dev, i2c_adap_act, act_i2c_board, NULL); /* reg_sd_act: */ if (!ccm_cfg->sd_act) { vfe_err("Error registering v4l2 act subdevice!\n"); return -EINVAL; } else vfe_print("registered actuator device succeed!\n"); ccm_cfg->act_ctrl = (struct actuator_ctrl_t *)container_of(ccm_cfg->sd_act, struct actuator_ctrl_t, sdev); return 0; } #endif static void cpy_ccm_power_settings(struct ccm_config *ccm_cfg) { strcpy(ccm_cfg->iovdd_str, ccm_cfg->sensor_cfg_ini->sub_power_str[ENUM_IOVDD]); ccm_cfg->power.iovdd_vol = ccm_cfg->sensor_cfg_ini->sub_power_vol[ENUM_IOVDD]; strcpy(ccm_cfg->avdd_str, ccm_cfg->sensor_cfg_ini->sub_power_str[ENUM_AVDD]); ccm_cfg->power.avdd_vol = ccm_cfg->sensor_cfg_ini->sub_power_vol[ENUM_AVDD]; strcpy(ccm_cfg->dvdd_str, ccm_cfg->sensor_cfg_ini->sub_power_str[ENUM_DVDD]); ccm_cfg->power.dvdd_vol = ccm_cfg->sensor_cfg_ini->sub_power_vol[ENUM_DVDD]; strcpy(ccm_cfg->afvdd_str, ccm_cfg->sensor_cfg_ini->sub_power_str[ENUM_AFVDD]); ccm_cfg->power.afvdd_vol = ccm_cfg->sensor_cfg_ini->sub_power_vol[ENUM_AFVDD]; } static int cpy_ccm_sub_device_cfg(struct ccm_config *ccm_cfg, int n) { strcpy(ccm_cfg->ccm, ccm_cfg->sensor_cfg_ini->camera_inst[n].name); if (strcmp(ccm_cfg->sensor_cfg_ini->camera_inst[n].isp_cfg_name, "")) strcpy(ccm_cfg->isp_cfg_name, ccm_cfg->sensor_cfg_ini->camera_inst[n].isp_cfg_name); else strcpy(ccm_cfg->isp_cfg_name, ccm_cfg->ccm); ccm_cfg->i2c_addr = ccm_cfg->sensor_cfg_ini->camera_inst[n].i2c_addr; ccm_cfg->hflip = ccm_cfg->sensor_cfg_ini->camera_inst[n].hflip; ccm_cfg->vflip = ccm_cfg->sensor_cfg_ini->camera_inst[n].vflip; ccm_cfg->hflip_thumb = ccm_cfg->sensor_cfg_ini->camera_inst[n].hflip; ccm_cfg->vflip_thumb = ccm_cfg->sensor_cfg_ini->camera_inst[n].vflip; ccm_cfg->power.stby_mode = ccm_cfg->sensor_cfg_ini->camera_inst[n].stdby_mode; if (ccm_cfg->sensor_cfg_ini->camera_inst[n].sensor_type == SENSOR_RAW) { ccm_cfg->is_bayer_raw = 1; ccm_cfg->is_isp_used = 1; } else if (ccm_cfg->sensor_cfg_ini->camera_inst[n].sensor_type == SENSOR_YUV) { ccm_cfg->is_bayer_raw = 0; ccm_cfg->is_isp_used = 0; } else { ccm_cfg->is_bayer_raw = 0; ccm_cfg->is_isp_used = 1; } strcpy(ccm_cfg->act_name, ccm_cfg->sensor_cfg_ini->camera_inst[n].act_name); if (strcmp(ccm_cfg->act_name, "")) { ccm_cfg->act_used = 1; vfe_print("VCM driver name is \"%s\".\n", ccm_cfg->act_name); } ccm_cfg->act_slave = ccm_cfg->sensor_cfg_ini->camera_inst[n].act_i2c_addr; return 0; } static const char * const sensor_info_type[] = { "YUV", "RAW", NULL, }; static struct v4l2_subdev *vfe_sensor_register_check(struct vfe_dev *dev, struct v4l2_device *v4l2_dev, struct ccm_config *ccm_cfg, struct i2c_board_info *sensor_i2c_board, int input_num) { int sensor_cnt, ret, sensor_num; struct sensor_item sensor_info; if (dev->vip_define_sensor_list == 1) { sensor_num = ccm_cfg->sensor_cfg_ini->detect_sensor_num; if (ccm_cfg->sensor_cfg_ini->detect_sensor_num == 0) sensor_num = 1; } else sensor_num = 1; for (sensor_cnt = 0; sensor_cnt < sensor_num; sensor_cnt++) { if (dev->vip_define_sensor_list == 1) { if (ccm_cfg->sensor_cfg_ini->detect_sensor_num > 0) cpy_ccm_sub_device_cfg(ccm_cfg, sensor_cnt); } if (get_sensor_info(ccm_cfg->ccm, &sensor_info) == 0) { if (ccm_cfg->i2c_addr != sensor_info.i2c_addr) vfe_warn("Sensor info \"%s\" i2c_addr is different from sys_config!\n", sensor_info.sensor_name); if (ccm_cfg->is_bayer_raw != sensor_info.sensor_type) { vfe_warn("Camer detect \"%s\" fmt is different from sys_config!\n", sensor_info_type[sensor_info.sensor_type]); vfe_warn("Apply detect fmt = %d replace sys_config fmt = %d!\n", sensor_info.sensor_type, ccm_cfg->is_bayer_raw); ccm_cfg->is_bayer_raw = sensor_info.sensor_type; } if (sensor_info.sensor_type == SENSOR_RAW) ccm_cfg->is_isp_used = 1; else ccm_cfg->act_used = 0; vfe_print("Find sensor name is \"%s\", i2c address is %x, type is \"%s\" !\n", sensor_info.sensor_name, sensor_info.i2c_addr, sensor_info_type[sensor_info.sensor_type]); } sensor_i2c_board->addr = (unsigned short)(ccm_cfg->i2c_addr>>1); strcpy(sensor_i2c_board->type, ccm_cfg->ccm); vfe_print("Sub device register \"%s\" i2c_addr = 0x%x start!\n", sensor_i2c_board->type, ccm_cfg->i2c_addr); ret = vfe_sensor_subdev_register_check(dev, v4l2_dev, ccm_cfg, sensor_i2c_board); if (ret == -1) { vfe_sensor_subdev_unregister(v4l2_dev, ccm_cfg, sensor_i2c_board); vfe_print("Sub device register \"%s\" failed!\n", sensor_i2c_board->type); ccm_cfg->sd = NULL; continue; } else if (ret == ENODEV || ret == EFAULT) continue; else if (ret == 0) { vfe_print("Sub device register \"%s\" is OK!\n", sensor_i2c_board->type); break; } } return ccm_cfg->sd; } static const struct v4l2_ctrl_config custom_ctrls[] = { { .ops = &vfe_ctrl_ops, .id = V4L2_CID_HOR_VISUAL_ANGLE, .name = "Horizontal Visual Angle", .type = V4L2_CTRL_TYPE_INTEGER, .min = 0, .max = 360, .step = 1, .def = 60, .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, }, { .ops = &vfe_ctrl_ops, .id = V4L2_CID_VER_VISUAL_ANGLE, .name = "Vertical Visual Angle", .type = V4L2_CTRL_TYPE_INTEGER, .min = 0, .max = 360, .step = 1, .def = 60, .flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY, }, { .ops = &vfe_ctrl_ops, .id = V4L2_CID_FOCUS_LENGTH, .name = "Focus Length", .type = V4L2_CTRL_TYPE_INTEGER, .min = 0, .max = 1000, .step = 1, .def = 280, .flags = V4L2_CTRL_FLAG_VOLATILE, }, { .ops = &vfe_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 = &vfe_ctrl_ops, .id = V4L2_CID_G_GAIN, .name = "G GAIN", .type = V4L2_CTRL_TYPE_INTEGER, .min = 32, .max = 1024, .step = 1, .def = 256, .flags = V4L2_CTRL_FLAG_VOLATILE, }, { .ops = &vfe_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, }, { .ops = &vfe_ctrl_ops, .id = V4L2_CID_AUTO_FOCUS_INIT, .name = "AutoFocus Initial", .type = V4L2_CTRL_TYPE_BUTTON, .min = 0, .max = 0, .step = 0, .def = 0, }, { .ops = &vfe_ctrl_ops, .id = V4L2_CID_AUTO_FOCUS_RELEASE, .name = "AutoFocus Release", .type = V4L2_CTRL_TYPE_BUTTON, .min = 0, .max = 0, .step = 0, .def = 0, }, { .ops = &vfe_ctrl_ops, .id = V4L2_CID_GSENSOR_ROTATION, .name = "Gsensor Rotaion", .type = V4L2_CTRL_TYPE_INTEGER, .min = -180, .max = 180, .step = 90, .def = 0, }, { .ops = &vfe_ctrl_ops, .id = V4L2_CID_TAKE_PICTURE, .name = "Take Picture", .type = V4L2_CTRL_TYPE_INTEGER, .min = 0, .max = 16, .step = 1, .def = 0, }, { .ops = &vfe_ctrl_ops, .id = V4L2_CID_SENSOR_TYPE, .name = "Sensor type", .type = V4L2_CTRL_TYPE_MENU, .min = 0, .max = 1, .def = 0, .menu_skip_mask = 0x0, .qmenu = sensor_info_type, .flags = V4L2_CTRL_FLAG_VOLATILE, }, }; static const s64 iso_qmenu[] = { 50, 100, 200, 400, 800, }; static const s64 exp_bias_qmenu[] = { -4, -3, -2, -1, 0, 1, 2, 3, 4, }; static int vfe_init_controls(struct v4l2_ctrl_handler *hdl) { struct v4l2_ctrl *ctrl; unsigned int i, ret = 0; v4l2_ctrl_handler_init(hdl, 37 + ARRAY_SIZE(custom_ctrls)); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_CONTRAST, 0, 128, 1, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_SATURATION, -4, 4, 1, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_HUE, -180, 180, 1, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); ctrl = v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_EXPOSURE, 0, 65536*16, 1, 0); if (ctrl != NULL) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); ctrl = v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_GAIN, 1*16, 64*16-1, 1, 1*16); if (ctrl != NULL) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; v4l2_ctrl_new_std_menu(hdl, &vfe_ctrl_ops, V4L2_CID_POWER_LINE_FREQUENCY, V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, V4L2_CID_POWER_LINE_FREQUENCY_AUTO); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_HUE_AUTO, 0, 1, 1, 1); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, 2800, 10000, 1, 6500); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_SHARPNESS, -32, 32, 1, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); v4l2_ctrl_new_std_menu(hdl, &vfe_ctrl_ops, V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR, 0, V4L2_COLORFX_NONE); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_BAND_STOP_FILTER, 0, 1, 1, 1); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); v4l2_ctrl_new_std_menu(hdl, &vfe_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_APERTURE_PRIORITY, 0, V4L2_EXPOSURE_AUTO); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_EXPOSURE_ABSOLUTE, 1, 1000000, 1, 1); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_EXPOSURE_AUTO_PRIORITY, 0, 1, 1, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_FOCUS_ABSOLUTE, 0, 127, 1, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_FOCUS_RELATIVE, -127, 127, 1, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_FOCUS_AUTO, 0, 1, 1, 1); v4l2_ctrl_new_int_menu(hdl, &vfe_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, ARRAY_SIZE(exp_bias_qmenu) - 1, ARRAY_SIZE(exp_bias_qmenu)/2, exp_bias_qmenu); v4l2_ctrl_new_std_menu(hdl, &vfe_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, V4L2_WHITE_BALANCE_SHADE, 0, V4L2_WHITE_BALANCE_AUTO); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); v4l2_ctrl_new_int_menu(hdl, &vfe_ctrl_ops, V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); v4l2_ctrl_new_std_menu(hdl, &vfe_ctrl_ops, V4L2_CID_ISO_SENSITIVITY_AUTO, V4L2_ISO_SENSITIVITY_AUTO, 0, V4L2_ISO_SENSITIVITY_AUTO); v4l2_ctrl_new_std_menu(hdl, &vfe_ctrl_ops, V4L2_CID_SCENE_MODE, V4L2_SCENE_MODE_TEXT, 0, V4L2_SCENE_MODE_NONE); ctrl = v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_3A_LOCK, 0, 7, 0, 0); if (ctrl != NULL) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_AUTO_FOCUS_START, 0, 0, 0, 0); v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_AUTO_FOCUS_STOP, 0, 0, 0, 0); ctrl = v4l2_ctrl_new_std(hdl, &vfe_ctrl_ops, V4L2_CID_AUTO_FOCUS_STATUS, 0, 7, 0, 0); if (ctrl != NULL) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; v4l2_ctrl_new_std_menu(hdl, &vfe_ctrl_ops, V4L2_CID_AUTO_FOCUS_RANGE, V4L2_AUTO_FOCUS_RANGE_INFINITY, 0, V4L2_AUTO_FOCUS_RANGE_AUTO); v4l2_ctrl_new_std_menu(hdl, &vfe_ctrl_ops, V4L2_CID_FLASH_LED_MODE, V4L2_FLASH_LED_MODE_RED_EYE, 0, V4L2_FLASH_LED_MODE_NONE); for (i = 0; i < ARRAY_SIZE(custom_ctrls); i++) v4l2_ctrl_new_custom(hdl, &custom_ctrls[i], NULL); if (hdl->error) { ret = hdl->error; v4l2_ctrl_handler_free(hdl); } return ret; } static void probe_work_handle(struct work_struct *work) { struct vfe_dev *dev = container_of(work, struct vfe_dev, probe_work.work); int ret = 0, i, video_cnt = 1, video_num; int input_num; int device_valid_count = 0; struct video_device *vfd; struct vb2_queue *q; mutex_lock(&probe_hdl_lock); vfe_print("probe_work_handle start!\n"); vfe_dbg(0, "v4l2_device_register\n"); /* v4l2 device register */ ret = v4l2_device_register(&dev->pdev->dev, &dev->v4l2_dev); if (ret) { vfe_err("Error registering v4l2 device\n"); goto probe_hdl_end; } ret = vfe_init_controls(&dev->ctrl_handler); if (ret) { vfe_err("Error v4l2 ctrls new!!\n"); goto probe_hdl_unreg_dev; } dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler; dev_set_drvdata(&dev->pdev->dev, (dev)); vfe_dbg(0, "v4l2 subdev register\n"); /* v4l2 subdev register */ /*Register ISP subdev*/ sunxi_isp_get_subdev(&dev->isp_sd, dev->isp_sel); sunxi_isp_register_subdev(&dev->v4l2_dev, dev->isp_sd); /*Register CSI subdev*/ sunxi_csi_get_subdev(&dev->csi_sd, dev->csi_sel); sunxi_csi_register_subdev(&dev->v4l2_dev, dev->csi_sd); /*Register MIPI subdev*/ sunxi_mipi_get_subdev(&dev->mipi_sd, dev->mipi_sel); sunxi_mipi_register_subdev(&dev->v4l2_dev, dev->mipi_sd); /*Register flash subdev*/ if (dev->ccm_cfg[0]->flash_used || dev->ccm_cfg[1]->flash_used) { sunxi_flash_get_subdev(&dev->flash_sd, dev->flash_sel); sunxi_flash_register_subdev(&dev->v4l2_dev, dev->flash_sd); } /*Register Sensor subdev*/ dev->is_same_module = 0; #ifdef CONFIG_PM_RUNTIME pm_runtime_enable(&dev->pdev->dev); #endif vfe_resume_trip(dev); for (input_num = 0; input_num < dev->dev_qty; input_num++) { vfe_print("v4l2 subdev register input_num = %d\n", input_num); if (!strcmp(dev->ccm_cfg[input_num]->ccm, "")) { vfe_err("Sensor name is NULL!\n"); goto snesor_register_end; } if (dev->is_same_module) { dev->ccm_cfg[input_num]->sd = dev->ccm_cfg[input_num-1]->sd; vfe_dbg(0, "num = %d , sd_0 = %p, sd_1 = %p\n", input_num, dev->ccm_cfg[input_num]->sd, dev->ccm_cfg[input_num-1]->sd); goto snesor_register_end; } if ((dev->dev_qty > 1) && (input_num+1 < dev->dev_qty)) { if ((!strcmp(dev->ccm_cfg[input_num]->ccm, dev->ccm_cfg[input_num+1]->ccm))) dev->is_same_module = 1; } if (dev->vip_define_sensor_list == 1) { if (dev->ccm_cfg[input_num]->sensor_cfg_ini->power_settings_enable == 1) cpy_ccm_power_settings(dev->ccm_cfg[input_num]); } #ifdef _REGULATOR_CHANGE_ #else if (vfe_device_regulator_get(dev->ccm_cfg[input_num])) { vfe_err("vfe_device_regulator_get error at input_num = %d\n", input_num); goto snesor_register_end; } #endif vfe_print("vfe sensor detect start! input_num = %d\n", input_num); dev->input = input_num; if (vfe_sensor_register_check(dev, &dev->v4l2_dev, dev->ccm_cfg[input_num], &dev->dev_sensor[input_num], input_num) == NULL) { vfe_err("vfe sensor register check error at input_num = %d\n", input_num); dev->device_valid_flag[input_num] = 0; /* goto snesor_register_end; */ } else { dev->device_valid_flag[input_num] = 1; device_valid_count++; } if (dev->ccm_cfg[input_num]->is_isp_used && dev->ccm_cfg[input_num]->is_bayer_raw) { if (read_ini_info(dev, input_num, "/system/etc/hawkview/")) vfe_warn("read ini info fail\n"); } if (dev->ccm_cfg[input_num]->act_used == 1) { dev->dev_act[input_num].addr = (unsigned short)(dev->ccm_cfg[input_num]->act_slave>>1); strcpy(dev->dev_act[input_num].type, dev->ccm_cfg[input_num]->act_name); vfe_actuator_subdev_register(dev, dev->ccm_cfg[input_num], &dev->dev_act[input_num]); } snesor_register_end: vfe_dbg(0, "dev->ccm_cfg[%d] = %p\n", input_num, dev->ccm_cfg[input_num]); vfe_dbg(0, "dev->ccm_cfg[%d]->sd = %p\n", input_num, dev->ccm_cfg[input_num]->sd); vfe_dbg(0, "dev->ccm_cfg[%d]->power.iovdd = %p\n", input_num, dev->ccm_cfg[input_num]->power.iovdd); vfe_dbg(0, "dev->ccm_cfg[%d]->power.avdd = %p\n", input_num, dev->ccm_cfg[input_num]->power.avdd); vfe_dbg(0, "dev->ccm_cfg[%d]->power.dvdd = %p\n", input_num, dev->ccm_cfg[input_num]->power.dvdd); vfe_dbg(0, "dev->ccm_cfg[%d]->power.afvdd = %p\n", input_num, dev->ccm_cfg[input_num]->power.afvdd); } dev->input = -1; /*video device register */ #if defined(CH_OUTPUT_IN_DIFFERENT_VIDEO) if (dev->id == 0) video_cnt = MAX_CH_NUM; else video_cnt = 1; #else video_cnt = 1; #endif for (i = 0; i < video_cnt; i++) { vfd = video_device_alloc(); if (!vfd) { vfe_err("Error video_device_alloc!!\n"); goto close_clk_pin_power; } *vfd = vfe_template[dev->id]; vfd->v4l2_dev = &dev->v4l2_dev; if (device_valid_count != 0) { #if defined(CH_OUTPUT_IN_DIFFERENT_VIDEO) if (dev->id == 0) video_num = i + VID_N_OFF; else video_num = dev->id; #else video_num = dev->id; #endif ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_num); if (ret < 0) { vfe_err("Error video_register_device!!\n"); goto probe_hdl_rel_vdev; } } vfd->lock = &dev->buf_lock; video_set_drvdata(vfd, dev); dev->vfd[i] = vfd; vfe_print("V4L2 device registered as %s\n", video_device_node_name(vfd)); /* Initialize videobuf2 queue as per the buffer type */ dev->allocate_dev[i] = &dev->pdev->dev; vb2_dma_contig_set_max_seg_size(&dev->pdev->dev, 0); if (IS_ERR(dev->allocate_dev[i])) { vfe_err("Failed to get the context\n"); goto probe_hdl_rel_vdev; } /* initialize queue */ q = &dev->vb_vidq[i]; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ; q->drv_priv = dev; q->buf_struct_size = sizeof(struct vfe_buffer); q->ops = &vfe_video_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; q->lock = &dev->buf_lock; ret = vb2_queue_init(q); if (ret) { vfe_err("vb2_queue_init() failed\n"); vb2_dma_contig_clear_max_seg_size(dev->allocate_dev[i]); goto probe_hdl_rel_vdev; } INIT_LIST_HEAD(&dev->vidq[i].active); } /* Now that everything is fine, let's add it to device list */ list_add_tail(&dev->devlist, &devlist); ret = sysfs_create_group(&dev->pdev->dev.kobj, &vfe_attribute_group); vfe_suspend_trip(dev); vfe_print("probe_work_handle end!\n"); mutex_unlock(&probe_hdl_lock); return; probe_hdl_rel_vdev: video_device_release(vfd); vfe_print("video_device_release @ probe_hdl!\n"); close_clk_pin_power: vfe_suspend_trip(dev); probe_hdl_unreg_dev: vfe_print("v4l2_device_unregister @ probe_hdl!\n"); v4l2_device_unregister(&dev->v4l2_dev); probe_hdl_end: vfe_err("Failed to install at probe handle\n"); mutex_unlock(&probe_hdl_lock); return; } static int vfe_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct vfe_dev *dev; int ret = 0; int input_num; unsigned int i; vfe_dbg(0, "vfe_probe\n"); /*request mem for dev*/ dev = kzalloc(sizeof(struct vfe_dev), GFP_KERNEL); if (!dev) { vfe_err("request dev mem failed!\n"); ret = -ENOMEM; goto ekzalloc; } for (i = 0; i < MAX_INPUT_NUM; i++) { dev->isp_gen_set[i] = kzalloc(sizeof(struct isp_gen_settings), GFP_KERNEL); if (!dev->isp_gen_set[i]) { vfe_err("request isp_gen_settings mem failed!\n"); return -ENOMEM; } } pdev->id = of_alias_get_id(np, "vfe"); if (pdev->id < 0) { vfe_err("VFE failed to get alias id\n"); ret = -EINVAL; goto freedev; } of_property_read_u32(np, "cci_sel", &dev->cci_sel); of_property_read_u32(np, "csi_sel", &dev->csi_sel); of_property_read_u32(np, "mipi_sel", &dev->mipi_sel); of_property_read_u32(np, "isp_sel", &dev->isp_sel); dev->platform_id = SUNXI_PLATFORM_ID; dev->id = pdev->id; dev->pdev = pdev; dev->generating = 0; dev->opened = 0; dev->vfe_sensor_power_cnt = 0; dev->vfe_s_input_flag = 0; vfe_print("pdev->id = %d\n", pdev->id); vfe_print("dev->cci_sel = %d\n", dev->cci_sel); vfe_print("dev->csi_sel = %d\n", dev->csi_sel); vfe_print("dev->mipi_sel = %d\n", dev->mipi_sel); vfe_print("dev->isp_sel = %d\n", dev->isp_sel); vfe_dev_gbl[dev->id] = dev; spin_lock_init(&dev->slock); vfe_dbg(0, "fetch sys_config\n"); /* fetch sys_config! */ for (input_num = 0; input_num < MAX_INPUT_NUM; input_num++) { dev->ccm_cfg[input_num] = &dev->ccm_cfg_content[input_num]; vfe_dbg(0, "dev->ccm_cfg[%d] = %p\n", input_num, dev->ccm_cfg[input_num]); dev->ccm_cfg[input_num]->i2c_addr = i2c_addr; strcpy(dev->ccm_cfg[input_num]->ccm, ccm); strcpy(dev->ccm_cfg[input_num]->isp_cfg_name, ccm); dev->ccm_cfg[input_num]->act_slave = act_slave; strcpy(dev->ccm_cfg[input_num]->act_name, act_name); dev->vip_define_sensor_list = define_sensor_list; } ret = fetch_config(dev); if (ret) { vfe_err("Error at fetch_config\n"); goto freedev; } if (vips != 0xffff) { pr_info("vips input 0x%x\n", vips); dev->ccm_cfg[0]->is_isp_used = (vips&0xf0)>>4; dev->ccm_cfg[0]->is_bayer_raw = (vips&0xf00)>>8; if ((vips&0xff0) == 0) dev->ccm_cfg[0]->act_used = 0; } vfe_get_regulator(dev); ret = vfe_resource_request(pdev, dev); if (ret < 0) goto freedev; /*initial parameter */ dev->cur_ch = 0; dev->isp_init_para.isp_src_ch_mode = ISP_SINGLE_CH; for (i = 0; i < MAX_ISP_SRC_CH_NUM; i++) dev->isp_init_para.isp_src_ch_en[i] = 0; dev->isp_init_para.isp_src_ch_en[dev->id] = 1; INIT_DELAYED_WORK(&dev->probe_work, probe_work_handle); mutex_init(&dev->stream_lock); mutex_init(&dev->opened_lock); mutex_init(&dev->buf_lock); schedule_delayed_work(&dev->probe_work, msecs_to_jiffies(1)); /* initial state */ dev->capture_mode = V4L2_MODE_PREVIEW; return 0; freedev: kfree(dev); ekzalloc: vfe_print("vfe probe err!\n"); return ret; } static int vfe_release(void) { struct vfe_dev *dev; struct list_head *list; vfe_dbg(0, "vfe_release\n"); while (!list_empty(&devlist)) { list = devlist.next; list_del(list); dev = list_entry(list, struct vfe_dev, devlist); kfree(dev); } vfe_print("vfe_release ok!\n"); return 0; } static int vfe_remove(struct platform_device *pdev) { struct vfe_dev *dev = (struct vfe_dev *)dev_get_drvdata(&pdev->dev); int input_num, i, video_cnt = 1; /*Unegister ISP subdev*/ sunxi_isp_unregister_subdev(dev->isp_sd); sunxi_isp_put_subdev(&dev->isp_sd, dev->isp_sel); /*Unegister CSI subdev*/ sunxi_csi_unregister_subdev(dev->csi_sd); sunxi_csi_put_subdev(&dev->csi_sd, dev->csi_sel); /*Unegister MIPI subdev*/ sunxi_mipi_unregister_subdev(dev->mipi_sd); sunxi_mipi_put_subdev(&dev->mipi_sd, dev->mipi_sel); /*Unegister flash subdev*/ if (dev->flash_used == 1) { sunxi_flash_unregister_subdev(dev->flash_sd); sunxi_flash_put_subdev(&dev->flash_sd, dev->flash_sel); } mutex_destroy(&dev->stream_lock); mutex_destroy(&dev->opened_lock); mutex_destroy(&dev->buf_lock); sysfs_remove_group(&dev->pdev->dev.kobj, &vfe_attribute_group); #ifdef USE_SPECIFIC_CCI csi_cci_bus_unmatch_helper(dev->cci_sel); #endif vfe_put_regulator(dev); #ifdef CONFIG_PM_RUNTIME pm_runtime_disable(&dev->pdev->dev); #endif for (input_num = 0; input_num < dev->dev_qty; input_num++) { #ifdef _REGULATOR_CHANGE_ #else vfe_device_regulator_put(dev->ccm_cfg[input_num]); #endif if (!dev->ccm_cfg[input_num]->sensor_cfg_ini) kfree(dev->ccm_cfg[input_num]->sensor_cfg_ini); } vfe_resource_release(dev); #if defined(CH_OUTPUT_IN_DIFFERENT_VIDEO) if (dev->id == 0) video_cnt = MAX_CH_NUM; else video_cnt = 1; #else video_cnt = 1; #endif for (i = 0; i < video_cnt; i++) { v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(dev->vfd[i])); video_unregister_device(dev->vfd[i]); vb2_dma_contig_clear_max_seg_size(dev->allocate_dev[i]); } v4l2_device_unregister(&dev->v4l2_dev); v4l2_ctrl_handler_free(&dev->ctrl_handler); for (i = 0; i < MAX_INPUT_NUM; i++) kfree(dev->isp_gen_set[i]); vfe_print("vfe_remove ok!\n"); return 0; } static void vfe_suspend_helper(struct vfe_dev *dev) { if (vfe_i2c_dbg) { vfe_print("vfe_i2c_dbg: pin power and clk will not close!!\n"); return; } vfe_clk_close(dev); vfe_disable_regulator_all(dev); vfe_pin_config(dev, 0); vfe_gpio_config(dev, 0); } static void vfe_resume_helper(struct vfe_dev *dev) { vfe_pin_config(dev, 1); vfe_gpio_config(dev, 1); vfe_enable_regulator_all(dev); vfe_clk_open(dev); } static void vfe_suspend_trip(struct vfe_dev *dev) { #ifdef CONFIG_PM_RUNTIME pm_runtime_put_sync(&dev->pdev->dev);/* call pm_runtime suspend */ #else vfe_suspend_helper(dev); #endif } static void vfe_resume_trip(struct vfe_dev *dev) { #ifdef CONFIG_PM_RUNTIME pm_runtime_get_sync(&dev->pdev->dev);/* call pm_runtime resume */ #else vfe_resume_helper(dev); #endif } #ifdef CONFIG_PM_RUNTIME static int vfe_runtime_suspend(struct device *d) { struct vfe_dev *dev = (struct vfe_dev *)dev_get_drvdata(d); vfe_print("vfe_runtime_suspend\n"); vfe_suspend_helper(dev); return 0; } static int vfe_runtime_resume(struct device *d) { struct vfe_dev *dev = (struct vfe_dev *)dev_get_drvdata(d); vfe_print("vfe_runtime_resume\n"); vfe_resume_helper(dev); return 0; } static int vfe_runtime_idle(struct device *d) { if (d) { pm_runtime_mark_last_busy(d); pm_request_autosuspend(d); } else vfe_err("%s, vfe device is null\n", __func__); return 0; } #endif static int vfe_suspend(struct device *d) { struct vfe_dev *dev = (struct vfe_dev *)dev_get_drvdata(d); vfe_print("vfe suspend\n"); if (vfe_is_opened(dev)) { vfe_err("FIXME: dev %s, err happened when calling %s.", dev_name(&dev->pdev->dev), __func__); return -1; } return 0; } static int vfe_resume(struct device *d) { vfe_print("vfe resume\n"); return 0; } static void vfe_shutdown(struct platform_device *pdev) { #if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_SUSPEND) vfe_print("Defined suspend!\n"); #else struct vfe_dev *dev = (struct vfe_dev *)dev_get_drvdata(&pdev->dev); unsigned int input_num; int ret = 0; /* close all the device power */ for (input_num = 0; input_num < dev->dev_qty; input_num++) { /* update target device info and select it */ update_ccm_info(dev, dev->ccm_cfg[input_num]); ret = vfe_set_sensor_power_off(dev); if (ret != 0) vfe_err("sensor power off error at device number %d when csi close!\n", input_num); } #endif vfe_print("Vfe Shutdown!\n"); } static const struct dev_pm_ops vfe_runtime_pm_ops = { #ifdef CONFIG_PM_RUNTIME .runtime_suspend = vfe_runtime_suspend, .runtime_resume = vfe_runtime_resume, .runtime_idle = vfe_runtime_idle, #endif .suspend = vfe_suspend, .resume = vfe_resume, }; static const struct of_device_id sunxi_vfe_match[] = { { .compatible = "allwinner,sunxi-vfe", }, {}, }; static struct platform_driver vfe_driver = { .probe = vfe_probe, .remove = vfe_remove, .shutdown = vfe_shutdown, .driver = { .name = VFE_MODULE_NAME, .owner = THIS_MODULE, .of_match_table = sunxi_vfe_match, .pm = &vfe_runtime_pm_ops, } }; static int __init vfe_init(void) { int ret; vfe_print("Welcome to Video Front End driver\n"); mutex_init(&probe_hdl_lock); sunxi_csi_platform_register(); sunxi_isp_platform_register(); sunxi_mipi_platform_register(); sunxi_flash_platform_register(); ret = platform_driver_register(&vfe_driver); if (ret) { vfe_err("platform driver register failed\n"); return ret; } vfe_print("vfe_init end\n"); return 0; } static void __exit vfe_exit(void) { vfe_print("vfe_exit\n"); platform_driver_unregister(&vfe_driver); vfe_print("platform_driver_unregister\n"); vfe_release(); mutex_destroy(&probe_hdl_lock); sunxi_csi_platform_unregister(); sunxi_isp_platform_unregister(); sunxi_mipi_platform_unregister(); sunxi_flash_platform_unregister(); vfe_print("vfe_exit end\n"); } module_init(vfe_init); module_exit(vfe_exit); MODULE_AUTHOR("raymonxiu"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Video front end driver for sunxi");