因为项目比较多,平台支持的 camera 模组已经有 10 多个了,代码比较繁杂,就把 camera 模组端的驱动架构优化了一下。总的思路就是将公共的接口统一起来,减少代码的耦合度,建立新的公共接口文件 cam_core.c 和 cam_core.h。
一、基础数据结构
新建立的数据结构如下:
- struct cam_info {
- struct i2c_client *i2c_dev; /* 指向模组的i2c从设备 */
- struct cam_priv *priv; /* 指向模组的操作接口 */
- struct v4l2_subdev sd; /* v4l2子设备 */
- uint32_t maxwidth; /* 模组支持的最大分辨率 */
- uint32_t maxheight;
- uint32_t minwidth; /* 模组支持的最小分辨率 */
- uint32_t minheight;
- /* current param used for each frame */
- uint32_t width; /* 模组的当前分辨率及帧率 */
- uint32_t height;
- uint32_t fps;
- uint8_t bl; /*0:auto, 1:50Hz light, 2:60Hz light */
- uint8_t af;
- unsigned long flags; /* flags for cam on/off, af on/off */
- unsigned long res_flags; /* flags for resolution */
- unsigned long night_mode;
- struct clk *camera; /* camera clk */
- };
struct cam_info {
struct i2c_client *i2c_dev; /* 指向模组的i2c从设备 */
struct cam_priv *priv; /* 指向模组的操作接口 */
struct v4l2_subdev sd; /* v4l2子设备 */
uint32_t maxwidth; /* 模组支持的最大分辨率 */
uint32_t maxheight;
uint32_t minwidth; /* 模组支持的最小分辨率 */
uint32_t minheight;
/* current param used for each frame */
uint32_t width; /* 模组的当前分辨率及帧率 */
uint32_t height;
uint32_t fps;
uint8_t bl; /*0:auto, 1:50Hz light, 2:60Hz light */
uint8_t af;
unsigned long flags; /* flags for cam on/off, af on/off */
unsigned long res_flags; /* flags for resolution */
unsigned long night_mode;
struct clk *camera; /* camera clk */
};
struct cam_info 结构主要负责和 cam_core.c 交互,通过指针 priv 指向模组的私有操作接口struct cam_priv,该结构定义如下:
- struct cam_priv {
- char *name; /* 模组名称 */
- uint16_t addr; /* 模组i2c地址 */
- uint32_t i2c_bus; /* 模组i2c总线号 */
- uint32_t subdev_id; /* 前后camera标记 */
- uint32_t fmt_num; /* 模组支持的格式数量 */
- uint32_t res_num; /* 模组支持的分辨率数量 */
- uint32_t ctl_num; /* 模组支持的特殊操作数量 */
- struct camera_fmt *fmt_list; /* 模组支持的格式列表 */
- struct camera_res *res_list; /* 模组支持的分辨率列表 */
- struct camera_control *ctl_list; /* 模组支持的特殊操作列表 */
- struct i2c_device_id *id_table; /* i2c id 列表 */
- int (*Open)(struct cam_info *info); /* 打开模组 */
- int (*Close)(struct cam_info *info); /* 关闭模组 */
- int (*Set_Preview)(struct cam_info *info); /* 预览操作接口 */
- int (*Set_Capture)(struct cam_info *info); /* 拍照操作接口 */
- int (*Set_Config)(struct cam_info *info); /* 配置操作接口 */
- int (*Set_Resolution)(struct cam_info *info, int val); /* 分辨率操作接口 */
- int (*Request_Gpio)(void); /* Gpio申请和释放 */
- int (*Free_Gpio)(void);
- int (*Detect_PowerON)(struct cam_info *info); /* 模组自动探测接口 */
- int (*Detect_PowerOFF)(struct cam_info *info);
- int (*Detect_ReadId)(struct cam_info *info);
- };
struct cam_priv {
char *name; /* 模组名称 */
uint16_t addr; /* 模组i2c地址 */
uint32_t i2c_bus; /* 模组i2c总线号 */
uint32_t subdev_id; /* 前后camera标记 */
uint32_t fmt_num; /* 模组支持的格式数量 */
uint32_t res_num; /* 模组支持的分辨率数量 */
uint32_t ctl_num; /* 模组支持的特殊操作数量 */
struct camera_fmt *fmt_list; /* 模组支持的格式列表 */
struct camera_res *res_list; /* 模组支持的分辨率列表 */
struct camera_control *ctl_list; /* 模组支持的特殊操作列表 */
struct i2c_device_id *id_table; /* i2c id 列表 */
int (*Open)(struct cam_info *info); /* 打开模组 */
int (*Close)(struct cam_info *info); /* 关闭模组 */
int (*Set_Preview)(struct cam_info *info); /* 预览操作接口 */
int (*Set_Capture)(struct cam_info *info); /* 拍照操作接口 */
int (*Set_Config)(struct cam_info *info); /* 配置操作接口 */
int (*Set_Resolution)(struct cam_info *info, int val); /* 分辨率操作接口 */
int (*Request_Gpio)(void); /* Gpio申请和释放 */
int (*Free_Gpio)(void);
int (*Detect_PowerON)(struct cam_info *info); /* 模组自动探测接口 */
int (*Detect_PowerOFF)(struct cam_info *info);
int (*Detect_ReadId)(struct cam_info *info);
};
二、cam_core 主要接口解析
1、register 接口
模组注册到内核用 init_atxx_cam -> register_cam_device 接口,接口定义如下:
- /* 主要完成camera模组的探测工作并将其注册到内核中 */
- static int register_cam_device(struct cam_info *info)
- {
- int ret;
- struct i2c_adapter *adapter;
- struct i2c_board_info board_info;
- memset(&board_info, 0, sizeof(struct i2c_board_info));
- board_info.addr = info->priv->addr;
- strlcpy(board_info.type, info->priv->name, I2C_NAME_SIZE);
- /* 动态创建i2c从设备 */
- adapter = i2c_get_adapter(info->priv->i2c_bus);
- if (adapter == NULL) {
- cam_err("can't get i2c adapter %d\n", info->priv->i2c_bus);
- return -ENODEV;
- }
- info->i2c_dev = i2c_new_device(adapter, &board_info);
- i2c_put_adapter(adapter);
- if (info->i2c_dev == NULL) {
- cam_err("can't add i2c device at 0x%x\n", board_info.addr);
- return -ENODEV;
- }
- info->camera = clk_get(&info->i2c_dev->dev, "camera");
- if (IS_ERR(info->camera)) {
- cam_err("can't get camera clock\n");
- ret = -ENODEV;
- goto exit;
- }
- /* 申请Gpio并打开模组电源 */
- info->priv->Request_Gpio();
- info->priv->Detect_PowerON(info);
- /* 设置时钟 */
- clk_set_rate(info->camera, ATXX_CAM_CLOCK);
- clk_enable(info->camera);
- msleep(10);
- /* 读取模组id */
- ret = info->priv->Detect_ReadId(info);
- if(ret) {
- cam_err("can't detect this camera: %s\n", info->priv->name);
- goto exit_detect;
- }
- if(info->priv->subdev_id == ATXX_SUBDEV_FORE_CAM) {
- fore_cam_driver.id_table = info->priv->id_table;
- ret = i2c_add_driver(&fore_cam_driver);
- if(ret) {
- cam_err("can't add i2c driver\n");
- goto exit_detect;
- }
- } else {
- rear_cam_driver.id_table = info->priv->id_table;
- ret = i2c_add_driver(&rear_cam_driver);
- if(ret) {
- cam_err("can't add i2c driver\n");
- goto exit_detect;
- }
- }
- /* 初始化v4l2子设备 */
- v4l2_i2c_subdev_init(&info->sd, info->i2c_dev, &cam_ops);
- info->priv->Set_Config(info);
- /* 注册到内核 */
- ret = atxx_cam_register_sensor(&info->sd, info->priv->subdev_id);
- if(ret) {
- cam_err("can't register this camera: %s\n", info->priv->name);
- goto exit_detect;
- }
- clk_disable(info->camera);
- info->priv->Detect_PowerOFF(info);
- info->priv->Free_Gpio();
- info->flags = 0;
- return 0;
- exit_detect:
- clk_disable(info->camera);
- clk_put(info->camera);
- info->priv->Detect_PowerOFF(info);
- info->priv->Free_Gpio();
- exit:
- i2c_unregister_device(info->i2c_dev);
- return ret;
- }
- /* 初始化模组调用接口,参数为模组的cam_priv */
- int init_atxx_cam(struct cam_priv *priv)
- {
- int ret;
- struct cam_info *info;
- info = kzalloc(sizeof(struct cam_info), GFP_KERNEL);
- if(info == NULL)
- return -ENOMEM;
- info->priv = priv;
- ret = register_cam_device(info);
- if(ret) {
- kfree(info);
- return ret;
- }
- return 0;
- }
- EXPORT_SYMBOL(init_atxx_cam);
/* 主要完成camera模组的探测工作并将其注册到内核中 */
static int register_cam_device(struct cam_info *info)
{
int ret;
struct i2c_adapter *adapter;
struct i2c_board_info board_info;
memset(&board_info, 0, sizeof(struct i2c_board_info));
board_info.addr = info->priv->addr;
strlcpy(board_info.type, info->priv->name, I2C_NAME_SIZE);
/* 动态创建i2c从设备 */
adapter = i2c_get_adapter(info->priv->i2c_bus);
if (adapter == NULL) {
cam_err("can't get i2c adapter %d\n", info->priv->i2c_bus);
return -ENODEV;
}
info->i2c_dev = i2c_new_device(adapter, &board_info);
i2c_put_adapter(adapter);
if (info->i2c_dev == NULL) {
cam_err("can't add i2c device at 0x%x\n", board_info.addr);
return -ENODEV;
}
info->camera = clk_get(&info->i2c_dev->dev, "camera");
if (IS_ERR(info->camera)) {
cam_err("can't get camera clock\n");
ret = -ENODEV;
goto exit;
}
/* 申请Gpio并打开模组电源 */
info->priv->Request_Gpio();
info->priv->Detect_PowerON(info);
/* 设置时钟 */
clk_set_rate(info->camera, ATXX_CAM_CLOCK);
clk_enable(info->camera);
msleep(10);
/* 读取模组id */
ret = info->priv->Detect_ReadId(info);
if(ret) {
cam_err("can't detect this camera: %s\n", info->priv->name);
goto exit_detect;
}
if(info->priv->subdev_id == ATXX_SUBDEV_FORE_CAM) {
fore_cam_driver.id_table = info->priv->id_table;
ret = i2c_add_driver(&fore_cam_driver);
if(ret) {
cam_err("can't add i2c driver\n");
goto exit_detect;
}
} else {
rear_cam_driver.id_table = info->priv->id_table;
ret = i2c_add_driver(&rear_cam_driver);
if(ret) {
cam_err("can't add i2c driver\n");
goto exit_detect;
}
}
/* 初始化v4l2子设备 */
v4l2_i2c_subdev_init(&info->sd, info->i2c_dev, &cam_ops);
info->priv->Set_Config(info);
/* 注册到内核 */
ret = atxx_cam_register_sensor(&info->sd, info->priv->subdev_id);
if(ret) {
cam_err("can't register this camera: %s\n", info->priv->name);
goto exit_detect;
}
clk_disable(info->camera);
info->priv->Detect_PowerOFF(info);
info->priv->Free_Gpio();
info->flags = 0;
return 0;
exit_detect:
clk_disable(info->camera);
clk_put(info->camera);
info->priv->Detect_PowerOFF(info);
info->priv->Free_Gpio();
exit:
i2c_unregister_device(info->i2c_dev);
return ret;
}
/* 初始化模组调用接口,参数为模组的cam_priv */
int init_atxx_cam(struct cam_priv *priv)
{
int ret;
struct cam_info *info;
info = kzalloc(sizeof(struct cam_info), GFP_KERNEL);
if(info == NULL)
return -ENOMEM;
info->priv = priv;
ret = register_cam_device(info);
if(ret) {
kfree(info);
return ret;
}
return 0;
}
EXPORT_SYMBOL(init_atxx_cam);
2、v4l2 接口在 cam_core 中主要完成 v4l2 子设备相关的操作,涉及到的数据结构和接口如下:
- static const struct v4l2_subdev_core_ops cam_core_ops = {
- .g_ctrl = cam_g_ctrl, /* 获取当前命令值 */
- .s_ctrl = cam_s_ctrl, /* 发送命令值 */
- .queryctrl = cam_queryctrl, /* 查询模组是否支持该命令 */
- .reset = cam_reset, /* 复位模组 */
- .init = cam_init, /* 初始化模组 */
- .ioctl = cam_ioctl, /* 保留备用 */
- };
- static const struct v4l2_subdev_video_ops cam_video_ops = {
- .s_fmt = cam_s_fmt, /* 设置模组捕获视频的格式 */
- .try_fmt = cam_try_fmt, /* 尝试是否支持该格式 */
- .enum_fmt = cam_enum_fmt, /* 枚举设备支持的格式 */
- };
- static const struct v4l2_subdev_ops cam_ops = {
- .core = &cam_core_ops, /* v4l2通用接口 */
- .video = &cam_video_ops, /* v4l2视频接口 */
- };
static const struct v4l2_subdev_core_ops cam_core_ops = {
.g_ctrl = cam_g_ctrl, /* 获取当前命令值 */
.s_ctrl = cam_s_ctrl, /* 发送命令值 */
.queryctrl = cam_queryctrl, /* 查询模组是否支持该命令 */
.reset = cam_reset, /* 复位模组 */
.init = cam_init, /* 初始化模组 */
.ioctl = cam_ioctl, /* 保留备用 */
};
static const struct v4l2_subdev_video_ops cam_video_ops = {
.s_fmt = cam_s_fmt, /* 设置模组捕获视频的格式 */
.try_fmt = cam_try_fmt, /* 尝试是否支持该格式 */
.enum_fmt = cam_enum_fmt, /* 枚举设备支持的格式 */
};
static const struct v4l2_subdev_ops cam_ops = {
.core = &cam_core_ops, /* v4l2通用接口 */
.video = &cam_video_ops, /* v4l2视频接口 */
};
3、i2c 通信接口
大部分 camera 寄存器的地址和值都是 8 位或者 16 位数据,因此可以将 i2c 通信接口简化,建立如下结构来描述寄存器地址和值:
- /* 8 bit */
- struct cam_reg {
- unsigned char reg; /* 8位寄存器地址 */
- unsigned char val; /* 8位寄存器值 */
- };
- /* 16 bit */
- struct cam_reg {
- unsigned short reg; /* 16位寄存器地址 */
- unsigned char val; /* 8位寄存器值 */
- };
/* 8 bit */
struct cam_reg {
unsigned char reg; /* 8位寄存器地址 */
unsigned char val; /* 8位寄存器值 */
};
/* 16 bit */
struct cam_reg {
unsigned short reg; /* 16位寄存器地址 */
unsigned char val; /* 8位寄存器值 */
};
封装的 8 位通信接口如下:
- uint8_t cam_read_byte(struct i2c_client *client, uint8_t reg_idx);
- int cam_write_byte(struct i2c_client *client, uint8_t reg_idx, uint8_t val);
uint8_t cam_read_byte(struct i2c_client *client, uint8_t reg_idx);
int cam_write_byte(struct i2c_client *client, uint8_t reg_idx, uint8_t val);
在模组驱动中使用示例如下:
- /***********************************************************************
- * Camera Common Function *
- ***********************************************************************/
- struct cam_reg {
- unsigned char reg;
- unsigned char val;
- };
- static int write_regs(struct cam_info *info, const struct cam_reg reglist[])
- {
- const struct cam_reg *next = reglist;
- while (!((next->reg == REG_TERM) && (next->val == VAL_TERM)))
- {
- cam_write_byte(info->i2c_dev, next->reg, next->val);
- next++;
- }
- return 0;
- }
- /***********************************************************************
- * Camera Initialize Function *
- ***********************************************************************/
- static struct cam_reg cam_set_initialize[] = {
- {0xfc, 0x16},
- ...
- {REG_TERM, VAL_TERM}
- };
- static struct cam_reg cam_set_preview[] = {
- {REG_TERM, VAL_TERM}
- };
- static struct cam_reg cam_set_capture[] = {
- {REG_TERM, VAL_TERM}
- };
- struct cam_reg* cam_reg_init[CAM_INIT_MAX] =
- {
- cam_set_initialize,
- cam_set_preview,
- cam_set_capture
- };
- static int cam_initialize(struct cam_info *info)
- {
- return write_regs(info, cam_reg_init[CAM_INIT]);
- }
- static int cam_preview(struct cam_info *info)
- {
- return write_regs(info, cam_reg_init[CAM_PREVIEW]);
- }
- static int cam_capture(struct cam_info *info)
- {
- return write_regs(info, cam_reg_init[CAM_CAPTRUE]);
- }
/***********************************************************************
* Camera Common Function *
***********************************************************************/
struct cam_reg {
unsigned char reg;
unsigned char val;
};
static int write_regs(struct cam_info *info, const struct cam_reg reglist[])
{
const struct cam_reg *next = reglist;
while (!((next->reg == REG_TERM) && (next->val == VAL_TERM)))
{
cam_write_byte(info->i2c_dev, next->reg, next->val);
next++;
}
return 0;
}
/***********************************************************************
* Camera Initialize Function *
***********************************************************************/
static struct cam_reg cam_set_initialize[] = {
{0xfc, 0x16},
...
{REG_TERM, VAL_TERM}
};
static struct cam_reg cam_set_preview[] = {
{REG_TERM, VAL_TERM}
};
static struct cam_reg cam_set_capture[] = {
{REG_TERM, VAL_TERM}
};
struct cam_reg* cam_reg_init[CAM_INIT_MAX] =
{
cam_set_initialize,
cam_set_preview,
cam_set_capture
};
static int cam_initialize(struct cam_info *info)
{
return write_regs(info, cam_reg_init[CAM_INIT]);
}
static int cam_preview(struct cam_info *info)
{
return write_regs(info, cam_reg_init[CAM_PREVIEW]);
}
static int cam_capture(struct cam_info *info)
{
return write_regs(info, cam_reg_init[CAM_CAPTRUE]);
}
三、camera 模组驱动接口解析在模组驱动中主要工作是实现 struct cam_priv 中的接口,然后调用 init_atxx_cam 注册:
- static struct cam_priv priv = {
- .name = ATXX_CAM_NAME,
- .addr = ATXX_CAM_ADDR,
- .i2c_bus = ATXX_CAM_BUS,
- .subdev_id = ATXX_CAM_SUBDEV,
- .fmt_num = N_FORMATS,
- .res_num = N_RESOLUTIONS,
- .ctl_num = N_CONTROLS,
- .fmt_list = cam_fmt_list,
- .res_list = cam_res_list,
- .ctl_list = cam_ctl_list,
- .id_table = cam_id_table,
- .Open = cam_open,
- .Close = cam_close,
- .Set_Preview = cam_preview,
- .Set_Capture = cam_capture,
- .Set_Config = cam_config,
- .Set_Resolution = cam_resolution,
- .Request_Gpio = cam_gpio_request,
- .Free_Gpio = cam_gpio_free,
- .Detect_PowerON = detect_poweron,
- .Detect_PowerOFF = detect_poweroff,
- .Detect_ReadId = detect_readid,
- };
- static int __init cam_init(void)
- {
- return init_atxx_cam(&priv);
- }
static struct cam_priv priv = {
.name = ATXX_CAM_NAME,
.addr = ATXX_CAM_ADDR,
.i2c_bus = ATXX_CAM_BUS,
.subdev_id = ATXX_CAM_SUBDEV,
.fmt_num = N_FORMATS,
.res_num = N_RESOLUTIONS,
.ctl_num = N_CONTROLS,
.fmt_list = cam_fmt_list,
.res_list = cam_res_list,
.ctl_list = cam_ctl_list,
.id_table = cam_id_table,
.Open = cam_open,
.Close = cam_close,
.Set_Preview = cam_preview,
.Set_Capture = cam_capture,
.Set_Config = cam_config,
.Set_Resolution = cam_resolution,
.Request_Gpio = cam_gpio_request,
.Free_Gpio = cam_gpio_free,
.Detect_PowerON = detect_poweron,
.Detect_PowerOFF = detect_poweroff,
.Detect_ReadId = detect_readid,
};
static int __init cam_init(void)
{
return init_atxx_cam(&priv);
}
经过优化之后 camera 模组的代码耦合度很低,新增模组 driver 时只需要将别的模组 driver 拷贝一份然后修改一下宏定义和寄存器值就可以用了,而且每个模组 driver 的代码比之前少了500行左右,看起来也比较整洁,达到了预期目标。