我是靠谱客的博主 大方水池,这篇文章主要介绍RK3399教程: cameraRK3399 camera,现在分享给大家,希望可以做个参考。

公众号

欢迎扫码关注本人微信公众号:公众号上分享更多嵌入式知识和资料,分享个人学习嵌入式的心得体会。欢迎大家一起来玩呀。
在这里插入图片描述

RK3399 camera

名词解释

在现代移动设备中,常用一种接口用来连接SOC和LCD和Camera,这种接口就是MIPI。
其中SOC和LCD连接叫 DSI(DisplayCommandSet),SOC和Camera连接叫CSI(DisplaySerialInterface)。

硬件连接

在这里插入图片描述

一般情况下,Camera和SOC有两个接口进行连接,分为为MIPI接口和I2C接口,其中MIPI接口用来传输图像的数据,数据传输路径为从Sensor传输到SOC。另一个接口为I2C接口,主要是用来SOC对Sensor初始化配置寄存器和摄像头参数的配置,比如要进行图像数据捕获的时候就需要通过i2c对Sensor的寄存器进行配置。

图像数据路径

在这里插入图片描述

在这里插入图片描述

由上面的两个图可以看到,光线经过Sensor之后,Sensor芯片经过ADC转换生成图像数据,然后Sensor生成的图像数据经过MIPI总线进入SOC,进入SOC之后经过ISP进行图像处理。所以由此可以可以看出,Camera驱动V4L2一定有这3部分组成,第一部分与Sensor相关的,比如控制Sensor的寄存器进行配置,这一部分是有Sensor厂家提供。第二部分和MIPI相关的,需要MIPI进行图像传输,所以驱动应该就有这一部分的驱动,这部分一般是由SOC厂家提供。第三部分就是ISP部分,有些SOC有ISP图像处理模块,经过MIPI传输的图像进入SOC之后需要在传入SOC的ISP模块对图像进一步进行加工,所以一定是有一部分驱动是描述ISP模块的。

代码路径

和硬件相关的驱动有3部分,分别为Sensor相关的,MIPI相关,ISP相关的。
Sensor: kerneldriversmediai2cov13850.c
MIPI相关:kerneldriversphyrockchipphy-rockchip-mipi-rx.c
ISP相关:kerneldriversmediaplatformrockchipisp1rkisp1.c

dts

我们已经知道Camera有3部分的驱动,分别是描述Sensor、MIPI相关、ISP相关的,所以在dts中也有描述这3部分的。
下面是和摄像头相关的dts

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//这部分是根Senser相关的 &i2c1 { status = "okay"; ov13850: ov13850@10 { compatible = "ovti,ov13850"; status = "disabled"; reg = <0x10>; //i2c地址 clocks = <&cru SCLK_CIF_OUT>; clock-names = "xvclk"; /* avdd-supply = <>; */ /* dvdd-supply = <>; */ /* dovdd-supply = <>; */ /* reset-gpios = <>; */ reset-gpios = <&gpio2 10 GPIO_ACTIVE_HIGH>; //复位脚配置 pwdn-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; //power down脚配置 pinctrl-names = "rockchip,camera_default"; pinctrl-0 = <&cif_clkout>; rockchip,camera-module-index = <0>; //其他的配置功能可以查看rochchip文档 rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "CMK-CT0116"; rockchip,camera-module-lens-name = "Largan-50013A1"; lens-focus = <&vm149c>; port { ucam_out0: endpoint { remote-endpoint = <&mipi_in_ucam0>; //Sensor连接到mipi //remote-endpoint = <&mipi_in_ucam1>; data-lanes = <1 2>; }; }; }; }; //这部分是和mipi相关 &mipi_dphy_rx0 { status = "disabled"; ports { #address-cells = <1>; #size-cells = <0>; //mipi有两端一段连接Sensor,另一端连接ISP port@0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; //连接到Sensor mipi_in_ucam0: endpoint@1 { reg = <1>; remote-endpoint = <&ucam_out0>; data-lanes = <1 2>; }; }; port@1 { reg = <1>; #address-cells = <1>; #size-cells = <0>; //连接到ISP dphy_rx0_out: endpoint@0 { reg = <0>; remote-endpoint = <&isp0_mipi_in>; }; }; }; }; //这部分是和ISP相关的 &rkisp1_0 { status = "disabled"; port { #address-cells = <1>; #size-cells = <0>; //连接到MIPI isp0_mipi_in: endpoint@0 { reg = <0>; remote-endpoint = <&dphy_rx0_out>; }; }; };

从dts我们可以看出Sensor和mipi和isp的连接关系,其中Sensor连接到mipi,然后mipi连接到isp。
ucam_out0 ->mipi_in_ucam0->dphy_rx0_out->isp0_mipi_in。

查看Sensor驱动ov13850.c

根据dts可以知道Sensor是挂载在i2c下的,根据dts的使用方法在ov13850.c一定会有一个struct i2c_driver,任何根据设备树的匹配规则,compatible = “ovti,ov13850”;和.of_match_table = of_match_ptr(ov13850_of_match), 进行匹配,然后i2c_driver里面的probe函数被调用,然后进入这个函数ov13850_probe,我们主要是查看ov13850_probe这个函数做了什么。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
static struct i2c_driver ov13850_i2c_driver = { .driver = { .name = OV13850_NAME, .pm = &ov13850_pm_ops, .of_match_table = of_match_ptr(ov13850_of_match), }, .probe = &ov13850_probe, //函数重要函数入口 .remove = &ov13850_remove, .id_table = ov13850_match_id, }; //完整代码,完整代码主要是解析dts里面的数据,获取reset脚等和硬件相关的配置,可以不用看,硬件操作我们不关心,因为不同的Sensor会有不同的硬件操作。 static int ov13850_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct device_node *node = dev->of_node; struct ov13850 *ov13850; struct v4l2_subdev *sd; char facing[2]; int ret; dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16, (DRIVER_VERSION & 0xff00) >> 8, DRIVER_VERSION & 0x00ff); ov13850 = devm_kzalloc(dev, sizeof(*ov13850), GFP_KERNEL); if (!ov13850) return -ENOMEM; ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, &ov13850->module_index); ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, &ov13850->module_facing); ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, &ov13850->module_name); ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, &ov13850->len_name); if (ret) { dev_err(dev, "could not get module information!n"); return -EINVAL; } ov13850->client = client; ov13850->cur_mode = &supported_modes[0]; ov13850->xvclk = devm_clk_get(dev, "xvclk"); if (IS_ERR(ov13850->xvclk)) { dev_err(dev, "Failed to get xvclkn"); return -EINVAL; } ret = clk_set_rate(ov13850->xvclk, OV13850_XVCLK_FREQ); if (ret < 0) { dev_err(dev, "Failed to set xvclk rate (24MHz)n"); return ret; } if (clk_get_rate(ov13850->xvclk) != OV13850_XVCLK_FREQ) dev_warn(dev, "xvclk mismatched, modes are based on 24MHzn"); ov13850->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov13850->reset_gpio)) dev_warn(dev, "Failed to get reset-gpiosn"); ov13850->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); if (IS_ERR(ov13850->pwdn_gpio)) dev_warn(dev, "Failed to get pwdn-gpiosn"); ret = ov13850_configure_regulators(ov13850); if (ret) { dev_err(dev, "Failed to get power regulatorsn"); return ret; } ov13850->pinctrl = devm_pinctrl_get(dev); if (!IS_ERR(ov13850->pinctrl)) { ov13850->pins_default = pinctrl_lookup_state(ov13850->pinctrl, OF_CAMERA_PINCTRL_STATE_DEFAULT); if (IS_ERR(ov13850->pins_default)) dev_err(dev, "could not get default pinstaten"); ov13850->pins_sleep = pinctrl_lookup_state(ov13850->pinctrl, OF_CAMERA_PINCTRL_STATE_SLEEP); if (IS_ERR(ov13850->pins_sleep)) dev_err(dev, "could not get sleep pinstaten"); } mutex_init(&ov13850->mutex); sd = &ov13850->subdev; v4l2_i2c_subdev_init(sd, client, &ov13850_subdev_ops); ret = ov13850_initialize_controls(ov13850); if (ret) goto err_destroy_mutex; ret = __ov13850_power_on(ov13850); if (ret) goto err_free_handler; ret = ov13850_check_sensor_id(ov13850, client); if (ret) goto err_power_off; #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov13850_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; #endif #if defined(CONFIG_MEDIA_CONTROLLER) ov13850->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ret = media_entity_init(&sd->entity, 1, &ov13850->pad, 0); if (ret < 0) goto err_power_off; #endif memset(facing, 0, sizeof(facing)); if (strcmp(ov13850->module_facing, "back") == 0) facing[0] = 'b'; else facing[0] = 'f'; snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", ov13850->module_index, facing, OV13850_NAME, dev_name(sd->dev)); ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { dev_err(dev, "v4l2 async register subdev failedn"); 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: __ov13850_power_off(ov13850); err_free_handler: v4l2_ctrl_handler_free(&ov13850->ctrl_handler); err_destroy_mutex: mutex_destroy(&ov13850->mutex); return ret; } //上面的函数精简版 static int ov13850_probe(struct i2c_client *client, const struct i2c_device_id *id) { //这个prob函数最重要的功能就是下面这两个函数。 //v4l2框架将Sensor统一描述为 struct v4l2_subdev对象,这个对象里面有硬件相关的操作函数,这函数描述的对象为struct v4l2_subdev_ops,由下面的函数可以知道我们将Sensor当成一个对象sd,ov13850_subdev_ops是Sensor操作硬件的函数。struct v4l2_subdev_ops可以指向struct v4l2_subdev_ops,所以应用就可以通过ioctrl找到Sensor这个实体,然后找到硬件操作函数,从而控制寄存器的配置。 v4l2_i2c_subdev_init(sd, client, &ov13850_subdev_ops); //最重要的函数,将Sensor对象sd,添加到框架的链表当中 ret = v4l2_async_register_subdev_sensor_common(sd); } //和Sensor相关的操作函数,最后是通过ioctrl一层一层的调用到这里, 这里面的操作函数对于不同的Sensor不一定全部相同,有些Sensor的功能多一点可能操作函数就多一点。 static const struct v4l2_subdev_ops ov13850_subdev_ops = { .core = &ov13850_core_ops, //对Sensor控制的核心操作函数 .video = &ov13850_video_ops, //录像的时候控制的操作函数 .pad = &ov13850_pad_ops, }; static const struct v4l2_subdev_core_ops ov13850_core_ops = { .s_power = ov13850_s_power, .ioctl = ov13850_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl32 = ov13850_compat_ioctl32, #endif }; static const struct v4l2_subdev_video_ops ov13850_video_ops = { .s_stream = ov13850_s_stream, .g_frame_interval = ov13850_g_frame_interval, }; static const struct v4l2_subdev_pad_ops ov13850_pad_ops = { .enum_mbus_code = ov13850_enum_mbus_code, .enum_frame_size = ov13850_enum_frame_sizes, .get_fmt = ov13850_get_fmt, .set_fmt = ov13850_set_fmt, };

查看MIPI相关驱动phy-rockchip-mipi-rx.c

MIPI相关的驱动主要是用来配置MIPI接口的寄存器,然后在使能接收MIPI接口等功能。其入口其实是和Sensor差不多的,只不过是使用struct platform_driver而已与struct i2c_driver类似,最后还是调用probe函数。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//MIPI相关驱动的入口函数rockchip_mipidphy_probe static struct platform_driver rockchip_isp_mipidphy_driver = { .probe = rockchip_mipidphy_probe, .remove = rockchip_mipidphy_remove, .driver = { .name = "rockchip-mipi-dphy-rx", .pm = &rockchip_mipidphy_pm_ops, .of_match_table = rockchip_mipidphy_match_id, }, }; //全部代码,下面会精简代码 static int rockchip_mipidphy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct v4l2_subdev *sd; struct mipidphy_priv *priv; struct regmap *grf; struct resource *res; const struct of_device_id *of_id; const struct dphy_drv_data *drv_data; int i, ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = dev; of_id = of_match_device(rockchip_mipidphy_match_id, dev); if (!of_id) return -EINVAL; grf = syscon_node_to_regmap(dev->parent->of_node); if (IS_ERR(grf)) { grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); if (IS_ERR(grf)) { dev_err(dev, "Can't find GRF sysconn"); return -ENODEV; } } priv->regmap_grf = grf; drv_data = of_id->data; for (i = 0; i < drv_data->num_clks; i++) { priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]); if (IS_ERR(priv->clks[i])) dev_dbg(dev, "Failed to get %sn", drv_data->clks[i]); } priv->grf_regs = drv_data->grf_regs; priv->txrx_regs = drv_data->txrx_regs; priv->csiphy_regs = drv_data->csiphy_regs; priv->drv_data = drv_data; if (drv_data->ctl_type == MIPI_DPHY_CTL_CSI_HOST) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->csihost_base_addr = devm_ioremap_resource(dev, res); priv->stream_on = csi_mipidphy_stream_on; priv->stream_off = csi_mipidphy_stream_off; } else { priv->stream_on = mipidphy_txrx_stream_on; priv->txrx_base_addr = NULL; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->txrx_base_addr = devm_ioremap_resource(dev, res); if (IS_ERR(priv->txrx_base_addr)) priv->stream_on = mipidphy_rx_stream_on; priv->stream_off = NULL; } sd = &priv->sd; v4l2_subdev_init(sd, &mipidphy_subdev_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "rockchip-mipi-dphy-rx"); sd->dev = dev; platform_set_drvdata(pdev, &sd->entity); ret = rockchip_mipidphy_media_init(priv); if (ret < 0) return ret; pm_runtime_enable(&pdev->dev); drv_data->individual_init(priv); return 0; } static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv) { int ret; priv->pads[MIPI_DPHY_RX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; priv->pads[MIPI_DPHY_RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; ret = media_entity_init(&priv->sd.entity, MIPI_DPHY_RX_PADS_NUM, priv->pads, 0); if (ret < 0) return ret; ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( priv->dev, &priv->notifier, sizeof(struct sensor_async_subdev), 0, rockchip_mipidphy_fwnode_parse); if (ret < 0) return ret; if (!priv->notifier.num_subdevs) return -ENODEV; /* no endpoint */ priv->sd.subdev_notifier = &priv->notifier; priv->notifier.ops = &rockchip_mipidphy_async_ops; ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier); if (ret) { dev_err(priv->dev, "failed to register async notifier : %dn", ret); v4l2_async_notifier_cleanup(&priv->notifier); return ret; } return v4l2_async_register_subdev(&priv->sd); } static int rockchip_mipidphy_probe(struct platform_device *pdev) { //分配一个struct v4l2_subdev *sd; //将mipidphy_subdev_ops放置到struct v4l2_subdev *sd //大家看到这里应该就明白了,不管是Sensor还是MIPI相关的还是ISP相关的,在V4L2的架构中,都会描述为一个对象struct v4l2_subdev,这个对象里面有对硬件操作的函数struct v4l2_subdev_ops,然后在将这个对象struct v4l2_subde注册到v4l2_async_register_subdev,从而注册到V4L2的框架的链表当中。然后将Sensor和MIPI和ISP绑定起来,形成一个完整的Camera驱动。 v4l2_subdev_init(sd, &mipidphy_subdev_ops); //将port绑定起来,还记得我们dts上面的port么?就是将Sensor和MIPI和ISP绑定起来。 ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port( priv->dev, &priv->notifier, sizeof(struct sensor_async_subdev), 0, rockchip_mipidphy_fwnode_parse); //将对象struct v4l2_subdev注册到V4L2框架中。 v4l2_async_register_subdev(&priv->sd); }

查看ISP相关驱动rkisp1.c

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//入口ISP函数rkisp1_plat_probe static struct platform_driver rkisp1_plat_drv = { .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(rkisp1_plat_of_match), .pm = &rkisp1_plat_pm_ops, }, .probe = rkisp1_plat_probe, .remove = rkisp1_plat_remove, }; //下面是isp代码从入口到注册到v4l2_device_register_subdev流程,原理和上面的Sensor类似,都是分配struct v4l2_subdev对象,然后注册到V4L2的架构当中。 rkisp1_plat_probe ret = rkisp1_register_platform_subdevs(isp_dev); ret = rkisp1_register_isp_subdev(dev, &dev->v4l2_dev); v4l2_subdev_init(sd, &rkisp1_isp_sd_ops); ret = v4l2_device_register_subdev(v4l2_dev, sd);

总结

通过上面可以知道不管是Sensor还是MIPI还是ISP,统一描述为struct v4l2_subdev对象,然后将这个对象struct v4l2_subdev通过不同函数注册到V4L2框架中。比如Sensor使用v4l2_async_register_subdev_sensor_common(),MIPI使用v4l2_async_register_subdev(&priv->sd);,ISP使用v4l2_device_register_subdev(v4l2_dev, sd);,其实功能都是一样的。

在这里插入图片描述

应用层ioctl如何调用到驱动

我们可以在linux console终端下面 ls /dev/video* 可以查看到我们的Camera设备,由此可见我们的Camera也是一个字符型设备,只是为了摄像头的复杂性,框架做的比较复杂而已,既然是字符型设备,肯定就少不了我们很熟悉的struct file_operations这个结构体里面的ioctl。

一般来说,摄像头驱动需要实现与向核心层提交下面十几个ioctl接口
VIDIOC_REQBUFS:分配内存
VIDIOC_QUERYCAP:查询驱动功能
VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
VIDIOC_S_FMT:设置当前驱动的频捕获格式
VIDIOC_G_FMT:读取当前驱动的频捕获格式
VIDIOC_TRY_FMT:验证当前驱动的显示格式
VIDIOC_CROPCAP:查询驱动的修剪能力
VIDIOC_S_CROP:设置视频信号的边框
VIDIOC_G_CROP:读取视频信号的边框
VIDIOC_QBUF:把数据放回缓存队列
VIDIOC_DQBUF:把数据从缓存中读取出来
VIDIOC_STREAMON:开始图像捕获
VIDIOC_STREAMOFF:结束图像捕获

我们以VIDIOC_STREAMON这个ioctl这个命令开始,一步一步探索应用成是如何使用这个命令,然后一步一步从应用到kernel层一步一步调用下来的。
我们在代码中搜索VIDIOC_STREAMON这个宏定义,可以查看到在kerneldriversmediav4l2-corev4l2-ioctl.c里面有如下的定于。

在这里插入图片描述

所以我们可以知道,应用层使用ioctl使用VIDIOC_STREAMON这个命令的时候,会调用到kernel里面的v4l_streamon这个函数,下面我们就跟踪这个函数看看如何是一步,一步调用到MIPI驱动里面的函数,设置MIPI控制器的寄存器的。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
v4l_streamon ops->vidioc_streamon(file, fh, *(unsigned int *)arg); //代码中查找vidioc_streamon,看看最后调用那个函数,最后查找到是调用,kerneldriversmediaplatformsoc_camerasoc_camera.c 中的soc_camera_streamon soc_camera_streamon v4l2_subdev_call(sd, video, s_stream, 1); ici->ops->s_stream(icd, 1); //到这里搜索s_stream,最后发现是有可能是调用mipidphy_s_stream,代码在phy-rockchip-mipi-rx.c,还记得phy-rockchip-mipi-rx.c么,就是我们说的Camera驱动有三部分,分别是Sensor相关的,MIPI相关的,ISP相关的,其他的部分MIPI相关的,ISP相关的,也是类似,下面都是以MIPI相关举例。 static const struct v4l2_subdev_video_ops mipidphy_video_ops = { .g_mbus_config = mipidphy_g_mbus_config, .s_stream = mipidphy_s_stream, }; //下面继续追踪mipidphy_s_stream这个函数 mipidphy_s_stream mipidphy_s_stream_start mipidphy_get_sensor_data_rate mipidphy_update_sensor_mbus priv->stream_on(priv, sd); csi_mipidphy_stream_on;//最终调用到这里,这个函数就是设置mipi的寄存器,初始化MIPI,开始接收图像 // in code kerneldriversphyrockchipphy-rockchip-mipi-rx.c static int csi_mipidphy_stream_on(struct mipidphy_priv *priv, struct v4l2_subdev *sd) { struct v4l2_subdev *sensor_sd = get_remote_sensor(sd); struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd); const struct dphy_drv_data *drv_data = priv->drv_data; const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges; int num_hsfreq_ranges = drv_data->num_hsfreq_ranges; int i, hsfreq = 0; write_grf_reg(priv, GRF_DVP_V18SEL, 0x1); /* phy start */ write_csiphy_reg(priv, CSIPHY_CTRL_PWRCTL, 0xe4); /* set data lane num and enable clock lane */ write_csiphy_reg(priv, CSIPHY_CTRL_LANE_ENABLE, ((GENMASK(sensor->lanes - 1, 0) << MIPI_CSI_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT) | (0x1 << MIPI_CSI_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT) | 0x1)); /* Reset dphy analog part */ write_csiphy_reg(priv, CSIPHY_CTRL_PWRCTL, 0xe0); usleep_range(500, 1000); /* Reset dphy digital part */ write_csiphy_reg(priv, CSIPHY_CTRL_DIG_RST, 0x1e); write_csiphy_reg(priv, CSIPHY_CTRL_DIG_RST, 0x1f); /* not into receive mode/wait stopstate */ write_grf_reg(priv, GRF_DPHY_CSIPHY_FORCERXMODE, 0x0); /* enable calibration */ if (priv->data_rate_mbps > 1500) { write_csiphy_reg(priv, CSIPHY_CLK_CALIB_ENABLE, 0x80); if (sensor->lanes > 0x00) write_csiphy_reg(priv, CSIPHY_LANE0_CALIB_ENABLE, 0x80); if (sensor->lanes > 0x01) write_csiphy_reg(priv, CSIPHY_LANE1_CALIB_ENABLE, 0x80); if (sensor->lanes > 0x02) write_csiphy_reg(priv, CSIPHY_LANE2_CALIB_ENABLE, 0x80); if (sensor->lanes > 0x03) write_csiphy_reg(priv, CSIPHY_LANE3_CALIB_ENABLE, 0x80); } /* set clock lane and data lane */ for (i = 0; i < num_hsfreq_ranges; i++) { if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) { hsfreq = hsfreq_ranges[i].cfg_bit; break; } } if (i == num_hsfreq_ranges) { i = num_hsfreq_ranges - 1; dev_warn(priv->dev, "data rate: %lld mbps, max support %d mbps", priv->data_rate_mbps, hsfreq_ranges[i].range_h + 1); hsfreq = hsfreq_ranges[i].cfg_bit; } csi_mipidphy_wr_ths_settle(priv, hsfreq, MIPI_DPHY_LANE_CLOCK); if (sensor->lanes > 0x00) csi_mipidphy_wr_ths_settle(priv, hsfreq, MIPI_DPHY_LANE_DATA0); if (sensor->lanes > 0x01) csi_mipidphy_wr_ths_settle(priv, hsfreq, MIPI_DPHY_LANE_DATA1); if (sensor->lanes > 0x02) csi_mipidphy_wr_ths_settle(priv, hsfreq, MIPI_DPHY_LANE_DATA2); if (sensor->lanes > 0x03) csi_mipidphy_wr_ths_settle(priv, hsfreq, MIPI_DPHY_LANE_DATA3); write_grf_reg(priv, GRF_DPHY_CSIPHY_CLKLANE_EN, 0x1); write_grf_reg(priv, GRF_DPHY_CSIPHY_DATALANE_EN, GENMASK(sensor->lanes - 1, 0)); return 0; }

全部总结

由上面分析我们可以知道在Camera的框架中在不同板子或者说不同平台,有3部分是需要实现的,第一部分Sensor相关即不同板子摄像头选型肯定是不一样的,这部分的驱动就是要实现Sensor的初始化,寄存器的配置等,比如启动图像捕获的时候,需要配置寄存器,从应用层会使用一个ioctl一步一步的调用下来到驱动中,所以这部分是Sensor相关的,需要Sensor提供相应的驱动。第二部分MIPI相关,比如我们常用的SOC和Sensor连接就是使用MIPI接口,所以就需要实现MIPI相关部分的驱动,这部分一般来说是由SOC厂商进行提供。第三部分ISP相关部分,如果我们的SOC平台如果有ISP模块那么就有ISP模块部分的驱动代码,这部分也是SOC厂商提供的。从dts的配置中我们可以看到,驱动的绑定路径为,从Sensor连接到MIPI然后MIPI连接到ISP,所以说Sensor捕获到的数据通过MIPI传入ISP,然后通过ISP处理后传动应用层进行处理,整个Camera的驱动框架大概就是这个流程。

最后

以上就是大方水池最近收集整理的关于RK3399教程: cameraRK3399 camera的全部内容,更多相关RK3399教程:内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(90)

评论列表共有 0 条评论

立即
投稿
返回
顶部