aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_edid.c
diff options
context:
space:
mode:
authorEgor Vorontsov <sdoregor@sdore.me>2025-04-15 12:13:30 +0300
committerJani Nikula <jani.nikula@intel.com>2025-04-16 20:40:51 +0300
commite79ce1639a865d93fa8c27b515e8165c60131c9b (patch)
treefa520d7f9aef8ad1af2710640ca38bbc9b0a1aea /drivers/gpu/drm/drm_edid.c
parentdrm/panel: boe-bf060y8m-aj0: transition to mipi_dsi wrapped functions (diff)
downloadlinux-e79ce1639a865d93fa8c27b515e8165c60131c9b.tar.gz
linux-e79ce1639a865d93fa8c27b515e8165c60131c9b.zip
drm/edid: Implement DisplayID Type IX & X timing blocks parsing
Some newer high refresh rate consumer monitors (including those by Samsung) make use of DisplayID 2.1 timing blocks in their EDID data, notably for their highest refresh rate modes. Such modes won't be available as of now. Implement partial support for such blocks in order to enable native support of HRR modes of most such monitors for users without having to rely on EDID patching/override (or need thereof). Closes: https://gitlab.freedesktop.org/drm/misc/kernel/-/issues/55 Suggested-by: Maximilian Boße <max@bosse.io> Signed-off-by: Egor Vorontsov <sdoregor@sdore.me> Reviewed-by: Jani Nikula <jani.nikula@intel.com> Link: https://lore.kernel.org/r/d795d27c6f60596df402ff151ce29938e2ad4f53.1744708239.git.jani.nikula@intel.com Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r--drivers/gpu/drm/drm_edid.c63
1 files changed, 63 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 1e69326283dc..002aa0832763 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -6833,6 +6833,66 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector,
return num_modes;
}
+static struct drm_display_mode *drm_mode_displayid_formula(struct drm_device *dev,
+ const struct displayid_formula_timings_9 *timings,
+ bool type_10)
+{
+ struct drm_display_mode *mode;
+ u16 hactive = le16_to_cpu(timings->hactive) + 1;
+ u16 vactive = le16_to_cpu(timings->vactive) + 1;
+ u8 timing_formula = timings->flags & 0x7;
+
+ /* TODO: support RB-v2 & RB-v3 */
+ if (timing_formula > 1)
+ return NULL;
+
+ /* TODO: support video-optimized refresh rate */
+ if (timings->flags & (1 << 4))
+ drm_dbg_kms(dev, "Fractional vrefresh is not implemented, proceeding with non-video-optimized refresh rate");
+
+ mode = drm_cvt_mode(dev, hactive, vactive, timings->vrefresh + 1, timing_formula == 1, false, false);
+ if (!mode)
+ return NULL;
+
+ /* TODO: interpret S3D flags */
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+ drm_mode_set_name(mode);
+
+ return mode;
+}
+
+static int add_displayid_formula_modes(struct drm_connector *connector,
+ const struct displayid_block *block)
+{
+ const struct displayid_formula_timing_block *formula_block = (struct displayid_formula_timing_block *)block;
+ int num_timings;
+ struct drm_display_mode *newmode;
+ int num_modes = 0;
+ bool type_10 = block->tag == DATA_BLOCK_2_TYPE_10_FORMULA_TIMING;
+ int timing_size = 6 + ((formula_block->base.rev & 0x70) >> 4);
+
+ /* extended blocks are not supported yet */
+ if (timing_size != 6)
+ return 0;
+
+ if (block->num_bytes % timing_size)
+ return 0;
+
+ num_timings = block->num_bytes / timing_size;
+ for (int i = 0; i < num_timings; i++) {
+ const struct displayid_formula_timings_9 *timings = &formula_block->timings[i];
+
+ newmode = drm_mode_displayid_formula(connector->dev, timings, type_10);
+ if (!newmode)
+ continue;
+
+ drm_mode_probed_add(connector, newmode);
+ num_modes++;
+ }
+ return num_modes;
+}
+
static int add_displayid_detailed_modes(struct drm_connector *connector,
const struct drm_edid *drm_edid)
{
@@ -6845,6 +6905,9 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,
if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING ||
block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING)
num_modes += add_displayid_detailed_1_modes(connector, block);
+ else if (block->tag == DATA_BLOCK_2_TYPE_9_FORMULA_TIMING ||
+ block->tag == DATA_BLOCK_2_TYPE_10_FORMULA_TIMING)
+ num_modes += add_displayid_formula_modes(connector, block);
}
displayid_iter_end(&iter);