camera驱动的实现
本篇文章主要介绍camera驱动的实现,这将会结合代码来叙述,这理解摄像头驱动需要三个前提,分别是:
摄像头基本的工作原理
platform_device和platform_driver工作原理
Linux内核I2C驱动架构
驱动的编写主要有配置GPIO、I2C、MIPI、电压、时钟等, 编写驱动代码,调通camera sensor驱动,并实现前后置双camera的切换。根据芯片手册,实现基本功能 - 预览, 拍照, 录像, 效果(scene, effect, ev, iso, wb, contrast等等)。当然还要实现进阶扩展功能 - 防抖, 自动对焦, 闪光灯, 固件升级, 720P, wdr, panorama等。本篇只讲述些基本的功能实现。
查看datesheet芯片上电时序如下图所示:
上电时序一
上电时序二
对应代码如下:
- static struct camera_gpios yuv5_sensor_gpio_keys[] = {
- [0] = CAMERA_GPIO("cam_power_en", CAMERA_POWER_GPIO, 1, 0, GPIO_FREE),
- [1] = CAMERA_GPIO("yuv5_sensor_pwdn", YUV5_PWR_DN_GPIO, 0, 0, GPIO_FREE),
- [2] = CAMERA_GPIO("yuv5_sensor_rst_lo", YUV5_RST_L_GPIO, 1, 0, GPIO_FREE),
- };
- static int yuv5_sensor_power_on(void)
- {
- int ret;
- int i;
- pr_err("%s: sandow pmu\n", __func__);
- for (i = 0; i < ARRAY_SIZE(yuv5_sensor_gpio_keys); i++) {
- tegra_gpio_enable(yuv5_sensor_gpio_keys[i].gpio);
- ret = gpio_request(yuv5_sensor_gpio_keys[i].gpio,
- yuv5_sensor_gpio_keys[i].name);
- if (ret < 0) {
- pr_err("%s: gpio_request failed for gpio #%d\n",
- __func__, i);
- goto fail;
- }
- gpio_direction_output(yuv5_sensor_gpio_keys[i].gpio,
- yuv5_sensor_gpio_keys[i].enabled);
- gpio_export(yuv5_sensor_gpio_keys[i].gpio, false);
- }
- return 0;
- fail:
- while (i--)
- gpio_free(yuv5_sensor_gpio_keys[i].gpio);
- return ret;
- }
- static int yuv5_sensor_power_off(void)
- {
- int i;
- int gpio_pw_dn, gpio_camera_power;
- pr_err("%s: sandow pmu\n", __func__);
- //set 5M to power-down
- gpio_direction_output(YUV5_PWR_DN_GPIO,1);
- //cut off power
- gpio_direction_output(CAMERA_POWER_GPIO,0);
- gpio_pw_dn = gpio_get_value(YUV5_PWR_DN_GPIO);
- gpio_camera_power= gpio_get_value(CAMERA_POWER_GPIO);
- printk("%s: sandow pmu gpio_pw_dn: %d, camera_power: %d", __func__, gpio_pw_dn, gpio_camera_power);
- i = ARRAY_SIZE(yuv5_sensor_gpio_keys);
- while (i--)
- gpio_free(yuv5_sensor_gpio_keys[i].gpio);
- return 0;
- }
- struct yuv5_sensor_platform_data yuv5_sensor_data = {
- .power_on = yuv5_sensor_power_on,
- .power_off = yuv5_sensor_power_off,
- };
I2C通信本身要注意两点:
1、SDA第9位ACK位为低时说明从设备有响应;
2、Slave address,Spec给出的是8位地址,第8位是指Write-0或者Read-1,实际的I2C芯片地址是7位的。Linux源码里struct i2c_board_info的板基信息应填写7位I2C地址。
代码位于:board-ventana-sensors.c
- {
- I2C_BOARD_INFO(SENSOR_5M_NAME, 0x3D),//I2C slave address
- .platform_data = &yuv5_sensor_data,
- },
- ……
- #define SENSOR_5M_NAME "mt9d111"
- ……
- // platform driver是用来"触发"真正的i2c //driver的注册
- static struct i2c_driver sensor_i2c_driver = { .driver = {
- .name = SENSOR_5M_NAME,//与i2c_board_info 的type匹配
- .owner = THIS_MODULE,
- },
- .probe = sensor_probe,// 当有i2c_client和i2c_driver匹配时调用
- .remove = sensor_remove,
- .id_table = sensor_id,// //匹配规则
- };
- static struct miscdevice sensor_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = SENSOR_5M_NAME,
- .fops = &sensor_fileops,
- };
本例的camera驱动代码在/kernel/driver/media/video/tegra下:
ltc3216.c(LED驱动器LTC3216)、yuv5_sensor.c、yuv_sensor.c、tegra_camera.c。这些源码里面最基础的是tegra_camera.c,这里面注册了一个platform_driver,在相应的平台代码里面有对应的platform_device的描述。这种SOC上的控制器一般都会挂接在platform_bus上以实现在系统初始化时的device和driver的匹配。在driver的probe函数里面,主要完成了资源获取以及misc设备的注册。
平台信息:
- struct yuv5_sensor_platform_data yuv5_sensor_data = {
- .power_on = yuv5_sensor_power_on,
- .power_off = yuv5_sensor_power_off,
- };
Probe具体流程如下图所示: