From 1d134898cd25003143efc3efdd097f5f872bd530 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 1 Dec 2017 20:50:44 -0800 Subject: [PATCH 1/3] drm/msm: refactor Y420CMDB block parsing logic Y420CMDB block parsing is too complex and incorrect in handling cases where more than one byte of bitmap is present. Fix this logic to make it more simple and capable to handle all such cases. Change-Id: I7aef80f588ec44def000c9f04e1da4c10020699d Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/sde_edid_parser.c | 44 +++++++++++++++------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c index 50667c5921a0..043e439835f1 100644 --- a/drivers/gpu/drm/msm/sde_edid_parser.c +++ b/drivers/gpu/drm/msm/sde_edid_parser.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -252,12 +252,13 @@ static void sde_edid_parse_Y420CMDB( struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl, const u8 *db) { - u32 offset = 0; u8 cmdb_len = 0; u8 svd_len = 0; const u8 *svd = NULL; - u32 i = 0, j = 0; + u32 i = 0; u32 video_format = 0; + u32 num_cmdb_svd = 0; + const u32 mult = 8; if (!edid_ctrl) { DEV_ERR("%s: edid_ctrl is NULL\n", __func__); @@ -271,8 +272,8 @@ const u8 *db) SDE_EDID_DEBUG("%s +\n", __func__); cmdb_len = db[0] & 0x1f; - /* Byte 3 to L+1 contain SVDs */ - offset += 2; + if (cmdb_len < 1) + return; svd = sde_edid_find_block(edid_ctrl->edid, VIDEO_DATA_BLOCK); @@ -282,21 +283,26 @@ const u8 *db) ++svd; } - for (i = 0; i < svd_len; i++, j++) { - video_format = *(svd + i) & 0x7F; - if (cmdb_len == 1) { - /* If cmdb_len is 1, it means all SVDs support YUV */ - sde_edid_set_y420_support(connector, video_format); - } else if (db[offset] & (1 << j)) { - sde_edid_set_y420_support(connector, video_format); + if (cmdb_len == 1) + num_cmdb_svd = svd_len; + else { + num_cmdb_svd = (cmdb_len - 1) * mult; + if (num_cmdb_svd > svd_len) + num_cmdb_svd = svd_len; + } - if (j & 0x80) { - j = j/8; - offset++; - if (offset >= cmdb_len) - break; - } - } + for (i = 0; i < num_cmdb_svd; i++) { + video_format = *(svd + i) & 0x7F; + /* + * If cmdb_len is 1, it means all SVDs support YUV + * Else, we check each byte of the cmdb bitmap bitwise + * and match those bits with the formats populated + * during the parsing of the Video Data Blocks. + * Refer to CTA 861-F section 7.5.11 YCBCR 4:2:0 Capability + * Map Data Block for more details on this. + */ + if (cmdb_len == 1 || (db[2 + i / mult] & (1 << (i % mult)))) + sde_edid_set_y420_support(connector, video_format); } SDE_EDID_DEBUG("%s -\n", __func__); From 03e2cf926fb46d296a69692d6793af2722936585 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Mon, 6 Aug 2018 19:24:43 -0700 Subject: [PATCH 2/3] drm/msm: Clear HDMI VSDB and VCDB info across hotplug SDE connector stores the information related to the HDMI VSDB and VCDB data blocks. This connector information is retained till the connector is destroyed which does not happen across hotplug. Clear the HDMI VSDB and VCDB related data fields when the bridge is disabled so that across a hotplug stale information is not retained. Change-Id: I4dabfda03a6446f38913ac45d9df2d2ae411a7f0 Signed-off-by: Abhinav Kumar --- .../drm/msm/hdmi-staging/sde_hdmi_bridge.c | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index 2d65fc924f07..37e76e994e38 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -125,6 +125,55 @@ static void sde_hdmi_clear_hdr_info(struct drm_bridge *bridge) connector->hdr_supported = false; } +static void sde_hdmi_clear_vsdb_info(struct drm_bridge *bridge) +{ + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct drm_connector *connector = hdmi->connector; + + connector->max_tmds_clock = 0; + connector->latency_present[0] = false; + connector->latency_present[1] = false; + connector->video_latency[0] = false; + connector->video_latency[1] = false; + connector->audio_latency[0] = false; + connector->audio_latency[1] = false; +} + +static void sde_hdmi_clear_hf_vsdb_info(struct drm_bridge *bridge) +{ + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct drm_connector *connector = hdmi->connector; + + connector->max_tmds_char = 0; + connector->scdc_present = false; + connector->rr_capable = false; + connector->supports_scramble = false; + connector->flags_3d = 0; +} + +static void sde_hdmi_clear_vcdb_info(struct drm_bridge *bridge) +{ + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct drm_connector *connector = hdmi->connector; + + connector->pt_scan_info = 0; + connector->it_scan_info = 0; + connector->ce_scan_info = 0; + connector->rgb_qs = false; + connector->yuv_qs = false; +} + +static void sde_hdmi_clear_vsdbs(struct drm_bridge *bridge) +{ + /* Clear fields of HDMI VSDB */ + sde_hdmi_clear_vsdb_info(bridge); + /* Clear fields of HDMI forum VSDB */ + sde_hdmi_clear_hf_vsdb_info(bridge); +} + static int _sde_hdmi_bridge_power_on(struct drm_bridge *bridge) { struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); @@ -606,6 +655,12 @@ static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) mutex_lock(&display->display_lock); + if (!bridge) { + SDE_ERROR("Invalid params\n"); + mutex_unlock(&display->display_lock); + return; + } + display->pll_update_enable = false; display->sink_hdcp_ver = SDE_HDMI_HDCP_NONE; display->sink_hdcp22_support = false; @@ -614,6 +669,11 @@ static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) sde_hdmi_hdcp_off(display); sde_hdmi_clear_hdr_info(bridge); + /* Clear HDMI VSDB blocks info */ + sde_hdmi_clear_vsdbs(bridge); + /* Clear HDMI VCDB block info */ + sde_hdmi_clear_vcdb_info(bridge); + mutex_unlock(&display->display_lock); } From 63f9f30f6f54b917c153684a7d1599699dd3f6e0 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Mon, 6 Aug 2018 19:30:10 -0700 Subject: [PATCH 3/3] drm/msm: Clear the mode private flags before setting Clear the display mode private flags before assigning the new value. These flags retain the values from the previous mode set which could be stale in cases where the mode is different across the hotplugs. Change-Id: I4bd7021970737e5ae22bade3074d8debfeddc7b3 Signed-off-by: Abhinav Kumar --- drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index 37e76e994e38..120a6defc868 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -1000,6 +1000,9 @@ static bool _sde_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + /* Clear the private flags before assigning new one */ + adjusted_mode->private_flags = 0; + adjusted_mode->private_flags |= _sde_hdmi_choose_best_format(hdmi, adjusted_mode); SDE_DEBUG("Adjusted mode private flags: 0x%x\n",