使用 V3s SDK 提供VFE框架的驱动移植到VIN框架下抓图成功。但是图像非常暗,并且撕裂,抓raw数据查看也是一样,考虑可能mclk不同步导致。
sensor0:sensor@0 {
device_type = "sensor0";
sensor0_mname = "ov5648_mipi";
sensor0_twi_cci_id = <0>;
sensor0_twi_addr = <0x6c>;
sensor0_mclk_id = <0>;
sensor0_pos = "rear";
sensor0_isp_used = <1>;
sensor0_fmt = <1>;
sensor0_stby_mode = <0>;
sensor0_vflip = <0>;
sensor0_hflip = <0>;
sensor0_iovdd-supply = <>;
sensor0_iovdd_vol = <1800000>;
sensor0_avdd-supply = <>;
sensor0_avdd_vol = <2800000>;
sensor0_dvdd-supply = <>;
sensor0_dvdd_vol = <1200000>;
sensor0_power_en = <>;
sensor0_reset = <&pio PA 18 1 0 1 0>;
sensor0_pwdn = <&pio PA 19 1 0 1 0>;
sensor0_sm_hs = <>;
sensor0_sm_vs = <>;
flash_handle = <>;
act_handle = <>;
status = "okay";
};
/*
* A V4L2 driver for ov5647 Raw cameras.
*
* Copyright (c) 2022 by YuzukiTsuru <gloomyghost@gloomyghost.com>
* Copyright (c) 2018 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
*
* Authors: Zheng ZeQun <zequnzheng@allwinnertech.com>
* Liang WeiJie <liangweijie@allwinnertech.com>
* YuzukiTsuru <gloomyghost@gloomyghost.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <linux/clk.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <linux/io.h>
#include "camera.h"
#include "sensor_helper.h"
MODULE_AUTHOR("YuzukiTsuru");
MODULE_DESCRIPTION("A low-level driver for ov5647 sensors");
MODULE_LICENSE("GPL");
#define MCLK (24*1000*1000)
#define V4L2_IDENT_SENSOR 0x5648
/*
* Our nominal (default) frame rate.
*/
#define SENSOR_FRAME_RATE 30
/*
* The GC0310 i2c address
*/
#define I2C_ADDR 0x6c
#define SENSOR_NAME "ov5648_mipi"
/*
* The default register settings
*/
static struct regval_list sensor_default_regs[] = {
//Slave_ID=0x6c;
{0x0100, 0x00},// ; software standby
{0x0103, 0x01},// ; software reset
{REG_DLY, 0x25},
{0x370c, 0x03},// ; analog control
{0x5000, 0x06},// ; lens off, bpc on, wpc on
{0x5003, 0x08},// ; buf_en
{0x5a00, 0x08},//
{0x3000, 0xff},// ; D[9:8] output
{0x3001, 0xff},// ; D[7:0] output
{0x3002, 0xff},// ; Vsync, Href, PCLK, Frex, Strobe, SDA, GPIO1, GPIO0 output
{0x301d, 0xf0},//
{0x3a18, 0x00},// ; gain ceiling = 15.5x
{0x3a19, 0xf8},// ; gain ceiling
{0x3c01, 0x80},// ; band detection manual mode
{0x3b07, 0x0c},// ; strobe frex mode
//; analog control
{0x3630, 0x2e},
{0x3632, 0xe2},
{0x3633, 0x23},
{0x3634, 0x44},
{0x3620, 0x64},
{0x3621, 0xe0},
{0x3600, 0x37},
{0x3704, 0xa0},
{0x3703, 0x5a},
{0x3715, 0x78},
{0x3717, 0x01},
{0x3731, 0x02},
{0x370b, 0x60},
{0x3705, 0x1a},
{0x3f05, 0x02},
{0x3f06, 0x10},
{0x3f01, 0x0a},
//; AG/AE target
{0x3a0f, 0x58},// ; stable in high
{0x3a10, 0x50},// ; stable in low
{0x3a1b, 0x58},// ; stable out high
{0x3a1e, 0x50},// ; stable out low
{0x3a11, 0x60},// ; fast zone high
{0x3a1f, 0x28},// ; fast zone low
{0x4001, 0x02},// ; BLC start line
{0x4000, 0x09},// ; BLC enable
{0x3000, 0x00},// ; D[9:8] input
{0x3001, 0x00},// ; D[7:0] input
{0x3002, 0x00},// ; Vsync, Href, PCLK, Frex, Strobe, SDA, GPIO1, GPIO0 input
{0x3017, 0xe0},// ; MIPI PHY
{0x301c, 0xfc},//
{0x3636, 0x06},// ; analog control
{0x3016, 0x08},// ; MIPI pad enable
{0x3827, 0xec},//
{0x3018, 0x44},// ; MIPI 2 lane, MIPI enable
{0x3035, 0x21},// ; PLL
{0x3106, 0xf5},// ; PLL
{0x3034, 0x1a},// ; PLL
{0x301c, 0xf8},//
{0x3503, 0x03},// ; Gain has no latch delay, AGC manual, AEC
{0x3501, 0x10},// ; exposure[15:8]
{0x3502, 0x80},// ; exposure[7:0]
{0x350a, 0x00},// ; gain[9:8]
{0x350b, 0x7f},// ; gain[7:0]
{0x5001, 0x01},// ; AWB on
{0x5180, 0x08},// ; AWB manual gain enable
{0x5186, 0x04},// ; manual red gain high
{0x5187, 0x00},// ; manual red gain low
{0x5188, 0x04},// ; manual green gain high
{0x5189, 0x00},// ; manual green gain low
{0x518a, 0x04},// ; manual blue gain high
{0x518b, 0x00},// ; manual blue gain low
{0x5000, 0x06},// ; lenc off, bpc on, wpc on
};
static struct regval_list sensor_qsxga_regs[] = { //qsxga: 2592*1936@15fps
{0x0100, 0x00},// ; software standby
{0x3035, 0x21},// ; PLL
{0x3036, 0x66},// ; PLL
{0x303c, 0x11},// ; PLL
{0x3821, 0x06},// ; ISP mirror on, Sensor mirror on
{0x3820, 0x00},// ; ISP flip off, Sensor flip off
{0x3612, 0x5b},// ; analog control
{0x3618, 0x04},// ; analog control
{0x380c, 0x0a},// ; HTS = 2752
{0x380d, 0xc0},// ; HTS
{0x380e, 0x07},// ; VTS = 1974
{0x380f, 0xb6},// ; VTS
{0x3814, 0x11},// ; X INC
{0x3815, 0x11},// ; X INC
{0x3708, 0x64},// ; analog control
{0x3709, 0x12},// ; analog control
{0x3808, 0x0a},// ; X OUTPUT SIZE = 2592
{0x3809, 0x20},// ; X OUTPUT SIZE
{0x380a, 0x07},// ; Y OUTPUT SIZE = 1944
{0x380b, 0x98},// ; Y OUTPUT SIZE
{0x3800, 0x00},// ; X Start
{0x3801, 0x0c},// ; X Start
{0x3802, 0x00},// ; Y Start
{0x3803, 0x02},// ; Y Start
{0x3804, 0x0a},// ; X End
{0x3805, 0x33},// ; X End
{0x3806, 0x07},// ; Y End
{0x3807, 0xa1},// ; Y End
///////////; Banding filter
{0x3a08, 0x01},// ; B50
{0x3a09, 0x28},// ; B50
{0x3a0a, 0x00},// ; B60
{0x3a0b, 0xf6},// ; B60
{0x3a0d, 0x07},// ; B60 max
{0x3a0e, 0x06},// ; B50 max
{0x4004, 0x04},// ; black line number
{0x4837, 0x19},// ; MIPI pclk period
{0x0100, 0x01},// ; wake up from software standby
};
static struct regval_list sensor_720p_regs[] = { //720: 1280*720@30fps
{0x0100, 0x00},// ; software standby
{0x3035, 0x21},// ; PLL
{0x3036, 0x46},// ; PLL
{0x303c, 0x11},// ; PLL
{0x3821, 0x07},// ; ISP mirror on, Sensor mirror on, bin on
{0x3820, 0x41},// ; ISP flip off, Sensor flip off, bin on
{0x3612, 0x59},// ; analog control
{0x3618, 0x00},// ; analog control
{0x380c, 0x07},// ; HTS = 1896
{0x380d, 0x68},// ; HTS
{0x380e, 0x03},// ; VTS = 984
{0x380f, 0xd8},// ; VTS
{0x3814, 0x31},// ; X INC
{0x3815, 0x31},// ; Y INC
{0x3708, 0x64},// ; analog control
{0x3709, 0x52},// ; analog control
{0x3808, 0x05},// ; X OUTPUT SIZE = 1280
{0x3809, 0x00},// ; X OUTPUT SIZE
{0x380a, 0x03},// ; Y OUTPUT SIZE = 960
{0x380b, 0xc0},// ; Y OUTPUT SIZE
{0x3800, 0x00},// ; X Start
{0x3801, 0x18},// ; X Start
{0x3802, 0x00},// ; Y Start
{0x3803, 0x0e},// ; Y Start
{0x3804, 0x0a},// ; X End
{0x3805, 0x27},// ; X End
{0x3806, 0x07},// ; Y End
{0x3807, 0x95},// ; Y End
// banding filter
{0x3a08, 0x01},// ; B50
{0x3a09, 0x27},// ; B50
{0x3a0a, 0x00},// ; B60
{0x3a0b, 0xf6},// ; B60
{0x3a0d, 0x04},// ; B50 max
{0x3a0e, 0x03},// ; B60 max
{0x4004, 0x02},// ; black line number
{0x4837, 0x24},// ; MIPI pclk period
{0x0100, 0x01},// ; wake up from software standby
};
static struct regval_list sensor_fmt_raw[] = {
};
/*
* Code for dealing with controls.
* fill with different sensor module
* different sensor module has different settings here
* if not support the follow function ,retrun -EINVAL
*/
static int sensor_g_exp(struct v4l2_subdev *sd, __s32 *value)
{
struct sensor_info *info = to_state(sd);
*value = info->exp;
sensor_print("sensor_get_exposure = %d\n", info->exp);
return 0;
}
static int sensor_s_exp(struct v4l2_subdev *sd, unsigned int exp_val)
{
unsigned char explow, expmid, exphigh;
struct sensor_info *info = to_state(sd);
if(exp_val>0xfffff)
exp_val=0xfffff;
sensor_write(sd, 0x3208, 0x00);//enter group write
sensor_write(sd, 0x3503, 0x13);
exphigh = (unsigned char) ( (0x0f0000&exp_val)>>16);
expmid = (unsigned char) ( (0x00ff00&exp_val)>>8);
explow = (unsigned char) ( (0x0000ff&exp_val) );
//sensor_write(sd, 0x3208, 0x00);//enter group write
sensor_write(sd, 0x3502, explow);
sensor_write(sd, 0x3501, expmid);
sensor_write(sd, 0x3500, exphigh);
sensor_write(sd, 0x3208, 0x10);//end group write
sensor_write(sd, 0x3208, 0xa0);//init group write
sensor_print("ov5647_mipi sensor_set_exp = %d, Done!\n", exp_val);
info->exp = exp_val;
return 0;
}
static int sensor_g_gain(struct v4l2_subdev *sd, __s32 *value)
{
struct sensor_info *info = to_state(sd);
*value = info->gain;
sensor_print("sensor_get_gain = %d\n", info->gain);
return 0;
}
static int sensor_s_gain(struct v4l2_subdev *sd, unsigned int gain_val)
{
struct sensor_info *info = to_state(sd);
unsigned char gainlow=0;
unsigned char gainhigh=0;
if(gain_val<1*16)
gain_val=16;
if(gain_val>64*16-1)
gain_val=64*16-1;
gainlow=(unsigned char)(gain_val&0xff);
gainhigh=(unsigned char)((gain_val>>8)&0x3);
sensor_write(sd, 0x3208, 0x00);//enter group write
sensor_write(sd, 0x3503, 0x13);
sensor_write(sd, 0x350b, gainlow);
sensor_write(sd, 0x350a, gainhigh);
sensor_write(sd, 0x3208, 0x10);//end group write
sensor_write(sd, 0x3208, 0xa0);//init group write
//printk("ov5647_mipi sensor_set_gain = %d, Done!\n", gain_val);
info->gain = gain_val;
return 0;
}
static int ov5648_sensor_vts;
static int sensor_s_exp_gain(struct v4l2_subdev *sd,
struct sensor_exp_gain *exp_gain)
{
int exp_val, gain_val,frame_length,shutter;
unsigned char explow=0,expmid=0,exphigh=0;
unsigned char gainlow=0,gainhigh=0;
struct sensor_info *info = to_state(sd);
exp_val = exp_gain->exp_val;
gain_val = exp_gain->gain_val;
if(gain_val<1*16)
gain_val=16;
if(gain_val>64*16-1)
gain_val=64*16-1;
if(exp_val>0xfffff)
exp_val=0xfffff;
gainlow=(unsigned char)(gain_val&0xff);
gainhigh=(unsigned char)((gain_val>>8)&0x3);
exphigh = (unsigned char) ( (0x0f0000&exp_val)>>16);
expmid = (unsigned char) ( (0x00ff00&exp_val)>>8);
explow = (unsigned char) ( (0x0000ff&exp_val) );
shutter = exp_val/16;
sensor_print("ov5648_sensor_vts = %d\n",ov5648_sensor_vts);
if(shutter > ov5648_sensor_vts- 4)
frame_length = shutter + 4;
else
frame_length = ov5648_sensor_vts;
sensor_write(sd, 0x3503, 0x07);
sensor_write(sd, 0x380f, (frame_length & 0xff));
sensor_write(sd, 0x380e, (frame_length >> 8));
sensor_print("exp_val = %d,gain_val = %d\n",exp_val,gain_val);
sensor_write(sd, 0x3208, 0x00);//enter group write
sensor_write(sd, 0x350b, gainlow);
sensor_write(sd, 0x350a, gainhigh);
sensor_write(sd, 0x3502, explow);
sensor_write(sd, 0x3501, expmid);
sensor_write(sd, 0x3500, exphigh);
sensor_write(sd, 0x3208, 0x10);//end group write
sensor_write(sd, 0x3208, 0xa0);//init group write
info->exp = exp_val;
info->gain = gain_val;
return 0;
}
static void sensor_s_sw_stby(struct v4l2_subdev *sd, int on_off)
{
int ret = 0;
return ret;
}
/*
* Stuff that knows about the sensor.
*/
static int sensor_power(struct v4l2_subdev *sd, int on)
{
int ret = 0;
sensor_print("ov5648 sensor_power\n");
switch (on) {
case STBY_ON:
sensor_print("STBY_ON!\n");
cci_lock(sd);
sensor_s_sw_stby(sd, STBY_ON);
usleep_range(1000, 1200);
cci_unlock(sd);
break;
case STBY_OFF:
sensor_print("STBY_OFF!\n");
cci_lock(sd);
usleep_range(1000, 1200);
sensor_s_sw_stby(sd, STBY_OFF);
cci_unlock(sd);
break;
case PWR_ON:
sensor_print("PWR_ON!100\n");
cci_lock(sd);
vin_gpio_set_status(sd, PWDN, 1);
vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
vin_gpio_set_status(sd, POWER_EN, 1);
vin_gpio_write(sd, PWDN, CSI_GPIO_LOW);
vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
vin_gpio_write(sd, POWER_EN, CSI_GPIO_HIGH);
usleep_range(7000, 8000);
vin_set_pmu_channel(sd, IOVDD, ON);
usleep_range(7000, 8000);
vin_set_pmu_channel(sd, AVDD, ON);
vin_set_pmu_channel(sd, AFVDD, ON);
usleep_range(7000, 8000);
vin_set_pmu_channel(sd, DVDD, ON);
usleep_range(7000, 8000);
vin_set_mclk_freq(sd, MCLK);
vin_set_mclk(sd, ON);
usleep_range(10000, 12000);
vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
vin_gpio_write(sd, PWDN, CSI_GPIO_HIGH);
vin_set_pmu_channel(sd, CAMERAVDD, ON);/*AFVCC ON*/
usleep_range(10000, 12000);
cci_unlock(sd);
break;
case PWR_OFF:
sensor_print("PWR_OFF!\n");
cci_lock(sd);
vin_gpio_write(sd, PWDN, CSI_GPIO_HIGH);
vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
vin_set_mclk(sd, OFF);
usleep_range(7000, 8000);
vin_set_pmu_channel(sd, DVDD, OFF);
vin_gpio_write(sd, PWDN, CSI_GPIO_LOW);
vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
vin_gpio_write(sd, POWER_EN, CSI_GPIO_LOW);
vin_set_pmu_channel(sd, AVDD, OFF);
vin_set_pmu_channel(sd, IOVDD, OFF);
vin_set_pmu_channel(sd, AFVDD, OFF);
vin_set_pmu_channel(sd, CAMERAVDD, OFF);/*AFVCC ON*/
cci_unlock(sd);
break;
default:
return -EINVAL;
}
return 0;
}
static int sensor_reset(struct v4l2_subdev *sd, u32 val)
{
switch (val) {
case 0:
vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
usleep_range(10000,12000);
break;
case 1:
vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
usleep_range(10000,12000);
break;
default:
return -EINVAL;
}
return 0;
}
static int sensor_detect(struct v4l2_subdev *sd)
{
data_type rdval;
unsigned int SENSOR_ID = 0;
sensor_read(sd, 0x300A, &rdval);
SENSOR_ID |= rdval;
SENSOR_ID |= (rdval << 8);
sensor_read(sd, 0x300B, &rdval);
SENSOR_ID |= (rdval);
sensor_print("V4L2_IDENT_SENSOR = 0x%x\n", SENSOR_ID);
if (SENSOR_ID != V4L2_IDENT_SENSOR) {
sensor_print("ov5648 %s error, chip found is not an target chip", __func__);
//return -ENODEV;
}
return 0;
}
static int sensor_init(struct v4l2_subdev *sd, u32 val)
{
int ret;
struct sensor_info *info = to_state(sd);
sensor_print("sensor_init\n");
/*Make sure it is a target sensor */
ret = sensor_detect(sd);
if (ret) {
sensor_err("chip found is not an target chip.\n");
return ret;
}
info->focus_status = 0;
info->low_speed = 0;
info->width = QSXGA_WIDTH;
info->height = QSXGA_HEIGHT;
info->hflip = 0;
info->vflip = 0;
info->gain = 0;
info->tpf.numerator = 1;
info->tpf.denominator = 30; /* 30fps */
info->preview_first_flag = 1;
return 0;
}
static long sensor_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
int ret = 0;
struct sensor_info *info = to_state(sd);
switch (cmd) {
case GET_CURRENT_WIN_CFG:
if (info->current_wins != NULL) {
memcpy(arg, info->current_wins,
sizeof(struct sensor_win_size));
ret = 0;
} else {
sensor_err("empty wins!\n");
ret = -1;
}
break;
case SET_FPS:
ret = 0;
break;
case VIDIOC_VIN_SENSOR_EXP_GAIN:
ret = sensor_s_exp_gain(sd, (struct sensor_exp_gain *)arg);
break;
case VIDIOC_VIN_SENSOR_CFG_REQ:
sensor_cfg_req(sd, (struct sensor_config *)arg);
break;
case VIDIOC_VIN_ACT_INIT:
ret = actuator_init(sd, (struct actuator_para *)arg);
break;
case VIDIOC_VIN_ACT_SET_CODE:
ret = actuator_set_code(sd, (struct actuator_ctrl *)arg);
break;
default:
return -EINVAL;
}
return ret;
}
/*
* Store information about the video data format.
*/
static struct sensor_format_struct sensor_formats[] = {
{
.desc = "Raw RGB Bayer",
.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
.regs = sensor_fmt_raw,
.regs_size = ARRAY_SIZE(sensor_fmt_raw),
.bpp = 1
},
};
#define N_FMTS ARRAY_SIZE(sensor_formats)
/*
* Then there is the issue of window sizes. Try to capture the info here.
*/
static struct sensor_win_size sensor_win_sizes[] = {
{
.width = QSXGA_WIDTH,
.height = QSXGA_HEIGHT,
.hoffset = 0,
.voffset = 4,
.hts = 2752,
.vts = 1974,
.pclk = 81486720,
.mipi_bps = 408*1000*1000,
.fps_fixed = 2,
.bin_factor = 1,
.intg_min = 1,
.intg_max = (1974)<<4,
.gain_min = 1<<4,
.gain_max = 12<<4,
.regs = sensor_qsxga_regs,
.regs_size = ARRAY_SIZE(sensor_qsxga_regs),
.set_size = NULL,
},
{
.width = HD720_WIDTH,
.height = HD720_HEIGHT,
.hoffset = 0,
.voffset = 120,
.hts = 1896,
.vts = 984,
.pclk = 56*1000*1000,
.mipi_bps = 280*1000*1000,
.fps_fixed = 1,
.bin_factor = 1,
.intg_min = 1,
.intg_max = 984<<4,
.gain_min = 1<<4,
.gain_max = 12<<4,
.regs = sensor_720p_regs,//
.regs_size = ARRAY_SIZE(sensor_720p_regs),//
.set_size = NULL,
},
};
#define N_WIN_SIZES (ARRAY_SIZE(sensor_win_sizes))
static int sensor_reg_init(struct sensor_info *info)
{
int ret;
struct v4l2_subdev *sd = &info->sd;
struct sensor_format_struct *sensor_fmt = info->fmt;
struct sensor_win_size *wsize = info->current_wins;
ret = sensor_write_array(sd, sensor_default_regs,
ARRAY_SIZE(sensor_default_regs));
if (ret < 0) {
sensor_err("write sensor_default_regs error\n");
return ret;
}
sensor_print("sensor_reg_init\n");
sensor_write_array(sd, sensor_fmt->regs, sensor_fmt->regs_size);
if (wsize->regs)
sensor_write_array(sd, wsize->regs, wsize->regs_size);
if (wsize->set_size)
wsize->set_size(sd);
info->width = wsize->width;
info->height = wsize->height;
info->exp = 0;
info->gain = 0;
ov5648_sensor_vts = wsize->vts;
sensor_print("s_fmt set width = %d, height = %d\n", wsize->width,
wsize->height);
return 0;
}
static int sensor_s_stream(struct v4l2_subdev *sd, int enable)
{
struct sensor_info *info = to_state(sd);
sensor_print("%s on = %d, %d*%d fps: %d code: %x\n", __func__, enable,
info->current_wins->width, info->current_wins->height,
info->current_wins->fps_fixed, info->fmt->mbus_code);
if (!enable)
return 0;
return sensor_reg_init(info);
}
static int sensor_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
cfg->type = V4L2_MBUS_CSI2;
cfg->flags = 0 | V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0;
return 0;
}
static int sensor_g_ctrl(struct v4l2_ctrl *ctrl)
{
struct sensor_info *info = container_of(ctrl->handler,
struct sensor_info, handler);
struct v4l2_subdev *sd = &info->sd;
switch (ctrl->id) {
case V4L2_CID_GAIN:
return sensor_g_gain(sd, &ctrl->val);
case V4L2_CID_EXPOSURE:
return sensor_g_exp(sd, &ctrl->val);
}
return -EINVAL;
}
static int sensor_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct sensor_info *info = container_of(ctrl->handler,
struct sensor_info, handler);
struct v4l2_subdev *sd = &info->sd;
switch (ctrl->id) {
case V4L2_CID_GAIN:
return sensor_s_gain(sd, ctrl->val);
case V4L2_CID_EXPOSURE:
return sensor_s_exp(sd, ctrl->val);
}
return -EINVAL;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops sensor_ctrl_ops = {
.g_volatile_ctrl = sensor_g_ctrl,
.s_ctrl = sensor_s_ctrl,
};
static const struct v4l2_subdev_core_ops sensor_core_ops = {
.reset = sensor_reset,
.init = sensor_init,
.s_power = sensor_power,
.ioctl = sensor_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = sensor_compat_ioctl32,
#endif
};
static const struct v4l2_subdev_video_ops sensor_video_ops = {
.s_parm = sensor_s_parm,
.g_parm = sensor_g_parm,
.s_stream = sensor_s_stream,
.g_mbus_config = sensor_g_mbus_config,
};
static const struct v4l2_subdev_pad_ops sensor_pad_ops = {
.enum_mbus_code = sensor_enum_mbus_code,
.enum_frame_size = sensor_enum_frame_size,
.get_fmt = sensor_get_fmt,
.set_fmt = sensor_set_fmt,
};
static const struct v4l2_subdev_ops sensor_ops = {
.core = &sensor_core_ops,
.video = &sensor_video_ops,
.pad = &sensor_pad_ops,
};
/* ----------------------------------------------------------------------- */
static struct cci_driver cci_drv = {
.name = SENSOR_NAME,
.addr_width = CCI_BITS_16,
.data_width = CCI_BITS_8,
};
static const struct v4l2_ctrl_config sensor_custom_ctrls[] = {
{
.ops = &sensor_ctrl_ops,
.id = V4L2_CID_FRAME_RATE,
.name = "frame rate",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 15,
.max = 120,
.step = 1,
.def = 120,
},
};
static int sensor_init_controls(struct v4l2_subdev *sd,
const struct v4l2_ctrl_ops *ops)
{
struct sensor_info *info = to_state(sd);
struct v4l2_ctrl_handler *handler = &info->handler;
struct v4l2_ctrl *ctrl;
int i;
int ret = 0;
v4l2_ctrl_handler_init(handler, 2 + ARRAY_SIZE(sensor_custom_ctrls));
v4l2_ctrl_new_std(handler, ops, V4L2_CID_GAIN, 1 * 1600,
256 * 1600, 1, 1 * 1600);
ctrl = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 0,
65536 * 16, 1, 0);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
for (i = 0; i < ARRAY_SIZE(sensor_custom_ctrls); i++)
v4l2_ctrl_new_custom(handler, &sensor_custom_ctrls[i], NULL);
if (handler->error) {
ret = handler->error;
v4l2_ctrl_handler_free(handler);
}
sd->ctrl_handler = handler;
return ret;
}
static int sensor_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct v4l2_subdev *sd;
struct sensor_info *info;
info = kzalloc(sizeof(struct sensor_info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
sd = &info->sd;
cci_dev_probe_helper(sd, client, &sensor_ops, &cci_drv);
sensor_init_controls(sd, &sensor_ctrl_ops);
mutex_init(&info->lock);
#ifdef CONFIG_SAME_I2C
info->sensor_i2c_addr = I2C_ADDR >> 1;
#endif
info->fmt = &sensor_formats[0];
info->fmt_pt = &sensor_formats[0];
info->win_pt = &sensor_win_sizes[0];
info->fmt_num = N_FMTS;
info->win_size_num = N_WIN_SIZES;
info->sensor_field = V4L2_FIELD_NONE;
info->stream_seq = MIPI_BEFORE_SENSOR;
info->af_first_flag = 1;
info->exp = 0;
info->gain = 0;
return 0;
}
static int sensor_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd;
sd = cci_dev_remove_helper(client, &cci_drv);
kfree(to_state(sd));
return 0;
}
static const struct i2c_device_id sensor_id[] = {
{SENSOR_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, sensor_id);
static struct i2c_driver sensor_driver = {
.driver = {
.owner = THIS_MODULE,
.name = SENSOR_NAME,
},
.probe = sensor_probe,
.remove = sensor_remove,
.id_table = sensor_id,
};
static __init int init_sensor(void)
{
return cci_dev_init_helper(&sensor_driver);
}
static __exit void exit_sensor(void)
{
cci_dev_exit_helper(&sensor_driver);
}
module_init(init_sensor);
module_exit(exit_sensor);
另外建议使用支持列表中的摄像头,例如gc2053,gc2063,这些摄像头已经适配量产完成并且调整ISP后画质更佳,也支持aiisp实现低照度全彩画质,树莓派的摄像头不推荐使用,因为他是外挂mclk的会引起芯片处于错误的模式,另外原厂也没有相应的支持(2017年前的芯片才有这个支持)
IMX219 同样可以使用,但是请注意4lane的摄像头不可适配2lane的数据





















