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