当前位置:   article > 正文

LCD模块加载启动分析_remote-endpoint

remote-endpoint

本文主要是基于rk3566/rk3568平台通过LCD uboot 和 设备树 的代码 对 vop - encoder/connector - panel 三者如何建立联系的进行分析。

一、设备树部分

设备树主要是借助remote-endpoint属性来达到选择相应的外设配置。

1、display-subsystem的设备树信息

1)以ports属性指定相应的vop端点

2)以route_dsi0 指定uboot 阶段的logo配置,其中的connect属性指定的vop需与ports的一致。

  1. display_subsystem: display-subsystem {
  2. compatible = "rockchip,display-subsystem";
  3. memory-region = <&drm_logo>, <&drm_cubic_lut>;
  4. memory-region-names = "drm-logo", "drm-cubic-lut";
  5. ports = <&vop_out>;
  6. devfreq = <&dmc>;
  7. route {
  8. route_dsi0: route-dsi0 {
  9. status = "okay";
  10. logo,uboot = "logo.bmp";
  11. logo,kernel = "logo_kernel.bmp";
  12. logo,mode = "center";
  13. charge_logo,mode = "center";
  14. connect = <&vp0_out_dsi0>;
  15. };
  16. route_dsi1: route-dsi1 {
  17. status = "disabled";
  18. logo,uboot = "logo.bmp";
  19. route {
  20. route_dsi0: route-dsi0 {
  21. status = "okay";
  22. logo,uboot = "logo.bmp";
  23. logo,kernel = "logo_kernel.bmp";
  24. logo,mode = "center";
  25. charge_logo,mode = "center";
  26. connect = <&vp0_out_dsi0>;
  27. };
  28. };
  29. };

2、vop的设备树信息 

1)以ports -> port -> endpoint -> remote-endpoint 的顺序,通过remote-endpoint 属性来指定远端的设备端点

  1. vop: vop@fe040000 {
  2. compatible = "rockchip,rk3568-vop";
  3. reg = <0x0 0xfe040000 0x0 0x3000>, <0x0 0xfe044000 0x0 0x1000>;
  4. reg-names = "regs", "gamma_lut";
  5. rockchip,grf = <&grf>;
  6. interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
  7. clocks = <&cru ACLK_VOP>, <&cru HCLK_VOP>, <&cru DCLK_VOP0>, <&cru DCLK_VOP1>, <&cru DCLK_VOP2>;
  8. clock-names = "aclk_vop", "hclk_vop", "dclk_vp0", "dclk_vp1", "dclk_vp2";
  9. iommus = <&vop_mmu>;
  10. power-domains = <&power RK3568_PD_VO>;
  11. status = "okay";
  12. vop_out: ports {
  13. #address-cells = <1>;
  14. #size-cells = <0>;
  15. vp0: port@0 {
  16. #address-cells = <1>;
  17. #size-cells = <0>;
  18. reg = <0>;
  19. vp0_out_dsi0: endpoint@0 {
  20. reg = <0>;
  21. remote-endpoint = <&dsi0_in_vp0>;
  22. };
  23. vp0_out_dsi1: endpoint@1 {
  24. reg = <1>;
  25. remote-endpoint = <&dsi1_in_vp0>;
  26. };
  27. vp0_out_edp: endpoint@2 {
  28. reg = <2>;
  29. remote-endpoint = <&edp_in_vp0>;
  30. };
  31. vp0_out_hdmi: endpoint@3 {
  32. reg = <3>;
  33. remote-endpoint = <&hdmi_in_vp0>;
  34. };
  35. };
  36. vp1: port@1 {
  37. #address-cells = <1>;
  38. #size-cells = <0>;
  39. reg = <1>;
  40. vp1_out_dsi0: endpoint@0 {
  41. reg = <0>;
  42. remote-endpoint = <&dsi0_in_vp1>;
  43. };
  44. vp1_out_dsi1: endpoint@1 {
  45. reg = <1>;
  46. remote-endpoint = <&dsi1_in_vp1>;
  47. };
  48. vp1_out_edp: endpoint@2 {
  49. reg = <2>;
  50. vp1: port@1 {
  51. #address-cells = <1>;
  52. #size-cells = <0>;
  53. reg = <1>;
  54. vp1_out_dsi0: endpoint@0 {
  55. reg = <0>;
  56. remote-endpoint = <&dsi0_in_vp1>;
  57. };
  58. vp1_out_dsi1: endpoint@1 {
  59. reg = <1>;
  60. remote-endpoint = <&dsi1_in_vp1>;
  61. };
  62. vp1_out_edp: endpoint@2 {
  63. reg = <2>;
  64. remote-endpoint = <&edp_in_vp1>;
  65. };
  66. vp1_out_hdmi: endpoint@3 {
  67. reg = <3>;
  68. remote-endpoint = <&hdmi_in_vp1>;
  69. };
  70. vp1_out_lvds: endpoint@4 {
  71. reg = <4>;
  72. remote-endpoint = <&lvds_in_vp1>;
  73. };
  74. };
  75. vp2: port@2 {
  76. #address-cells = <1>;
  77. #size-cells = <0>;
  78. reg = <2>;
  79. vp2_out_lvds: endpoint@0 {
  80. reg = <0>;
  81. remote-endpoint = <&lvds_in_vp2>;
  82. };
  83. vp2_out_rgb: endpoint@1 {
  84. reg = <1>;
  85. remote-endpoint = <&rgb_in_vp2>;
  86. };
  87. };
  88. };
  89. };

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上。

  1. dsi0: dsi@fe060000 {
  2. compatible = "rockchip,rk3568-mipi-dsi";
  3. reg = <0x0 0xfe060000 0x0 0x10000>;
  4. interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
  5. clocks = <&cru PCLK_DSITX_0>, <&cru HCLK_VO>, <&video_phy0>;
  6. clock-names = "pclk", "hclk", "hs_clk";
  7. resets = <&cru SRST_P_DSITX_0>;
  8. reset-names = "apb";
  9. phys = <&video_phy0>;
  10. phy-names = "mipi_dphy";
  11. power-domains = <&power RK3568_PD_VO>;
  12. rockchip,grf = <&grf>;
  13. #address-cells = <1>;
  14. #size-cells = <0>;
  15. status = "okay";
  16. ports {
  17. #address-cells = <1>;
  18. #size-cells = <0>;
  19. dsi0_in: port@0 {
  20. reg = <0>;
  21. #address-cells = <1>;
  22. #size-cells = <0>;
  23. dsi0_in_vp0: endpoint@0 {
  24. reg = <0>;
  25. remote-endpoint = <&vp0_out_dsi0>;
  26. status = "okay";
  27. };
  28. };
  29. port@1 {
  30. reg = <1>;
  31. dsi_out_panel1: endpoint {
  32. remote-endpoint = <&panel1_in_dsi>;
  33. };
  34. };
  35. port@2 {
  36. reg = <2>;
  37. dsi_out_panel2: endpoint {
  38. remote-endpoint = <&panel2_in_dsi>;
  39. };
  40. };
  41. port@3 {
  42. reg = <3>;
  43. dsi_out_panel3: endpoint {
  44. remote-endpoint = <&panel3_in_dsi>;
  45. };
  46. };
  47. };
  48. panel@0 {
  49. status = "okay";
  50. compatible = "simple-panel-dsi";
  51. reg = <0>;
  52. num = <0>;
  53. id = [22];
  54. lcd-ic = "st7703";
  55. lcd-vendor = "hbs";
  56. id-reg = <0xDA>;
  57. power-supply = <&vcc3v3_lcd0_n>;
  58. reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_LOW>;
  59. enable-gpios = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>;
  60. //backlight = <&backlight>;
  61. reset-delay-ms = <60>;
  62. enable-delay-ms = <60>;
  63. prepare-delay-ms = <60>;
  64. unprepare-delay-ms = <60>;
  65. disable-delay-ms = <60>;
  66. init-delay-ms = <60>;
  67. dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
  68. MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;
  69. dsi,format = <MIPI_DSI_FMT_RGB888>;
  70. dsi,lanes = <4>;
  71. panel-init-sequence = [
  72. 39 00 04 B9 F1 12 83
  73. 39 00 04 B2 C8 12 30
  74. ...//省略无关代码
  75. 13 FA 02 11 00
  76. 13 32 02 29 00
  77. ];
  78. panel-exit-sequence = [
  79. 05 32 01 28
  80. 05 FA 01 10
  81. ];
  82. display-timings {
  83. native-mode = <&dsi0_timing0>;
  84. dsi0_timing0: timing0 {
  85. clock-frequency = <66276000>;
  86. hactive = <720>;
  87. vactive = <1280>;
  88. hfront-porch = <50>;
  89. hsync-len = <20>;
  90. hback-porch = <50>;
  91. vfront-porch = <15>;
  92. vsync-len = <5>;
  93. vback-porch = <15>;
  94. hsync-active = <0>;
  95. vsync-active = <0>;
  96. de-active = <0>;
  97. pixelclk-active = <1>;
  98. };
  99. };
  100. ports {
  101. #address-cells = <1>;
  102. #size-cells = <0>;
  103. port@0 {
  104. reg = <0>;
  105. panel1_in_dsi: endpoint {
  106. remote-endpoint = <&dsi_out_panel1>;
  107. };
  108. };
  109. };
  110. };
  111. panel@1 {
  112. status = "okay";
  113. compatible = "simple-panel-dsi";
  114. reg = <0>;
  115. num = <1>;
  116. id = [15];
  117. id-reg = <0xDA>;
  118. power-supply = <&vcc3v3_lcd0_n>;
  119. reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_LOW>;
  120. enable-gpios = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>;
  121. //backlight = <&backlight>;
  122. reset-delay-ms = <60>;
  123. enable-delay-ms = <60>;
  124. prepare-delay-ms = <60>;
  125. unprepare-delay-ms = <60>;
  126. disable-delay-ms = <60>;
  127. init-delay-ms = <60>;
  128. dsi,flags = <(MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
  129. MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;
  130. dsi,format = <MIPI_DSI_FMT_RGB888>;
  131. dsi,lanes = <4>;
  132. panel-init-sequence = [
  133. 39 00 04 B9 F1 12 83
  134. ...//省略无关代码
  135. 39 00 03 C8 10 01
  136. 13 78 02 11 00
  137. 13 0A 02 29 00
  138. ];
  139. panel-exit-sequence = [
  140. 05 0A 01 28
  141. 05 78 01 10
  142. ];
  143. display-timings {
  144. native-mode = <&dsi0_timing1>;
  145. dsi0_timing1: timing0 {
  146. clock-frequency = <62640000>;
  147. hactive = <720>;
  148. vactive = <1280>;
  149. hfront-porch = <10>;
  150. hsync-len = <25>;
  151. hback-porch = <45>;
  152. vfront-porch = <10>;
  153. vsync-len = <4>;
  154. vback-porch = <11>;
  155. hsync-active = <0>;
  156. vsync-active = <0>;
  157. de-active = <0>;
  158. pixelclk-active = <1>;
  159. };
  160. };
  161. ports {
  162. #address-cells = <1>;
  163. #size-cells = <0>;
  164. port@0 {
  165. reg = <0>;
  166. panel2_in_dsi: endpoint {
  167. remote-endpoint = <&dsi_out_panel2>;
  168. };
  169. };
  170. };
  171. };
  172. };

 二、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。

  1. static int rockchip_display_probe(struct udevice *dev)
  2. {
  3. struct video_priv *uc_priv = dev_get_uclass_priv(dev);
  4. struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
  5. const void *blob = gd->fdt_blob;
  6. //省略无关代码
  7. route_node = dev_read_subnode(dev, "route");
  8. if (!ofnode_valid(route_node))
  9. return -ENODEV;
  10. ofnode_for_each_subnode(node, route_node) {
  11. if (!ofnode_is_available(node))
  12. continue;
  13. phandle = ofnode_read_u32_default(node, "connect", -1);
  14. if (phandle < 0) {
  15. printf("Warn: can't find connect node's handle\n");
  16. continue;
  17. }
  18. ep_node = of_find_node_by_phandle(phandle);
  19. if (!ofnode_valid(np_to_ofnode(ep_node))) {
  20. printf("Warn: can't find endpoint node from phandle\n");
  21. continue;
  22. }
  23. port_node = of_get_parent(ep_node);
  24. if (!ofnode_valid(np_to_ofnode(port_node))) {
  25. printf("Warn: can't find port node from phandle\n");
  26. continue;
  27. }
  28. port_parent_node = of_get_parent(port_node);
  29. if (!ofnode_valid(np_to_ofnode(port_parent_node))) {
  30. printf("Warn: can't find port parent node from phandle\n");
  31. continue;
  32. }
  33. is_ports_node = strstr(port_parent_node->full_name, "ports") ? 1 : 0;
  34. if (is_ports_node) {
  35. vop_node = of_get_parent(port_parent_node);
  36. if (!ofnode_valid(np_to_ofnode(vop_node))) {
  37. printf("Warn: can't find crtc node from phandle\n");
  38. continue;
  39. }
  40. } else {
  41. vop_node = port_parent_node;
  42. }
  43. ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC,
  44. np_to_ofnode(vop_node),
  45. &crtc_dev);
  46. if (ret) {
  47. printf("Warn: can't find crtc driver %d\n", ret);
  48. continue;
  49. }
  50. crtc = (struct rockchip_crtc *)dev_get_driver_data(crtc_dev);
  51. conn_dev = rockchip_of_find_connector(np_to_ofnode(ep_node));
  52. if (!conn_dev) {
  53. printf("Warn: can't find connect driver\n");
  54. continue;
  55. }
  56. conn = (const struct rockchip_connector *)dev_get_driver_data(conn_dev);
  57. phy = rockchip_of_find_phy(conn_dev);
  58. bridge = rockchip_of_find_bridge(conn_dev);
  59. if (bridge)
  60. panel = rockchip_of_find_panel(bridge->dev);
  61. else
  62. panel = rockchip_of_find_panel(conn_dev);
  63. s = malloc(sizeof(*s));
  64. if (!s)
  65. continue;
  66. return 0;
  67. }

5、 通过子节点获取父节点的操作来定位,因此rockchip_of_find_connector函数下的conn即为dsi0节点,因此再次通过uclass_get_device_by_ofnode来加载dsi设备,即会调用dw_mipi_dsi_probe函数。

  1. static struct udevice *rockchip_of_find_connector(ofnode endpoint)
  2. {
  3. ofnode ep, port, ports, conn;
  4. uint phandle;
  5. struct udevice *dev;
  6. int ret;
  7. if (ofnode_read_u32(endpoint, "remote-endpoint", &phandle))
  8. return NULL;
  9. ep = ofnode_get_by_phandle(phandle);
  10. if (!ofnode_valid(ep) || !ofnode_is_available(ep))
  11. return NULL;
  12. port = ofnode_get_parent(ep);
  13. if (!ofnode_valid(port))
  14. return NULL;
  15. ports = ofnode_get_parent(port);
  16. if (!ofnode_valid(ports))
  17. return NULL;
  18. conn = ofnode_get_parent(ports);
  19. if (!ofnode_valid(conn) || !ofnode_is_available(conn))
  20. return NULL;
  21. ret = uclass_get_device_by_ofnode(UCLASS_DISPLAY, conn, &dev);
  22. if (ret)
  23. return NULL;
  24. return dev;
  25. }

 6、rockchip_of_find_bridge查找是否存在bridge设备

  1. static struct rockchip_bridge *rockchip_of_find_bridge(struct udevice *conn_dev)
  2. {
  3. ofnode node, ports, port, ep;
  4. struct udevice *dev;
  5. int ret;
  6. ports = dev_read_subnode(conn_dev, "ports");
  7. if (!ofnode_valid(ports))
  8. return NULL;
  9. ofnode_for_each_subnode(port, ports) {
  10. u32 reg;
  11. if (ofnode_read_u32(port, "reg", &reg))
  12. continue;
  13. if (reg != PORT_DIR_OUT)
  14. continue;
  15. ofnode_for_each_subnode(ep, port) {
  16. ofnode _ep, _port, _ports;
  17. uint phandle;
  18. if (ofnode_read_u32(ep, "remote-endpoint", &phandle))
  19. continue;
  20. _ep = ofnode_get_by_phandle(phandle);
  21. if (!ofnode_valid(_ep))
  22. continue;
  23. _port = ofnode_get_parent(_ep);
  24. if (!ofnode_valid(_port))
  25. continue;
  26. _ports = ofnode_get_parent(_port);
  27. if (!ofnode_valid(_ports))
  28. continue;
  29. node = ofnode_get_parent(_ports);
  30. if (!ofnode_valid(node))
  31. continue;
  32. ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE,
  33. node, &dev);
  34. if (!ret)
  35. goto found;
  36. }
  37. }
  38. return NULL;
  39. found:
  40. return (struct rockchip_bridge *)dev_get_driver_data(dev);
  41. }

7、在dsi0的节点下,通过ports -> port -> remote-endpoint来找到panel@1/2/3节点下的port 的endpoint节点,再依次通过获取父节点的方式获取到panel的节点,最后通过uclass_get_device_by_ofnode函数启动panel,即执行rockchip_panel_probe回调 

  1. static struct rockchip_panel *rockchip_of_find_panel(struct udevice *dev)
  2. {
  3. ofnode panel_node, ports, port, ep, port_parent_node;
  4. struct udevice *panel_dev;
  5. int ret;
  6. panel_node = dev_read_subnode(dev, "panel");
  7. if (ofnode_valid(panel_node) && ofnode_is_available(panel_node)) {
  8. ret = uclass_get_device_by_ofnode(UCLASS_PANEL, panel_node,
  9. &panel_dev);
  10. if (!ret)
  11. goto found;
  12. }
  13. ports = dev_read_subnode(dev, "ports");
  14. if (!ofnode_valid(ports))
  15. return NULL;
  16. ofnode_for_each_subnode(port, ports) {
  17. u32 reg;
  18. if (ofnode_read_u32(port, "reg", &reg))
  19. continue;
  20. if (reg != PORT_DIR_OUT)
  21. continue;
  22. ofnode_for_each_subnode(ep, port) {
  23. ofnode _ep, _port;
  24. uint phandle;
  25. bool is_ports_node = false;
  26. if (ofnode_read_u32(ep, "remote-endpoint", &phandle))
  27. continue;
  28. _ep = ofnode_get_by_phandle(phandle);
  29. if (!ofnode_valid(_ep))
  30. continue;
  31. _port = ofnode_get_parent(_ep);
  32. if (!ofnode_valid(_port))
  33. continue;
  34. port_parent_node = ofnode_get_parent(_port);
  35. is_ports_node = strstr(port_parent_node.np->full_name, "ports") ? 1 : 0;
  36. if (is_ports_node)
  37. panel_node = ofnode_get_parent(port_parent_node);
  38. else
  39. panel_node = ofnode_get_parent(_port);
  40. if (!ofnode_valid(panel_node))
  41. continue;
  42. ret = uclass_get_device_by_ofnode(UCLASS_PANEL,
  43. panel_node,
  44. &panel_dev);
  45. if (!ret)
  46. goto found;
  47. }
  48. }
  49. return NULL;
  50. found:
  51. return (struct rockchip_panel *)dev_get_driver_data(panel_dev);
  52. }

三、总结

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等设备。

如下:整体的框架

2f2b18c25ede42ea8fa41cf48283be19.png

 注:

1)vop *(video out processor)代表有多个处理器时的情况。

2)panel* 是代表存在多屏情况下的因素。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/309270
推荐阅读
相关标签
  

闽ICP备14008679号