diff options
| author | Keith Packard <keithp@keithp.com> | 2009-06-12 01:31:31 -0400 |
|---|---|---|
| committer | Keith Packard <keithp@keithp.com> | 2009-06-18 18:54:14 -0400 |
| commit | fb0f8fbf97e8a25074c81c629500d94cafa9e366 (patch) | |
| tree | 61998d92216f4d23cda286853d0e5e5fe1397bc9 /drivers/gpu | |
| parent | a5b3da543d4882d57a2f3e05d37ad8e1e1453489 (diff) | |
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) | |||
| 154 | dst[i] = src >> ((3-i) * 8); | 154 | dst[i] = src >> ((3-i) * 8); |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | /* hrawclock is 1/4 the FSB frequency */ | ||
| 158 | static int | ||
| 159 | intel_hrawclk(struct drm_device *dev) | ||
| 160 | { | ||
| 161 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
| 162 | uint32_t clkcfg; | ||
| 163 | |||
| 164 | clkcfg = I915_READ(CLKCFG); | ||
| 165 | switch (clkcfg & CLKCFG_FSB_MASK) { | ||
| 166 | case CLKCFG_FSB_400: | ||
| 167 | return 100; | ||
| 168 | case CLKCFG_FSB_533: | ||
| 169 | return 133; | ||
| 170 | case CLKCFG_FSB_667: | ||
| 171 | return 166; | ||
| 172 | case CLKCFG_FSB_800: | ||
| 173 | return 200; | ||
| 174 | case CLKCFG_FSB_1067: | ||
| 175 | return 266; | ||
| 176 | case CLKCFG_FSB_1333: | ||
| 177 | return 333; | ||
| 178 | /* these two are just a guess; one of them might be right */ | ||
| 179 | case CLKCFG_FSB_1600: | ||
| 180 | case CLKCFG_FSB_1600_ALT: | ||
| 181 | return 400; | ||
| 182 | default: | ||
| 183 | return 133; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 157 | static int | 187 | static int |
| 158 | intel_dp_aux_ch(struct intel_output *intel_output, | 188 | intel_dp_aux_ch(struct intel_output *intel_output, |
| 159 | uint8_t *send, int send_bytes, | 189 | uint8_t *send, int send_bytes, |
| @@ -169,44 +199,52 @@ intel_dp_aux_ch(struct intel_output *intel_output, | |||
| 169 | int recv_bytes; | 199 | int recv_bytes; |
| 170 | uint32_t ctl; | 200 | uint32_t ctl; |
| 171 | uint32_t status; | 201 | uint32_t status; |
| 172 | 202 | uint32_t aux_clock_divider; | |
| 173 | /* Load the send data into the aux channel data registers */ | 203 | int try; |
| 174 | for (i = 0; i < send_bytes; i += 4) { | ||
| 175 | uint32_t d = pack_aux(send + i, send_bytes - i);; | ||
| 176 | |||
| 177 | I915_WRITE(ch_data + i, d); | ||
| 178 | } | ||
| 179 | 204 | ||
| 180 | /* The clock divider is based off the hrawclk, | 205 | /* The clock divider is based off the hrawclk, |
| 181 | * and would like to run at 2MHz. The 133 below assumes | 206 | * and would like to run at 2MHz. So, take the |
| 182 | * a 266MHz hrawclk; need to figure out how we're supposed | 207 | * hrawclk value and divide by 2 and use that |
| 183 | * to know what hrawclk is... | ||
| 184 | */ | 208 | */ |
| 185 | ctl = (DP_AUX_CH_CTL_SEND_BUSY | | 209 | aux_clock_divider = intel_hrawclk(dev) / 2; |
| 186 | DP_AUX_CH_CTL_TIME_OUT_1600us | | 210 | /* Must try at least 3 times according to DP spec */ |
| 187 | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | | 211 | for (try = 0; try < 5; try++) { |
| 188 | (5 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | | 212 | /* Load the send data into the aux channel data registers */ |
| 189 | (133 << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | | 213 | for (i = 0; i < send_bytes; i += 4) { |
| 190 | DP_AUX_CH_CTL_TIME_OUT_ERROR | | 214 | uint32_t d = pack_aux(send + i, send_bytes - i);; |
| 191 | DP_AUX_CH_CTL_RECEIVE_ERROR); | 215 | |
| 192 | 216 | I915_WRITE(ch_data + i, d); | |
| 193 | /* Send the command and wait for it to complete */ | 217 | } |
| 194 | I915_WRITE(ch_ctl, ctl); | 218 | |
| 195 | (void) I915_READ(ch_ctl); | 219 | ctl = (DP_AUX_CH_CTL_SEND_BUSY | |
| 196 | for (;;) { | 220 | DP_AUX_CH_CTL_TIME_OUT_400us | |
| 197 | udelay(100); | 221 | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | |
| 198 | status = I915_READ(ch_ctl); | 222 | (5 << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | |
| 199 | if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) | 223 | (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | |
| 224 | DP_AUX_CH_CTL_DONE | | ||
| 225 | DP_AUX_CH_CTL_TIME_OUT_ERROR | | ||
| 226 | DP_AUX_CH_CTL_RECEIVE_ERROR); | ||
| 227 | |||
| 228 | /* Send the command and wait for it to complete */ | ||
| 229 | I915_WRITE(ch_ctl, ctl); | ||
| 230 | (void) I915_READ(ch_ctl); | ||
| 231 | for (;;) { | ||
| 232 | udelay(100); | ||
| 233 | status = I915_READ(ch_ctl); | ||
| 234 | if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) | ||
| 235 | break; | ||
| 236 | } | ||
| 237 | |||
| 238 | /* Clear done status and any errors */ | ||
| 239 | I915_WRITE(ch_ctl, (ctl | | ||
| 240 | DP_AUX_CH_CTL_DONE | | ||
| 241 | DP_AUX_CH_CTL_TIME_OUT_ERROR | | ||
| 242 | DP_AUX_CH_CTL_RECEIVE_ERROR)); | ||
| 243 | (void) I915_READ(ch_ctl); | ||
| 244 | if ((status & DP_AUX_CH_CTL_TIME_OUT_ERROR) == 0) | ||
| 200 | break; | 245 | break; |
| 201 | } | 246 | } |
| 202 | 247 | ||
| 203 | /* Clear done status and any errors */ | ||
| 204 | I915_WRITE(ch_ctl, (ctl | | ||
| 205 | DP_AUX_CH_CTL_DONE | | ||
| 206 | DP_AUX_CH_CTL_TIME_OUT_ERROR | | ||
| 207 | DP_AUX_CH_CTL_RECEIVE_ERROR)); | ||
| 208 | (void) I915_READ(ch_ctl); | ||
| 209 | |||
| 210 | if ((status & DP_AUX_CH_CTL_DONE) == 0) { | 248 | if ((status & DP_AUX_CH_CTL_DONE) == 0) { |
| 211 | printk(KERN_ERR "dp_aux_ch not done status 0x%08x\n", status); | 249 | printk(KERN_ERR "dp_aux_ch not done status 0x%08x\n", status); |
| 212 | return -EBUSY; | 250 | return -EBUSY; |
