From: Aric Cyr Date: Fri, 8 Oct 2021 01:34:53 +0000 (-0400) Subject: drm/amd/display: Handle I2C-over-AUX write channel status update X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=8df219bb7d4b14e4e82b3db6da4a73f1b0b767d3;p=uclinux-h8%2Flinux.git drm/amd/display: Handle I2C-over-AUX write channel status update [Why] When writing long AUX commands some sinks will respond will write status update requiring source to read status. [How] When a write request is replied with data (AUX_ACK_M), retry a read of write status to determine when the write is completed. Reviewed-by: Martin Leung Tested-by: Daniel Wheeler Acked-by: Agustin Gutierrez Signed-off-by: Aric Cyr Signed-off-by: Alex Deucher --- 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 1a99fcc27078..874a49b605c7 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 @@ -64,6 +64,8 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, payload.i2c_over_aux = (msg->request & DP_AUX_NATIVE_WRITE) == 0; payload.write = (msg->request & DP_AUX_I2C_READ) == 0; payload.mot = (msg->request & DP_AUX_I2C_MOT) != 0; + payload.write_status_update = + (msg->request & DP_AUX_I2C_WRITE_STATUS_UPDATE) != 0; payload.defer_delay = 0; result = dc_link_aux_transfer_raw(TO_DM_AUX(aux)->ddc_service, &payload, 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 471a67a64299..60539b1f2a80 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 @@ -554,6 +554,7 @@ bool dal_ddc_service_query_ddc_data( payload.address = address; payload.reply = NULL; payload.defer_delay = get_defer_delay(ddc); + payload.write_status_update = false; if (write_size != 0) { payload.write = true; @@ -625,24 +626,24 @@ bool dal_ddc_submit_aux_command(struct ddc_service *ddc, do { struct aux_payload current_payload; bool is_end_of_payload = (retrieved + DEFAULT_AUX_MAX_DATA_SIZE) >= - payload->length; + payload->length ? true : false; + uint32_t payload_length = is_end_of_payload ? + payload->length - retrieved : DEFAULT_AUX_MAX_DATA_SIZE; current_payload.address = payload->address; current_payload.data = &payload->data[retrieved]; current_payload.defer_delay = payload->defer_delay; current_payload.i2c_over_aux = payload->i2c_over_aux; - current_payload.length = is_end_of_payload ? - payload->length - retrieved : DEFAULT_AUX_MAX_DATA_SIZE; - /* set mot (middle of transaction) to false - * if it is the last payload - */ + current_payload.length = payload_length; + /* set mot (middle of transaction) to false if it is the last payload */ current_payload.mot = is_end_of_payload ? payload->mot:true; + current_payload.write_status_update = false; current_payload.reply = payload->reply; current_payload.write = payload->write; ret = dc_link_aux_transfer_with_retries(ddc, ¤t_payload); - retrieved += current_payload.length; + retrieved += payload_length; } while (retrieved < payload->length && ret == true); return ret; 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 95cb4d7cc76a..6d42a9cc9916 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c @@ -534,17 +534,26 @@ struct dce_aux *dce110_aux_engine_construct(struct aux_engine_dce110 *aux_engine static enum i2caux_transaction_action i2caux_action_from_payload(struct aux_payload *payload) { if (payload->i2c_over_aux) { + if (payload->write_status_update) { + if (payload->mot) + return I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST_MOT; + else + return I2CAUX_TRANSACTION_ACTION_I2C_STATUS_REQUEST; + } if (payload->write) { if (payload->mot) return I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT; - return I2CAUX_TRANSACTION_ACTION_I2C_WRITE; + else + return I2CAUX_TRANSACTION_ACTION_I2C_WRITE; } if (payload->mot) return I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT; + return I2CAUX_TRANSACTION_ACTION_I2C_READ; } if (payload->write) return I2CAUX_TRANSACTION_ACTION_DP_WRITE; + return I2CAUX_TRANSACTION_ACTION_DP_READ; } @@ -698,7 +707,8 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, aux_defer_retries = 0, aux_i2c_defer_retries = 0, aux_timeout_retries = 0, - aux_invalid_reply_retries = 0; + aux_invalid_reply_retries = 0, + aux_ack_m_retries = 0; if (ddc_pin) { aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; @@ -758,9 +768,27 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, aux_defer_retries, AUX_MAX_RETRIES); goto fail; - } else { + } else udelay(300); + } else if (payload->write && ret > 0) { + /* sink requested more time to complete the write via AUX_ACKM */ + if (++aux_ack_m_retries >= AUX_MAX_RETRIES) { + DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR, + LOG_FLAG_Error_I2cAux, + "dce_aux_transfer_with_retries: FAILURE: aux_ack_m_retries=%d >= AUX_MAX_RETRIES=%d", + aux_ack_m_retries, + AUX_MAX_RETRIES); + goto fail; } + + /* retry reading the write status until complete + * NOTE: payload is modified here + */ + payload->write = false; + payload->write_status_update = true; + payload->length = 0; + udelay(300); + } else return true; break; diff --git a/drivers/gpu/drm/amd/display/include/i2caux_interface.h b/drivers/gpu/drm/amd/display/include/i2caux_interface.h index c7fbb9c3ad6b..418fbf8c5c3a 100644 --- a/drivers/gpu/drm/amd/display/include/i2caux_interface.h +++ b/drivers/gpu/drm/amd/display/include/i2caux_interface.h @@ -41,6 +41,8 @@ struct aux_payload { * reset it to read data */ bool write; bool mot; + bool write_status_update; + uint32_t address; uint32_t length; uint8_t *data; @@ -53,6 +55,7 @@ struct aux_payload { * zero means "use default value" */ uint32_t defer_delay; + }; struct aux_command {