From 186784c03e990ad012a1c4be8fe8a25324b26ae6 Mon Sep 17 00:00:00 2001 From: jsg Date: Sat, 15 Jun 2024 04:05:42 +0000 Subject: [PATCH] drm/dp: Don't attempt AUX transfers when eDP panels are not powered From Douglas Anderson 9429b12dfcbd7eca89795730305cd1400bf97ec9 in linux-6.6.y/6.6.33 8df1ddb5bf11ab820ad991e164dab82c0960add9 in mainline linux --- sys/dev/pci/drm/display/drm_dp_helper.c | 35 +++++++++++++++++++ .../drm/include/drm/display/drm_dp_helper.h | 6 ++++ 2 files changed, 41 insertions(+) diff --git a/sys/dev/pci/drm/display/drm_dp_helper.c b/sys/dev/pci/drm/display/drm_dp_helper.c index cdcb0627942..c2926c2eb6f 100644 --- a/sys/dev/pci/drm/display/drm_dp_helper.c +++ b/sys/dev/pci/drm/display/drm_dp_helper.c @@ -532,6 +532,15 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, mutex_lock(&aux->hw_mutex); + /* + * If the device attached to the aux bus is powered down then there's + * no reason to attempt a transfer. Error out immediately. + */ + if (aux->powered_down) { + ret = -EBUSY; + goto unlock; + } + /* * The specification doesn't give any recommendation on how often to * retry native transactions. We used to retry 7 times like for @@ -599,6 +608,29 @@ int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset) } EXPORT_SYMBOL(drm_dp_dpcd_probe); +/** + * drm_dp_dpcd_set_powered() - Set whether the DP device is powered + * @aux: DisplayPort AUX channel; for convenience it's OK to pass NULL here + * and the function will be a no-op. + * @powered: true if powered; false if not + * + * If the endpoint device on the DP AUX bus is known to be powered down + * then this function can be called to make future transfers fail immediately + * instead of needing to time out. + * + * If this function is never called then a device defaults to being powered. + */ +void drm_dp_dpcd_set_powered(struct drm_dp_aux *aux, bool powered) +{ + if (!aux) + return; + + mutex_lock(&aux->hw_mutex); + aux->powered_down = !powered; + mutex_unlock(&aux->hw_mutex); +} +EXPORT_SYMBOL(drm_dp_dpcd_set_powered); + /** * drm_dp_dpcd_read() - read a series of bytes from the DPCD * @aux: DisplayPort AUX channel (SST or MST) @@ -1855,6 +1887,9 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, struct drm_dp_aux_msg msg; int err = 0; + if (aux->powered_down) + return -EBUSY; + dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES); memset(&msg, 0, sizeof(msg)); diff --git a/sys/dev/pci/drm/include/drm/display/drm_dp_helper.h b/sys/dev/pci/drm/include/drm/display/drm_dp_helper.h index 6aa750e3cba..133d996b4d0 100644 --- a/sys/dev/pci/drm/include/drm/display/drm_dp_helper.h +++ b/sys/dev/pci/drm/include/drm/display/drm_dp_helper.h @@ -449,9 +449,15 @@ struct drm_dp_aux { * @is_remote: Is this AUX CH actually using sideband messaging. */ bool is_remote; + + /** + * @powered_down: If true then the remote endpoint is powered down. + */ + bool powered_down; }; int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset); +void drm_dp_dpcd_set_powered(struct drm_dp_aux *aux, bool powered); ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, void *buffer, size_t size); ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, -- 2.20.1