RK3288 android 6.0 实现 lvds + edp 双屏异显

rk3288 默认支持 edp+hdmi 双屏显示,或者 lvds + hdmi, 但不支持 lvds + edp 双屏异显。业务要求,需要使用RK3288 Android 6.0 系统,支持 lvds, edp 两种接口的双屏异显。本文介绍具体的实现方法。

  • 平台: RK3288
  • 系统: Android 6.0
  • 屏幕: LVDS(7 inch 1024x600), edp(11 inch 1920x1080)
  • 需求: LVDS (Primary) + edp (EXTEND) 双屏异显

主屏副屏的切换只要修改dts相关参数即可,lvds edp 主副互换比较简单。

kernel

首先修改 kernel, 这里参考了博客[RK3288][Android6.0] 调试笔记 --- 双屏显示内核Patch , 具体细节会有差异。

dts

首先配置dts , 两个屏幕的参数分别放置在一个 dtsi 文件中。

lvds dts

  • arch/arm/boot/dts/lcd-lvds-1024x600_dual.dtsi
display-timings {
    native-mode = <&timing0>;
    // 7" 1024x600 LVDS panel
    timing0: timing0 {
        screen-type = <SCREEN_LVDS>;
        lvds-format = <LVDS_8BIT_1>;
        out-face    = <OUT_D888_P666>;
        color-mode = <COLOR_RGB>;
        clock-frequency = <52000000>;
        hactive = <1024>;
        vactive = <600>;
        hback-porch = <150>;
        hfront-porch = <160>;
        vback-porch = <22>;
        vfront-porch = <12>;
        hsync-len = <10>;
        vsync-len = <1>;
        hsync-active = <0>;
        vsync-active = <0>;
        de-active = <0>;
        pixelclk-active = <1>;
        swap-rb = <0>;
        swap-rg = <0>;
        swap-gb = <0>;
    };
};

edp dts

  • arch/arm/boot/dts/lcd-1920x1080_dual.dtsi
disp_timings: display-timings {
    native-mode = <&timing2>;
    // 11.6" 1920x1080 eDP panel
    timing2: timing2 {
        screen-type = <SCREEN_EDP>;
        out-face    = <OUT_P888>;
        clock-frequency = <138700000>;
        hactive = <1920>;
        vactive = <1080>;
        hback-porch = <35>;
        hfront-porch = <120>;
        vback-porch = <10>;
        vfront-porch = <20>;
        hsync-len = <5>;
        vsync-len = <2>;
        hsync-active = <0>;
        vsync-active = <0>;
        de-active = <0>;
        pixelclk-active = <1>;
        swap-rb = <0>;
        swap-rg = <0>;
        swap-gb = <0>;
    };
};

lcd-b116xan.dtsi

上面定义好了 disp_timings, 原有的 lcd-b116xan.dtsi 中的定义就不需要了。

diff --git a/arch/arm/boot/dts/lcd-b116xan.dtsi b/arch/arm/boot/dts/lcd-b116xan.dtsi
index 271364e..4b5ee39 100644
--- a/arch/arm/boot/dts/lcd-b116xan.dtsi
+++ b/arch/arm/boot/dts/lcd-b116xan.dtsi
@@ -23,7 +23,7 @@
         enable-gpios = <&gpio7 GPIO_A2 GPIO_ACTIVE_HIGH>;
     };
 
-    disp_timings: display-timings {
+/*    disp_timings: display-timings {
         //native-mode = <&timing0>;
         // 7" 1024x600 LVDS panel
         timing0: timing0 {
@@ -138,6 +138,6 @@
             swap-rg = <0>;
             swap-gb = <0>;
         };
-    };
+    }; */
 };
 

rk3288-tb_8846.dts

接下来是重头,就是 rk3288 的dts,添加了两个屏幕的配置,禁用了 hdmi, 配置 lvds 和 edp,并将 lvds 设为主屏。

diff --git a/arch/arm/boot/dts/rk3288-tb_8846.dts b/arch/arm/boot/dts/rk3288-tb_8846.dts
index 8ed4044..6cac1c9 100644
--- a/arch/arm/boot/dts/rk3288-tb_8846.dts
+++ b/arch/arm/boot/dts/rk3288-tb_8846.dts
@@ -518,7 +518,7 @@
     };
 
     goodix_ts@5d {
-        status = "disabled";
+        //status = "disabled";
         compatible = "goodix,gt1x", "goodix,gt9xx";        // gt5688, gt911
         reg = <0x5d>;
         goodix,rst-gpio = <&gpio7 GPIO_A5 GPIO_ACTIVE_LOW>;
@@ -532,17 +532,62 @@
 };
 
 &fb {
-    rockchip,disp-mode = <DUAL>;
-    rockchip,uboot-logo-on = <1>;
+    rockchip,disp-mode = <DUAL_LCD>;
+    rockchip,uboot-logo-on = <0>;
     rockchip,disp-policy = <DISPLAY_POLICY_BOX>;
 };
 
-&disp_timings {
-    native-mode = <&timing2>;    // timing0:7"LVDS1024x600; timing1:11.6"LVDS1366x768; timing2:11.6"eDP1920x1080; timing4:10.1"LVDS1280x800
-};
+//&disp_timings {
+//    native-mode = <&timing2>;    // timing0:7"LVDS1024x600; timing1:11.6"LVDS1366x768; timing2:11.6"eDP1920x1080; timing4:10.1"LVDS1280x800
+//};
 
 &rk_screen {
-     display-timings = <&disp_timings>;
+    //display-timings = <&disp_timings>;
+    status = "okay";
+    screen0 {
+        screen_prop = <EXTEND>;
+        native-mode = <DEFAULT_MODE>;
+        power_ctr {
+            lcd_en {
+                rockchip,power_type = <GPIO>;
+                gpios = <&gpio7 GPIO_A3 GPIO_ACTIVE_HIGH>;
+                rockchip,delay = <10>;
+            };
+            /*lcd_cs {
+                rockchip,power_type = <GPIO>;
+                gpios = <&gpio7 GPIO_A4 GPIO_ACTIVE_HIGH>;
+                rockchip,delay = <10>;
+            }; */
+        };
+        #include "lcd-edp-1920x1080_dual.dtsi"
+    };
+    screen1 {
+        screen_prop = <PRMRY>;
+        native-mode = <DEFAULT_MODE>;
+        power_ctr {
+            lcd_en {
+                rockchip,power_type = <GPIO>;
+                gpios = <&gpio7 GPIO_A3 GPIO_ACTIVE_HIGH>;
+                rockchip,delay = <10>;
+            };
+            /*lcd_cs {
+                rockchip,power_type = <GPIO>;
+                gpios = <&gpio7 GPIO_A4 GPIO_ACTIVE_HIGH>;
+                rockchip,delay = <10>;
+            }; */
+        };
+        #include "lcd-lvds-1024x600.dtsi"
+    };
+};
+
+&lvds {
+    status = "okay";
+    prop = <PRMRY>;
+};
+
+&edp {
+    status = "okay";
+    prop = <EXTEND>;
 };
 
 /*lcdc0 as PRMRY(LCD),lcdc1 as EXTEND(HDMI)*/
@@ -553,11 +598,11 @@
     power_ctr: power_ctr {
         rockchip,debug = <0>;
 
-        lcd_en:lcd_en {
+        /* lcd_en:lcd_en {
             rockchip,power_type = <GPIO>;
             gpios = <&gpio7 GPIO_A3 GPIO_ACTIVE_HIGH>;
             rockchip,delay = <10>;
-        };
+        }; */
     };
 };
 
@@ -568,7 +613,7 @@
 };
 
 &hdmi {
-    status = "okay";
+    status = "disabled";
     rockchip,cec_enable = <0>;
     rockchip,hdcp_enable = <0>;
     rockchip,hdmi_audio_source = <1>;

这里将 rockchip,uboot-logo-on 设为0,是禁用 uboot logo的意思,不禁用的话会导致开机异常,具体原因暂不清楚。

driver

dts 修改完了,接下来是驱动部分,这部分修改甚多,一定要注意。其实下面这个全局 CONFIG_SMART_DUAL_LCD 不是必须的,可以直接将宏定义的判断部分改为直接替换。

diff --git a/arch/arm/boot/dts/include/dt-bindings/rkfb/rk_fb.h b/arch/arm/boot/dts/include/dt-bindings/rkfb/rk_fb.h
index 6d75770..8b2ac5b 100755
--- a/arch/arm/boot/dts/include/dt-bindings/rkfb/rk_fb.h
+++ b/arch/arm/boot/dts/include/dt-bindings/rkfb/rk_fb.h
@@ -12,6 +12,9 @@
 #define NO_DUAL        0
 #define ONE_DUAL    1
 #define DUAL        2
+#define DUAL_LCD    3
+#define DEFAULT_MODE    0
+
 /********************************************************************
 **          display output interface supported by rockchip       **
 ********************************************************************/
diff --git a/arch/arm/configs/rockchip_defconfig b/arch/arm/configs/rockchip_defconfig
index 3a48555..2481bd0 100644
--- a/arch/arm/configs/rockchip_defconfig
+++ b/arch/arm/configs/rockchip_defconfig
@@ -394,6 +394,7 @@ CONFIG_RK_TRSM=y
 CONFIG_RK32_LVDS=y
 CONFIG_RK32_DP=y
 # CONFIG_RK_VGA is not set
+CONFIG_SMART_DUAL_LCD=y
 CONFIG_RK_HDMI=y
 CONFIG_ROCKCHIP_RGA=y
 CONFIG_ROCKCHIP_RGA2=y
diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig
index 6034139..39543bc 100755
--- a/drivers/video/rockchip/Kconfig
+++ b/drivers/video/rockchip/Kconfig
@@ -58,6 +58,9 @@ config THREE_FB_BUFFER
         help
            select y if android support three buffer,like Jelly Bean
 
+config SMART_DUAL_LCD
+    bool"smart dual lcd support"
+    default n
 
 source "drivers/video/rockchip/lcdc/Kconfig"
 source "drivers/video/rockchip/screen/Kconfig"
diff --git a/drivers/video/rockchip/rk_fb.c b/drivers/video/rockchip/rk_fb.c
index c45387c..af30098 100755
--- a/drivers/video/rockchip/rk_fb.c
+++ b/drivers/video/rockchip/rk_fb.c
@@ -71,9 +71,16 @@ EXPORT_SYMBOL(video_data_to_mirroring);
 extern phys_addr_t uboot_logo_base;
 extern phys_addr_t uboot_logo_size;
 extern phys_addr_t uboot_logo_offset;
+
+#ifndef CONFIG_SMART_DUAL_LCD
 static struct rk_fb_trsm_ops *trsm_lvds_ops;
 static struct rk_fb_trsm_ops *trsm_edp_ops;
 static struct rk_fb_trsm_ops *trsm_mipi_ops;
+#else
+static struct rk_fb_trsm_ops *trsm_prmry_ops;
+static struct rk_fb_trsm_ops *trsm_extend_ops;
+#endif
+
 static int uboot_logo_on;
 
 static int rk_fb_debug_lvl;
@@ -107,6 +114,7 @@ int rk_fb_get_display_policy(void)
 
 int rk_fb_trsm_ops_register(struct rk_fb_trsm_ops *ops, int type)
 {
+#ifndef CONFIG_SMART_DUAL_LCD
     switch (type) {
     case SCREEN_RGB:
     case SCREEN_LVDS:
@@ -127,6 +135,15 @@ int rk_fb_trsm_ops_register(struct rk_fb_trsm_ops *ops, int type)
             __func__, type);
         break;
     }
+#else
+    if (type == PRMRY)
+        trsm_prmry_ops = ops;
+    else if (type == EXTEND)
+        trsm_extend_ops = ops;
+    else
+        pr_err("%s, type:%d\n", __func__, type);
+#endif
+
     return 0;
 }
 
@@ -134,6 +151,7 @@ struct rk_fb_trsm_ops *rk_fb_trsm_ops_get(int type)
 {
     struct rk_fb_trsm_ops *ops;
 
+#ifndef CONFIG_SMART_DUAL_LCD
     switch (type) {
     case SCREEN_RGB:
     case SCREEN_LVDS:
@@ -155,6 +173,15 @@ struct rk_fb_trsm_ops *rk_fb_trsm_ops_get(int type)
             __func__, type);
         break;
     }
+#else
+    if (type == PRMRY)
+        ops = trsm_prmry_ops;
+    else if (type == EXTEND)
+        ops = trsm_extend_ops;
+    else
+        pr_err("%s, type:%d\n", __func__, type);
+#endif
+
     return ops;
 }
 
@@ -309,10 +336,19 @@ static int rk_fb_data_fmt(int data_format, int bits_per_pixel)
 /*
  * rk display power control parse from dts
  */
+#ifndef CONFIG_SMART_DUAL_LCD
 int rk_disp_pwr_ctr_parse_dt(struct rk_lcdc_driver *dev_drv)
+#else
+int rk_disp_pwr_ctr_parse_dt(struct device_node *np, struct rk_screen *rk_screen)
+#endif
 {
+#ifndef CONFIG_SMART_DUAL_LCD
     struct device_node *root = of_get_child_by_name(dev_drv->dev->of_node,
                             "power_ctr");
+#else
+    struct device_node *root = of_get_child_by_name(np, "power_ctr");
+#endif
+
     struct device_node *child;
     struct rk_disp_pwr_ctr_list *pwr_ctr;
     struct list_head *pos;
@@ -321,10 +357,20 @@ int rk_disp_pwr_ctr_parse_dt(struct rk_lcdc_driver *dev_drv)
     u32 debug = 0;
     int ret;
 
+#ifndef CONFIG_SMART_DUAL_LCD
     INIT_LIST_HEAD(&dev_drv->pwrlist_head);
+#else
+    INIT_LIST_HEAD(rk_screen->pwrlist_head);
+#endif
+
     if (!root) {
+#ifndef CONFIG_SMART_DUAL_LCD
         dev_err(dev_drv->dev, "can't find power_ctr node for lcdc%d\n",
             dev_drv->id);
+#else
+        dev_err(rk_screen->dev, "can't find power_ctr node for lcdc%d\n",
+                rk_screen->lcdc_id);
+#endif
         return -ENODEV;
     }
 
@@ -337,17 +383,29 @@ int rk_disp_pwr_ctr_parse_dt(struct rk_lcdc_driver *dev_drv)
                 pwr_ctr->pwr_ctr.type = GPIO;
                 pwr_ctr->pwr_ctr.gpio = of_get_gpio_flags(child, 0, &flags);
                 if (!gpio_is_valid(pwr_ctr->pwr_ctr.gpio)) {
+#ifndef CONFIG_SMART_DUAL_LCD
                     dev_err(dev_drv->dev, "%s ivalid gpio\n",
                         child->name);
+#else
+                    dev_err(rk_screen->dev, "%s ivalid gpio\n",
+                        child->name);
+#endif
+
                     return -EINVAL;
                 }
                 pwr_ctr->pwr_ctr.atv_val = !(flags & OF_GPIO_ACTIVE_LOW);
                 ret = gpio_request(pwr_ctr->pwr_ctr.gpio,
                            child->name);
                 if (ret) {
+#ifndef CONFIG_SMART_DUAL_LCD
                     dev_err(dev_drv->dev,
                         "request %s gpio fail:%d\n",
                         child->name, ret);
+#else
+                    dev_err(rk_screen->dev,
+                        "request %s gpio fail:%d\n",
+                        child->name, ret);
+#endif
                 }
 
             } else {
@@ -356,7 +414,11 @@ int rk_disp_pwr_ctr_parse_dt(struct rk_lcdc_driver *dev_drv)
                 ret = of_property_read_string(child, "rockchip,regulator_name",
                                   &(pwr_ctr->pwr_ctr.rgl_name));
                 if (ret || IS_ERR_OR_NULL(pwr_ctr->pwr_ctr.rgl_name))
+#ifndef CONFIG_SMART_DUAL_LCD
                     dev_err(dev_drv->dev, "get regulator name failed!\n");
+#else
+                    dev_err(rk_screen->dev, "get regulator name failed!\n");
+#endif
                 if (!of_property_read_u32(child, "rockchip,regulator_voltage", &val))
                     pwr_ctr->pwr_ctr.volt = val;
                 else
@@ -368,13 +430,21 @@ int rk_disp_pwr_ctr_parse_dt(struct rk_lcdc_driver *dev_drv)
             pwr_ctr->pwr_ctr.delay = val;
         else
             pwr_ctr->pwr_ctr.delay = 0;
+#ifndef CONFIG_SMART_DUAL_LCD
         list_add_tail(&pwr_ctr->list, &dev_drv->pwrlist_head);
+#else
+       list_add_tail(&pwr_ctr->list, rk_screen->pwrlist_head);
+#endif
     }
 
     of_property_read_u32(root, "rockchip,debug", &debug);
 
     if (debug) {
+#ifndef CONFIG_SMART_DUAL_LCD
         list_for_each(pos, &dev_drv->pwrlist_head) {
+#else
+       list_for_each(pos, rk_screen->pwrlist_head) {
+#endif
             pwr_ctr = list_entry(pos, struct rk_disp_pwr_ctr_list,
                          list);
             pr_info("pwr_ctr_name:%s\n"
@@ -401,9 +471,25 @@ int rk_disp_pwr_enable(struct rk_lcdc_driver *dev_drv)
     struct regulator *regulator_lcd = NULL;
     int count = 10;
 
+#ifndef CONFIG_SMART_DUAL_LCD
     if (list_empty(&dev_drv->pwrlist_head))
         return 0;
-    list_for_each(pos, &dev_drv->pwrlist_head) {
+#else
+   if (!dev_drv->cur_screen->pwrlist_head) {
+       pr_info("error:  %s, lcdc%d screen pwrlist null\n",
+           __func__, dev_drv->id);
+       return 0;
+   }
+   if (list_empty(dev_drv->cur_screen->pwrlist_head))
+       return 0;
+#endif
+
+#ifndef CONFIG_SMART_DUAL_LCD
+   list_for_each(pos, &dev_drv->pwrlist_head) 
+#else
+   list_for_each(pos, dev_drv->cur_screen->pwrlist_head) 
+#endif
+   {
         pwr_ctr_list = list_entry(pos, struct rk_disp_pwr_ctr_list,
                       list);
         pwr_ctr = &pwr_ctr_list->pwr_ctr;
@@ -446,9 +532,25 @@ int rk_disp_pwr_disable(struct rk_lcdc_driver *dev_drv)
     struct regulator *regulator_lcd = NULL;
     int count = 10;
 
+#ifndef CONFIG_SMART_DUAL_LCD
     if (list_empty(&dev_drv->pwrlist_head))
         return 0;
-    list_for_each(pos, &dev_drv->pwrlist_head) {
+#else
+   if (!dev_drv->cur_screen->pwrlist_head) {
+       pr_info("error:  %s, lcdc%d screen pwrlist null\n",
+           __func__, dev_drv->id);
+       return 0;
+   }
+   if (list_empty(dev_drv->cur_screen->pwrlist_head))
+       return 0;
+#endif
+
+#ifndef CONFIG_SMART_DUAL_LCD
+   list_for_each(pos, &dev_drv->pwrlist_head) 
+#else
+   list_for_each(pos, dev_drv->cur_screen->pwrlist_head) 
+#endif
+   {
         pwr_ctr_list = list_entry(pos, struct rk_disp_pwr_ctr_list,
                       list);
         pwr_ctr = &pwr_ctr_list->pwr_ctr;
@@ -533,7 +635,11 @@ int rk_fb_prase_timing_dt(struct device_node *np, struct rk_screen *screen)
         pr_err("parse display timing err\n");
         return -EINVAL;
     }
+#ifndef CONFIG_SMART_DUAL_LCD
     dt = display_timings_get(disp_timing, disp_timing->native_mode);
+#else
+   dt = display_timings_get(disp_timing, screen->native_mode);
+#endif
     rk_fb_video_mode_from_timing(dt, screen);
 
     return 0;
@@ -1657,8 +1763,14 @@ static void rk_fb_update_win(struct rk_lcdc_driver *dev_drv,
                     reg_win_data->reg_area_data[i].ion_handle;
                 win->area[i].smem_start =
                     reg_win_data->reg_area_data[i].smem_start;
+#ifndef CONFIG_SMART_DUAL_LCD
+               if (inf->disp_mode == DUAL ||
+                   inf->disp_mode == NO_DUAL) {
+#else
                 if (inf->disp_mode == DUAL ||
+                    inf->disp_mode == DUAL_LCD ||
                     inf->disp_mode == NO_DUAL) {
+#endif
                     win->area[i].xpos =
                         reg_win_data->reg_area_data[i].xpos;
                     win->area[i].ypos =
@@ -3884,7 +3996,12 @@ static int rk_fb_alloc_buffer(struct fb_info *fbi)
         win = dev_drv->win[win_id];
 
     if (!strcmp(fbi->fix.id, "fb0")) {
+#ifndef CONFIG_SMART_DUAL_LCD
         fb_mem_size = get_fb_size(dev_drv->reserved_fb);
+#else
+        fb_mem_size = get_fb_size(dev_drv->reserved_fb, dev_drv->cur_screen);
+#endif
+
 #if defined(CONFIG_ION_ROCKCHIP)
         if (rk_fb_alloc_buffer_by_ion(fbi, win, fb_mem_size) < 0)
             return -ENOMEM;
@@ -3906,7 +4023,12 @@ static int rk_fb_alloc_buffer(struct fb_info *fbi)
             struct rk_lcdc_driver *dev_drv_prmry;
             int win_id_prmry;
 
+#ifndef CONFIG_SMART_DUAL_LCD
             fb_mem_size = get_fb_size(dev_drv->reserved_fb);
+#else
+            fb_mem_size = get_fb_size(dev_drv->reserved_fb, dev_drv->cur_screen);
+#endif
+
 #if defined(CONFIG_ION_ROCKCHIP)
             dev_drv_prmry = rk_get_prmry_lcdc_drv();
             if (dev_drv_prmry == NULL)
@@ -4071,6 +4193,7 @@ static int init_lcdc_device_driver(struct rk_fb *rk_fb,
         dev_drv->area_support[i] = 1;
     if (dev_drv->ops->area_support_num)
         dev_drv->ops->area_support_num(dev_drv, dev_drv->area_support);
+#ifndef CONFIG_SMART_DUAL_LCD
     rk_disp_pwr_ctr_parse_dt(dev_drv);
     if (dev_drv->prop == PRMRY) {
         rk_fb_set_prmry_screen(screen);
@@ -4079,6 +4202,11 @@ static int init_lcdc_device_driver(struct rk_fb *rk_fb,
     dev_drv->trsm_ops = rk_fb_trsm_ops_get(screen->type);
     if (dev_drv->prop != PRMRY)
         rk_fb_get_extern_screen(screen);
+#else
+   rk_fb_set_screen(screen, dev_drv->prop);
+   rk_fb_get_screen(screen, dev_drv->prop);
+   dev_drv->trsm_ops = rk_fb_trsm_ops_get(dev_drv->prop);
+#endif
     dev_drv->output_color = screen->color_mode;
 
     return 0;
@@ -4419,13 +4547,28 @@ int rk_fb_register(struct rk_lcdc_driver *dev_drv,
         struct fb_info *extend_fbi = rk_fb->fb[dev_drv->fb_index_base];
 
         extend_fbi->var.pixclock = rk_fb->fb[0]->var.pixclock;
+#ifdef CONFIG_SMART_DUAL_LCD
+        extend_fbi->var.xres_virtual = rk_fb->fb[0]->var.xres_virtual;
+        extend_fbi->var.yres_virtual = rk_fb->fb[0]->var.yres_virtual;
+#endif
         extend_fbi->fbops->fb_open(extend_fbi, 1);
         if (dev_drv->iommu_enabled) {
             if (dev_drv->mmu_dev)
                 rockchip_iovmm_set_fault_handler(dev_drv->dev,
                                  rk_fb_sysmmu_fault_handler);
+#ifdef CONFIG_SMART_DUAL_LCD
+           if (dev_drv->ops->mmu_en)
+               dev_drv->ops->mmu_en(dev_drv);
+#endif
         }
+
         rk_fb_alloc_buffer(extend_fbi);
+#ifdef CONFIG_SMART_DUAL_LCD
+       if (rk_fb->disp_mode == DUAL_LCD) {
+           extend_fbi->fbops->fb_set_par(extend_fbi);
+           extend_fbi->fbops->fb_pan_display(&extend_fbi->var, extend_fbi);
+       }
+#endif
     }
 #endif
     return 0;
diff --git a/drivers/video/rockchip/screen/rk_screen.c b/drivers/video/rockchip/screen/rk_screen.c
index 11ff587..9b1ebc5 100755
--- a/drivers/video/rockchip/screen/rk_screen.c
+++ b/drivers/video/rockchip/screen/rk_screen.c
@@ -4,14 +4,33 @@
 #include "lcd.h"
 #include "../hdmi/rockchip-hdmi.h"
 
+#ifndef CONFIG_SMART_DUAL_LCD
 static struct rk_screen *rk_screen;
+#else
+static struct rk_screen *prmry_screen;
+static struct rk_screen *extend_screen;
+
+static void rk_screen_info_error(struct rk_screen *screen, int prop)
+{
+    pr_err(">>>>>>>>>>>>>>>>>>>>error<<<<<<<<<<<<<<<<<<<<\n");
+    pr_err(">>please init %s screen info in dtsi file<<\n",
+            (prop == PRMRY) ? "prmry" : "extend");
+    pr_err(">>>>>>>>>>>>>>>>>>>>error<<<<<<<<<<<<<<<<<<<<\n");
+}
+#endif
 
 int rk_fb_get_extern_screen(struct rk_screen *screen)
 {
+#ifndef CONFIG_SMART_DUAL_LCD
     if (unlikely(!rk_screen) || unlikely(!screen))
         return -1;
-
     memcpy(screen, rk_screen, sizeof(struct rk_screen));
+#else
+   if (unlikely(!extend_screen) || unlikely(!screen))
+       return -1;
+   memcpy(screen, extend_screen, sizeof(struct rk_screen));
+#endif
+   
     screen->dsp_lut = NULL;
     screen->cabc_lut = NULL;
     screen->type = SCREEN_NULL;
@@ -21,13 +40,21 @@ int rk_fb_get_extern_screen(struct rk_screen *screen)
 
 int  rk_fb_get_prmry_screen(struct rk_screen *screen)
 {
+#ifndef CONFIG_SMART_DUAL_LCD
     if (unlikely(!rk_screen) || unlikely(!screen))
         return -1;
 
     memcpy(screen, rk_screen, sizeof(struct rk_screen));
+#else
+   if (unlikely(!prmry_screen) || unlikely(!screen))
+       return -1;
+   memcpy(screen, prmry_screen, sizeof(struct rk_screen));
+
+#endif
     return 0;
 }
 
+#ifndef CONFIG_SMART_DUAL_LCD
 int rk_fb_set_prmry_screen(struct rk_screen *screen)
 {
     if (unlikely(!rk_screen) || unlikely(!screen))
@@ -43,19 +70,87 @@ int rk_fb_set_prmry_screen(struct rk_screen *screen)
     rk_screen->overscan.bottom = screen->overscan.left;
     return 0;
 }
+#else
+int rk_fb_get_screen(struct rk_screen *screen, int prop)
+{
+   struct rk_screen *cur_screen = NULL;
+   
+   if (unlikely(!screen))
+       return -1;
+   if (prop == PRMRY) {
+       if (unlikely(!prmry_screen)) {
+           rk_screen_info_error(screen, prop);
+           return -1;
+       }
+       cur_screen = prmry_screen;
+   } else {
+       if (unlikely(!extend_screen)) {
+           rk_screen_info_error(screen, prop);
+           return -1;
+       }
+       cur_screen = extend_screen;
+   }
+
+   memcpy(screen, cur_screen, sizeof(struct rk_screen));
+
+   return 0;
+
+}
+int rk_fb_set_screen(struct rk_screen *screen, int prop)
+{
+    struct rk_screen *cur_screen = NULL;
+
+    if (unlikely(!screen))
+        return -1;
+    if (prop == PRMRY) {
+        if (unlikely(!prmry_screen)) {
+            rk_screen_info_error(screen, prop);
+            return -1;
+        }
+        cur_screen = prmry_screen;
+    } else {
+        if (unlikely(!extend_screen)) {
+            rk_screen_info_error(screen, prop);
+            return -1;
+        }
+        cur_screen = extend_screen;
+    }
+
+    cur_screen->lcdc_id = screen->lcdc_id;
+    cur_screen->screen_id = screen->screen_id;
+    cur_screen->x_mirror = screen->x_mirror;
+    cur_screen->y_mirror = screen->y_mirror;
+    cur_screen->overscan.left = screen->overscan.left;
+    cur_screen->overscan.top = screen->overscan.left;
+    cur_screen->overscan.right = screen->overscan.left;
+    cur_screen->overscan.bottom = screen->overscan.left;
+
+    return 0;
+}
 
+#endif
+
+#ifndef CONFIG_SMART_DUAL_LCD
 size_t get_fb_size(u8 reserved_fb)
+#else
+size_t get_fb_size(u8 reserved_fb, struct rk_screen *screen)
+#endif
 {
     size_t size = 0;
     u32 xres = 0;
     u32 yres = 0;
 
+#ifndef CONFIG_SMART_DUAL_LCD
     if (unlikely(!rk_screen))
         return 0;
-
     xres = rk_screen->mode.xres;
     yres = rk_screen->mode.yres;
-
+#else
+   if (unlikely(!screen))
+       return 0;
+   xres = screen->mode.xres;
+   yres = screen->mode.yres;
+#endif
     /* align as 64 bytes(16*4) in an odd number of times */
     xres = ALIGN_64BYTE_ODD_TIMES(xres, ALIGN_PIXEL_64BYTE_RGB8888);
     if (reserved_fb == ONE_FB_BUFFER)
@@ -75,12 +170,19 @@ size_t get_fb_size(u8 reserved_fb)
 static int rk_screen_probe(struct platform_device *pdev)
 {
     struct device_node *np = pdev->dev.of_node;
-    int ret;
 
+#ifndef CONFIG_SMART_DUAL_LCD
+   int ret;
+#else
+   struct device_node *screen_np;
+   struct rk_screen *rk_screen;
+   int ret, screen_prop;
+#endif
     if (!np) {
         dev_err(&pdev->dev, "Missing device tree node.\n");
         return -EINVAL;
     }
+#ifndef CONFIG_SMART_DUAL_LCD
     rk_screen = devm_kzalloc(&pdev->dev,
             sizeof(struct rk_screen), GFP_KERNEL);
     if (!rk_screen) {
@@ -91,6 +193,43 @@ static int rk_screen_probe(struct platform_device *pdev)
     dev_info(&pdev->dev, "rockchip screen probe %s\n",
                 ret ? "failed" : "success");
     return ret;
+#else
+   for_each_child_of_node(np, screen_np) {
+       rk_screen = devm_kzalloc(&pdev->dev,
+                    sizeof(struct rk_screen), GFP_KERNEL);
+       if (!rk_screen) {
+           dev_err(&pdev->dev, "kmalloc for rk screen fail!");
+           return  -ENOMEM;
+       }
+       rk_screen->pwrlist_head = devm_kzalloc(&pdev->dev,
+               sizeof(struct list_head), GFP_KERNEL);
+       if (!rk_screen->pwrlist_head) {
+           dev_err(&pdev->dev, "kmalloc for rk_screen pwrlist_head fail!");
+           return  -ENOMEM;
+       }
+       of_property_read_u32(screen_np, "screen_prop", &screen_prop);
+       if (screen_prop == PRMRY)
+           prmry_screen = rk_screen;
+       else if (screen_prop == EXTEND)
+           extend_screen = rk_screen;
+       else
+           dev_err(&pdev->dev, "unknow screen prop: %d\n",
+               screen_prop);
+       rk_screen->prop = screen_prop;
+       of_property_read_u32(screen_np, "native-mode", &rk_screen->native_mode);
+       rk_screen->dev = &pdev->dev;
+       ret = rk_fb_prase_timing_dt(screen_np, rk_screen);
+       pr_info("%s screen timing parse %s\n",
+           (screen_prop == PRMRY) ? "prmry" : "extend",
+           ret ? "failed" : "success");
+       ret = rk_disp_pwr_ctr_parse_dt(screen_np, rk_screen);
+       pr_info("%s screen power ctrl parse %s\n",
+           (screen_prop == PRMRY) ? "prmry" : "extend",
+           ret ? "failed" : "success");
+   }
+   dev_info(&pdev->dev, "rockchip screen probe success\n");
+   return 0;
+#endif
 }
 
 static const struct of_device_id rk_screen_dt_ids[] = {
diff --git a/drivers/video/rockchip/transmitter/rk32_dp.c b/drivers/video/rockchip/transmitter/rk32_dp.c
index 2b3457c..9ae99cc 100755
--- a/drivers/video/rockchip/transmitter/rk32_dp.c
+++ b/drivers/video/rockchip/transmitter/rk32_dp.c
@@ -119,7 +119,11 @@ static int rk32_edp_init_edp(struct rk32_edp *edp)
     struct rk_screen *screen = &edp->screen;
     u32 val = 0;
 
+#ifndef CONFIG_SMART_DUAL_LCD
     rk_fb_get_prmry_screen(screen);
+#else
+   rk_fb_get_screen(screen, edp->prop);
+#endif
 
     if (cpu_is_rk3288()) {
         if (screen->lcdc_id == 1)  /*select lcdc*/
@@ -1712,17 +1716,25 @@ static int rk32_edp_probe(struct platform_device *pdev)
     struct resource *res;
     struct device_node *np = pdev->dev.of_node;
     int ret;
-
+#ifdef CONFIG_SMART_DUAL_LCD
+   int prop;
+#endif
     if (!np) {
         dev_err(&pdev->dev, "Missing device tree node.\n");
         return -EINVAL;
     }
-
+#ifdef CONFIG_SMART_DUAL_LCD
+   of_property_read_u32(np, "prop", &prop);
+   pr_info("Use EDP as %s screen\n", (prop == PRMRY) ? "primary" : "extend");
+#endif
     edp = devm_kzalloc(&pdev->dev, sizeof(struct rk32_edp), GFP_KERNEL);
     if (!edp) {
         dev_err(&pdev->dev, "no memory for state\n");
         return -ENOMEM;
     }
+#ifdef CONFIG_SMART_DUAL_LCD
+    edp->prop = prop;
+#endif
     edp->dev = &pdev->dev;
     edp->video_info.h_sync_polarity    = 0;
     edp->video_info.v_sync_polarity    = 0;
@@ -1734,7 +1746,11 @@ static int rk32_edp_probe(struct platform_device *pdev)
 
     edp->video_info.link_rate    = LINK_RATE_1_62GBPS;
     edp->video_info.lane_count    = LANE_CNT4;
+#ifndef CONFIG_SMART_DUAL_LCD
     rk_fb_get_prmry_screen(&edp->screen);
+#else
+    rk_fb_get_screen(&edp->screen, prop);
+#endif
     if (edp->screen.type != SCREEN_EDP) {
         dev_err(&pdev->dev, "screen is not edp!\n");
         return -EINVAL;
@@ -1809,7 +1825,11 @@ static int rk32_edp_probe(struct platform_device *pdev)
     if (!support_uboot_display())
         rk32_edp_clk_disable(edp);
     rk32_edp = edp;
+#ifndef CONFIG_SMART_DUAL_LCD
     rk_fb_trsm_ops_register(&trsm_edp_ops, SCREEN_EDP);
+#else
+    rk_fb_trsm_ops_register(&trsm_edp_ops, prop);
+#endif
 #if defined(CONFIG_DEBUG_FS)
     edp->debugfs_dir = debugfs_create_dir("edp", NULL);
     if (IS_ERR(edp->debugfs_dir)) {
diff --git a/drivers/video/rockchip/transmitter/rk32_dp.h b/drivers/video/rockchip/transmitter/rk32_dp.h
index 08347b5..cd1b3d9 100755
--- a/drivers/video/rockchip/transmitter/rk32_dp.h
+++ b/drivers/video/rockchip/transmitter/rk32_dp.h
@@ -566,6 +566,9 @@ struct rk32_edp {
     bool clk_on;
     bool edp_en;
     struct dentry *debugfs_dir;
+#ifdef CONFIG_SMART_DUAL_LCD
+    int prop;
+#endif
 };
 
 
@@ -663,4 +666,4 @@ int rk32_edp_wait_hw_lt_done(struct rk32_edp *edp);
 enum dp_irq_type rk32_edp_get_irq_type(struct rk32_edp *edp);
 void rk32_edp_clear_hotplug_interrupts(struct rk32_edp *edp);
 
-#endif
\ No newline at end of file
+#endif
diff --git a/drivers/video/rockchip/transmitter/rk32_lvds.c b/drivers/video/rockchip/transmitter/rk32_lvds.c
index d0a8b7a..433b858 100644
--- a/drivers/video/rockchip/transmitter/rk32_lvds.c
+++ b/drivers/video/rockchip/transmitter/rk32_lvds.c
@@ -59,8 +59,11 @@ static int rk32_lvds_en(void)
     u32 h_bp = 0;
     u32 val = 0;
 
+#ifndef CONFIG_SMART_DUAL_LCD
     rk_fb_get_prmry_screen(screen);
-
+#else
+    rk_fb_get_screen(screen, lvds->prop);
+#endif
     /* enable clk */
     rk32_lvds_clk_enable(lvds);
 
@@ -141,18 +144,29 @@ static int rk32_lvds_probe(struct platform_device *pdev)
     struct resource *res;
     struct device_node *np = pdev->dev.of_node;
 
+#ifdef CONFIG_SMART_DUAL_LCD
+   int prop;
+#endif
     if (!np) {
         dev_err(&pdev->dev, "Missing device tree node.\n");
         return -EINVAL;
     }
 
+#ifdef CONFIG_SMART_DUAL_LCD
+   of_property_read_u32(np, "prop", &prop);
+   pr_info("Use LVDS as %s screen\n", (prop == PRMRY) ? "prmry":"extend");
+#endif
     lvds = devm_kzalloc(&pdev->dev, sizeof(struct rk32_lvds), GFP_KERNEL);
     if (!lvds) {
         dev_err(&pdev->dev, "no memory for state\n");
         return -ENOMEM;
     }
     lvds->dev = &pdev->dev;
+#ifndef CONFIG_SMART_DUAL_LCD
     rk_fb_get_prmry_screen(&lvds->screen);
+#else
+   rk_fb_get_screen(&lvds->screen, prop);
+#endif
     if ((lvds->screen.type != SCREEN_RGB) && 
         (lvds->screen.type != SCREEN_LVDS) &&
         (lvds->screen.type != SCREEN_DUAL_LVDS) &&
@@ -185,7 +199,12 @@ static int rk32_lvds_probe(struct platform_device *pdev)
     }
 
     rk32_lvds = lvds;
+#ifndef CONFIG_SMART_DUAL_LCD
     rk_fb_trsm_ops_register(&trsm_lvds_ops,SCREEN_LVDS);
+#else
+   lvds->prop = prop;
+   rk_fb_trsm_ops_register(&trsm_lvds_ops, prop);
+#endif
     dev_info(&pdev->dev, "rk32 lvds driver probe success\n");
 
     return 0;
diff --git a/drivers/video/rockchip/transmitter/rk32_lvds.h b/drivers/video/rockchip/transmitter/rk32_lvds.h
index ca424a7..6dec093 100755
--- a/drivers/video/rockchip/transmitter/rk32_lvds.h
+++ b/drivers/video/rockchip/transmitter/rk32_lvds.h
@@ -34,6 +34,9 @@ struct rk32_lvds {
     struct clk              *pd;
     struct rk_screen    screen;
     bool            clk_on;
+#ifdef CONFIG_SMART_DUAL_LCD
+   int prop;
+#endif
 };
 
 static int inline lvds_writel(struct rk32_lvds *lvds, u32 offset, u32 val)
diff --git a/include/dt-bindings/rkfb/rk_fb.h b/include/dt-bindings/rkfb/rk_fb.h
index 6d75770..8b2ac5b 100755
--- a/include/dt-bindings/rkfb/rk_fb.h
+++ b/include/dt-bindings/rkfb/rk_fb.h
@@ -12,6 +12,9 @@
 #define NO_DUAL        0
 #define ONE_DUAL    1
 #define DUAL        2
+#define DUAL_LCD    3
+#define DEFAULT_MODE    0
+
 /********************************************************************
 **          display output interface supported by rockchip       **
 ********************************************************************/
diff --git a/include/linux/rk_fb.h b/include/linux/rk_fb.h
index 62647a9..386cd14 100755
--- a/include/linux/rk_fb.h
+++ b/include/linux/rk_fb.h
@@ -808,11 +808,25 @@ extern int rk_fb_register(struct rk_lcdc_driver *dev_drv,
                 struct rk_lcdc_win *win, int id);
 extern int rk_fb_unregister(struct rk_lcdc_driver *dev_drv);
 extern struct rk_lcdc_driver *rk_get_lcdc_drv(char *name);
+#ifndef CONFIG_SMART_DUAL_LCD
 extern int rk_fb_get_extern_screen(struct rk_screen *screen);
+#endif
 extern int rk_fb_get_prmry_screen( struct rk_screen *screen);
+#ifndef CONFIG_SMART_DUAL_LCD
 extern int rk_fb_set_prmry_screen(struct rk_screen *screen);
+#endif
+
+#ifdef CONFIG_SMART_DUAL_LCD
+extern int rk_fb_get_screen(struct rk_screen *screen, int prop);
+extern int rk_fb_set_screen(struct rk_screen *screen, int prop);
+#endif
+
 extern u32 rk_fb_get_prmry_screen_pixclock(void);
+#ifndef CONFIG_SMART_DUAL_LCD
 extern int rk_disp_pwr_ctr_parse_dt(struct rk_lcdc_driver *dev_drv);
+#else
+extern int rk_disp_pwr_ctr_parse_dt(struct device_node *np, struct rk_screen *rk_screen);
+#endif
 extern int rk_disp_pwr_enable(struct rk_lcdc_driver *dev_drv);
 extern int rk_disp_pwr_disable(struct rk_lcdc_driver *dev_drv);
 extern bool is_prmry_rk_lcdc_registered(void);
diff --git a/include/linux/rk_screen.h b/include/linux/rk_screen.h
index af0ffe7..a78f3ae 100644
--- a/include/linux/rk_screen.h
+++ b/include/linux/rk_screen.h
@@ -61,6 +61,13 @@ struct overscan {
 *ft: the time need to display one frame time
 */
 struct rk_screen {
+#ifdef CONFIG_SMART_DUAL_LCD
+   struct device   *dev;
+   int prop;
+   struct list_head *pwrlist_head;
+   int native_mode;
+#endif
+
     u16 type;
     u16 lvds_format; 
     u16 face;
@@ -144,8 +151,12 @@ struct rk29fb_info {
 };
 
 extern void set_lcd_info(struct rk_screen *screen, struct rk29lcd_info *lcd_info);
+#ifndef CONFIG_SMART_DUAL_LCD
 extern size_t get_fb_size(u8 reserved_fb);
-
+#else
+extern size_t get_fb_size(u8 reserved_fb, struct rk_screen *screen);
+extern size_t get_rotate_fb_size(struct rk_screen *screen);
+#endif
 extern void set_tv_info(struct rk_screen *screen);
 extern void set_hdmi_info(struct rk_screen *screen);
 

驱rk3288-ubuntu1404-backup-20210618-rootfs.img动部分花费时间最长,调试许久才成功,驱动调好后,两个屏幕就可以同时显示了,主屏一切正常,但是副屏会花屏。

android system

为了解决花屏的问题,继续探索,然后看到这篇文章:RK3288 增加双屏异显 eDP+LVDS

这篇文章是在 android 5.1 基础上支持双屏异显,可以参考下,最后发现 5.1 6.0 的 framework 部分差异较大,无法使用文章中的 patch, 索性不加了,但是其它部分改动不大,引入后重新编译系统,居然可以正常启动了,花屏问题也不存在了。下面给出Android系统层面的改动。

device hwcomposer

  • hardware/rockchip/hwcomposer
diff --git a/rk_hwcomposer.cpp b/rk_hwcomposer.cpp
index d36b4b8..2266fcb 100755
--- a/rk_hwcomposer.cpp
+++ b/rk_hwcomposer.cpp
@@ -79,6 +79,7 @@ static int  hwc_free_buffer(buffer_handle_t hnd);
 
 int         hwc_sprite_replace(hwcContext * Context, hwc_display_contents_1_t * list);
 void*       hwc_control_3dmode_thread(void *arg);
+int         hwc_parse_screen_info(int *outX, int *outY);
 
 void*   hotplug_try_register(void *arg);
 void    hotplug_get_resolution(int* w,int* h);
@@ -11917,6 +11918,10 @@ int hotplug_get_config(int flag){
     int outX = 0;
     int outY = 0;
     hotplug_parse_mode(&outX, &outY);
+    if (hwc_get_int_property("ro.htg.force", "0"))
+        hwc_parse_screen_info(&outX, &outY);
+    else
+        hotplug_parse_mode(&outX, &outY);
     info.xres = outX;
     info.yres = outY;
     info.yres_virtual = info.yres * 3;
@@ -12242,6 +12247,29 @@ OnError:
 
 }
 
+int hwc_parse_screen_info(int *outX, int *outY)
+{
+    char buf[100];
+    int width = 0;
+    int height = 0;
+    int fdExternal = -1;
+    fdExternal = open("/sys/class/graphics/fb4/screen_info", O_RDONLY);
+    if(fdExternal < 0){
+        ALOGE("hotplug_get_config:open fb screen_info error,cvbsfd=%d",fdExternal);
+        return -errno;
+    }
+    if(read(fdExternal,buf,sizeof(buf)) < 0){
+        ALOGE("error reading fb screen_info: %s", strerror(errno));
+        return -1;
+    }
+    close(fdExternal);
+    sscanf(buf,"xres:%d yres:%d",&width,&height);
+    ALOGD("hotplug_get_config:width=%d,height=%d",width,height);
+    *outX = width;
+    *outY = height;
+    return 0;
+}
+
 int hotplug_parse_mode(int *outX, int *outY)
 {
    int fd = open("/sys/class/display/HDMI/mode", O_RDONLY);
@@ -12428,7 +12456,12 @@ void *hotplug_try_register(void *arg)
     if(getHdmiMode() == 1){
         handle_hotplug_event(1, 6);
         ALOGI("hotplug_try_register at line = %d",__LINE__);
-    }else{
+    } else if (hwc_get_int_property("ro.htg.force", "0")) {
+        hotplug_free_dimbuffer();
+        hotplug_get_config(0);
+        handle_hotplug_event(1, 6);
+        ALOGI("hotplug_try_register at line = %d",__LINE__);
+    } else {
 #if (defined(RK3368_BOX) || defined(RK3288_BOX) || defined(RK3399_BOX))
 #if RK3288_BOX
         if(context->mLcdcNum == 1){

/sys/class/graphics/fb0 对应主屏, /sys/class/graphics/fb4 对应副屏。

system.prop

  • device/rockchip/rk3288/system.prop
diff --git a/system.prop b/system.prop
index d3534f3..0938841 100644
--- a/system.prop
+++ b/system.prop
@@ -12,6 +12,7 @@ wifi.interface=wlan0
 #rild.libargs=-d /dev/ttyACM0
 persist.tegra.nvmmlite = 1
 ro.audio.monitorOrientation=true
+ro.htg.force=1
 
 #NFC
 debug.nfc.fw_download=false

Notes

scrcpy

安装 scrcpy 方便在电脑上操作Android 设备.

snap install scrcpy

issues

当 dts 中的显示器参数设置不当时,会出现以下异常。

root@rk3288:/ # dmesg |grep -i edp                                             
[    0.871339] Use EDP as primary screen
[    0.871461] rk32-edp rk32-edp: failed to get reset
[    0.871474] rk32-edp rk32-edp: failed to get reset
[    0.871615] rk32-edp rk32-edp: rk32 edp driver probe success
[    0.894143] rk32-edp rk32-edp: edp pll locked
[    0.951750] rk32-edp rk32-edp: max link rate:0.0Gps max number of lanes:0
[    0.951765] rk32-edp rk32-edp: Rx Mx Link Rate is abnormal:0!default link rate:1.62Gps
[    0.951777] rk32-edp rk32-edp: Rx Max Lane count is abnormal :0 !use default lanes:4
[    0.957711] rk32-edp rk32-edp: hw lt err:1
[    0.957722] rk32-edp rk32-edp: link train failed!

这是 GPIO 接口配置错误情况下会出现的,当然是在排除硬件故障情况下。

reference