drm/edid: Allow HDMI infoframe without VIC or S3D
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
This commit is contained in:
Родитель
593f4b19a0
Коммит
f1781e9bb2
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче