drm/amdgpu: add new device to use atpx quirk
[sfrench/cifs-2.6.git] / drivers / gpu / drm / drm_edid.c
index cb487148359a8dca321fae3920c1dd89dd140056..ddd5379145758014f1a5a9915abb1528edc1cf17 100644 (file)
@@ -3398,6 +3398,7 @@ static int
 do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
                   const u8 *video_db, u8 video_len)
 {
+       struct drm_display_info *info = &connector->display_info;
        int modes = 0, offset = 0, i, multi_present = 0, multi_len;
        u8 vic_len, hdmi_3d_len = 0;
        u16 mask;
@@ -3525,6 +3526,8 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
        }
 
 out:
+       if (modes > 0)
+               info->has_hdmi_infoframe = true;
        return modes;
 }
 
@@ -3761,8 +3764,8 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db)
 {
        u8 len = cea_db_payload_len(db);
 
-       if (len >= 6)
-               connector->eld[5] |= (db[6] >> 7) << 1;  /* Supports_AI */
+       if (len >= 6 && (db[6] & (1 << 7)))
+               connector->eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_SUPPORTS_AI;
        if (len >= 8) {
                connector->latency_present[0] = db[8] >> 7;
                connector->latency_present[1] = (db[8] >> 6) & 1;
@@ -3834,16 +3837,27 @@ void drm_edid_get_monitor_name(struct edid *edid, char *name, int bufsize)
 }
 EXPORT_SYMBOL(drm_edid_get_monitor_name);
 
-/**
+static void clear_eld(struct drm_connector *connector)
+{
+       memset(connector->eld, 0, sizeof(connector->eld));
+
+       connector->latency_present[0] = false;
+       connector->latency_present[1] = false;
+       connector->video_latency[0] = 0;
+       connector->audio_latency[0] = 0;
+       connector->video_latency[1] = 0;
+       connector->audio_latency[1] = 0;
+}
+
+/*
  * drm_edid_to_eld - build ELD from EDID
  * @connector: connector corresponding to the HDMI/DP sink
  * @edid: EDID to parse
  *
  * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The
- * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to
- * fill in.
+ * HDCP and Port_ID ELD fields are left for the graphics driver to fill in.
  */
-void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
+static void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
 {
        uint8_t *eld = connector->eld;
        u8 *cea;
@@ -3852,14 +3866,7 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
        int mnl;
        int dbl;
 
-       memset(eld, 0, sizeof(connector->eld));
-
-       connector->latency_present[0] = false;
-       connector->latency_present[1] = false;
-       connector->video_latency[0] = 0;
-       connector->audio_latency[0] = 0;
-       connector->video_latency[1] = 0;
-       connector->audio_latency[1] = 0;
+       clear_eld(connector);
 
        if (!edid)
                return;
@@ -3870,17 +3877,18 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
                return;
        }
 
-       mnl = get_monitor_name(edid, eld + 20);
+       mnl = get_monitor_name(edid, &eld[DRM_ELD_MONITOR_NAME_STRING]);
+       DRM_DEBUG_KMS("ELD monitor %s\n", &eld[DRM_ELD_MONITOR_NAME_STRING]);
 
-       eld[4] = (cea[1] << 5) | mnl;
-       DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20);
+       eld[DRM_ELD_CEA_EDID_VER_MNL] = cea[1] << DRM_ELD_CEA_EDID_VER_SHIFT;
+       eld[DRM_ELD_CEA_EDID_VER_MNL] |= mnl;
 
-       eld[0] = 2 << 3;                /* ELD version: 2 */
+       eld[DRM_ELD_VER] = DRM_ELD_VER_CEA861D;
 
-       eld[16] = edid->mfg_id[0];
-       eld[17] = edid->mfg_id[1];
-       eld[18] = edid->prod_code[0];
-       eld[19] = edid->prod_code[1];
+       eld[DRM_ELD_MANUFACTURER_NAME0] = edid->mfg_id[0];
+       eld[DRM_ELD_MANUFACTURER_NAME1] = edid->mfg_id[1];
+       eld[DRM_ELD_PRODUCT_CODE0] = edid->prod_code[0];
+       eld[DRM_ELD_PRODUCT_CODE1] = edid->prod_code[1];
 
        if (cea_revision(cea) >= 3) {
                int i, start, end;
@@ -3901,14 +3909,14 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
                                /* Audio Data Block, contains SADs */
                                sad_count = min(dbl / 3, 15 - total_sad_count);
                                if (sad_count >= 1)
-                                       memcpy(eld + 20 + mnl + total_sad_count * 3,
+                                       memcpy(&eld[DRM_ELD_CEA_SAD(mnl, total_sad_count)],
                                               &db[1], sad_count * 3);
                                total_sad_count += sad_count;
                                break;
                        case SPEAKER_BLOCK:
                                /* Speaker Allocation Data Block */
                                if (dbl >= 1)
-                                       eld[7] = db[1];
+                                       eld[DRM_ELD_SPEAKER] = db[1];
                                break;
                        case VENDOR_BLOCK:
                                /* HDMI Vendor-Specific Data Block */
@@ -3920,7 +3928,13 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
                        }
                }
        }
-       eld[5] |= total_sad_count << 4;
+       eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= total_sad_count << DRM_ELD_SAD_COUNT_SHIFT;
+
+       if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
+           connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+               eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_DP;
+       else
+               eld[DRM_ELD_SAD_COUNT_CONN_TYPE] |= DRM_ELD_CONN_TYPE_HDMI;
 
        eld[DRM_ELD_BASELINE_ELD_LEN] =
                DIV_ROUND_UP(drm_eld_calc_baseline_block_size(eld), 4);
@@ -3928,7 +3942,6 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
        DRM_DEBUG_KMS("ELD size %d, SAD count %d\n",
                      drm_eld_size(eld), total_sad_count);
 }
-EXPORT_SYMBOL(drm_edid_to_eld);
 
 /**
  * drm_edid_to_sad - extracts SADs from EDID
@@ -4238,6 +4251,8 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
        struct drm_display_info *display = &connector->display_info;
        struct drm_hdmi_info *hdmi = &display->hdmi;
 
+       display->has_hdmi_infoframe = true;
+
        if (hf_vsdb[6] & 0x80) {
                hdmi->scdc.supported = true;
                if (hf_vsdb[6] & 0x40)
@@ -4413,6 +4428,7 @@ drm_reset_display_info(struct drm_connector *connector)
        info->cea_rev = 0;
        info->max_tmds_clock = 0;
        info->dvi_dual = false;
+       info->has_hdmi_infoframe = false;
 
        info->non_desktop = 0;
 }
@@ -4433,6 +4449,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
        info->cea_rev = 0;
        info->max_tmds_clock = 0;
        info->dvi_dual = false;
+       info->has_hdmi_infoframe = false;
 
        info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP);
 
@@ -4634,8 +4651,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,
  * @edid: EDID data
  *
  * Add the specified modes to the connector's mode list. Also fills out the
- * &drm_display_info structure in @connector with any information which can be
- * derived from the edid.
+ * &drm_display_info structure and ELD in @connector with any information which
+ * can be derived from the edid.
  *
  * Return: The number of modes added or 0 if we couldn't find any.
  */
@@ -4645,14 +4662,18 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        u32 quirks;
 
        if (edid == NULL) {
+               clear_eld(connector);
                return 0;
        }
        if (!drm_edid_is_valid(edid)) {
+               clear_eld(connector);
                dev_warn(connector->dev->dev, "%s: EDID invalid.\n",
                         connector->name);
                return 0;
        }
 
+       drm_edid_to_eld(connector, edid);
+
        /*
         * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks.
         * To avoid multiple parsing of same block, lets parse that map
@@ -4850,6 +4871,11 @@ EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);
  * @mode: DRM display mode
  * @rgb_quant_range: RGB quantization range (Q)
  * @rgb_quant_range_selectable: Sink support selectable RGB quantization range (QS)
+ * @is_hdmi2_sink: HDMI 2.0 sink, which has different default recommendations
+ *
+ * Note that @is_hdmi2_sink can be derived by looking at the
+ * &drm_scdc.supported flag stored in &drm_hdmi_info.scdc,
+ * &drm_display_info.hdmi, which can be found in &drm_connector.display_info.
  */
 void
 drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame,
@@ -4928,6 +4954,7 @@ s3d_structure_from_display_mode(const struct drm_display_mode *mode)
  * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with
  * data from a DRM display mode
  * @frame: HDMI vendor infoframe
+ * @connector: the connector
  * @mode: DRM display mode
  *
  * Note that there's is a need to send HDMI vendor infoframes only when using a
@@ -4938,8 +4965,15 @@ s3d_structure_from_display_mode(const struct drm_display_mode *mode)
  */
 int
 drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
+                                           struct drm_connector *connector,
                                            const struct drm_display_mode *mode)
 {
+       /*
+        * FIXME: sil-sii8620 doesn't have a connector around when
+        * we need one, so we have to be prepared for a NULL connector.
+        */
+       bool has_hdmi_infoframe = connector ?
+               connector->display_info.has_hdmi_infoframe : false;
        int err;
        u32 s3d_flags;
        u8 vic;
@@ -4947,11 +4981,21 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
        if (!frame || !mode)
                return -EINVAL;
 
+       if (!has_hdmi_infoframe)
+               return -EINVAL;
+
        vic = drm_match_hdmi_mode(mode);
        s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK;
 
-       if (!vic && !s3d_flags)
-               return -EINVAL;
+       /*
+        * Even if it's not absolutely necessary to send the infoframe
+        * (ie.vic==0 and s3d_struct==0) we will still send it if we
+        * know that the sink can handle it. This is based on a
+        * suggestion in HDMI 2.0 Appendix F. Apparently some sinks
+        * have trouble realizing that they shuld switch from 3D to 2D
+        * mode if the source simply stops sending the infoframe when
+        * it wants to switch from 3D to 2D.
+        */
 
        if (vic && s3d_flags)
                return -EINVAL;
@@ -4960,10 +5004,8 @@ drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
        if (err < 0)
                return err;
 
-       if (vic)
-               frame->vic = vic;
-       else
-               frame->s3d_struct = s3d_structure_from_display_mode(mode);
+       frame->vic = vic;
+       frame->s3d_struct = s3d_structure_from_display_mode(mode);
 
        return 0;
 }