drm: bridge: dw-hdmi: optimize edid reading process
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_edid.c
index 5d42e20f87521a670f1f9d9475289df7019a96a6..d9f2c0ff760e2ee3ef6f6754fa4451d11d192789 100644 (file)
@@ -144,6 +144,9 @@ static struct edid_quirk {
 
        /* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */
        { "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC },
+
+       /* Rotel RSX-1058 forwards sink's EDID but only does HDMI 1.1*/
+       { "ETR", 13896, EDID_QUIRK_FORCE_8BPC },
 };
 
 /*
@@ -993,6 +996,221 @@ static const struct drm_display_mode edid_cea_modes[] = {
                   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
         .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+       /* 65 - 1280x720@24Hz */
+       { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
+                  3080, 3300, 0, 720, 725, 730, 750, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 66 - 1280x720@25Hz */
+       { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
+                  3740, 3960, 0, 720, 725, 730, 750, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 67 - 1280x720@30Hz */
+       { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
+                  3080, 3300, 0, 720, 725, 730, 750, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 68 - 1280x720@50Hz */
+       { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+                  1760, 1980, 0, 720, 725, 730, 750, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 69 - 1280x720@60Hz */
+       { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+                  1430, 1650, 0, 720, 725, 730, 750, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 70 - 1280x720@100Hz */
+       { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
+                  1760, 1980, 0, 720, 725, 730, 750, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 71 - 1280x720@120Hz */
+       { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
+                  1430, 1650, 0, 720, 725, 730, 750, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 72 - 1920x1080@24Hz */
+       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
+                  2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 73 - 1920x1080@25Hz */
+       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+                  2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 74 - 1920x1080@30Hz */
+       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+                  2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 75 - 1920x1080@50Hz */
+       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+                  2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 76 - 1920x1080@60Hz */
+       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+                  2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+         .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 77 - 1920x1080@100Hz */
+       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
+                  2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+        .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 78 - 1920x1080@120Hz */
+       { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
+                  2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+        .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 79 - 1680x720@24Hz */
+       { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040,
+               3080, 3300, 0, 720, 725, 730, 750, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 80 - 1680x720@25Hz */
+       { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908,
+               2948, 3168, 0, 720, 725, 730, 750, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 81 - 1680x720@30Hz */
+       { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380,
+               2420, 2640, 0, 720, 725, 730, 750, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 82 - 1680x720@50Hz */
+       { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940,
+               1980, 2200, 0, 720, 725, 730, 750, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 83 - 1680x720@60Hz */
+       { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940,
+               1980, 2200, 0, 720, 725, 730, 750, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 84 - 1680x720@100Hz */
+       { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740,
+               1780, 2000, 0, 720, 725, 730, 825, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 85 - 1680x720@120Hz */
+       { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740,
+               1780, 2000, 0, 720, 725, 730, 825, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 86 - 2560x1080@24Hz */
+       { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558,
+               3602, 3750, 0, 1080, 1084, 1089, 1100, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 87 - 2560x1080@25Hz */
+       { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008,
+               3052, 3200, 0, 1080, 1084, 1089, 1125, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 88 - 2560x1080@30Hz */
+       { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328,
+               3372, 3520, 0, 1080, 1084, 1089, 1125, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 89 - 2560x1080@50Hz */
+       { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108,
+               3152, 3300, 0, 1080, 1084, 1089, 1125, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 90 - 2560x1080@60Hz */
+       { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808,
+               2852, 3000, 0, 1080, 1084, 1089, 1100, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 91 - 2560x1080@100Hz */
+       { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778,
+               2822, 2970, 0, 1080, 1084, 1089, 1250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 92 - 2560x1080@120Hz */
+       { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108,
+               3152, 3300, 0, 1080, 1084, 1089, 1250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, },
+       /* 93 - 3840x2160p@24Hz 16:9 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+               5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,},
+       /* 94 - 3840x2160p@25Hz 16:9 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896,
+               4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+       /* 95 - 3840x2160p@30Hz 16:9 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+               4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+       /* 96 - 3840x2160p@50Hz 16:9 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+               4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+       /* 97 - 3840x2160p@60Hz 16:9 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+               4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9},
+       /* 98 - 4096x2160p@24Hz 256:135 */
+       { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116,
+               5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+       /* 99 - 4096x2160p@25Hz 256:135 */
+       { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064,
+               5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+       /* 100 - 4096x2160p@30Hz 256:135 */
+       { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184,
+               4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+       /* 101 - 4096x2160p@50Hz 256:135 */
+       { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064,
+               5152, 5280, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+       /* 102 - 4096x2160p@60Hz 256:135 */
+       { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184,
+               4272, 4400, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135},
+       /* 103 - 3840x2160p@24Hz 64:27 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116,
+               5204, 5500, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+       /* 104 - 3840x2160p@25Hz 64:27 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+               4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+       /* 105 - 3840x2160p@30Hz 64:27 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016,
+               4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+       /* 106 - 3840x2160p@50Hz 64:27 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896,
+               4984, 5280, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
+       /* 107 - 3840x2160p@60Hz 64:27 */
+       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016,
+               4104, 4400, 0, 2160, 2168, 2178, 2250, 0,
+               DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+       .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27},
 };
 
 /*
@@ -1025,12 +1243,6 @@ static const struct drm_display_mode edid_4k_modes[] = {
                   2160, 2168, 2178, 2250, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
          .vrefresh = 24, },
-       /* 5 - 3840x2160@60Hz */
-       { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000,
-                  3840, 4016, 4104, 4400, 0,
-                  2160, 2168, 2178, 2250, 0,
-                  DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
-         .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
 };
 
 /*** DDC fetch and block validation ***/
@@ -2497,6 +2709,8 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
 #define VENDOR_BLOCK    0x03
 #define SPEAKER_BLOCK  0x04
 #define VIDEO_CAPABILITY_BLOCK 0x07
+#define VIDEO_DATA_BLOCK_420   0x0E
+#define VIDEO_CAP_BLOCK_420    0x0F
 #define EDID_BASIC_AUDIO       (1 << 6)
 #define EDID_CEA_YCRCB444      (1 << 5)
 #define EDID_CEA_YCRCB422      (1 << 4)
@@ -2956,6 +3170,99 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
        return modes;
 }
 
+static int add_420_mode(struct drm_connector *connector, u8 vic)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_display_mode *newmode;
+
+       if (!drm_valid_cea_vic(vic))
+               return 0;
+
+       newmode = drm_mode_duplicate(dev, &edid_cea_modes[vic]);
+       if (!newmode)
+               return 0;
+
+       newmode->flags |= DRM_MODE_FLAG_420_ONLY;
+       drm_mode_probed_add(connector, newmode);
+
+       return 1;
+}
+
+static int add_420_vdb_modes(struct drm_connector *connector, const u8 *svds,
+               u8 svds_len)
+{
+       int modes = 0, i;
+
+       for (i = 0; i < svds_len; i++)
+               modes += add_420_mode(connector, svds[i]);
+
+       return modes;
+}
+
+static int add_420_vcb_modes(struct drm_connector *connector, const u8 *svds,
+               u8 svds_len, const u8 *video_db, u8 video_len)
+{
+       struct drm_display_mode *newmode = NULL;
+       int modes = 0, i, j;
+
+       for (i = 0; i < svds_len; i++) {
+               u8 mask = svds[i];
+
+               for (j = 0; j < 8; j++) {
+                       if (mask & (1 << j)) {
+                               newmode = drm_display_mode_from_vic_index(
+                                               connector, video_db, video_len,
+                                               i * 8 + j);
+                               if (newmode) {
+                                       newmode->flags |= DRM_MODE_FLAG_420;
+                                       drm_mode_probed_add(connector, newmode);
+                                       modes++;
+                               }
+                       }
+               }
+       }
+
+       return modes;
+}
+
+static int add_420_vcb_modes_all(struct drm_connector *connector,
+               const u8 *video_db, u8 video_len)
+{
+       struct drm_display_mode *newmode = NULL;
+       int modes = 0, i;
+
+       for (i = 0; i < video_len; i++) {
+               newmode = drm_display_mode_from_vic_index(connector, video_db,
+                               video_len, i);
+               if (newmode) {
+                       newmode->flags |= DRM_MODE_FLAG_420;
+                       drm_mode_probed_add(connector, newmode);
+                       modes++;
+               }
+       }
+
+       return modes;
+}
+
+static int do_hdmi_420_modes(struct drm_connector *connector, const u8 *vdb,
+               u8 vdb_len, const u8 *vcb, u8 vcb_len, const u8 *video_db,
+               u8 video_len)
+{
+       int modes = 0;
+
+       if (vdb && (vdb_len > 1)) /* Add 4:2:0 modes present in EDID */
+               modes += add_420_vdb_modes(connector, &vdb[2], vdb_len - 1);
+
+       if (vcb && (vcb_len > 1)) /* Parse bit mask of supported modes */
+               modes += add_420_vcb_modes(connector, &vcb[2], vcb_len - 1,
+                               video_db, video_len);
+       else if (vcb) /* All modes support 4:2:0 mode */
+               modes += add_420_vcb_modes_all(connector, video_db, video_len);
+
+       DRM_DEBUG("added %d 4:2:0 modes\n", modes);
+       return modes;
+}
+
 /*
  * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block
  * @connector: connector corresponding to the HDMI sink
@@ -3111,6 +3418,12 @@ cea_db_tag(const u8 *db)
        return db[0] >> 5;
 }
 
+static int
+cea_db_extended_tag(const u8 *db)
+{
+       return db[1];
+}
+
 static int
 cea_revision(const u8 *cea)
 {
@@ -3160,6 +3473,28 @@ static bool cea_db_is_hdmi_hf_vsdb(const u8 *db)
        return hdmi_id == HDMI_IEEE_OUI_HF;
 }
 
+static bool cea_db_is_hdmi_vdb420(const u8 *db)
+{
+       if (cea_db_tag(db) != VIDEO_CAPABILITY_BLOCK)
+               return false;
+
+       if (cea_db_extended_tag(db) != VIDEO_DATA_BLOCK_420)
+               return false;
+
+       return true;
+}
+
+static bool cea_db_is_hdmi_vcb420(const u8 *db)
+{
+       if (cea_db_tag(db) != VIDEO_CAPABILITY_BLOCK)
+               return false;
+
+       if (cea_db_extended_tag(db) != VIDEO_CAP_BLOCK_420)
+               return false;
+
+       return true;
+}
+
 #define for_each_cea_db(cea, i, start, end) \
        for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
 
@@ -3167,8 +3502,9 @@ static int
 add_cea_modes(struct drm_connector *connector, struct edid *edid)
 {
        const u8 *cea = drm_find_cea_extension(edid);
-       const u8 *db, *hdmi = NULL, *video = NULL;
-       u8 dbl, hdmi_len, video_len = 0;
+       const u8 *db, *hdmi = NULL, *video = NULL, *vdb420 = NULL,
+             *vcb420 = NULL;
+       u8 dbl, hdmi_len, video_len = 0, vdb420_len = 0, vcb420_len = 0;
        int modes = 0;
 
        if (cea && cea_revision(cea) >= 3) {
@@ -3189,6 +3525,12 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
                        else if (cea_db_is_hdmi_vsdb(db)) {
                                hdmi = db;
                                hdmi_len = dbl;
+                       } else if (cea_db_is_hdmi_vdb420(db)) {
+                               vdb420 = db;
+                               vdb420_len = dbl;
+                       } else if (cea_db_is_hdmi_vcb420(db)) {
+                               vcb420 = db;
+                               vcb420_len = dbl;
                        }
                }
        }
@@ -3201,6 +3543,10 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
                modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video,
                                            video_len);
 
+       if (vdb420 || vcb420)
+               modes += do_hdmi_420_modes(connector, vdb420, vdb420_len,
+                               vcb420, vcb420_len, video, video_len);
+
        return modes;
 }
 
@@ -3338,7 +3684,7 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
        u8 *cea;
        u8 *name;
        u8 *db;
-       int sad_count = 0;
+       int total_sad_count = 0;
        int mnl;
        int dbl;
 
@@ -3352,6 +3698,7 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
 
        name = NULL;
        drm_for_each_detailed_block((u8 *)edid, monitor_name, &name);
+       /* max: 13 bytes EDID, 16 bytes ELD */
        for (mnl = 0; name && mnl < 13; mnl++) {
                if (name[mnl] == 0x0a)
                        break;
@@ -3380,11 +3727,15 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
                        dbl = cea_db_payload_len(db);
 
                        switch (cea_db_tag(db)) {
+                               int sad_count;
+
                        case AUDIO_BLOCK:
                                /* Audio Data Block, contains SADs */
-                               sad_count = dbl / 3;
-                               if (dbl >= 1)
-                                       memcpy(eld + 20 + mnl, &db[1], dbl);
+                               sad_count = min(dbl / 3, 15 - total_sad_count);
+                               if (sad_count >= 1)
+                                       memcpy(eld + 20 + mnl + total_sad_count * 3,
+                                              &db[1], sad_count * 3);
+                               total_sad_count += sad_count;
                                break;
                        case SPEAKER_BLOCK:
                                /* Speaker Allocation Data Block */
@@ -3404,13 +3755,13 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
                        }
                }
        }
-       eld[5] |= sad_count << 4;
+       eld[5] |= total_sad_count << 4;
 
        eld[DRM_ELD_BASELINE_ELD_LEN] =
                DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4);
 
        DRM_DEBUG_KMS("ELD size %d, SAD count %d\n",
-                     drm_eld_size(eld), sad_count);
+                     drm_eld_size(eld), total_sad_count);
 }
 EXPORT_SYMBOL(drm_edid_to_eld);
 
@@ -4037,12 +4388,14 @@ EXPORT_SYMBOL(drm_set_preferred_mode);
  *                                              data from a DRM display mode
  * @frame: HDMI AVI infoframe
  * @mode: DRM display mode
+ * @is_hdmi2: Sink is HDMI 2.0 compliant
  *
  * Return: 0 on success or a negative error code on failure.
  */
 int
 drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
-                                        const struct drm_display_mode *mode)
+                                        const struct drm_display_mode *mode,
+                                        bool is_hdmi2)
 {
        int err;
 
@@ -4058,6 +4411,14 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
 
        frame->video_code = drm_match_cea_mode(mode);
 
+       /*
+        * HDMI 1.4 VIC range: 1 <= VIC <= 64 (CEA-861-D) but
+        * HDMI 2.0 VIC range: 1 <= VIC <= 107 (CEA-861-F). So we
+        * have to make sure we dont break HDMI 1.4 sinks.
+        */
+       if (!is_hdmi2 && frame->video_code > 64)
+               frame->video_code = 0;
+
        frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE;
 
        /*