当前位置: 首页 > article >正文

IMX335摄像头驱动注册分析

设备树

设备树中对imx335设备摄像头节点的描述信息:

&i2c1 {													#&i2c1: 表示使用 I2C 总线 1,这通常在设备树中用于引用特定的 I2C 控制器
	status = "okay";									#I2C 总线 1 已启用并且工作正常
	clock-frequency = <400000>;							#设置 I2C 总线的频率为 400 kHz,即工作在快速模式。该频率在sensor数据手册指定

	imx335: imx335@1a {									#imx335 的设备节点,@1a 表示 I2C 地址为 0x1a
		   compatible = "sony,imx335";
		   /* i2c client address: 0x34 >> 1 == 0x1a */	# 设备地址由来,右移1位,是最低位为I2C设备的读写位
		   reg = <0x1a>;								# I2C 地址,即 0x1a
		   clocks = <&cru CLK_MIPICSI_OUT>;				#指定了该设备所依赖的时钟源。&cru CLK_MIPICSI_OUT 是设备树中的时钟节点,表示为摄像头提供时钟信号
		   clock-names = "xvclk";						#指定了时钟名称,用于与其他设备或系统组件协调
		   power-domains = <&power RV1126_PD_VI>;		#指定了设备所属的电源域(在此为 RV1126_PD_VI)
		   pinctrl-names = "rockchip,camera_default";	#指定了使用的引脚的默认状态
		   pinctrl-0 = <&mipicsi_clk0>;					#MIPI时钟引脚
		   avdd-supply = <&vcc3v3_sys>;					#电源要求 - 模拟电压3.3v
		   dovdd-supply = <&vcc_1v8>;					#电源要求 - 接口电压1.8v
		   dvdd-supply = <&vcc_dvdd>;					#电源要求 - 数字电压1.2v
		   /* reset is always pulled high in v10 */
		   reset-gpios = <&gpio1 RK_PD5 GPIO_ACTIVE_LOW>;#复位引脚。GPIO引脚是gpio1中的RK_PD5,低电平有效
		   rockchip,camera-module-index = <1>;			#指定摄像头模块的索引号(此处为 1)
		   rockchip,camera-module-facing = "front";		#指定摄像头的朝向(此处为 "front",表示前置摄像头)
		   rockchip,camera-module-name = "YT10092";		#指定摄像头模块的名称(此处为 "YT10092")
		   rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20";#指定摄像头镜头的名称(此处为 "IR0147-50IRC-8M-F20")
		   //ir-cut = <&cam_ircut0>;
		   //flash-leds = <&flash_ir>;
   		   lens-focus = <&hal_dc_motor>;				#指定了用于控制镜头对焦的电机(此处为hal_dc_motor)。这通常与硬件对焦组件(如DC 电机)相关联
		   port {										#定义 MIPI 数据端口的节点
				   ucam_out0: endpoint {				定义了一个名为 ucam_out0 的端点
						   remote-endpoint = <&mipi_in_ucam0>;#指定了这个端点与另一个节点(mipi_in_ucam0)相连接,这是另一个设备或控制器,负责接收摄像头数据
						   data-lanes = <1 2 3 4>;		#定义了 MIPI 数据通道的编号,通常用于设置数据传输的通道
				   };
		   };
	};

imx335摄像头驱动分析

驱动文件路径:linux-4.19.111/drivers/media/i2c

由设备树可知,imx335摄像头设备其实对内核来说,是一个i2c设备,故驱动程序是以一个i2c设备形式进行驱动的注册

#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id imx335_of_match[] = {
	{ .compatible = "sony,imx335" },
	{},
};
MODULE_DEVICE_TABLE(of, imx335_of_match);
#endif

static const struct i2c_device_id imx335_match_id[] = {
	{ "sony,imx335", 0 },
	{ },
};

static struct i2c_driver imx335_i2c_driver = {
	.driver = {
		.name = IMX335_NAME,
		.pm = &imx335_pm_ops,
		.of_match_table = of_match_ptr(imx335_of_match),
	},
	.probe		= &imx335_probe,
	.remove		= &imx335_remove,
	.id_table	= imx335_match_id,
};

static int __init sensor_mod_init(void)
{
	return i2c_add_driver(&imx335_i2c_driver);
}

static void __exit sensor_mod_exit(void)
{
	i2c_del_driver(&imx335_i2c_driver);
}

device_initcall_sync(sensor_mod_init);
module_exit(sensor_mod_exit);

MODULE_DESCRIPTION("Sony imx335 sensor driver");
MODULE_LICENSE("GPL v2");

在驱动中,中来描述一个imx335设备

struct imx335 {
    struct i2c_client *client;                          // I2C 客户端
    struct clk *xvclk;                                  // 外部时钟(XCLK)
    struct gpio_desc *reset_gpio;                       // 重置 GPIO 引脚
    struct regulator_bulk_data supplies[IMX335_NUM_SUPPLIES]; // 电源管理,supplies 数组用来管理多个电源输入
    
    struct pinctrl *pinctrl;                            // 引脚控制
    struct pinctrl_state *pins_default;                 // 默认引脚配置
    struct pinctrl_state *pins_sleep;                   // 休眠状态下的引脚配置
    
    struct v4l2_subdev subdev;                          // V4L2 子设备,用于将 IMX335 摄像头与 V4L2 子设备框架集成。subdev 用于在 V4L2 系统中注册和管理设备
    struct media_pad pad;                               // 媒体接口中的连接点,用于描述视频输入或输出的连接点,通常与摄像头的输入或输出数据流相关
    struct v4l2_ctrl_handler ctrl_handler;              // 控制器处理,用于管理与摄像头相关的控制参数(如曝光、增益等)
    struct v4l2_ctrl *exposure;                         // 曝光控制器
    struct v4l2_ctrl *anal_a_gain;                      // 模拟增益控制器,用于调节模拟信号的增益,即提高或降低图像信号的强度
    struct v4l2_ctrl *digi_gain;                        // 数字增益控制器,用于调整数字信号的增益。
    struct v4l2_ctrl *hblank;                           // 图像传感器水平空白控制器
    struct v4l2_ctrl *vblank;                           // 图像传感器垂直空白控制器
    struct v4l2_ctrl *pixel_rate;                       // 像素率控制器。用于设置传感器的像素传输速率
    struct v4l2_ctrl *link_freq;                        // 链路频率控制器,用于控制摄像头与主机之间的数据传输频率
    struct mutex mutex;                                 // 互斥锁,保证线程安全
    bool streaming;                                     // 是否正在传输视频流
    bool power_on;                                      // 电源状态
    const struct imx335_mode *cur_mode;                 // 当前模式配置
    u32 module_index;                                   // 摄像头模块索引
    u32 cfg_num;                                        // 配置编号
    const char *module_facing;                          // 摄像头模块朝向
    const char *module_name;                            // 摄像头模块名称
    const char *len_name;                               // 镜头名称,标识连接到摄像头模块的镜头名称
    u32 cur_vts;                                        // 当前垂直同步时间,通常与图像传感器的垂直扫描时间有关
    bool has_init_exp;                                  // 标志位,表示是否已初始化曝光值。
    struct preisp_hdrae_exp_s init_hdrae_exp;           // 曝光配置。用于存储在初始化时的自动曝光参数
};

设备与驱动匹配成功后,执行probe函数: 

static int imx335_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct device *dev = &client->dev;
	struct device_node *node = dev->of_node;
	struct imx335 *imx335;
	struct v4l2_subdev *sd;
	char facing[2];
	int ret;
	u32 i, hdr_mode = 0;

	dev_info(dev, "driver version: %02x.%02x.%02x",
		DRIVER_VERSION >> 16,
		(DRIVER_VERSION & 0xff00) >> 8,
		DRIVER_VERSION & 0x00ff);

	//动态申请内容,保存imx335结构体参数
	imx335 = devm_kzalloc(dev, sizeof(*imx335), GFP_KERNEL);
	if (!imx335)
		return -ENOMEM;
	
	//设备树获取模块的索引号、摄像头的方向、摄像头模块的名称、摄像头镜头的名称
	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
				   &imx335->module_index);
	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
				       &imx335->module_facing);
	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
				       &imx335->module_name);
	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
				       &imx335->len_name);
	if (ret) {
		dev_err(dev, "could not get module information!\n");
		return -EINVAL;
	}

	ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode);	//从设备树中获取hdr模式
	if (ret) {
		hdr_mode = NO_HDR;
		dev_warn(dev, " Get hdr mode failed! no hdr default\n");
	}
	imx335->client = client;
	imx335->cfg_num = ARRAY_SIZE(supported_modes);		//获取支持模式的成员数
	for (i = 0; i < imx335->cfg_num; i++) {
		if (hdr_mode == supported_modes[i].hdr_mode) {	//匹配设备树中的模式,并设置hdr下的参数
			imx335->cur_mode = &supported_modes[i];
			break;
		}
	}

	imx335->xvclk = devm_clk_get(dev, "xvclk");		//从设备树中获取imx335的时钟值
	if (IS_ERR(imx335->xvclk)) {
		dev_err(dev, "Failed to get xvclk\n");
		return -EINVAL;
	}

	imx335->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);		//设备树中获取复位引脚值
	if (IS_ERR(imx335->reset_gpio))
		dev_warn(dev, "Failed to get reset-gpios\n");

	imx335->pinctrl = devm_pinctrl_get(dev);		//获取设备树中的引脚状态
	if (!IS_ERR(imx335->pinctrl)) {
		imx335->pins_default =
			pinctrl_lookup_state(imx335->pinctrl,
					     OF_CAMERA_PINCTRL_STATE_DEFAULT);	//绑定pinctrl默认状态
		if (IS_ERR(imx335->pins_default))
			dev_info(dev, "could not get default pinstate\n");

		imx335->pins_sleep =
			pinctrl_lookup_state(imx335->pinctrl,
					     OF_CAMERA_PINCTRL_STATE_SLEEP);	//绑定pinctrl睡眠状态
		if (IS_ERR(imx335->pins_sleep))
			dev_info(dev, "could not get sleep pinstate\n");
	} else {
		dev_info(dev, "no pinctrl\n");
	}

	ret = imx335_configure_regulators(imx335);		//配置电源管理
	if (ret) {
		dev_err(dev, "Failed to get power regulators\n");
		return ret;
	}

	mutex_init(&imx335->mutex);		//初始化互斥锁

	sd = &imx335->subdev;		//imx335子设备,这里指的sensor
	v4l2_i2c_subdev_init(sd, client, &imx335_subdev_ops);	//初始化v4l2子设备
	ret = imx335_initialize_controls(imx335);	//初始化imx335->subdev下的control 处理
	if (ret)
		goto err_destroy_mutex;

	ret = __imx335_power_on(imx335);	//设置上电前的准备,配置引脚状态、时钟频率
	if (ret)
		goto err_free_handler;

	ret = imx335_check_sensor_id(imx335, client);	//检查sersor id
	if (ret)
		goto err_power_off;

	sd->internal_ops = &imx335_internal_ops;	//subdev 内部操作函数
	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
		     V4L2_SUBDEV_FL_HAS_EVENTS;

#if defined(CONFIG_MEDIA_CONTROLLER)
	imx335->pad.flags = MEDIA_PAD_FL_SOURCE;
	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
	ret = media_entity_pads_init(&sd->entity, 1, &imx335->pad);	//建立 subdev->entity和imx335->pad联系
	if (ret < 0)
		goto err_power_off;
#endif

	memset(facing, 0, sizeof(facing));
	if (strcmp(imx335->module_facing, "back") == 0)	//前置还是后置摄像头
		facing[0] = 'b';
	else
		facing[0] = 'f';

	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
		 imx335->module_index, facing,
		 IMX335_NAME, dev_name(sd->dev));				//subdev 名字
	ret = v4l2_async_register_subdev_sensor_common(sd);	//注册v4l2 subdev
	if (ret) {
		dev_err(dev, "v4l2 async register subdev failed\n");
		goto err_clean_entity;
	}

	pm_runtime_set_active(dev);		//设置电源管理的状态
	pm_runtime_enable(dev);
	pm_runtime_idle(dev);

	return 0;

err_clean_entity:
#if defined(CONFIG_MEDIA_CONTROLLER)
	media_entity_cleanup(&sd->entity);
#endif
err_power_off:
	__imx335_power_off(imx335);
err_free_handler:
	v4l2_ctrl_handler_free(&imx335->ctrl_handler);
err_destroy_mutex:
	mutex_destroy(&imx335->mutex);

	return ret;
}


http://www.kler.cn/a/592783.html

相关文章:

  • AI学习——卷积神经网络(CNN)入门
  • uniapp笔记-底部和首部标签页菜单生成
  • Java基础编程练习第34题-正则表达式
  • 【源码阅读】多个函数抽象为类(实现各种类型文件转为PDF)
  • docker(1) -- centos镜像
  • FOC——Butterworth (巴特沃斯)数字滤波器(2025.03.18)
  • 【sklearn 04】DNN、CNN、RNN
  • 【工具类】Java的 LocalDate 获取本月第一天和最后一天
  • 鸿蒙NEXT项目实战-百得知识库04
  • 网络爬虫【爬虫库request】
  • Rust学习之实现命令行小工具minigrep(一)
  • 关于HAL库的知识1----MSP函数
  • [解决] PDF转图片,中文乱码或显示方框的解决方案
  • 华为ipd流程华为流程体系管理华为数字化转型流程数字化管理解决方案介绍81页精品PPT
  • gralloc usage flags
  • dns实现主服务器
  • 如何解析返回的商品信息?
  • 深度解析扣减系统设计:从架构到实践
  • HAL库编程知识点---Can.c和Driver_can.c分层开发
  • 【论文阅读】Availability Attacks Create Shortcuts