Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1a43e709 authored by Tatenda Chipeperekwa's avatar Tatenda Chipeperekwa Committed by Gerrit - the friendly Code Review server
Browse files

drm/msm/dp: fix EDID read for non-compliant sinks



Fix EDID read for non-compliant sinks that do not
handle the i2c middle-of-transaction flag correctly.

CRs-Fixed: 2105346
Change-Id: I6f3fb67d81e082ff212188d933d42ac82825f764
Signed-off-by: default avatarTatenda Chipeperekwa <tatendac@codeaurora.org>
parent 504e2895
Loading
Loading
Loading
Loading
+93 −4
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ struct dp_aux_private {
	bool read;
	bool no_send_addr;
	bool no_send_stop;
	u32 offset;
	u32 segment;

	struct drm_dp_aux drm_aux;
};
@@ -104,6 +106,8 @@ static u32 dp_aux_write(struct dp_aux_private *aux,
		aux->catalog->write_data(aux->catalog);
	}

	aux->catalog->clear_trans(aux->catalog, false);

	reg = 0; /* Transaction number == 1 */
	if (!aux->native) { /* i2c */
		reg |= BIT(8);
@@ -162,6 +166,8 @@ static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
	u32 i, actual_i;
	u32 len = msg->size;

	aux->catalog->clear_trans(aux->catalog, true);

	data = 0;
	data |= DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
	data |= BIT(0);  /* read */
@@ -264,6 +270,87 @@ static void dp_aux_reconfig(struct dp_aux *dp_aux)
	aux->catalog->reset(aux->catalog);
}

static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
		struct drm_dp_aux_msg *input_msg)
{
	u32 const edid_address = 0x50;
	u32 const segment_address = 0x30;
	bool i2c_read = input_msg->request &
		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
	u8 *data = NULL;

	if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
		(input_msg->address != segment_address)))
		return;


	data = input_msg->buffer;
	if (input_msg->address == segment_address)
		aux->segment = *data;
	else
		aux->offset = *data;
}

/**
 * dp_aux_transfer_helper() - helper function for EDID read transactions
 *
 * @aux: DP AUX private structure
 * @input_msg: input message from DRM upstream APIs
 *
 * return: void
 *
 * This helper function is used to fix EDID reads for non-compliant
 * sinks that do not handle the i2c middle-of-transaction flag correctly.
 */
static void dp_aux_transfer_helper(struct dp_aux_private *aux,
		struct drm_dp_aux_msg *input_msg)
{
	struct drm_dp_aux_msg helper_msg;
	u32 const message_size = 0x10;
	u32 const segment_address = 0x30;
	bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
	bool i2c_read = input_msg->request &
		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);

	if (!i2c_mot || !i2c_read || (input_msg->size == 0))
		return;

	aux->read = false;
	aux->cmd_busy = true;
	aux->no_send_addr = true;
	aux->no_send_stop = true;

	/*
	 * Send the segment address for every i2c read in which the
	 * middle-of-tranaction flag is set. This is required to support EDID
	 * reads of more than 2 blocks as the segment address is reset to 0
	 * since we are overriding the middle-of-transaction flag for read
	 * transactions.
	 */
	memset(&helper_msg, 0, sizeof(helper_msg));
	helper_msg.address = segment_address;
	helper_msg.buffer = &aux->segment;
	helper_msg.size = 1;
	dp_aux_cmd_fifo_tx(aux, &helper_msg);

	/*
	 * Send the offset address for every i2c read in which the
	 * middle-of-transaction flag is set. This will ensure that the sink
	 * will update its read pointer and return the correct portion of the
	 * EDID buffer in the subsequent i2c read trasntion triggered in the
	 * native AUX transfer function.
	 */
	memset(&helper_msg, 0, sizeof(helper_msg));
	helper_msg.address = input_msg->address;
	helper_msg.buffer = &aux->offset;
	helper_msg.size = 1;
	dp_aux_cmd_fifo_tx(aux, &helper_msg);
	aux->offset += message_size;

	if (aux->offset == 0x80 || aux->offset == 0x100)
		aux->segment = 0x0; /* reset segment at end of block */
}

/*
 * This function does the real job to process an AUX transaction.
 * It will call aux_reset() function to reset the AUX channel,
@@ -282,8 +369,6 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
	mutex_lock(&aux->mutex);

	aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
	aux->cmd_busy = true;

	/* Ignore address only message */
	if ((msg->size == 0) || (msg->buffer == NULL)) {
@@ -302,6 +387,12 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
		goto unlock_exit;
	}

	dp_aux_update_offset_and_segment(aux, msg);
	dp_aux_transfer_helper(aux, msg);

	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
	aux->cmd_busy = true;

	if (aux->read) {
		aux->no_send_addr = true;
		aux->no_send_stop = false;
@@ -322,8 +413,6 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
		goto unlock_exit;
	}

	aux->catalog->clear_trans(aux->catalog);

	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
		if (aux->read)
			dp_aux_cmd_fifo_rx(aux, msg);
+9 −8
Original line number Diff line number Diff line
@@ -131,10 +131,10 @@ static int dp_catalog_aux_write_trans(struct dp_catalog_aux *aux)
	return rc;
}

static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux)
static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux, bool read)
{
	int rc = 0;
	u32 data;
	u32 data = 0;
	struct dp_catalog_private *catalog;
	void __iomem *base;

@@ -147,9 +147,13 @@ static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux)
	dp_catalog_get_priv(aux);
	base = catalog->io->ctrl_io.base;

	if (read) {
		data = dp_read(base + DP_AUX_TRANS_CTRL);
		data &= ~BIT(9);
		dp_write(base + DP_AUX_TRANS_CTRL, data);
	} else {
		dp_write(base + DP_AUX_TRANS_CTRL, 0);
	}
end:
	return rc;
}
@@ -275,9 +279,6 @@ static void dp_catalog_aux_get_irq(struct dp_catalog_aux *aux, bool cmd_busy)
	dp_catalog_get_priv(aux);
	base = catalog->io->ctrl_io.base;

	if (cmd_busy)
		dp_write(base + DP_AUX_TRANS_CTRL, 0x0);

	aux->isr = dp_read(base + DP_INTR_STATUS);
	aux->isr &= ~DP_INTR_MASK1;
	ack = aux->isr & DP_INTERRUPT_STATUS1;
+1 −1
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ struct dp_catalog_aux {
	u32 (*read_data)(struct dp_catalog_aux *aux);
	int (*write_data)(struct dp_catalog_aux *aux);
	int (*write_trans)(struct dp_catalog_aux *aux);
	int (*clear_trans)(struct dp_catalog_aux *aux);
	int (*clear_trans)(struct dp_catalog_aux *aux, bool read);
	void (*reset)(struct dp_catalog_aux *aux);
	void (*enable)(struct dp_catalog_aux *aux, bool enable);
	void (*update_aux_cfg)(struct dp_catalog_aux *aux,