From 1cbc1f0d324ba6c4d1b10ac6362b5e0b029f63d5 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:16 +0200 Subject: drm/edid: fix AVI infoframe aspect ratio handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We try to avoid sending VICs defined in the later specs in AVI infoframes to sinks that conform to the earlier specs, to not upset them, and use 0 for the VIC instead. However, we do this detection and conversion to 0 too early, as we'll need the actual VIC to figure out the aspect ratio. In particular, for a mode with 64:27 aspect ratio, 0 for VIC fails the AVI infoframe generation altogether with -EINVAL. Separate the VIC lookup from the "filtering", and postpone the filtering, to use the proper VIC for aspect ratio handling, and the 0 VIC for the infoframe video code as needed. Reported-by: William Tseng Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/6153 References: https://lore.kernel.org/r/20220920062316.43162-1-william.tseng@intel.com Cc: Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/c3e78cc6d01ed237f71ad0038826b08d83d75eef.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 3841aba17abd..23f7146e6a9b 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6885,8 +6885,6 @@ static u8 drm_mode_hdmi_vic(const struct drm_connector *connector, static u8 drm_mode_cea_vic(const struct drm_connector *connector, const struct drm_display_mode *mode) { - u8 vic; - /* * HDMI spec says if a mode is found in HDMI 1.4b 4K modes * we should send its VIC in vendor infoframes, else send the @@ -6896,13 +6894,18 @@ static u8 drm_mode_cea_vic(const struct drm_connector *connector, if (drm_mode_hdmi_vic(connector, mode)) return 0; - vic = drm_match_cea_mode(mode); + return drm_match_cea_mode(mode); +} - /* - * HDMI 1.4 VIC range: 1 <= VIC <= 64 (CEA-861-D) but - * HDMI 2.0 VIC range: 1 <= VIC <= 107 (CEA-861-F). So we - * have to make sure we dont break HDMI 1.4 sinks. - */ +/* + * Avoid sending VICs defined in HDMI 2.0 in AVI infoframes to sinks that + * conform to HDMI 1.4. + * + * HDMI 1.4 (CTA-861-D) VIC range: [1..64] + * HDMI 2.0 (CTA-861-F) VIC range: [1..107] + */ +static u8 vic_for_avi_infoframe(const struct drm_connector *connector, u8 vic) +{ if (!is_hdmi2_sink(connector) && vic > 64) return 0; @@ -6978,7 +6981,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, picture_aspect = HDMI_PICTURE_ASPECT_NONE; } - frame->video_code = vic; + frame->video_code = vic_for_avi_infoframe(connector, vic); frame->picture_aspect = picture_aspect; frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; -- cgit From 72794d16bd535a984e6653a18f5862405b49b5f9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:17 +0200 Subject: drm/edid: fix parsing of 3D modes from HDMI VSDB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 537d9ed2f6c1 ("drm/edid: convert add_cea_modes() to use cea db iter") inadvertently moved the do_hdmi_vsdb_modes() call within the db iteration loop, always passing NULL as the CTA VDB to do_hdmi_vsdb_modes(), skipping a lot of stereo modes. Move the call back outside of the loop. This does mean only one CTA VDB and HDMI VSDB combination will be handled, but it's an unlikely scenario to have more than one of either block, and it was not accounted for before the regression either. Fixes: 537d9ed2f6c1 ("drm/edid: convert add_cea_modes() to use cea db iter") Cc: # v6.0+ Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/cf159b8816191ed595a3cb954acaf189c4528cc7.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 23f7146e6a9b..b94adb9bbefb 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5249,13 +5249,12 @@ static int add_cea_modes(struct drm_connector *connector, { const struct cea_db *db; struct cea_db_iter iter; + const u8 *hdmi = NULL, *video = NULL; + u8 hdmi_len = 0, video_len = 0; int modes = 0; cea_db_iter_edid_begin(drm_edid, &iter); cea_db_iter_for_each(db, &iter) { - const u8 *hdmi = NULL, *video = NULL; - u8 hdmi_len = 0, video_len = 0; - if (cea_db_tag(db) == CTA_DB_VIDEO) { video = cea_db_data(db); video_len = cea_db_payload_len(db); @@ -5271,18 +5270,17 @@ static int add_cea_modes(struct drm_connector *connector, modes += do_y420vdb_modes(connector, vdb420, cea_db_payload_len(db) - 1); } - - /* - * We parse the HDMI VSDB after having added the cea modes as we - * will be patching their flags when the sink supports stereo - * 3D. - */ - if (hdmi) - modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, - video, video_len); } cea_db_iter_end(&iter); + /* + * We parse the HDMI VSDB after having added the cea modes as we will be + * patching their flags when the sink supports stereo 3D. + */ + if (hdmi) + modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, + video, video_len); + return modes; } -- cgit From c3292ab5fbd7045f019418b2ce1977891419ad28 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:18 +0200 Subject: drm/edid: parse VICs from CTA VDB early MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A number of places need access to the VICs. Just parse them early for easy access. Gracefully handle multiple CTA VDBs. It's unlikely to have more than one, but the CTA-861 references "Video Data Block(s)", so err on the safe side. Start parsing them now, convert users in follow-up to have fewer moving parts in one go. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/7989b2b37837be68953c5d20afd3e93762bfd626.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index b94adb9bbefb..90846dc69061 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5862,6 +5862,36 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode) } EXPORT_SYMBOL(drm_default_rgb_quant_range); +/* CTA-861 Video Data Block (CTA VDB) */ +static void parse_cta_vdb(struct drm_connector *connector, const struct cea_db *db) +{ + struct drm_display_info *info = &connector->display_info; + int i, vic_index, len = cea_db_payload_len(db); + const u8 *svds = cea_db_data(db); + u8 *vics; + + if (!len) + return; + + /* Gracefully handle multiple VDBs, however unlikely that is */ + vics = krealloc(info->vics, info->vics_len + len, GFP_KERNEL); + if (!vics) + return; + + vic_index = info->vics_len; + info->vics_len += len; + info->vics = vics; + + for (i = 0; i < len; i++) { + u8 vic = svd_to_vic(svds[i]); + + if (!drm_valid_cea_vic(vic)) + vic = 0; + + info->vics[vic_index++] = vic; + } +} + static void drm_parse_vcdb(struct drm_connector *connector, const u8 *db) { struct drm_display_info *info = &connector->display_info; @@ -6205,6 +6235,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector, drm_parse_vcdb(connector, data); else if (cea_db_is_hdmi_hdr_metadata_block(db)) drm_parse_hdr_metadata_block(connector, data); + else if (cea_db_tag(db) == CTA_DB_VIDEO) + parse_cta_vdb(connector, db); } cea_db_iter_end(&iter); } @@ -6372,6 +6404,10 @@ static void drm_reset_display_info(struct drm_connector *connector) info->mso_stream_count = 0; info->mso_pixel_overlap = 0; info->max_dsc_bpp = 0; + + kfree(info->vics); + info->vics = NULL; + info->vics_len = 0; } static u32 update_display_info(struct drm_connector *connector, -- cgit From 6a40a75f71b997f2248664021f28af0dc7796d18 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:19 +0200 Subject: drm/edid: Use the pre-parsed VICs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have all the VICs in info->vics, use them to simplify access based on VIC index, i.e. on the order of VICs in the EDID, and avoid passing CTA VDB pointers around. This also fixes the highly unlikely scenarios of a) multiple HDMI VSDBs, and b) HDMI VSDB 3D modes using VIC indexes that span across multiple CTA VDBs, and the combination of the two. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/30f1a97193171e70ec1c26c4b685d8930799b9a6.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 92 ++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 60 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 90846dc69061..7f0386175230 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4468,28 +4468,20 @@ static u8 svd_to_vic(u8 svd) return svd; } +/* + * Return a display mode for the 0-based vic_index'th VIC across all CTA VDBs in + * the EDID, or NULL on errors. + */ static struct drm_display_mode * -drm_display_mode_from_vic_index(struct drm_connector *connector, - const u8 *video_db, u8 video_len, - u8 video_index) +drm_display_mode_from_vic_index(struct drm_connector *connector, int vic_index) { + const struct drm_display_info *info = &connector->display_info; struct drm_device *dev = connector->dev; - struct drm_display_mode *newmode; - u8 vic; - if (video_db == NULL || video_index >= video_len) + if (!info->vics || vic_index >= info->vics_len || !info->vics[vic_index]) return NULL; - /* CEA modes are numbered 1..127 */ - vic = svd_to_vic(video_db[video_index]); - if (!drm_valid_cea_vic(vic)) - return NULL; - - newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic)); - if (!newmode) - return NULL; - - return newmode; + return drm_display_mode_from_cea_vic(dev, info->vics[vic_index]); } /* @@ -4538,9 +4530,8 @@ static int do_y420vdb_modes(struct drm_connector *connector, * Makes an entry for a videomode in the YCBCR 420 bitmap */ static void -drm_add_cmdb_modes(struct drm_connector *connector, u8 svd) +drm_add_cmdb_modes(struct drm_connector *connector, u8 vic) { - u8 vic = svd_to_vic(svd); struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; if (!drm_valid_cea_vic(vic)) @@ -4577,16 +4568,20 @@ drm_display_mode_from_cea_vic(struct drm_device *dev, } EXPORT_SYMBOL(drm_display_mode_from_cea_vic); -static int -do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) +/* Add modes based on VICs parsed in parse_cta_vdb() */ +static int add_cta_vdb_modes(struct drm_connector *connector) { + const struct drm_display_info *info = &connector->display_info; + const struct drm_hdmi_info *hdmi = &info->hdmi; int i, modes = 0; - struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; - for (i = 0; i < len; i++) { + if (!info->vics) + return 0; + + for (i = 0; i < info->vics_len; i++) { struct drm_display_mode *mode; - mode = drm_display_mode_from_vic_index(connector, db, len, i); + mode = drm_display_mode_from_vic_index(connector, i); if (mode) { /* * YCBCR420 capability block contains a bitmap which @@ -4598,7 +4593,7 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) * Add YCBCR420 modes only if sink is HDMI 2.0 capable. */ if (i < 64 && hdmi->y420_cmdb_map & (1ULL << i)) - drm_add_cmdb_modes(connector, db[i]); + drm_add_cmdb_modes(connector, info->vics[i]); drm_mode_probed_add(connector, mode); modes++; @@ -4693,15 +4688,13 @@ static int add_hdmi_mode(struct drm_connector *connector, u8 vic) } static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, - const u8 *video_db, u8 video_len, u8 video_index) + int vic_index) { struct drm_display_mode *newmode; int modes = 0; if (structure & (1 << 0)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); + newmode = drm_display_mode_from_vic_index(connector, vic_index); if (newmode) { newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING; drm_mode_probed_add(connector, newmode); @@ -4709,9 +4702,7 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, } } if (structure & (1 << 6)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); + newmode = drm_display_mode_from_vic_index(connector, vic_index); if (newmode) { newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; drm_mode_probed_add(connector, newmode); @@ -4719,9 +4710,7 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, } } if (structure & (1 << 8)) { - newmode = drm_display_mode_from_vic_index(connector, video_db, - video_len, - video_index); + newmode = drm_display_mode_from_vic_index(connector, vic_index); if (newmode) { newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; drm_mode_probed_add(connector, newmode); @@ -4742,8 +4731,7 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, * also adds the stereo 3d modes when applicable. */ static int -do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, - const u8 *video_db, u8 video_len) +do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) { struct drm_display_info *info = &connector->display_info; int modes = 0, offset = 0, i, multi_present = 0, multi_len; @@ -4818,9 +4806,7 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, for (i = 0; i < 16; i++) { if (mask & (1 << i)) modes += add_3d_struct_modes(connector, - structure_all, - video_db, - video_len, i); + structure_all, i); } } @@ -4857,8 +4843,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, if (newflag != 0) { newmode = drm_display_mode_from_vic_index(connector, - video_db, - video_len, vic_index); if (newmode) { @@ -5249,20 +5233,16 @@ static int add_cea_modes(struct drm_connector *connector, { const struct cea_db *db; struct cea_db_iter iter; - const u8 *hdmi = NULL, *video = NULL; - u8 hdmi_len = 0, video_len = 0; - int modes = 0; + int modes; + + /* CTA VDB block VICs parsed earlier */ + modes = add_cta_vdb_modes(connector); cea_db_iter_edid_begin(drm_edid, &iter); cea_db_iter_for_each(db, &iter) { - if (cea_db_tag(db) == CTA_DB_VIDEO) { - video = cea_db_data(db); - video_len = cea_db_payload_len(db); - modes += do_cea_modes(connector, video, video_len); - } else if (cea_db_is_hdmi_vsdb(db)) { - /* FIXME: Switch to use cea_db_data() */ - hdmi = (const u8 *)db; - hdmi_len = cea_db_payload_len(db); + if (cea_db_is_hdmi_vsdb(db)) { + modes += do_hdmi_vsdb_modes(connector, (const u8 *)db, + cea_db_payload_len(db)); } else if (cea_db_is_y420vdb(db)) { const u8 *vdb420 = cea_db_data(db) + 1; @@ -5273,14 +5253,6 @@ static int add_cea_modes(struct drm_connector *connector, } cea_db_iter_end(&iter); - /* - * We parse the HDMI VSDB after having added the cea modes as we will be - * patching their flags when the sink supports stereo 3D. - */ - if (hdmi) - modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, - video, video_len); - return modes; } -- cgit From 4ed29f398b5aa55f12e8f8da7cdf7c22d82018b7 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:20 +0200 Subject: drm/edid: use VIC in AVI infoframe if sink lists it in CTA VDB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently there are HDMI 1.4 compatible displays out there that support VICs from specs later than CTA-861-D, i.e. VIC > 64, although HDMI 1.4 references CTA-861-D only. We try to avoid using VICs from the later specs in the AVI infoframes to avoid upsetting sinks that conform to earlier specs. However, it seems reasonable to do this when the sink claims it supports the VIC. With the pre-parsed list of VICs handy, this is now trivial. References: https://gitlab.freedesktop.org/drm/intel/-/issues/6153 Cc: Ville Syrjälä Cc: William Tseng Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/775124fd07a5b7892e869becc3dd8dadb328ae5f.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 7f0386175230..3dfcd6450f10 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5864,6 +5864,22 @@ static void parse_cta_vdb(struct drm_connector *connector, const struct cea_db * } } +static bool cta_vdb_has_vic(const struct drm_connector *connector, u8 vic) +{ + const struct drm_display_info *info = &connector->display_info; + int i; + + if (!vic || !info->vics) + return false; + + for (i = 0; i < info->vics_len; i++) { + if (info->vics[i] == vic) + return true; + } + + return false; +} + static void drm_parse_vcdb(struct drm_connector *connector, const u8 *db) { struct drm_display_info *info = &connector->display_info; @@ -6909,10 +6925,14 @@ static u8 drm_mode_cea_vic(const struct drm_connector *connector, * * HDMI 1.4 (CTA-861-D) VIC range: [1..64] * HDMI 2.0 (CTA-861-F) VIC range: [1..107] + * + * If the sink lists the VIC in CTA VDB, assume it's fine, regardless of HDMI + * version. */ static u8 vic_for_avi_infoframe(const struct drm_connector *connector, u8 vic) { - if (!is_hdmi2_sink(connector) && vic > 64) + if (!is_hdmi2_sink(connector) && vic > 64 && + !cta_vdb_has_vic(connector, vic)) return 0; return vic; -- cgit From 26c2ff77349927d6544db107f65757f447574dae Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:21 +0200 Subject: drm/edid: rename struct drm_display_info *display to *info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the local variable to info for consistency. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/d35a50c714e21869afcabfafd5c5e590936b791a.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 3dfcd6450f10..4e9108e9fc96 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6011,14 +6011,14 @@ static void drm_parse_dsc_info(struct drm_hdmi_dsc_cap *hdmi_dsc, static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, const u8 *hf_scds) { - struct drm_display_info *display = &connector->display_info; - struct drm_hdmi_info *hdmi = &display->hdmi; + struct drm_display_info *info = &connector->display_info; + struct drm_hdmi_info *hdmi = &info->hdmi; struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap; int max_tmds_clock = 0; u8 max_frl_rate = 0; bool dsc_support = false; - display->has_hdmi_infoframe = true; + info->has_hdmi_infoframe = true; if (hf_scds[6] & 0x80) { hdmi->scdc.supported = true; @@ -6042,7 +6042,7 @@ static void drm_parse_hdmi_forum_scds(struct drm_connector *connector, max_tmds_clock = hf_scds[5] * 5000; if (max_tmds_clock > 340000) { - display->max_tmds_clock = max_tmds_clock; + info->max_tmds_clock = max_tmds_clock; } if (scdc->supported) { -- cgit From 61e05fdc096bc27b32a93fa85f31ccf535d6a5a9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:22 +0200 Subject: drm/edid: refactor CTA Y420CMDB parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have pre-parsed CTA VDB VICs stored in info->vics, leverage that to simplify CTA Y420CMDB parsing. Move updating the y420_cmdb_modes bitmap to the display info parsing stage, instead of updating it during add modes. This allows us to drop the intermediate y420_cmdb_map from display info, and replace it with a local variable. This is prerequisite work for overall better separation of the two parsing steps (updating display info and adding modes). Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/7a0e5e99a83f203b6a8981d263b89b2bb7d2fe15.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 86 +++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 43 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 4e9108e9fc96..ead7a4ce0422 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4522,24 +4522,6 @@ static int do_y420vdb_modes(struct drm_connector *connector, return modes; } -/* - * drm_add_cmdb_modes - Add a YCBCR 420 mode into bitmap - * @connector: connector corresponding to the HDMI sink - * @vic: CEA vic for the video mode to be added in the map - * - * Makes an entry for a videomode in the YCBCR 420 bitmap - */ -static void -drm_add_cmdb_modes(struct drm_connector *connector, u8 vic) -{ - struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; - - if (!drm_valid_cea_vic(vic)) - return; - - bitmap_set(hdmi->y420_cmdb_modes, vic, 1); -} - /** * drm_display_mode_from_cea_vic() - return a mode for CEA VIC * @dev: DRM device @@ -4572,7 +4554,6 @@ EXPORT_SYMBOL(drm_display_mode_from_cea_vic); static int add_cta_vdb_modes(struct drm_connector *connector) { const struct drm_display_info *info = &connector->display_info; - const struct drm_hdmi_info *hdmi = &info->hdmi; int i, modes = 0; if (!info->vics) @@ -4583,18 +4564,6 @@ static int add_cta_vdb_modes(struct drm_connector *connector) mode = drm_display_mode_from_vic_index(connector, i); if (mode) { - /* - * YCBCR420 capability block contains a bitmap which - * gives the index of CEA modes from CEA VDB, which - * can support YCBCR 420 sampling output also (apart - * from RGB/YCBCR444 etc). - * For example, if the bit 0 in bitmap is set, - * first mode in VDB can support YCBCR420 output too. - * Add YCBCR420 modes only if sink is HDMI 2.0 capable. - */ - if (i < 64 && hdmi->y420_cmdb_map & (1ULL << i)) - drm_add_cmdb_modes(connector, info->vics[i]); - drm_mode_probed_add(connector, mode); modes++; } @@ -5188,20 +5157,26 @@ static int edid_hfeeodb_extension_block_count(const struct edid *edid) return cta[4 + 2]; } -static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, - const u8 *db) +/* + * CTA-861 YCbCr 4:2:0 Capability Map Data Block (CTA Y420CMDB) + * + * Y420CMDB contains a bitmap which gives the index of CTA modes from CTA VDB, + * which can support YCBCR 420 sampling output also (apart from RGB/YCBCR444 + * etc). For example, if the bit 0 in bitmap is set, first mode in VDB can + * support YCBCR420 output too. + */ +static void parse_cta_y420cmdb(struct drm_connector *connector, + const struct cea_db *db, u64 *y420cmdb_map) { struct drm_display_info *info = &connector->display_info; - struct drm_hdmi_info *hdmi = &info->hdmi; - u8 map_len = cea_db_payload_len(db) - 1; - u8 count; + int i, map_len = cea_db_payload_len(db) - 1; + const u8 *data = cea_db_data(db) + 1; u64 map = 0; if (map_len == 0) { /* All CEA modes support ycbcr420 sampling also.*/ - hdmi->y420_cmdb_map = U64_MAX; - info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; - return; + map = U64_MAX; + goto out; } /* @@ -5219,13 +5194,14 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, if (WARN_ON_ONCE(map_len > 8)) map_len = 8; - for (count = 0; count < map_len; count++) - map |= (u64)db[2 + count] << (8 * count); + for (i = 0; i < map_len; i++) + map |= (u64)data[i] << (8 * i); +out: if (map) info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; - hdmi->y420_cmdb_map = map; + *y420cmdb_map = map; } static int add_cea_modes(struct drm_connector *connector, @@ -5864,6 +5840,26 @@ static void parse_cta_vdb(struct drm_connector *connector, const struct cea_db * } } +/* + * Update y420_cmdb_modes based on previously parsed CTA VDB and Y420CMDB. + * + * Translate the y420cmdb_map based on VIC indexes to y420_cmdb_modes indexed + * using the VICs themselves. + */ +static void update_cta_y420cmdb(struct drm_connector *connector, u64 y420cmdb_map) +{ + struct drm_display_info *info = &connector->display_info; + struct drm_hdmi_info *hdmi = &info->hdmi; + int i, len = min_t(int, info->vics_len, BITS_PER_TYPE(y420cmdb_map)); + + for (i = 0; i < len; i++) { + u8 vic = info->vics[i]; + + if (vic && y420cmdb_map & BIT_ULL(i)) + bitmap_set(hdmi->y420_cmdb_modes, vic, 1); + } +} + static bool cta_vdb_has_vic(const struct drm_connector *connector, u8 vic) { const struct drm_display_info *info = &connector->display_info; @@ -6181,6 +6177,7 @@ static void drm_parse_cea_ext(struct drm_connector *connector, const struct cea_db *db; struct cea_db_iter iter; const u8 *edid_ext; + u64 y420cmdb_map = 0; drm_edid_iter_begin(drm_edid, &edid_iter); drm_edid_iter_for_each(edid_ext, &edid_iter) { @@ -6218,7 +6215,7 @@ static void drm_parse_cea_ext(struct drm_connector *connector, else if (cea_db_is_microsoft_vsdb(db)) drm_parse_microsoft_vsdb(connector, data); else if (cea_db_is_y420cmdb(db)) - drm_parse_y420cmdb_bitmap(connector, data); + parse_cta_y420cmdb(connector, db, &y420cmdb_map); else if (cea_db_is_vcdb(db)) drm_parse_vcdb(connector, data); else if (cea_db_is_hdmi_hdr_metadata_block(db)) @@ -6227,6 +6224,9 @@ static void drm_parse_cea_ext(struct drm_connector *connector, parse_cta_vdb(connector, db); } cea_db_iter_end(&iter); + + if (y420cmdb_map) + update_cta_y420cmdb(connector, y420cmdb_map); } static -- cgit From c54e2e23c3fc3112c821b490e78ea22a7ecea288 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:23 +0200 Subject: drm/edid: split CTA Y420VDB info and mode parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate the parsing of display info and modes from the CTA Y420VDB. This is prerequisite work for overall better separation of the two parsing steps. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/3bc5fe6650a6ce4249803f7192096764ea724e05.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index ead7a4ce0422..076ba125c38d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4497,10 +4497,8 @@ drm_display_mode_from_vic_index(struct drm_connector *connector, int vic_index) static int do_y420vdb_modes(struct drm_connector *connector, const u8 *svds, u8 svds_len) { - int modes = 0, i; struct drm_device *dev = connector->dev; - struct drm_display_info *info = &connector->display_info; - struct drm_hdmi_info *hdmi = &info->hdmi; + int modes = 0, i; for (i = 0; i < svds_len; i++) { u8 vic = svd_to_vic(svds[i]); @@ -4512,13 +4510,10 @@ static int do_y420vdb_modes(struct drm_connector *connector, newmode = drm_mode_duplicate(dev, cea_mode_for_vic(vic)); if (!newmode) break; - bitmap_set(hdmi->y420_vdb_modes, vic, 1); drm_mode_probed_add(connector, newmode); modes++; } - if (modes > 0) - info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; return modes; } @@ -5876,6 +5871,26 @@ static bool cta_vdb_has_vic(const struct drm_connector *connector, u8 vic) return false; } +/* CTA-861-H YCbCr 4:2:0 Video Data Block (CTA Y420VDB) */ +static void parse_cta_y420vdb(struct drm_connector *connector, + const struct cea_db *db) +{ + struct drm_display_info *info = &connector->display_info; + struct drm_hdmi_info *hdmi = &info->hdmi; + const u8 *svds = cea_db_data(db) + 1; + int i; + + for (i = 0; i < cea_db_payload_len(db) - 1; i++) { + u8 vic = svd_to_vic(svds[i]); + + if (!drm_valid_cea_vic(vic)) + continue; + + bitmap_set(hdmi->y420_vdb_modes, vic, 1); + info->color_formats |= DRM_COLOR_FORMAT_YCBCR420; + } +} + static void drm_parse_vcdb(struct drm_connector *connector, const u8 *db) { struct drm_display_info *info = &connector->display_info; @@ -6216,6 +6231,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector, drm_parse_microsoft_vsdb(connector, data); else if (cea_db_is_y420cmdb(db)) parse_cta_y420cmdb(connector, db, &y420cmdb_map); + else if (cea_db_is_y420vdb(db)) + parse_cta_y420vdb(connector, db); else if (cea_db_is_vcdb(db)) drm_parse_vcdb(connector, data); else if (cea_db_is_hdmi_hdr_metadata_block(db)) -- cgit From 1ee3e217f98e4a14a792ed2d7b42417ad1194922 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:24 +0200 Subject: drm/edid: fix and clarify HDMI VSDB audio latency parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helpers for Latency_Fields_Present and I_Latency_Fields_Present bits, and fix the parsing: - Respect specification regarding "I_Latency_Fields_Present shall be zero if Latency_Fields_Present is zero". - Don't claim latency fields are present if the data block isn't big enough to hold them. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/80426772a2d2e17bebf6f58d99b7d0cf6260c2d6.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 076ba125c38d..847076b29594 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4685,6 +4685,16 @@ static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, return modes; } +static bool hdmi_vsdb_latency_present(const u8 *db) +{ + return db[8] & BIT(7); +} + +static bool hdmi_vsdb_i_latency_present(const u8 *db) +{ + return hdmi_vsdb_latency_present(db) && db[8] & BIT(6); +} + /* * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block * @connector: connector corresponding to the HDMI sink @@ -5357,6 +5367,7 @@ drm_parse_hdr_metadata_block(struct drm_connector *connector, const u8 *db) } } +/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */ static void drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db) { @@ -5364,18 +5375,18 @@ drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db) 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; - } - if (len >= 9) + + if (len >= 10 && hdmi_vsdb_latency_present(db)) { + connector->latency_present[0] = true; connector->video_latency[0] = db[9]; - if (len >= 10) connector->audio_latency[0] = db[10]; - if (len >= 11) + } + + if (len >= 12 && hdmi_vsdb_i_latency_present(db)) { + connector->latency_present[1] = true; connector->video_latency[1] = db[11]; - if (len >= 12) connector->audio_latency[1] = db[12]; + } drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI: latency present %d %d, video latency %d %d, audio latency %d %d\n", -- cgit From cba83c1fc38612c3d2c7b1bfed9d882e4848fb0d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:25 +0200 Subject: drm/edid: add helper for HDMI VSDB audio latency field length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper for skipping the HDMI VSDB audio latency fields. There's a functional change for HDMI VSDB blocks that do not respect the spec: "I_Latency_Fields_Present shall be zero if Latency_Fields_Present is zero". We assume this to hold when skipping the latency fields, and ignore non-zero I_Latency_Fields_Present if Latency_Fields_Present is zero. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/da4293203ef2ddeb0bf66a2bfdbc129ab609c543.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 847076b29594..93067b8dd9f6 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4695,6 +4695,16 @@ static bool hdmi_vsdb_i_latency_present(const u8 *db) return hdmi_vsdb_latency_present(db) && db[8] & BIT(6); } +static int hdmi_vsdb_latency_length(const u8 *db) +{ + if (hdmi_vsdb_i_latency_present(db)) + return 4; + else if (hdmi_vsdb_latency_present(db)) + return 2; + else + return 0; +} + /* * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block * @connector: connector corresponding to the HDMI sink @@ -4720,13 +4730,7 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) if (!(db[8] & (1 << 5))) goto out; - /* Latency_Fields_Present */ - if (db[8] & (1 << 7)) - offset += 2; - - /* I_Latency_Fields_Present */ - if (db[8] & (1 << 6)) - offset += 2; + offset += hdmi_vsdb_latency_length(db); /* the declared length is not long enough for the 2 first bytes * of additional video format capabilities */ -- cgit From 783dedc535703ec6fa7c96754ac264b482821e2f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:27 +0200 Subject: drm/edid: store quirks in display info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although the quirks are internal to EDID parsing, it'll be helpful to store them in display info to avoid having to pass them around. This will also help separate adding probed modes (which needs the quirks) from updating display info. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/819b908f64ad2d158245917f436f24d33a65b95d.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 93067b8dd9f6..41458c80d2af 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6428,18 +6428,20 @@ static void drm_reset_display_info(struct drm_connector *connector) kfree(info->vics); info->vics = NULL; info->vics_len = 0; + + info->quirks = 0; } -static u32 update_display_info(struct drm_connector *connector, - const struct drm_edid *drm_edid) +static void update_display_info(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_display_info *info = &connector->display_info; const struct edid *edid = drm_edid->edid; - u32 quirks = edid_get_quirks(drm_edid); - drm_reset_display_info(connector); + info->quirks = edid_get_quirks(drm_edid); + info->width_mm = edid->width_cm * 10; info->height_mm = edid->height_cm * 10; @@ -6510,17 +6512,15 @@ static u32 update_display_info(struct drm_connector *connector, drm_update_mso(connector, drm_edid); out: - if (quirks & EDID_QUIRK_NON_DESKTOP) { + if (info->quirks & EDID_QUIRK_NON_DESKTOP) { drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Non-desktop display%s\n", connector->base.id, connector->name, info->non_desktop ? " (redundant quirk)" : ""); info->non_desktop = true; } - if (quirks & EDID_QUIRK_CAP_DSC_15BPP) + if (info->quirks & EDID_QUIRK_CAP_DSC_15BPP) info->max_dsc_bpp = 15; - - return quirks; } static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, @@ -6618,8 +6618,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, static int _drm_edid_connector_update(struct drm_connector *connector, const struct drm_edid *drm_edid) { + struct drm_display_info *info = &connector->display_info; int num_modes = 0; - u32 quirks; if (!drm_edid) { drm_reset_display_info(connector); @@ -6632,7 +6632,7 @@ static int _drm_edid_connector_update(struct drm_connector *connector, * To avoid multiple parsing of same block, lets parse that map * from sink info, before parsing CEA modes. */ - quirks = update_display_info(connector, drm_edid); + update_display_info(connector, drm_edid); /* Depends on info->cea_rev set by update_display_info() above */ drm_edid_to_eld(connector, drm_edid); @@ -6651,7 +6651,7 @@ static int _drm_edid_connector_update(struct drm_connector *connector, * * XXX order for additional mode types in extension blocks? */ - num_modes += add_detailed_modes(connector, drm_edid, quirks); + num_modes += add_detailed_modes(connector, drm_edid, info->quirks); num_modes += add_cvt_modes(connector, drm_edid); num_modes += add_standard_modes(connector, drm_edid); num_modes += add_established_modes(connector, drm_edid); @@ -6661,20 +6661,20 @@ static int _drm_edid_connector_update(struct drm_connector *connector, if (drm_edid->edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ) num_modes += add_inferred_modes(connector, drm_edid); - if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) - edid_fixup_preferred(connector, quirks); + if (info->quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) + edid_fixup_preferred(connector, info->quirks); - if (quirks & EDID_QUIRK_FORCE_6BPC) - connector->display_info.bpc = 6; + if (info->quirks & EDID_QUIRK_FORCE_6BPC) + info->bpc = 6; - if (quirks & EDID_QUIRK_FORCE_8BPC) - connector->display_info.bpc = 8; + if (info->quirks & EDID_QUIRK_FORCE_8BPC) + info->bpc = 8; - if (quirks & EDID_QUIRK_FORCE_10BPC) - connector->display_info.bpc = 10; + if (info->quirks & EDID_QUIRK_FORCE_10BPC) + info->bpc = 10; - if (quirks & EDID_QUIRK_FORCE_12BPC) - connector->display_info.bpc = 12; + if (info->quirks & EDID_QUIRK_FORCE_12BPC) + info->bpc = 12; return num_modes; } -- cgit From 4959b693d72d61e4c98bb16bca00c36c9faf524a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:28 +0200 Subject: drm/edid: stop passing quirks around MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that quirks are stored in display info, we can just look them up using the connector instead of having to pass them around. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/d55049dd9b2e48e63103f2dfa49bc9b25dd57f82.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 41458c80d2af..ffc5a2f0781f 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -96,7 +96,6 @@ struct detailed_mode_closure { struct drm_connector *connector; const struct drm_edid *drm_edid; bool preferred; - u32 quirks; int modes; }; @@ -2887,9 +2886,9 @@ static u32 edid_get_quirks(const struct drm_edid *drm_edid) * Walk the mode list for connector, clearing the preferred status on existing * modes and setting it anew for the right mode ala quirks. */ -static void edid_fixup_preferred(struct drm_connector *connector, - u32 quirks) +static void edid_fixup_preferred(struct drm_connector *connector) { + const struct drm_display_info *info = &connector->display_info; struct drm_display_mode *t, *cur_mode, *preferred_mode; int target_refresh = 0; int cur_vrefresh, preferred_vrefresh; @@ -2897,9 +2896,9 @@ static void edid_fixup_preferred(struct drm_connector *connector, if (list_empty(&connector->probed_modes)) return; - if (quirks & EDID_QUIRK_PREFER_LARGE_60) + if (info->quirks & EDID_QUIRK_PREFER_LARGE_60) target_refresh = 60; - if (quirks & EDID_QUIRK_PREFER_LARGE_75) + if (info->quirks & EDID_QUIRK_PREFER_LARGE_75) target_refresh = 75; preferred_mode = list_first_entry(&connector->probed_modes, @@ -3401,9 +3400,9 @@ drm_mode_do_interlace_quirk(struct drm_display_mode *mode, */ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connector, const struct drm_edid *drm_edid, - const struct detailed_timing *timing, - u32 quirks) + const struct detailed_timing *timing) { + const struct drm_display_info *info = &connector->display_info; struct drm_device *dev = connector->dev; struct drm_display_mode *mode; const struct detailed_pixel_timing *pt = &timing->data.pixel_data; @@ -3437,7 +3436,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto return NULL; } - if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) { + if (info->quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) { mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false); if (!mode) return NULL; @@ -3449,7 +3448,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto if (!mode) return NULL; - if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) + if (info->quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) mode->clock = 1088 * 10; else mode->clock = le16_to_cpu(timing->pixel_clock) * 10; @@ -3472,7 +3471,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto drm_mode_do_interlace_quirk(mode, pt); - if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { + if (info->quirks & EDID_QUIRK_DETAILED_SYNC_PP) { mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC; } else { mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? @@ -3485,12 +3484,12 @@ set_size: mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; - if (quirks & EDID_QUIRK_DETAILED_IN_CM) { + if (info->quirks & EDID_QUIRK_DETAILED_IN_CM) { mode->width_mm *= 10; mode->height_mm *= 10; } - if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { + if (info->quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { mode->width_mm = drm_edid->edid->width_cm * 10; mode->height_mm = drm_edid->edid->height_cm * 10; } @@ -4003,8 +4002,7 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) return; newmode = drm_mode_detailed(closure->connector, - closure->drm_edid, timing, - closure->quirks); + closure->drm_edid, timing); if (!newmode) return; @@ -4027,15 +4025,13 @@ do_detailed_mode(const struct detailed_timing *timing, void *c) * add_detailed_modes - Add modes from detailed timings * @connector: attached connector * @drm_edid: EDID block to scan - * @quirks: quirks to apply */ static int add_detailed_modes(struct drm_connector *connector, - const struct drm_edid *drm_edid, u32 quirks) + const struct drm_edid *drm_edid) { struct detailed_mode_closure closure = { .connector = connector, .drm_edid = drm_edid, - .quirks = quirks, }; if (drm_edid->edid->revision >= 4) @@ -6651,7 +6647,7 @@ static int _drm_edid_connector_update(struct drm_connector *connector, * * XXX order for additional mode types in extension blocks? */ - num_modes += add_detailed_modes(connector, drm_edid, info->quirks); + num_modes += add_detailed_modes(connector, drm_edid); num_modes += add_cvt_modes(connector, drm_edid); num_modes += add_standard_modes(connector, drm_edid); num_modes += add_established_modes(connector, drm_edid); @@ -6662,7 +6658,7 @@ static int _drm_edid_connector_update(struct drm_connector *connector, num_modes += add_inferred_modes(connector, drm_edid); if (info->quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) - edid_fixup_preferred(connector, info->quirks); + edid_fixup_preferred(connector); if (info->quirks & EDID_QUIRK_FORCE_6BPC) info->bpc = 6; -- cgit From 45ea02d1ca3b18fa3479a561ea4f6d03737f2d30 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:29 +0200 Subject: drm/edid: merge ELD handling to update_display_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify display info update by merging ELD handling as well as clearing of the data in update_display_info(). The connector->eld really should be moved under display_info altogether, but that's for another time. Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/1f2e7424b998fbcdd9cea488e7d6d7cbb26c460f.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index ffc5a2f0781f..f58b0fdd582b 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5485,8 +5485,6 @@ static void drm_edid_to_eld(struct drm_connector *connector, int total_sad_count = 0; int mnl; - clear_eld(connector); - if (!drm_edid) return; @@ -6432,9 +6430,15 @@ static void update_display_info(struct drm_connector *connector, const struct drm_edid *drm_edid) { struct drm_display_info *info = &connector->display_info; - const struct edid *edid = drm_edid->edid; + const struct edid *edid; drm_reset_display_info(connector); + clear_eld(connector); + + if (!drm_edid) + return; + + edid = drm_edid->edid; info->quirks = edid_get_quirks(drm_edid); @@ -6517,6 +6521,9 @@ out: if (info->quirks & EDID_QUIRK_CAP_DSC_15BPP) info->max_dsc_bpp = 15; + + /* Depends on info->cea_rev set by drm_parse_cea_ext() above */ + drm_edid_to_eld(connector, drm_edid); } static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, @@ -6617,12 +6624,6 @@ static int _drm_edid_connector_update(struct drm_connector *connector, struct drm_display_info *info = &connector->display_info; int num_modes = 0; - if (!drm_edid) { - drm_reset_display_info(connector); - clear_eld(connector); - return 0; - } - /* * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks. * To avoid multiple parsing of same block, lets parse that map @@ -6630,8 +6631,8 @@ static int _drm_edid_connector_update(struct drm_connector *connector, */ update_display_info(connector, drm_edid); - /* Depends on info->cea_rev set by update_display_info() above */ - drm_edid_to_eld(connector, drm_edid); + if (!drm_edid) + return 0; /* * EDID spec says modes should be preferred in this order: @@ -6768,10 +6769,7 @@ static int _drm_connector_update_edid_property(struct drm_connector *connector, * that it seems better to duplicate it rather than attempt to ensure * some arbitrary ordering of calls. */ - if (drm_edid) - update_display_info(connector, drm_edid); - else - drm_reset_display_info(connector); + update_display_info(connector, drm_edid); _drm_update_tile_info(connector, drm_edid); -- cgit From 43bde505d66a41c2ad706d603e97b2c8aa2fbe4a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 4 Jan 2023 12:05:30 +0200 Subject: drm/edid: move EDID BPC quirk application to update_display_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BPC quirks are closer to home in update_display_info(). Cc: Ville Syrjälä Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/8997e0fa3b0fd03c920e72d1dff24c0d96ff4dd0.1672826282.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index f58b0fdd582b..e8b67f3f5c91 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6522,6 +6522,18 @@ out: if (info->quirks & EDID_QUIRK_CAP_DSC_15BPP) info->max_dsc_bpp = 15; + if (info->quirks & EDID_QUIRK_FORCE_6BPC) + info->bpc = 6; + + if (info->quirks & EDID_QUIRK_FORCE_8BPC) + info->bpc = 8; + + if (info->quirks & EDID_QUIRK_FORCE_10BPC) + info->bpc = 10; + + if (info->quirks & EDID_QUIRK_FORCE_12BPC) + info->bpc = 12; + /* Depends on info->cea_rev set by drm_parse_cea_ext() above */ drm_edid_to_eld(connector, drm_edid); } @@ -6621,7 +6633,7 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, static int _drm_edid_connector_update(struct drm_connector *connector, const struct drm_edid *drm_edid) { - struct drm_display_info *info = &connector->display_info; + const struct drm_display_info *info = &connector->display_info; int num_modes = 0; /* @@ -6661,18 +6673,6 @@ static int _drm_edid_connector_update(struct drm_connector *connector, if (info->quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) edid_fixup_preferred(connector); - if (info->quirks & EDID_QUIRK_FORCE_6BPC) - info->bpc = 6; - - if (info->quirks & EDID_QUIRK_FORCE_8BPC) - info->bpc = 8; - - if (info->quirks & EDID_QUIRK_FORCE_10BPC) - info->bpc = 10; - - if (info->quirks & EDID_QUIRK_FORCE_12BPC) - info->bpc = 12; - return num_modes; } -- cgit From 919d320fb8c6a40669ef0eb64aa97326f192415d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 19 Jan 2023 18:18:54 +0200 Subject: drm/edid: split HDMI VSDB info and mode parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate the parsing of display info and modes from the HDMI VSDB. This is prerequisite work for overall better separation of the two parsing steps. The info parsing is about figuring out whether the sink supports HDMI infoframes. Since they were added in HDMI 1.4, assume the sink supports HDMI infoframes if it has the HDMI_Video_present bit set (introduced in HDMI 1.4). For details, see commit f1781e9bb2dd ("drm/edid: Allow HDMI infoframe without VIC or S3D"). The logic is not exactly the same, but since it was somewhat heuristic to begin with, assume this is close enough. v2: - Simplify to only check HDMI_Video_present bit (Ville) - Drop cea_db_raw_size() helper (Ville) Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/238e15f7ab15a86f7fd1812271dcaec9bc6e1506.1674144945.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index e8b67f3f5c91..ee453e39562a 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4713,7 +4713,6 @@ static int hdmi_vsdb_latency_length(const u8 *db) static int do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 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; @@ -4831,8 +4830,6 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) } out: - if (modes > 0) - info->has_hdmi_infoframe = true; return modes; } @@ -6153,6 +6150,7 @@ static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, } } +/* HDMI Vendor-Specific Data Block (HDMI VSDB, H14b-VSDB) */ static void drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db) { @@ -6166,6 +6164,15 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db) if (len >= 7) info->max_tmds_clock = db[7] * 5000; + /* + * Try to infer whether the sink supports HDMI infoframes. + * + * HDMI infoframe support was first added in HDMI 1.4. Assume the sink + * supports infoframes if HDMI_Video_present is set. + */ + if (len >= 8 && db[8] & BIT(5)) + info->has_hdmi_infoframe = true; + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] HDMI: DVI dual %d, max TMDS clock %d kHz\n", connector->base.id, connector->name, info->dvi_dual, info->max_tmds_clock); -- cgit From e8b1f0d4b6ec09fe32a0f66b99a4951214d768ee Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 19 Jan 2023 18:18:55 +0200 Subject: drm/edid: refactor _drm_edid_connector_update() and rename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By moving update_display_info() out of _drm_edid_connector_update() we make the function purely about adding modes. Rename accordingly. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/e9880bbb2b5724d9aac88a90a31ba3ba9af9da3f.1674144945.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index ee453e39562a..f9502df3dbca 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6637,19 +6637,12 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, return num_modes; } -static int _drm_edid_connector_update(struct drm_connector *connector, - const struct drm_edid *drm_edid) +static int _drm_edid_connector_add_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { const struct drm_display_info *info = &connector->display_info; int num_modes = 0; - /* - * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks. - * To avoid multiple parsing of same block, lets parse that map - * from sink info, before parsing CEA modes. - */ - update_display_info(connector, drm_edid); - if (!drm_edid) return 0; @@ -6754,7 +6747,9 @@ int drm_edid_connector_update(struct drm_connector *connector, { int count; - count = _drm_edid_connector_update(connector, drm_edid); + update_display_info(connector, drm_edid); + + count = _drm_edid_connector_add_modes(connector, drm_edid); _drm_update_tile_info(connector, drm_edid); @@ -6824,7 +6819,8 @@ EXPORT_SYMBOL(drm_connector_update_edid_property); */ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) { - struct drm_edid drm_edid; + struct drm_edid _drm_edid; + const struct drm_edid *drm_edid; if (edid && !drm_edid_is_valid(edid)) { drm_warn(connector->dev, "[CONNECTOR:%d:%s] EDID invalid.\n", @@ -6832,8 +6828,11 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) edid = NULL; } - return _drm_edid_connector_update(connector, - drm_edid_legacy_init(&drm_edid, edid)); + drm_edid = drm_edid_legacy_init(&_drm_edid, edid); + + update_display_info(connector, drm_edid); + + return _drm_edid_connector_add_modes(connector, drm_edid); } EXPORT_SYMBOL(drm_add_edid_modes); -- cgit From c533b5167c7e40e82dd724f2a0ffa691f7f386d2 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 19 Jan 2023 18:18:56 +0200 Subject: drm/edid: add separate drm_edid_connector_add_modes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original goal with drm_edid_connector_update() was to have a single call for updating the connector and adding probed modes, in this order, but that turned out to be problematic. Drivers that need to update the connector in the .detect() callback would end up updating the probed modes as well. Turns out the callback may be called so many times that the probed mode list fills up without bounds, and this is amplified by add_alternate_cea_modes() duplicating the CEA modes on every call, actually running out of memory on some machines. Kudos to Imre Deak for explaining this to me. Go back to having separate drm_edid_connector_update() and drm_edid_connector_add_modes() calls. The former may be called from .detect(), .force(), or .get_modes(), but the latter only from .get_modes(). Unlike drm_add_edid_modes(), have drm_edid_connector_add_modes() update the probed modes from the EDID property instead of the passed in EDID. This is mainly to enforce two things: 1) drm_edid_connector_update() must be called before drm_edid_connector_add_modes(). Display info and quirks are needed for parsing the modes, and we don't want to call update_display_info() again to ensure the info is available, like drm_add_edid_modes() does. 2) The same EDID is used for both updating the connector and adding the probed modes. Fortunately, the change is easy, because no driver has actually adopted drm_edid_connector_update(). Not even i915, and that's mainly because of the problem described above. Cc: Imre Deak Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/e86fff1579f14ebf6334692526c8f6831cd02cac.1674144945.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index f9502df3dbca..d0c21d27b978 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6735,30 +6735,54 @@ out: * @connector: Connector * @drm_edid: EDID * - * Update the connector mode list, display info, ELD, HDR metadata, relevant - * properties, etc. from the passed in EDID. + * Update the connector display info, ELD, HDR metadata, relevant properties, + * etc. from the passed in EDID. * * If EDID is NULL, reset the information. * - * Return: The number of modes added or 0 if we couldn't find any. + * Must be called before calling drm_edid_connector_add_modes(). + * + * Return: 0 on success, negative error on errors. */ int drm_edid_connector_update(struct drm_connector *connector, const struct drm_edid *drm_edid) { + update_display_info(connector, drm_edid); + + _drm_update_tile_info(connector, drm_edid); + + return _drm_edid_connector_property_update(connector, drm_edid); +} +EXPORT_SYMBOL(drm_edid_connector_update); + +/** + * drm_edid_connector_add_modes - Update probed modes from the EDID property + * @connector: Connector + * + * Add the modes from the previously updated EDID property to the connector + * probed modes list. + * + * drm_edid_connector_update() must have been called before this to update the + * EDID property. + * + * Return: The number of modes added, or 0 if we couldn't find any. + */ +int drm_edid_connector_add_modes(struct drm_connector *connector) +{ + const struct drm_edid *drm_edid = NULL; int count; - update_display_info(connector, drm_edid); + if (connector->edid_blob_ptr) + drm_edid = drm_edid_alloc(connector->edid_blob_ptr->data, + connector->edid_blob_ptr->length); count = _drm_edid_connector_add_modes(connector, drm_edid); - _drm_update_tile_info(connector, drm_edid); - - /* Note: Ignore errors for now. */ - _drm_edid_connector_property_update(connector, drm_edid); + drm_edid_free(drm_edid); return count; } -EXPORT_SYMBOL(drm_edid_connector_update); +EXPORT_SYMBOL(drm_edid_connector_add_modes); static int _drm_connector_update_edid_property(struct drm_connector *connector, const struct drm_edid *drm_edid) @@ -6813,7 +6837,7 @@ EXPORT_SYMBOL(drm_connector_update_edid_property); * &drm_display_info structure and ELD in @connector with any information which * can be derived from the edid. * - * This function is deprecated. Use drm_edid_connector_update() instead. + * This function is deprecated. Use drm_edid_connector_add_modes() instead. * * Return: The number of modes added or 0 if we couldn't find any. */ -- cgit From b494d6283deb97d18b527bbfe88e5c7be7db05ed Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 19 Jan 2023 18:18:57 +0200 Subject: drm/edid: remove redundant _drm_connector_update_edid_property() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Realize that drm_edid_connector_update() and _drm_connector_update_edid_property() are now the same thing. Drop the latter. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/712cc299afe33d8f6279a15d5b0117aeeab88bb4.1674144945.git.jani.nikula@intel.com --- drivers/gpu/drm/drm_edid.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) (limited to 'drivers/gpu/drm/drm_edid.c') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d0c21d27b978..3d0a4da661bc 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6784,24 +6784,6 @@ int drm_edid_connector_add_modes(struct drm_connector *connector) } EXPORT_SYMBOL(drm_edid_connector_add_modes); -static int _drm_connector_update_edid_property(struct drm_connector *connector, - const struct drm_edid *drm_edid) -{ - /* - * Set the display info, using edid if available, otherwise resetting - * the values to defaults. This duplicates the work done in - * drm_add_edid_modes, but that function is not consistently called - * before this one in all drivers and the computation is cheap enough - * that it seems better to duplicate it rather than attempt to ensure - * some arbitrary ordering of calls. - */ - update_display_info(connector, drm_edid); - - _drm_update_tile_info(connector, drm_edid); - - return _drm_edid_connector_property_update(connector, drm_edid); -} - /** * drm_connector_update_edid_property - update the edid property of a connector * @connector: drm connector @@ -6823,8 +6805,7 @@ int drm_connector_update_edid_property(struct drm_connector *connector, { struct drm_edid drm_edid; - return _drm_connector_update_edid_property(connector, - drm_edid_legacy_init(&drm_edid, edid)); + return drm_edid_connector_update(connector, drm_edid_legacy_init(&drm_edid, edid)); } EXPORT_SYMBOL(drm_connector_update_edid_property); -- cgit