ARM: tegra: p1852: Dual-display support for all SKUs
Dongfang Shi [Thu, 3 May 2012 23:40:49 +0000 (16:40 -0700)]
Ported Peter's original change 86413 to main.

board-p1852-panel.c:
Add support for primary and secondary LVDS displays, and secondary HDMI display.

board-p1852-pinmux.c:
Add configuration for HDMI and LVDS

board-p1852.c:
board-p1852.h:
Support for determining which p1852 sku is in use

hdmi.c:If no edid retrieved, but there's a hardwired mode, enable it
(used to support HDMI->LVDS output on p1852 sku 2)

devices.c:added secondary display data.

Bug 977859
Bug 994011

Change-Id: Ide8fb6bf7dd873b1d50269fb98d7c1687e4d9073
Signed-off-by: Dongfang Shi <dshi@nvidia.com>
Reviewed-on: http://git-master/r/100438
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

arch/arm/mach-tegra/board-p1852-panel.c
arch/arm/mach-tegra/board-p1852.c
arch/arm/mach-tegra/board-p1852.h
arch/arm/mach-tegra/devices.c
arch/arm/mach-tegra/devices.h
drivers/video/tegra/dc/hdmi.c

index 4e86476..970133d 100644 (file)
 #include "board.h"
 #include "devices.h"
 #include "tegra3_host1x_devices.h"
+#include "board-p1852.h"
+#include "gpio-names.h"
 
+#define P1852_LVDS_ENA1 TEGRA_GPIO_PV0
+#define P1852_LVDS_ENA2 TEGRA_GPIO_PV1
+#define P1852_HDMI_HPD  TEGRA_GPIO_PN7
+#define P1852_HDMI_RGB  TEGRA_GPIO_PW1
+#define P1852_LVDS_SER1_ADDR 0xd
+#define P1852_LVDS_SER2_ADDR 0xc
+
+
+/* RGB panel requires no special enable/disable */
 static int p1852_panel_enable(void)
 {
        return 0;
@@ -40,6 +51,186 @@ static int p1852_panel_disable(void)
        return 0;
 }
 
+/* enable primary LVDS */
+static int p1852_lvds_enable(void)
+{
+       struct i2c_adapter *adapter;
+       struct i2c_board_info info = {{0}};
+       static struct i2c_client *client;
+       struct i2c_msg msg[2];
+       u8 cmd_buf[] = {0x4, 0};
+       int status=-1;
+
+       /* Turn on serializer chip */
+       gpio_set_value(P1852_LVDS_ENA1, 1);
+
+       /* Program the serializer */
+       adapter = i2c_get_adapter(3);
+       if (!adapter)
+               pr_warning("%s: adapter is null\n", __func__);
+       else {
+               info.addr = P1852_LVDS_SER1_ADDR;
+               if (!client)
+                       client = i2c_new_device(adapter, &info);
+               i2c_put_adapter(adapter);
+               if (!client)
+                       pr_warning("%s: client is null\n", __func__);
+               else {
+                       msg[0].addr = P1852_LVDS_SER1_ADDR;
+                       msg[0].flags = 0;
+                       msg[0].len = 1;
+                       msg[0].buf = &cmd_buf[0];
+
+                       status = i2c_transfer(client->adapter, msg, 1);
+                       /* ignore first write status */
+
+                       msg[0].addr = P1852_LVDS_SER1_ADDR;
+                       msg[0].flags = 0;
+                       msg[0].len = 1;
+                       msg[0].buf = &cmd_buf[0];
+
+                       msg[1].addr = P1852_LVDS_SER1_ADDR;
+                       msg[1].flags = I2C_M_RD;
+                       msg[1].len = 1;
+                       msg[1].buf = &cmd_buf[1];
+
+                       status = i2c_transfer(client->adapter, msg, 2);
+                       if (status < 0) {
+                               pr_warning("%s: i2c failed, addr=0x%x, reg=%d, ret=%d\n",
+                                       __func__, P1852_LVDS_SER1_ADDR, cmd_buf[0], status);
+                       }
+                       else {
+                               cmd_buf[1] |= (1 << 2);
+                               cmd_buf[1] |= (1 << 3);
+
+                               msg[0].addr = P1852_LVDS_SER1_ADDR;
+                               msg[0].flags = 0;
+                               msg[0].len = 2;
+                               msg[0].buf = &cmd_buf[0];
+
+                               status = i2c_transfer(client->adapter, msg, 1);
+                               if (status < 0) {
+                                       pr_warning("%s: i2c err, addr=0x%x, reg=%d, ret=%d\n",
+                                               __func__, P1852_LVDS_SER1_ADDR, cmd_buf[0], status);
+                               }
+                       }
+               }
+       }
+       return (status<0 ? status : 0);
+}
+
+/* Disable primary LVDS */
+static int p1852_lvds_disable(void)
+{
+       /* Turn off serializer chip */
+       gpio_set_value(P1852_LVDS_ENA1, 0);
+
+       return 0;
+}
+
+/* Enable secondary LVDS */
+static int p1852_lvds2_enable(void)
+{
+       struct i2c_adapter *adapter;
+       struct i2c_board_info info = {{0}};
+       static struct i2c_client *client;
+       struct i2c_msg msg[2];
+       u8 cmd_buf[] = {0x4, 0};
+       int status=-1;
+
+       /* Enable HDMI HPD */
+       /* need nothing here */
+
+       /* Turn on HDMI-RGB converter */
+       gpio_set_value(P1852_HDMI_RGB, 1);
+
+       /* Turn on serializer chip */
+       gpio_set_value(P1852_LVDS_ENA2, 1);
+
+       /* Program the serializer */
+       adapter = i2c_get_adapter(3);
+       if (!adapter)
+               pr_warning("%s: adapter is null\n", __func__);
+       else {
+               info.addr = P1852_LVDS_SER2_ADDR;
+               if (!client)
+                       client = i2c_new_device(adapter, &info);
+               i2c_put_adapter(adapter);
+               if (!client)
+                       pr_warning("%s: client is null\n", __func__);
+               else {
+                       msg[0].addr = P1852_LVDS_SER2_ADDR;
+                       msg[0].flags = 0;
+                       msg[0].len = 1;
+                       msg[0].buf = &cmd_buf[0];
+
+                       status = i2c_transfer(client->adapter, msg, 1);
+                       /* ignore first write status */
+
+                       msg[0].addr = P1852_LVDS_SER2_ADDR;
+                       msg[0].flags = 0;
+                       msg[0].len = 1;
+                       msg[0].buf = &cmd_buf[0];
+
+                       msg[1].addr = P1852_LVDS_SER2_ADDR;
+                       msg[1].flags = I2C_M_RD;
+                       msg[1].len = 1;
+                       msg[1].buf = &cmd_buf[1];
+
+                       status = i2c_transfer(client->adapter, msg, 2);
+                       if (status < 0) {
+                               pr_warning("%s: i2c failed, addr=0x%x, reg=%d, ret=%d\n",
+                                       __func__, P1852_LVDS_SER2_ADDR, cmd_buf[0], status);
+                       }
+                       else {
+                               cmd_buf[1] |= (1 << 2);
+                               cmd_buf[1] |= (1 << 3);
+
+                               msg[0].addr = P1852_LVDS_SER2_ADDR;
+                               msg[0].flags = 0;
+                               msg[0].len = 2;
+                               msg[0].buf = &cmd_buf[0];
+
+                               status = i2c_transfer(client->adapter, msg, 1);
+                               if (status < 0) {
+                                       pr_warning("%s: i2c err, addr=0x%x, reg=%d, ret=%d\n",
+                                               __func__, P1852_LVDS_SER2_ADDR, cmd_buf[0], status);
+                               }
+                       }
+               }
+       }
+       return (status<0 ? status : 0);
+}
+
+/* Disable secondary LVDS */
+static int p1852_lvds2_disable(void)
+{
+       /* Turn off serializer chip */
+       gpio_set_value(P1852_LVDS_ENA2, 0);
+
+       /* Turn off HDMI-RGB converter */
+       gpio_set_value(P1852_HDMI_RGB, 0);
+
+       /* Turn off HDMI */
+       /* need nothing here */
+
+       return 0;
+}
+
+/* Enable secondary HDMI */
+static int p1852_hdmi_enable(void)
+{
+       /* need nothing here */
+       return 0;
+}
+
+/* Disable secondary HDMI */
+static int p1852_hdmi_disable(void)
+{
+       /* need nothing here */
+       return 0;
+}
+
 #ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT
 
 static struct tegra_dc_mode p1852_panel_modes[] = {
@@ -123,9 +314,42 @@ static struct tegra_fb_data p1852_fb_data = {
 
 #endif
 
+/* Mode data for secondary LVDS out */
+static struct tegra_dc_mode p1852_hdmi_lvds_modes[] = {
+       {
+               /* 800x480@60 */
+               .pclk = 33260000,
+               .h_ref_to_sync = 1,
+               .v_ref_to_sync = 1,
+               .h_sync_width = 64,
+               .v_sync_width = 3,
+               .h_back_porch = 128,
+               .v_back_porch = 38,
+               .h_front_porch = 64,
+               .v_front_porch = 4,
+               .h_active = 800,
+               .v_active = 480,
+       },
+};
+
+static struct tegra_fb_data p1852_hdmi_fb_data = {
+       .win            = 0,
+       .xres           = 800,
+       .yres           = 480,
+       .bits_per_pixel = 32,
+       .flags          = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+/* Start of DC_OUT data
+ *  disp1 = Primary RGB out
+ *  ser1  = Primary LVDS out
+ *  ser2  = Secondary LVDS out
+ *  hdmi  = Secondary HDMI out
+ */
 static struct tegra_dc_out p1852_disp1_out = {
        .align          = TEGRA_DC_ALIGN_MSB,
        .order          = TEGRA_DC_ORDER_RED_BLUE,
+       .parent_clk = "pll_d_out0",
        .type           = TEGRA_DC_OUT_RGB,
        .modes          = p1852_panel_modes,
        .n_modes        = ARRAY_SIZE(p1852_panel_modes),
@@ -133,6 +357,51 @@ static struct tegra_dc_out p1852_disp1_out = {
        .disable        = p1852_panel_disable,
 };
 
+static struct tegra_dc_out p1852_ser1_out = {
+       .align          = TEGRA_DC_ALIGN_MSB,
+       .order          = TEGRA_DC_ORDER_RED_BLUE,
+       .parent_clk     = "pll_d_out0",
+       .type           = TEGRA_DC_OUT_RGB,
+       .modes          = p1852_panel_modes,
+       .n_modes        = ARRAY_SIZE(p1852_panel_modes),
+       .enable         = p1852_lvds_enable,
+       .disable        = p1852_lvds_disable,
+};
+
+static struct tegra_dc_out p1852_ser2_out = {
+       .align          = TEGRA_DC_ALIGN_MSB,
+       .order          = TEGRA_DC_ORDER_RED_BLUE,
+       .parent_clk     = "pll_d2_out0",
+       .type           = TEGRA_DC_OUT_HDMI,
+       .flags          = TEGRA_DC_OUT_HOTPLUG_LOW |
+                         TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND,
+       .max_pixclock   = KHZ2PICOS(148500),
+       .hotplug_gpio   = P1852_HDMI_HPD,
+       .modes          = p1852_hdmi_lvds_modes,
+       .n_modes        = ARRAY_SIZE(p1852_hdmi_lvds_modes),
+       .enable         = p1852_lvds2_enable,
+       .disable        = p1852_lvds2_disable,
+       .dcc_bus        = 3,
+};
+
+static struct tegra_dc_out p1852_hdmi_out = {
+       .align          = TEGRA_DC_ALIGN_MSB,
+       .order          = TEGRA_DC_ORDER_RED_BLUE,
+       .parent_clk     = "pll_d2_out0",
+       .type           = TEGRA_DC_OUT_HDMI,
+       .flags          = TEGRA_DC_OUT_HOTPLUG_LOW |
+                         TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND,
+       .max_pixclock   = KHZ2PICOS(148500),
+       .hotplug_gpio   = P1852_HDMI_HPD,
+       .enable         = p1852_hdmi_enable,
+       .disable        = p1852_hdmi_disable,
+
+       .dcc_bus        = 1,
+};
+
+/* End of DC_OUT data */
+
+/* Start of platform data */
 static struct tegra_dc_platform_data p1852_disp1_pdata = {
        .flags          = TEGRA_DC_FLAG_ENABLED,
        .default_out    = &p1852_disp1_out,
@@ -140,6 +409,29 @@ static struct tegra_dc_platform_data p1852_disp1_pdata = {
        .fb             = &p1852_fb_data,
 };
 
+static struct tegra_dc_platform_data p1852_ser1_pdata = {
+       .flags          = TEGRA_DC_FLAG_ENABLED,
+       .default_out    = &p1852_ser1_out,
+       .emc_clk_rate   = 300000000,
+       .fb             = &p1852_fb_data,
+};
+
+static struct tegra_dc_platform_data p1852_ser2_pdata = {
+       .flags          = TEGRA_DC_FLAG_ENABLED,
+       .default_out    = &p1852_ser2_out,
+       .emc_clk_rate   = 300000000,
+       .fb             = &p1852_hdmi_fb_data,
+};
+
+static struct tegra_dc_platform_data p1852_hdmi_pdata = {
+       .flags          = TEGRA_DC_FLAG_ENABLED,
+       .default_out    = &p1852_hdmi_out,
+       .emc_clk_rate   = 300000000,
+       .fb             = &p1852_hdmi_fb_data,
+};
+
+/* End of platform data */
+
 static struct nvmap_platform_carveout p1852_carveouts[] = {
        [0] = {
                .name           = "iram",
@@ -166,7 +458,7 @@ static struct platform_device *p1852_gfx_devices[] __initdata = {
        &tegra_nvmap_device,
 };
 
-int __init p1852_panel_init(void)
+static int __init p1852_sku2_panel_init(void)
 {
        int err;
        struct resource *res;
@@ -174,16 +466,62 @@ int __init p1852_panel_init(void)
        p1852_carveouts[1].base = tegra_carveout_start;
        p1852_carveouts[1].size = tegra_carveout_size;
        tegra_nvmap_device.dev.platform_data = &p1852_nvmap_data;
-       tegra_disp1_device.dev.platform_data = &p1852_disp1_pdata;
+       /*
+        * sku2 has primary LVDS out and secondary LVDS out
+        * (via HDMI->RGB->Serializer)
+        */
+       tegra_disp1_device.dev.platform_data = &p1852_ser1_pdata;
+       tegra_disp2_device.dev.platform_data = &p1852_ser2_pdata;
 
+#ifdef CONFIG_TEGRA_GRHOST
+       err = tegra3_register_host1x_devices();
+       if (err)
+               return err;
+#endif
+
+       err = platform_add_devices(p1852_gfx_devices,
+                               ARRAY_SIZE(p1852_gfx_devices));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
        res = nvhost_get_resource_byname(&tegra_disp1_device,
                                         IORESOURCE_MEM, "fbmem");
-       if (!res) {
-               pr_err("No memory resources\n");
-               return -ENODEV;
+       if (res) {
+               res->start = tegra_fb_start;
+               res->end = tegra_fb_start + tegra_fb_size - 1;
+       }
+
+       if (!err)
+               err = nvhost_device_register(&tegra_disp1_device);
+
+       res = nvhost_get_resource_byname(&tegra_disp2_device,
+                                        IORESOURCE_MEM, "fbmem");
+       if (res) {
+               res->start = tegra_fb2_start;
+               res->end = tegra_fb2_start + tegra_fb2_size - 1;
        }
-       res->start = tegra_fb_start;
-       res->end = tegra_fb_start + tegra_fb_size - 1;
+
+       if (!err)
+               err = nvhost_device_register(&tegra_disp2_device);
+#endif
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_NVAVP)
+       if (!err)
+               err = nvhost_device_register(&nvavp_device);
+#endif
+       return err;
+}
+
+static int __init p1852_sku8_panel_init(void)
+{
+       int err;
+       struct resource *res;
+
+       p1852_carveouts[1].base = tegra_carveout_start;
+       p1852_carveouts[1].size = tegra_carveout_size;
+       tegra_nvmap_device.dev.platform_data = &p1852_nvmap_data;
+       /* sku 8 has primary RGB out and secondary HDMI out */
+       tegra_disp1_device.dev.platform_data = &p1852_disp1_pdata;
+       tegra_disp2_device.dev.platform_data = &p1852_hdmi_pdata;
 
 #ifdef CONFIG_TEGRA_GRHOST
        err = tegra3_register_host1x_devices();
@@ -193,12 +531,50 @@ int __init p1852_panel_init(void)
 
        err = platform_add_devices(p1852_gfx_devices,
                                ARRAY_SIZE(p1852_gfx_devices));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+       res = nvhost_get_resource_byname(&tegra_disp1_device,
+                                        IORESOURCE_MEM, "fbmem");
+       if (res) {
+               res->start = tegra_fb_start;
+               res->end = tegra_fb_start + tegra_fb_size - 1;
+       }
+
        if (!err)
                err = nvhost_device_register(&tegra_disp1_device);
 
+       res = nvhost_get_resource_byname(&tegra_disp2_device,
+                                        IORESOURCE_MEM, "fbmem");
+       if (res) {
+               res->start = tegra_fb2_start;
+               res->end = tegra_fb2_start + tegra_fb2_size - 1;
+       }
+
+       if (!err)
+               err = nvhost_device_register(&tegra_disp2_device);
+#endif
+
 #if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_NVAVP)
        if (!err)
                err = nvhost_device_register(&nvavp_device);
 #endif
        return err;
 }
+
+int __init p1852_panel_init(void)
+{
+       int skuid;
+
+       skuid = p1852_get_skuid();
+
+       switch (skuid) {
+       case 2:
+               return p1852_sku2_panel_init();
+       case 5: /* Sku 5 display is same as 8 */
+       case 8:
+               return p1852_sku8_panel_init();
+       default:
+               pr_warning("%s: unknown skuid %d\n", __func__, skuid);
+               return 1;
+       }
+}
index bc5e9a2..70a6eb9 100644 (file)
@@ -611,12 +611,29 @@ static void __init tegra_p1852_init(void)
 static void __init tegra_p1852_reserve(void)
 {
 #if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM)
-       tegra_reserve(0, SZ_8M, 0);
+       tegra_reserve(0, SZ_8M, SZ_8M);
 #else
-       tegra_reserve(SZ_128M, SZ_8M, 0);
+       tegra_reserve(SZ_128M, SZ_8M, SZ_8M);
 #endif
 }
 
+int p1852_get_skuid()
+{
+       switch (system_rev) {
+       case TEGRA_P1852_SKU2_A00:
+       case TEGRA_P1852_SKU2_B00:
+               return 2;
+       case TEGRA_P1852_SKU5_A00:
+       case TEGRA_P1852_SKU5_B00:
+               return 5;
+       case TEGRA_P1852_SKU8_A00:
+       case TEGRA_P1852_SKU8_B00:
+               return 8;
+       default:
+               return -1;
+       }
+}
+
 MACHINE_START(P1852, "p1852")
        .atag_offset    = 0x100,
        .soc            = &tegra_soc_desc,
index 1ac0968..529bd38 100644 (file)
 
 #define AC_PRESENT_INT         (TPS6591X_INT_GPIO4 + TPS6591X_IRQ_BASE)
 
+/* List of P1852 skus - replicated from core/include/nvmachtypes.h */
+#define TEGRA_P1852_SKU2_A00  0x020000UL
+#define TEGRA_P1852_SKU2_B00  0x020200UL
+#define TEGRA_P1852_SKU5_A00  0x050000UL
+#define TEGRA_P1852_SKU5_B00  0x050200UL
+#define TEGRA_P1852_SKU8_A00  0x080000UL
+#define TEGRA_P1852_SKU8_B00  0x080200UL
+
 int p1852_sdhci_init(void);
 int p1852_pinmux_init(void);
 int p1852_panel_init(void);
 int p1852_gpio_init(void);
 int p1852_pins_state_init(void);
 
+int p1852_get_skuid(void);
+
 #ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT
 #define TOUCH_GPIO_IRQ_ATMEL_T9 TEGRA_GPIO_PEE1
 #define TOUCH_GPIO_RST_ATMEL_T9 TEGRA_GPIO_PW2
 #define TOUCH_BUS_ATMEL_T9  0
 #endif
 
+
 #endif
index cdebce9..16f46bc 100644 (file)
@@ -2055,6 +2055,43 @@ struct nvhost_device tegra_disp1_device = {
        .num_resources  = ARRAY_SIZE(tegra_disp1_resources),
 };
 
+static struct resource tegra_disp2_resources[] = {
+       {
+               .name   = "irq",
+               .start  = INT_DISPLAY_B_GENERAL,
+               .end    = INT_DISPLAY_B_GENERAL,
+               .flags  = IORESOURCE_IRQ,
+       },
+       {
+               .name   = "regs",
+               .start  = TEGRA_DISPLAY2_BASE,
+               .end    = TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .name   = "fbmem",
+               .flags  = IORESOURCE_MEM,
+               .start  = 0,
+               .end    = 0,
+       },
+       {
+               .name   = "hdmi_regs",
+               .start  = TEGRA_HDMI_BASE,
+               .end    = TEGRA_HDMI_BASE + TEGRA_HDMI_SIZE - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+struct nvhost_device tegra_disp2_device = {
+       .name           = "tegradc",
+       .id             = 1,
+       .resource       = tegra_disp2_resources,
+       .num_resources  = ARRAY_SIZE(tegra_disp2_resources),
+       .dev = {
+               .platform_data = 0,
+       },
+};
+
 struct platform_device tegra_nvmap_device = {
        .name   = "tegra-nvmap",
        .id     = -1,
index 48b885e..1eecd39 100644 (file)
@@ -162,6 +162,7 @@ extern struct platform_device debug_uarte_device;
 #endif
 
 extern struct nvhost_device tegra_disp1_device;
+extern struct nvhost_device tegra_disp2_device;
 extern struct platform_device tegra_nvmap_device;
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
 extern struct platform_device tegra_cec_device;
index 9cd6cef..961fc66 100644 (file)
@@ -1371,18 +1371,31 @@ bool tegra_dc_hdmi_detect_test(struct tegra_dc *dc, unsigned char *edid_ptr)
 
        err = tegra_edid_get_monspecs_test(hdmi->edid, &specs, edid_ptr);
        if (err < 0) {
-               dev_err(&dc->ndev->dev, "error reading edid\n");
-               goto fail;
-       }
+               /* Check if there's a hard-wired mode, if so, enable it */
+               if (dc->out->n_modes)
+                       tegra_dc_enable(dc);
+               else {
+                       dev_err(&dc->ndev->dev, "error reading edid\n");
+                       goto fail;
+               }
+#ifdef CONFIG_SWITCH
+               hdmi->hpd_switch.state = 0;
+               switch_set_state(&hdmi->hpd_switch, 1);
+#endif
+               dev_info(&dc->ndev->dev, "display detected\n");
 
-       err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld);
-       if (err < 0) {
-               dev_err(&dc->ndev->dev, "error populating eld\n");
-               goto fail;
-       }
-       hdmi->eld_retrieved = true;
+               dc->connected = true;
+               tegra_dc_ext_process_hotplug(dc->ndev->id);
+       } else {
+               err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld);
+               if (err < 0) {
+                       dev_err(&dc->ndev->dev, "error populating eld\n");
+                       goto fail;
+               }
+               hdmi->eld_retrieved = true;
 
-       tegra_dc_hdmi_detect_config(dc, &specs);
+               tegra_dc_hdmi_detect_config(dc, &specs);
+       }
 
        return true;
 
@@ -1407,18 +1420,30 @@ static bool tegra_dc_hdmi_detect(struct tegra_dc *dc)
 
        err = tegra_edid_get_monspecs(hdmi->edid, &specs);
        if (err < 0) {
-               dev_err(&dc->ndev->dev, "error reading edid\n");
-               goto fail;
-       }
+               if (dc->out->n_modes)
+                       tegra_dc_enable(dc);
+               else {
+                       dev_err(&dc->ndev->dev, "error reading edid\n");
+                       goto fail;
+               }
+#ifdef CONFIG_SWITCH
+               hdmi->hpd_switch.state = 0;
+               switch_set_state(&hdmi->hpd_switch, 1);
+#endif
+               dev_info(&dc->ndev->dev, "display detected\n");
 
-       err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld);
-       if (err < 0) {
-               dev_err(&dc->ndev->dev, "error populating eld\n");
-               goto fail;
-       }
-       hdmi->eld_retrieved = true;
+               dc->connected = true;
+               tegra_dc_ext_process_hotplug(dc->ndev->id);
+       } else {
+               err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld);
+               if (err < 0) {
+                       dev_err(&dc->ndev->dev, "error populating eld\n");
+                       goto fail;
+               }
+               hdmi->eld_retrieved = true;
 
-       tegra_dc_hdmi_detect_config(dc, &specs);
+               tegra_dc_hdmi_detect_config(dc, &specs);
+       }
 
        return true;