㉓AW-H3 Linux驱动开发之mipi camera(CSI)驱动程序

本次说一下mipi camera的驱动开发,平台用的是全志的H3芯片,项目代号:sun8iw7p1,这次使用运行在H3上面的Ubuntu进行验证的.

Linux代码:https://github.com/orangepi-xunlong/orangepi_h3_linux

Ubuntu镜像:https://pan.baidu.com/s/1kUF3Dx9

MIPI: MIPI联盟由ARM、诺基亚、意法半导体和德州仪器发起成立,作为移动行业领导者的合作组织,MIPI目的是把手机内部的接口如摄像头、显示屏接口、射频/基带接口等标准化,从而减少手机设计的复杂程度和增加设计灵活性。MIPI联盟下面有不同的WorkGroup ,分别定义了一系列的手机内部接口标准,比如摄像头接口CSI、显示接口DSI、射频接口DigRF、麦克风/喇叭接口SLIMbus等。统一接口标准的好处是手机厂商根据需要可以从市面上灵活选择不同的芯片和模组,更改设计和功能时更加快捷方便。

CSI: CMOS Sensor Interface,串行相机接口,CSI接口通常从CMOS Sensor、Video Encoder和其它视频输出设备收集数据。

CSI-1
相机最初的标准MIPI接口。它作为一种架构来定义相机和主处理器之间的接口。其继任者是MIPI CSI-2和MIPI CSI-3,两个标准仍在不断发展。
CSI-2
规范于2005年发布。它使用D-PHY或C-PHY(这两个标准均由MIPI联盟设定)作为物理层选项。该协议分为以下几个层次:
物理层(C-PHY / D-PHY)
车道合并层
低层协议层
像素到字节转换层
应用程序层
CSI-3
高速双向协议,主要用于多层对等基于UniPro的M-PHY设备网络中摄像机和主机之间的图像和视频传输。它最初于2012年发布,并于2014年在1.1版本中重新发布。

本次使用的是上海格科微的GC2035的camera,2M pixel(1616V x 1232H),框架如下:

分为两部分一部分是CSI控制器,一部分是CCI控制器,其中CCI控制器,我们这里使用其I2C部分,其它用的都是CSI部分.

我们先来了解一下camera的一些基础知识(参考:https://blog.csdn.net/radianceblau/article/details/76460907):

曝光三要素

光圈大小: 光圈就是指曝光瞬间开孔多大;
快门速度: 快门速度就是指快门开启的时间;
感光度(ISO): 感光度是指图像传感器对光线的敏感程度。

1.光圈

光圈是一个用来控制光线透过镜头,进入机身内感光面光量的装置,它通常是在镜头内。表达光圈大小我们是用f值。对于已经制造好的镜头,我们不可能随意改变镜头的直径,但是我们可以通过在镜头内部加入多边形或者圆形,并且面积可变的孔状光栅来达到控制镜头通光量,这个装置就叫做光圈。
光圈F值 = 镜头的焦距 / 镜头光圈的直径
完整的光圈值系列如下:

1/1.0,1/1.4,1/2.0,1/2.8,1/4.0,1/5.6,1/8.0,1/11,1/16,1/22,1/32,1/44,1/64

光圈的档位设计是相邻的两档的数值相差1.4倍(2的平方根1.414的近似值)相邻的两档之间,透光孔直径相差根号2倍,透光孔的面积相差一倍,底片上形成的影像的亮度相差一倍,维持相同曝光量所需要的时间相差一倍。

当光圈开得越大;背景虚化效果越明显,小光圈背景虚化效果则越不明显。在拍摄时,背景越虚化,主体突出就越明显,相对于一些人像、静物运用比较多。如果你需要烘托环境,不希望背景虚化到什么都看不清,那么采用小光圈,大景深就更适合了。

2、快门速度:

用时间表示。30s,15s,8s,4s,2s,1s,1/2  1/4  1/8   1/15   1/30  1/60    1/125    1/250   1/500   1/1000    1/2000.......
同样相邻的快门速度相差一档曝光量。很明显,数值上相差一倍。

简单的说就是:当你改变快门速度的同时也意味着改变了运动物体成像的方式(是否叠加)。快门速度越慢,运动物体越模糊;速度越快,运动物体越清晰。 

3、ISO感光度:

国标表示ISO100   200    400   800  1600   3200   6400....
同样相邻的ISO相差一档曝光量。很明显,数值上成2的级数。
ISO越大噪点越明显,画质越差;但ISO提高可以使用小光圈或提高快门速度,有利于摄影师意图的实现。

摄像头构成

1,镜头:           就是凸透镜用来成像
2,对焦马达:    用来调整焦距
3,红外滤光片: 用来滤除红外光,调整光线摄入角度为直角,来达到更好的成像效果。
4,影像传感器: 核心器件,用来将光学图像转化为数字化的图片raw data。

图像传感器

图像传感器是将光信号转换为电信号的装置.

自动聚焦

自动聚焦目的是获得清晰度更高得图像。常用的聚焦方法分两类,传统的聚焦方法和基于数字图像处理方式的图像聚焦方法。传统的方式中,自动聚焦通过红外线或者超生波测距的方式来实现。数字处理方法中,自动聚焦的关键在于构造图像的清晰度评价函数。己经提出的图像清晰度评价函数苞括灰度方差、梯度能量、嫡函数和一些频域函数法。图像清晰度评价函数必须具有良好的单峰性和尖锐性,而且要计算量适度,从而可以快速的实现精准对焦。 

白平衡

白平衡:字面上的理解是白色的平衡。用色彩学的知识解释,白色是指反射到人眼中的光线由于蓝、绿、红三种色光比例相同且具有一定的亮度所形成的视觉反应。摄像头并不能像人眼那样具有适应性,所以如果摄像机的色彩调整同景物照明的色温不一致就会发生偏色。白平衡就是针对不同色温条件下,通过调摄像头内部的色彩电路使拍摄出来的影像抵消偏色,更接近人眼的视觉习惯。白平衡也可以简单地理解为在任意色温条件下,摄像头所拍摄的标准白色经过电路的调整,使之成像后仍然为白色。

自动白平衡

白平衡电路自动调节红、绿、蓝增益,使得白色物体无论在任何光源下都呈白色。
手动模式:红、绿、蓝增益由手动控制。
自动模式:红、绿、蓝增益由自动白平衡电路控制。

电子快门

摄像机基本都是电子快门,概念和传统照相机中快门的概念有些出入,但基本原理都是一样的,摄像机是一秒有多少个画面,相机中的快门也是类似的.

我们来看一下板子的电路图:

结合电路图,我们看一下全志的dataset(CSI和CCI两部分):

上图是CSI的框架图,下图是CCI的框架图:

CCI这里就不说了,I2C,大家的基础,这里说说CSI,我们来看一下CSI的时序图:

从上图的figure 6-3我们可以知道,全志平台的CSI控制器支持8/10/12 bit的Sensor,我们这里是使用的8 bit,因为摄像头模组gc2035是8 bit的,下面看一下CSI寄存器的列表,具体配置列表这里就不列出来了,只列一个列表:

驱动代码我们用的是全志官方提供的linux-3.4.113,主要涉及到v4l2和具体Sensor的相关数据及提供给v4l2使用的API的实现.这部分代码在:linux-3.4.113/drivers/media/video/sunxi-vfe,这里代码编译后会生成一些.ko的文件,其中vfe_v4l2.ko和gc2035.ko就是我们要用的,vfe_v4l2.ko是v4l2的相关代码,gc2035.ko是具体Sensor的实现代码,下面我们通过Makefile文件来看这两个.ko文件的组成:

obj-$(CONFIG_CSI_VFE) += vfe_os.o
obj-$(CONFIG_CSI_VFE) += vfe_subdev.o
obj-$(CONFIG_CSI_VFE) += device/
obj-$(CONFIG_CSI_VFE) += actuator/
obj-$(CONFIG_CSI_VFE) += csi_cci/
#obj-$(CONFIG_CSI_VFE) += flash_light/
#obj-$(CONFIG_CSI_VFE) += camera_detector/

obj-$(CONFIG_CSI_VFE) += vfe_v4l2.o
vfe_v4l2-y					:= csi/csi_reg.o
vfe_v4l2-y					+= csi/bsp_csi.o 
vfe_v4l2-y					+= bsp_common.o
vfe_v4l2-y					+= config.o
vfe_v4l2-y					+= utility/sensor_info.o
vfe_v4l2-y					+= utility/cfg_op.o
vfe_v4l2-y					+= vfe.o
vfe_v4l2-y					+= lib/libisp

ifneq ($(strip $(CONFIG_ARCH_SUN9I)),)
vfe_v4l2-y					+= lib/lib_mipicsi2_v2
else ifneq ($(strip $(CONFIG_ARCH_SUN8IW6)),)
obj-$(CONFIG_CSI_VFE) += vfe_v4l2.o
vfe_v4l2-y					+= mipi_csi/bsp_mipi_csi.o
else ifneq ($(strip $(CONFIG_ARCH_SUN8IW8)),)
obj-$(CONFIG_CSI_VFE) += vfe_v4l2.o
vfe_v4l2-y					+= mipi_csi/bsp_mipi_csi_v1.o \
							mipi_csi/protocol/protocol_reg.o \
							mipi_csi/dphy/dphy_reg.o
else
obj-$(CONFIG_CSI_VFE) += vfe_v4l2.o
vfe_v4l2-y					+= lib/lib_mipicsi2_v1
endif

可以看到vfe_v4l2.ko的组成文件是很多的,上面的这些个文件会编译到一起组成它,可见还是比较复杂的,其中vfe.c这个文件是入口. gc2035.ko这个文件则只有gc2035.c组成,这部分是需要我们去实现,不过格科微已经提供了,主要是实现camera寄存器的初始化,然后实现v4l2需要使用的一些API.

我们再看一下v4l2的框架:

在Android上,要稍微再复杂一点,所以这里用Linux + Ubuntu core来测试,不过这里贴一个Android上的架构,毕竟这个是目前比较流行的方式,架构如下:

下面把vfe.c的内容贴出来:

/*
 * 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/videobuf-dma-contig.h>

#include <linux/regulator/consumer.h>
#include <linux/ion_sunxi.h>

#include "vfe.h"

#include "bsp_common.h"
#include "lib/bsp_isp_algo.h"
#include "csi_cci/bsp_cci.h"
#include "csi_cci/cci_helper.h"
#include "config.h"
#include "device/camera_cfg.h"
#include "platform/vfe_resource.h"
#include "utility/sensor_info.h"
#include "utility/vfe_io.h"
#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 REG_DBG_EN

#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 FLASH_EN_POL 1
#define FLASH_MODE_POL 1

#define _FLASH_FUNC_
//#define _REGULATOR_CHANGE_

static struct flash_dev_info fl_info;

static char ccm[I2C_NAME_SIZE] = "";
static uint i2c_addr = 0xff;

static char act_name[I2C_NAME_SIZE] = "";
static uint act_slave = 0xff;
static uint define_sensor_list = 0xff;
static uint vfe_i2c_dbg = 0;
static uint vips = 0xffff;

static int touch_flash_flag = 0;
static int ev_cumul = 0;
static unsigned int isp_va_flag = 0;
static unsigned int isp_va_alloc_sel = 0;
static struct vfe_mm isp_load_mm;
static struct vfe_mm isp_saved_mm;

static unsigned int vfe_opened_num = 0;
unsigned int isp_reparse_flag = 0;

static unsigned int frame_cnt = 0;
static unsigned int vfe_dump = 0;
struct mutex probe_hdl_lock;

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(vips, uint, S_IRUGO | S_IWUSR);
static ssize_t vfe_dbg_en_show(struct device *dev,
                               struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "%d\n", vfe_dbg_en);
}

static ssize_t vfe_dbg_en_store(struct device *dev,
                                struct device_attribute *attr,
                                const char *buf, size_t count)
{
    int err;
    unsigned long val;

    err = strict_strtoul(buf, 10, &val);
    if (err)
    {
        vfe_print("Invalid size\n");
        return err;
    }

    if (val < 0 || val > 1)
    {
        vfe_print("Invalid value, 0~1 is expected!\n");
    }
    else
    {
        vfe_dbg_en = val;
        vfe_print("vfe_dbg_en = %ld\n", val);
    }

    return count;
}

static ssize_t vfe_dbg_lv_show(struct device *dev,
                               struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "%d\n", vfe_dbg_lv);
}

static ssize_t vfe_dbg_lv_store(struct device *dev,
                                struct device_attribute *attr,
                                const char *buf, size_t count)
{
    int err;
    unsigned long val;

    err = strict_strtoul(buf, 10, &val);
    if (err)
    {
        vfe_print("Invalid size\n");
        return err;
    }

    if (val < 0 || val > 4)
    {
        vfe_print("Invalid value, 0~3 is expected!\n");
    }
    else
    {
        vfe_dbg_lv = val;
        vfe_print("vfe_dbg_lv = %d\n", vfe_dbg_lv);
    }

    return count;
}

static ssize_t isp_reparse_flag_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "%d\n", isp_reparse_flag);
}

static ssize_t isp_reparse_flag_store(struct device *dev,
                                      struct device_attribute *attr,
                                      const char *buf, size_t count)
{
    int err;
    unsigned long val;

    err = strict_strtoul(buf, 10, &val);
    if (err)
    {
        vfe_print("Invalid size\n");
        return err;
    }

    if (val < 0 || val > 4)
    {
        vfe_print("Invalid value, 0~1 is expected!\n");
    }
    else
    {
        isp_reparse_flag = val;
        vfe_print("isp_reparse_flag = %ld\n", val);
    }
    return count;
}
static ssize_t vfe_dbg_dump_show(struct device *dev,
                                 struct device_attribute *attr, char *buf)
{
    return sprintf(buf, "%d\n", vfe_dump);
}

static ssize_t vfe_dbg_dump_store(struct device *dev,
                                  struct device_attribute *attr,
                                  const char *buf, size_t count)
{
    int err;
    unsigned long val;

    err = strict_strtoul(buf, 10, &val);
    if (err)
    {
        vfe_print("Invalid size\n");
        return err;
    }

    if (val < 0 || val > 3)
    {
        vfe_print("Invalid value, 0~3 is expected!\n");
    }
    else
    {
        vfe_dump = val;
        vfe_print("vfe_dump = %ld\n", val);
    }
    return count;
}

static DEVICE_ATTR(vfe_dbg_en, S_IRUGO | S_IWUSR | S_IWGRP,
                   vfe_dbg_en_show, vfe_dbg_en_store);

static DEVICE_ATTR(vfe_dbg_lv, S_IRUGO | S_IWUSR | S_IWGRP,
                   vfe_dbg_lv_show, vfe_dbg_lv_store);
static DEVICE_ATTR(vfe_dump, S_IRUGO | S_IWUSR | S_IWGRP,
                   vfe_dbg_dump_show, vfe_dbg_dump_store);
static DEVICE_ATTR(isp_reparse_flag, S_IRUGO | S_IWUSR | S_IWGRP,
                   isp_reparse_flag_show, isp_reparse_flag_store);

static struct attribute *vfe_attributes[] = {
    &dev_attr_vfe_dbg_en.attr,
    &dev_attr_vfe_dbg_lv.attr,
    &dev_attr_vfe_dump.attr,
    &dev_attr_isp_reparse_flag.attr,
    NULL};

static struct attribute_group vfe_attribute_group = {
    .name = "vfe_attr",
    .attrs = vfe_attributes};

static struct vfe_fmt formats[] = {
    {
        .name = "planar YUV 422",
        .fourcc = V4L2_PIX_FMT_YUV422P,
        .depth = 16,
        .planes_cnt = 3,
    },
    {
        .name = "planar YUV 420",
        .fourcc = V4L2_PIX_FMT_YUV420,
        .depth = 12,
        .planes_cnt = 3,
    },
    {
        .name = "planar YVU 420",
        .fourcc = V4L2_PIX_FMT_YVU420,
        .depth = 12,
        .planes_cnt = 3,
    },
    {
        .name = "planar YUV 422 UV combined",
        .fourcc = V4L2_PIX_FMT_NV16,
        .depth = 16,
        .planes_cnt = 2,
    },
    {
        .name = "planar YUV 420 UV combined",
        .fourcc = V4L2_PIX_FMT_NV12,
        .depth = 12,
        .planes_cnt = 2,
    },
    {
        .name = "planar YUV 422 VU combined",
        .fourcc = V4L2_PIX_FMT_NV61,
        .depth = 16,
        .planes_cnt = 2,
    },
    {
        .name = "planar YUV 420 VU combined",
        .fourcc = V4L2_PIX_FMT_NV21,
        .depth = 12,
        .planes_cnt = 2,
    },
    {
        .name = "MB YUV420",
        .fourcc = V4L2_PIX_FMT_HM12,
        .depth = 12,
        .planes_cnt = 2,
    },
    {
        .name = "YUV422 YUYV",
        .fourcc = V4L2_PIX_FMT_YUYV,
        .depth = 16,
        .planes_cnt = 1,
    },
    {
        .name = "YUV422 YVYU",
        .fourcc = V4L2_PIX_FMT_YVYU,
        .depth = 16,
        .planes_cnt = 1,
    },
    {
        .name = "YUV422 UYVY",
        .fourcc = V4L2_PIX_FMT_UYVY,
        .depth = 16,
        .planes_cnt = 1,
    },
    {
        .name = "YUV422 VYUY",
        .fourcc = V4L2_PIX_FMT_VYUY,
        .depth = 16,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer BGGR 8bit",
        .fourcc = V4L2_PIX_FMT_SBGGR8,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer GBRG 8bit",
        .fourcc = V4L2_PIX_FMT_SGBRG8,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer GRBG 8bit",
        .fourcc = V4L2_PIX_FMT_SGRBG8,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer RGGB 8bit",
        .fourcc = V4L2_PIX_FMT_SGRBG8,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer BGGR 10bit",
        .fourcc = V4L2_PIX_FMT_SBGGR10,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer GBRG 10bit",
        .fourcc = V4L2_PIX_FMT_SGBRG10,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer GRBG 10bit",
        .fourcc = V4L2_PIX_FMT_SGRBG10,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer RGGB 10bit",
        .fourcc = V4L2_PIX_FMT_SGRBG10,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer BGGR 12bit",
        .fourcc = V4L2_PIX_FMT_SBGGR12,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer GBRG 12bit",
        .fourcc = V4L2_PIX_FMT_SGBRG12,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer GRBG 12bit",
        .fourcc = V4L2_PIX_FMT_SGRBG12,
        .depth = 8,
        .planes_cnt = 1,
    },
    {
        .name = "RAW Bayer RGGB 12bit",
        .fourcc = V4L2_PIX_FMT_SGRBG12,
        .depth = 8,
        .planes_cnt = 1,
    },
};

static enum v4l2_mbus_pixelcode try_yuv422_bus[] = {
    V4L2_MBUS_FMT_UYVY10_20X1,
    V4L2_MBUS_FMT_UYVY8_16X1,

    V4L2_MBUS_FMT_YUYV10_2X10,
    V4L2_MBUS_FMT_YVYU10_2X10,
    V4L2_MBUS_FMT_UYVY8_2X8,
    V4L2_MBUS_FMT_VYUY8_2X8,
    V4L2_MBUS_FMT_YUYV8_2X8,
    V4L2_MBUS_FMT_YVYU8_2X8,

    V4L2_MBUS_FMT_YUYV10_1X20,
    V4L2_MBUS_FMT_YVYU10_1X20,
    V4L2_MBUS_FMT_UYVY8_1X16,
    V4L2_MBUS_FMT_VYUY8_1X16,
    V4L2_MBUS_FMT_YUYV8_1X16,
    V4L2_MBUS_FMT_YVYU8_1X16,

    V4L2_MBUS_FMT_YUV8_1X24,
};

#define N_TRY_YUV422 ARRAY_SIZE(try_yuv422_bus)

static enum v4l2_mbus_pixelcode try_yuv420_bus[] = {
    V4L2_MBUS_FMT_YY10_UYVY10_15X1,
    V4L2_MBUS_FMT_YY8_UYVY8_12X1,
};

#define N_TRY_YUV420 ARRAY_SIZE(try_yuv420_bus)

static enum v4l2_mbus_pixelcode try_bayer_rgb_bus[] = {
    V4L2_MBUS_FMT_SBGGR12_12X1,
    V4L2_MBUS_FMT_SGBRG12_12X1,
    V4L2_MBUS_FMT_SGRBG12_12X1,
    V4L2_MBUS_FMT_SRGGB12_12X1,
    V4L2_MBUS_FMT_SBGGR12_1X12,
    V4L2_MBUS_FMT_SGBRG12_1X12,
    V4L2_MBUS_FMT_SGRBG12_1X12,
    V4L2_MBUS_FMT_SRGGB12_1X12,
    V4L2_MBUS_FMT_SBGGR10_10X1,
    V4L2_MBUS_FMT_SGBRG10_10X1,
    V4L2_MBUS_FMT_SGRBG10_10X1,
    V4L2_MBUS_FMT_SRGGB10_10X1,
    V4L2_MBUS_FMT_SBGGR10_1X10,
    V4L2_MBUS_FMT_SGBRG10_1X10,
    V4L2_MBUS_FMT_SGRBG10_1X10,
    V4L2_MBUS_FMT_SRGGB10_1X10,
    V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
    V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
    V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
    V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
    V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE,
    V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE,
    V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE,
    V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE,
    V4L2_MBUS_FMT_SBGGR8_8X1,
    V4L2_MBUS_FMT_SGBRG8_8X1,
    V4L2_MBUS_FMT_SGRBG8_8X1,
    V4L2_MBUS_FMT_SRGGB8_8X1,
    V4L2_MBUS_FMT_SBGGR8_1X8,
    V4L2_MBUS_FMT_SGBRG8_1X8,
    V4L2_MBUS_FMT_SGRBG8_1X8,
    V4L2_MBUS_FMT_SRGGB8_1X8,
};

#define N_TRY_BAYER ARRAY_SIZE(try_bayer_rgb_bus)

static enum v4l2_mbus_pixelcode try_rgb565_bus[] = {
    V4L2_MBUS_FMT_RGB565_16X1,
    V4L2_MBUS_FMT_BGR565_2X8_BE,
    V4L2_MBUS_FMT_BGR565_2X8_LE,
    V4L2_MBUS_FMT_RGB565_2X8_BE,
    V4L2_MBUS_FMT_RGB565_2X8_LE,
};

#define N_TRY_RGB565 ARRAY_SIZE(try_rgb565_bus)

static enum v4l2_mbus_pixelcode try_rgb888_bus[] = {
    V4L2_MBUS_FMT_RGB888_24X1,
};

#define N_TRY_RGB888 ARRAY_SIZE(try_rgb888_bus)
/*
static int V4L2_AF_STATUS_TBL[] = 
{
	V4L2_AUTO_FOCUS_STATUS_IDLE,  //AUTO_FOCUS_STATUS_IDLE
	V4L2_AUTO_FOCUS_STATUS_BUSY,  //AUTO_FOCUS_STATUS_BUSY
	V4L2_AUTO_FOCUS_STATUS_REACHED,  //AUTO_FOCUS_STATUS_REACHED
	V4L2_AUTO_FOCUS_STATUS_REACHED,  //AUTO_FOCUS_STATUS_APPROCH
	V4L2_AUTO_FOCUS_STATUS_BUSY,  //AUTO_FOCUS_STATUS_REFOCUS
	V4L2_AUTO_FOCUS_STATUS_REACHED,  //AUTO_FOCUS_STATUS_FINDED
	V4L2_AUTO_FOCUS_STATUS_FAILED,  //AUTO_FOCUS_STATUS_FAILED
};
*/
static int isp_resource_request(struct vfe_dev *dev)
{
    unsigned int isp_used_flag = 0, i;
    void *pa_base, *va_base, *dma_base;
    int ret;

    //requeset for isp table and statistic buffer
    for (i = 0; i < dev->dev_qty; i++)
    {
        if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw)
        {
            dev->isp_lut_tbl_buf_mm[i].size = ISP_LINEAR_LUT_LENS_GAMMA_MEM_SIZE;
            ret = os_mem_alloc(&dev->isp_lut_tbl_buf_mm[i]);
            if (!ret)
            {
                pa_base = dev->isp_lut_tbl_buf_mm[i].phy_addr;
                va_base = dev->isp_lut_tbl_buf_mm[i].vir_addr;
                dma_base = dev->isp_lut_tbl_buf_mm[i].dma_addr;
                dev->isp_tbl_addr[i].isp_def_lut_tbl_paddr = (void *)(pa_base + ISP_LUT_MEM_OFS);
                dev->isp_tbl_addr[i].isp_def_lut_tbl_dma_addr = (void *)(dma_base + ISP_LUT_MEM_OFS);
                dev->isp_tbl_addr[i].isp_def_lut_tbl_vaddr = (void *)(va_base + ISP_LUT_MEM_OFS);
                dev->isp_tbl_addr[i].isp_lsc_tbl_paddr = (void *)(pa_base + ISP_LENS_MEM_OFS);
                dev->isp_tbl_addr[i].isp_lsc_tbl_dma_addr = (void *)(dma_base + ISP_LENS_MEM_OFS);
                dev->isp_tbl_addr[i].isp_lsc_tbl_vaddr = (void *)(va_base + ISP_LENS_MEM_OFS);
                dev->isp_tbl_addr[i].isp_gamma_tbl_paddr = (void *)(pa_base + ISP_GAMMA_MEM_OFS);
                dev->isp_tbl_addr[i].isp_gamma_tbl_dma_addr = (void *)(dma_base + ISP_GAMMA_MEM_OFS);
                dev->isp_tbl_addr[i].isp_gamma_tbl_vaddr = (void *)(va_base + ISP_GAMMA_MEM_OFS);

                dev->isp_tbl_addr[i].isp_linear_tbl_paddr = (void *)(pa_base + ISP_LINEAR_MEM_OFS);
                dev->isp_tbl_addr[i].isp_linear_tbl_dma_addr = (void *)(dma_base + ISP_LINEAR_MEM_OFS);
                dev->isp_tbl_addr[i].isp_linear_tbl_vaddr = (void *)(va_base + ISP_LINEAR_MEM_OFS);
                vfe_dbg(0, "isp_def_lut_tbl_vaddr[%d] = %p\n", i, dev->isp_tbl_addr[i].isp_def_lut_tbl_vaddr);
                vfe_dbg(0, "isp_lsc_tbl_vaddr[%d] = %p\n", i, dev->isp_tbl_addr[i].isp_lsc_tbl_vaddr);
                vfe_dbg(0, "isp_gamma_tbl_vaddr[%d] = %p\n", i, dev->isp_tbl_addr[i].isp_gamma_tbl_vaddr);
            }
            else
            {
                vfe_err("isp lut_lens_gamma table request pa failed!\n");
                return -ENOMEM;
            }
        }

        if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw)
        {
            dev->isp_drc_tbl_buf_mm[i].size = ISP_DRC_DISC_MEM_SIZE;
            ret = os_mem_alloc(&dev->isp_drc_tbl_buf_mm[i]);
            if (!ret)
            {
                pa_base = dev->isp_drc_tbl_buf_mm[i].phy_addr;
                va_base = dev->isp_drc_tbl_buf_mm[i].vir_addr;
                dma_base = dev->isp_drc_tbl_buf_mm[i].dma_addr;

                dev->isp_tbl_addr[i].isp_drc_tbl_paddr = (void *)(pa_base + ISP_DRC_MEM_OFS);
                dev->isp_tbl_addr[i].isp_drc_tbl_dma_addr = (void *)(dma_base + ISP_DRC_MEM_OFS);
                dev->isp_tbl_addr[i].isp_drc_tbl_vaddr = (void *)(va_base + ISP_DRC_MEM_OFS);

                dev->isp_tbl_addr[i].isp_disc_tbl_paddr = (void *)(pa_base + ISP_DISC_MEM_OFS);
                dev->isp_tbl_addr[i].isp_disc_tbl_dma_addr = (void *)(dma_base + ISP_DISC_MEM_OFS);
                dev->isp_tbl_addr[i].isp_disc_tbl_vaddr = (void *)(va_base + ISP_DISC_MEM_OFS);

                vfe_dbg(0, "isp_drc_tbl_vaddr[%d] = %p\n", i, dev->isp_tbl_addr[i].isp_drc_tbl_vaddr);
            }
            else
            {
                vfe_err("isp drc table request pa failed!\n");
                return -ENOMEM;
            }
        }
    }

    for (i = 0; i < dev->dev_qty; i++)
    {
        if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw)
        {
            isp_used_flag = 1;
            break;
        }
    }

    if (isp_used_flag)
    {
        for (i = 0; i < MAX_ISP_STAT_BUF; i++)
        {
            dev->isp_stat_buf_mm[i].size = ISP_STAT_TOTAL_SIZE;
            ret = os_mem_alloc(&dev->isp_stat_buf_mm[i]);
            if (!ret)
            {
                pa_base = dev->isp_stat_buf_mm[i].phy_addr;
                va_base = dev->isp_stat_buf_mm[i].vir_addr;
                dma_base = dev->isp_stat_buf_mm[i].dma_addr;
                INIT_LIST_HEAD(&dev->isp_stat_bq.isp_stat[i].queue);
                dev->isp_stat_bq.isp_stat[i].id = i;
                dev->isp_stat_bq.isp_stat[i].paddr = (void *)(pa_base);
                dev->isp_stat_bq.isp_stat[i].dma_addr = (void *)(dma_base);
                dev->isp_stat_bq.isp_stat[i].isp_stat_buf.stat_buf = (void *)(va_base);
                dev->isp_stat_bq.isp_stat[i].isp_stat_buf.buf_size = ISP_STAT_TOTAL_SIZE;
                dev->isp_stat_bq.isp_stat[i].isp_stat_buf.buf_status = BUF_IDLE;
                vfe_dbg(0, "dev->isp_stat_bq.isp_stat[i].isp_stat_buf.stat_buf[%d] = %p\n", i, dev->isp_stat_bq.isp_stat[i].isp_stat_buf.stat_buf);
            }
            else
            {
                vfe_err("isp statistic buffer request pa failed!\n");
                return -ENOMEM;
            }
        }
    }
    return 0;
}
static int vfe_device_regulator_get(struct ccm_config *ccm_cfg);
static int vfe_device_regulator_put(struct ccm_config *ccm_cfg);
static int vfe_set_sensor_power_on(struct vfe_dev *dev);
static int vfe_set_sensor_power_off(struct vfe_dev *dev);

static void isp_resource_release(struct vfe_dev *dev)
{
    unsigned int isp_used_flag = 0, i;

    //release isp table and statistic buffer
    for (i = 0; i < dev->dev_qty; i++)
    {
        if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw)
        {
            os_mem_free(&dev->isp_lut_tbl_buf_mm[i]);
            os_mem_free(&dev->isp_drc_tbl_buf_mm[i]);
        }
    }

    for (i = 0; i < dev->dev_qty; i++)
    {
        if (dev->ccm_cfg[i]->is_isp_used && dev->ccm_cfg[i]->is_bayer_raw)
        {
            isp_used_flag = 1;
            break;
        }
    }

    if (isp_used_flag)
    {
        for (i = 0; i < MAX_ISP_STAT_BUF; i++)
        {
            os_mem_free(&dev->isp_stat_buf_mm[i]);
        }
    }
}

static int vfe_clk_get(struct vfe_dev *dev)
{
#ifdef VFE_CLK
    int ret;
    //Get Core clk!
    if (VFE_CORE_CLK)
    {
        dev->clock.vfe_core_clk = os_clk_get(NULL, VFE_CORE_CLK);
        if (VFE_CORE_CLK_SRC)
        {
            dev->clock.vfe_core_clk_src = os_clk_get(NULL, VFE_CORE_CLK_SRC);
            if (dev->clock.vfe_core_clk && dev->clock.vfe_core_clk_src)
            {
                ret = os_clk_set_parent(dev->clock.vfe_core_clk, dev->clock.vfe_core_clk_src);
                if (ret != 0)
                {
                    vfe_err(" vfe core clock set parent failed \n");
                    return -1;
                }
            }
        }
        os_clk_set_rate(dev->clock.vfe_core_clk, VFE_CORE_CLK_RATE);
        vfe_dbg(0, "vfe core clk = %ld\n", clk_get_rate(dev->clock.vfe_core_clk));
    }
    //Get Master clk!
    if (dev->vip_sel == 0) {
        if (VFE_MASTER_CLK0) {
            dev->clock.vfe_master_clk = os_clk_get(NULL, VFE_MASTER_CLK0);
        }
    }
    else if (dev->vip_sel == 1) {
        if (VFE_MASTER_CLK1) {
            dev->clock.vfe_master_clk = os_clk_get(NULL, VFE_MASTER_CLK1);
        }
    }

    if (VFE_MASTER_CLK_24M_SRC) {
        dev->clock.vfe_master_clk_24M_src = os_clk_get(NULL, VFE_MASTER_CLK_24M_SRC);
    }
    
    if (VFE_MASTER_CLK_PLL_SRC) {
        dev->clock.vfe_master_clk_pll_src = os_clk_get(NULL, VFE_MASTER_CLK_PLL_SRC);
    }

    if (!dev->clock.vfe_master_clk || !dev->clock.vfe_core_clk) {
        vfe_err("vfe core clk or vfe master clk is NULL!\n");
        ret = -1;
    }

    //Get MIPI clk!
    if (VFE_MIPI_DPHY_CLK) {
        dev->clock.vfe_dphy_clk = os_clk_get(NULL, VFE_MIPI_DPHY_CLK);
        if (VFE_MIPI_DPHY_CLK_SRC) {
            dev->clock.vfe_dphy_clk_src = os_clk_get(NULL, VFE_MIPI_DPHY_CLK_SRC);
        }
    }

    if (VFE_MIPI_CSI_CLK) {
        dev->clock.vfe_mipi_csi_clk = os_clk_get(NULL, VFE_MIPI_CSI_CLK);
    }
#ifdef VFE_MISC_CLK
    dev->clock.vfe_misc_clk = os_clk_get(NULL, VFE_MISC_CLK);
#endif

#endif
    return 0;
}

static int vfe_dphy_clk_set(struct vfe_dev *dev, unsigned long freq)
{
#ifdef VFE_CLK
    if (VFE_MIPI_DPHY_CLK_SRC) {
        if (dev->clock.vfe_dphy_clk && dev->clock.vfe_dphy_clk_src) {
            if (os_clk_set_parent(dev->clock.vfe_dphy_clk, dev->clock.vfe_dphy_clk_src)) {
                vfe_err("set vfe dphy clock source failed \n");
                return -1;
            }
        }
        else {
            vfe_err("vfe dphy clock is null\n");
            return -1;
        }
    }

    if (VFE_MIPI_DPHY_CLK) {
        if (dev->clock.vfe_dphy_clk) {
            if (os_clk_set_rate(dev->clock.vfe_dphy_clk, freq)) {
                vfe_err("set vip%d dphy clock error\n", dev->vip_sel);
                return -1;
            }
        }
        else {
            vfe_err("vfe master clock is null\n");
            return -1;
        }
    }
#endif
    return 0;
}

static int vfe_clk_enable(struct vfe_dev *dev)
{
    int ret = 0;
#ifdef VFE_CLK
    if (dev->clock.vfe_core_clk) {
        if (os_clk_prepare_enable(dev->clock.vfe_core_clk))
        {
            vfe_err("vfe core clock enable error\n");
            ret = -1;
        }
    }
    else {
        vfe_err("vfe core clock is null\n");
        ret = -1;
    }

    //
    if (dev->clock.vfe_dphy_clk) {
        if (os_clk_prepare_enable(dev->clock.vfe_dphy_clk))
        {
            vfe_err("vfe dphy clock enable error\n");
            ret = -1;
        }
    }
    else {
        vfe_dbg(0, "vfe dphy clock is null\n");
        ret = -1;
    }

    if (dev->clock.vfe_mipi_csi_clk) {
        if (os_clk_prepare_enable(dev->clock.vfe_mipi_csi_clk))
        {
            vfe_err("vfe mipi csi clock enable error\n");
            ret = -1;
        }
    }
    else {
        vfe_dbg(0, "vfe mipi csi clock  is null\n");
        ret = -1;
    }

    if (dev->clock.vfe_misc_clk) {
        if (os_clk_prepare_enable(dev->clock.vfe_misc_clk))
        {
            vfe_err("vfe misc clock enable error\n");
            ret = -1;
        }
    }
    else {
        vfe_dbg(0, "vfe misc clock is null\n");
        ret = -1;
    }
#endif
    return ret;
}

static void vfe_clk_disable(struct vfe_dev *dev)
{
#ifdef VFE_CLK
    if (dev->clock.vfe_core_clk)
        os_clk_disable_unprepare(dev->clock.vfe_core_clk);
    else
        vfe_dbg(0, "vfe core clock is null\n");

    if (dev->clock.vfe_dphy_clk)
        os_clk_disable_unprepare(dev->clock.vfe_dphy_clk);
    else
        vfe_dbg(0, "vfe dphy clock is null\n");

    if (dev->clock.vfe_misc_clk)
        os_clk_disable_unprepare(dev->clock.vfe_misc_clk);
    else
        vfe_dbg(0, "vfe dphy clock is null\n");

    if (dev->clock.vfe_mipi_csi_clk)
        os_clk_disable_unprepare(dev->clock.vfe_mipi_csi_clk);
    else
        vfe_dbg(0, "vfe mipi csi clock is null\n");
#endif
}

static void vfe_clk_release(struct vfe_dev *dev)
{
#ifdef VFE_CLK
    if (dev->clock.vfe_core_clk)
        os_clk_put(dev->clock.vfe_core_clk);
    else
        vfe_err("vfe core clock is null\n");

    if (dev->clock.vfe_master_clk)
        os_clk_put(dev->clock.vfe_master_clk);
    else
        vfe_err("vip%d master clock is null\n", dev->vip_sel);

    if (dev->clock.vfe_dphy_clk)
        os_clk_put(dev->clock.vfe_dphy_clk);
    else
        vfe_err("vfe dphy clock is null\n");

    if (dev->clock.vfe_mipi_csi_clk)
        os_clk_put(dev->clock.vfe_mipi_csi_clk);
    else
        vfe_warn("vfe mipi csi clock is null\n");

    if (dev->clock.vfe_core_clk_src)
        os_clk_put(dev->clock.vfe_core_clk_src);
    else
        vfe_warn("vfe core clock source is null\n");

    if (dev->clock.vfe_master_clk_24M_src)
        os_clk_put(dev->clock.vfe_master_clk_24M_src);
    else
        vfe_err("vfe master clock 24M source is null\n");

    if (dev->clock.vfe_master_clk_pll_src)
        os_clk_put(dev->clock.vfe_master_clk_pll_src);
    else
        vfe_warn("vfe master clock pll source is null\n");

    if (dev->clock.vfe_dphy_clk_src)
        os_clk_put(dev->clock.vfe_dphy_clk_src);
    else
        vfe_warn("vfe dphy clock source is null\n");
#endif
}

static void vfe_reset_enable(struct vfe_dev *dev)
{
    os_clk_reset_assert(dev->clock.vfe_core_clk);
    //  os_clk_reset_assert(dev->clock.vfe_ahb_clk);
}

static void vfe_reset_disable(struct vfe_dev *dev)
{
    os_clk_reset_deassert(dev->clock.vfe_core_clk);
    //  os_clk_reset_deassert(dev->clock.vfe_ahb_clk);
}

static int inline vfe_is_generating(struct vfe_dev *dev)
{
    return test_bit(0, &dev->generating);
}

static void inline vfe_start_generating(struct vfe_dev *dev)
{
    set_bit(0, &dev->generating);
    return;
}

static void inline vfe_stop_generating(struct vfe_dev *dev)
{
    dev->first_flag = 0;
    clear_bit(0, &dev->generating);
    return;
}

static int vfe_is_opened(struct vfe_dev *dev)
{
    int ret;
    mutex_lock(&dev->opened_lock);
    ret = test_bit(0, &dev->opened);
    mutex_unlock(&dev->opened_lock);
    return ret;
}

static void vfe_start_opened(struct vfe_dev *dev)
{
    mutex_lock(&dev->opened_lock);
    set_bit(0, &dev->opened);
    mutex_unlock(&dev->opened_lock);
}

static void vfe_stop_opened(struct vfe_dev *dev)
{
    mutex_lock(&dev->opened_lock);
    clear_bit(0, &dev->opened);
    mutex_unlock(&dev->opened_lock);
}

static void update_ccm_info(struct vfe_dev *dev, struct ccm_config *ccm_cfg)
{
    dev->sd = ccm_cfg->sd;
    dev->sd_act = ccm_cfg->sd_act;
    dev->ctrl_para.vflip = ccm_cfg->vflip;
    dev->ctrl_para.hflip = ccm_cfg->hflip;
    dev->ctrl_para.vflip_thumb = ccm_cfg->vflip_thumb;
    dev->ctrl_para.hflip_thumb = ccm_cfg->hflip_thumb;
    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 vflip = %d hflip = %d\n", dev->ctrl_para.vflip, dev->ctrl_para.hflip);
    vfe_dbg(0, "module vflip_thumb = %d hflip_thumb = %d\n", dev->ctrl_para.vflip_thumb, dev->ctrl_para.hflip_thumb);
    vfe_dbg(0, "module is_isp_used = %d is_bayer_raw= %d\n", dev->is_isp_used, dev->is_bayer_raw);
}

static void update_isp_setting(struct vfe_dev *dev)
{
    dev->isp_3a_result_pt = &dev->isp_3a_result[dev->input];
    dev->isp_gen_set_pt = &dev->isp_gen_set[dev->input];
    dev->isp_gen_set_pt->module_cfg.isp_platform_id = dev->platform_id;
    if (dev->is_bayer_raw)
    {
        mutex_init(&dev->isp_3a_result_mutex);
        dev->isp_gen_set_pt->module_cfg.lut_src0_table = dev->isp_tbl_addr[dev->input].isp_def_lut_tbl_vaddr;
        dev->isp_gen_set_pt->module_cfg.gamma_table = dev->isp_tbl_addr[dev->input].isp_gamma_tbl_vaddr;
        dev->isp_gen_set_pt->module_cfg.lens_table = dev->isp_tbl_addr[dev->input].isp_lsc_tbl_vaddr;
        dev->isp_gen_set_pt->module_cfg.linear_table = dev->isp_tbl_addr[dev->input].isp_linear_tbl_vaddr;
        dev->isp_gen_set_pt->module_cfg.disc_table = dev->isp_tbl_addr[dev->input].isp_disc_tbl_vaddr;
        bsp_isp_update_lut_lens_gamma_table(&dev->isp_tbl_addr[dev->input]);
    }
    dev->isp_gen_set_pt->module_cfg.drc_table = dev->isp_tbl_addr[dev->input].isp_drc_tbl_vaddr;
    bsp_isp_update_drc_table(&dev->isp_tbl_addr[dev->input]);
}

static int get_mbus_config(struct vfe_dev *dev, struct v4l2_mbus_config *mbus_config)
{
    int ret;

    ret = v4l2_subdev_call(dev->sd, video, g_mbus_config, mbus_config);
    if (ret < 0)
    {
        vfe_err("v4l2 sub device g_mbus_config error!\n");
        return -EFAULT;
    }

    return 0;
};
//static int isp_addr_curr = 0;
//static int isp_addr_pst = 0;
static inline void vfe_set_addr(struct vfe_dev *dev, struct vfe_buffer *buffer)
{
    struct vfe_buffer *buf = buffer;
    dma_addr_t addr_org;
    struct videobuf_buffer *vb_buf = &buf->vb;
    if (vb_buf == NULL || vb_buf->priv == NULL)
    {
        vfe_err("videobuf_buffer->priv is NULL!\n");
        return;
    }
    //vfe_dbg(3,"buf ptr=%p\n",buf);
    addr_org = videobuf_to_dma_contig(vb_buf) - CPU_DRAM_PADDR_ORG + HW_DMA_OFFSET;
    //isp_addr_curr = vfe_reg_readl((volatile void __iomem*)(0xf1cb8210));
    //if(isp_addr_pst != isp_addr_curr)
    //{
    //	vfe_warn("isp_addr_pst = %d, isp_addr_curr = %d.......\n", isp_addr_pst, isp_addr_curr);
    //}
    //isp_addr_pst = addr_org /4;
    if (dev->is_isp_used)
    {
        bsp_isp_set_output_addr(addr_org);
    }
    else
    {
        bsp_csi_set_addr(dev->vip_sel, addr_org);
    }
    buf->image_quality = dev->isp_3a_result_pt->image_quality.dwval;
    vfe_dbg(3, "csi_buf_addr_orginal=%x\n", addr_org);
}

static unsigned int common_af_status_to_v4l2(enum auto_focus_status af_status)
{
    switch (af_status)
    {
    case AUTO_FOCUS_STATUS_IDLE:
        return V4L2_AUTO_FOCUS_STATUS_IDLE;
    case AUTO_FOCUS_STATUS_BUSY:
        return V4L2_AUTO_FOCUS_STATUS_BUSY;
    case AUTO_FOCUS_STATUS_APPROCH:
        return V4L2_AUTO_FOCUS_STATUS_BUSY;
    case AUTO_FOCUS_STATUS_FINDED:
        return V4L2_AUTO_FOCUS_STATUS_REACHED;
    case AUTO_FOCUS_STATUS_FAILED:
        return V4L2_AUTO_FOCUS_STATUS_FAILED;
    case AUTO_FOCUS_STATUS_REFOCUS:
        return V4L2_AUTO_FOCUS_STATUS_BUSY;
    default:
        return V4L2_AUTO_FOCUS_STATUS_IDLE;
    }
}
static void vfe_dump_csi_regs(struct vfe_dev *dev)
{
    int i = 0;
    if (vfe_dump & DUMP_CSI)
    {
        if (5 == frame_cnt % 10)
        {
            printk("Vfe dump CSI regs :\n");
            for (i = 0; i < 0xb0; i = i + 4)
            {
                if (i % 0x10 == 0)
                    printk("0x%08x:    ", i);
                printk("0x%08x, ", vfe_reg_readl(dev->regs.csi_regs + i));
                if (i % 0x10 == 0xc)
                    printk("\n");
            }
        }
    }
}
static void vfe_dump_isp_regs(struct vfe_dev *dev)
{
    int i = 0;
    if (vfe_dump & DUMP_ISP)
    {
        if (9 == (frame_cnt % 10))
        {
            printk("Vfe dump ISP regs :\n");
            for (i = 0; i < 0x40; i = i + 4)
            {
                if (i % 0x10 == 0)
                    printk("0x%08x:  ", i);
                printk("0x%08x, ", vfe_reg_readl(dev->regs.isp_regs + i));
                if (i % 0x10 == 0xc)
                    printk("\n");
            }
            for (i = 0x40; i < 0x240; i = i + 4)
            {
                if (i % 0x10 == 0)
                    printk("0x%08x:  ", i);
                printk("0x%08x, ", vfe_reg_readl(dev->regs.isp_load_regs + i));
                if (i % 0x10 == 0xc)
                    printk("\n");
            }
        }
    }
}

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;
    vfe_dump_isp_regs(dev);
    if (dev->is_bayer_raw)
    {
        mutex_lock(&dev->isp_3a_result_mutex);
        if (1 == isp_reparse_flag)
        {
            vfe_print("ISP reparse ini file!\n");
            if (read_ini_info(dev, dev->input))
            {
                vfe_warn("ISP reparse ini fail, please check isp config!\n");
                goto ISP_REPARSE_END;
            }
            isp_param_init(dev->isp_gen_set_pt);
            isp_config_init(dev->isp_gen_set_pt);
            isp_module_init(dev->isp_gen_set_pt, dev->isp_3a_result_pt);
        ISP_REPARSE_END:
            isp_reparse_flag = 0;
        }
        if (2 == isp_reparse_flag)
        {
            vfe_reg_set(IO_ADDRESS(ISP_REGS_BASE + 0x10), (1 << 20));
        }
        if (3 == isp_reparse_flag)
        {
            vfe_reg_clr_set(IO_ADDRESS(ISP_REGS_BASE + 0x10), (0xF << 16), (1 << 16));
            vfe_reg_set(IO_ADDRESS(ISP_REGS_BASE + 0x10), (1 << 20));
        }
        if (4 == isp_reparse_flag)
        {
            //vfe_reg_clr_set(IO_ADDRESS(ISP_REGS_BASE+0x10), (0xF << 16), (1 << 16));
            vfe_reg_clr(IO_ADDRESS(ISP_REGS_BASE + 0x10), (1 << 20));
            vfe_reg_clr(IO_ADDRESS(ISP_REGS_BASE + 0x10), (0xF << 16));
        }
        isp_isr(dev->isp_gen_set_pt, dev->isp_3a_result_pt);
        if ((dev->ctrl_para.prev_focus_pos != dev->isp_3a_result_pt->real_vcm_pos ||
             dev->isp_gen_set_pt->isp_ini_cfg.isp_test_settings.isp_test_mode != 0 ||
             dev->isp_gen_set_pt->isp_ini_cfg.isp_test_settings.af_en == 0) &&
            dev->sd_act)
        {
            vcm_ctrl.code = dev->isp_3a_result_pt->real_vcm_pos;
            vcm_ctrl.sr = 0x0;
            if (v4l2_subdev_call(dev->sd_act, core, ioctl, ACT_SET_CODE, &vcm_ctrl))
            {
                vfe_warn("set vcm error!\n");
            }
            else
            {
                dev->ctrl_para.prev_focus_pos = dev->isp_3a_result_pt->real_vcm_pos;
            }
        }

        mutex_unlock(&dev->isp_3a_result_mutex);
    }
    else
    {
        isp_isr(dev->isp_gen_set_pt, NULL);
    }

    FUNCTION_LOG;
}

int set_sensor_shutter(struct vfe_dev *dev, int shutter)
{
    struct v4l2_control ctrl;
    if (shutter <= 0)
    {
        return -EINVAL;
    }
    ctrl.id = V4L2_CID_EXPOSURE;
    ctrl.value = shutter;
    if (v4l2_subdev_call(dev->sd, core, s_ctrl, &ctrl) != 0)
    {
        vfe_err("set sensor exposure line error!\n");
        return -1;
    }
    else
    {
        dev->ctrl_para.prev_exp_line = shutter;
        return 0;
    }
}

int set_sensor_gain(struct vfe_dev *dev, int gain)
{
    struct v4l2_control ct
  • 11
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Linux mipi csi 驱动开发是指在 Linux 系统下开发 mipi csi 接口的驱动程序Mipi csi 是一种高速串行接口,主要用于连接像素设备,如摄像头、显卡等,实现图像数据的传输。因此,开发 mipi csi 接口的驱动程序有很高的实用价值。 在 Linux 中,开发 mipi csi 驱动程序需要使用 V4L2 框架。V4L2 框架是 Linux 中的一种视频设备驱动框架,它可以用于管理摄像头、屏幕和视频编码器等设备。V4L2 框架的开发需要掌握一定的 C/C++ 编程技能和 Linux 系统编程知识,同时需要了解设备驱动编程的基本概念和方法。 在开发 mipi csi 驱动程序的过程中,需要进行驱动程序的初始化、配置和控制等操作。同时,还需要实现视频数据的采集、处理和输出等功能。为了确保驱动程序的稳定性和可靠性,在开发过程中需要进行充分的测试和调试。 总之,Linux mipi csi 驱动开发是一项具有重要实用价值的工作。通过合理的驱动程序设计和优化,可以实现高效、可靠的视频数据传输和处理,为应用开发和嵌入式系统提供了很高的灵活性和扩展性。 ### 回答2: Mipi-CSI是一种用于数据传输的接口标准,Linux开发Mipi-CSI驱动时需要了解相关的硬件和软件知识。 首先需要了解Mipi-CSI接口的硬件设计,包括信号电路、时序和电源。在驱动开发过程中,需要通过驱动程序操作相关的硬件寄存器、GPIO等,确保数据的正确传输。对于一些复杂的处理需求,可以利用硬件加速和DMA技术来优化数据传输。 其次需要了解Linux内核下的设备驱动框架,驱动开发一般基于Linux内核提供的v4l2框架。借助于v4l2框架,可以完成从硬件数据流采集到数据处理,并向用户应用提供图像数据流的过程。 在驱动开发过程中,还需要对Linux内核的编译和调试有深入的了解,如如何编写Makefile、如何使用打印调试信息、如何使用GDB调试等。 此外,在开发Mipi-CSI驱动时需要对图像视频处理有一定的了解,如帧率控制、分辨率调整、色彩空间转换等等。 综上所述,在开发Mipi-CSI驱动时需具备一定的硬件电路设计和软件驱动开发等知识,并需要了解Linux内核下的设备驱动框架和图像/视频处理相关的技术。只有掌握这些技术和知识,才能实现高性能的Mipi-CSI驱动程序。 ### 回答3: Mipi CSI是一种高速串行接口协议,广泛应用于移动设备和嵌入式系统中,用于连接图像传感器和芯片组。在Linux系统中,为了使用Mipi CSI接口连接图像传感器,需要开发相应的驱动程序驱动开发可以分为内核驱动和用户空间驱动两部分。内核驱动需要根据硬件平台不同进行相应的修改,采用适当的数据结构表示CSI接口和图像传感器,实现CSI接口的初始化、使能和数据传输等操作。用户空间驱动则负责将采集到的图像数据传输到上层应用程序,并提供相应的API接口,方便上层应用程序对图像数据的处理和显示。 在驱动开发的过程中,需要注意以下几点: 1. 硬件平台兼容性:在驱动开发之前,需要了解目标硬件平台的特性和限制,确保所开发驱动程序能够兼容该硬件平台。同时,需要了解CSI接口的规范和协议,以保证数据传输的正确性和稳定性。 2. 驱动维护性和可扩展性:在设计驱动程序时,应尽可能考虑到其维护性和可扩展性,以后可以更容易地适应各种硬件平台和应用场景的需求。 3. 性能优化:图像传输对于实时性和稳定性有较高的要求,因此驱动程序应尽量优化传输效率,减少CPU负载和延迟,提高图像采集的精度和速度。 总之,Mipi CSI驱动开发需要掌握一定的硬件和软件知识,了解CSI接口的规范和协议,并注重程序的维护性、可扩展性和性能优化。通过不断的实践和研究,可以编写出高效、稳定和可靠的Mipi CSI驱动程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值