mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
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 <Martin.Leung@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Acked-by: Agustin Gutierrez <agustin.gutierrez@amd.com> Signed-off-by: Aric Cyr <aric.cyr@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
1072461cd7
commit
8df219bb7d
4 changed files with 44 additions and 10 deletions
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue