Merge tag 'for-5.18/write-streams-2022-03-18' of git://git.kernel.dk/linux-block
[linux-block.git] / drivers / gpu / drm / drm_edid.c
index f5f5de362ff2c722734fea68c6c17885e03510e2..cc7bd58369dfef7224c4350dc0b66129c7798fb0 100644 (file)
@@ -93,6 +93,8 @@ static int oui(u8 first, u8 second, u8 third)
 /* Non desktop display (i.e. HMD) */
 #define EDID_QUIRK_NON_DESKTOP                 (1 << 12)
 
+#define MICROSOFT_IEEE_OUI     0xca125c
+
 struct detailed_mode_closure {
        struct drm_connector *connector;
        struct edid *edid;
@@ -212,9 +214,7 @@ static const struct edid_quirk {
 
        /* Windows Mixed Reality Headsets */
        EDID_QUIRK('A', 'C', 'R', 0x7fce, EDID_QUIRK_NON_DESKTOP),
-       EDID_QUIRK('H', 'P', 'N', 0x3515, EDID_QUIRK_NON_DESKTOP),
        EDID_QUIRK('L', 'E', 'N', 0x0408, EDID_QUIRK_NON_DESKTOP),
-       EDID_QUIRK('L', 'E', 'N', 0xb800, EDID_QUIRK_NON_DESKTOP),
        EDID_QUIRK('F', 'U', 'J', 0x1970, EDID_QUIRK_NON_DESKTOP),
        EDID_QUIRK('D', 'E', 'L', 0x7fce, EDID_QUIRK_NON_DESKTOP),
        EDID_QUIRK('S', 'E', 'C', 0x144a, EDID_QUIRK_NON_DESKTOP),
@@ -3776,7 +3776,7 @@ static int do_y420vdb_modes(struct drm_connector *connector,
        }
 
        if (modes > 0)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB420;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
        return modes;
 }
 
@@ -4222,6 +4222,17 @@ static bool cea_db_is_hdmi_forum_vsdb(const u8 *db)
        return oui(db[3], db[2], db[1]) == HDMI_FORUM_IEEE_OUI;
 }
 
+static bool cea_db_is_microsoft_vsdb(const u8 *db)
+{
+       if (cea_db_tag(db) != VENDOR_BLOCK)
+               return false;
+
+       if (cea_db_payload_len(db) != 21)
+               return false;
+
+       return oui(db[3], db[2], db[1]) == MICROSOFT_IEEE_OUI;
+}
+
 static bool cea_db_is_vcdb(const u8 *db)
 {
        if (cea_db_tag(db) != USE_EXTENDED_TAG)
@@ -4279,7 +4290,7 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
        if (map_len == 0) {
                /* All CEA modes support ycbcr420 sampling also.*/
                hdmi->y420_cmdb_map = U64_MAX;
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB420;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
                return;
        }
 
@@ -4302,7 +4313,7 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,
                map |= (u64)db[2 + count] << (8 * count);
 
        if (map)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB420;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR420;
 
        hdmi->y420_cmdb_map = map;
 }
@@ -4848,7 +4859,8 @@ bool drm_detect_monitor_audio(struct edid *edid)
        if (!edid_ext)
                goto end;
 
-       has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0);
+       has_audio = (edid_ext[0] == CEA_EXT &&
+                   (edid_ext[3] & EDID_BASIC_AUDIO) != 0);
 
        if (has_audio) {
                DRM_DEBUG_KMS("Monitor has basic audio support\n");
@@ -5075,21 +5087,21 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
 
        if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
                dc_bpc = 10;
-               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30;
+               info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_30;
                DRM_DEBUG("%s: HDMI sink does deep color 30.\n",
                          connector->name);
        }
 
        if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
                dc_bpc = 12;
-               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36;
+               info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_36;
                DRM_DEBUG("%s: HDMI sink does deep color 36.\n",
                          connector->name);
        }
 
        if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
                dc_bpc = 16;
-               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48;
+               info->edid_hdmi_rgb444_dc_modes |= DRM_EDID_HDMI_DC_48;
                DRM_DEBUG("%s: HDMI sink does deep color 48.\n",
                          connector->name);
        }
@@ -5104,16 +5116,9 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
                  connector->name, dc_bpc);
        info->bpc = dc_bpc;
 
-       /*
-        * Deep color support mandates RGB444 support for all video
-        * modes and forbids YCRCB422 support for all video modes per
-        * HDMI 1.3 spec.
-        */
-       info->color_formats = DRM_COLOR_FORMAT_RGB444;
-
        /* YCRCB444 is optional according to spec. */
        if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+               info->edid_hdmi_ycbcr444_dc_modes = info->edid_hdmi_rgb444_dc_modes;
                DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n",
                          connector->name);
        }
@@ -5149,6 +5154,25 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
        drm_parse_hdmi_deep_color_info(connector, db);
 }
 
+/*
+ * See EDID extension for head-mounted and specialized monitors, specified at:
+ * https://docs.microsoft.com/en-us/windows-hardware/drivers/display/specialized-monitors-edid-extension
+ */
+static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
+                                    const u8 *db)
+{
+       struct drm_display_info *info = &connector->display_info;
+       u8 version = db[4];
+       bool desktop_usage = db[5] & BIT(6);
+
+       /* Version 1 and 2 for HMDs, version 3 flags desktop usage explicitly */
+       if (version == 1 || version == 2 || (version == 3 && !desktop_usage))
+               info->non_desktop = true;
+
+       drm_dbg_kms(connector->dev, "HMD or specialized display VSDB version %u: 0x%02x\n",
+                   version, db[5]);
+}
+
 static void drm_parse_cea_ext(struct drm_connector *connector,
                              const struct edid *edid)
 {
@@ -5164,10 +5188,14 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
 
        /* The existence of a CEA block should imply RGB support */
        info->color_formats = DRM_COLOR_FORMAT_RGB444;
-       if (edid_ext[3] & EDID_CEA_YCRCB444)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
-       if (edid_ext[3] & EDID_CEA_YCRCB422)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
+
+       /* CTA DisplayID Data Block does not have byte #3 */
+       if (edid_ext[0] == CEA_EXT) {
+               if (edid_ext[3] & EDID_CEA_YCRCB444)
+                       info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
+               if (edid_ext[3] & EDID_CEA_YCRCB422)
+                       info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
+       }
 
        if (cea_db_offsets(edid_ext, &start, &end))
                return;
@@ -5179,6 +5207,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
                        drm_parse_hdmi_vsdb_video(connector, db);
                if (cea_db_is_hdmi_forum_vsdb(db))
                        drm_parse_hdmi_forum_vsdb(connector, db);
+               if (cea_db_is_microsoft_vsdb(db))
+                       drm_parse_microsoft_vsdb(connector, db);
                if (cea_db_is_y420cmdb(db))
                        drm_parse_y420cmdb_bitmap(connector, db);
                if (cea_db_is_vcdb(db))
@@ -5315,6 +5345,9 @@ drm_reset_display_info(struct drm_connector *connector)
        info->rgb_quant_range_selectable = false;
        memset(&info->hdmi, 0, sizeof(info->hdmi));
 
+       info->edid_hdmi_rgb444_dc_modes = 0;
+       info->edid_hdmi_ycbcr444_dc_modes = 0;
+
        info->non_desktop = 0;
        memset(&info->monitor_range, 0, sizeof(info->monitor_range));
 
@@ -5333,17 +5366,13 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
        info->width_mm = edid->width_cm * 10;
        info->height_mm = edid->height_cm * 10;
 
-       info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP);
-
        drm_get_monitor_range(connector, edid);
 
-       DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop);
-
        if (edid->revision < 3)
-               return quirks;
+               goto out;
 
        if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
-               return quirks;
+               goto out;
 
        info->color_formats |= DRM_COLOR_FORMAT_RGB444;
        drm_parse_cea_ext(connector, edid);
@@ -5364,7 +5393,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
 
        /* Only defined for 1.4 with digital displays */
        if (edid->revision < 4)
-               return quirks;
+               goto out;
 
        switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
        case DRM_EDID_DIGITAL_DEPTH_6:
@@ -5395,17 +5424,25 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
                          connector->name, info->bpc);
 
        if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR444;
        if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
-               info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
+               info->color_formats |= DRM_COLOR_FORMAT_YCBCR422;
 
        drm_update_mso(connector, edid);
 
+out:
+       if (quirks & EDID_QUIRK_NON_DESKTOP) {
+               drm_dbg_kms(connector->dev, "Non-desktop display%s\n",
+                           info->non_desktop ? " (redundant quirk)" : "");
+               info->non_desktop = true;
+       }
+
        return quirks;
 }
 
 static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev,
-                                                           struct displayid_detailed_timings_1 *timings)
+                                                           struct displayid_detailed_timings_1 *timings,
+                                                           bool type_7)
 {
        struct drm_display_mode *mode;
        unsigned pixel_clock = (timings->pixel_clock[0] |
@@ -5426,7 +5463,8 @@ static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *d
        if (!mode)
                return NULL;
 
-       mode->clock = pixel_clock * 10;
+       /* resolution is kHz for type VII, and 10 kHz for type I */
+       mode->clock = type_7 ? pixel_clock : pixel_clock * 10;
        mode->hdisplay = hactive;
        mode->hsync_start = mode->hdisplay + hsync;
        mode->hsync_end = mode->hsync_start + hsync_width;
@@ -5457,6 +5495,7 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector,
        int num_timings;
        struct drm_display_mode *newmode;
        int num_modes = 0;
+       bool type_7 = block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING;
        /* blocks must be multiple of 20 bytes length */
        if (block->num_bytes % 20)
                return 0;
@@ -5465,7 +5504,7 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector,
        for (i = 0; i < num_timings; i++) {
                struct displayid_detailed_timings_1 *timings = &det->timings[i];
 
-               newmode = drm_mode_displayid_detailed(connector->dev, timings);
+               newmode = drm_mode_displayid_detailed(connector->dev, timings, type_7);
                if (!newmode)
                        continue;
 
@@ -5484,7 +5523,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,
 
        displayid_iter_edid_begin(edid, &iter);
        displayid_iter_for_each(block, &iter) {
-               if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING)
+               if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING ||
+                   block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING)
                        num_modes += add_displayid_detailed_1_modes(connector, block);
        }
        displayid_iter_end(&iter);
@@ -5652,7 +5692,7 @@ static bool is_hdmi2_sink(const struct drm_connector *connector)
                return true;
 
        return connector->display_info.hdmi.scdc.supported ||
-               connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB420;
+               connector->display_info.color_formats & DRM_COLOR_FORMAT_YCBCR420;
 }
 
 static inline bool is_eotf_supported(u8 output_eotf, u8 sink_eotf)
@@ -5891,13 +5931,13 @@ static const u32 hdmi_colorimetry_val[] = {
 #undef ACE
 
 /**
- * drm_hdmi_avi_infoframe_colorspace() - fill the HDMI AVI infoframe
- *                                       colorspace information
+ * drm_hdmi_avi_infoframe_colorimetry() - fill the HDMI AVI infoframe
+ *                                       colorimetry information
  * @frame: HDMI AVI infoframe
  * @conn_state: connector state
  */
 void
-drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
+drm_hdmi_avi_infoframe_colorimetry(struct hdmi_avi_infoframe *frame,
                                  const struct drm_connector_state *conn_state)
 {
        u32 colorimetry_val;
@@ -5916,7 +5956,7 @@ drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
        frame->extended_colorimetry = (colorimetry_val >> 2) &
                                        EXTENDED_COLORIMETRY_MASK;
 }
-EXPORT_SYMBOL(drm_hdmi_avi_infoframe_colorspace);
+EXPORT_SYMBOL(drm_hdmi_avi_infoframe_colorimetry);
 
 /**
  * drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe