当前位置:   article > 正文

camera 模组驱动优化_摄像头驱动模块的调试优化方法

摄像头驱动模块的调试优化方法

原文转载自http://blog.csdn.net/g_salamander/article/details/8372571

因为项目比较多,平台支持的 camera 模组已经有 10 多个了,代码比较繁杂,就把 camera 模组端的驱动架构优化了一下。总的思路就是将公共的接口统一起来,减少代码的耦合度,建立新的公共接口文件 cam_core.c 和 cam_core.h。

一、基础数据结构

新建立的数据结构如下:

[cpp]  view plain copy
  1. struct cam_info {  
  2.     struct i2c_client   *i2c_dev;  /* 指向模组的i2c从设备 */  
  3.     struct cam_priv     *priv;     /* 指向模组的操作接口 */  
  4.     struct v4l2_subdev  sd;        /* v4l2子设备 */  
  5.   
  6.     uint32_t maxwidth;             /* 模组支持的最大分辨率 */  
  7.     uint32_t maxheight;  
  8.     uint32_t minwidth;             /* 模组支持的最小分辨率 */  
  9.     uint32_t minheight;  
  10.   
  11.     /* current param used for each frame */  
  12.     uint32_t width;                /* 模组的当前分辨率及帧率 */  
  13.     uint32_t height;  
  14.     uint32_t fps;  
  15.       
  16.     uint8_t bl;                    /*0:auto, 1:50Hz light, 2:60Hz light */  
  17.     uint8_t af;  
  18.     unsigned long flags;           /* flags for cam on/off, af on/off */  
  19.     unsigned long res_flags;       /* flags for resolution */  
  20.   
  21.     unsigned long night_mode;  
  22.   
  23.     struct clk *camera;            /* camera clk */  
  24. };  
struct cam_info  结构主要负责和 cam_core.c 交互,通过指针 priv 指向模组的私有操作接口 struct cam_priv ,该结构定义如下:

[cpp]  view plain copy
  1. struct cam_priv {  
  2.     char     *name;        /* 模组名称 */  
  3.     uint16_t addr;         /* 模组i2c地址 */  
  4.     uint32_t i2c_bus;      /* 模组i2c总线号 */  
  5.     uint32_t subdev_id;    /* 前后camera标记 */  
  6.   
  7.     uint32_t fmt_num;      /* 模组支持的格式数量 */  
  8.     uint32_t res_num;      /* 模组支持的分辨率数量 */  
  9.     uint32_t ctl_num;      /* 模组支持的特殊操作数量 */  
  10.   
  11.     struct camera_fmt      *fmt_list;  /* 模组支持的格式列表 */  
  12.     struct camera_res      *res_list;  /* 模组支持的分辨率列表 */  
  13.     struct camera_control  *ctl_list;  /* 模组支持的特殊操作列表 */  
  14.     struct i2c_device_id   *id_table;  /* i2c id 列表 */  
  15.   
  16.     int (*Open)(struct cam_info *info);  /* 打开模组 */  
  17.     int (*Close)(struct cam_info *info); /* 关闭模组 */  
  18.   
  19.     int (*Set_Preview)(struct cam_info *info);   /* 预览操作接口 */  
  20.     int (*Set_Capture)(struct cam_info *info);   /* 拍照操作接口 */  
  21.     int (*Set_Config)(struct cam_info *info);    /* 配置操作接口 */  
  22.     int (*Set_Resolution)(struct cam_info *info, int val);  /* 分辨率操作接口 */  
  23.   
  24.     int (*Request_Gpio)(void);  /* Gpio申请和释放 */  
  25.     int (*Free_Gpio)(void);  
  26.   
  27.     int (*Detect_PowerON)(struct cam_info *info); /* 模组自动探测接口 */  
  28.     int (*Detect_PowerOFF)(struct cam_info *info);  
  29.     int (*Detect_ReadId)(struct cam_info *info);  
  30. };  
二、cam_core 主要接口解析

1、register 接口

模组注册到内核用 init_atxx_cam -> register_cam_device 接口,接口定义如下:

[cpp]  view plain copy
  1. /* 主要完成camera模组的探测工作并将其注册到内核中 */  
  2. static int register_cam_device(struct cam_info *info)  
  3. {  
  4.     int ret;  
  5.     struct i2c_adapter *adapter;  
  6.     struct i2c_board_info board_info;  
  7.     memset(&board_info, 0, sizeof(struct i2c_board_info));  
  8.     board_info.addr = info->priv->addr;  
  9.     strlcpy(board_info.type, info->priv->name, I2C_NAME_SIZE);  
  10.     /* 动态创建i2c从设备 */  
  11.     adapter = i2c_get_adapter(info->priv->i2c_bus);  
  12.     if (adapter == NULL) {  
  13.         cam_err("can't get i2c adapter %d\n", info->priv->i2c_bus);  
  14.         return -ENODEV;  
  15.     }  
  16.   
  17.     info->i2c_dev = i2c_new_device(adapter, &board_info);  
  18.     i2c_put_adapter(adapter);  
  19.     if (info->i2c_dev == NULL) {  
  20.         cam_err("can't add i2c device at 0x%x\n", board_info.addr);  
  21.         return -ENODEV;  
  22.     }  
  23.   
  24.     info->camera = clk_get(&info->i2c_dev->dev, "camera");  
  25.     if (IS_ERR(info->camera)) {  
  26.         cam_err("can't get camera clock\n");  
  27.         ret = -ENODEV;  
  28.         goto exit;  
  29.     }  
  30.     /* 申请Gpio并打开模组电源 */  
  31.     info->priv->Request_Gpio();  
  32.     info->priv->Detect_PowerON(info);  
  33.     /* 设置时钟 */  
  34.     clk_set_rate(info->camera, ATXX_CAM_CLOCK);  
  35.     clk_enable(info->camera);  
  36.     msleep(10);  
  37.     /* 读取模组id */  
  38.     ret = info->priv->Detect_ReadId(info);  
  39.     if(ret) {  
  40.         cam_err("can't detect this camera: %s\n", info->priv->name);  
  41.         goto exit_detect;  
  42.     }  
  43.   
  44.     if(info->priv->subdev_id == ATXX_SUBDEV_FORE_CAM) {  
  45.         fore_cam_driver.id_table = info->priv->id_table;  
  46.         ret = i2c_add_driver(&fore_cam_driver);  
  47.         if(ret) {  
  48.             cam_err("can't add i2c driver\n");  
  49.             goto exit_detect;  
  50.         }  
  51.     } else {  
  52.         rear_cam_driver.id_table = info->priv->id_table;  
  53.         ret = i2c_add_driver(&rear_cam_driver);  
  54.         if(ret) {  
  55.             cam_err("can't add i2c driver\n");  
  56.             goto exit_detect;  
  57.         }  
  58.     }  
  59.     /* 初始化v4l2子设备 */  
  60.     v4l2_i2c_subdev_init(&info->sd, info->i2c_dev, &cam_ops);  
  61.     info->priv->Set_Config(info);  
  62.     /* 注册到内核 */  
  63.     ret = atxx_cam_register_sensor(&info->sd, info->priv->subdev_id);  
  64.     if(ret) {  
  65.         cam_err("can't register this camera: %s\n", info->priv->name);  
  66.         goto exit_detect;  
  67.     }  
  68.   
  69.     clk_disable(info->camera);  
  70.     info->priv->Detect_PowerOFF(info);  
  71.     info->priv->Free_Gpio();  
  72.     info->flags = 0;  
  73.   
  74.     return 0;  
  75.   
  76. exit_detect:  
  77.     clk_disable(info->camera);  
  78.     clk_put(info->camera);  
  79.     info->priv->Detect_PowerOFF(info);  
  80.     info->priv->Free_Gpio();  
  81. exit:  
  82.     i2c_unregister_device(info->i2c_dev);  
  83.     return ret;  
  84. }  
  85.   
  86. /* 初始化模组调用接口,参数为模组的cam_priv */  
  87. int init_atxx_cam(struct cam_priv *priv)  
  88. {  
  89.     int ret;  
  90.     struct cam_info *info;  
  91.   
  92.     info = kzalloc(sizeof(struct cam_info), GFP_KERNEL);  
  93.     if(info == NULL)  
  94.         return -ENOMEM;  
  95.   
  96.     info->priv = priv;  
  97.   
  98.     ret = register_cam_device(info);  
  99.     if(ret) {  
  100.         kfree(info);  
  101.         return ret;  
  102.     }  
  103.   
  104.     return 0;  
  105. }  
  106. EXPORT_SYMBOL(init_atxx_cam);  
2、v4l2 接口

在 cam_core 中主要完成 v4l2 子设备相关的操作,涉及到的数据结构和接口如下:

[cpp]  view plain copy
  1. static const struct v4l2_subdev_core_ops cam_core_ops = {  
  2.     .g_ctrl    = cam_g_ctrl,    /* 获取当前命令值 */  
  3.     .s_ctrl    = cam_s_ctrl,    /* 发送命令值 */  
  4.     .queryctrl = cam_queryctrl, /* 查询模组是否支持该命令 */  
  5.     .reset     = cam_reset,     /* 复位模组 */  
  6.     .init      = cam_init,      /* 初始化模组 */  
  7.     .ioctl     = cam_ioctl,     /* 保留备用 */  
  8. };  
  9.   
  10. static const struct v4l2_subdev_video_ops cam_video_ops = {  
  11.     .s_fmt    = cam_s_fmt,      /* 设置模组捕获视频的格式 */  
  12.     .try_fmt  = cam_try_fmt,    /* 尝试是否支持该格式 */  
  13.     .enum_fmt = cam_enum_fmt,   /* 枚举设备支持的格式 */  
  14. };  
  15.   
  16. static const struct v4l2_subdev_ops cam_ops = {  
  17.     .core  = &cam_core_ops,     /* v4l2通用接口 */  
  18.     .video = &cam_video_ops,    /* v4l2视频接口 */  
  19. };  
3、i2c 通信接口

大部分 camera 寄存器的地址和值都是 8 位或者 16 位数据,因此可以将 i2c 通信接口简化,建立如下结构来描述寄存器地址和值:

[cpp]  view plain copy
  1. /* 8 bit */  
  2. struct cam_reg {  
  3.     unsigned char reg;   /* 8位寄存器地址 */  
  4.     unsigned char val;   /* 8位寄存器值 */  
  5. };  
  6. /* 16 bit */  
  7. struct cam_reg {  
  8.     unsigned short reg;   /* 16位寄存器地址 */  
  9.     unsigned char val;    /* 8位寄存器值 */  
  10. };  

封装的 8 位通信接口如下:

[cpp]  view plain copy
  1. uint8_t cam_read_byte(struct i2c_client *client, uint8_t reg_idx);  
  2. int cam_write_byte(struct i2c_client *client, uint8_t reg_idx, uint8_t val);  

在模组驱动中使用示例如下:

[cpp]  view plain copy
  1. /*********************************************************************** 
  2.  * Camera Common Function                                              * 
  3.  ***********************************************************************/  
  4.   
  5. struct cam_reg {  
  6.     unsigned char reg;  
  7.     unsigned char val;  
  8. };  
  9.   
  10. static int write_regs(struct cam_info *info, const struct cam_reg reglist[])  
  11. {  
  12.     const struct cam_reg *next = reglist;  
  13.   
  14.     while (!((next->reg == REG_TERM) && (next->val == VAL_TERM)))  
  15.     {  
  16.         cam_write_byte(info->i2c_dev, next->reg, next->val);  
  17.         next++;  
  18.     }  
  19.   
  20.     return 0;  
  21. }  
  22.   
  23. /*********************************************************************** 
  24.  * Camera Initialize Function                                          * 
  25.  ***********************************************************************/  
  26.   
  27. static struct cam_reg cam_set_initialize[] = {  
  28.     {0xfc, 0x16},  
  29.     ...  
  30.     {REG_TERM, VAL_TERM}  
  31. };  
  32. static struct cam_reg cam_set_preview[] = {  
  33.     {REG_TERM, VAL_TERM}  
  34. };  
  35.   
  36. static struct cam_reg cam_set_capture[] = {  
  37.     {REG_TERM, VAL_TERM}  
  38. };  
  39.   
  40. struct cam_reg* cam_reg_init[CAM_INIT_MAX] =  
  41. {  
  42.     cam_set_initialize,  
  43.     cam_set_preview,  
  44.     cam_set_capture  
  45. };  
  46.   
  47. static int cam_initialize(struct cam_info *info)  
  48. {  
  49.     return write_regs(info, cam_reg_init[CAM_INIT]);  
  50. }  
  51.   
  52. static int cam_preview(struct cam_info *info)  
  53. {  
  54.     return write_regs(info, cam_reg_init[CAM_PREVIEW]);  
  55. }  
  56.   
  57. static int cam_capture(struct cam_info *info)  
  58. {  
  59.     return write_regs(info, cam_reg_init[CAM_CAPTRUE]);  
  60. }  
三、camera 模组驱动接口解析
在模组驱动中主要工作是实现 struct cam_priv 中的接口,然后调用 init_atxx_cam 注册:

[cpp]  view plain copy
  1. static struct cam_priv priv = {  
  2.     .name       = ATXX_CAM_NAME,  
  3.     .addr       = ATXX_CAM_ADDR,  
  4.     .i2c_bus    = ATXX_CAM_BUS,  
  5.     .subdev_id  = ATXX_CAM_SUBDEV,  
  6.   
  7.     .fmt_num    = N_FORMATS,  
  8.     .res_num    = N_RESOLUTIONS,  
  9.     .ctl_num    = N_CONTROLS,  
  10.   
  11.     .fmt_list   = cam_fmt_list,  
  12.     .res_list   = cam_res_list,  
  13.     .ctl_list   = cam_ctl_list,  
  14.     .id_table   = cam_id_table,  
  15.   
  16.     .Open       = cam_open,  
  17.     .Close      = cam_close,  
  18.   
  19.     .Set_Preview     = cam_preview,  
  20.     .Set_Capture     = cam_capture,  
  21.     .Set_Config      = cam_config,  
  22.     .Set_Resolution  = cam_resolution,  
  23.   
  24.     .Request_Gpio    = cam_gpio_request,  
  25.     .Free_Gpio       = cam_gpio_free,  
  26.   
  27.     .Detect_PowerON  = detect_poweron,  
  28.     .Detect_PowerOFF = detect_poweroff,  
  29.     .Detect_ReadId   = detect_readid,  
  30. };  
  31.   
  32. static int __init cam_init(void)  
  33. {  
  34.     return init_atxx_cam(&priv);  
  35. }  
经过优化之后 camera 模组的代码耦合度很低,新增模组 driver 时只需要将别的模组 driver 拷贝一份然后修改一下宏定义和寄存器值就可以用了,而且每个模组 driver 的代码比之前少了500行左右,看起来也比较整洁,达到了预期目标。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/263324
推荐阅读
相关标签
  

闽ICP备14008679号