]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
drm/i915: Generate 2MHz clock for display port aux channel I/O. Retry I/O.
authorKeith Packard <keithp@keithp.com>
Fri, 12 Jun 2009 05:31:31 +0000 (22:31 -0700)
committerKeith Packard <keithp@keithp.com>
Thu, 18 Jun 2009 22:54:14 +0000 (15:54 -0700)
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>
drivers/gpu/drm/i915/intel_dp.c

index 818fe34f2b5ce4dde442df346ad2c539dd524b7d..8f8d37d5663a0cacb53f2b776abbae0b2e2b42f9 100644 (file)
@@ -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;