]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
drm/edid: Allow HDMI infoframe without VIC or S3D
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Mon, 13 Nov 2017 17:04:19 +0000 (19:04 +0200)
committerVille Syrjälä <ville.syrjala@linux.intel.com>
Wed, 22 Nov 2017 17:24:34 +0000 (19:24 +0200)
Appedix F of HDMI 2.0 says that some HDMI sink may fail to switch from
3D to 2D mode in a timely fashion if the source simply stops sending the
HDMI infoframe. The suggested workaround is to keep sending the
infoframe even when strictly not necessary (ie. no VIC and no S3D).
HDMI 1.4 does allow for this behaviour, stating that sending the
infoframe is optional in this case.

The infoframe was first specified in HDMI 1.4, so in theory sinks
predating that may not appreciate us sending an uknown infoframe
their way. To avoid regressions let's try to determine if the sink
supports the infoframe or not. Unfortunately there's no direct way
to do that, so instead we'll just check if we managed to parse any
HDMI 1.4 4k or stereo modes from the EDID, and if so we assume the
sink will accept the infoframe. Also if the EDID contains the HDMI
2.0 HDMI Forum VSDB we can assume the sink is prepared to receive
the infoframe.

v2: Fix getting has_hdmi_infoframe from display_info
    Always fail constructing the infoframe if the display
    possibly can't handle it

Cc: Shashank Sharma <shashank.sharma@intel.com>
Cc: Andrzej Hajda <a.hajda@samsung.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Shashank Sharma <shashank.sharma@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20171113170427.4150-3-ville.syrjala@linux.intel.com
12 files changed:
drivers/gpu/drm/bridge/sil-sii8620.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/exynos/exynos_hdmi.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/mediatek/mtk_hdmi.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/rockchip/inno_hdmi.c
drivers/gpu/drm/sti/sti_hdmi.c
drivers/gpu/drm/zte/zx_hdmi.c
include/drm/drm_connector.h
include/drm/drm_edid.h

index d58db13502bb6e6361454cf33679c67601f96000..86789f8918a4958124f63493770523bc66cfa6a3 100644 (file)
@@ -2233,8 +2233,9 @@ end:
                        union hdmi_infoframe frm;
                        u8 mhl_vic[] = { 0, 95, 94, 93, 98 };
 
+                       /* FIXME: We need the connector here */
                        drm_hdmi_vendor_infoframe_from_display_mode(
-                               &frm.vendor.hdmi, adjusted_mode);
+                               &frm.vendor.hdmi, NULL, adjusted_mode);
                        vic = frm.vendor.hdmi.vic;
                        if (vic >= ARRAY_SIZE(mhl_vic))
                                vic = 0;
index a64ce71122881696fae983ed008998ce3883db8b..b172139502d6dc8a66a50b7b70842961009c69a2 100644 (file)
@@ -1437,7 +1437,9 @@ static void hdmi_config_vendor_specific_infoframe(struct dw_hdmi *hdmi,
        u8 buffer[10];
        ssize_t err;
 
-       err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode);
+       err = drm_hdmi_vendor_infoframe_from_display_mode(&frame,
+                                                         &hdmi->connector,
+                                                         mode);
        if (err < 0)
                /*
                 * Going into that statement does not means vendor infoframe
index 749d07a017729ed1acc945d07a9570d9973489fd..9ada0ccf50dff96a7bfe57c4f407db73bf81ca9d 100644 (file)
@@ -3393,6 +3393,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;
@@ -3520,6 +3521,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;
 }
 
@@ -4243,6 +4246,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)
@@ -4416,6 +4421,7 @@ static void drm_add_display_info(struct drm_connector *connector,
        info->cea_rev = 0;
        info->max_tmds_clock = 0;
        info->dvi_dual = false;
+       info->has_hdmi_infoframe = false;
 
        if (edid->revision < 3)
                return;
@@ -4903,6 +4909,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
@@ -4913,8 +4920,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;
@@ -4922,11 +4936,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;
@@ -4935,10 +4959,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;
 }
index 82d1b7e2febea2803cfd862ccf16fe37f07ac6f0..a4b75a46f946306165e7140fa4b753fe116868ca 100644 (file)
@@ -829,7 +829,8 @@ static void hdmi_reg_infoframes(struct hdmi_context *hdata)
                DRM_INFO("%s: invalid AVI infoframe (%d)\n", __func__, ret);
        }
 
-       ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi, m);
+       ret = drm_hdmi_vendor_infoframe_from_display_mode(&frm.vendor.hdmi,
+                                                         &hdata->connector, m);
        if (!ret)
                ret = hdmi_vendor_infoframe_pack(&frm.vendor.hdmi, buf,
                                sizeof(buf));
index 5132dc8147884f9ace0af2615f7047f63b15a9a2..59247a49a07730052b06bc6828623a29ccd06478 100644 (file)
@@ -512,12 +512,14 @@ static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder,
 
 static void
 intel_hdmi_set_hdmi_infoframe(struct drm_encoder *encoder,
-                             const struct intel_crtc_state *crtc_state)
+                             const struct intel_crtc_state *crtc_state,
+                             const struct drm_connector_state *conn_state)
 {
        union hdmi_infoframe frame;
        int ret;
 
        ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+                                                         conn_state->connector,
                                                          &crtc_state->base.adjusted_mode);
        if (ret < 0)
                return;
@@ -584,7 +586,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
 
        intel_hdmi_set_avi_infoframe(encoder, crtc_state);
        intel_hdmi_set_spd_infoframe(encoder, crtc_state);
-       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state);
+       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state, conn_state);
 }
 
 static bool hdmi_sink_is_deep_color(const struct drm_connector_state *conn_state)
@@ -725,7 +727,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
 
        intel_hdmi_set_avi_infoframe(encoder, crtc_state);
        intel_hdmi_set_spd_infoframe(encoder, crtc_state);
-       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state);
+       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state, conn_state);
 }
 
 static void cpt_set_infoframes(struct drm_encoder *encoder,
@@ -768,7 +770,7 @@ static void cpt_set_infoframes(struct drm_encoder *encoder,
 
        intel_hdmi_set_avi_infoframe(encoder, crtc_state);
        intel_hdmi_set_spd_infoframe(encoder, crtc_state);
-       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state);
+       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state, conn_state);
 }
 
 static void vlv_set_infoframes(struct drm_encoder *encoder,
@@ -821,7 +823,7 @@ static void vlv_set_infoframes(struct drm_encoder *encoder,
 
        intel_hdmi_set_avi_infoframe(encoder, crtc_state);
        intel_hdmi_set_spd_infoframe(encoder, crtc_state);
-       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state);
+       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state, conn_state);
 }
 
 static void hsw_set_infoframes(struct drm_encoder *encoder,
@@ -854,7 +856,7 @@ static void hsw_set_infoframes(struct drm_encoder *encoder,
 
        intel_hdmi_set_avi_infoframe(encoder, crtc_state);
        intel_hdmi_set_spd_infoframe(encoder, crtc_state);
-       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state);
+       intel_hdmi_set_hdmi_infoframe(encoder, crtc_state, conn_state);
 }
 
 void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable)
index b78791061983970cb67475e16e3268cdaf5eae0a..59a11026dceb4cea5c2e4ee1459dc3d1854463a5 100644 (file)
@@ -1054,7 +1054,8 @@ static int mtk_hdmi_setup_vendor_specific_infoframe(struct mtk_hdmi *hdmi,
        u8 buffer[10];
        ssize_t err;
 
-       err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode);
+       err = drm_hdmi_vendor_infoframe_from_display_mode(&frame,
+                                                         &hdmi->conn, mode);
        if (err) {
                dev_err(hdmi->dev,
                        "Failed to get vendor infoframe from mode: %zd\n", err);
index 9c61e767950d6f903d63485d6cd0cd7dd2ff935e..fdfeae12ef62206e870ed97e83c7fd40190749ae 100644 (file)
@@ -2756,7 +2756,8 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
                        = hdmi_infoframe_pack(&avi_frame, args.infoframes, 17);
        }
 
-       ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi, mode);
+       ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi,
+                                                         &nv_connector->base, mode);
        if (!ret) {
                /* We have a Vendor InfoFrame, populate it to the display */
                args.pwr.vendor_infoframe_length
index ee584d87111fb2fe4ea3d74431d0945c4e1c9192..fab30927a8894862ccab9ebca92be72945220612 100644 (file)
@@ -282,6 +282,7 @@ static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
        int rc;
 
        rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+                                                        &hdmi->connector,
                                                         mode);
 
        return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
index 1c7423f4d423e90102c227aedcfaca807a933317..4ea1cc1c032e28ef93c379de53965ca5ea32cb70 100644 (file)
@@ -515,7 +515,9 @@ static int hdmi_vendor_infoframe_config(struct sti_hdmi *hdmi)
 
        DRM_DEBUG_DRIVER("\n");
 
-       ret = drm_hdmi_vendor_infoframe_from_display_mode(&infoframe, mode);
+       ret = drm_hdmi_vendor_infoframe_from_display_mode(&infoframe,
+                                                         hdmi->drm_connector,
+                                                         mode);
        if (ret < 0) {
                /*
                 * Going into that statement does not means vendor infoframe
index b8abb1b496ff5b56c8f3e51e4dc54cbcd0b369d4..13ea90f7a185acb222a7a8e9190b4e0de5ac33c5 100644 (file)
@@ -108,6 +108,7 @@ static int zx_hdmi_config_video_vsi(struct zx_hdmi *hdmi,
        int ret;
 
        ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+                                                         &hdmi->connector,
                                                          mode);
        if (ret) {
                DRM_DEV_ERROR(hdmi->dev, "failed to get vendor infoframe: %d\n",
index 2b97d1e28f6029e69e08bb854ce40859d7b76b01..1543212b044963622a45b577c3b0c07986ee3b5d 100644 (file)
@@ -269,6 +269,11 @@ struct drm_display_info {
         */
        bool dvi_dual;
 
+       /**
+        * @has_hdmi_infoframe: Does the sink support the HDMI infoframe?
+        */
+       bool has_hdmi_infoframe;
+
        /**
         * @edid_hdmi_dc_modes: Mask of supported hdmi deep color modes. Even
         * more stuff redundant with @bus_formats.
index 9e4e23524840ed4662afc9dd624a5b5dd96eecdf..3c8740ad1db685cc7af1c2e3aa3c6e2f30b3d754 100644 (file)
@@ -356,6 +356,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
                                         bool is_hdmi2_sink);
 int
 drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,
+                                           struct drm_connector *connector,
                                            const struct drm_display_mode *mode);
 void
 drm_hdmi_avi_infoframe_quant_range(struct hdmi_avi_infoframe *frame,