drm/edid: parse the DisplayID v2.0 VESA vendor block for MSO

The VESA Organization Vendor-Specific Data Block, defined in VESA
DisplayID Standard v2.0, specifies the eDP Multi-SST Operation (MSO)
stream count and segment pixel overlap.

DisplayID v1.3 has Appendix B: DisplayID as an EDID Extension,
describing how DisplayID sections may be embedded in EDID extension
blocks. DisplayID v2.0 does not have such a section, perhaps implying
that DisplayID v2.0 data should not be included in EDID extensions, but
rather in a "pure" DisplayID structure at its own DDC address pair
A4h/A5h, as described in VESA E-DDC Standard v1.3 chapter 3.

However, in practice, displays out in the field have embedded DisplayID
v2.0 data blocks in EDID extensions, including, in particular, some eDP
MSO displays, where a pure DisplayID structure is not available at all.

Parse the MSO data from the DisplayID data block. Do it as part of
drm_add_display_info(), extending it to parse also DisplayID data to
avoid requiring extra calls to update the information.

v2: Check for VESA OUI (Ville)

Reviewed-by: Uma Shankar <uma.shankar@intel.com>
Acked-by: Maxime Ripard <maxime@cerno.tech>
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/73ca2887e7b37880690f5c9ba4594c9cd1170669.1630419362.git.jani.nikula@intel.com
This commit is contained in:
Jani Nikula 2021-08-31 17:17:33 +03:00
parent 37eab1fe61
commit 18a9cbbe55
3 changed files with 97 additions and 0 deletions

View File

@ -28,6 +28,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#include <linux/bitfield.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
@ -5145,6 +5146,71 @@ void drm_get_monitor_range(struct drm_connector *connector,
info->monitor_range.max_vfreq);
}
static void drm_parse_vesa_mso_data(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;
if (block->num_bytes < 3) {
drm_dbg_kms(connector->dev, "Unexpected vendor block size %u\n",
block->num_bytes);
return;
}
if (oui(vesa->oui[0], vesa->oui[1], vesa->oui[2]) != VESA_IEEE_OUI)
return;
if (sizeof(*vesa) != sizeof(*block) + block->num_bytes) {
drm_dbg_kms(connector->dev, "Unexpected VESA vendor block size\n");
return;
}
switch (FIELD_GET(DISPLAYID_VESA_MSO_MODE, vesa->mso)) {
default:
drm_dbg_kms(connector->dev, "Reserved MSO mode value\n");
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 = 0;
return;
}
info->mso_pixel_overlap = FIELD_GET(DISPLAYID_VESA_MSO_OVERLAP, vesa->mso);
if (info->mso_pixel_overlap > 8) {
drm_dbg_kms(connector->dev, "Reserved MSO pixel overlap value %u\n",
info->mso_pixel_overlap);
info->mso_pixel_overlap = 8;
}
drm_dbg_kms(connector->dev, "MSO stream count %u, pixel overlap %u\n",
info->mso_stream_count, info->mso_pixel_overlap);
}
static void drm_update_mso(struct drm_connector *connector, const struct edid *edid)
{
const struct displayid_block *block;
struct displayid_iter iter;
displayid_iter_edid_begin(edid, &iter);
displayid_iter_for_each(block, &iter) {
if (block->tag == DATA_BLOCK_2_VENDOR_SPECIFIC)
drm_parse_vesa_mso_data(connector, block);
}
displayid_iter_end(&iter);
}
/* A connector has no EDID information, so we've got no EDID to compute quirks from. Reset
* all of the values which would have been set from EDID
*/
@ -5168,6 +5234,9 @@ drm_reset_display_info(struct drm_connector *connector)
info->non_desktop = 0;
memset(&info->monitor_range, 0, sizeof(info->monitor_range));
info->mso_stream_count = 0;
info->mso_pixel_overlap = 0;
}
u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid)
@ -5246,6 +5315,9 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi
info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
drm_update_mso(connector, edid);
return quirks;
}

View File

@ -590,6 +590,18 @@ struct drm_display_info {
* @monitor_range: Frequency range supported by monitor range descriptor
*/
struct drm_monitor_range_info monitor_range;
/**
* @mso_stream_count: eDP Multi-SST Operation (MSO) stream count from
* the DisplayID VESA vendor block. 0 for conventional Single-Stream
* Transport (SST), or 2 or 4 MSO streams.
*/
u8 mso_stream_count;
/**
* @mso_pixel_overlap: eDP MSO segment pixel overlap, 0-8 pixels.
*/
u8 mso_pixel_overlap;
};
int drm_display_info_set_bus_formats(struct drm_display_info *info,

View File

@ -23,9 +23,12 @@
#define DRM_DISPLAYID_H
#include <linux/types.h>
#include <linux/bits.h>
struct edid;
#define VESA_IEEE_OUI 0x3a0292
/* DisplayID Structure versions */
#define DISPLAY_ID_STRUCTURE_VER_12 0x12
#define DISPLAY_ID_STRUCTURE_VER_20 0x20
@ -126,6 +129,16 @@ struct displayid_detailed_timing_block {
struct displayid_detailed_timings_1 timings[];
};
#define DISPLAYID_VESA_MSO_OVERLAP GENMASK(3, 0)
#define DISPLAYID_VESA_MSO_MODE GENMASK(6, 5)
struct displayid_vesa_vendor_specific_block {
struct displayid_block base;
u8 oui[3];
u8 data_structure_type;
u8 mso;
} __packed;
/* DisplayID iteration */
struct displayid_iter {
const struct edid *edid;