现在的位置: 首页 > 综合 > 正文

camera 模组驱动优化

2013年02月27日 ⁄ 综合 ⁄ 共 6715字 ⁄ 字号 评论关闭

因为项目比较多,平台支持的 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 结构主要负责和 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);
};

二、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);

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视频接口 */
};

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 位通信接口如下:

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 模组驱动接口解析
在模组驱动中主要工作是实现 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);
}

经过优化之后 camera 模组的代码耦合度很低,新增模组 driver 时只需要将别的模组 driver 拷贝一份然后修改一下宏定义和寄存器值就可以用了,而且每个模组 driver 的代码比之前少了500行左右,看起来也比较整洁,达到了预期目标。

抱歉!评论已关闭.