aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_dp.c
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2009-06-12 01:31:31 -0400
committerKeith Packard <keithp@keithp.com>2009-06-18 18:54:14 -0400
commitfb0f8fbf97e8a25074c81c629500d94cafa9e366 (patch)
tree61998d92216f4d23cda286853d0e5e5fe1397bc9 /drivers/gpu/drm/i915/intel_dp.c
parenta5b3da543d4882d57a2f3e05d37ad8e1e1453489 (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/drm/i915/intel_dp.c')
-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)
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 */
158static int
159intel_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
157static int 187static int
158intel_dp_aux_ch(struct intel_output *intel_output, 188intel_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;