Commit 8df219bb authored by Aric Cyr's avatar Aric Cyr Committed by Alex Deucher
Browse files

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: default avatarMartin Leung <Martin.Leung@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Acked-by: default avatarAgustin Gutierrez <agustin.gutierrez@amd.com>
Signed-off-by: default avatarAric Cyr <aric.cyr@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 1072461c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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,
+8 −7
Original line number Diff line number Diff line
@@ -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, &current_payload);

		retrieved += current_payload.length;
		retrieved += payload_length;
	} while (retrieved < payload->length && ret == true);

	return ret;
+31 −3
Original line number Diff line number Diff line
@@ -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;
			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;
+3 −0
Original line number Diff line number Diff line
@@ -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 {