提交 fb0f8fbf 编写于 作者: K Keith Packard

drm/i915: Generate 2MHz clock for display port aux channel I/O. Retry I/O.

The display port aux channel clock is taken from the hrawclk value, which is
provided to the chip as the FSB frequency (as far as I can determine). The
strapping values for that are available in the CLKCFG register, now used to
select an appropriate divider to generate a 2MHz clock.

In addition, the DisplayPort spec requires that each aux channel I/O be
retried 'at least 3 times' in case the sink is idle when the first request
comes in.
Signed-off-by: NKeith Packard <keithp@keithp.com>
上级 a5b3da54
...@@ -154,6 +154,36 @@ unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) ...@@ -154,6 +154,36 @@ unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes)
dst[i] = src >> ((3-i) * 8); dst[i] = src >> ((3-i) * 8);
} }
/* hrawclock is 1/4 the FSB frequency */
static int
intel_hrawclk(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t clkcfg;
clkcfg = I915_READ(CLKCFG);
switch (clkcfg & CLKCFG_FSB_MASK) {
case CLKCFG_FSB_400:
return 100;
case CLKCFG_FSB_533:
return 133;
case CLKCFG_FSB_667:
return 166;
case CLKCFG_FSB_800:
return 200;
case CLKCFG_FSB_1067:
return 266;
case CLKCFG_FSB_1333:
return 333;
/* these two are just a guess; one of them might be right */
case CLKCFG_FSB_1600:
case CLKCFG_FSB_1600_ALT:
return 400;
default:
return 133;
}
}
static int static int
intel_dp_aux_ch(struct intel_output *intel_output, intel_dp_aux_ch(struct intel_output *intel_output,
uint8_t *send, int send_bytes, uint8_t *send, int send_bytes,
...@@ -169,44 +199,52 @@ intel_dp_aux_ch(struct intel_output *intel_output, ...@@ -169,44 +199,52 @@ intel_dp_aux_ch(struct intel_output *intel_output,
int recv_bytes; int recv_bytes;
uint32_t ctl; uint32_t ctl;
uint32_t status; uint32_t status;
uint32_t aux_clock_divider;
/* Load the send data into the aux channel data registers */ int try;
for (i = 0; i < send_bytes; i += 4) {
uint32_t d = pack_aux(send + i, send_bytes - i);;
I915_WRITE(ch_data + i, d);
}
/* The clock divider is based off the hrawclk, /* The clock divider is based off the hrawclk,
* and would like to run at 2MHz. The 133 below assumes * and would like to run at 2MHz. So, take the
* a 266MHz hrawclk; need to figure out how we're supposed * hrawclk value and divide by 2 and use that
* to know what hrawclk is...
*/ */
ctl = (DP_AUX_CH_CTL_SEND_BUSY | aux_clock_divider = intel_hrawclk(dev) / 2;
DP_AUX_CH_CTL_TIME_OUT_1600us | /* Must try at least 3 times according to DP spec */
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | for (try = 0; try < 5; try++) {
(5 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | /* Load the send data into the aux channel data registers */
(133 << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | for (i = 0; i < send_bytes; i += 4) {
DP_AUX_CH_CTL_TIME_OUT_ERROR | uint32_t d = pack_aux(send + i, send_bytes - i);;
DP_AUX_CH_CTL_RECEIVE_ERROR);
I915_WRITE(ch_data + i, d);
/* Send the command and wait for it to complete */ }
I915_WRITE(ch_ctl, ctl);
(void) I915_READ(ch_ctl); ctl = (DP_AUX_CH_CTL_SEND_BUSY |
for (;;) { DP_AUX_CH_CTL_TIME_OUT_400us |
udelay(100); (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
status = I915_READ(ch_ctl); (5 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_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 (;;) {
udelay(100);
status = I915_READ(ch_ctl);
if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
break;
}
/* Clear done status and any errors */
I915_WRITE(ch_ctl, (ctl |
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR));
(void) I915_READ(ch_ctl);
if ((status & DP_AUX_CH_CTL_TIME_OUT_ERROR) == 0)
break; break;
} }
/* Clear done status and any errors */
I915_WRITE(ch_ctl, (ctl |
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR));
(void) I915_READ(ch_ctl);
if ((status & DP_AUX_CH_CTL_DONE) == 0) { if ((status & DP_AUX_CH_CTL_DONE) == 0) {
printk(KERN_ERR "dp_aux_ch not done status 0x%08x\n", status); printk(KERN_ERR "dp_aux_ch not done status 0x%08x\n", status);
return -EBUSY; return -EBUSY;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册