diff options
author | Keith Packard <keithp@keithp.com> | 2009-06-12 07:31:31 +0200 |
---|---|---|
committer | Keith Packard <keithp@keithp.com> | 2009-06-19 00:54:14 +0200 |
commit | fb0f8fbf97e8a25074c81c629500d94cafa9e366 (patch) | |
tree | 61998d92216f4d23cda286853d0e5e5fe1397bc9 /drivers/gpu | |
parent | drm/i915: Clarify error returns from display port aux channel I/O (diff) | |
download | linux-fb0f8fbf97e8a25074c81c629500d94cafa9e366.tar.xz linux-fb0f8fbf97e8a25074c81c629500d94cafa9e366.zip |
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: Keith Packard <keithp@keithp.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/i915/intel_dp.c | 102 |
1 files changed, 70 insertions, 32 deletions
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 818fe34f2b5c..8f8d37d5663a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -154,6 +154,36 @@ unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) 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 intel_dp_aux_ch(struct intel_output *intel_output, uint8_t *send, int send_bytes, @@ -169,44 +199,52 @@ intel_dp_aux_ch(struct intel_output *intel_output, int recv_bytes; uint32_t ctl; uint32_t status; - - /* Load the send data into the aux channel data registers */ - for (i = 0; i < send_bytes; i += 4) { - uint32_t d = pack_aux(send + i, send_bytes - i);; - - I915_WRITE(ch_data + i, d); - } + uint32_t aux_clock_divider; + int try; /* The clock divider is based off the hrawclk, - * and would like to run at 2MHz. The 133 below assumes - * a 266MHz hrawclk; need to figure out how we're supposed - * to know what hrawclk is... + * and would like to run at 2MHz. So, take the + * hrawclk value and divide by 2 and use that */ - ctl = (DP_AUX_CH_CTL_SEND_BUSY | - DP_AUX_CH_CTL_TIME_OUT_1600us | - (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | - (5 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | - (133 << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | - 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) + aux_clock_divider = intel_hrawclk(dev) / 2; + /* Must try at least 3 times according to DP spec */ + for (try = 0; try < 5; try++) { + /* Load the send data into the aux channel data registers */ + for (i = 0; i < send_bytes; i += 4) { + uint32_t d = pack_aux(send + i, send_bytes - i);; + + I915_WRITE(ch_data + i, d); + } + + ctl = (DP_AUX_CH_CTL_SEND_BUSY | + DP_AUX_CH_CTL_TIME_OUT_400us | + (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | + (5 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | + (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; } - /* 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) { printk(KERN_ERR "dp_aux_ch not done status 0x%08x\n", status); return -EBUSY; |