OSHW-DEIMOS/SOFTWARE/A64-TERES/linux-a64/drivers/media/platform/sunxi-vfe/vfe.c
Dimitar Gamishev f9b0e7a283 linux
2017-10-13 14:07:04 +03:00

4842 lines
143 KiB
C
Executable File

/*
* sunxi Camera Interface driver
* Author: raymonxiu
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/delay.h>
#include <linux/string.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
#include <linux/freezer.h>
#endif
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/moduleparam.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/videobuf2-dma-contig.h>
#include <linux/regulator/consumer.h>
#ifdef CONFIG_DEVFREQ_DRAM_FREQ_WITH_SOFT_NOTIFY
#include <linux/sunxi_dramfreq.h>
#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 "platform/vfe_resource.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 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_
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 = 0;
static uint isp_log = 0;
static uint vips = 0xffff;
static int touch_flash_flag = 0;
static int ev_cumul=0;
static unsigned int vfe_opened_num = 0;
unsigned int isp_reparse_flag = 0;
static unsigned int frame_cnt = 0;
static unsigned int vfe_dump = 0;
struct mutex probe_hdl_lock;
struct file* fp_dbg = NULL;
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 = strict_strtoul(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 = strict_strtoul(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 = strict_strtoul(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 = strict_strtoul(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,
},
};
static enum v4l2_mbus_pixelcode try_yuv422_bus[] = {
V4L2_MBUS_FMT_UYVY10_20X1,
V4L2_MBUS_FMT_UYVY8_16X1,
V4L2_MBUS_FMT_YUYV10_2X10,
V4L2_MBUS_FMT_YVYU10_2X10,
V4L2_MBUS_FMT_UYVY8_2X8,
V4L2_MBUS_FMT_VYUY8_2X8,
V4L2_MBUS_FMT_YUYV8_2X8,
V4L2_MBUS_FMT_YVYU8_2X8,
V4L2_MBUS_FMT_YUYV10_1X20,
V4L2_MBUS_FMT_YVYU10_1X20,
V4L2_MBUS_FMT_UYVY8_1X16,
V4L2_MBUS_FMT_VYUY8_1X16,
V4L2_MBUS_FMT_YUYV8_1X16,
V4L2_MBUS_FMT_YVYU8_1X16,
V4L2_MBUS_FMT_YUV8_1X24,
};
#define N_TRY_YUV422 ARRAY_SIZE(try_yuv422_bus)
static enum v4l2_mbus_pixelcode try_yuv420_bus[] = {
V4L2_MBUS_FMT_YY10_UYVY10_15X1,
V4L2_MBUS_FMT_YY8_UYVY8_12X1,
};
#define N_TRY_YUV420 ARRAY_SIZE(try_yuv420_bus)
static enum v4l2_mbus_pixelcode try_bayer_rgb_bus[] = {
V4L2_MBUS_FMT_SBGGR12_12X1,
V4L2_MBUS_FMT_SGBRG12_12X1,
V4L2_MBUS_FMT_SGRBG12_12X1,
V4L2_MBUS_FMT_SRGGB12_12X1,
V4L2_MBUS_FMT_SBGGR12_1X12,
V4L2_MBUS_FMT_SGBRG12_1X12,
V4L2_MBUS_FMT_SGRBG12_1X12,
V4L2_MBUS_FMT_SRGGB12_1X12,
V4L2_MBUS_FMT_SBGGR10_10X1,
V4L2_MBUS_FMT_SGBRG10_10X1,
V4L2_MBUS_FMT_SGRBG10_10X1,
V4L2_MBUS_FMT_SRGGB10_10X1,
V4L2_MBUS_FMT_SBGGR10_1X10,
V4L2_MBUS_FMT_SGBRG10_1X10,
V4L2_MBUS_FMT_SGRBG10_1X10,
V4L2_MBUS_FMT_SRGGB10_1X10,
V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE,
V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE,
V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE,
V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE,
V4L2_MBUS_FMT_SBGGR8_8X1,
V4L2_MBUS_FMT_SGBRG8_8X1,
V4L2_MBUS_FMT_SGRBG8_8X1,
V4L2_MBUS_FMT_SRGGB8_8X1,
V4L2_MBUS_FMT_SBGGR8_1X8,
V4L2_MBUS_FMT_SGBRG8_1X8,
V4L2_MBUS_FMT_SGRBG8_1X8,
V4L2_MBUS_FMT_SRGGB8_1X8,
};
#define N_TRY_BAYER ARRAY_SIZE(try_bayer_rgb_bus)
static enum v4l2_mbus_pixelcode try_rgb565_bus[] = {
V4L2_MBUS_FMT_RGB565_16X1,
V4L2_MBUS_FMT_BGR565_2X8_BE,
V4L2_MBUS_FMT_BGR565_2X8_LE,
V4L2_MBUS_FMT_RGB565_2X8_BE,
V4L2_MBUS_FMT_RGB565_2X8_LE,
};
#define N_TRY_RGB565 ARRAY_SIZE(try_rgb565_bus)
static enum v4l2_mbus_pixelcode try_rgb888_bus[] = {
V4L2_MBUS_FMT_RGB888_24X1,
};
#define N_TRY_RGB888 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->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->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->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->isp_lut_tbl_buf_mm[i]);
os_mem_free(&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->isp_stat_buf_mm[i]);
}
}
}
static char * clk_name[CLK_NUM] = {
"vfe_core_clk",
"vfe_master_clk",
"vfe_misc_clk",
"vfe_mipi_csi_clk",
"vfe_dphy_clk",
};
static char * clk_src_name[CLK_SRC_NUM] = {
"vfe_core_clk_src",
"vfe_master_clk_24M_src",
"vfe_master_clk_pll_src",
"vfe_mipi_csi_clk_src",
"vfe_dphy_clk_src",
};
int clk_index[][CLK_NUM] = { //index of clk in device tree
{0,0,0,0,0},
{0,0,0,0,0},
{0,0,0,0,0},
{0,1,2,NOCLK,NOCLK},
{0,1,2,NOCLK,NOCLK},
{0,1,NOCLK,NOCLK,NOCLK},
};
int clk_src_index[][CLK_SRC_NUM] = { //index of clk source in device tree
{0,0,0,0,0},
{0,0,0,0,0},
{0,0,0,0,0},
{3,4,5,NOCLK,NOCLK},
{3,4,5,NOCLK,NOCLK},
{2,3,NOCLK,NOCLK,NOCLK},
};
static int vfe_clk_get(struct vfe_dev *dev)
{
#ifdef VFE_CLK
int i;
int id = dev->platform_id;
struct device_node *np = dev->pdev->dev.of_node;
for(i = 0; i < CLK_NUM; i++)
{
if(clk_index[id][i] != NOCLK){
dev->clock[i] = os_clk_get(np, clk_index[id][i]);
if(!dev->clock[i])
vfe_warn("Get clk Index:%d , Name:%s is NULL!\n", (int)clk_index[id][i], clk_name[i]);
vfe_dbg(0,"Get clk Name:%s !\n", dev->clock[i]->name);
}
}
for(i = 0; i < CLK_SRC_NUM; i++)
{
if(clk_src_index[id][i] != NOCLK){
dev->clock_src[i] = os_clk_get(np, clk_src_index[id][i]);
if(!dev->clock_src[i])
vfe_warn("Get clk Index:%d , Name:%s is NULL!\n", (int)clk_src_index[id][i], clk_src_name[i]);
vfe_dbg(0,"Get clk Name:%s !\n", dev->clock_src[i]->name);
}
}
if(dev->clock[VFE_CORE_CLK] && dev->clock_src[VFE_CORE_CLK_SRC]) {
if (os_clk_set_parent(dev->clock[VFE_CORE_CLK], dev->clock_src[VFE_CORE_CLK_SRC])) {
vfe_err("sclk src Name:%s, vfe core clock set parent failed \n",dev->clock_src[VFE_CORE_CLK_SRC]->name);
return -1;
}
if (os_clk_set_rate(dev->clock[VFE_CORE_CLK], VFE_CORE_CLK_RATE)) {
vfe_err("set core clock rate error\n");
return -1;
}
} else {
vfe_err("vfe core clock is null\n");
return -1;
}
vfe_dbg(0,"vfe core clk = %ld\n",clk_get_rate(dev->clock[VFE_CORE_CLK]));
#endif
return 0;
}
static int vfe_dphy_clk_set(struct vfe_dev *dev, unsigned long freq)
{
#ifdef VFE_CLK
if(dev->clock[VFE_MIPI_DPHY_CLK] && dev->clock_src[VFE_MIPI_DPHY_CLK_SRC]) {
if(os_clk_set_parent(dev->clock[VFE_MIPI_DPHY_CLK], dev->clock_src[VFE_MIPI_DPHY_CLK_SRC])) {
vfe_err("set vfe dphy clock source failed \n");
return -1;
}
if(os_clk_set_rate(dev->clock[VFE_MIPI_DPHY_CLK], freq)) {
vfe_err("set vip%d dphy clock error\n",dev->vip_sel);
return -1;
}
} else {
vfe_warn("vfe dphy clock is null\n");
return -1;
}
#endif
return 0;
}
static int vfe_clk_enable(struct vfe_dev *dev)
{
int ret = 0;
#ifdef VFE_CLK
int i;
for(i = 0; i < CLK_NUM; i++)
{
if(VFE_MASTER_CLK != i) {
if(dev->clock[i]) {
if(os_clk_prepare_enable(dev->clock[i])) {
vfe_err("%s enable error\n",clk_name[i]);
ret = -1;
}
} else {
vfe_dbg(0,"%s is null\n",clk_name[i]);
ret = -1;
}
}
}
#endif
return ret;
}
static void vfe_clk_disable(struct vfe_dev *dev)
{
#ifdef VFE_CLK
int i;
for(i = 0; i < CLK_NUM; i++)
{
if(VFE_MASTER_CLK != i) {
if(dev->clock[i])
os_clk_disable_unprepare(dev->clock[i]);
else
vfe_dbg(0,"%s is null\n",clk_name[i]);
}
}
#endif
}
static void vfe_clk_release(struct vfe_dev *dev)
{
#ifdef VFE_CLK
int i;
for(i = 0; i < CLK_NUM; i++)
{
if(dev->clock[i])
os_clk_put(dev->clock[i]);
else
vfe_dbg(0,"%s is null\n",clk_name[i]);
}
for(i = 0; i < CLK_SRC_NUM; i++)
{
if(dev->clock_src[i])
os_clk_put(dev->clock_src[i]);
else
vfe_dbg(0,"%s is null\n",clk_src_name[i]);
}
#endif
}
static void vfe_reset_enable(struct vfe_dev *dev)
{
os_clk_reset_assert(dev->clock[VFE_CORE_CLK]);
}
static void vfe_reset_disable(struct vfe_dev *dev)
{
os_clk_reset_deassert(dev->clock[VFE_CORE_CLK]);
}
static int inline vfe_is_generating(struct vfe_dev *dev)
{
return test_bit(0, &dev->generating);
}
static void inline vfe_start_generating(struct vfe_dev *dev)
{
set_bit(0, &dev->generating);
return;
}
static void inline vfe_stop_generating(struct vfe_dev *dev)
{
dev->first_flag = 0;
clear_bit(0, &dev->generating);
return;
}
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;
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;
bsp_isp_update_drc_table(&dev->isp_tbl_addr[dev->input]);
}
//static int isp_addr_curr = 0;
//static int isp_addr_pst = 0;
static inline void vfe_set_addr(struct vfe_dev *dev,struct vfe_buffer *buffer)
{
struct vfe_buffer *buf = buffer;
dma_addr_t addr_org;
struct vb2_buffer *vb_buf = &buf->vb;
if(vb_buf == NULL || vb_buf->planes[0].mem_priv == NULL)
{
vfe_err("vb_buf->priv is NULL!\n");
return;
}
//vfe_dbg(3,"buf ptr=%p\n",buf);
addr_org = vb2_dma_contig_plane_dma_addr(vb_buf, 0) - CPU_DRAM_PADDR_ORG + HW_DMA_OFFSET;
//isp_addr_curr = vfe_reg_readl((volatile void __iomem*)(0xf1cb8210));
//if(isp_addr_pst != isp_addr_curr)
//{
// vfe_warn("isp_addr_pst = %d, isp_addr_curr = %d.......\n", isp_addr_pst, isp_addr_curr);
//}
//isp_addr_pst = addr_org /4;
if(dev->is_isp_used) {
sunxi_isp_set_output_addr(addr_org);
} else {
bsp_csi_set_addr(dev->vip_sel, addr_org);
}
vfe_dbg(3,"csi_buf_addr_orginal=%pa\n", &addr_org);//=>vfe_dbg(3,"csi_buf_addr_orginal=0x%016llx\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{
//if(cfg_write_file(fp_dbg, "0123456789abcdef\n", 16) < 0)
//{
// vfe_err("/system/etc/hawkview/log.txt write test failed.");
//}
;
}
}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(1 == isp_reparse_flag)
{
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(2 == isp_reparse_flag)
{
vfe_reg_set((void __iomem*) (unsigned long)(ISP_REGS_BASE+0x10), (1 << 20));
}
if(3 == isp_reparse_flag)
{
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(4 == isp_reparse_flag)
{
//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_subdev_call(dev->sd,core,s_ctrl,&ctrl) != 0)
{
vfe_err("set sensor exposure line error!\n");
return -1;
}
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_subdev_call(dev->sd,core,s_ctrl,&ctrl) != 0)
{
vfe_err("set sensor gain error!\n");
return -1;
} 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 -1;
}
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->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->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->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->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->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);
}
return;
}
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);
}
void vfe_csi_isp_reset(unsigned long data)
{
struct vfe_dev *dev = (struct vfe_dev *)data;
mod_timer(&dev->timer_for_reset, jiffies + HZ);
bsp_csi_enable(dev->vip_sel);
bsp_csi_disable(dev->vip_sel);
bsp_csi_enable(dev->vip_sel);
if(dev->is_isp_used)
{
bsp_isp_enable();
bsp_isp_disable();
bsp_isp_enable();
}
vfe_print("cs/isp reset after csi/isp interrupt timeout!\n");
}
static int vfe_timer_init(struct vfe_dev *dev)
{
init_timer(&dev->timer_for_reset);
dev->timer_for_reset.data = (unsigned long)dev;
dev->timer_for_reset.expires = jiffies + 2*HZ;
dev->timer_for_reset.function = vfe_csi_isp_reset;
add_timer(&dev->timer_for_reset);
return 0;
}
/*
* 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;
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;
FUNCTION_LOG;
vfe_dbg(0,"vfe interrupt!!!\n");
if(vfe_is_generating(dev) == 0)
{
bsp_csi_int_clear_status(dev->vip_sel, dev->cur_ch,CSI_INT_ALL);
if(dev->is_isp_used)
bsp_isp_clr_irq_status(ISP_IRQ_EN_ALL);
return IRQ_HANDLED;
}
bsp_csi_int_get_status(dev->vip_sel, dev->cur_ch, &status);
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->vip_sel, dev->cur_ch,CSI_INT_ALL);
if(dev->is_isp_used)
bsp_isp_clr_irq_status(ISP_IRQ_EN_ALL);
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->vip_sel, dev->cur_ch,CSI_INT_VSYNC_TRIG);
return IRQ_HANDLED;
}
}
if(vfe_dump & DUMP_CSI)
{
if(5 == frame_cnt % 10)
{
sunxi_csi_dump_regs(dev->csi_sd);
}
}
frame_cnt++;
mod_timer(&dev->timer_for_reset, jiffies + HZ );
FUNCTION_LOG;
spin_lock_irqsave(&dev->slock, flags);
FUNCTION_LOG;
//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->vip_sel, dev->cur_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->vip_sel, dev->cur_ch,CSI_INT_HBLANK_OVERFLOW);
vfe_err("hblank overflow\n");
}
vfe_err("reset csi module\n");
bsp_csi_reset(dev->vip_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->vip_sel, dev->cur_ch,CSI_INT_CAPTURE_DONE);
vfe_print("capture image mode!\n");
buf = list_entry(dma_q->active.next,struct vfe_buffer, list);
list_del(&buf->list);
vb2_buffer_done(&buf->vb, 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->vip_sel, dev->cur_ch,CSI_INT_FRAME_DONE);
if (dev->first_flag == 0) {
dev->first_flag++;
vfe_print("capture video mode!\n");
goto set_isp_stat_addr;
}
if (dev->first_flag == 1) {
dev->first_flag++;
vfe_print("capture video first frame done!\n");
}
//video buffer handle:
if ((&dma_q->active) == dma_q->active.next->next->next) {
vfe_warn("Only two buffer left for csi\n");
dev->first_flag=0;
goto unlock;
}
buf = list_entry(dma_q->active.next,struct vfe_buffer, list);
/* Nobody is waiting on this buffer*/
if (!waitqueue_active(&buf->vb.vb2_queue->done_wq)) {
vfe_warn(" Nobody is waiting on this video buffer,buf = 0x%p\n",buf);
}
list_del(&buf->list);
v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
vfe_dbg(2,"video buffer frame interval = %ld\n",buf->vb.v4l2_buf.timestamp.tv_sec*1000000+buf->vb.v4l2_buf.timestamp.tv_usec
- (dev->sec*1000000+dev->usec));
dev->sec = buf->vb.v4l2_buf.timestamp.tv_sec;
dev->usec = buf->vb.v4l2_buf.timestamp.tv_usec;
dev->ms += jiffies_to_msecs(jiffies - dev->jiffies);
dev->jiffies = jiffies;
buf->vb.image_quality = dev->isp_3a_result_pt->image_quality.dwval;
vb2_buffer_done(&buf->vb, 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");
goto set_next_output_addr;
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);
}
}
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);
unlock:
spin_unlock_irqrestore(&dev->slock, flags);
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(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->vip_sel, dev->cur_ch,CSI_INT_FRAME_DONE);
//bsp_csi_int_clear_status(dev->vip_sel, dev->cur_ch,CSI_INT_VSYNC_TRIG);
//bsp_csi_int_clear_status(dev->vip_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->vip_sel, dev->cur_ch,CSI_INT_FRAME_DONE);
}
return IRQ_HANDLED;
}
/*
* Videobuf operations
*/
static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
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 (0 == *nbuffers)
*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_ctxs[0] = dev->alloc_ctx;
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 vfe_buffer *buf = container_of(vb, 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, 0, size);
vb->v4l2_planes[0].m.mem_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 vfe_buffer *buf = container_of(vb, struct vfe_buffer, vb);
struct vfe_dmaqueue *vidq = &dev->vidq;
unsigned long flags = 0;
vfe_dbg(1,"buffer_queue\n");
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__);
vfe_start_generating(dev);
return 0;
}
/* abort streaming and wait for last buffer */
static int stop_streaming(struct vb2_queue *vq)
{
struct vfe_dev *dev = vb2_get_drv_priv(vq);
struct vfe_dmaqueue *dma_q = &dev->vidq;
vfe_dbg(1, "%s\n", __func__);
vfe_stop_generating(dev);
/* 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_STATE_ERROR);
vfe_dbg(2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
}
return 0;
}
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;
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);
vfe_dbg(0, "vidioc_enum_framesizes\n");
if (dev == NULL || dev->sd->ops->video->enum_framesizes==NULL) {
return -EINVAL;
}
return v4l2_subdev_call(dev->sd,video,enum_framesizes,fsize);
}
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 enum v4l2_mbus_pixelcode *try_fmt_from_sensor(struct vfe_dev *dev, enum v4l2_mbus_pixelcode *bus_pix_code_array,
int size, char *bus_name, char *pix_name, struct v4l2_mbus_framefmt *ccm_fmt)
{
int ret = 0;
enum v4l2_mbus_pixelcode *bus_pix_code;
for(bus_pix_code = bus_pix_code_array; bus_pix_code < bus_pix_code_array + size; bus_pix_code++) {
ccm_fmt->code = *bus_pix_code;
ret = v4l2_subdev_call(dev->sd,video,try_mbus_fmt,ccm_fmt);
if (ret >= 0) {
vfe_dbg(0,"try %s bus ok when pix fmt is %s!\n",bus_name, pix_name);
break;
}
}
if (ret < 0) {
bus_pix_code = NULL;
vfe_err("try %s bus error when pix fmt is %s!\n",bus_name, pix_name);
}
return bus_pix_code;
}
static enum v4l2_mbus_pixelcode *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_mbus_framefmt ccm_fmt;
enum v4l2_mbus_pixelcode *bus_pix_code;
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) {
if(dev->is_isp_used&&dev->is_bayer_raw) {
bus_pix_code = try_fmt_from_sensor(dev, try_bayer_rgb_bus,N_TRY_BAYER, "bayer", "yuv422/yuv420", &ccm_fmt);
if (NULL == bus_pix_code) {
bus_pix_code = try_fmt_from_sensor(dev, try_yuv422_bus,N_TRY_YUV422, "yuv422", "yuv422/yuv420", &ccm_fmt);
if (NULL == bus_pix_code) {
if(pix_fmt_type == YUV420_PL || pix_fmt_type == YUV420_SPL) {
bus_pix_code = try_fmt_from_sensor(dev, try_yuv420_bus,N_TRY_YUV420, "yuv420", "yuv422/yuv420", &ccm_fmt);
} else {
return NULL;
}
}
}
} else {
bus_pix_code = try_fmt_from_sensor(dev, try_yuv422_bus,N_TRY_YUV422, "yuv422", "yuv422/yuv420", &ccm_fmt);
if (NULL == bus_pix_code) {
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, try_yuv422_bus,N_TRY_YUV422, "yuv422", "yuv422", &ccm_fmt);
} else if (pix_fmt_type == BAYER_RGB) {
bus_pix_code = try_fmt_from_sensor(dev, try_bayer_rgb_bus,N_TRY_BAYER, "bayer", "bayer", &ccm_fmt);
} else if (pix_fmt_type == RGB565) {
bus_pix_code = try_fmt_from_sensor(dev, try_rgb565_bus,N_TRY_RGB565, "rgb565", "rgb565", &ccm_fmt);
} else if (pix_fmt_type == RGB888 || pix_fmt_type == PRGB888) {
bus_pix_code = try_fmt_from_sensor(dev, try_rgb888_bus,N_TRY_RGB888, "rgb888", "rgb888/prgb888", &ccm_fmt);
} else {
return NULL;
}
if (NULL == bus_pix_code) {
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);
enum v4l2_mbus_pixelcode *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 vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vfe_dev *dev = video_drvdata(file);
struct v4l2_mbus_framefmt ccm_fmt;
struct v4l2_mbus_config mbus_cfg;
enum v4l2_mbus_pixelcode *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);
if(dev->clock[VFE_MIPI_DPHY_CLK]) {
os_clk_disable(dev->clock[VFE_MIPI_DPHY_CLK]);
} else {
vfe_warn("vfe dphy clock is null\n");
}
bsp_mipi_csi_dphy_enable(dev->mipi_sel);
if(dev->clock[VFE_MIPI_DPHY_CLK]) {
if(os_clk_enable(dev->clock[VFE_MIPI_DPHY_CLK]))
vfe_err("vfe dphy clock enable error\n");
} else {
vfe_warn("vfe dphy clock is null\n");
}
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,video,s_mbus_fmt,&ccm_fmt);
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 = ccm_fmt;
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)
{
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(V4L2_MODE_IMAGE == dev->capture_mode)
{
dev->isp_gen_set_pt->sensor_mod = CAPTURE_MODE;
}
else if(V4L2_MODE_VIDEO == dev->capture_mode)
{
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_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
struct vfe_dev *dev = video_drvdata(file);
vfe_dbg(0,"vidioc_reqbufs\n");
return vb2_reqbufs(&dev->vb_vidq, p);
}
static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vfe_dev *dev = video_drvdata(file);
return vb2_querybuf(&dev->vb_vidq, p);
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vfe_dev *dev = video_drvdata(file);
return vb2_qbuf(&dev->vb_vidq, p);
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
int ret = 0;
struct vfe_dev *dev = video_drvdata(file);
vfe_dbg(2,"vidioc dqbuf\n");
ret = vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);
return ret;
}
#ifdef CONFIG_VIDEO_V4L1_COMPAT
static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
{
struct vfe_dev *dev = video_drvdata(file);
return videobuf_cgmbuf(&dev->vb_vidq, mbuf, 8);
}
#endif
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct vfe_dev *dev = video_drvdata(file);
struct vfe_dmaqueue *dma_q = &dev->vidq;
struct vfe_isp_stat_buf_queue *isp_stat_bq = &dev->isp_stat_bq;
struct vfe_buffer *buf;
struct vfe_isp_stat_buf *stat_buf_pt;
int ret = 0;
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 (vfe_is_generating(dev)) {
vfe_err("stream has been already on\n");
ret = -1;
goto streamon_unlock;
}
bsp_csi_enable(dev->vip_sel);
bsp_csi_disable(dev->vip_sel);
bsp_csi_enable(dev->vip_sel);
if(dev->is_isp_used)
bsp_isp_enable();
/* Resets frame counters */
dev->ms = 0;
dev->jiffies = jiffies;
dma_q->frame = 0;
dma_q->ini_jiffies = jiffies;
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(i=0; i < MAX_ISP_STAT_BUF; i++) {
isp_stat_bq->isp_stat[i].isp_stat_buf.buf_status = BUF_ACTIVE;
list_add_tail(&isp_stat_bq->isp_stat[i].queue, &isp_stat_bq->active);
}
}
ret = vb2_streamon(&dev->vb_vidq, i);
if (ret)
goto streamon_unlock;
buf = list_entry(dma_q->active.next,struct vfe_buffer, list);
vfe_set_addr(dev,buf);
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(NULL == stat_buf_pt){
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->vip_sel, dev->cur_ch, CSI_INT_VSYNC_TRIG);
} else {
bsp_csi_int_clear_status(dev->vip_sel, dev->cur_ch,CSI_INT_ALL);
bsp_csi_int_enable(dev->vip_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();
}
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
vfe_start_generating(dev);
vfe_timer_init(dev);
streamon_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 vfe_dmaqueue *dma_q = &dev->vidq;
int ret = 0;
mutex_lock(&dev->stream_lock);
vfe_dbg(0,"video stream off\n");
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);
del_timer(&dev->timer_for_reset);
vfe_stop_generating(dev);
/* Resets frame counters */
dev->ms = 0;
dev->jiffies = jiffies;
dma_q->frame = 0;
dma_q->ini_jiffies = jiffies;
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->vip_sel, dev->cur_ch, CSI_INT_ALL);
bsp_csi_int_clear_status(dev->vip_sel, dev->cur_ch, CSI_INT_ALL);
}
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);
ret = vb2_streamoff(&dev->vb_vidq, i);
if (ret!=0) {
vfe_err("videobu_streamoff error!\n");
goto streamoff_unlock;
}
if(dev->is_isp_used)
bsp_isp_disable();
bsp_csi_disable(dev->vip_sel);
streamoff_unlock:
mutex_unlock(&dev->stream_lock);
return ret;
}
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 (0 == dev->device_valid_flag[inp->index]) {
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;
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)
{
os_clk_set_rate(dev->clock[VFE_CORE_CLK], sensor_info.core_clk_for_sensor);
vfe_print("Set vfe core clk = %d, after Set vfe core clk = %ld \n",sensor_info.core_clk_for_sensor, clk_get_rate(dev->clock[VFE_CORE_CLK]));
}
else
{
os_clk_set_rate(dev->clock[VFE_CORE_CLK], VFE_CORE_CLK_RATE);
vfe_warn("Not find this sensor info, Set vfe core clk = %d, after Set vfe core clk = %ld \n",VFE_CORE_CLK_RATE, clk_get_rate(dev->clock[VFE_CORE_CLK]));
}
//alternate isp setting
update_isp_setting(dev);
if(dev->is_bayer_raw)
{
isp_param_init(dev->isp_gen_set_pt);
}
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->vip_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->vip_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 */
if(dev->ccm_cfg[i]->vflip == 0)
{
sunxi_isp_set_flip(MAIN_CH, DISABLE);
sunxi_isp_set_flip(SUB_CH, DISABLE);
}
else
{
sunxi_isp_set_flip(MAIN_CH, ENABLE);
sunxi_isp_set_flip(SUB_CH, ENABLE);
}
if(dev->ccm_cfg[i]->hflip == 0)
{
sunxi_isp_set_mirror(MAIN_CH, DISABLE);
sunxi_isp_set_mirror(SUB_CH, DISABLE);
}
else
{
sunxi_isp_set_mirror(MAIN_CH, ENABLE);
sunxi_isp_set_mirror(SUB_CH, ENABLE);
}
} else {
//bsp_isp_exit();
/* Set the initial flip */
ctrl.id = V4L2_CID_VFLIP;
ctrl.value = dev->ccm_cfg[i]->vflip;
ret = v4l2_subdev_call(dev->sd,core, s_ctrl, &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_subdev_call(dev->sd,core, s_ctrl, &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);
int ret;
vfe_dbg(0,"%s ,input_num = %d\n",__func__,i);
ret = internal_s_input(dev , i);
return ret;
}
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(V4L2_TO_ISP == flag)
{
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(ISP_TO_V4L2 == flag)
{
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;//400;
}
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;
}
//printk("V4L2_CID_AUTO_EXPOSURE_WIN_NUM, [%d, %d, %d, %d]\n", win_coor[i].x1, win_coor[i].y1, win_coor[i].x2, win_coor[i].y2);
}
//TODO
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;
//v4l2_dbg(2, debug, &dev->v4l2_dev, "vfe_param_handler\n");
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);
if(vfe_is_generating(dev)) {
return vb2_read(&dev->vb_vidq, data, count, ppos,
file->f_flags & O_NONBLOCK);
} else {
vfe_err("csi is not generating!\n");
return -EINVAL;
}
}
static unsigned int vfe_poll(struct file *file, struct poll_table_struct *wait)
{
struct vfe_dev *dev = video_drvdata(file);
struct vb2_queue *q = &dev->vb_vidq;
if(vfe_is_generating(dev)) {
return vb2_poll(q, file, wait);
} else {
vfe_err("csi is not generating!\n");
return -EINVAL;
}
}
void vfe_clk_open(struct vfe_dev *dev)
{
//hardware
vfe_print("..........................vfe clk open!.......................\n");
vfe_dphy_clk_set(dev,DPHY_CLK);
vfe_clk_enable(dev);
vfe_reset_disable(dev);
}
void vfe_clk_close(struct vfe_dev *dev)
{
vfe_print("..........................vfe clk close!.......................\n");
vfe_clk_disable(dev);
if(vfe_opened_num < 2)
{
vfe_reset_enable(dev);
}
vfe_opened_num--;
}
static void vfe_suspend_trip(struct vfe_dev *dev);
static void vfe_resume_trip(struct vfe_dev *dev);
static int vfe_open(struct file *file)
{
struct vfe_dev *dev = video_drvdata(file);
int ret;//,input_num;
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); //Notification for DRAM dynamic frequency
#endif
vfe_resume_trip(dev);
#ifdef USE_SPECIFIC_CCI
csi_cci_init_helper(dev->vip_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;
}
//resource
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;//default input null
dev->first_flag = 0;
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");
vfe_opened_num ++;
}
return ret;
}
static int vfe_close(struct file *file)
{
struct vfe_dev *dev = video_drvdata(file);
int ret;
vfe_print("vfe_close\n");
//device
del_timer(&dev->timer_for_reset);
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->vip_sel, dev->cur_ch,CSI_INT_ALL);
v4l2_subdev_call(dev->csi_sd, video, s_stream, 0);
bsp_csi_disable(dev->vip_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);
//software
vb2_queue_release(&dev->vb_vidq);
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); //Notification for DRAM dynamic frequency
#endif
return 0;
}
static int vfe_mmap(struct file *file, struct vm_area_struct *vma)
{
struct vfe_dev *dev = video_drvdata(file);
int ret;
vfe_dbg(0,"mmap called, vma=0x%08lx\n", (unsigned long)vma);
ret = vb2_mmap(&dev->vb_vidq, vma);
vfe_dbg(0,"vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start,
(unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
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_subdev_call(dev->sd,core,g_ctrl,&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_subdev_call(dev->flash_sd,core,g_ctrl,&c);
break;
case V4L2_CID_AUTO_FOCUS_STATUS:
ret = v4l2_subdev_call(dev->sd,core,g_ctrl,&c);
if(c.value != V4L2_AUTO_FOCUS_STATUS_BUSY)
sunxi_flash_stop(dev->flash_sd);
break;
default:
ret = v4l2_subdev_call(dev->sd,core,g_ctrl,&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:
return v4l2_subdev_call(dev->sd,core,s_ctrl,&c);
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:
return v4l2_subdev_call(dev->sd,core,s_ctrl,&c);
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:
//printk("set V4L2_CID_FOCUS_AUTO ctrl->value = %d!\n",ctrl->value);
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->val);
//printk("ctrl->value=%d\n",ctrl->value);
dev->ctrl_para.exp_bias = 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->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:
return -EINVAL;
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:
//printk("V4L2_CID_3A_LOCK = %x!\n",ctrl->value);
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) {
//printk("V4L2_CID_3A_LOCK AF!\n");
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:
//if(dev->ctrl_para.auto_focus == 0)
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:
return 0;
case V4L2_CID_AUTO_FOCUS_RELEASE:
return 0;
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:
return -EINVAL;
}
return 0;
} 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_subdev_call(dev->flash_sd, core, s_ctrl, &c);
break;
case V4L2_CID_AUTO_FOCUS_START:
sunxi_flash_check_to_start(dev->flash_sd, SW_CTRL_TORCH_ON);
ret = v4l2_subdev_call(dev->sd, core, s_ctrl, &c);
break;
case V4L2_CID_AUTO_FOCUS_STOP:
sunxi_flash_stop(dev->flash_sd);
ret = v4l2_subdev_call(dev->sd, core, s_ctrl, &c);
break;
default:
ret = v4l2_subdev_call(dev->sd, core, s_ctrl, &c);
break;
}
if (ret < 0)
vfe_warn("v4l2 sub device 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_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret = -ENOIOCTLCMD;
if (file->f_op->unlocked_ioctl)
ret = file->f_op->unlocked_ioctl(file, cmd, arg);
return ret;
}
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);
err = vfe_ioctl(file, cmd, (unsigned long)&karg);
set_fs(old_fs);
}
switch (cmd) {
case VIDIOC_ISP_AE_STAT_REQ:
case VIDIOC_ISP_HIST_STAT_REQ:
case VIDIOC_ISP_AF_STAT_REQ:
case VIDIOC_ISP_GAMMA_REQ:
if (put_isp_stat_buf32(&karg.isb, up))
err = -EFAULT;
break;
}
return err;
}
#endif
/* ------------------------------------------------------------------
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,
.ioctl = video_ioctl2,
//.unlocked_ioctl =
#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,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidiocgmbuf,
#endif
.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(1 == enable){
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,*clk_base;
vfe_print("directly write pin config @ FPGA\n");
clk_base = ioremap(0x01c20000, 0x200);
if (!clk_base) {
printk("clk_base directly write pin config EIO\n");
return -EIO;
}
writel(0xffffffff,(clk_base+0x64));
writel(0xffffffff,(clk_base+0x2c4));
writel(0x0000000f,(clk_base+0x100));
writel(0x80000000,(clk_base+0x130));//open misc clk gate
writel(0x80018000,(clk_base+0x134));//set sclk src pll_periph0 and mclk src clk_hosc
gpio_base = ioremap(GPIO_REGS_VBASE, 0x120);
if (!gpio_base) {
printk("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 (0 == bon)
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;
}
#ifndef FPGA_VER
ret = request_irq(dev->irq, vfe_isr, IRQF_DISABLED, 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");
/*clock resource*/
if (vfe_clk_get(dev)) {
vfe_err("vfe clock get failed!\n");
return -ENXIO;
}
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
#ifdef CONFIG_ARCH_SUN8IW6P1
#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);
dev->vfe_sensor_power_cnt--;
}
else
{
vfe_warn("Sensor is already power off!\n");
dev->vfe_sensor_power_cnt = 0;
}
#ifdef CONFIG_ARCH_SUN8IW6P1
#else
usleep_range(10000,12000);
if(!IS_ERR_OR_NULL(dev->sd))
{
vfe_set_pmu_channel(dev->sd, IOVDD, OFF);
}
#endif
#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->vip_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->vip_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->id, 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->id, 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);
//printk("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 );
//vfe_warn("Sensor info i2c_addr = %d, sys_config i2c_addr = %d!\n", sensor_info.i2c_addr, ccm_cfg->i2c_addr);
//ccm_cfg->i2c_addr = sensor_info.i2c_addr;
}
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, 4, 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, 4, 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;
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->id);
sunxi_isp_register_subdev(&dev->v4l2_dev, dev->isp_sd);
/*Register CSI subdev*/
sunxi_csi_get_subdev(&dev->csi_sd, dev->id);
sunxi_csi_register_subdev(&dev->v4l2_dev, dev->csi_sd);
/*Register MIPI subdev*/
sunxi_mipi_get_subdev(&dev->mipi_sd, dev->id);
sunxi_mipi_register_subdev(&dev->v4l2_dev, dev->mipi_sd);
/*Register flash subdev*/
sunxi_flash_get_subdev(&dev->flash_sd, dev->id);
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 */
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(0 != device_valid_count)
{
ret = video_register_device(vfd, VFL_TYPE_GRABBER, dev->id);
if (ret < 0)
{
vfe_err("Error video_register_device!!\n");
goto probe_hdl_rel_vdev;
}
}
//Provide a mutex to v4l2 core. It will be used to protect all fops and v4l2 ioctls.
//vfd->lock = &dev->buf_lock;
video_set_drvdata(vfd, dev);
/* Now that everything is fine, let's add it to device list */
list_add_tail(&dev->devlist, &devlist);
dev->vfd = vfd;
vfe_print("V4L2 device registered as %s\n",video_device_node_name(vfd));
/* Initialize videobuf2 queue as per the buffer type */
dev->alloc_ctx = vb2_dma_contig_init_ctx(&dev->pdev->dev);
if (IS_ERR(dev->alloc_ctx)) {
vfe_err("Failed to get the context\n");
goto probe_hdl_rel_vdev;
}
/* initialize queue */
q = &dev->vb_vidq;
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_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
ret = vb2_queue_init(q);
if (ret) {
vfe_err("vb2_queue_init() failed\n");
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
goto probe_hdl_rel_vdev;
}
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;
struct sunxi_vip_platform_data *pdata = NULL;
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;
}
pdata = kzalloc(sizeof(struct csi_platform_data), GFP_KERNEL);
if (pdata == NULL) {
ret = -ENOMEM;
goto freedev;
}
pdev->dev.platform_data = pdata;
pdev->id = of_alias_get_id(np, "vfe");
if (pdev->id < 0) {
vfe_err("VFE failed to get alias id\n");
ret = -EINVAL;
goto freepdata;
}
pdata->vip_sel = pdev->id;
dev->platform_id = SUNXI_PLATFORM_ID;
dev->id = pdev->id;
dev->pdev = pdev;
dev->vip_sel = pdata->vip_sel;
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->vip_sel = %d\n",pdata->vip_sel);
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 freepdata;
}
if(vips!=0xffff)
{
printk("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 freepdata;
/*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 video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
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;
freepdata:
kfree(pdata);
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;
/*Unegister ISP subdev*/
sunxi_isp_unregister_subdev(dev->isp_sd);
sunxi_isp_put_subdev(&dev->isp_sd, dev->id);
/*Unegister CSI subdev*/
sunxi_csi_unregister_subdev(dev->csi_sd);
sunxi_csi_put_subdev(&dev->csi_sd, dev->id);
/*Unegister MIPI subdev*/
sunxi_mipi_unregister_subdev(dev->mipi_sd);
sunxi_mipi_put_subdev(&dev->mipi_sd, dev->id);
/*Unegister flash subdev*/
sunxi_flash_unregister_subdev(dev->flash_sd);
sunxi_flash_put_subdev(&dev->flash_sd, dev->id);
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->vip_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);
v4l2_info(&dev->v4l2_dev, "unregistering %s\n", video_device_node_name(dev->vfd));
video_unregister_device(dev->vfd);
v4l2_device_unregister(&dev->v4l2_dev);
v4l2_ctrl_handler_free(&dev->ctrl_handler);
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
vfe_print("vfe_remove ok!\n");
return 0;
}
static void vfe_suspend_helper(struct vfe_dev *dev)
{
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");