CMOS摄像头驱动分析-i2c驱动
文章目录
- CMOS摄像头驱动分析-i2c驱动
- 设备树内容
- module_i2c_driver宏分析
- ov2640_i2c_driver
- ov2640_probe
设备树内容
ov2640: camera@0x30 {compatible = "ovti,ov2640";reg = <0x30>;status = "okay";pinctrl-names = "default";pinctrl-0 = <&pinctrl_csi1&csi_pwn_rst>;resetb = <&gpio1 2 GPIO_ACTIVE_LOW>;pwdn = <&gpio1 4 GPIO_ACTIVE_HIGH>;clocks = <&clks IMX6UL_CLK_CSI>;clock-names = "xvclk";port {camera_ep: endpoint {remote-endpoint = <&csi_ep>;bus-width = <8>;};};};
module_i2c_driver宏分析
// 注册i2c驱动程序
module_i2c_driver(ov2640_i2c_driver);
module_i2c_driver(ov2640_i2c_driver),可以分析出以下信息:
module_i2c_driver是一个宏,它可能是在编程中定义的一个宏,用于简化I2C驱动模块的注册。
ov2640_i2c_driver是一个I2C驱动的结构体或变量名。它可能定义了有关OV2640摄像头的I2C通信设置和功能的信息。
宏module_i2c_driver可能在内部执行一些操作,以便将ov2640_i2c_driver的I2C驱动注册到系统中。这通常涉及使用相关的函数和数据结构将驱动程序添加到I2C驱动程序列表中,并与I2C总线进行关联。
总的来说,module_i2c_driver(ov2640_i2c_driver)宏的作用是将ov2640_i2c_driver所定义的I2C驱动注册到系统中,以便系统能够正确识别和使用与OV2640摄像头相关的I2C通信功能。
module_i2c_driver原型
#define module_i2c_driver(__i2c_driver) \module_driver(__i2c_driver, i2c_add_driver, \i2c_del_driver)
根据您提供的代码宏定义,module_i2c_driver宏用于简化I2C驱动模块的注册和注销过程,并使用了module_driver宏。
该宏定义的详细分析如下:
module_i2c_driver是宏的名称。
__i2c_driver是一个传入的参数,表示要注册的I2C驱动程序。宏的具体实现包括以下步骤:
使用module_driver宏,传入__i2c_driver作为驱动参数,以及i2c_add_driver和i2c_del_driver作为注册和注销函数。
i2c_add_driver是用于将I2C驱动程序添加到系统中的函数。
i2c_del_driver是用于从系统中注销I2C驱动程序的函数。
通过使用module_i2c_driver宏,可以将注册和注销I2C驱动程序的过程简化为调用module_driver宏,并传入适当的参数。这样可以减少手动编写注册和注销函数的工作量,并提高代码的可读性和可维护性。
总结而言,module_i2c_driver宏的作用是将指定的I2C驱动程序注册到系统中,并在加载模块时调用注册函数,以及在卸载模块时调用注销函数。
module_i2c_driver的原型为module_driver,定义如下
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
module_driver宏用于简化驱动模块的注册和注销过程,并提供了模块的初始化和退出函数。
该宏定义的详细分析如下:
module_driver是宏的名称。
__driver是一个传入的参数,表示要注册的驱动程序。
__register是一个传入的参数,表示用于注册驱动的函数。
__unregister是一个传入的参数,表示用于注销驱动的函数。
…表示可变参数,用于传递额外的参数给注册和注销函数。
宏的具体实现包括以下步骤:
定义一个静态的初始化函数__driver##_init,该函数在模块初始化时被调用。
在__driver##_init函数中,调用__register函数来注册驱动程序,传递驱动程序结构体和额外的参数。
使用module_init宏将__driver##_init函数指定为模块的初始化函数,确保在加载模块时会调用该函数进行初始化。
定义一个静态的退出函数__driver##_exit,该函数在模块注销时被调用。
在__driver##_exit函数中,调用__unregister函数来注销驱动程序,传递驱动程序结构体和额外的参数。
使用module_exit宏将__driver##_exit函数指定为模块的退出函数,确保在卸载模块时会调用该函数进行注销。
通过使用module_driver宏,可以简化驱动模块的注册和注销过程,减少了手动编写初始化和退出函数的工作量,提高了代码的可读性和可维护性。
ov2640_i2c_driver
ov2640的i2c驱动程序
// 定义ov2640的i2c设备ID
static const struct i2c_device_id ov2640_id[] = {{ "ov2640", 0 }, // 设备名为ov2640,ID为0{ } // 结束符
};MODULE_DEVICE_TABLE(i2c, ov2640_id); // 将ov2640_id注册到i2c设备表中,以便内核能够自动加载驱动程序// 定义设备树匹配表
static const struct of_device_id ov2640_of_match[] = {{.compatible = "ovti,ov2640", }, // 匹配ovti,ov2640{}, // 结束符
};MODULE_DEVICE_TABLE(of, ov2640_of_match); // 将ov2640_of_match注册到设备树匹配表中,以便内核能够自动加载驱动程序// 定义ov2640的i2c驱动程序
static struct i2c_driver ov2640_i2c_driver = {.driver = {.name = "ov2640", // 驱动程序名为ov2640.of_match_table = of_match_ptr(ov2640_of_match), // 设置设备树匹配表},.probe = ov2640_probe, // 设置探测函数.remove = ov2640_remove, // 设置反初始化函数.id_table = ov2640_id, // 设置i2c设备ID
};
ov2640_probe
这个函数是用于初始化并探测OV2640摄像头的驱动程序。下面是对该函数的概括总结:
检查所使用的I2C适配器是否支持SMBUS功能,如果不支持则返回错误码。
分配内存并初始化ov2640_priv结构体。
获取摄像头的时钟,如果获取失败则返回延迟探测错误码。
检查是否存在soc_camera_subdev_desc结构体或设备树节点,如果都不存在则返回错误码。
如果不存在soc_camera_subdev_desc结构体,从设备树中获取OV2640的GPIO引脚并进行初始化。
初始化v4l2子设备,使用ov2640_subdev_ops作为操作函数。
初始化v4l2控制器,创建VFLIP和HFLIP控制器。
将控制器绑定到子设备的控制器处理器中。
如果控制器存在错误,则返回错误码。
进行视频探测,初始化摄像头的视频功能。
如果视频探测失败,则释放控制器并释放时钟,并返回错误码。
注册v4l2异步子设备。
如果注册失败,则释放探测的视频,并返回错误码。
打印OV2640已成功探测的消息。
返回0表示探测成功。
如果探测失败,将会在相应的错误标签处释放控制器和时钟,并返回相应的错误码。
static int ov2640_probe(struct i2c_client *client,const struct i2c_device_id *did)
{struct ov2640_priv *priv;struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); // 获取soc_camera_subdev_descstruct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); // 获取i2c_adapterint ret;if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { // 如果i2c_adapter不支持SMBUSdev_err(&adapter->dev,"OV2640: I2C-Adapter doesn't support SMBUS\n"); // 打印错误信息return -EIO; // 返回-EIO}priv = devm_kzalloc(&client->dev, sizeof(struct ov2640_priv), GFP_KERNEL); // 分配内存if (!priv) { // 如果分配失败dev_err(&adapter->dev,"Failed to allocate memory for private data!\n"); // 打印错误信息return -ENOMEM; // 返回-ENOMEM}priv->clk = v4l2_clk_get(&client->dev, "xvclk"); // 获取时钟if (IS_ERR(priv->clk)) // 如果获取失败return -EPROBE_DEFER; // 返回-EPROBE_DEFERif (!ssdd && !client->dev.of_node) { // 如果soc_camera_subdev_desc不存在且设备树节点不存在dev_err(&client->dev, "Missing platform_data for driver\n"); // 打印错误信息ret = -EINVAL; // 返回-EINVALgoto err_clk; // 跳转到err_clk}//if (!ssdd) {ret = ov2640_probe_dt(client, priv); // 从设备树中获取ov2640的GPIO引脚并进行初始化if (ret) // 如果初始化失败goto err_clk; // 跳转到err_clk//}// 初始化v4l2子设备v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);// 初始化v4l2控制器v4l2_ctrl_handler_init(&priv->hdl, 2);// 添加vflip控制器v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,V4L2_CID_VFLIP, 0, 1, 1, 0);// 添加hflip控制器v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,V4L2_CID_HFLIP, 0, 1, 1, 0);// 设置子设备的控制器priv->subdev.ctrl_handler = &priv->hdl;// 如果控制器有错误,则返回错误if (priv->hdl.error) {ret = priv->hdl.error;goto err_clk;}// 进行视频探测ret = ov2640_video_probe(client);// 如果探测失败,则跳转到err_videoprobeif (ret < 0)goto err_videoprobe;// 注册v4l2异步子设备ret = v4l2_async_register_subdev(&priv->subdev);// 如果注册失败,则跳转到err_videoprobeif (ret < 0)goto err_videoprobe;// 打印信息dev_info(&adapter->dev, "OV2640 Probed\n");// 返回0return 0;// 如果探测失败,则释放控制器并释放时钟
err_videoprobe:v4l2_ctrl_handler_free(&priv->hdl);
err_clk:v4l2_clk_put(priv->clk);// 返回错误码return ret;
}
ov2640_remove
这个函数是用于从i2c_client中获取ov2640_priv结构体,并进行反初始化操作。下面是对该函数的概括总结:
从i2c_client中获取ov2640_priv结构体。
取消v4l2异步子设备的注册。
释放时钟资源。
取消v4l2子设备的注册。
释放控制器资源。
返回0表示反初始化成功。
该函数主要用于释放与OV2640摄像头驱动程序相关的资源,包括时钟、控制器和子设备的注册。
// 从i2c_client中获取ov2640_priv,并进行反初始化
static int ov2640_remove(struct i2c_client *client)
{struct ov2640_priv *priv = to_ov2640(client); // 获取ov2640_privv4l2_async_unregister_subdev(&priv->subdev); // 取消v4l2异步子设备的注册v4l2_clk_put(priv->clk); // 释放时钟v4l2_device_unregister_subdev(&priv->subdev); // 取消v4l2子设备的注册v4l2_ctrl_handler_free(&priv->hdl); // 释放控制器return 0; // 返回0
}