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_desc
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); // 获取i2c_adapter
int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { // 如果i2c_adapter不支持SMBUS
dev_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_DEFER
if (!ssdd && !client->dev.of_node) { // 如果soc_camera_subdev_desc不存在且设备树节点不存在
dev_err(&client->dev, "Missing platform_data for driver\n"); // 打印错误信息
ret = -EINVAL; // 返回-EINVAL
goto 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_videoprobe
if (ret < 0)
goto err_videoprobe;
// 注册v4l2异步子设备
ret = v4l2_async_register_subdev(&priv->subdev);
// 如果注册失败,则跳转到err_videoprobe
if (ret < 0)
goto err_videoprobe;
// 打印信息
dev_info(&adapter->dev, "OV2640 Probed\n");
// 返回0
return 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_priv
v4l2_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
}