camera驱动小节二:添加同时基于V4L2和I2C的设备和驱动
2015-02-11 01:52
615 查看
//设备信息
//arch/arm/mach-s5pv210/mach-cw210.c //设备以ov9650为例
static struct i2c_board_info ov9650_i2c_info =
{
I2C_BOARD_INFO("OV9650", 0x60>>1),//会被 扫描 并生成 i2c_client
.platform_data = &ov9650_plat,
};
static struct s3c_platform_camera ov9650 = {
//设备信息
.id = CAMERA_PAR_A,
.type = CAM_TYPE_ITU,
.fmt = ITU_601_YCBCR422_8BIT,
.order422
= CAM_ORDER422_8BIT_CBYCRY,
.i2c_busnum
= IIC_NUM_CAM_USED,
.info = &ov9650_i2c_info,
.pixelformat
= V4L2_PIX_FMT_VYUY,
.srclk_name
= "mout_mpll",
.clk_name
= "sclk_cam0",
.clk_rate
= 24000000,
// .line_length= 640,
.line_length
= 1920,
.width
= 640,
.height
= 480,
.window
= {
.left = 0,
.top = 0,
.width
= 640,
.height= 480,
},
/* Polarity */
.inv_pclk
= 0,
.inv_vsync
= 0,
.inv_href
= 0,
.inv_hsync
= 0,
.initialized
= 0,
// .cam_power
= smdkv210_OV9650_power,
.cam_power
= tqcam_OV9650_power,
};
//这里,设备信息 会被添加到平台数据,以下为添加过程
static struct s3c_platform_fimc fimc_plat_lsi= {
.srclk_name
= "mout_mpll",
.clk_name
= "sclk_fimc",
.lclk_name
= "sclk_fimc_lclk",
.clk_rate
= 166750000,
.default_cam
= CAMERA_PAR_A,
.camera
= {
#ifdef CONFIG_CAMERA_OV3640
&ov3640,
#endif
#ifdef CONFIG_CAMERA_OV9650
&ov9650,
#endif
#ifdef CONFIG_CAMERA_SAA7113
&saa7113,
#endif
#ifdef CONFIG_CAMERA_TVP5150
&tvp5150,
#endif
},
.hw_ver
= 0x43,
};
//查找fimc_plat_lsi
static void __init smdkc110_machine_init(void)
{
arm_pm_restart = smdkc110_pm_restart;
s3c_usb_set_serial();
platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));
#ifdef CONFIG_ANDROID_PMEM
platform_device_register(&pmem_gpu1_device);
#endif
pm_power_off = smdkc110_power_off ;
#ifdef CONFIG_ANDROID_PMEM
android_pmem_set_platdata();
#endif
/* i2c */
s3c_i2c0_set_platdata(NULL);
s3c_i2c1_set_platdata(NULL);
s3c_i2c2_set_platdata(NULL);
sound_init();
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));
i2c_register_board_info(5, i2c_devs5, ARRAY_SIZE(i2c_devs5));
#ifdef CONFIG_DM9000
smdkc110_dm9000_set();
#endif
#if defined(CONFIG_FB_S3C_LTE480WV)||defined(CONFIG_FB_S3C_LCD800480)||defined(CONFIG_FB_S3C_LCD800600)||defined(CONFIG_FB_S3C_VGA1024768)||defined(CONFIG_FB_S3C_LCD640480)||defined(CONFIG_FB_S3C_LCD480272)||defined(CONFIG_FB_AT070TN92) ||defined (CONFIG_FB_S3C_VGA640480)
s3cfb_set_platdata(<e480wv_fb_data);
#endif
#ifdef CONFIG_CW210_VGA
s3cfb_set_platdata(&vga_fb_data);
#endif
/* spi */
#ifdef CONFIG_S3C64XX_DEV_SPI
if (!gpio_request(S5PV210_GPB(1), "SPI_CS0")) {
gpio_direction_output(S5PV210_GPB(1), 1);
s3c_gpio_cfgpin(S5PV210_GPB(1), S3C_GPIO_SFN(1));
s3c_gpio_setpull(S5PV210_GPB(1), S3C_GPIO_PULL_UP);
s5pv210_spi_set_info(0, S5PV210_SPI_SRCCLK_PCLK,
ARRAY_SIZE(smdk_spi0_csi));
}
if (!gpio_request(S5PV210_GPB(5), "SPI_CS1")) {
gpio_direction_output(S5PV210_GPB(5), 1);
s3c_gpio_cfgpin(S5PV210_GPB(5), S3C_GPIO_SFN(1));
s3c_gpio_setpull(S5PV210_GPB(5), S3C_GPIO_PULL_UP);
s5pv210_spi_set_info(1, S5PV210_SPI_SRCCLK_PCLK,
ARRAY_SIZE(smdk_spi1_csi));
}
spi_register_board_info(s3c_spi_devs, ARRAY_SIZE(s3c_spi_devs));
#endif
#if defined(CONFIG_TOUCHSCREEN_S3C)
s3c_ts_set_platdata(&s3c_t
4000
s_platform);
#endif
#if defined(CONFIG_S5P_ADC)
s3c_adc_set_platdata(&s3c_adc_platform);
#endif
#if defined(CONFIG_PM)
s3c_pm_init();
#endif
#ifdef CONFIG_VIDEO_FIMC
/* fimc */
s3c_fimc0_set_platdata(&fimc_plat_lsi);//在这里会被添加到平台数据,便于以后调用,由于有三个fimc控制器,所以设备需要每个 fimc添加一次
s3c_fimc1_set_platdata(&fimc_plat_lsi);
s3c_fimc2_set_platdata(&fimc_plat_lsi);
/* external camera */
/* smdkv210_cam0_power(1); */
/* smdkv210_cam1_power(1); */
#endif
#ifdef CONFIG_VIDEO_FIMC_MIPI
s3c_csis_set_platdata(NULL);
#endif
#ifdef CONFIG_VIDEO_JPEG_V2
s3c_jpeg_set_platdata(&jpeg_plat);
#endif
#ifdef CONFIG_VIDEO_MFC50
/* mfc */
s3c_mfc_set_platdata(NULL);
#endif
#ifdef CONFIG_VIDEO_TV20
s3c_set_qos();
#endif
#ifdef CONFIG_S3C_DEV_HSMMC
s5pv210_default_sdhci0();
#endif
#ifdef CONFIG_S3C_DEV_HSMMC1
s5pv210_default_sdhci1();
#endif
#ifdef CONFIG_S3C_DEV_HSMMC2
s5pv210_default_sdhci2();
#endif
#ifdef CONFIG_S3C_DEV_HSMMC3
s5pv210_default_sdhci3();
#endif
#ifdef CONFIG_S5PV210_SETUP_SDHCI
s3c_sdhci_set_platdata();
#endif
#ifdef CONFIG_BACKLIGHT_PWM
smdk_backlight_register();
#endif
regulator_has_full_constraints();
smdkc110_setup_clocks();
}
//设备信息添加到平台数据(pdata)后,会创建i2c设备和v4l2设备
//以下为创建过程
//没有典型 ov9650实例,driver部分以mt9v011为例
在/driver/media/video/samsung/fimc_capture.c中会调用v4l2_i2c_new_subdev_board同时注册V4L2和I2C设备,这里暂时未查找以上调用过程。
//定义
/* Load an i2c sub-device. */
struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
struct i2c_adapter *adapter, const char *module_name,
struct i2c_board_info *info, const unsigned short *probe_addrs)
{
struct v4l2_subdev *sd = NULL;
struct i2c_client *client;
BUG_ON(!v4l2_dev);
if (module_name)
request_module(module_name);
/* Create the i2c client */
if (info->addr == 0 && probe_addrs)
client = i2c_new_probed_device(adapter, info, probe_addrs);
else
client = i2c_new_device(adapter, info);//创建i2c_client设备 调用 driver -> prop函数
/* Note: by loading the module first we are certain that c->driver
will be set if the driver was found. If the module was not loaded
first, then the i2c core tries to delay-load the module for us,
and then c->driver is still NULL until the module is finally
loaded. This delay-load mechanism doesn't work if other drivers
want to use the i2c device, so explicitly loading the module
is the best alternative. */
if (client == NULL || client->driver == NULL)
goto error;
/* Lock the module so we can safely get the v4l2_subdev pointer */
if (!try_module_get(client->driver->driver.owner))
goto error;
sd = i2c_get_clientdata(client);
/* Register with the v4l2_device which increases the module's
use count as well. */
if (v4l2_device_register_subdev(v4l2_dev, sd))//注册subdev 设备 注册成功会调用v4l2_subdev_call
-> v4l2_subdev_core_ops.s_config
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
module_put(client->driver->driver.owner);
if (sd) {
/* We return errors from v4l2_subdev_call only if we have the
callback as the .s_config is not mandatory */
int err = v4l2_subdev_call(sd, core, s_config,
info->irq, info->platform_data);
if (err && err != -ENOIOCTLCMD) {
v4l2_device_unregister_subdev(sd);
sd = NULL;
}
}
error:
/* If we have a client but no subdev, then something went wrong and
we must unregister the client. */
if (client && sd == NULL)
i2c_unregister_device(client);
return sd;
}
/*****************************************以上i2c_client和sub_dev均创建并注册完毕*********************************************/
/*****************************************以下为驱动driver 部分********************************************************************/
//即将注册的驱动,因为没有注册函数,所以需要小小转变一下
static struct v4l2_i2c_driver_data
v4l2_i2c_data = {
.name = "mt9v011",
.probe = mt9v011_probe,
.remove = mt9v011_remove,
.id_table = mt9v011_id,
};
//在include/media/v4l2-i2c-drv.h
struct v4l2_i2c_driver_data {
const char * const name;
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
int (*suspend)(struct i2c_client *client, pm_message_t state);
int (*resume)(struct i2c_client *client);
const struct i2c_device_id *id_table;
};
static struct v4l2_i2c_driver_data
v4l2_i2c_data;
static struct i2c_driver v4l2_i2c_driver;
/* Bus-based I2C implementation for kernels >= 2.6.26 */
static int __init v4l2_i2c_drv_init(void)
{
v4l2_i2c_driver.driver.name =v4l2_i2c_data.name;//加载模块后 会自动由v4l2_i2c_data 转变为 v4l2_i2c_driver,然后注册v4l2_i2c_driver
v4l2_i2c_driver.command =v4l2_i2c_data.command;
v4l2_i2c_driver.probe =v4l2_i2c_data.probe;
v4l2_i2c_driver.remove =v4l2_i2c_data.remove;
v4l2_i2c_driver.suspend =v4l2_i2c_data.suspend;
v4l2_i2c_driver.resume =v4l2_i2c_data.resume;
v4l2_i2c_driver.id_table =v4l2_i2c_data.id_table;
return i2c_add_driver(&v4l2_i2c_driver);//注册i2c驱动
}
//驱动操作接口
//针对v4l2-subdev,驱动里面注册了两套操作集合,分别是v4l2_subdev_video_ops和v4l2_subdev_core_ops。
static const structv4l2_subdev_core_ops
mt9v011_core_ops = {
.queryctrl = mt9v011_queryctrl,
.g_ctrl = mt9v011_g_ctrl,
.s_ctrl = mt9v011_s_ctrl,
.reset = mt9v011_reset,
.s_config = mt9v011_s_config,
.g_chip_ident = mt9v011_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9v011_g_register,
.s_register = mt9v011_s_register,
#endif
};
static const struct v4l2_subdev_video_opsmt9v011_video_ops = {
.enum_mbus_fmt = mt9v011_enum_mbus_fmt,
.try_mbus_fmt = mt9v011_try_mbus_fmt,
.s_mbus_fmt = mt9v011_s_mbus_fmt,
.g_parm = mt9v011_g_parm,
.s_parm = mt9v011_s_parm,
};
static const struct v4l2_subdev_ops mt9v011_ops = {
.core = &mt9v011_core_ops,
.video = &mt9v011_video_ops,
};
//在调用probe回调时 会执行
/****************************************************************************
I2C Client & Driver
****************************************************************************/
static int mt9v011_probe(struct i2c_client *c,
const struct i2c_device_id *id)
{
u16 version;
struct mt9v011 *core;
struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(c->adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return -EIO;
core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL);
if (!core)
return -ENOMEM;
sd = &core->sd;
v4l2_i2c_subdev_init(sd, c, &mt9v011_ops);
/* Check if the sensor is really a MT9V011 */
version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
if ((version != MT9V011_VERSION) &&
(version != MT9V011_REV_B_VERSION)) {
v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n",
version);
kfree(core);
return -EINVAL;
}
core->global_gain = 0x0024;
core->width = 640;
core->height = 480;
core->xtal = 27000000;/* Hz */
v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n",
c->addr << 1, c->adapter->name, version);
return 0;
}
/* I2C Helper functions */
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
const struct v4l2_subdev_ops *ops)
{
v4l2_subdev_init(sd, ops);
sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
/* the owner is the same as the i2c_client's driver owner */
sd->owner = client->driver->driver.owner;
/* i2c_client and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, client);//分别关联V412和I2C设备
i2c_set_clientdata(client, sd);
/* initialize name */
snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
client->driver->driver.name, i2c_adapter_id(client->adapter),
client->addr);
}
//arch/arm/mach-s5pv210/mach-cw210.c //设备以ov9650为例
static struct i2c_board_info ov9650_i2c_info =
{
I2C_BOARD_INFO("OV9650", 0x60>>1),//会被 扫描 并生成 i2c_client
.platform_data = &ov9650_plat,
};
static struct s3c_platform_camera ov9650 = {
//设备信息
.id = CAMERA_PAR_A,
.type = CAM_TYPE_ITU,
.fmt = ITU_601_YCBCR422_8BIT,
.order422
= CAM_ORDER422_8BIT_CBYCRY,
.i2c_busnum
= IIC_NUM_CAM_USED,
.info = &ov9650_i2c_info,
.pixelformat
= V4L2_PIX_FMT_VYUY,
.srclk_name
= "mout_mpll",
.clk_name
= "sclk_cam0",
.clk_rate
= 24000000,
// .line_length= 640,
.line_length
= 1920,
.width
= 640,
.height
= 480,
.window
= {
.left = 0,
.top = 0,
.width
= 640,
.height= 480,
},
/* Polarity */
.inv_pclk
= 0,
.inv_vsync
= 0,
.inv_href
= 0,
.inv_hsync
= 0,
.initialized
= 0,
// .cam_power
= smdkv210_OV9650_power,
.cam_power
= tqcam_OV9650_power,
};
//这里,设备信息 会被添加到平台数据,以下为添加过程
static struct s3c_platform_fimc fimc_plat_lsi= {
.srclk_name
= "mout_mpll",
.clk_name
= "sclk_fimc",
.lclk_name
= "sclk_fimc_lclk",
.clk_rate
= 166750000,
.default_cam
= CAMERA_PAR_A,
.camera
= {
#ifdef CONFIG_CAMERA_OV3640
&ov3640,
#endif
#ifdef CONFIG_CAMERA_OV9650
&ov9650,
#endif
#ifdef CONFIG_CAMERA_SAA7113
&saa7113,
#endif
#ifdef CONFIG_CAMERA_TVP5150
&tvp5150,
#endif
},
.hw_ver
= 0x43,
};
//查找fimc_plat_lsi
static void __init smdkc110_machine_init(void)
{
arm_pm_restart = smdkc110_pm_restart;
s3c_usb_set_serial();
platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));
#ifdef CONFIG_ANDROID_PMEM
platform_device_register(&pmem_gpu1_device);
#endif
pm_power_off = smdkc110_power_off ;
#ifdef CONFIG_ANDROID_PMEM
android_pmem_set_platdata();
#endif
/* i2c */
s3c_i2c0_set_platdata(NULL);
s3c_i2c1_set_platdata(NULL);
s3c_i2c2_set_platdata(NULL);
sound_init();
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));
i2c_register_board_info(5, i2c_devs5, ARRAY_SIZE(i2c_devs5));
#ifdef CONFIG_DM9000
smdkc110_dm9000_set();
#endif
#if defined(CONFIG_FB_S3C_LTE480WV)||defined(CONFIG_FB_S3C_LCD800480)||defined(CONFIG_FB_S3C_LCD800600)||defined(CONFIG_FB_S3C_VGA1024768)||defined(CONFIG_FB_S3C_LCD640480)||defined(CONFIG_FB_S3C_LCD480272)||defined(CONFIG_FB_AT070TN92) ||defined (CONFIG_FB_S3C_VGA640480)
s3cfb_set_platdata(<e480wv_fb_data);
#endif
#ifdef CONFIG_CW210_VGA
s3cfb_set_platdata(&vga_fb_data);
#endif
/* spi */
#ifdef CONFIG_S3C64XX_DEV_SPI
if (!gpio_request(S5PV210_GPB(1), "SPI_CS0")) {
gpio_direction_output(S5PV210_GPB(1), 1);
s3c_gpio_cfgpin(S5PV210_GPB(1), S3C_GPIO_SFN(1));
s3c_gpio_setpull(S5PV210_GPB(1), S3C_GPIO_PULL_UP);
s5pv210_spi_set_info(0, S5PV210_SPI_SRCCLK_PCLK,
ARRAY_SIZE(smdk_spi0_csi));
}
if (!gpio_request(S5PV210_GPB(5), "SPI_CS1")) {
gpio_direction_output(S5PV210_GPB(5), 1);
s3c_gpio_cfgpin(S5PV210_GPB(5), S3C_GPIO_SFN(1));
s3c_gpio_setpull(S5PV210_GPB(5), S3C_GPIO_PULL_UP);
s5pv210_spi_set_info(1, S5PV210_SPI_SRCCLK_PCLK,
ARRAY_SIZE(smdk_spi1_csi));
}
spi_register_board_info(s3c_spi_devs, ARRAY_SIZE(s3c_spi_devs));
#endif
#if defined(CONFIG_TOUCHSCREEN_S3C)
s3c_ts_set_platdata(&s3c_t
4000
s_platform);
#endif
#if defined(CONFIG_S5P_ADC)
s3c_adc_set_platdata(&s3c_adc_platform);
#endif
#if defined(CONFIG_PM)
s3c_pm_init();
#endif
#ifdef CONFIG_VIDEO_FIMC
/* fimc */
s3c_fimc0_set_platdata(&fimc_plat_lsi);//在这里会被添加到平台数据,便于以后调用,由于有三个fimc控制器,所以设备需要每个 fimc添加一次
s3c_fimc1_set_platdata(&fimc_plat_lsi);
s3c_fimc2_set_platdata(&fimc_plat_lsi);
/* external camera */
/* smdkv210_cam0_power(1); */
/* smdkv210_cam1_power(1); */
#endif
#ifdef CONFIG_VIDEO_FIMC_MIPI
s3c_csis_set_platdata(NULL);
#endif
#ifdef CONFIG_VIDEO_JPEG_V2
s3c_jpeg_set_platdata(&jpeg_plat);
#endif
#ifdef CONFIG_VIDEO_MFC50
/* mfc */
s3c_mfc_set_platdata(NULL);
#endif
#ifdef CONFIG_VIDEO_TV20
s3c_set_qos();
#endif
#ifdef CONFIG_S3C_DEV_HSMMC
s5pv210_default_sdhci0();
#endif
#ifdef CONFIG_S3C_DEV_HSMMC1
s5pv210_default_sdhci1();
#endif
#ifdef CONFIG_S3C_DEV_HSMMC2
s5pv210_default_sdhci2();
#endif
#ifdef CONFIG_S3C_DEV_HSMMC3
s5pv210_default_sdhci3();
#endif
#ifdef CONFIG_S5PV210_SETUP_SDHCI
s3c_sdhci_set_platdata();
#endif
#ifdef CONFIG_BACKLIGHT_PWM
smdk_backlight_register();
#endif
regulator_has_full_constraints();
smdkc110_setup_clocks();
}
//设备信息添加到平台数据(pdata)后,会创建i2c设备和v4l2设备
//以下为创建过程
//没有典型 ov9650实例,driver部分以mt9v011为例
在/driver/media/video/samsung/fimc_capture.c中会调用v4l2_i2c_new_subdev_board同时注册V4L2和I2C设备,这里暂时未查找以上调用过程。
//定义
/* Load an i2c sub-device. */
struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
struct i2c_adapter *adapter, const char *module_name,
struct i2c_board_info *info, const unsigned short *probe_addrs)
{
struct v4l2_subdev *sd = NULL;
struct i2c_client *client;
BUG_ON(!v4l2_dev);
if (module_name)
request_module(module_name);
/* Create the i2c client */
if (info->addr == 0 && probe_addrs)
client = i2c_new_probed_device(adapter, info, probe_addrs);
else
client = i2c_new_device(adapter, info);//创建i2c_client设备 调用 driver -> prop函数
/* Note: by loading the module first we are certain that c->driver
will be set if the driver was found. If the module was not loaded
first, then the i2c core tries to delay-load the module for us,
and then c->driver is still NULL until the module is finally
loaded. This delay-load mechanism doesn't work if other drivers
want to use the i2c device, so explicitly loading the module
is the best alternative. */
if (client == NULL || client->driver == NULL)
goto error;
/* Lock the module so we can safely get the v4l2_subdev pointer */
if (!try_module_get(client->driver->driver.owner))
goto error;
sd = i2c_get_clientdata(client);
/* Register with the v4l2_device which increases the module's
use count as well. */
if (v4l2_device_register_subdev(v4l2_dev, sd))//注册subdev 设备 注册成功会调用v4l2_subdev_call
-> v4l2_subdev_core_ops.s_config
sd = NULL;
/* Decrease the module use count to match the first try_module_get. */
module_put(client->driver->driver.owner);
if (sd) {
/* We return errors from v4l2_subdev_call only if we have the
callback as the .s_config is not mandatory */
int err = v4l2_subdev_call(sd, core, s_config,
info->irq, info->platform_data);
if (err && err != -ENOIOCTLCMD) {
v4l2_device_unregister_subdev(sd);
sd = NULL;
}
}
error:
/* If we have a client but no subdev, then something went wrong and
we must unregister the client. */
if (client && sd == NULL)
i2c_unregister_device(client);
return sd;
}
/*****************************************以上i2c_client和sub_dev均创建并注册完毕*********************************************/
/*****************************************以下为驱动driver 部分********************************************************************/
//即将注册的驱动,因为没有注册函数,所以需要小小转变一下
static struct v4l2_i2c_driver_data
v4l2_i2c_data = {
.name = "mt9v011",
.probe = mt9v011_probe,
.remove = mt9v011_remove,
.id_table = mt9v011_id,
};
//在include/media/v4l2-i2c-drv.h
struct v4l2_i2c_driver_data {
const char * const name;
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
int (*suspend)(struct i2c_client *client, pm_message_t state);
int (*resume)(struct i2c_client *client);
const struct i2c_device_id *id_table;
};
static struct v4l2_i2c_driver_data
v4l2_i2c_data;
static struct i2c_driver v4l2_i2c_driver;
/* Bus-based I2C implementation for kernels >= 2.6.26 */
static int __init v4l2_i2c_drv_init(void)
{
v4l2_i2c_driver.driver.name =v4l2_i2c_data.name;//加载模块后 会自动由v4l2_i2c_data 转变为 v4l2_i2c_driver,然后注册v4l2_i2c_driver
v4l2_i2c_driver.command =v4l2_i2c_data.command;
v4l2_i2c_driver.probe =v4l2_i2c_data.probe;
v4l2_i2c_driver.remove =v4l2_i2c_data.remove;
v4l2_i2c_driver.suspend =v4l2_i2c_data.suspend;
v4l2_i2c_driver.resume =v4l2_i2c_data.resume;
v4l2_i2c_driver.id_table =v4l2_i2c_data.id_table;
return i2c_add_driver(&v4l2_i2c_driver);//注册i2c驱动
}
//驱动操作接口
//针对v4l2-subdev,驱动里面注册了两套操作集合,分别是v4l2_subdev_video_ops和v4l2_subdev_core_ops。
static const structv4l2_subdev_core_ops
mt9v011_core_ops = {
.queryctrl = mt9v011_queryctrl,
.g_ctrl = mt9v011_g_ctrl,
.s_ctrl = mt9v011_s_ctrl,
.reset = mt9v011_reset,
.s_config = mt9v011_s_config,
.g_chip_ident = mt9v011_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
.g_register = mt9v011_g_register,
.s_register = mt9v011_s_register,
#endif
};
static const struct v4l2_subdev_video_opsmt9v011_video_ops = {
.enum_mbus_fmt = mt9v011_enum_mbus_fmt,
.try_mbus_fmt = mt9v011_try_mbus_fmt,
.s_mbus_fmt = mt9v011_s_mbus_fmt,
.g_parm = mt9v011_g_parm,
.s_parm = mt9v011_s_parm,
};
static const struct v4l2_subdev_ops mt9v011_ops = {
.core = &mt9v011_core_ops,
.video = &mt9v011_video_ops,
};
//在调用probe回调时 会执行
/****************************************************************************
I2C Client & Driver
****************************************************************************/
static int mt9v011_probe(struct i2c_client *c,
const struct i2c_device_id *id)
{
u16 version;
struct mt9v011 *core;
struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(c->adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return -EIO;
core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL);
if (!core)
return -ENOMEM;
sd = &core->sd;
v4l2_i2c_subdev_init(sd, c, &mt9v011_ops);
/* Check if the sensor is really a MT9V011 */
version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
if ((version != MT9V011_VERSION) &&
(version != MT9V011_REV_B_VERSION)) {
v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n",
version);
kfree(core);
return -EINVAL;
}
core->global_gain = 0x0024;
core->width = 640;
core->height = 480;
core->xtal = 27000000;/* Hz */
v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n",
c->addr << 1, c->adapter->name, version);
return 0;
}
/* I2C Helper functions */
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
const struct v4l2_subdev_ops *ops)
{
v4l2_subdev_init(sd, ops);
sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
/* the owner is the same as the i2c_client's driver owner */
sd->owner = client->driver->driver.owner;
/* i2c_client and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, client);//分别关联V412和I2C设备
i2c_set_clientdata(client, sd);
/* initialize name */
snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
client->driver->driver.name, i2c_adapter_id(client->adapter),
client->addr);
}
相关文章推荐
- Linux I2C设备驱动分析 基于2440 2.6.32内核
- at24c08 E2PROM的I2C设备驱动实例——基于mini2440
- linux3.3 v4l2视频采集驱动框架(vfe, camera i2c driver,v4l2_subdev等之间的联系)
- v4l2视频采集驱动框架(vfe, camera i2c driver,v4l2_subdev等之间的联系)
- 基于V4L2的视频驱动开发(1)---Camera
- i2c 与 spi 设备在新版内核中不采用DTS设备树形式 在驱动添加设备信息(board_info)的方法
- linux驱动由浅入深系列:camera驱动之二(基于高通平台的V4L2结构及代码分析)
- Linux设备驱动简析—基于I2C的E2PROM驱动
- Camera硬件及基于V4L2驱动源码分析
- 基于V4L2的视频驱动开发(1)---Camera
- Linux I2C核心、总线与设备驱动
- s3c2440基于linux的gpio led字符设备驱动实践
- Windows CE LCD显示驱动简析(1)(基于WinCE5.0 SMDK2410 BSP的LCD显示设备驱动)
- s3c2440基于linux的gpio led字符设备驱动实践
- 第15章 Linux的I2C核心、总线与设备驱动
- Linux下I2C设备驱动开发和实现
- 基于ARM9开发板的按键字符设备驱动实现
- Linux下I2C设备驱动开发和实现
- 基于ARM的嵌入式Linux移植真实体验(4)――设备驱动
- 一个基于v4l2框架的输出驱动分析