summaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2009-06-12 07:31:31 +0200
committerKeith Packard <keithp@keithp.com>2009-06-19 00:54:14 +0200
commitfb0f8fbf97e8a25074c81c629500d94cafa9e366 (patch)
tree61998d92216f4d23cda286853d0e5e5fe1397bc9 /drivers/gpu
parentdrm/i915: Clarify error returns from display port aux channel I/O (diff)
downloadlinux-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.c102
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;