赞
踩
本文主要是基于rk3566/rk3568平台通过LCD uboot 和 设备树 的代码 对 vop - encoder/connector - panel 三者如何建立联系的进行分析。
一、设备树部分
设备树主要是借助remote-endpoint属性来达到选择相应的外设配置。
1、display-subsystem的设备树信息
1)以ports属性指定相应的vop端点
2)以route_dsi0 指定uboot 阶段的logo配置,其中的connect属性指定的vop需与ports的一致。
- display_subsystem: display-subsystem {
- compatible = "rockchip,display-subsystem";
- memory-region = <&drm_logo>, <&drm_cubic_lut>;
- memory-region-names = "drm-logo", "drm-cubic-lut";
- ports = <&vop_out>;
- devfreq = <&dmc>;
-
- route {
- route_dsi0: route-dsi0 {
- status = "okay";
- logo,uboot = "logo.bmp";
- logo,kernel = "logo_kernel.bmp";
- logo,mode = "center";
- charge_logo,mode = "center";
- connect = <&vp0_out_dsi0>;
- };
- route_dsi1: route-dsi1 {
- status = "disabled";
- logo,uboot = "logo.bmp";
-
- route {
- route_dsi0: route-dsi0 {
- status = "okay";
- logo,uboot = "logo.bmp";
- logo,kernel = "logo_kernel.bmp";
- logo,mode = "center";
- charge_logo,mode = "center";
- connect = <&vp0_out_dsi0>;
- };
-
- };
- };
2、vop的设备树信息
1)以ports -> port -> endpoint -> remote-endpoint 的顺序,通过remote-endpoint 属性来指定远端的设备端点
-
- vop: vop@fe040000 {
- compatible = "rockchip,rk3568-vop";
- reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
- reg-names = "regs", "gamma_lut";
- rockchip,grf = <&grf>;
- interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>, <&cru DCLK_VOP0>, <&cru DCLK_VOP1>, <&cru DCLK_VOP2>;
- clock-names = "aclk_vop", "hclk_vop", "dclk_vp0", "dclk_vp1", "dclk_vp2";
- iommus = <&vop_mmu>;
- power-domains = <&power RK3568_PD_VO>;
- status = "okay";
-
- vop_out: ports {
- #address-cells = <1>;
- #size-cells = <0>;
-
- vp0: port@0 {
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <0>;
-
- vp0_out_dsi0: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&dsi0_in_vp0>;
- };
-
- vp0_out_dsi1: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&dsi1_in_vp0>;
- };
-
- vp0_out_edp: endpoint@2 {
- reg = <2>;
- remote-endpoint = <&edp_in_vp0>;
- };
-
- vp0_out_hdmi: endpoint@3 {
- reg = <3>;
- remote-endpoint = <&hdmi_in_vp0>;
- };
- };
-
- vp1: port@1 {
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <1>;
-
- vp1_out_dsi0: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&dsi0_in_vp1>;
- };
-
- vp1_out_dsi1: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&dsi1_in_vp1>;
- };
-
- vp1_out_edp: endpoint@2 {
- reg = <2>;
-
- vp1: port@1 {
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <1>;
-
- vp1_out_dsi0: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&dsi0_in_vp1>;
- };
-
- vp1_out_dsi1: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&dsi1_in_vp1>;
- };
-
- vp1_out_edp: endpoint@2 {
- reg = <2>;
- remote-endpoint = <&edp_in_vp1>;
- };
-
- vp1_out_hdmi: endpoint@3 {
- reg = <3>;
- remote-endpoint = <&hdmi_in_vp1>;
- };
-
- vp1_out_lvds: endpoint@4 {
- reg = <4>;
- remote-endpoint = <&lvds_in_vp1>;
- };
- };
-
- vp2: port@2 {
- #address-cells = <1>;
- #size-cells = <0>;
-
- reg = <2>;
-
- vp2_out_lvds: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&lvds_in_vp2>;
- };
-
- vp2_out_rgb: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&rgb_in_vp2>;
- };
- };
- };
- };
3、dsi的设备树信息
1)dsi -> ports -> port dsi0_in_vp0(endpoint) 下的remote-endpoint 为dsi与vop间建立关系的属性配置,其中dsi作为输入端,另一端接的是vop out端。
2)dsi -> ports -> port -> dsi_out_panel1/dsi_out_panel2/dsi_out_panel3(endpoint)下的remote-endpoint 为dsi 与 panel 间建立关系的属性配置。此处代表有三个panel。因此 若想多屏可以在此处增加节点达到和新panel建立联系。
3)dsi->panel 为panel的配置。正常是单独创建个屏dtsi文件后,在文件中引用(&dsi0) 来进行新增屏。此处只是为了更好的分析而集成在一起(本身设备树在编译的时候也是覆盖的操作)。
4)dsi -> panel -> ports -> port -> panel1_in_dsi(endpoint) 下的remote-endpoint 为panel与dsi间的关系,即此处是连到dsi_out_panel1上。
- dsi0: dsi@fe060000 {
- compatible = "rockchip,rk3568-mipi-dsi";
- reg = <0x0 0xfe060000 0x0 0x10000>;
- interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&cru PCLK_DSITX_0>, <&cru HCLK_VO>, <&video_phy0>;
- clock-names = "pclk", "hclk", "hs_clk";
- resets = <&cru SRST_P_DSITX_0>;
- reset-names = "apb";
- phys = <&video_phy0>;
- phy-names = "mipi_dphy";
- power-domains = <&power RK3568_PD_VO>;
- rockchip,grf = <&grf>;
- #address-cells = <1>;
- #size-cells = <0>;
- status = "okay";
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
-
- dsi0_in: port@0 {
- reg = <0>;
- #address-cells = <1>;
- #size-cells = <0>;
-
- dsi0_in_vp0: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&vp0_out_dsi0>;
- status = "okay";
- };
-
- };
- port@1 {
- reg = <1>;
- dsi_out_panel1: endpoint {
- remote-endpoint = <&panel1_in_dsi>;
- };
- };
-
- port@2 {
- reg = <2>;
- dsi_out_panel2: endpoint {
- remote-endpoint = <&panel2_in_dsi>;
- };
- };
-
- port@3 {
- reg = <3>;
- dsi_out_panel3: endpoint {
- remote-endpoint = <&panel3_in_dsi>;
- };
- };
- };
- panel@0 {
- status = "okay";
- compatible = "simple-panel-dsi";
- reg = <0>;
- num = <0>;
- id = [22];
- lcd-ic = "st7703";
- lcd-vendor = "hbs";
- id-reg = <0xDA>;
- power-supply = <&vcc3v3_lcd0_n>;
- reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_LOW>;
- enable-gpios = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>;
- //backlight = <&backlight>;
- reset-delay-ms = <60>;
- enable-delay-ms = <60>;
- prepare-delay-ms = <60>;
- unprepare-delay-ms = <60>;
- disable-delay-ms = <60>;
- init-delay-ms = <60>;
- dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
- MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;
- dsi,format = <MIPI_DSI_FMT_RGB888>;
- dsi,lanes = <4>;
- panel-init-sequence = [
- 39 00 04 B9 F1 12 83
- 39 00 04 B2 C8 12 30
- ...//省略无关代码
- 13 FA 02 11 00
- 13 32 02 29 00
- ];
-
- panel-exit-sequence = [
- 05 32 01 28
- 05 FA 01 10
- ];
-
- display-timings {
- native-mode = <&dsi0_timing0>;
- dsi0_timing0: timing0 {
- clock-frequency = <66276000>;
- hactive = <720>;
- vactive = <1280>;
- hfront-porch = <50>;
- hsync-len = <20>;
- hback-porch = <50>;
- vfront-porch = <15>;
- vsync-len = <5>;
- vback-porch = <15>;
- hsync-active = <0>;
- vsync-active = <0>;
- de-active = <0>;
- pixelclk-active = <1>;
- };
- };
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
-
- port@0 {
- reg = <0>;
- panel1_in_dsi: endpoint {
- remote-endpoint = <&dsi_out_panel1>;
- };
- };
- };
- };
- panel@1 {
- status = "okay";
- compatible = "simple-panel-dsi";
- reg = <0>;
- num = <1>;
- id = [15];
- id-reg = <0xDA>;
- power-supply = <&vcc3v3_lcd0_n>;
- reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_LOW>;
- enable-gpios = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>;
- //backlight = <&backlight>;
- reset-delay-ms = <60>;
- enable-delay-ms = <60>;
- prepare-delay-ms = <60>;
- unprepare-delay-ms = <60>;
- disable-delay-ms = <60>;
- init-delay-ms = <60>;
- dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
- MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;
- dsi,format = <MIPI_DSI_FMT_RGB888>;
- dsi,lanes = <4>;
- panel-init-sequence = [
- 39 00 04 B9 F1 12 83
- ...//省略无关代码
- 39 00 03 C8 10 01
- 13 78 02 11 00
- 13 0A 02 29 00
- ];
-
- panel-exit-sequence = [
- 05 0A 01 28
- 05 78 01 10
- ];
-
- display-timings {
- native-mode = <&dsi0_timing1>;
- dsi0_timing1: timing0 {
- clock-frequency = <62640000>;
- hactive = <720>;
- vactive = <1280>;
- hfront-porch = <10>;
- hsync-len = <25>;
- hback-porch = <45>;
- vfront-porch = <10>;
- vsync-len = <4>;
- vback-porch = <11>;
- hsync-active = <0>;
- vsync-active = <0>;
- de-active = <0>;
- pixelclk-active = <1>;
- };
- };
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
-
- port@0 {
- reg = <0>;
- panel2_in_dsi: endpoint {
- remote-endpoint = <&dsi_out_panel2>;
- };
- };
- };
- };
- };
二、uboot部分
uboot部分主要是打算借助uboot上解析设备树部分的代码来进行分析。
1、通过ofnode_for_each_subnode遍历route节点下的子节点,即为route-dsi0。
2、通过route-dsi0 -> connect的属性值来定位到vop上的节点vp0_out_dsi0。
3、找到vop的节点后,通过uclass_get_device_by_ofnode启动vop设备,最终会执行到相应的rockchip_vop_probe函数(正常下,使用U_BOOT_DRIVER只会回调bind函数,而不会执行probe的)。
4、在执行到rockchip_of_find_connector函数时,实际传进的参数为vop节点下的vp0_out_dsi0。
- static int rockchip_display_probe(struct udevice *dev)
- {
- struct video_priv *uc_priv = dev_get_uclass_priv(dev);
- struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
- const void *blob = gd->fdt_blob;
-
- //省略无关代码
-
- route_node = dev_read_subnode(dev, "route");
- if (!ofnode_valid(route_node))
- return -ENODEV;
-
- ofnode_for_each_subnode(node, route_node) {
- if (!ofnode_is_available(node))
- continue;
- phandle = ofnode_read_u32_default(node, "connect", -1);
- if (phandle < 0) {
- printf("Warn: can't find connect node's handle\n");
- continue;
- }
- ep_node = of_find_node_by_phandle(phandle);
- if (!ofnode_valid(np_to_ofnode(ep_node))) {
- printf("Warn: can't find endpoint node from phandle\n");
- continue;
- }
- port_node = of_get_parent(ep_node);
- if (!ofnode_valid(np_to_ofnode(port_node))) {
- printf("Warn: can't find port node from phandle\n");
- continue;
- }
-
- port_parent_node = of_get_parent(port_node);
- if (!ofnode_valid(np_to_ofnode(port_parent_node))) {
- printf("Warn: can't find port parent node from phandle\n");
- continue;
- }
-
- is_ports_node = strstr(port_parent_node->full_name, "ports") ? 1 : 0;
- if (is_ports_node) {
- vop_node = of_get_parent(port_parent_node);
- if (!ofnode_valid(np_to_ofnode(vop_node))) {
- printf("Warn: can't find crtc node from phandle\n");
- continue;
- }
- } else {
- vop_node = port_parent_node;
- }
-
- ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC,
- np_to_ofnode(vop_node),
- &crtc_dev);
- if (ret) {
- printf("Warn: can't find crtc driver %d\n", ret);
- continue;
- }
- crtc = (struct rockchip_crtc *)dev_get_driver_data(crtc_dev);
-
- conn_dev = rockchip_of_find_connector(np_to_ofnode(ep_node));
- if (!conn_dev) {
- printf("Warn: can't find connect driver\n");
- continue;
- }
-
- conn = (const struct rockchip_connector *)dev_get_driver_data(conn_dev);
-
- phy = rockchip_of_find_phy(conn_dev);
-
- bridge = rockchip_of_find_bridge(conn_dev);
- if (bridge)
- panel = rockchip_of_find_panel(bridge->dev);
- else
- panel = rockchip_of_find_panel(conn_dev);
-
- s = malloc(sizeof(*s));
- if (!s)
- continue;
-
- return 0;
- }
5、 通过子节点获取父节点的操作来定位,因此rockchip_of_find_connector函数下的conn即为dsi0节点,因此再次通过uclass_get_device_by_ofnode来加载dsi设备,即会调用dw_mipi_dsi_probe函数。
- static struct udevice *rockchip_of_find_connector(ofnode endpoint)
- {
- ofnode ep, port, ports, conn;
- uint phandle;
- struct udevice *dev;
- int ret;
-
- if (ofnode_read_u32(endpoint, "remote-endpoint", &phandle))
- return NULL;
-
- ep = ofnode_get_by_phandle(phandle);
- if (!ofnode_valid(ep) || !ofnode_is_available(ep))
- return NULL;
-
- port = ofnode_get_parent(ep);
- if (!ofnode_valid(port))
- return NULL;
-
- ports = ofnode_get_parent(port);
- if (!ofnode_valid(ports))
- return NULL;
-
- conn = ofnode_get_parent(ports);
- if (!ofnode_valid(conn) || !ofnode_is_available(conn))
- return NULL;
-
- ret = uclass_get_device_by_ofnode(UCLASS_DISPLAY, conn, &dev);
- if (ret)
- return NULL;
-
- return dev;
- }
6、rockchip_of_find_bridge查找是否存在bridge设备
- static struct rockchip_bridge *rockchip_of_find_bridge(struct udevice *conn_dev)
- {
- ofnode node, ports, port, ep;
- struct udevice *dev;
- int ret;
-
- ports = dev_read_subnode(conn_dev, "ports");
- if (!ofnode_valid(ports))
- return NULL;
-
- ofnode_for_each_subnode(port, ports) {
- u32 reg;
-
- if (ofnode_read_u32(port, "reg", ®))
- continue;
-
- if (reg != PORT_DIR_OUT)
- continue;
-
- ofnode_for_each_subnode(ep, port) {
- ofnode _ep, _port, _ports;
- uint phandle;
-
- if (ofnode_read_u32(ep, "remote-endpoint", &phandle))
- continue;
-
- _ep = ofnode_get_by_phandle(phandle);
- if (!ofnode_valid(_ep))
- continue;
-
- _port = ofnode_get_parent(_ep);
- if (!ofnode_valid(_port))
- continue;
-
- _ports = ofnode_get_parent(_port);
- if (!ofnode_valid(_ports))
- continue;
-
- node = ofnode_get_parent(_ports);
- if (!ofnode_valid(node))
- continue;
-
- ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE,
- node, &dev);
- if (!ret)
- goto found;
- }
- }
-
- return NULL;
-
- found:
- return (struct rockchip_bridge *)dev_get_driver_data(dev);
- }
7、在dsi0的节点下,通过ports -> port -> remote-endpoint来找到panel@1/2/3节点下的port 的endpoint节点,再依次通过获取父节点的方式获取到panel的节点,最后通过uclass_get_device_by_ofnode函数启动panel,即执行rockchip_panel_probe回调
- static struct rockchip_panel *rockchip_of_find_panel(struct udevice *dev)
- {
- ofnode panel_node, ports, port, ep, port_parent_node;
- struct udevice *panel_dev;
- int ret;
-
- panel_node = dev_read_subnode(dev, "panel");
- if (ofnode_valid(panel_node) && ofnode_is_available(panel_node)) {
- ret = uclass_get_device_by_ofnode(UCLASS_PANEL, panel_node,
- &panel_dev);
- if (!ret)
- goto found;
- }
-
- ports = dev_read_subnode(dev, "ports");
- if (!ofnode_valid(ports))
- return NULL;
-
- ofnode_for_each_subnode(port, ports) {
- u32 reg;
-
- if (ofnode_read_u32(port, "reg", ®))
- continue;
-
- if (reg != PORT_DIR_OUT)
- continue;
-
- ofnode_for_each_subnode(ep, port) {
- ofnode _ep, _port;
- uint phandle;
- bool is_ports_node = false;
-
- if (ofnode_read_u32(ep, "remote-endpoint", &phandle))
- continue;
-
- _ep = ofnode_get_by_phandle(phandle);
- if (!ofnode_valid(_ep))
- continue;
-
- _port = ofnode_get_parent(_ep);
- if (!ofnode_valid(_port))
- continue;
-
- port_parent_node = ofnode_get_parent(_port);
- is_ports_node = strstr(port_parent_node.np->full_name, "ports") ? 1 : 0;
- if (is_ports_node)
- panel_node = ofnode_get_parent(port_parent_node);
- else
- panel_node = ofnode_get_parent(_port);
- if (!ofnode_valid(panel_node))
- continue;
-
- ret = uclass_get_device_by_ofnode(UCLASS_PANEL,
- panel_node,
- &panel_dev);
- if (!ret)
- goto found;
- }
- }
-
- return NULL;
-
- found:
- return (struct rockchip_panel *)dev_get_driver_data(panel_dev);
- }
三、总结
1、由设备树分析可知:在display-subsystem 下通过ports -> port ->endpoint -> remote-endpoint的方式来实现建立vop - dsi - panel 的联系
2、由uboot分析可知:在rockchip_display_probe中先解析对应设备树节点,通过dev_read_subnode、of_find_node_by_phandle、of_get_parent、uclass_get_device_by_xxx(uclass_get_device_by_ofnode)等系列函数去启动vop 、connector(dsi)、panel等设备。
如下:整体的框架
注:
1)vop *(video out processor)代表有多个处理器时的情况。
2)panel* 是代表存在多屏情况下的因素。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。