diff options
Diffstat (limited to 'drivers/gpu/drm/drm_dp_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_dp_helper.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 46d88ef4f59a..9ca88e6c7882 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -433,6 +433,83 @@ static u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) return port_count; } +static int drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux, + u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + u8 dpcd_ext[6]; + int ret; + + /* + * Prior to DP1.3 the bit represented by + * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved. + * If it is set DP_DPCD_REV at 0000h could be at a value less than + * the true capability of the panel. The only way to check is to + * then compare 0000h and 2200h. + */ + if (!(dpcd[DP_TRAINING_AUX_RD_INTERVAL] & + DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)) + return 0; + + ret = drm_dp_dpcd_read(aux, DP_DP13_DPCD_REV, &dpcd_ext, + sizeof(dpcd_ext)); + if (ret < 0) + return ret; + if (ret != sizeof(dpcd_ext)) + return -EIO; + + if (dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) { + DRM_DEBUG_KMS("%s: Extended DPCD rev less than base DPCD rev (%d > %d)\n", + aux->name, dpcd[DP_DPCD_REV], + dpcd_ext[DP_DPCD_REV]); + return 0; + } + + if (!memcmp(dpcd, dpcd_ext, sizeof(dpcd_ext))) + return 0; + + DRM_DEBUG_KMS("%s: Base DPCD: %*ph\n", + aux->name, DP_RECEIVER_CAP_SIZE, dpcd); + + memcpy(dpcd, dpcd_ext, sizeof(dpcd_ext)); + + return 0; +} + +/** + * drm_dp_read_dpcd_caps() - read DPCD caps and extended DPCD caps if + * available + * @aux: DisplayPort AUX channel + * @dpcd: Buffer to store the resulting DPCD in + * + * Attempts to read the base DPCD caps for @aux. Additionally, this function + * checks for and reads the extended DPRX caps (%DP_DP13_DPCD_REV) if + * present. + * + * Returns: %0 if the DPCD was read successfully, negative error code + * otherwise. + */ +int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux, + u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + int ret; + + ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); + if (ret < 0) + return ret; + if (ret != DP_RECEIVER_CAP_SIZE || dpcd[DP_DPCD_REV] == 0) + return -EIO; + + ret = drm_dp_read_extended_dpcd_caps(aux, dpcd); + if (ret < 0) + return ret; + + DRM_DEBUG_KMS("%s: DPCD: %*ph\n", + aux->name, DP_RECEIVER_CAP_SIZE, dpcd); + + return ret; +} +EXPORT_SYMBOL(drm_dp_read_dpcd_caps); + /** * drm_dp_read_downstream_info() - read DPCD downstream port info if available * @aux: DisplayPort AUX channel |