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
版权声明:本博客所有文章除特殊声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明出处 litreily的博客!