Linux下v4l2-camera子系统之soc_camera_host(三)
平台:君正x1000
linux kernel: 3.10
camera sensor:ov7725
上节分析了camera_device感觉并没接触到核心,那么camera_host就该完成关键代码了。
Camera_host设备
static struct resource jz_cim_resources[] = {
[0] = {
.flags = IORESOURCE_MEM,
.start = CIM_IOBASE,
.end = CIM_IOBASE + 0x10000 - 1,
},
[1] = {
.flags = IORESOURCE_IRQ,
.start = IRQ_CIM,
}
};
struct platform_device jz_cim_device = {
.name = "jz-cim",
.id = 0,
.resource = jz_cim_resources,
.num_resources = ARRAY_SIZE(jz_cim_resources),
};
struct jz_camera_pdata mensa_camera_pdata = {
.mclk_10khz = 2400,
.flags = 0,
.cam_sensor_pdata[FRONT_CAMERA_INDEX] = {
.gpio_rst = CAMERA_SENSOR_RESET,
.gpio_power = CAMERA_FRONT_SENSOR_PWDN,
.gpio_en = CAMERA_VDD_EN,
},
};
jz_device_register(&jz_cim_device, &mensa_camera_pdata);
int jz_device_register(struct platform_device *pdev,void *pdata)
{
pdev->dev.platform_data = pdata;
return platform_device_register(pdev);
}
这里也是把host做为平台设备注册在platform总线下。
Camera_host驱动
static struct platform_driver jz_camera_driver = {
.remove = __exit_p(jz_camera_remove),
.driver = {
.name = DRIVER_NAME, /* #define DRIVER_NAME “jz-cim” */
.owner = THIS_MODULE,
},
};
static int __init jz_camera_init(void) {
return platform_driver_probe(&jz_camera_driver, jz_camera_probe);
}
struct jz_camera_dev {
struct soc_camera_host soc_host;
struct soc_camera_device *icd[MAX_SOC_CAM_NUM];
struct jz_camera_pdata *pdata;
...
}
static int __init jz_camera_probe(struct platform_device *pdev) {
struct jz_camera_dev *pcdev;
...
pcdev->pdata = pdev->dev.platform_data;
pcdev->soc_host.drv_name = DRIVER_NAME;
pcdev->soc_host.ops = &jz_soc_camera_host_ops;
pcdev->soc_host.priv = pcdev;
/*内嵌的v4l2框架定义的设备就是该camera host平台设备 */
pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
pcdev->soc_host.nr = 0;
pcdev->poll_flag = 0;
pcdev->vb_address_flag = 0;
err = soc_camera_host_register(&pcdev->soc_host);
}
那么此时就又引出来一个结构体struct soc_camera_host,它内嵌struct v4l2_device,就是我们刚开始分析的,camera_host在v4l2框架中代表父设备。
/* \include\media\soc_camera.h */
struct soc_camera_host {
struct v4l2_device v4l2_dev;
struct list_head list;
struct mutex host_lock; /* Protect pipeline modifications */
unsigned char nr; /* Host number */
u32 capabilities;
void *priv;
const char *drv_name;
struct soc_camera_host_ops *ops;
};
/* \drivers\media\platform\soc_camera\soc_camera.c */
int soc_camera_host_register(struct soc_camera_host ici)
{
/ 调用v4l2接口注册初始化camera host ,使之做为父设备 /
ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);
/ 把camera host添加到全局&hosts链表 */
list_add_tail(&ici->list, &hosts);
……
scan_add_host(ici);
}
static void scan_add_host(struct soc_camera_host *ici)
{
struct soc_camera_device *icd;
/*扫描devices全局链表, 匹配icd->iface和ici->nr, icd->iface就是soc_camera_link的bus_id,nr就是camera host平台设备描述信息的id */
list_for_each_entry(icd, &devices, list) {
if (icd->iface == ici->nr) {
/* 这就是把camera_host 做为camera_device的parent*/
icd->parent = ici->v4l2_dev.dev;
soc_camera_probe(icd);
}
}
}
/* \drivers\media\platform\soc_camera\soc_camera.c */
static int soc_camera_probe(struct soc_camera_device icd)
{
/ 既然camera_host 是camera_device的parent,那么这就可以icd->parent获取camera_host */
struct soc_camera_host ici = to_soc_camera_host(icd->parent);
/ 获取扳级文件里面camera_device的soc_camera_link信息 */
struct soc_camera_desc *sdesc = to_soc_camera_desc(icd);
struct soc_camera_host_desc *shd = &sdesc->host_desc;
struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
struct device *control = NULL;
struct v4l2_subdev sd;
struct v4l2_mbus_framefmt mf;
……
/ v4l2_ctrl处理机制,服务于sensor驱动,是应用层对驱动的ioctl的调用 /
ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);
ret = video_dev_create(icd); <1> / 创建camera host的应用层的设备文件 */
if (shd->board_info) {
ret = soc_camera_init_i2c(icd, sdesc); <2>
}
sd = soc_camera_to_subdev(icd);
sd->grp_id = soc_camera_grp_id(icd);
v4l2_set_subdev_hostdata(sd, icd);
ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL);
ret = soc_camera_init_user_formats(icd);
ret = soc_camera_video_start(icd); <3>
}
<1>标记
这其实是对v4l2框架中struct video_device的初始化,它就是向应用层生成设备节点的关键结构。
static int video_dev_create(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct video_device *vdev = video_device_alloc();
strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
vdev->parent = icd->pdev;
vdev->current_norm = V4L2_STD_UNKNOWN;
vdev->fops = &soc_camera_fops;
vdev->ioctl_ops = &soc_camera_ioctl_ops;
vdev->release = video_device_release;
vdev->tvnorms = V4L2_STD_UNKNOWN;
vdev->ctrl_handler = &icd->ctrl_handler;
vdev->lock = &ici->host_lock;
icd->vdev = vdev;
return 0;
}
<2>标记
/* \drivers\media\platform\soc_camera\soc_camera.c */
static int soc_camera_init_i2c(struct soc_camera_device *icd,
struct soc_camera_desc *sdesc)
{
struct i2c_client *client;
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct soc_camera_host_desc *shd = &sdesc->host_desc;
struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id);
struct v4l2_subdev *subdev;
shd->board_info->platform_data = &sdesc->subdev_desc;
//这里面就是处理i2c sensor
subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,shd->board_info, NULL);
client = v4l2_get_subdevdata(subdev);
/* Use to_i2c_client(dev) to recover the i2c client */
icd->control = &client->dev;
return 0;
}
/* \drivers\media\v4l2-core\v4l2-common.c */
struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
struct i2c_adapter *adapter, struct i2c_board_info *info,
const unsigned short *probe_addrs)
{
struct v4l2_subdev *sd = NULL;
struct i2c_client *client;
/* Create the i2c client */
if (info->addr == 0 && probe_addrs)
client = i2c_new_probed_device(adapter, info, probe_addrs, NULL);
else
client = i2c_new_device(adapter, info);//创建一个i2c设备
sd = i2c_get_clientdata(client);// 获取i2c sensor驱动里client的v4l2_subdev
/* 将v4l2_subdev与v4l2_device绑定,将sensor里的所有的ioctl操作添加到icd的ctrl里 */
if (v4l2_device_register_subdev(v4l2_dev, sd))
sd = NULL;
return sd;
}
这里i2c_new_device(adapter, info)创建一个i2c设备后就会找对应的驱动匹配,匹配后就进入驱动的probe函数。等probe执行完后i2c_new_device返回,
<3>标记
/* \drivers\media\platform\soc_camera\soc_camera.c */
static int soc_camera_video_start(struct soc_camera_device *icd)
{
const struct device_type *type = icd->vdev->dev.type;
/* 调用v4l2框架函数,在应用层创建/dev/videox */
ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
}