一般摄像头传感器都会带有i2c接口等类似的总线接口来进行配置他,所以学习V4l2 的也不免会引入其他驱动框架的学习。
在学习V4L2 之前,先看一下I2C 驱动框架的实现。
从网上找了一张I2C的linux 驱动接口图:
网上很多资料将I2C驱动归结为3层。分为I2c 设备驱动层,I2c 核心层 以及I2c 总线驱动层。下面说一下我对这些层的理解。
1)I2c驱动层:分为i2c_client和i2c_driver,这个层是用来描述i2c总线上面挂载的某个具体设备的,比如说mcu的i2c总线上挂载了一个i2c 控制的camera,则需要实现i2c_client和i2c_driver。i2c_client 用来描述这个设备的具体硬件特性,而i2c_driver 则用来提供该设备的具体操作方法。
2 )I2c 核心层:该层基本不需要改动,主要是提供了一些注册,以及驱动层和总线层交互的一些函数,linux 把一些通用的东西抽象出来,放在核心层,便于驱动人员进行二次开发。
3 )I2c 总线驱动层,这边主要是实现I2c_adapter的一个结构,该结构要来描述mcu上面具体某个i2c 控制器,该层一般由soc厂家来做,对于某个soc,一般需要提供具体的I2c 控制器驱动。最终在i2c 总线上的数据收发,都会通过 i2c_adapter 这个结构提供的方法来实现。
然后再介绍一下v4l2的框架,下面是从网上找到的一张不错的结构图:
v4l2 有几个比较重要的结构: vidoe_device,v4l2_dev,v4l2_subdev
其中 vidoe_device 这个结构,用来向上层应用软件提供字符设备接口的,该设备名称通常是 /dev/videox ,通常利用video_register_device 注册一个字符设备接口,并提供file_operation 操作函数。vidoe_device 一般指代camera 控制器,当然vidoe_device通常被嵌入更大的结构体里面,来描述更复杂的camera 控制器。而v4l2_dev则用来管理v4l2_subdev,一个v4l2_dev可以对应多个v4l2_subdev,v4l2_subdev通常是用来描述某个子设备的,比如说mcu外接的某个camera,则该camera可描述为v4l2_subdev。一般会把vidoe_device和v4l2_dev关联起来,这样通过camera 控制器就能找到v4l2_dev,进而再找到v4l2_subdev
上面的图里面具体也可以分为几层:
V4L2核心驱动: 核心层基本都不需要改动,提供通用的一些操作函数。
平台V4L2 驱动: 该层一般来说主要由soc厂家来实现,对于soc上面的具体的camera控制器,适配相应的驱动
具体的sensor驱动:当我们要使用不同的外接camera时,需要实现这一层。
所以,对于某个带i2c接口的camera来说,其在linux下v4l2和i2c的调用关系就变成如下图:
应用层软件,可以通过video接口,调用video的一些通用函数,来设备camera 控制器以及i2c camera sensor,当然也可以直接使用i2c的字符设备接口,来设置i2c总线上面挂载的设备。
有了上面的具体概念,下面就从代码层面来分析i2c和v4l2的调用流程,具体驱动代码基于nxp的IMX6ULRM,linux 内核版本为4.1.15 .
首先看一下I2c总线驱动(i2c控制器)的具体注册过程,具体代码在drivers/i2c/busses/i2c-imx.c中。
static const struct of_device_id i2c_imx_dt_ids[] = {
{ .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
{ /* sentinel */ }
};
static struct platform_driver i2c_imx_driver = {
.probe = i2c_imx_probe,
.remove = i2c_imx_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = i2c_imx_dt_ids,
.pm = IMX_I2C_PM,
},
.id_table = imx_i2c_devtype,
};
static int __init i2c_adap_imx_init(void)
{
return platform_driver_register(&i2c_imx_driver);
}
subsys_initcall(i2c_adap_imx_init);
可以看到利用了平台总线的方式,注册了i2c_imx_driver,这边有platform_driver,那么就需要platform_device作匹配,在dts中找到如下信息:
i2c1: i2c@43f80000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx35-i2c", "fsl,imx1-i2c";
reg = <0x43f80000 0x4000>;
clocks = <&clks 51>;
clock-names = "ipg_per";
interrupts = <10>;
status = "disabled";
};
可以看到 comptiable 里面fsl,imx1-i2c是能匹配成功的,相应的设备信息也有了,所以这边会调用platform_driver的i2c_imx_probe。
static int i2c_imx_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
&pdev->dev);
struct imx_i2c_struct *i2c_imx;
struct resource *res;
struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
void __iomem *base;
int irq, ret;
dma_addr_t phy_addr;
dev_dbg(&pdev->dev, "<%s>\n", __func__);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "can't get irq number\n");
return irq;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
phy_addr = (dma_addr_t)res->start;
i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);
if (!i2c_imx)
return -ENOMEM;
if (of_id)
i2c_imx->hwdata = of_id->data;
else
i2c_imx->hwdata = (struct imx_i2c_hwdata *)
platform_get_device_id(pdev)->driver_data;
/* Setup i2c_imx driver structure */
strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
i2c_imx->adapter.owner = THIS_MODULE;
i2c_imx->adapter.algo = &i2c_imx_algo;
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
i2c_imx->base = base;
/* Get I2C clock */
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c_imx->clk)) {
dev_err(&pdev->dev, "can't get I2C clock\n");
return PTR_ERR(i2c_imx->clk);
}
ret = clk_prepare_enable(i2c_imx->clk);
if (ret) {
dev_err(&pdev->dev, "can't enable I2C clock\n");
return ret;
}
/* Request IRQ */
ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,
IRQF_NO_SUSPEND, pdev->name, i2c_imx);
if (ret) {
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
goto clk_disable;
}
/* Init queue */
init_waitqueue_head(&i2c_imx->queue);
/* Set up adapter data */
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
/* Set up clock divider */
i2c_imx->bitrate = IMX_I2C_BIT_RATE;
ret = of_property_read_u32(pdev->dev.of_node,
"clock-frequency", &i2c_imx->bitrate);
if (ret < 0 && pdata && pdata->bitrate)
i2c_imx->bitrate = pdata->bitrate;
/* Set up chip registers to defaults */
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
i2c_imx, IMX_I2C_I2CR);
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < 0) {
dev_err(&pdev->dev, "registration failed\n");
goto clk_disable;
}
/* Set up platform driver data */
platform_set_drvdata(pdev, i2c_imx);
clk_disable_unprepare(i2c_imx->clk);
dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
i2c_imx->adapter.name);
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
/* Init DMA config if supported */
i2c_imx_dma_request(i2c_imx, phy_addr);
return 0; /* Return OK */
clk_disable:
clk_disable_unprepare(i2c_imx->clk);
return ret;
}
在i2c_imx_probe中,主要关注i2c_add_numbered_adapter 函数,该函数注册了一个i2c_adapter 设备。
i2c_add_numbered_adapter
------->i2c_add_adapter
------->i2c_register_adapter
其中在i2c_add_adapter中:
int i2c_add_adapter(struct i2c_adapter *adapter)
{
struct device *dev = &adapter->dev;
int id;
if (dev->of_node) {
id = of_alias_get_id(dev->of_node, "i2c");
if (id >= 0) {
adapter->nr = id;
return __i2c_add_numbered_adapter(adapter);
}
}
mutex_lock(&core_lock);
id = idr_alloc(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
mutex_unlock(&core_lock);
if (id < 0)
return id;
adapter->nr = id;
return i2c_register_adapter(adapter);
}
可以看到设置了adapter->nr ,这个nr比较重要,后面在字符设备操作的时候,会利用这个总线number nr来得到某个i2c_adapter.
static struct i2c_algorithm i2c_imx_algo = {
.master_xfer = i2c_imx_xfer,
.functionality = i2c_imx_func,
};
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
}
/* Sanity checks */
if (unlikely(adap->name[0] == '\0')) {
pr_err("i2c-core: Attempt to register an adapter with "
"no name!\n");
return -EINVAL;
}
if (unlikely(!adap->algo)) {
pr_err("i2c-core: Attempt to register adapter '%s' with "
"no algo!\n", adap->name);
return -EINVAL;
}
rt_mutex_init(&adap->bus_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
......................................................................................................................................................................................................................................................................................................................................................................................................................
exit_recovery:
/* create pre-declared device nodes */
of_i2c_register_devices(adap);
acpi_i2c_register_devices(adap);
acpi_i2c_install_space_handler(adap);
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);
return 0;
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
}
可以看到把adapter总线设置为i2c_bus_type,并注册该adapter设备,而且可以看到adapter的i2c_imx_algo结构,该结构是adapter的核心结构,使用i2c控制器收发收据都由该结构中的函数来完成。
同时下面有两个比较重要的函数of_i2c_register_devices和i2c_scan_static_board_info。其中of_i2c_register_devices 用于在dts中描述了i2c子设备,来建立一个i2c_client,和后面注册i2c_drvier的时候进行匹配。而i2c_scan_static_board_info 则是当不用dts,利用了board_info 这个结构来描述了i2c子设备时,构建相应的i2c_client。我们这边使用的dts,所以进去看一下of_i2c_register_devices函数:
static void of_i2c_register_devices(struct i2c_adapter *adap)
{
struct device_node *node;
/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
for_each_available_child_of_node(adap->dev.of_node, node)
of_i2c_register_device(adap, node);
}
for_each_available_child_of_node为寻找adapter的子节,在dts中可以看到i2c的子节点:
&i2c2 {
ar0134: ar0134@18 {
compatible = "hon,sensors";
reg = <0x18>;
clocks = <&clks IMX6UL_CLK_CSI>;
clock-names = "csi_mclk";
csi_id = <0>;
mclk = <24000000>;
status = "okay";
gpio_reset = <&gpio3 14 GPIO_ACTIVE_LOW>;
gpio_power = <&gpio3 28 GPIO_ACTIVE_LOW>;
reset_base = <&gpio3>;
power_base = <&gpio3>;
port {
ar0134_ep: endpoint {
remote-endpoint = <&csi1_ep>;
};
};
};
};
其中hon,sensors即为camera sensor,作为i2c的一个子节点存在,所以就调用of_i2c_register_device
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
struct i2c_client *result;
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr;
int len;
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
node->full_name);
return ERR_PTR(-EINVAL);
}
addr = of_get_property(node, "reg", &len);
if (!addr || (len < sizeof(int))) {
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
node->full_name);
return ERR_PTR(-EINVAL);
}
info.addr = be32_to_cpup(addr);
if (info.addr > (1 << 10) - 1) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
info.addr, node->full_name);
return ERR_PTR(-EINVAL);
}
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
result = i2c_new_device(adap, &info);
if (result == NULL) {
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
node->full_name);
of_node_put(node);
return ERR_PTR(-EINVAL);
}
return result;
}
然后调用i2c_new_device:
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}
/* Check for address business */
status = i2c_check_addr_busy(adap, client->addr);
if (status)
goto out_err;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
client->dev.fwnode = info->fwnode;
i2c_dev_set_name(adap, client);
status = device_register(&client->dev);
if (status)
goto out_err;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}
可以看到注册了一个i2c_client device ,并且把i2c_client 和adapter 关联起来。
前面已经实现了adapter 和i2c_client,那么字符设备操作的接口和i2c_driver也是需要的。先来看如何注册i2c_adapter的字符设备操作接口。查看drivers/i2c/i2c-dev.c 文件
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
module_init(i2c_dev_init);
可以看到注册了一个字符设备,其中i2cdev_fops操作函数是所有i2c 字符设备的操作入口函数。
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
res = register_chrdev(I2C_MAJOR, "karry-i2c", &i2cdev_fops);
if (res)
goto out;
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
其中最后的i2c_for_each_dev(NULL, i2cdev_attach_adapter); 遍历device,找到每个adapter,找到以后,调用i2cdev_attach_adapter,然后为其建立在/sys/下创建设备节点,最终用户层会由udev 会在/dev目录下生成i2c-x设备节点。
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
/* register this i2c device with the driver core */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
error:
return_i2c_dev(i2c_dev);
return res;
}
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);用于生成设备节点,可以看到是以adap->nr为次设备号生成节点,可以再看一下字符设备i2cdev_open函数:
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
i2c_dev = i2c_dev_get_by_minor(minor);
if (!i2c_dev)
return -ENODEV;
adap = i2c_get_adapter(i2c_dev->adap->nr);
if (!adap)
return -ENODEV;
/* This creates an anonymous i2c_client, which may later be
* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
*
* This client is ** NEVER REGISTERED ** with the driver model
* or I2C core code!! It just holds private copies of addressing
* information and maybe a PEC flag.
*/
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->adapter = adap;
file->private_data = client;
return 0;
}
可以看到 minor = iminor(inode);以次设备号为索引,找到对应的adapter,所以对于同一套file_operation,可以打开不同的i2c_adapter设备。这个时候,已经可以直接利用/dev/i2c-x字符设备接口,来操作i2c总线上的i2c设备了。但是对于camera设备,当然需要video相关的操作才能继续进行。这边把v4l2相关的整合放到i2c_driver里面实现,在介绍i2c_driver之前,先介绍一下v4l2 video的初始化过程。先分析/drivers/media/mx6ul_capture.c 文件。
static const struct of_device_id mx6s_csi_dt_ids[] = {
{ .compatible = "fsl,imx6ul-csi", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mx6s_csi_dt_ids);
static struct platform_driver mx6s_csi_driver = {
.driver = {
.name = MX6UL_CAM_DRV_NAME,
.of_match_table = of_match_ptr(mx6s_csi_dt_ids),
.pm = &mx6s_csi_pm_ops,
},
.probe = mx6s_csi_probe,
.remove = mx6s_csi_remove,
};
module_platform_driver(mx6s_csi_driver);
系统初始化的时候,注册mx6s_csi_driver,也是利用pltform_bus的方式进行初始化的,mx6s_csi_driver指的就是camera 控制器的驱动,查看一下dts,可以发现:
csi: csi@021c4000 {
compatible = "fsl,imx6ul-csi", "fsl,imx6s-csi";
reg = <0x021c4000 0x4000>;
interrupts = ;
clocks = <&clks IMX6UL_CLK_DUMMY>,
<&clks IMX6UL_CLK_CSI>,
<&clks IMX6UL_CLK_DUMMY>;
clock-names = "disp-axi", "csi_mclk", "disp_dcic";
status = "disabled";
};
匹配imx6ul-csi,说明 platform_device 会被系统初始化好,match以后,会调用mx6s_csi_probe:
static int mx6s_csi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mx6s_csi_dev *csi_dev;
struct video_device *vdev;
struct resource *res;
struct device_node *np = pdev->dev.of_node;
int ret = 0;
printk(KERN_INFO "mx6s_csi_probe\n");
dev_dbg(dev, "initialising\n");
/* Prepare our private structure */
csi_dev = devm_kzalloc(dev, sizeof(struct mx6s_csi_dev), GFP_ATOMIC);
if (!csi_dev) {
dev_err(dev, "Can't allocate private structure\n");
return -ENODEV;
}
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
ret = v4l2_device_register(dev, &csi_dev->v4l2_dev);
if (ret < 0) {
dev_err(dev, "v4l2_device_register() failed: %d\n", ret);
return -ENODEV;
}
/* initialize locks */
mutex_init(&csi_dev->lock);
spin_lock_init(&csi_dev->slock);
/* Allocate memory for video device */
vdev = video_device_alloc();
if (vdev == NULL) {
ret = -ENOMEM;
goto err_vdev;
}
snprintf(vdev->name, sizeof(vdev->name), "mx6ul-csi");
vdev->v4l2_dev = &csi_dev->v4l2_dev;
vdev->fops = &mx6s_csi_fops;
vdev->ioctl_ops = &mx6s_csi_ioctl_ops;
vdev->release = video_device_release;
vdev->lock = &csi_dev->lock;
vdev->queue = &csi_dev->vb2_vidq;
csi_dev->vdev = vdev;
video_set_drvdata(csi_dev->vdev, csi_dev);
mutex_lock(&csi_dev->lock);
ret = video_register_device(csi_dev->vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
video_device_release(csi_dev->vdev);
mutex_unlock(&csi_dev->lock);
goto err_vdev;
}
/* install interrupt handler */
if (devm_request_irq(dev, csi_dev->irq, mx6s_csi_irq_handler,
0, "csi", (void *)csi_dev)) {
mutex_unlock(&csi_dev->lock);
dev_err(dev, "Request CSI IRQ failed.\n");
ret = -ENODEV;
goto err_irq;
}
mutex_unlock(&csi_dev->lock);
ret = mx6sx_register_subdevs(csi_dev);
if (ret < 0)
goto err_irq;
pm_runtime_enable(csi_dev->dev);
return 0;
err_irq:
video_unregister_device(csi_dev->vdev);
err_vdev:
v4l2_device_unregister(&csi_dev->v4l2_dev);
return ret;
}
v4l2_device_register中好像没做什么太重要的事情,把device和v4l2_dev 关联起来。
vdev = video_device_alloc();这个函数比较重要,分配了一个video_device,并且
vdev->fops = &mx6s_csi_fops;
vdev->ioctl_ops = &mx6s_csi_ioctl_ops;这两个参数很重要,mx6s_csi_fops; 是个v4l2_file_operations,v4l2的相关操作都是调到这里来,mx6s_csi_ioctl_ops则是v4l2 ioctl 的具体实现都在这个函数里。vdev->queue = &csi_dev->vb2_vidq;这个queue则用来管理图像采集的buffer。
在video_register_device 函数中,还注册了一个字符设备:
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.get_unmapped_area = v4l2_get_unmapped_area,
.mmap = v4l2_mmap,
.unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l2_compat_ioctl32,
#endif
.release = v4l2_release,
.poll = v4l2_poll,
.llseek = no_llseek,
};
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
/* A minor value of -1 marks this video device as never
having been registered */
vdev->minor = -1;
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
goto cleanup;
}
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
}
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
vdev->dev.parent = vdev->dev_parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto cleanup;
}
/* Register the release callback that will be called when the last
reference to the device goes away. */
vdev->dev.release = v4l2_device_release;
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
return 0;
cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
video_device[vdev->minor] = NULL;
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
vdev->minor = -1;
return ret;
}
注册字符设备以后,最终生成/dev/videox节点,video_device[vdev->minor] = vdev; 操作根据次设备号在video_device中记录该video结构。对video_device节点的操作,直接调用到v4l2_fops,可以看一下v4l2_open:
static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev;
int ret = 0;
/* Check if the video device is available */
mutex_lock(&videodev_lock);
vdev = video_devdata(filp);
/* return ENODEV if the video device has already been removed. */
if (vdev == NULL || !video_is_registered(vdev)) {
mutex_unlock(&videodev_lock);
return -ENODEV;
}
/* and increase the device refcount */
video_get(vdev);
mutex_unlock(&videodev_lock);
if (vdev->fops->open) {
if (video_is_registered(vdev))
ret = vdev->fops->open(filp);
else
ret = -ENODEV;
}
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
printk(KERN_DEBUG "%s: open (%d)\n",
video_device_node_name(vdev), ret);
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
return ret;
}
vdev = video_devdata(filp); 根据次设备号从video_device[]数组中取出注册的video_device,然后调用vdev->fops->open函数,即前面的v4l2_file_operations操作集mx6s_csi_fops。贴一下这边实现的v4l2_file_operations 和v4l2_ioctl_ops 操作集函数:
static const struct v4l2_ioctl_ops mx6s_csi_ioctl_ops = {
.vidioc_querycap = mx6s_vidioc_querycap,
.vidioc_enum_fmt_vid_cap = mx6s_vidioc_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = mx6s_vidioc_try_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = mx6s_vidioc_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = mx6s_vidioc_s_fmt_vid_cap,
.vidioc_cropcap = mx6s_vidioc_cropcap,
.vidioc_s_crop = mx6s_vidioc_s_crop,
.vidioc_g_crop = mx6s_vidioc_g_crop,
.vidioc_reqbufs = mx6s_vidioc_reqbufs,
.vidioc_querybuf = mx6s_vidioc_querybuf,
.vidioc_qbuf = mx6s_vidioc_qbuf,
.vidioc_dqbuf = mx6s_vidioc_dqbuf,
.vidioc_g_std = mx6s_vidioc_g_std,
.vidioc_s_std = mx6s_vidioc_s_std,
.vidioc_querystd = mx6s_vidioc_querystd,
.vidioc_enum_input = mx6s_vidioc_enum_input,
.vidioc_g_input = mx6s_vidioc_g_input,
.vidioc_s_input = mx6s_vidioc_s_input,
.vidioc_streamon = mx6s_vidioc_streamon,
.vidioc_streamoff = mx6s_vidioc_streamoff,
.vidioc_g_parm = mx6s_vidioc_g_parm,
.vidioc_s_parm = mx6s_vidioc_s_parm,
.vidioc_enum_framesizes = mx6s_vidioc_enum_framesizes,
.vidioc_enum_frameintervals = mx6s_vidioc_enum_frameintervals,
.vidioc_s_ctrl = mx6s_vidioc_s_ctrl,
.vidioc_g_ctrl = mx6s_vidioc_g_ctrl,
.vidioc_default = mx6s_vidioc_default,
};
static struct v4l2_file_operations mx6s_csi_fops = {
.owner = THIS_MODULE,
.open = mx6s_csi_open,
.release = mx6s_csi_close,
.read = mx6s_csi_read,
.poll = mx6s_csi_poll,
.unlocked_ioctl = mx6s_csi_ioctl, /* V4L2 ioctl handler */
.mmap = mx6s_csi_mmap,
};
这边有些函数是一定要实现的,有些是不一定要实现的。
在mx6s_csi_probe函数最后,还调用了mx6sx_register_subdevs函数,这个函数也比较重要,会在后面的i2c_driver 里面匹配v4l2_sudev使用:
static int mx6sx_register_subdevs(struct mx6s_csi_dev *csi_dev)
{
struct device_node *parent = csi_dev->dev->of_node;
struct device_node *node, *port, *rem;
int ret;
/* Attach sensors linked to csi receivers */
for_each_available_child_of_node(parent, node) {
if (of_node_cmp(node->name, "port"))
continue;
/* The csi node can have only port subnode. */
port = of_get_next_child(node, NULL);
if (!port)
continue;
rem = of_graph_get_remote_port_parent(port);
of_node_put(port);
if (rem == NULL) {
v4l2_info(&csi_dev->v4l2_dev,
"Remote device at %s not found\n",
port->full_name);
return -1;
}
csi_dev->asd.match_type = V4L2_ASYNC_MATCH_OF;
csi_dev->asd.match.of.node = rem;
csi_dev->async_subdevs[0] = &csi_dev->asd;
of_node_put(rem);
break;
}
csi_dev->subdev_notifier.subdevs = csi_dev->async_subdevs;
csi_dev->subdev_notifier.num_subdevs = 1;
csi_dev->subdev_notifier.bound = subdev_notifier_bound;
ret = v4l2_async_notifier_register(&csi_dev->v4l2_dev,
&csi_dev->subdev_notifier);
if (ret)
dev_err(csi_dev->dev,
"Error register async notifier regoster\n");
return ret;
}
这边会csi_dev->dev->of_node;根据camera控制器的节点,找到相应的port,然后进一步提取子节点,可以看到如下dts:
&csi {
port {
csi1_ep: endpoint {
remote-endpoint = <&ar0134_ep>;
};
};
};
&i2c2 {
ar0134: ar0134@18 {
compatible = "hon,sensors";
reg = <0x18>;
clocks = <&clks IMX6UL_CLK_CSI>;
clock-names = "csi_mclk";
csi_id = <0>;
mclk = <24000000>;
status = "okay";
gpio_reset = <&gpio3 14 GPIO_ACTIVE_LOW>;
gpio_power = <&gpio3 28 GPIO_ACTIVE_LOW>;
reset_base = <&gpio3>;
power_base = <&gpio3>;
port {
ar0134_ep: endpoint {
remote-endpoint = <&csi1_ep>;
};
};
};
};
可以看到最终会找到ar0134_ep,而ar0134_ep 就是i2c 设备的节点。所以这边先建立一个异步的async_subdevs,放到通知链里面,等会后再进行subdevs的初始化。
csi_dev->subdev_notifier.bound = subdev_notifier_bound;设置绑定函数,等match了再调用。调用v4l2_async_notifier_register,把v4l2_dev 注册到subdev_notifier上面。
这边实现了camera驱动器的相关操作,现在通过/dev/videox 节点可以使用控制器了,但是camera 子设备还没看到呢,接下来分析一下i2c_driver的实现,即如何挂载v4l2_subdev.平常所说给一个产品编写一个camera驱动也大多指的是这边驱动代码的实现。
#ifdef CONFIG_USE_OF
static const struct of_device_id sensor_dt_match[] = {
{.compatible = "hon,sensors", .data = 0},
{}
};
MODULE_DEVICE_TABLE(of, sensor_dt_match);
#endif
static struct i2c_driver sensor_i2c_driver = {
.driver = {
.name = MODULE_NAME,
#ifdef CONFIG_USE_OF
.of_match_table = sensor_dt_match,
#endif
.pm = &i2c_sensor_pm_ops,
},
.probe = sensor_probe,
.remove = sensor_remove,
.id_table = sensor_id,
};
module_i2c_driver(sensor_i2c_driver);
可以看到初始化的时候调用module_i2c_driver这个宏注册了i2c_driver,在注册的时候,会在device上面匹配i2c_client,因为前面注册完i2c_adapter 会根据dts注册i2c_client,所以这边会match,然后调用sensor_probe函数。
static int sensor_probe(struct i2c_client *client,
const struct i2c_device_id *devid)
{
int ret;
struct device *dev = &client->dev;
struct sensor_dev *sensor;
struct i2c_adapter * adapter = client->adapter; //to_i2c_adapter(client->dev.parent);
const __be32 *parp;
struct device_node *np = NULL;
dev_info(&client->dev, "devid=%s, name=%s-%x\n",
devid->name, client->name, client->addr);
sensor = kzalloc(sizeof(struct sensor_dev), GFP_KERNEL);
if (!sensor) {
dev_err(&client->dev, "Failed to allocate memory for private data.\n");
return -ENOMEM;
}
if (strcmp("jade", dev->of_node->name) == 0) {
sensor->type = SENSOR_JADE;
} else if (strcmp("ar0134", dev->of_node->name) == 0) {
sensor->type = SENSOR_AR0134;
} else {
sensor->type = SENSOR_GENERIC;
}
sensor->sensor_clk = devm_clk_get(dev, "csi_mclk");
if (IS_ERR(sensor->sensor_clk)) {
dev_err(dev, "get mclk failed\n");
ret = PTR_ERR(sensor->sensor_clk);
goto err_data;
}
ret = of_property_read_u32(dev->of_node, "mclk",
&sensor->mclk);
if (ret) {
dev_err(dev, "mclk frequency is invalid\n");
goto err_data;
}
sensor->i2c_client = client;
sensor_set_clk_rate(sensor);
v4l2_i2c_subdev_init(&sensor->subdev, client, &sensor_subdev_ops);
ret = v4l2_async_register_subdev(&sensor->subdev);
if (ret < 0) {
dev_err(dev,
"%s--Async register failed, ret=%d\n", __func__, ret);
goto err_data;
}
if (sensor->type == SENSOR_AR0134 || sensor->type == SENSOR_JADE)
{
sensor->power_gpio = of_get_named_gpio(dev->of_node, "gpio_power", 0);
if (!gpio_is_valid(sensor->power_gpio))
dev_err(&client->dev, "can't find power gpio.\n");
parp = of_get_property(dev->of_node, "reset_base", NULL);
np = of_find_node_by_phandle(be32_to_cpup(parp));
sensor->reset_base = of_iomap(np, 0);
parp = of_get_property(dev->of_node, "power_base", NULL);
np = of_find_node_by_phandle(be32_to_cpup(parp));
sensor->power_base = of_iomap(np, 0);
}
if (sensor->type == SENSOR_JADE)
{
sensor->standby_gpio = of_get_named_gpio(dev->of_node, "gpio_standby", 0);
if (!gpio_is_valid(sensor->power_gpio))
dev_err(&client->dev, "can't find standby gpio.\n");
parp = of_get_property(dev->of_node, "standby_base", NULL);
np = of_find_node_by_phandle(be32_to_cpup(parp));
sensor->standby_base = of_iomap(np, 0);
}
sensor->keepPowerOn = 0;
dev_info(&client->dev, "device successfully probed.\n");
device_init_wakeup(dev, true);
pm_set_wakeup_min_time_ms(dev,1000);
return 0;
err_data:
kfree(dev);
return ret;
}
可以看到调用v4l2_i2c_subdev_init:
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
const struct v4l2_subdev_ops *ops)
{
v4l2_subdev_init(sd, ops);
sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
/* the owner is the same as the i2c_client's driver owner */
sd->owner = client->dev.driver->owner;
sd->dev = &client->dev;
/* i2c_client and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, client);
i2c_set_clientdata(client, sd);
/* initialize name */
snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
client->dev.driver->name, i2c_adapter_id(client->adapter),
client->addr);
}
把 i2c_client 和 v4l2_subdev关联起来,并且设置了v4l2_subdev的操作函数sensor_subdev_ops,当vidoe的相关操作需要camera sensor支持时,则会进一步调用到sensor_subdev_ops中实现的函数,可以看一下里面的具体内容:
static struct v4l2_subdev_core_ops sensor_subdev_core_ops = {
.s_power = sensor_s_power,
.ioctl = sensor_ioctl,
.init = sensor_init,
.g_ctrl = sensor_subdev_g_ctrl,
.s_ctrl = sensor_subdev_s_ctrl,
};
static struct v4l2_subdev_video_ops sensor_subdev_video_ops = {
.enum_mbus_fmt = sensor_enum_fmt, /* enum_mbus_fmt: enumerate pixel formats, provided by a video data source */
.try_mbus_fmt = sensor_try_fmt, /* try_mbus_fmt: try to set a pixel format on a video data source */
.g_mbus_fmt = sensor_g_fmt, /* g_mbus_fmt: get the current pixel format, provided by a video data source */
.s_mbus_fmt = sensor_s_fmt, /* s_mbus_fmt: set a pixel format on a video data source */
.cropcap = sensor_cropcap,
.g_crop = sensor_g_crop,
.s_stream = sensor_s_stream,
};
static struct v4l2_subdev_ops sensor_subdev_ops = {
.core = &sensor_subdev_core_ops,
.video = &sensor_subdev_video_ops,
};
通常会以这样的v4l2_subdev_call 形式来调用里面的操作函数。
下面再看一下probe中的v4l2_async_register_subdev函数:
int v4l2_async_register_subdev(struct v4l2_subdev *sd)
{
struct v4l2_async_notifier *notifier;
mutex_lock(&list_lock);
INIT_LIST_HEAD(&sd->async_list);
list_for_each_entry(notifier, ¬ifier_list, list) {
struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
if (asd) {
int ret = v4l2_async_test_notify(notifier, sd, asd);
mutex_unlock(&list_lock);
return ret;
}
}
/* None matched, wait for hot-plugging */
list_add(&sd->async_list, &subdev_list);
mutex_unlock(&list_lock);
return 0;
}
从notifier_list链中取出挂载的v4l2_async_notifier,前面在video 控制器初始化的时候我们已经挂载了一个结构在里面,所以这边能找到v4l2_async_subdev asd,调用v4l2_async_test_notify:
static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd,
struct v4l2_async_subdev *asd)
{
int ret;
/* Remove from the waiting list */
list_del(&asd->list);
sd->asd = asd;
sd->notifier = notifier;
if (notifier->bound) {
ret = notifier->bound(notifier, sd, asd);
if (ret < 0)
return ret;
}
/* Move from the global subdevice list to notifier's done */
list_move(&sd->async_list, ¬ifier->done);
ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
if (ret < 0) {
if (notifier->unbind)
notifier->unbind(notifier, sd, asd);
return ret;
}
if (list_empty(¬ifier->waiting) && notifier->complete)
return notifier->complete(notifier);
return 0;
}
调用notifier->bound,该函数就是前面设置的subdev_notifier_bound:
static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
{
struct mx6s_csi_dev *csi_dev = notifier_to_mx6s_dev(notifier);
/* Find platform data for this sensor subdev */
if (csi_dev->asd.match.of.node == subdev->dev->of_node)
csi_dev->sd = subdev;
if (subdev == NULL)
return -EINVAL;
v4l2_info(&csi_dev->v4l2_dev, "Registered sensor subdevice: %s\n",
subdev->name);
return 0;
}
可以看到设置了csi_dev->sd = subdev;这下可以直接从控制器 device直接寻址到subdev,video的操作从上到下基本打通了。
然后调用v4l2_device_register_subdev,把subdev赋值给v4l2_subdev 链表。
随便看一个subdev提供的操作函数:
int sensor_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct sensor_dev *sensor = to_sensor(client);
if (sensor->type == SENSOR_AR0134) {
u16 val;
switch (control->id) {
case V4L2_CID_GAIN:
sensor_read(client, 0x30B0, &val);
val &= 0xffcf;
val |= ((control->value << 4) & 0x0030);
return sensor_write(client, 0x30B0, val & 0xFFFF);
case V4L2_CID_HFLIP:
return -EINVAL;
case V4L2_CID_EXPOSURE:
/* 0x3012 - coarse
* 0x3014 - fine
*/
return sensor_write(client, 0x3012, control->value & 0xFFFF);
}
} else {
dev_err(&client->dev, "error: unsupported control 0x%08X\n", control->id);
}
return 0;
}
可以看到获取了i2c_client,然后调用sensor_write:
static int sensor_write(struct i2c_client *client, u16 reg, u16 val)
{
struct i2c_msg msg;
u8 buf[4];
int ret;
buf[0] = reg >> 8;
buf[1] = reg & 0xFF;
buf[2] = val >> 8;
buf[3] = val & 0xFF;
msg.addr = client->addr;
msg.flags = client->flags;
msg.buf = buf;
msg.len = sizeof(buf);
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret >= 0)
return 0;
dev_err(&client->dev, "sensor_write reg(0x%x val:0x%x) failed !\n", reg, val);
return ret;
}
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int ret;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_adapter(adap);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_adapter(adap);
}
ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_adapter(adap);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
最终调用adapter 提供的方法来读写i2c设备。可以把上面讲的调用流程用下面的一张图来概括:
下面总结一下v4l2 里面重要操作集函数的调用流程。
从网上找了一张数据采集的流程图,稍微加工了下: