4842 lines
143 KiB
C
Executable File
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");
|