diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -7116,6 +7116,11 @@ static void fill_stream_properties_from_drm_display_mode( stream->output_color_space = get_output_color_space(timing_out, connector_state); stream->content_type = get_output_content_type(connector_state); + + /* DisplayID Type VII pass-through timings. */ + if (mode_in->dsc_passthrough_timings_support && info->dp_dsc_bpp_x16 != 0) { + stream->timing.dsc_fixed_bits_per_pixel_x16 = info->dp_dsc_bpp_x16; + } } static void fill_audio_info(struct audio_info *audio_info, @@ -7636,6 +7641,7 @@ create_stream_for_sink(struct drm_connector *connector, struct drm_display_mode mode; struct drm_display_mode saved_mode; struct drm_display_mode *freesync_mode = NULL; + struct drm_display_mode *dsc_passthru_mode = NULL; bool native_mode_found = false; bool recalculate_timing = false; bool scale = dm_state->scaling != RMX_OFF; @@ -7727,6 +7733,16 @@ create_stream_for_sink(struct drm_connector *connector, } } + list_for_each_entry(dsc_passthru_mode, &connector->modes, head) { + if (dsc_passthru_mode->hdisplay == mode.hdisplay && + dsc_passthru_mode->vdisplay == mode.vdisplay && + drm_mode_vrefresh(dsc_passthru_mode) == mode_refresh) { + mode.dsc_passthrough_timings_support = + dsc_passthru_mode->dsc_passthrough_timings_support; + break; + } + } + if (recalculate_timing) drm_mode_set_crtcinfo(&saved_mode, 0); diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h --- a/drivers/gpu/drm/drm_displayid_internal.h +++ b/drivers/gpu/drm/drm_displayid_internal.h @@ -98,6 +98,7 @@ struct displayid_header { u8 ext_count; } __packed; +#define DISPLAYID_BLOCK_REV GENMASK(2, 0) struct displayid_block { u8 tag; u8 rev; @@ -126,6 +127,7 @@ struct displayid_detailed_timings_1 { __le16 vsw; } __packed; +#define DISPLAYID_BLOCK_PASSTHROUGH_TIMINGS_SUPPORT BIT(3) struct displayid_detailed_timing_block { struct displayid_block base; struct displayid_detailed_timings_1 timings[]; @@ -138,19 +140,28 @@ struct displayid_formula_timings_9 { u8 vrefresh; } __packed; +#define DISPLAYID_BLOCK_DESCRIPTOR_PAYLOAD_BYTES GENMASK(6, 4) struct displayid_formula_timing_block { struct displayid_block base; struct displayid_formula_timings_9 timings[]; } __packed; +#define DISPLAYID_VESA_DP_TYPE GENMASK(2, 0) #define DISPLAYID_VESA_MSO_OVERLAP GENMASK(3, 0) #define DISPLAYID_VESA_MSO_MODE GENMASK(6, 5) +#define DISPLAYID_VESA_DSC_BPP_INT GENMASK(5, 0) +#define DISPLAYID_VESA_DSC_BPP_FRACT GENMASK(3, 0) + +#define DISPLAYID_VESA_DP_TYPE_EDP 0 +#define DISPLAYID_VESA_DP_TYPE_DP 1 struct displayid_vesa_vendor_specific_block { struct displayid_block base; u8 oui[3]; u8 data_structure_type; u8 mso; + u8 dsc_bpp_int; + u8 dsc_bpp_fract; } __packed; /* diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include "drm_crtc_internal.h" @@ -6683,12 +6684,13 @@ static bool drm_update_displayid_adaptive_sync_range(struct drm_connector *conne return false; } -static void drm_parse_vesa_mso_data(struct drm_connector *connector, - const struct displayid_block *block) +static void drm_parse_vesa_specific_block(struct drm_connector *connector, + const struct displayid_block *block) { struct displayid_vesa_vendor_specific_block *vesa = (struct displayid_vesa_vendor_specific_block *)block; struct drm_display_info *info = &connector->display_info; + int dp_type; if (block->num_bytes < 3) { drm_dbg_kms(connector->dev, @@ -6700,51 +6702,73 @@ static void drm_parse_vesa_mso_data(struct drm_connector *connector, if (oui(vesa->oui[0], vesa->oui[1], vesa->oui[2]) != VESA_IEEE_OUI) return; - if (sizeof(*vesa) != sizeof(*block) + block->num_bytes) { + if (block->num_bytes < 5) { drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Unexpected VESA vendor block size\n", connector->base.id, connector->name); return; } - switch (FIELD_GET(DISPLAYID_VESA_MSO_MODE, vesa->mso)) { - default: - drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Reserved MSO mode value\n", + dp_type = FIELD_GET(DISPLAYID_VESA_DP_TYPE, vesa->data_structure_type); + if (dp_type > 1) { + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Reserved dp type value\n", connector->base.id, connector->name); - fallthrough; - case 0: - info->mso_stream_count = 0; - break; - case 1: - info->mso_stream_count = 2; /* 2 or 4 links */ - break; - case 2: - info->mso_stream_count = 4; /* 4 links */ - break; } - if (!info->mso_stream_count) { + /* MSO is only supported for eDP */ + if (dp_type == DISPLAYID_VESA_DP_TYPE_EDP) { + switch (FIELD_GET(DISPLAYID_VESA_MSO_MODE, vesa->mso)) { + default: + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Reserved MSO mode value\n", + connector->base.id, connector->name); + fallthrough; + case 0: + info->mso_stream_count = 0; + break; + case 1: + info->mso_stream_count = 2; /* 2 or 4 links */ + break; + case 2: + info->mso_stream_count = 4; /* 4 links */ + break; + } + } + + if (info->mso_stream_count) { + info->mso_pixel_overlap = FIELD_GET(DISPLAYID_VESA_MSO_OVERLAP, vesa->mso); + if (info->mso_pixel_overlap > 8) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Reserved MSO pixel overlap value %u\n", + connector->base.id, connector->name, + info->mso_pixel_overlap); + info->mso_pixel_overlap = 8; + } + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] MSO stream count %u, pixel overlap %u\n", + connector->base.id, connector->name, + info->mso_stream_count, info->mso_pixel_overlap); + } else { info->mso_pixel_overlap = 0; + } + + if (block->num_bytes < 7) { + /* DSC bpp is optional */ return; } - info->mso_pixel_overlap = FIELD_GET(DISPLAYID_VESA_MSO_OVERLAP, vesa->mso); - if (info->mso_pixel_overlap > 8) { - drm_dbg_kms(connector->dev, - "[CONNECTOR:%d:%s] Reserved MSO pixel overlap value %u\n", - connector->base.id, connector->name, - info->mso_pixel_overlap); - info->mso_pixel_overlap = 8; - } + info->dp_dsc_bpp_x16 = FIELD_GET(DISPLAYID_VESA_DSC_BPP_INT, vesa->dsc_bpp_int) << 4 | + FIELD_GET(DISPLAYID_VESA_DSC_BPP_FRACT, vesa->dsc_bpp_fract); - drm_dbg_kms(connector->dev, - "[CONNECTOR:%d:%s] MSO stream count %u, pixel overlap %u\n", - connector->base.id, connector->name, - info->mso_stream_count, info->mso_pixel_overlap); + if (info->dp_dsc_bpp_x16 > 0) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] DSC bits per pixel " FXP_Q4_FMT "\n", + connector->base.id, connector->name, + FXP_Q4_ARGS(info->dp_dsc_bpp_x16)); + } } -static void drm_update_mso(struct drm_connector *connector, - const struct drm_edid *drm_edid) +static void drm_update_vesa_specific_block(struct drm_connector *connector, + const struct drm_edid *drm_edid) { const struct displayid_block *block; struct displayid_iter iter; @@ -6752,7 +6776,7 @@ static void drm_update_mso(struct drm_connector *connector, displayid_iter_edid_begin(drm_edid, &iter); displayid_iter_for_each(block, &iter) { if (block->tag == DATA_BLOCK_2_VENDOR_SPECIFIC) - drm_parse_vesa_mso_data(connector, block); + drm_parse_vesa_specific_block(connector, block); } displayid_iter_end(&iter); } @@ -6789,6 +6813,7 @@ 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; + info->dp_dsc_bpp_x16 = 0; kfree(info->vics); info->vics = NULL; @@ -6930,7 +6955,7 @@ static void update_display_info(struct drm_connector *connector, if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422) info->color_formats |= BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422); - drm_update_mso(connector, drm_edid); + drm_update_vesa_specific_block(connector, drm_edid); out: if (drm_edid_has_internal_quirk(connector, EDID_QUIRK_NON_DESKTOP)) { @@ -6960,8 +6985,8 @@ static void update_display_info(struct drm_connector *connector, } static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, - const struct displayid_detailed_timings_1 *timings, - bool type_7) + const struct displayid_block *block, + const struct displayid_detailed_timings_1 *timings) { struct drm_display_mode *mode; unsigned int pixel_clock = (timings->pixel_clock[0] | @@ -6977,11 +7002,16 @@ static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *d unsigned int vsync_width = le16_to_cpu(timings->vsw) + 1; bool hsync_positive = le16_to_cpu(timings->hsync) & (1 << 15); bool vsync_positive = le16_to_cpu(timings->vsync) & (1 << 15); + bool type_7 = block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING; mode = drm_mode_create(dev); if (!mode) return NULL; + if (type_7 && FIELD_GET(DISPLAYID_BLOCK_REV, block->rev) >= 1) + mode->dsc_passthrough_timings_support = + block->rev & DISPLAYID_BLOCK_PASSTHROUGH_TIMINGS_SUPPORT; + /* resolution is kHz for type VII, and 10 kHz for type I */ mode->clock = type_7 ? pixel_clock : pixel_clock * 10; mode->hdisplay = hactive; @@ -7014,7 +7044,6 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector, int num_timings; struct drm_display_mode *newmode; int num_modes = 0; - bool type_7 = block->tag == DATA_BLOCK_2_TYPE_7_DETAILED_TIMING; /* blocks must be multiple of 20 bytes length */ if (block->num_bytes % 20) return 0; @@ -7023,7 +7052,7 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector, for (i = 0; i < num_timings; i++) { struct displayid_detailed_timings_1 *timings = &det->timings[i]; - newmode = drm_mode_displayid_detailed(connector->dev, timings, type_7); + newmode = drm_mode_displayid_detailed(connector->dev, block, timings); if (!newmode) continue; @@ -7070,7 +7099,8 @@ static int add_displayid_formula_modes(struct drm_connector *connector, 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); + int timing_size = 6 + + FIELD_GET(DISPLAYID_BLOCK_DESCRIPTOR_PAYLOAD_BYTES, formula_block->base.rev); /* extended blocks are not supported yet */ if (timing_size != 6) diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -939,6 +939,12 @@ struct drm_display_info { */ u32 max_dsc_bpp; + /** + * @dp_dsc_bpp: DP Display-Stream-Compression (DSC) timing's target + * DSC bits per pixel in 6.4 fixed point format. 0 means undefined. + */ + u16 dp_dsc_bpp_x16; + /** * @vics: Array of vics_len VICs. Internal to EDID parsing. */ diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -417,6 +417,16 @@ struct drm_display_mode { */ enum hdmi_picture_aspect picture_aspect_ratio; + /** + * @dsc_passthrough_timing_support: + * + * Indicates whether this mode timing descriptor is supported + * with specific target DSC bits per pixel only. + * + * VESA vendor-specific data block shall exist with the relevant + * DSC bits per pixel declaration when this flag is set to true. + */ + bool dsc_passthrough_timings_support; }; /**