oleavr-rgl-a500-mini-linux-.../drivers/media/platform/sunxi-vfe/device/bf3a03.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

1706 lines
37 KiB
C
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* A V4L2 driver for GalaxyCore BF3A03 cameras.
*
*/
#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-chip-ident.h>
#include <media/v4l2-mediabus.h>
#include <linux/io.h>
#include "camera.h"
#include "sensor_helper.h"
MODULE_AUTHOR("raymonxiu");
MODULE_DESCRIPTION("A low-level driver for GalaxyCore BF3A03 sensors");
MODULE_LICENSE("GPL");
//for internel driver debug
#define DEV_DBG_EN 1
#if(DEV_DBG_EN == 1)
#define vfe_dev_dbg(x,arg...) printk("[CSI_DEBUG][BF3A03]"x,##arg)
#else
#define vfe_dev_dbg(x,arg...)
#endif
#define vfe_dev_err(x,arg...) printk("[CSI_ERR][BF3A03]"x,##arg)
#define vfe_dev_print(x,arg...) printk("[CSI][BF3A03]"x,##arg)
#define LOG_ERR_RET(x) { \
int ret; \
ret = x; \
if(ret < 0) {\
vfe_dev_err("error at %s\n",__func__); \
return ret; \
} \
}
//define module timing
#define MCLK (24*1000*1000)
#define VREF_POL V4L2_MBUS_VSYNC_ACTIVE_HIGH
#define HREF_POL V4L2_MBUS_HSYNC_ACTIVE_HIGH
#define CLK_POL V4L2_MBUS_PCLK_SAMPLE_RISING
#define V4L2_IDENT_SENSOR 0x3a03
/*
* Our nominal (default) frame rate.
*/
#define SENSOR_FRAME_RATE 10
/*
* The BF3A03 sits on i2c with ID 0x42
*/
#define I2C_ADDR 0xDC
#define SENSOR_NAME "bf3a03"
/*
* Information we maintain about a known sensor.
*/
struct sensor_format_struct; /* coming later */
struct cfg_array { /* coming later */
struct regval_list * regs;
int size;
};
static inline struct sensor_info *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct sensor_info, sd);
}
/*
* The default register settings
*
*/
static struct regval_list sensor_default_regs_24M[] = {
};
static struct regval_list sensor_default_regs[] = {
/* init 640X480 VGA */
// Initail Sequence Write In.
{0x09,0X55},
{0x15,0X02},
{0x1e,0X70},//HV mirror
//Analog signals
{0x06,0X78},//0x68
{0x21,0X00},
{0x3e,0X37},
{0x29,0X2b},//0x29
{0x27,0X98},
//Clock
{0x2f,0X4e},//???????????????0x42;
{0x11,0X10},//???[1:0]
{0x1b,0X09},//??? PLL'????0x2f[1]??0x1b[3]
//????????
{0x4a,0X98},//Sub??Win
{0x12,0X00},//YUV??sub???
{0x3a,0X02},//YUV??? //00
//Manual
{0x13,0X08},//???bit[3]GLB GAIN0
//{0x01,0X14},
//{0x02,0X20},
{0x8c,0X02},
{0x8d,0X4c},
{0x87,0X16},//GLB GAIN0
//Auto
{0x13,0X07},
//Denoise
{0x70,0X0f},//???g????
{0x3b,0X00},//?????????
{0x71,0X0c},//???g????
{0x73,0X27},//Denoise
{0x75,0X88},//Outdoor denoise
{0x76,0Xd8},
{0x77,0X0a},//Low light denoise
{0x78,0Xff},
{0x79,0X14},
{0x7a,0X24},//??????//0x12
{0x9e,0X04},//0xc4
{0x7d,0X2a},//????
//Gamma default
{0x39,0Xa0},//Gamma offset//c0//b0//a8
{0x3f,0Xa0},//b0
{0x90,0X20},
{0x5f,0X00},//Dark_sel gamma// 0x01//0x03 zj
{0x40,0X22},
{0x41,0X23},
{0x42,0X28},
{0x43,0X25},
{0x44,0X1d},
{0x45,0X17},
{0x46,0X13},
{0x47,0X12},
{0x48,0X10},
{0x49,0X0d},
{0x4b,0X0b},
{0x4c,0X0b},
{0x4e,0X09},
{0x4f,0X07},
{0x50,0X06},
//AE
{0x24,0X50},//??A??Y_target //0x48
{0x97,0X40},//A??Y_target
{0x25,0X88},//AE_LOC
{0x81,0X00},//AE speed
{0x82,0X18},
{0x83,0X30},
{0x84,0X20},
{0x85,0X38},
{0x86,0X55},
{0x94,0X82},//??AE?v??????
{0x80,0X92},//??????????,bit[1]choose 50HZ or 60HZ step;
{0x98,0X88},//??????????
{0x89,0X55},//?????bit[7:3]//8a
{0x8e,0X2c},
{0x8f,0X86},
//Banding
{0x2b,0X20},
{0x8a,0X93},//50HZ
{0x8b,0X7a},//60HZ
{0x92,0X6D},
//Color
{0x5a,0Xec},//Outdoor color
{0x51,0X90},
{0x52,0X10},
{0x53,0X8d},
{0x54,0X88},
{0x57,0X82},
{0x58,0X8d},
{0x5a,0X7c},//A light color
{0x51,0X80},
{0x52,0X04},
{0x53,0X8d},
{0x54,0X88},
{0x57,0X82},
{0x58,0X8d},
//Color defult
{0x5a,0X6c},//Indoor color
{0x51,0X93},
{0x52,0X04},
{0x53,0X8a},
{0x54,0X88},
{0x57,0X02},
{0x58,0X8d},
//Color??????
{0x5a,0X6c},//Indoor color
{0x51,0X90},
{0x52,0X0a},
{0x53,0X84},
{0x54,0X05},
{0x57,0X05},
{0x58,0X87},
//Saturation
{0xb0,0Xa0},//A?????
{0xb1,0X26},
{0xb2,0X1c},
{0xb4,0Xfd},
{0xb0,0X30},//??A?????
{0xb1,0Xd8},
{0xb2,0X90}, //0xb0
{0xb4,0Xf1},
//Contrast
{0x3c,0X40},//K1
{0x56,0X48},//K2 0xb4[4]???new or old
{0x4d,0X40},//K3
{0x59,0X40},//K4
//G gainСһЩ
{0x35,0x46},//shading R
{0x65,0x38},//shading G
{0x66,0x42},//shading B
//AWB
{0x6a,0xd1},//AWB
{0x23,0x11},//G GAIN
{0xa2,0x0b},
{0xa3,0x26},
{0xa4,0x04},
{0xa5,0x20},//0x26
{0xa7,0x13},//B GAIN
{0xa8,0x8e},//R GAIN
{0xa9,0x16},
{0xaa,0x16},
{0xab,0x16},
{0xac,0x30},
{0xad,0xf0},
{0xae,0x57},
{0xc5,0x66},
{0xc7,0x38},//bit[4]
{0xc8,0x0d},
{0xc9,0x16},
{0xd3,0x09},
{0xd4,0x15},
{0xd0,0x00},//offset
{0xd1,0x01},//offset
{0xd2,0x18},
{0x20,0X00},
{0x16,0X25},
{0x00,0x00}
};
/*
* The white balance settings
* Here only tune the R G B channel gain.
* The white balance enalbe bit is modified in sensor_s_autowb and sensor_s_wb
*/
static struct regval_list sensor_wb_manual[] = {
//null
};
static struct regval_list sensor_wb_auto_regs[] = {
{0x13,0x07},//for AWB can adjust back, first write the RGB wb gain, and then enable AWB
};
static struct regval_list sensor_wb_incandescence_regs[] = {
//bai re guang
{0x13,0x05},
{0x01,0x1f},
{0x02,0x20},
};
static struct regval_list sensor_wb_fluorescent_regs[] = {
//ri guang deng
{0x13,0x05},
{0x01,0x1a},
{0x02,0x15},
};
static struct regval_list sensor_wb_tungsten_regs[] = {
//wu si deng
{0x13,0x05},
{0x01,0x28},
{0x02,0x0a},
};
static struct regval_list sensor_wb_horizon[] = {
//null
};
static struct regval_list sensor_wb_daylight_regs[] = {
{0x13,0x05},
{0x01,0x15},
{0x02,0x20},
};
static struct regval_list sensor_wb_flash[] = {
//null
};
static struct regval_list sensor_wb_cloud_regs[] = {
{0x13,0x05},
{0x01,0x10},
{0x02,0x2c},
};
static struct regval_list sensor_wb_shade[] = {
//null
};
static struct cfg_array sensor_wb[] = {
{
.regs = sensor_wb_manual, //V4L2_WHITE_BALANCE_MANUAL
.size = ARRAY_SIZE(sensor_wb_manual),
},
{
.regs = sensor_wb_auto_regs, //V4L2_WHITE_BALANCE_AUTO
.size = ARRAY_SIZE(sensor_wb_auto_regs),
},
{
.regs = sensor_wb_incandescence_regs, //V4L2_WHITE_BALANCE_INCANDESCENT
.size = ARRAY_SIZE(sensor_wb_incandescence_regs),
},
{
.regs = sensor_wb_fluorescent_regs, //V4L2_WHITE_BALANCE_FLUORESCENT
.size = ARRAY_SIZE(sensor_wb_fluorescent_regs),
},
{
.regs = sensor_wb_tungsten_regs, //V4L2_WHITE_BALANCE_FLUORESCENT_H
.size = ARRAY_SIZE(sensor_wb_tungsten_regs),
},
{
.regs = sensor_wb_horizon, //V4L2_WHITE_BALANCE_HORIZON
.size = ARRAY_SIZE(sensor_wb_horizon),
},
{
.regs = sensor_wb_daylight_regs, //V4L2_WHITE_BALANCE_DAYLIGHT
.size = ARRAY_SIZE(sensor_wb_daylight_regs),
},
{
.regs = sensor_wb_flash, //V4L2_WHITE_BALANCE_FLASH
.size = ARRAY_SIZE(sensor_wb_flash),
},
{
.regs = sensor_wb_cloud_regs, //V4L2_WHITE_BALANCE_CLOUDY
.size = ARRAY_SIZE(sensor_wb_cloud_regs),
},
{
.regs = sensor_wb_shade, //V4L2_WHITE_BALANCE_SHADE
.size = ARRAY_SIZE(sensor_wb_shade),
},
};
/*
* The color effect settings
*/
static struct regval_list sensor_colorfx_none_regs[] = {
{0x70,0x0f},
{0x69,0x00},
{0x67,0x80},
{0x68,0x80},
{0xb4,0x91},
};
static struct regval_list sensor_colorfx_bw_regs[] = {
{0x70,0x0f},
{0x69,0x20},
{0x67,0x80},
{0x68,0x80},
{0xb4,0x91},
};
static struct regval_list sensor_colorfx_sepia_regs[] = {
{0x70,0x0b},
{0x69,0x20},
{0x67,0x60},
{0x68,0xa0},
{0xb4,0x91},
};
static struct regval_list sensor_colorfx_negative_regs[] = {
{0x70,0x0f},
{0x69,0x01},
{0x67,0x80},
{0x68,0x80},
{0xb4,0x91},
};
static struct regval_list sensor_colorfx_emboss_regs[] = {
{0x70,0x1b},
{0x69,0x01},
{0x67,0x80},
{0x68,0x80},
{0xb4,0x91},
};
static struct regval_list sensor_colorfx_sketch_regs[] = {
{0x70,0x3b},
{0x69,0x00},
{0x67,0x80},
{0x68,0x80},
{0xb4,0x91},
};
static struct regval_list sensor_colorfx_sky_blue_regs[] = {
{0x70,0x0b},
{0x69,0x20},
{0x67,0xe0},
{0x68,0x50},
{0xb4,0x91},
};
static struct regval_list sensor_colorfx_grass_green_regs[] = {
{0x70,0x0b},
{0x69,0x20},
{0x67,0x60},
{0x68,0x60},
{0xb4,0x91},
};
static struct regval_list sensor_colorfx_skin_whiten_regs[] = {
//NULL
};
static struct regval_list sensor_colorfx_vivid_regs[] = {
//NULL
};
static struct regval_list sensor_colorfx_aqua_regs[] = {
//null
};
static struct regval_list sensor_colorfx_art_freeze_regs[] = {
//null
};
static struct regval_list sensor_colorfx_silhouette_regs[] = {
//null
};
static struct regval_list sensor_colorfx_solarization_regs[] = {
//null
};
static struct regval_list sensor_colorfx_antique_regs[] = {
//null
};
static struct regval_list sensor_colorfx_set_cbcr_regs[] = {
//null
};
static struct cfg_array sensor_colorfx[] = {
{
.regs = sensor_colorfx_none_regs, //V4L2_COLORFX_NONE = 0,
.size = ARRAY_SIZE(sensor_colorfx_none_regs),
},
{
.regs = sensor_colorfx_bw_regs, //V4L2_COLORFX_BW = 1,
.size = ARRAY_SIZE(sensor_colorfx_bw_regs),
},
{
.regs = sensor_colorfx_sepia_regs, //V4L2_COLORFX_SEPIA = 2,
.size = ARRAY_SIZE(sensor_colorfx_sepia_regs),
},
{
.regs = sensor_colorfx_negative_regs, //V4L2_COLORFX_NEGATIVE = 3,
.size = ARRAY_SIZE(sensor_colorfx_negative_regs),
},
{
.regs = sensor_colorfx_emboss_regs, //V4L2_COLORFX_EMBOSS = 4,
.size = ARRAY_SIZE(sensor_colorfx_emboss_regs),
},
{
.regs = sensor_colorfx_sketch_regs, //V4L2_COLORFX_SKETCH = 5,
.size = ARRAY_SIZE(sensor_colorfx_sketch_regs),
},
{
.regs = sensor_colorfx_sky_blue_regs, //V4L2_COLORFX_SKY_BLUE = 6,
.size = ARRAY_SIZE(sensor_colorfx_sky_blue_regs),
},
{
.regs = sensor_colorfx_grass_green_regs, //V4L2_COLORFX_GRASS_GREEN = 7,
.size = ARRAY_SIZE(sensor_colorfx_grass_green_regs),
},
{
.regs = sensor_colorfx_skin_whiten_regs, //V4L2_COLORFX_SKIN_WHITEN = 8,
.size = ARRAY_SIZE(sensor_colorfx_skin_whiten_regs),
},
{
.regs = sensor_colorfx_vivid_regs, //V4L2_COLORFX_VIVID = 9,
.size = ARRAY_SIZE(sensor_colorfx_vivid_regs),
},
{
.regs = sensor_colorfx_aqua_regs, //V4L2_COLORFX_AQUA = 10,
.size = ARRAY_SIZE(sensor_colorfx_aqua_regs),
},
{
.regs = sensor_colorfx_art_freeze_regs, //V4L2_COLORFX_ART_FREEZE = 11,
.size = ARRAY_SIZE(sensor_colorfx_art_freeze_regs),
},
{
.regs = sensor_colorfx_silhouette_regs, //V4L2_COLORFX_SILHOUETTE = 12,
.size = ARRAY_SIZE(sensor_colorfx_silhouette_regs),
},
{
.regs = sensor_colorfx_solarization_regs, //V4L2_COLORFX_SOLARIZATION = 13,
.size = ARRAY_SIZE(sensor_colorfx_solarization_regs),
},
{
.regs = sensor_colorfx_antique_regs, //V4L2_COLORFX_ANTIQUE = 14,
.size = ARRAY_SIZE(sensor_colorfx_antique_regs),
},
{
.regs = sensor_colorfx_set_cbcr_regs, //V4L2_COLORFX_SET_CBCR = 15,
.size = ARRAY_SIZE(sensor_colorfx_set_cbcr_regs),
},
};
/*
* The brightness setttings
*/
static struct regval_list sensor_brightness_neg4_regs[] = {
//NULL
};
static struct regval_list sensor_brightness_neg3_regs[] = {
//NULL
};
static struct regval_list sensor_brightness_neg2_regs[] = {
//NULL
};
static struct regval_list sensor_brightness_neg1_regs[] = {
//NULL
};
static struct regval_list sensor_brightness_zero_regs[] = {
//NULL
};
static struct regval_list sensor_brightness_pos1_regs[] = {
//NULL
};
static struct regval_list sensor_brightness_pos2_regs[] = {
//NULL
};
static struct regval_list sensor_brightness_pos3_regs[] = {
//NULL
};
static struct regval_list sensor_brightness_pos4_regs[] = {
//NULL
};
static struct cfg_array sensor_brightness[] = {
{
.regs = sensor_brightness_neg4_regs,
.size = ARRAY_SIZE(sensor_brightness_neg4_regs),
},
{
.regs = sensor_brightness_neg3_regs,
.size = ARRAY_SIZE(sensor_brightness_neg3_regs),
},
{
.regs = sensor_brightness_neg2_regs,
.size = ARRAY_SIZE(sensor_brightness_neg2_regs),
},
{
.regs = sensor_brightness_neg1_regs,
.size = ARRAY_SIZE(sensor_brightness_neg1_regs),
},
{
.regs = sensor_brightness_zero_regs,
.size = ARRAY_SIZE(sensor_brightness_zero_regs),
},
{
.regs = sensor_brightness_pos1_regs,
.size = ARRAY_SIZE(sensor_brightness_pos1_regs),
},
{
.regs = sensor_brightness_pos2_regs,
.size = ARRAY_SIZE(sensor_brightness_pos2_regs),
},
{
.regs = sensor_brightness_pos3_regs,
.size = ARRAY_SIZE(sensor_brightness_pos3_regs),
},
{
.regs = sensor_brightness_pos4_regs,
.size = ARRAY_SIZE(sensor_brightness_pos4_regs),
},
};
/*
* The contrast setttings
*/
static struct regval_list sensor_contrast_neg4_regs[] = {
//NULL
};
static struct regval_list sensor_contrast_neg3_regs[] = {
//NULL
};
static struct regval_list sensor_contrast_neg2_regs[] = {
//NULL
};
static struct regval_list sensor_contrast_neg1_regs[] = {
//NULL
};
static struct regval_list sensor_contrast_zero_regs[] = {
//NULL
};
static struct regval_list sensor_contrast_pos1_regs[] = {
//NULL
};
static struct regval_list sensor_contrast_pos2_regs[] = {
//NULL
};
static struct regval_list sensor_contrast_pos3_regs[] = {
//NULL
};
static struct regval_list sensor_contrast_pos4_regs[] = {
};
static struct cfg_array sensor_contrast[] = {
{
.regs = sensor_contrast_neg4_regs,
.size = ARRAY_SIZE(sensor_contrast_neg4_regs),
},
{
.regs = sensor_contrast_neg3_regs,
.size = ARRAY_SIZE(sensor_contrast_neg3_regs),
},
{
.regs = sensor_contrast_neg2_regs,
.size = ARRAY_SIZE(sensor_contrast_neg2_regs),
},
{
.regs = sensor_contrast_neg1_regs,
.size = ARRAY_SIZE(sensor_contrast_neg1_regs),
},
{
.regs = sensor_contrast_zero_regs,
.size = ARRAY_SIZE(sensor_contrast_zero_regs),
},
{
.regs = sensor_contrast_pos1_regs,
.size = ARRAY_SIZE(sensor_contrast_pos1_regs),
},
{
.regs = sensor_contrast_pos2_regs,
.size = ARRAY_SIZE(sensor_contrast_pos2_regs),
},
{
.regs = sensor_contrast_pos3_regs,
.size = ARRAY_SIZE(sensor_contrast_pos3_regs),
},
{
.regs = sensor_contrast_pos4_regs,
.size = ARRAY_SIZE(sensor_contrast_pos4_regs),
},
};
/*
* The saturation setttings
*/
static struct regval_list sensor_saturation_neg4_regs[] = {
//NULL
};
static struct regval_list sensor_saturation_neg3_regs[] = {
//NULL
};
static struct regval_list sensor_saturation_neg2_regs[] = {
//NULL
};
static struct regval_list sensor_saturation_neg1_regs[] = {
//NULL
};
static struct regval_list sensor_saturation_zero_regs[] = {
//NULL
};
static struct regval_list sensor_saturation_pos1_regs[] = {
//NULL
};
static struct regval_list sensor_saturation_pos2_regs[] = {
//NULL
};
static struct regval_list sensor_saturation_pos3_regs[] = {
//NULL
};
static struct regval_list sensor_saturation_pos4_regs[] = {
//NULL
};
static struct cfg_array sensor_saturation[] = {
{
.regs = sensor_saturation_neg4_regs,
.size = ARRAY_SIZE(sensor_saturation_neg4_regs),
},
{
.regs = sensor_saturation_neg3_regs,
.size = ARRAY_SIZE(sensor_saturation_neg3_regs),
},
{
.regs = sensor_saturation_neg2_regs,
.size = ARRAY_SIZE(sensor_saturation_neg2_regs),
},
{
.regs = sensor_saturation_neg1_regs,
.size = ARRAY_SIZE(sensor_saturation_neg1_regs),
},
{
.regs = sensor_saturation_zero_regs,
.size = ARRAY_SIZE(sensor_saturation_zero_regs),
},
{
.regs = sensor_saturation_pos1_regs,
.size = ARRAY_SIZE(sensor_saturation_pos1_regs),
},
{
.regs = sensor_saturation_pos2_regs,
.size = ARRAY_SIZE(sensor_saturation_pos2_regs),
},
{
.regs = sensor_saturation_pos3_regs,
.size = ARRAY_SIZE(sensor_saturation_pos3_regs),
},
{
.regs = sensor_saturation_pos4_regs,
.size = ARRAY_SIZE(sensor_saturation_pos4_regs),
},
};
/*
* The exposure target setttings
*/
static struct regval_list sensor_ev_neg4_regs[] = {
{0x55,0xc0},
};
static struct regval_list sensor_ev_neg3_regs[] = {
{0x55,0xb0},
};
static struct regval_list sensor_ev_neg2_regs[] = {
{0x55,0xa0},
};
static struct regval_list sensor_ev_neg1_regs[] = {
{0x55,0x90},
};
static struct regval_list sensor_ev_zero_regs[] = {
{0x55,0x00},
};
static struct regval_list sensor_ev_pos1_regs[] = {
{0x55,0x10},
};
static struct regval_list sensor_ev_pos2_regs[] = {
{0x55,0x20},
};
static struct regval_list sensor_ev_pos3_regs[] = {
{0x55,0x30},
};
static struct regval_list sensor_ev_pos4_regs[] = {
{0x55,0x40},
};
static struct cfg_array sensor_ev[] = {
{
.regs = sensor_ev_neg4_regs,
.size = ARRAY_SIZE(sensor_ev_neg4_regs),
},
{
.regs = sensor_ev_neg3_regs,
.size = ARRAY_SIZE(sensor_ev_neg3_regs),
},
{
.regs = sensor_ev_neg2_regs,
.size = ARRAY_SIZE(sensor_ev_neg2_regs),
},
{
.regs = sensor_ev_neg1_regs,
.size = ARRAY_SIZE(sensor_ev_neg1_regs),
},
{
.regs = sensor_ev_zero_regs,
.size = ARRAY_SIZE(sensor_ev_zero_regs),
},
{
.regs = sensor_ev_pos1_regs,
.size = ARRAY_SIZE(sensor_ev_pos1_regs),
},
{
.regs = sensor_ev_pos2_regs,
.size = ARRAY_SIZE(sensor_ev_pos2_regs),
},
{
.regs = sensor_ev_pos3_regs,
.size = ARRAY_SIZE(sensor_ev_pos3_regs),
},
{
.regs = sensor_ev_pos4_regs,
.size = ARRAY_SIZE(sensor_ev_pos4_regs),
},
};
/*
* Here we'll try to encapsulate the changes for just the output
* video format.
*
*/
static struct regval_list sensor_fmt_yuv422_yuyv[] = {
//YCbYCr
};
static struct regval_list sensor_fmt_yuv422_yvyu[] = {
//YCrYCb
};
static struct regval_list sensor_fmt_yuv422_vyuy[] = {
//CrYCbY
};
static struct regval_list sensor_fmt_yuv422_uyvy[] = {
//CbYCrY
};
static struct regval_list sensor_fmt_raw[] = {
};
static int sensor_g_hflip(struct v4l2_subdev *sd, __s32 *value)
{
return 0;
}
static int sensor_s_hflip(struct v4l2_subdev *sd, int value)
{
struct sensor_info *info = to_state(sd);
info->hflip = value;
return 0;
}
static int sensor_g_vflip(struct v4l2_subdev *sd, __s32 *value)
{
return 0;
}
static int sensor_s_vflip(struct v4l2_subdev *sd, int value)
{
return 0;
}
static int sensor_g_autogain(struct v4l2_subdev *sd, __s32 *value)
{
return -EINVAL;
}
static int sensor_s_autogain(struct v4l2_subdev *sd, int value)
{
return -EINVAL;
}
static int sensor_g_autoexp(struct v4l2_subdev *sd, __s32 *value)
{
return 0;
}
static int sensor_s_autoexp(struct v4l2_subdev *sd,
enum v4l2_exposure_auto_type value)
{
return 0;
}
static int sensor_g_autowb(struct v4l2_subdev *sd, int *value)
{
return 0;
}
static int sensor_s_autowb(struct v4l2_subdev *sd, int value)
{
return 0;
}
static int sensor_g_hue(struct v4l2_subdev *sd, __s32 *value)
{
return -EINVAL;
}
static int sensor_s_hue(struct v4l2_subdev *sd, int value)
{
return -EINVAL;
}
static int sensor_g_gain(struct v4l2_subdev *sd, __s32 *value)
{
return -EINVAL;
}
static int sensor_s_gain(struct v4l2_subdev *sd, int value)
{
return -EINVAL;
}
/* *********************************************end of ******************************************** */
static int sensor_g_brightness(struct v4l2_subdev *sd, __s32 *value)
{
struct sensor_info *info = to_state(sd);
*value = info->brightness;
return 0;
}
static int sensor_s_brightness(struct v4l2_subdev *sd, int value)
{
struct sensor_info *info = to_state(sd);
if(info->brightness == value)
return 0;
if(value < -4 || value > 4)
return -ERANGE;
LOG_ERR_RET(sensor_write_array(sd, sensor_brightness[value+4].regs, sensor_brightness[value+4].size))
info->brightness = value;
return 0;
}
static int sensor_g_contrast(struct v4l2_subdev *sd, __s32 *value)
{
struct sensor_info *info = to_state(sd);
*value = info->contrast;
return 0;
}
static int sensor_s_contrast(struct v4l2_subdev *sd, int value)
{
struct sensor_info *info = to_state(sd);
if(info->contrast == value)
return 0;
if(value < -4 || value > 4)
return -ERANGE;
LOG_ERR_RET(sensor_write_array(sd, sensor_contrast[value+4].regs, sensor_contrast[value+4].size))
info->contrast = value;
return 0;
}
static int sensor_g_saturation(struct v4l2_subdev *sd, __s32 *value)
{
struct sensor_info *info = to_state(sd);
*value = info->saturation;
return 0;
}
static int sensor_s_saturation(struct v4l2_subdev *sd, int value)
{
struct sensor_info *info = to_state(sd);
if(info->saturation == value)
return 0;
if(value < -4 || value > 4)
return -ERANGE;
LOG_ERR_RET(sensor_write_array(sd, sensor_saturation[value+4].regs, sensor_saturation[value+4].size))
info->saturation = value;
return 0;
}
static int sensor_g_exp_bias(struct v4l2_subdev *sd, __s32 *value)
{
struct sensor_info *info = to_state(sd);
*value = info->exp_bias;
return 0;
}
static int sensor_s_exp_bias(struct v4l2_subdev *sd, int value)
{
struct sensor_info *info = to_state(sd);
if(info->exp_bias == value)
return 0;
if(value < -4 || value > 4)
return -ERANGE;
LOG_ERR_RET(sensor_write_array(sd, sensor_ev[value+4].regs, sensor_ev[value+4].size))
info->exp_bias = value;
return 0;
}
static int sensor_g_wb(struct v4l2_subdev *sd, int *value)
{
struct sensor_info *info = to_state(sd);
enum v4l2_auto_n_preset_white_balance *wb_type = (enum v4l2_auto_n_preset_white_balance*)value;
*wb_type = info->wb;
return 0;
}
static int sensor_s_wb(struct v4l2_subdev *sd,
enum v4l2_auto_n_preset_white_balance value)
{
struct sensor_info *info = to_state(sd);
if(info->capture_mode == V4L2_MODE_IMAGE)
return 0;
if(info->wb == value)
return 0;
LOG_ERR_RET(sensor_write_array(sd, sensor_wb[value].regs ,sensor_wb[value].size) )
if (value == V4L2_WHITE_BALANCE_AUTO)
info->autowb = 1;
else
info->autowb = 0;
info->wb = value;
return 0;
}
static int sensor_g_colorfx(struct v4l2_subdev *sd,
__s32 *value)
{
struct sensor_info *info = to_state(sd);
enum v4l2_colorfx *clrfx_type = (enum v4l2_colorfx*)value;
*clrfx_type = info->clrfx;
return 0;
}
static int sensor_s_colorfx(struct v4l2_subdev *sd,
enum v4l2_colorfx value)
{
struct sensor_info *info = to_state(sd);
if(info->clrfx == value)
return 0;
LOG_ERR_RET(sensor_write_array(sd, sensor_colorfx[value].regs, sensor_colorfx[value].size))
info->clrfx = value;
return 0;
}
static int sensor_g_flash_mode(struct v4l2_subdev *sd,
__s32 *value)
{
struct sensor_info *info = to_state(sd);
enum v4l2_flash_led_mode *flash_mode = (enum v4l2_flash_led_mode*)value;
*flash_mode = info->flash_mode;
return 0;
}
static int sensor_s_flash_mode(struct v4l2_subdev *sd,
enum v4l2_flash_led_mode value)
{
struct sensor_info *info = to_state(sd);
info->flash_mode = value;
return 0;
}
/*
* Stuff that knows about the sensor.
*/
static int sensor_power(struct v4l2_subdev *sd, int on)
{
cci_lock(sd);
switch(on)
{
case CSI_SUBDEV_STBY_ON:
vfe_dev_dbg("CSI_SUBDEV_STBY_ON\n");
vfe_gpio_write(sd,PWDN,CSI_GPIO_HIGH);
usleep_range(30000,31000);
vfe_set_mclk(sd,OFF);
break;
case CSI_SUBDEV_STBY_OFF:
vfe_dev_dbg("CSI_SUBDEV_STBY_OFF\n");
vfe_set_mclk_freq(sd,MCLK);
vfe_set_mclk(sd,ON);
usleep_range(30000,31000);
vfe_gpio_write(sd,PWDN,CSI_GPIO_LOW);
usleep_range(10000,12000);
break;
case CSI_SUBDEV_PWR_ON:
vfe_dev_dbg("CSI_SUBDEV_PWR_ON\n");
vfe_gpio_set_status(sd,PWDN,1);//set the gpio to output
vfe_gpio_set_status(sd,RESET,1);//set the gpio to output
usleep_range(10000,12000);
vfe_gpio_write(sd,PWDN,CSI_GPIO_HIGH);
vfe_gpio_write(sd,RESET,CSI_GPIO_LOW);
vfe_gpio_write(sd,POWER_EN,CSI_GPIO_HIGH);
vfe_set_pmu_channel(sd,IOVDD,ON);
vfe_set_pmu_channel(sd,AVDD,ON);
vfe_set_pmu_channel(sd,DVDD,ON);
vfe_set_pmu_channel(sd,AFVDD,ON);
usleep_range(20000,22000);
vfe_gpio_write(sd,PWDN,CSI_GPIO_LOW);
usleep_range(10000,12000);
vfe_set_mclk_freq(sd,MCLK);
vfe_set_mclk(sd,ON);
usleep_range(10000,12000);
vfe_gpio_write(sd,RESET,CSI_GPIO_LOW);
usleep_range(30000,31000);
vfe_gpio_write(sd,RESET,CSI_GPIO_HIGH);
usleep_range(30000,31000);
break;
case CSI_SUBDEV_PWR_OFF:
vfe_dev_dbg("CSI_SUBDEV_PWR_OFF\n");
usleep_range(10000,12000);
vfe_gpio_write(sd,RESET,CSI_GPIO_LOW);
usleep_range(10000,12000);
vfe_set_mclk(sd,OFF);
vfe_gpio_write(sd,POWER_EN,CSI_GPIO_LOW);
vfe_set_pmu_channel(sd,AFVDD,OFF);
vfe_set_pmu_channel(sd,DVDD,OFF);
vfe_set_pmu_channel(sd,AVDD,OFF);
vfe_set_pmu_channel(sd,IOVDD,OFF);
usleep_range(10000,12000);
vfe_gpio_write(sd,PWDN,CSI_GPIO_HIGH);
usleep_range(10000,12000);
vfe_gpio_set_status(sd,RESET,0);//set the gpio to input
vfe_gpio_set_status(sd,PWDN,0);//set the gpio to input
break;
default:
return -EINVAL;
}
cci_unlock(sd);
return 0;
}
static int sensor_reset(struct v4l2_subdev *sd, u32 val)
{
switch(val)
{
case 0:
vfe_gpio_write(sd,RESET,CSI_GPIO_HIGH);
usleep_range(10000,12000);
break;
case 1:
vfe_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)
{
int ret;
data_type val;
ret = sensor_read(sd, 0xfc, &val);
if (ret < 0) {
vfe_dev_err("sensor_read err at sensor_detect!\n");
return ret;
}
if(val != 0x3a)
return -ENODEV;
return 0;
}
static int sensor_init(struct v4l2_subdev *sd, u32 val)
{
int ret;
vfe_dev_dbg("sensor_init\n");
/*Make sure it is a target sensor*/
ret = sensor_detect(sd);
if (ret) {
vfe_dev_err("chip found is not an target chip.\n");
return ret;
}
sensor_write_array(sd, sensor_default_regs_24M , ARRAY_SIZE(sensor_default_regs_24M));
return sensor_write_array(sd, sensor_default_regs , ARRAY_SIZE(sensor_default_regs));
}
static long sensor_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
int ret=0;
return ret;
}
/*
* Store information about the video data format.
*/
static struct sensor_format_struct {
__u8 *desc;
//__u32 pixelformat;
enum v4l2_mbus_pixelcode mbus_code;//linux-3.0
struct regval_list *regs;
int regs_size;
int bpp; /* Bytes per pixel */
} sensor_formats[] = {
{
.desc = "YUYV 4:2:2",
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,//linux-3.0
.regs = sensor_fmt_yuv422_yuyv,
.regs_size = ARRAY_SIZE(sensor_fmt_yuv422_yuyv),
.bpp = 2,
},
{
.desc = "YVYU 4:2:2",
.mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,//linux-3.0
.regs = sensor_fmt_yuv422_yvyu,
.regs_size = ARRAY_SIZE(sensor_fmt_yuv422_yvyu),
.bpp = 2,
},
{
.desc = "UYVY 4:2:2",
.mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,//linux-3.0
.regs = sensor_fmt_yuv422_uyvy,
.regs_size = ARRAY_SIZE(sensor_fmt_yuv422_uyvy),
.bpp = 2,
},
{
.desc = "VYUY 4:2:2",
.mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,//linux-3.0
.regs = sensor_fmt_yuv422_vyuy,
.regs_size = ARRAY_SIZE(sensor_fmt_yuv422_vyuy),
.bpp = 2,
},
{
.desc = "Raw RGB Bayer",
.mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,//linux-3.0
.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[] = {
/* VGA */
{
.width = VGA_WIDTH,
.height = VGA_HEIGHT,
.hoffset = 0,
.voffset = 0,
.regs = NULL,
.regs_size = 0,
.set_size = NULL,
},
};
#define N_WIN_SIZES (ARRAY_SIZE(sensor_win_sizes))
static int sensor_enum_fmt(struct v4l2_subdev *sd, unsigned index,
enum v4l2_mbus_pixelcode *code)
{
if (index >= N_FMTS)
return -EINVAL;
*code = sensor_formats[index].mbus_code;
return 0;
}
static int sensor_enum_size(struct v4l2_subdev *sd,
struct v4l2_frmsizeenum *fsize)
{
if(fsize->index > N_WIN_SIZES-1)
return -EINVAL;
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->discrete.width = sensor_win_sizes[fsize->index].width;
fsize->discrete.height = sensor_win_sizes[fsize->index].height;
return 0;
}
static int sensor_try_fmt_internal(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt,
struct sensor_format_struct **ret_fmt,
struct sensor_win_size **ret_wsize)
{
int index;
struct sensor_win_size *wsize;
for (index = 0; index < N_FMTS; index++)
if (sensor_formats[index].mbus_code == fmt->code)
break;
if (index >= N_FMTS)
return -EINVAL;
if (ret_fmt != NULL)
*ret_fmt = sensor_formats + index;
/*
* Fields: the sensor devices claim to be progressive.
*/
fmt->field = V4L2_FIELD_NONE;
/*
* Round requested image size down to the nearest
* we support, but not below the smallest.
*/
for (wsize = sensor_win_sizes; wsize < sensor_win_sizes + N_WIN_SIZES; wsize++)
if (fmt->width >= wsize->width && fmt->height >= wsize->height)
break;
if (wsize >= sensor_win_sizes + N_WIN_SIZES)
wsize--; /* Take the smallest one */
if (ret_wsize != NULL)
*ret_wsize = wsize;
/*
* Note the size we'll actually handle.
*/
fmt->width = wsize->width;
fmt->height = wsize->height;
return 0;
}
static int sensor_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt)//linux-3.0
{
return sensor_try_fmt_internal(sd, fmt, NULL, NULL);
}
static int sensor_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
cfg->type = V4L2_MBUS_PARALLEL;
cfg->flags = V4L2_MBUS_MASTER | VREF_POL | HREF_POL | CLK_POL ;
return 0;
}
/*
* Set a format.
*/
static int sensor_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt)//linux-3.0
{
int ret;
struct sensor_format_struct *sensor_fmt;
struct sensor_win_size *wsize;
struct sensor_info *info = to_state(sd);
vfe_dev_dbg("sensor_s_fmt\n");
ret = sensor_try_fmt_internal(sd, fmt, &sensor_fmt, &wsize);
if (ret)
return ret;
sensor_write_array(sd, sensor_fmt->regs , sensor_fmt->regs_size);
ret = 0;
if (wsize->regs)
{
ret = sensor_write_array(sd, wsize->regs , wsize->regs_size);
if (ret < 0)
return ret;
}
if (wsize->set_size)
{
ret = wsize->set_size(sd);
if (ret < 0)
return ret;
}
info->fmt = sensor_fmt;
info->width = wsize->width;
info->height = wsize->height;
return 0;
}
/*
* Implement G/S_PARM. There is a "high quality" mode we could try
* to do someday; for now, we just do the frame rate tweak.
*/
static int sensor_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
{
struct v4l2_captureparm *cp = &parms->parm.capture;
//struct sensor_info *info = to_state(sd);
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
memset(cp, 0, sizeof(struct v4l2_captureparm));
cp->capability = V4L2_CAP_TIMEPERFRAME;
cp->timeperframe.numerator = 1;
cp->timeperframe.denominator = SENSOR_FRAME_RATE;
return 0;
}
static int sensor_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
{
return 0;
}
/*
* 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
*/
/* *********************************************begin of ******************************************** */
static int sensor_queryctrl(struct v4l2_subdev *sd,
struct v4l2_queryctrl *qc)
{
/* Fill in min, max, step and default value for these controls. */
/* see include/linux/videodev2.h for details */
/* see sensor_s_parm and sensor_g_parm for the meaning of value */
switch (qc->id) {
// case V4L2_CID_BRIGHTNESS:
// return v4l2_ctrl_query_fill(qc, -4, 4, 1, 1);
// case V4L2_CID_CONTRAST:
// return v4l2_ctrl_query_fill(qc, -4, 4, 1, 1);
// case V4L2_CID_SATURATION:
// return v4l2_ctrl_query_fill(qc, -4, 4, 1, 1);
// case V4L2_CID_HUE:
// return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0);
case V4L2_CID_VFLIP:
case V4L2_CID_HFLIP:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
// case V4L2_CID_GAIN:
// return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
// case V4L2_CID_AUTOGAIN:
// return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
case V4L2_CID_EXPOSURE:
case V4L2_CID_AUTO_EXPOSURE_BIAS:
return v4l2_ctrl_query_fill(qc, -4, 4, 1, 0);
case V4L2_CID_EXPOSURE_AUTO:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
return v4l2_ctrl_query_fill(qc, 0, 9, 1, 1);
case V4L2_CID_AUTO_WHITE_BALANCE:
return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
case V4L2_CID_COLORFX:
return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
case V4L2_CID_FLASH_LED_MODE:
return v4l2_ctrl_query_fill(qc, 0, 4, 1, 0);
}
return -EINVAL;
}
static int sensor_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
return sensor_g_brightness(sd, &ctrl->value);
case V4L2_CID_CONTRAST:
return sensor_g_contrast(sd, &ctrl->value);
case V4L2_CID_SATURATION:
return sensor_g_saturation(sd, &ctrl->value);
case V4L2_CID_HUE:
return sensor_g_hue(sd, &ctrl->value);
case V4L2_CID_VFLIP:
return sensor_g_vflip(sd, &ctrl->value);
case V4L2_CID_HFLIP:
return sensor_g_hflip(sd, &ctrl->value);
case V4L2_CID_GAIN:
return sensor_g_gain(sd, &ctrl->value);
case V4L2_CID_AUTOGAIN:
return sensor_g_autogain(sd, &ctrl->value);
case V4L2_CID_EXPOSURE:
case V4L2_CID_AUTO_EXPOSURE_BIAS:
return sensor_g_exp_bias(sd, &ctrl->value);
case V4L2_CID_EXPOSURE_AUTO:
return sensor_g_autoexp(sd, &ctrl->value);
case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
return sensor_g_wb(sd, &ctrl->value);
case V4L2_CID_AUTO_WHITE_BALANCE:
return sensor_g_autowb(sd, &ctrl->value);
case V4L2_CID_COLORFX:
return sensor_g_colorfx(sd, &ctrl->value);
case V4L2_CID_FLASH_LED_MODE:
return sensor_g_flash_mode(sd, &ctrl->value);
}
return -EINVAL;
}
static int sensor_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
struct v4l2_queryctrl qc;
int ret;
qc.id = ctrl->id;
ret = sensor_queryctrl(sd, &qc);
if (ret < 0) {
return ret;
}
if (qc.type == V4L2_CTRL_TYPE_MENU ||
qc.type == V4L2_CTRL_TYPE_INTEGER ||
qc.type == V4L2_CTRL_TYPE_BOOLEAN)
{
if (ctrl->value < qc.minimum || ctrl->value > qc.maximum) {
return -ERANGE;
}
}
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
return sensor_s_brightness(sd, ctrl->value);
case V4L2_CID_CONTRAST:
return sensor_s_contrast(sd, ctrl->value);
case V4L2_CID_SATURATION:
return sensor_s_saturation(sd, ctrl->value);
case V4L2_CID_HUE:
return sensor_s_hue(sd, ctrl->value);
case V4L2_CID_VFLIP:
return sensor_s_vflip(sd, ctrl->value);
case V4L2_CID_HFLIP:
return sensor_s_hflip(sd, ctrl->value);
case V4L2_CID_GAIN:
return sensor_s_gain(sd, ctrl->value);
case V4L2_CID_AUTOGAIN:
return sensor_s_autogain(sd, ctrl->value);
case V4L2_CID_EXPOSURE:
case V4L2_CID_AUTO_EXPOSURE_BIAS:
return sensor_s_exp_bias(sd, ctrl->value);
case V4L2_CID_EXPOSURE_AUTO:
return sensor_s_autoexp(sd, (enum v4l2_exposure_auto_type) ctrl->value);
case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
return sensor_s_wb(sd, (enum v4l2_auto_n_preset_white_balance) ctrl->value);
case V4L2_CID_AUTO_WHITE_BALANCE:
return sensor_s_autowb(sd, ctrl->value);
case V4L2_CID_COLORFX:
return sensor_s_colorfx(sd, (enum v4l2_colorfx) ctrl->value);
case V4L2_CID_FLASH_LED_MODE:
return sensor_s_flash_mode(sd, (enum v4l2_flash_led_mode) ctrl->value);
}
return -EINVAL;
}
static int sensor_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SENSOR, 0);
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops sensor_core_ops = {
.g_chip_ident = sensor_g_chip_ident,
.g_ctrl = sensor_g_ctrl,
.s_ctrl = sensor_s_ctrl,
.queryctrl = sensor_queryctrl,
.reset = sensor_reset,
.init = sensor_init,
.s_power = sensor_power,
.ioctl = sensor_ioctl,
};
static const struct v4l2_subdev_video_ops sensor_video_ops = {
.enum_mbus_fmt = sensor_enum_fmt,
.enum_framesizes = sensor_enum_size,
.try_mbus_fmt = sensor_try_fmt,
.s_mbus_fmt = sensor_s_fmt,
.s_parm = sensor_s_parm,
.g_parm = sensor_g_parm,
.g_mbus_config = sensor_g_mbus_config,
};
static const struct v4l2_subdev_ops sensor_ops = {
.core = &sensor_core_ops,
.video = &sensor_video_ops,
};
/* ----------------------------------------------------------------------- */
static struct cci_driver cci_drv = {
.name = SENSOR_NAME,
.addr_width = CCI_BITS_8,
.data_width = CCI_BITS_8,
};
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);
info->fmt = &sensor_formats[0];
info->brightness = 0;
info->contrast = 0;
info->saturation = 0;
info->hue = 0;
info->hflip = 0;
info->vflip = 0;
info->gain = 0;
info->autogain = 1;
info->exp = 0;
info->autoexp = 0;
info->autowb = 1;
info->wb = 0;
info->clrfx = 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);