198 lines
6.1 KiB
Diff
198 lines
6.1 KiB
Diff
From: Codex <[email protected]>
|
|
Subject: [PATCH 2/2] video: sprd: select HDMI by sink EDID
|
|
|
|
The LT8912B is a DSI-to-HDMI bridge and does not answer a MIPI DCS panel
|
|
ID transaction. Its HPD status register also reads low before the bridge
|
|
has been fully configured, which caused a connected HDMI sink to be missed.
|
|
|
|
The 2h10 board routes HDMI DDC to AP I2C3. Enable HDMI power, read the
|
|
standard EDID header at address 0x50 using 100 kHz DDC, and select DSI0 only
|
|
when a real HDMI sink responds. Otherwise power the bridge down and continue
|
|
probing the ILI9881 panel on DSI1. HDMI has priority if both are connected.
|
|
---
|
|
arch/arm/dts/lcd/lcd_lt8912b_mipi_hdmi_1080p.dtsi | 9 +-
|
|
arch/arm/dts/uis7885_2h10.dts | 4 +
|
|
drivers/video/sprd/sprd_panel.c | 104 +++++++++++++++++++++-
|
|
3 files changed, 112 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/arch/arm/dts/lcd/lcd_lt8912b_mipi_hdmi_1080p.dtsi b/arch/arm/dts/lcd/lcd_lt8912b_mipi_hdmi_1080p.dtsi
|
|
--- a/arch/arm/dts/lcd/lcd_lt8912b_mipi_hdmi_1080p.dtsi
|
|
+++ b/arch/arm/dts/lcd/lcd_lt8912b_mipi_hdmi_1080p.dtsi
|
|
@@ -17,13 +17,14 @@
|
|
|
|
/* panel interface type: 2:MIPI; 3:SPI */
|
|
sprd,panel-if-type = <2>;
|
|
+ sprd,mipi-port = <0>;
|
|
sprd,video-lp-cmd-enable = <0>;
|
|
sprd,hporch-lp-disable = <1>;
|
|
|
|
- /*----Id register ----*/
|
|
- sprd,lcd-id-register = [F4];
|
|
- sprd,lcd-id-value-len = <1>;
|
|
- sprd,lcd-id-value = [96];
|
|
+ /* LT8912B has no MIPI DCS panel ID; detect the sink by EDID. */
|
|
+ sprd,detect-lt8912b-hdmi;
|
|
+ sprd,hdmi-ddc-i2c-bus = <3>;
|
|
+ sprd,hdmi-ddc-i2c-addr = <0x50>;
|
|
sprd,lcd-id-tokernel = <0x96>;
|
|
|
|
sprd,reset-on-sequence = <1 5>, <0 5>, <1 5>, <0 5>, <1 20>;
|
|
diff --git a/arch/arm/dts/uis7885_2h10.dts b/arch/arm/dts/uis7885_2h10.dts
|
|
--- a/arch/arm/dts/uis7885_2h10.dts
|
|
+++ b/arch/arm/dts/uis7885_2h10.dts
|
|
@@ -190,6 +190,10 @@
|
|
sprd,lcd-avdd-gpio = <&ap_gpio 153 GPIO_ACTIVE_HIGH>;
|
|
sprd,lcd-avee-gpio = <&ap_gpio 178 GPIO_ACTIVE_HIGH>;
|
|
sprd,lcd-reset-gpio = <&ap_gpio 152 GPIO_ACTIVE_HIGH>;
|
|
+
|
|
+ /* LT8912B HDMI bridge power and reset */
|
|
+ sprd,hdmi-power-gpio = <&ap_gpio 99 GPIO_ACTIVE_HIGH>;
|
|
+ sprd,hdmi-reset-gpio = <&ap_gpio 11 GPIO_ACTIVE_HIGH>;
|
|
};
|
|
|
|
lcd-video {
|
|
diff --git a/drivers/video/sprd/sprd_panel.c b/drivers/video/sprd/sprd_panel.c
|
|
--- a/drivers/video/sprd/sprd_panel.c
|
|
+++ b/drivers/video/sprd/sprd_panel.c
|
|
@@ -21,7 +21,10 @@
|
|
#include <fdtdec.h>
|
|
#include <asm/global_data.h>
|
|
#include <dm.h>
|
|
+#include <i2c.h>
|
|
#include <panel.h>
|
|
+#include <linux/delay.h>
|
|
+#include <asm-generic/gpio.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
@@ -30,6 +33,93 @@
|
|
static struct panel_driver lcd_drv;
|
|
struct panel_info panel_device;
|
|
|
|
+static int lt8912b_hdmi_detect(struct udevice *dev, const void *fdt,
|
|
+ int panel_offset)
|
|
+{
|
|
+ static const u8 edid_header[] = {
|
|
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
|
|
+ };
|
|
+ struct gpio_desc power_gpio;
|
|
+ struct gpio_desc reset_gpio;
|
|
+ struct udevice *chip;
|
|
+ u32 bus = 3, addr = 0x50;
|
|
+ u8 header[sizeof(edid_header)];
|
|
+ int retry;
|
|
+ int ret;
|
|
+
|
|
+ fdt_getprop_u32(fdt, panel_offset, "sprd,hdmi-ddc-i2c-bus", &bus);
|
|
+ fdt_getprop_u32(fdt, panel_offset, "sprd,hdmi-ddc-i2c-addr", &addr);
|
|
+
|
|
+ ret = gpio_request_by_name(dev, "sprd,hdmi-power-gpio", 0,
|
|
+ &power_gpio, GPIOD_IS_OUT);
|
|
+ if (ret) {
|
|
+ pr_err("LT8912B: request power gpio failed: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = gpio_request_by_name(dev, "sprd,hdmi-reset-gpio", 0,
|
|
+ &reset_gpio, GPIOD_IS_OUT);
|
|
+ if (ret) {
|
|
+ pr_err("LT8912B: request reset gpio failed: %d\n", ret);
|
|
+ dm_gpio_free(dev, &power_gpio);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dm_gpio_set_value(&power_gpio, 1);
|
|
+ mdelay(5);
|
|
+ dm_gpio_set_value(&reset_gpio, 1);
|
|
+ mdelay(5);
|
|
+ dm_gpio_set_value(&reset_gpio, 0);
|
|
+ mdelay(5);
|
|
+ dm_gpio_set_value(&reset_gpio, 1);
|
|
+ mdelay(100);
|
|
+
|
|
+ ret = i2c_get_chip_for_busnum(bus, addr, 1, &chip);
|
|
+ if (ret) {
|
|
+ pr_err("LT8912B: cannot access HDMI DDC i2c%u addr 0x%x: %d\n",
|
|
+ bus, addr, ret);
|
|
+ goto not_connected;
|
|
+ }
|
|
+
|
|
+ /* EDID/DDC is standard-mode I2C. Some sinks reject a 400 kHz read. */
|
|
+ ret = dm_i2c_set_bus_speed(dev_get_parent(chip), 100000);
|
|
+ if (ret) {
|
|
+ pr_err("LT8912B: cannot set HDMI DDC speed: %d\n", ret);
|
|
+ goto not_connected;
|
|
+ }
|
|
+
|
|
+ for (retry = 0; retry < 3; retry++) {
|
|
+ memset(header, 0, sizeof(header));
|
|
+ ret = dm_i2c_read(chip, 0, header, sizeof(header));
|
|
+ /* The sprd I2C driver returns the transferred message count. */
|
|
+ if (ret >= 0 &&
|
|
+ !memcmp(header, edid_header, sizeof(edid_header)))
|
|
+ break;
|
|
+ mdelay(20);
|
|
+ }
|
|
+
|
|
+ if (retry == 3) {
|
|
+ pr_info("LT8912B: HDMI sink not detected by EDID "
|
|
+ "(ret=%d, header=%02x %02x %02x %02x %02x %02x %02x %02x)\n",
|
|
+ ret, header[0], header[1], header[2], header[3],
|
|
+ header[4], header[5], header[6], header[7]);
|
|
+ ret = -ENODEV;
|
|
+ goto not_connected;
|
|
+ }
|
|
+
|
|
+ pr_info("LT8912B: HDMI sink detected by EDID\n");
|
|
+ dm_gpio_free(dev, &reset_gpio);
|
|
+ dm_gpio_free(dev, &power_gpio);
|
|
+ return 0;
|
|
+
|
|
+not_connected:
|
|
+ dm_gpio_set_value(&reset_gpio, 0);
|
|
+ dm_gpio_set_value(&power_gpio, 0);
|
|
+ dm_gpio_free(dev, &reset_gpio);
|
|
+ dm_gpio_free(dev, &power_gpio);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
#ifdef CONFIG_BACKLIGHT_I2C
|
|
void set_backlight(int brightness)
|
|
{
|
|
@@ -828,6 +914,7 @@ static int sprd_panel_probe(struct udevice *dev)
|
|
int lcd_panel_offset = 0;
|
|
int info_size;
|
|
bool panel_powered;
|
|
+ bool detect_lt8912b;
|
|
struct udevice *panel_dev = NULL;
|
|
const void *fdt = gd->fdt_blob;
|
|
|
|
@@ -884,13 +971,24 @@ static int sprd_panel_probe(struct udevice *dev)
|
|
of_parse_panel(fdt, offset, info);
|
|
of_parse_timing(fdt, offset, info);
|
|
}
|
|
+ detect_lt8912b = fdt_getprop(fdt, offset,
|
|
+ "sprd,detect-lt8912b-hdmi", NULL) != NULL;
|
|
//get ops
|
|
if(info->mipi_port == 0)
|
|
panel_if_init();
|
|
else
|
|
panel_slave_if_init();
|
|
ops = panel_drv->ops;
|
|
- if (ops && ops->power) {
|
|
+ if (detect_lt8912b) {
|
|
+ ret = lt8912b_hdmi_detect(dev, fdt, offset);
|
|
+ if (ret)
|
|
+ goto panel_probe_failed;
|
|
+
|
|
+ of_parse_initdata(fdt, offset, info);
|
|
+ printf("attach HDMI bridge 0x%x success\n",
|
|
+ info->lcd_id);
|
|
+ break;
|
|
+ } else if (ops && ops->power) {
|
|
printf("enter power panel\n");
|
|
ret = ops->power(dev, true);
|
|
if (ret) {
|
|
--
|
|
2.34.1
|