diff options
Diffstat (limited to 'common/recipes-kernel/linux/linux-yocto-4.19.8/1529-drm-amd-display-Respect-aux-return-values.patch')
-rw-r--r-- | common/recipes-kernel/linux/linux-yocto-4.19.8/1529-drm-amd-display-Respect-aux-return-values.patch | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/linux-yocto-4.19.8/1529-drm-amd-display-Respect-aux-return-values.patch b/common/recipes-kernel/linux/linux-yocto-4.19.8/1529-drm-amd-display-Respect-aux-return-values.patch new file mode 100644 index 00000000..9e0c9d85 --- /dev/null +++ b/common/recipes-kernel/linux/linux-yocto-4.19.8/1529-drm-amd-display-Respect-aux-return-values.patch @@ -0,0 +1,310 @@ +From 61776120042cbdba0d51171b8be077f2996eda22 Mon Sep 17 00:00:00 2001 +From: Thomas Lim <Thomas.Lim@amd.com> +Date: Wed, 16 Jan 2019 15:56:56 -0500 +Subject: [PATCH 1529/2940] drm/amd/display: Respect aux return values + +[Why] +The new aux implementation was not up to spec. This caused us to fail DP +compliance as well as introduced serious delays during system resume. + +[How] +Make dce_aux_transfer_raw return the operation result + +Make dce_aux_transfer_with_retries delay with udelay instead +of msleep, and only on invalid reply. Also fail on the second +invalid reply, third timeout, or first of any other error + +Convert return values to drm error codes in amdgpu_dm + +As the two aux transfer functions are now noticeably +different, change the names to better reflect their +functionality and document. + +There was one last call to dc_link_aux_transfer that +should have retries, fix that + +Change-Id: I1b28808ec03fe7e838736bb7bfb6c2937b0b7573 +Signed-off-by: David Francis <David.Francis@amd.com> +Signed-off-by: Thomas Lim <Thomas.Lim@amd.com> +Reviewed-by: David Francis <David.Francis@amd.com> +Acked-by: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com> +Acked-by: Eric Yang <eric.yang2@amd.com> +--- + .../display/amdgpu_dm/amdgpu_dm_mst_types.c | 21 +++- + .../gpu/drm/amd/display/dc/core/dc_link_ddc.c | 22 +++- + drivers/gpu/drm/amd/display/dc/dce/dce_aux.c | 115 +++++++++++++----- + drivers/gpu/drm/amd/display/dc/dce/dce_aux.h | 5 +- + .../gpu/drm/amd/display/dc/inc/dc_link_ddc.h | 5 +- + 5 files changed, 129 insertions(+), 39 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +index 5e524d733249..a6f44a47adcb 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +@@ -84,6 +84,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, + { + ssize_t result = 0; + struct aux_payload payload; ++ enum aux_channel_operation_result operation_result; + + if (WARN_ON(msg->size > 16)) + return -E2BIG; +@@ -97,13 +98,27 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, + payload.mot = (msg->request & DP_AUX_I2C_MOT) != 0; + payload.defer_delay = 0; + +- result = dc_link_aux_transfer(TO_DM_AUX(aux)->ddc_service, &payload); ++ result = dc_link_aux_transfer_raw(TO_DM_AUX(aux)->ddc_service, &payload, ++ &operation_result); + + if (payload.write) + result = msg->size; + +- if (result < 0) /* DC doesn't know about kernel error codes */ +- result = -EIO; ++ if (result < 0) ++ switch (operation_result) { ++ case AUX_CHANNEL_OPERATION_SUCCEEDED: ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: ++ case AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN: ++ result = -EIO; ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: ++ result = -EBUSY; ++ break; ++ case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: ++ result = -ETIMEDOUT; ++ break; ++ } + + return result; + } +diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c +index b7ee63cd8dc7..f02092a0dc76 100644 +--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c ++++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c +@@ -573,12 +573,28 @@ bool dal_ddc_service_query_ddc_data( + return ret; + } + +-int dc_link_aux_transfer(struct ddc_service *ddc, +- struct aux_payload *payload) ++/* dc_link_aux_transfer_raw() - Attempt to transfer ++ * the given aux payload. This function does not perform ++ * retries or handle error states. The reply is returned ++ * in the payload->reply and the result through ++ * *operation_result. Returns the number of bytes transferred, ++ * or -1 on a failure. ++ */ ++int dc_link_aux_transfer_raw(struct ddc_service *ddc, ++ struct aux_payload *payload, ++ enum aux_channel_operation_result *operation_result) + { +- return dce_aux_transfer(ddc, payload); ++ return dce_aux_transfer_raw(ddc, payload, operation_result); + } + ++/* dc_link_aux_transfer_with_retries() - Attempt to submit an ++ * aux payload, retrying on timeouts, defers, and busy states ++ * as outlined in the DP spec. Returns true if the request ++ * was successful. ++ * ++ * Unless you want to implement your own retry semantics, this ++ * is probably the one you want. ++ */ + bool dc_link_aux_transfer_with_retries(struct ddc_service *ddc, + struct aux_payload *payload) + { +diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c +index 2f50be33ab15..65b290d80143 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c ++++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c +@@ -438,12 +438,12 @@ static enum i2caux_transaction_action i2caux_action_from_payload(struct aux_payl + return I2CAUX_TRANSACTION_ACTION_DP_READ; + } + +-int dce_aux_transfer(struct ddc_service *ddc, +- struct aux_payload *payload) ++int dce_aux_transfer_raw(struct ddc_service *ddc, ++ struct aux_payload *payload, ++ enum aux_channel_operation_result *operation_result) + { + struct ddc *ddc_pin = ddc->ddc_pin; + struct dce_aux *aux_engine; +- enum aux_channel_operation_result operation_result; + struct aux_request_transaction_data aux_req; + struct aux_reply_transaction_data aux_rep; + uint8_t returned_bytes = 0; +@@ -470,28 +470,26 @@ int dce_aux_transfer(struct ddc_service *ddc, + aux_req.data = payload->data; + + submit_channel_request(aux_engine, &aux_req); +- operation_result = get_channel_status(aux_engine, &returned_bytes); +- +- switch (operation_result) { +- case AUX_CHANNEL_OPERATION_SUCCEEDED: +- res = read_channel_reply(aux_engine, payload->length, +- payload->data, payload->reply, +- &status); +- break; +- case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: +- res = 0; +- break; +- case AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN: +- case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: +- case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: ++ *operation_result = get_channel_status(aux_engine, &returned_bytes); ++ ++ if (*operation_result == AUX_CHANNEL_OPERATION_SUCCEEDED) { ++ read_channel_reply(aux_engine, payload->length, ++ payload->data, payload->reply, ++ &status); ++ res = returned_bytes; ++ } else { + res = -1; +- break; + } ++ + release_engine(aux_engine); + return res; + } + +-#define AUX_RETRY_MAX 7 ++#define AUX_MAX_RETRIES 7 ++#define AUX_MAX_DEFER_RETRIES 7 ++#define AUX_MAX_I2C_DEFER_RETRIES 7 ++#define AUX_MAX_INVALID_REPLY_RETRIES 2 ++#define AUX_MAX_TIMEOUT_RETRIES 3 + + bool dce_aux_transfer_with_retries(struct ddc_service *ddc, + struct aux_payload *payload) +@@ -499,24 +497,83 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, + int i, ret = 0; + uint8_t reply; + bool payload_reply = true; ++ enum aux_channel_operation_result operation_result; ++ int aux_ack_retries = 0, ++ aux_defer_retries = 0, ++ aux_i2c_defer_retries = 0, ++ aux_timeout_retries = 0, ++ aux_invalid_reply_retries = 0; + + if (!payload->reply) { + payload_reply = false; + payload->reply = &reply; + } + +- for (i = 0; i < AUX_RETRY_MAX; i++) { +- ret = dce_aux_transfer(ddc, payload); +- +- if (ret >= 0) { +- if (*payload->reply == 0) { +- if (!payload_reply) +- payload->reply = NULL; +- return true; ++ for (i = 0; i < AUX_MAX_RETRIES; i++) { ++ ret = dce_aux_transfer_raw(ddc, payload, &operation_result); ++ switch (operation_result) { ++ case AUX_CHANNEL_OPERATION_SUCCEEDED: ++ aux_timeout_retries = 0; ++ aux_invalid_reply_retries = 0; ++ ++ switch (*payload->reply) { ++ case AUX_TRANSACTION_REPLY_AUX_ACK: ++ if (!payload->write && payload->length != ret) { ++ if (++aux_ack_retries >= AUX_MAX_RETRIES) ++ goto fail; ++ else ++ udelay(300); ++ } else ++ return true; ++ break; ++ ++ case AUX_TRANSACTION_REPLY_AUX_DEFER: ++ if (++aux_defer_retries >= AUX_MAX_DEFER_RETRIES) ++ goto fail; ++ break; ++ ++ case AUX_TRANSACTION_REPLY_I2C_DEFER: ++ aux_defer_retries = 0; ++ if (++aux_i2c_defer_retries >= AUX_MAX_I2C_DEFER_RETRIES) ++ goto fail; ++ break; ++ ++ case AUX_TRANSACTION_REPLY_AUX_NACK: ++ case AUX_TRANSACTION_REPLY_HPD_DISCON: ++ default: ++ goto fail; + } +- } ++ break; ++ ++ case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: ++ if (++aux_invalid_reply_retries >= AUX_MAX_INVALID_REPLY_RETRIES) ++ goto fail; ++ else ++ udelay(400); ++ break; ++ ++ case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: ++ if (++aux_timeout_retries >= AUX_MAX_TIMEOUT_RETRIES) ++ goto fail; ++ else { ++ /* ++ * DP 1.4, 2.8.2: AUX Transaction Response/Reply Timeouts ++ * According to the DP spec there should be 3 retries total ++ * with a 400us wait inbetween each. Hardware already waits ++ * for 550us therefore no wait is required here. ++ */ ++ } ++ break; + +- udelay(1000); ++ case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: ++ case AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN: ++ default: ++ goto fail; ++ } + } ++ ++fail: ++ if (!payload_reply) ++ payload->reply = NULL; + return false; + } +diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h +index d27f22c05e4b..aab5f0c34584 100644 +--- a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h ++++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.h +@@ -123,8 +123,9 @@ bool dce110_aux_engine_acquire( + struct dce_aux *aux_engine, + struct ddc *ddc); + +-int dce_aux_transfer(struct ddc_service *ddc, +- struct aux_payload *cmd); ++int dce_aux_transfer_raw(struct ddc_service *ddc, ++ struct aux_payload *cmd, ++ enum aux_channel_operation_result *operation_result); + + bool dce_aux_transfer_with_retries(struct ddc_service *ddc, + struct aux_payload *cmd); +diff --git a/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h b/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h +index 16fd4dc6c4dd..b1fab251c09b 100644 +--- a/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h ++++ b/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h +@@ -95,8 +95,9 @@ bool dal_ddc_service_query_ddc_data( + uint8_t *read_buf, + uint32_t read_size); + +-int dc_link_aux_transfer(struct ddc_service *ddc, +- struct aux_payload *payload); ++int dc_link_aux_transfer_raw(struct ddc_service *ddc, ++ struct aux_payload *payload, ++ enum aux_channel_operation_result *operation_result); + + bool dc_link_aux_transfer_with_retries(struct ddc_service *ddc, + struct aux_payload *payload); +-- +2.17.1 + |