tegra: dc: hdmi: Add fallback 720p 60Hz edid
Ivan Raul Guadarrama [Thu, 12 Nov 2015 22:43:45 +0000 (00:43 +0200)]
In case the EDID read procedure fails after all retries,
use a fallback 720p 60Hz edid to provide the default mode.

Bug 200144977

Change-Id: Ic2c422165cf9cb744b7f6521aeb1cd48e3677686
Signed-off-by: Ivan Raul Guadarrama <iguadarrama@nvidia.com>
Reviewed-on: http://git-master/r/832409
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Mitch Luban <mluban@nvidia.com>

drivers/video/tegra/dc/dp.c
drivers/video/tegra/dc/edid.c
drivers/video/tegra/dc/edid.h
drivers/video/tegra/dc/hdmi2.0.c

index a878351..ad2ef61 100644 (file)
@@ -2634,7 +2634,7 @@ static bool tegra_dp_mode_filter(const struct tegra_dc *dc,
         * CTS mandates that if edid is corrupted
         * use fail-safe mode i.e. VGA 640x480@60
         */
-       if (dc->edid->checksum_corrupted)
+       if (dc->edid->errors)
                return (mode->xres == 640 && mode->yres == 480
                        && mode->refresh == 60) ? true : false;
 
index 5a26730..dd17239 100644 (file)
@@ -56,6 +56,42 @@ struct tegra_edid_pvt {
        struct tegra_dc_edid            dc_edid;
 };
 
+/* 720p 60Hz EDID */
+static const char default_720p_edid[256] = {
+       0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+       0x4c, 0x2d, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00,
+       0x2c, 0x0e, 0x01, 0x03, 0x80, 0x59, 0x32, 0x8c,
+       0x0a, 0xe2, 0xbd, 0xa1, 0x5b, 0x4a, 0x98, 0x24,
+       0x15, 0x47, 0x4a, 0x20, 0x00, 0x00, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1d,
+       0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28,
+       0x55, 0x00, 0x75, 0xf2, 0x31, 0x00, 0x00, 0x1e,
+       0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20,
+       0xb8, 0x28, 0x55, 0x40, 0x75, 0xf2, 0x31, 0x00,
+       0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32,
+       0x3d, 0x0f, 0x2e, 0x08, 0x00, 0x0a, 0x20, 0x20,
+       0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+       0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47,
+       0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x81,
+       0x02, 0x03, 0x19, 0x71, 0x46, 0x84, 0x13, 0x05,
+       0x14, 0x03, 0x12, 0x23, 0x09, 0x07, 0x07, 0x83,
+       0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00, 0x10,
+       0x00, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16,
+       0x20, 0x58, 0x2c, 0x25, 0x00, 0x75, 0xf2, 0x31,
+       0x00, 0x00, 0x9e, 0x01, 0x1d, 0x80, 0xd0, 0x72,
+       0x1c, 0x16, 0x20, 0x10, 0x2c, 0x25, 0x80, 0x75,
+       0xf2, 0x31, 0x00, 0x00, 0x9e, 0x8c, 0x0a, 0xd0,
+       0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96,
+       0x00, 0x75, 0xf2, 0x31, 0x00, 0x00, 0x18, 0x8c,
+       0x0a, 0xd0, 0x90, 0x20, 0x40, 0x31, 0x20, 0x0c,
+       0x40, 0x55, 0x00, 0x75, 0xf2, 0x31, 0x00, 0x00,
+       0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca,
+};
+
 #ifdef DEBUG
 static char tegra_edid_dump_buff[16 * 1024];
 
@@ -232,7 +268,7 @@ int tegra_edid_read_block(struct tegra_edid *edid, int block, u8 *data)
        if (last_checksum != 0) {
                u8 checksum = 0;
 
-               edid->checksum_corrupted = 1;
+               edid->errors |= EDID_ERRORS_CHECKSUM_CORRUPTED;
 
                for (i = 0; i < 127; i++)
                        checksum += data[i];
@@ -578,6 +614,7 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs)
        struct tegra_edid_pvt *new_data, *old_data;
        u8 checksum = 0;
        u8 *data;
+       bool use_fallback = false;
 
        new_data = vzalloc(SZ_32K + sizeof(struct tegra_edid_pvt));
        if (!new_data)
@@ -585,7 +622,10 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs)
 
        kref_init(&new_data->refcnt);
 
-       edid->checksum_corrupted = 0;
+       if (edid->errors & EDID_ERRORS_READ_FAILED)
+               use_fallback = true;
+
+       edid->errors = 0;
 
        data = new_data->dc_edid.buf;
 
@@ -599,6 +639,9 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs)
                        ret = -EINVAL;
                        goto fail;
                }
+       } else if (use_fallback) {
+               memcpy(data, default_720p_edid, 128);
+               /* no checksum test needed */
        } else {
                ret = tegra_edid_read_block(edid, 0, data);
                if (ret)
@@ -621,6 +664,13 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs)
 
        extension_blocks = data[0x7e];
 
+       /* verify only one extension block (to stay in bounds) */
+       if (use_fallback && extension_blocks != 1) {
+               pr_err("%s: fallback edid parsing failed\n", __func__);
+               ret = -EINVAL;
+               goto fail;
+       }
+
        for (i = 1; i <= extension_blocks; i++) {
                if (edid->dc->vedid) {
                        memcpy(data + i * 128,
@@ -632,6 +682,10 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs)
                                ret = -EINVAL;
                                goto fail;
                        }
+               } else if (use_fallback) {
+                       /* only one extension block, verified above */
+                       memcpy(data + i * 128,
+                               default_720p_edid + i * 128, 128);
                } else {
                        ret = tegra_edid_read_block(edid, i, data + i * 128);
                        if (ret < 0)
@@ -726,6 +780,8 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs)
                }
        }
 #endif
+       if (use_fallback)
+               edid->errors |= EDID_ERRORS_USING_FALLBACK;
 
        new_data->dc_edid.len = i * 128;
 
index d3f121f..0b4f86f 100644 (file)
@@ -93,6 +93,19 @@ enum {
        EDID_SRC_DT,
 };
 
+/* Flag panel edid checksum is corrupted.
+ * SW fixes checksum before passing on the
+ * edid block to parser. For now just represent checksum
+ * corruption on any of the edid blocks.
+ */
+#define EDID_ERRORS_CHECKSUM_CORRUPTED 0x01
+
+/* Flag edid read failed after all retries. */
+#define EDID_ERRORS_READ_FAILED                0x02
+
+/* Flag fallback edid is in use. */
+#define EDID_ERRORS_USING_FALLBACK     0x04
+
 struct tegra_edid {
        struct tegra_edid_pvt   *data;
 
@@ -100,14 +113,8 @@ struct tegra_edid {
        struct tegra_dc_i2c_ops i2c_ops;
        struct tegra_dc         *dc;
 
-       /*
-        * flag panel edid checksum is corrupted.
-        * SW fixes checksum before passing on the
-        * edid block to parser. For now just represent checksum
-        * corruption on any of the edid blocks.
-        * Field can be extended as bitmap in future.
-        */
-       u8 checksum_corrupted;
+       /* Bitmap to flag EDID reading / parsing error conditions. */
+       u8 errors;
 };
 
 /*
index 596b4f5..bfa9158 100644 (file)
@@ -433,7 +433,17 @@ static int tegra_hdmi_get_mon_spec(struct tegra_hdmi *hdmi)
 
        if (err < 0) {
                dev_err(&hdmi->dc->ndev->dev, "hdmi: edid read failed\n");
-               return err;
+               /* Try to load and parse the fallback edid */
+               hdmi->edid->errors = EDID_ERRORS_READ_FAILED;
+               err = tegra_edid_get_monspecs(hdmi->edid, &hdmi->mon_spec);
+               if (err < 0) {
+                       dev_err(&hdmi->dc->ndev->dev,
+                               "hdmi: parsing fallback edid failed\n");
+                       return err;
+               } else {
+                       dev_err(&hdmi->dc->ndev->dev,
+                               "hdmi: using fallback edid\n");
+               }
        }
 
        hdmi->mon_spec_valid = true;