336 lines
12 KiB
Diff
336 lines
12 KiB
Diff
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
|
|
@@ -7096,6 +7096,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,
|
|
@@ -7616,6 +7621,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;
|
|
@@ -7707,6 +7713,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 <drm/drm_edid.h>
|
|
#include <drm/drm_eld.h>
|
|
#include <drm/drm_encoder.h>
|
|
+#include <drm/drm_fixed.h>
|
|
#include <drm/drm_print.h>
|
|
|
|
#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;
|
|
};
|
|
|
|
/**
|