Commit 18a9cbbe authored by Jani Nikula's avatar Jani Nikula
Browse files

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: default avatarUma Shankar <uma.shankar@intel.com>
Acked-by: default avatarMaxime Ripard <maxime@cerno.tech>
Signed-off-by: default avatarJani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/73ca2887e7b37880690f5c9ba4594c9cd1170669.1630419362.git.jani.nikula@intel.com
parent 37eab1fe
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -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;
}

+12 −0
Original line number Diff line number Diff line
@@ -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,
+13 −0
Original line number Diff line number Diff line
@@ -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;