Commit 4f7f7b7e authored by Chris Wilson's avatar Chris Wilson

drm/i915/dp: Really try 5 times before giving up.

Only stop trying if the aux channel sucessfully reports that the
transmission was completed, otherwise try again. On the 5th failure,
bail and report that something is amiss.

This fixes a sporadic failure in reading the EDID for my external panel
over DP.
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: stable@kernel.org
parent b66d8424
...@@ -239,7 +239,6 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, ...@@ -239,7 +239,6 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
uint32_t ch_data = ch_ctl + 4; uint32_t ch_data = ch_ctl + 4;
int i; int i;
int recv_bytes; int recv_bytes;
uint32_t ctl;
uint32_t status; uint32_t status;
uint32_t aux_clock_divider; uint32_t aux_clock_divider;
int try, precharge; int try, precharge;
...@@ -263,16 +262,22 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, ...@@ -263,16 +262,22 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
else else
precharge = 5; precharge = 5;
if (I915_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) {
DRM_ERROR("dp_aux_ch not started status 0x%08x\n",
I915_READ(ch_ctl));
return -EBUSY;
}
/* Must try at least 3 times according to DP spec */ /* Must try at least 3 times according to DP spec */
for (try = 0; try < 5; try++) { for (try = 0; try < 5; try++) {
/* Load the send data into the aux channel data registers */ /* Load the send data into the aux channel data registers */
for (i = 0; i < send_bytes; i += 4) { for (i = 0; i < send_bytes; i += 4)
uint32_t d = pack_aux(send + i, send_bytes - i); I915_WRITE(ch_data + i,
pack_aux(send + i, send_bytes - i));
I915_WRITE(ch_data + i, d); /* Send the command and wait for it to complete */
} I915_WRITE(ch_ctl,
DP_AUX_CH_CTL_SEND_BUSY |
ctl = (DP_AUX_CH_CTL_SEND_BUSY |
DP_AUX_CH_CTL_TIME_OUT_400us | DP_AUX_CH_CTL_TIME_OUT_400us |
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
(precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
...@@ -280,24 +285,20 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, ...@@ -280,24 +285,20 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
DP_AUX_CH_CTL_DONE | DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR); DP_AUX_CH_CTL_RECEIVE_ERROR);
/* Send the command and wait for it to complete */
I915_WRITE(ch_ctl, ctl);
(void) I915_READ(ch_ctl);
for (;;) { for (;;) {
udelay(100);
status = I915_READ(ch_ctl); status = I915_READ(ch_ctl);
if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
break; break;
udelay(100);
} }
/* Clear done status and any errors */ /* Clear done status and any errors */
I915_WRITE(ch_ctl, (status | I915_WRITE(ch_ctl,
status |
DP_AUX_CH_CTL_DONE | DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR)); DP_AUX_CH_CTL_RECEIVE_ERROR);
(void) I915_READ(ch_ctl); if (status & DP_AUX_CH_CTL_DONE)
if ((status & DP_AUX_CH_CTL_TIME_OUT_ERROR) == 0)
break; break;
} }
...@@ -324,15 +325,12 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, ...@@ -324,15 +325,12 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
/* Unload any bytes sent back from the other side */ /* Unload any bytes sent back from the other side */
recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >> recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >>
DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT); DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT);
if (recv_bytes > recv_size) if (recv_bytes > recv_size)
recv_bytes = recv_size; recv_bytes = recv_size;
for (i = 0; i < recv_bytes; i += 4) { for (i = 0; i < recv_bytes; i += 4)
uint32_t d = I915_READ(ch_data + i); unpack_aux(I915_READ(ch_data + i),
recv + i, recv_bytes - i);
unpack_aux(d, recv + i, recv_bytes - i);
}
return recv_bytes; return recv_bytes;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment