Commit 0883ce81 authored by Lyude Paul's avatar Lyude Paul
Browse files

drm/dp: Introduce EDID-based quirks



The whole point of using OUIs is so that we can recognize certain
devices and potentially apply quirks for them. Normally this should work
quite well, but there appears to be quite a number of laptop panels out
there that will fill the OUI but not the device ID. As such, for devices
like this I can't imagine it's a very good idea to try relying on OUIs
for applying quirks. As well, some laptop vendors have confirmed to us
that their panels have this exact issue.

So, let's introduce the ability to apply DP quirks based on EDID
identification. We reuse the same quirk bits for OUI-based quirks, so
that callers can simply check all possible quirks using
drm_dp_has_quirk().

Signed-off-by: default avatarLyude Paul <lyude@redhat.com>
Cc: Jani Nikula <jani.nikula@intel.com>
Reviewed-by: default avatarAdam Jackson <ajax@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200211183358.157448-2-lyude@redhat.com
parent 391615d9
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
@@ -1221,6 +1221,67 @@ drm_dp_get_quirks(const struct drm_dp_dpcd_ident *ident, bool is_branch)
#undef DEVICE_ID_ANY
#undef DEVICE_ID

struct edid_quirk {
	u8 mfg_id[2];
	u8 prod_id[2];
	u32 quirks;
};

#define MFG(first, second) { (first), (second) }
#define PROD_ID(first, second) { (first), (second) }

/*
 * Some devices have unreliable OUIDs where they don't set the device ID
 * correctly, and as a result we need to use the EDID for finding additional
 * DP quirks in such cases.
 */
static const struct edid_quirk edid_quirk_list[] = {
};

#undef MFG
#undef PROD_ID

/**
 * drm_dp_get_edid_quirks() - Check the EDID of a DP device to find additional
 * DP-specific quirks
 * @edid: The EDID to check
 *
 * While OUIDs are meant to be used to recognize a DisplayPort device, a lot
 * of manufacturers don't seem to like following standards and neglect to fill
 * the dev-ID in, making it impossible to only use OUIDs for determining
 * quirks in some cases. This function can be used to check the EDID and look
 * up any additional DP quirks. The bits returned by this function correspond
 * to the quirk bits in &drm_dp_quirk.
 *
 * Returns: a bitmask of quirks, if any. The driver can check this using
 * drm_dp_has_quirk().
 */
u32 drm_dp_get_edid_quirks(const struct edid *edid)
{
	const struct edid_quirk *quirk;
	u32 quirks = 0;
	int i;

	if (!edid)
		return 0;

	for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
		quirk = &edid_quirk_list[i];
		if (memcmp(quirk->mfg_id, edid->mfg_id,
			   sizeof(edid->mfg_id)) == 0 &&
		    memcmp(quirk->prod_id, edid->prod_code,
			   sizeof(edid->prod_code)) == 0)
			quirks |= quirk->quirks;
	}

	DRM_DEBUG_KMS("DP sink: EDID mfg %*phD prod-ID %*phD quirks: 0x%04x\n",
		      (int)sizeof(edid->mfg_id), edid->mfg_id,
		      (int)sizeof(edid->prod_code), edid->prod_code, quirks);

	return quirks;
}
EXPORT_SYMBOL(drm_dp_get_edid_quirks);

/**
 * drm_dp_read_desc - read sink/branch descriptor from DPCD
 * @aux: DisplayPort AUX channel
+2 −1
Original line number Diff line number Diff line
@@ -5452,7 +5452,8 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port)
	if (drm_dp_read_desc(port->mgr->aux, &desc, true))
		return NULL;

	if (drm_dp_has_quirk(&desc, DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) &&
	if (drm_dp_has_quirk(&desc, 0,
			     DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) &&
	    port->mgr->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14 &&
	    port->parent == port->mgr->mst_primary) {
		u8 downstreamport;
+1 −0
Original line number Diff line number Diff line
@@ -1280,6 +1280,7 @@ struct intel_dp {
	int max_link_rate;
	/* sink or branch descriptor */
	struct drm_dp_desc desc;
	u32 edid_quirks;
	struct drm_dp_aux aux;
	u32 aux_busy_last_status;
	u8 train_set[4];
+7 −4
Original line number Diff line number Diff line
@@ -2398,7 +2398,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
	struct intel_connector *intel_connector = intel_dp->attached_connector;
	struct intel_digital_connector_state *intel_conn_state =
		to_intel_digital_connector_state(conn_state);
	bool constant_n = drm_dp_has_quirk(&intel_dp->desc,
	bool constant_n = drm_dp_has_quirk(&intel_dp->desc, 0,
					   DP_DPCD_QUIRK_CONSTANT_N);
	int ret = 0, output_bpp;

@@ -4514,7 +4514,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
	 * it don't care about read it here and in intel_edp_init_dpcd().
	 */
	if (!intel_dp_is_edp(intel_dp) &&
	    !drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_NO_SINK_COUNT)) {
	    !drm_dp_has_quirk(&intel_dp->desc, 0,
			      DP_DPCD_QUIRK_NO_SINK_COUNT)) {
		u8 count;
		ssize_t r;

@@ -5681,6 +5682,7 @@ intel_dp_set_edid(struct intel_dp *intel_dp)

	intel_dp->has_audio = drm_detect_monitor_audio(edid);
	drm_dp_cec_set_edid(&intel_dp->aux, edid);
	intel_dp->edid_quirks = drm_dp_get_edid_quirks(edid);
}

static void
@@ -5693,6 +5695,7 @@ intel_dp_unset_edid(struct intel_dp *intel_dp)
	intel_connector->detect_edid = NULL;

	intel_dp->has_audio = false;
	intel_dp->edid_quirks = 0;
}

static int
@@ -7565,8 +7568,8 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
	edid = drm_get_edid(connector, &intel_dp->aux.ddc);
	if (edid) {
		if (drm_add_edid_modes(connector, edid)) {
			drm_connector_update_edid_property(connector,
								edid);
			drm_connector_update_edid_property(connector, edid);
			intel_dp->edid_quirks = drm_dp_get_edid_quirks(edid);
		} else {
			kfree(edid);
			edid = ERR_PTR(-EINVAL);
+1 −1
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
	const struct drm_display_mode *adjusted_mode =
		&crtc_state->hw.adjusted_mode;
	void *port = connector->port;
	bool constant_n = drm_dp_has_quirk(&intel_dp->desc,
	bool constant_n = drm_dp_has_quirk(&intel_dp->desc, 0,
					   DP_DPCD_QUIRK_CONSTANT_N);
	int bpp, slots = -EINVAL;

Loading