diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2012-03-09 01:22:56 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-03-13 03:15:03 -0400 |
commit | 8663bc7cde00c8e832d985354f8a6d57a52f7d92 (patch) | |
tree | e11120d8757c5c80b346ab819a6cfced10c69d43 /drivers/gpu/drm/nouveau | |
parent | 8c1dcb6573ae71ffae392edf5f458543d310607e (diff) |
drm/nouveau/dp: move all nv50/sor-specific code out of nouveau_dp.c
Off-chip encoders (which we don't support yet anyway), and newer chipsets
(such as NVD9...), will need their own code for this.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dp.c | 233 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_encoder.h | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_display.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_sor.c | 196 |
5 files changed, 231 insertions, 228 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 1a613ab4e08f..f38f561a0058 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c | |||
@@ -161,116 +161,6 @@ out: | |||
161 | return ret; | 161 | return ret; |
162 | } | 162 | } |
163 | 163 | ||
164 | static u32 | ||
165 | dp_link_bw_get(struct drm_device *dev, int or, int link) | ||
166 | { | ||
167 | u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800)); | ||
168 | if (!(ctrl & 0x000c0000)) | ||
169 | return 162000; | ||
170 | return 270000; | ||
171 | } | ||
172 | |||
173 | static int | ||
174 | dp_lane_count_get(struct drm_device *dev, int or, int link) | ||
175 | { | ||
176 | u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | ||
177 | switch (ctrl & 0x000f0000) { | ||
178 | case 0x00010000: return 1; | ||
179 | case 0x00030000: return 2; | ||
180 | default: | ||
181 | return 4; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | void | ||
186 | nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) | ||
187 | { | ||
188 | const u32 symbol = 100000; | ||
189 | int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; | ||
190 | int TU, VTUi, VTUf, VTUa; | ||
191 | u64 link_data_rate, link_ratio, unk; | ||
192 | u32 best_diff = 64 * symbol; | ||
193 | u32 link_nr, link_bw, r; | ||
194 | |||
195 | /* calculate packed data rate for each lane */ | ||
196 | link_nr = dp_lane_count_get(dev, or, link); | ||
197 | link_data_rate = (clk * bpp / 8) / link_nr; | ||
198 | |||
199 | /* calculate ratio of packed data rate to link symbol rate */ | ||
200 | link_bw = dp_link_bw_get(dev, or, link); | ||
201 | link_ratio = link_data_rate * symbol; | ||
202 | r = do_div(link_ratio, link_bw); | ||
203 | |||
204 | for (TU = 64; TU >= 32; TU--) { | ||
205 | /* calculate average number of valid symbols in each TU */ | ||
206 | u32 tu_valid = link_ratio * TU; | ||
207 | u32 calc, diff; | ||
208 | |||
209 | /* find a hw representation for the fraction.. */ | ||
210 | VTUi = tu_valid / symbol; | ||
211 | calc = VTUi * symbol; | ||
212 | diff = tu_valid - calc; | ||
213 | if (diff) { | ||
214 | if (diff >= (symbol / 2)) { | ||
215 | VTUf = symbol / (symbol - diff); | ||
216 | if (symbol - (VTUf * diff)) | ||
217 | VTUf++; | ||
218 | |||
219 | if (VTUf <= 15) { | ||
220 | VTUa = 1; | ||
221 | calc += symbol - (symbol / VTUf); | ||
222 | } else { | ||
223 | VTUa = 0; | ||
224 | VTUf = 1; | ||
225 | calc += symbol; | ||
226 | } | ||
227 | } else { | ||
228 | VTUa = 0; | ||
229 | VTUf = min((int)(symbol / diff), 15); | ||
230 | calc += symbol / VTUf; | ||
231 | } | ||
232 | |||
233 | diff = calc - tu_valid; | ||
234 | } else { | ||
235 | /* no remainder, but the hw doesn't like the fractional | ||
236 | * part to be zero. decrement the integer part and | ||
237 | * have the fraction add a whole symbol back | ||
238 | */ | ||
239 | VTUa = 0; | ||
240 | VTUf = 1; | ||
241 | VTUi--; | ||
242 | } | ||
243 | |||
244 | if (diff < best_diff) { | ||
245 | best_diff = diff; | ||
246 | bestTU = TU; | ||
247 | bestVTUa = VTUa; | ||
248 | bestVTUf = VTUf; | ||
249 | bestVTUi = VTUi; | ||
250 | if (diff == 0) | ||
251 | break; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | if (!bestTU) { | ||
256 | NV_ERROR(dev, "DP: unable to find suitable config\n"); | ||
257 | return; | ||
258 | } | ||
259 | |||
260 | /* XXX close to vbios numbers, but not right */ | ||
261 | unk = (symbol - link_ratio) * bestTU; | ||
262 | unk *= link_ratio; | ||
263 | r = do_div(unk, symbol); | ||
264 | r = do_div(unk, symbol); | ||
265 | unk += 6; | ||
266 | |||
267 | nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); | ||
268 | nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | | ||
269 | bestVTUf << 16 | | ||
270 | bestVTUi << 8 | | ||
271 | unk); | ||
272 | } | ||
273 | |||
274 | u8 * | 164 | u8 * |
275 | nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) | 165 | nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) |
276 | { | 166 | { |
@@ -318,13 +208,10 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) | |||
318 | * link training | 208 | * link training |
319 | *****************************************************************************/ | 209 | *****************************************************************************/ |
320 | struct dp_state { | 210 | struct dp_state { |
211 | struct dp_train_func *func; | ||
321 | struct dcb_entry *dcb; | 212 | struct dcb_entry *dcb; |
322 | u8 *table; | ||
323 | u8 *entry; | ||
324 | int auxch; | 213 | int auxch; |
325 | int crtc; | 214 | int crtc; |
326 | int or; | ||
327 | int link; | ||
328 | u8 *dpcd; | 215 | u8 *dpcd; |
329 | int link_nr; | 216 | int link_nr; |
330 | u32 link_bw; | 217 | u32 link_bw; |
@@ -335,100 +222,48 @@ struct dp_state { | |||
335 | static void | 222 | static void |
336 | dp_set_link_config(struct drm_device *dev, struct dp_state *dp) | 223 | dp_set_link_config(struct drm_device *dev, struct dp_state *dp) |
337 | { | 224 | { |
338 | int or = dp->or, link = dp->link; | 225 | u8 sink[2]; |
339 | u8 *entry, sink[2]; | ||
340 | u32 dp_ctrl; | ||
341 | u16 script; | ||
342 | 226 | ||
343 | NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); | 227 | NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); |
344 | 228 | ||
345 | /* set selected link rate on source */ | 229 | /* set desired link configuration on the source */ |
346 | switch (dp->link_bw) { | 230 | dp->func->link_set(dev, dp->dcb, dp->crtc, dp->link_nr, dp->link_bw, |
347 | case 270000: | 231 | dp->dpcd[2] & DP_ENHANCED_FRAME_CAP); |
348 | nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000); | ||
349 | sink[0] = DP_LINK_BW_2_7; | ||
350 | break; | ||
351 | default: | ||
352 | nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000); | ||
353 | sink[0] = DP_LINK_BW_1_62; | ||
354 | break; | ||
355 | } | ||
356 | |||
357 | /* offset +0x0a of each dp encoder table entry is a pointer to another | ||
358 | * table, that has (among other things) pointers to more scripts that | ||
359 | * need to be executed, this time depending on link speed. | ||
360 | */ | ||
361 | entry = ROMPTR(dev, dp->entry[10]); | ||
362 | if (entry) { | ||
363 | if (dp->table[0] < 0x30) { | ||
364 | while (dp->link_bw < (ROM16(entry[0]) * 10)) | ||
365 | entry += 4; | ||
366 | script = ROM16(entry[2]); | ||
367 | } else { | ||
368 | while (dp->link_bw < (entry[0] * 27000)) | ||
369 | entry += 3; | ||
370 | script = ROM16(entry[1]); | ||
371 | } | ||
372 | 232 | ||
373 | nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); | 233 | /* inform the sink of the new configuration */ |
374 | } | 234 | sink[0] = dp->link_bw / 27000; |
375 | |||
376 | /* configure lane count on the source */ | ||
377 | dp_ctrl = ((1 << dp->link_nr) - 1) << 16; | ||
378 | sink[1] = dp->link_nr; | 235 | sink[1] = dp->link_nr; |
379 | if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) { | 236 | if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) |
380 | dp_ctrl |= 0x00004000; | ||
381 | sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; | 237 | sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; |
382 | } | ||
383 | |||
384 | nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); | ||
385 | 238 | ||
386 | /* inform the sink of the new configuration */ | ||
387 | auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); | 239 | auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); |
388 | } | 240 | } |
389 | 241 | ||
390 | static void | 242 | static void |
391 | dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) | 243 | dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern) |
392 | { | 244 | { |
393 | u8 sink_tp; | 245 | u8 sink_tp; |
394 | 246 | ||
395 | NV_DEBUG_KMS(dev, "training pattern %d\n", tp); | 247 | NV_DEBUG_KMS(dev, "training pattern %d\n", pattern); |
396 | 248 | ||
397 | nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); | 249 | dp->func->train_set(dev, dp->dcb, pattern); |
398 | 250 | ||
399 | auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); | 251 | auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); |
400 | sink_tp &= ~DP_TRAINING_PATTERN_MASK; | 252 | sink_tp &= ~DP_TRAINING_PATTERN_MASK; |
401 | sink_tp |= tp; | 253 | sink_tp |= pattern; |
402 | auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); | 254 | auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); |
403 | } | 255 | } |
404 | 256 | ||
405 | static const u8 nv50_lane_map[] = { 16, 8, 0, 24 }; | ||
406 | static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 }; | ||
407 | |||
408 | static int | 257 | static int |
409 | dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) | 258 | dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) |
410 | { | 259 | { |
411 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
412 | u32 mask = 0, drv = 0, pre = 0, unk = 0; | ||
413 | const u8 *shifts; | ||
414 | int link = dp->link; | ||
415 | int or = dp->or; | ||
416 | int i; | 260 | int i; |
417 | 261 | ||
418 | if (dev_priv->chipset != 0xaf) | ||
419 | shifts = nv50_lane_map; | ||
420 | else | ||
421 | shifts = nvaf_lane_map; | ||
422 | |||
423 | for (i = 0; i < dp->link_nr; i++) { | 262 | for (i = 0; i < dp->link_nr; i++) { |
424 | u8 *conf = dp->entry + dp->table[4]; | ||
425 | u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; | 263 | u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; |
426 | u8 lpre = (lane & 0x0c) >> 2; | 264 | u8 lpre = (lane & 0x0c) >> 2; |
427 | u8 lvsw = (lane & 0x03) >> 0; | 265 | u8 lvsw = (lane & 0x03) >> 0; |
428 | 266 | ||
429 | mask |= 0xff << shifts[i]; | ||
430 | unk |= 1 << (shifts[i] >> 3); | ||
431 | |||
432 | dp->conf[i] = (lpre << 3) | lvsw; | 267 | dp->conf[i] = (lpre << 3) | lvsw; |
433 | if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) | 268 | if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200) |
434 | dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; | 269 | dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; |
@@ -436,41 +271,9 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) | |||
436 | dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; | 271 | dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; |
437 | 272 | ||
438 | NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); | 273 | NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); |
439 | 274 | dp->func->train_adj(dev, dp->dcb, i, lvsw, lpre); | |
440 | if (dp->table[0] < 0x30) { | ||
441 | u8 *last = conf + (dp->entry[4] * dp->table[5]); | ||
442 | while (lvsw != conf[0] || lpre != conf[1]) { | ||
443 | conf += dp->table[5]; | ||
444 | if (conf >= last) | ||
445 | return -EINVAL; | ||
446 | } | ||
447 | |||
448 | conf += 2; | ||
449 | } else { | ||
450 | /* no lookup table anymore, set entries for each | ||
451 | * combination of voltage swing and pre-emphasis | ||
452 | * level allowed by the DP spec. | ||
453 | */ | ||
454 | switch (lvsw) { | ||
455 | case 0: lpre += 0; break; | ||
456 | case 1: lpre += 4; break; | ||
457 | case 2: lpre += 7; break; | ||
458 | case 3: lpre += 9; break; | ||
459 | } | ||
460 | |||
461 | conf = conf + (lpre * dp->table[5]); | ||
462 | conf++; | ||
463 | } | ||
464 | |||
465 | drv |= conf[0] << shifts[i]; | ||
466 | pre |= conf[1] << shifts[i]; | ||
467 | unk = (unk & ~0x0000ff00) | (conf[2] << 8); | ||
468 | } | 275 | } |
469 | 276 | ||
470 | nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv); | ||
471 | nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre); | ||
472 | nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk); | ||
473 | |||
474 | return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); | 277 | return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); |
475 | } | 278 | } |
476 | 279 | ||
@@ -598,7 +401,8 @@ dp_link_train_fini(struct drm_device *dev, struct dp_state *dp) | |||
598 | } | 401 | } |
599 | 402 | ||
600 | bool | 403 | bool |
601 | nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) | 404 | nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, |
405 | struct dp_train_func *func) | ||
602 | { | 406 | { |
603 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 407 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
604 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | 408 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); |
@@ -614,15 +418,10 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) | |||
614 | if (!auxch) | 418 | if (!auxch) |
615 | return false; | 419 | return false; |
616 | 420 | ||
617 | dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry); | 421 | dp.func = func; |
618 | if (!dp.table) | ||
619 | return -EINVAL; | ||
620 | |||
621 | dp.dcb = nv_encoder->dcb; | 422 | dp.dcb = nv_encoder->dcb; |
622 | dp.crtc = nv_crtc->index; | 423 | dp.crtc = nv_crtc->index; |
623 | dp.auxch = auxch->drive; | 424 | dp.auxch = auxch->drive; |
624 | dp.or = nv_encoder->or; | ||
625 | dp.link = !(nv_encoder->dcb->sorconf.link & 1); | ||
626 | dp.dpcd = nv_encoder->dp.dpcd; | 425 | dp.dpcd = nv_encoder->dp.dpcd; |
627 | 426 | ||
628 | /* some sinks toggle hotplug in response to some of the actions | 427 | /* some sinks toggle hotplug in response to some of the actions |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 4bb6aeec91f6..cf0e74a2c667 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -1162,14 +1162,6 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); | |||
1162 | /* nouveau_hdmi.c */ | 1162 | /* nouveau_hdmi.c */ |
1163 | void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); | 1163 | void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); |
1164 | 1164 | ||
1165 | /* nouveau_dp.c */ | ||
1166 | int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, | ||
1167 | uint8_t *data, int data_nr); | ||
1168 | bool nouveau_dp_detect(struct drm_encoder *); | ||
1169 | bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate); | ||
1170 | void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32); | ||
1171 | u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); | ||
1172 | |||
1173 | /* nv04_fb.c */ | 1165 | /* nv04_fb.c */ |
1174 | extern int nv04_fb_vram_init(struct drm_device *); | 1166 | extern int nv04_fb_vram_init(struct drm_device *); |
1175 | extern int nv04_fb_init(struct drm_device *); | 1167 | extern int nv04_fb_init(struct drm_device *); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index e5d6e3faff3d..fa431742271e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h | |||
@@ -32,6 +32,14 @@ | |||
32 | 32 | ||
33 | #define NV_DPMS_CLEARED 0x80 | 33 | #define NV_DPMS_CLEARED 0x80 |
34 | 34 | ||
35 | struct dp_train_func { | ||
36 | void (*link_set)(struct drm_device *, struct dcb_entry *, int crtc, | ||
37 | int nr, u32 bw, bool enhframe); | ||
38 | void (*train_set)(struct drm_device *, struct dcb_entry *, u8 pattern); | ||
39 | void (*train_adj)(struct drm_device *, struct dcb_entry *, | ||
40 | u8 lane, u8 swing, u8 preem); | ||
41 | }; | ||
42 | |||
35 | struct nouveau_encoder { | 43 | struct nouveau_encoder { |
36 | struct drm_encoder_slave base; | 44 | struct drm_encoder_slave base; |
37 | 45 | ||
@@ -78,9 +86,19 @@ get_slave_funcs(struct drm_encoder *enc) | |||
78 | return to_encoder_slave(enc)->slave_funcs; | 86 | return to_encoder_slave(enc)->slave_funcs; |
79 | } | 87 | } |
80 | 88 | ||
89 | /* nouveau_dp.c */ | ||
90 | int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, | ||
91 | uint8_t *data, int data_nr); | ||
92 | bool nouveau_dp_detect(struct drm_encoder *); | ||
93 | bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate, | ||
94 | struct dp_train_func *); | ||
95 | u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **); | ||
96 | |||
81 | struct nouveau_connector * | 97 | struct nouveau_connector * |
82 | nouveau_encoder_connector_get(struct nouveau_encoder *encoder); | 98 | nouveau_encoder_connector_get(struct nouveau_encoder *encoder); |
83 | int nv50_sor_create(struct drm_connector *, struct dcb_entry *); | 99 | int nv50_sor_create(struct drm_connector *, struct dcb_entry *); |
100 | void nv50_sor_dp_calc_tu(struct drm_device *, int, int, u32, u32); | ||
84 | int nv50_dac_create(struct drm_connector *, struct dcb_entry *); | 101 | int nv50_dac_create(struct drm_connector *, struct dcb_entry *); |
85 | 102 | ||
103 | |||
86 | #endif /* __NOUVEAU_ENCODER_H__ */ | 104 | #endif /* __NOUVEAU_ENCODER_H__ */ |
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index ce440e2e58ca..0e47a898f415 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c | |||
@@ -863,9 +863,9 @@ nv50_display_unk20_handler(struct drm_device *dev) | |||
863 | if (type == OUTPUT_DP) { | 863 | if (type == OUTPUT_DP) { |
864 | int link = !(dcb->dpconf.sor.link & 1); | 864 | int link = !(dcb->dpconf.sor.link & 1); |
865 | if ((mc & 0x000f0000) == 0x00020000) | 865 | if ((mc & 0x000f0000) == 0x00020000) |
866 | nouveau_dp_tu_update(dev, or, link, pclk, 18); | 866 | nv50_sor_dp_calc_tu(dev, or, link, pclk, 18); |
867 | else | 867 | else |
868 | nouveau_dp_tu_update(dev, or, link, pclk, 24); | 868 | nv50_sor_dp_calc_tu(dev, or, link, pclk, 24); |
869 | } | 869 | } |
870 | 870 | ||
871 | if (dcb->type != OUTPUT_ANALOG) { | 871 | if (dcb->type != OUTPUT_ANALOG) { |
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c index c4423ba9c9bf..ba1b8cc03545 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c | |||
@@ -36,6 +36,193 @@ | |||
36 | #include "nouveau_crtc.h" | 36 | #include "nouveau_crtc.h" |
37 | #include "nv50_display.h" | 37 | #include "nv50_display.h" |
38 | 38 | ||
39 | static u32 | ||
40 | nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane) | ||
41 | { | ||
42 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
43 | static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ | ||
44 | static const u8 nv50[] = { 16, 8, 0, 24 }; | ||
45 | if (dev_priv->card_type == 0xaf) | ||
46 | return nvaf[lane]; | ||
47 | return nv50[lane]; | ||
48 | } | ||
49 | |||
50 | static void | ||
51 | nv50_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern) | ||
52 | { | ||
53 | u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); | ||
54 | nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x0f000000, pattern << 24); | ||
55 | } | ||
56 | |||
57 | static void | ||
58 | nv50_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb, | ||
59 | u8 lane, u8 swing, u8 preem) | ||
60 | { | ||
61 | u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); | ||
62 | u32 shift = nv50_sor_dp_lane_map(dev, dcb, lane); | ||
63 | u32 mask = 0x000000ff << shift; | ||
64 | u8 *table, *entry, *config; | ||
65 | |||
66 | table = nouveau_dp_bios_data(dev, dcb, &entry); | ||
67 | if (!table || (table[0] != 0x20 && table[0] != 0x21)) { | ||
68 | NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); | ||
69 | return; | ||
70 | } | ||
71 | |||
72 | config = entry + table[4]; | ||
73 | while (config[0] != swing || config[1] != preem) { | ||
74 | config += table[5]; | ||
75 | if (config >= entry + table[4] + entry[4] * table[5]) | ||
76 | return; | ||
77 | } | ||
78 | |||
79 | nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, config[2] << shift); | ||
80 | nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, config[3] << shift); | ||
81 | nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff00, config[4] << 8); | ||
82 | } | ||
83 | |||
84 | static void | ||
85 | nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc, | ||
86 | int link_nr, u32 link_bw, bool enhframe) | ||
87 | { | ||
88 | u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); | ||
89 | u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & ~0x001f4000; | ||
90 | u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)) & ~0x000c0000; | ||
91 | u8 *table, *entry, mask; | ||
92 | int i; | ||
93 | |||
94 | table = nouveau_dp_bios_data(dev, dcb, &entry); | ||
95 | if (!table || (table[0] != 0x20 && table[0] != 0x21)) { | ||
96 | NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n"); | ||
97 | return; | ||
98 | } | ||
99 | |||
100 | entry = ROMPTR(dev, entry[10]); | ||
101 | if (entry) { | ||
102 | while (link_bw < ROM16(entry[0]) * 10) | ||
103 | entry += 4; | ||
104 | |||
105 | nouveau_bios_run_init_table(dev, ROM16(entry[2]), dcb, crtc); | ||
106 | } | ||
107 | |||
108 | dpctrl |= ((1 << link_nr) - 1) << 16; | ||
109 | if (enhframe) | ||
110 | dpctrl |= 0x00004000; | ||
111 | |||
112 | if (link_bw > 162000) | ||
113 | clksor |= 0x00040000; | ||
114 | |||
115 | nv_wr32(dev, 0x614300 + (or * 0x800), clksor); | ||
116 | nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), dpctrl); | ||
117 | |||
118 | mask = 0; | ||
119 | for (i = 0; i < link_nr; i++) | ||
120 | mask |= 1 << (nv50_sor_dp_lane_map(dev, dcb, i) >> 3); | ||
121 | nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000000f, mask); | ||
122 | } | ||
123 | |||
124 | static void | ||
125 | nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw) | ||
126 | { | ||
127 | u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000; | ||
128 | u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)); | ||
129 | if (clksor & 0x000c0000) | ||
130 | *bw = 270000; | ||
131 | else | ||
132 | *bw = 162000; | ||
133 | |||
134 | if (dpctrl > 0x00030000) *nr = 4; | ||
135 | else if (dpctrl > 0x00010000) *nr = 2; | ||
136 | else *nr = 1; | ||
137 | } | ||
138 | |||
139 | void | ||
140 | nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) | ||
141 | { | ||
142 | const u32 symbol = 100000; | ||
143 | int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; | ||
144 | int TU, VTUi, VTUf, VTUa; | ||
145 | u64 link_data_rate, link_ratio, unk; | ||
146 | u32 best_diff = 64 * symbol; | ||
147 | u32 link_nr, link_bw, r; | ||
148 | |||
149 | /* calculate packed data rate for each lane */ | ||
150 | nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw); | ||
151 | link_data_rate = (clk * bpp / 8) / link_nr; | ||
152 | |||
153 | /* calculate ratio of packed data rate to link symbol rate */ | ||
154 | link_ratio = link_data_rate * symbol; | ||
155 | r = do_div(link_ratio, link_bw); | ||
156 | |||
157 | for (TU = 64; TU >= 32; TU--) { | ||
158 | /* calculate average number of valid symbols in each TU */ | ||
159 | u32 tu_valid = link_ratio * TU; | ||
160 | u32 calc, diff; | ||
161 | |||
162 | /* find a hw representation for the fraction.. */ | ||
163 | VTUi = tu_valid / symbol; | ||
164 | calc = VTUi * symbol; | ||
165 | diff = tu_valid - calc; | ||
166 | if (diff) { | ||
167 | if (diff >= (symbol / 2)) { | ||
168 | VTUf = symbol / (symbol - diff); | ||
169 | if (symbol - (VTUf * diff)) | ||
170 | VTUf++; | ||
171 | |||
172 | if (VTUf <= 15) { | ||
173 | VTUa = 1; | ||
174 | calc += symbol - (symbol / VTUf); | ||
175 | } else { | ||
176 | VTUa = 0; | ||
177 | VTUf = 1; | ||
178 | calc += symbol; | ||
179 | } | ||
180 | } else { | ||
181 | VTUa = 0; | ||
182 | VTUf = min((int)(symbol / diff), 15); | ||
183 | calc += symbol / VTUf; | ||
184 | } | ||
185 | |||
186 | diff = calc - tu_valid; | ||
187 | } else { | ||
188 | /* no remainder, but the hw doesn't like the fractional | ||
189 | * part to be zero. decrement the integer part and | ||
190 | * have the fraction add a whole symbol back | ||
191 | */ | ||
192 | VTUa = 0; | ||
193 | VTUf = 1; | ||
194 | VTUi--; | ||
195 | } | ||
196 | |||
197 | if (diff < best_diff) { | ||
198 | best_diff = diff; | ||
199 | bestTU = TU; | ||
200 | bestVTUa = VTUa; | ||
201 | bestVTUf = VTUf; | ||
202 | bestVTUi = VTUi; | ||
203 | if (diff == 0) | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | if (!bestTU) { | ||
209 | NV_ERROR(dev, "DP: unable to find suitable config\n"); | ||
210 | return; | ||
211 | } | ||
212 | |||
213 | /* XXX close to vbios numbers, but not right */ | ||
214 | unk = (symbol - link_ratio) * bestTU; | ||
215 | unk *= link_ratio; | ||
216 | r = do_div(unk, symbol); | ||
217 | r = do_div(unk, symbol); | ||
218 | unk += 6; | ||
219 | |||
220 | nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); | ||
221 | nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | | ||
222 | bestVTUf << 16 | | ||
223 | bestVTUi << 8 | | ||
224 | unk); | ||
225 | } | ||
39 | static void | 226 | static void |
40 | nv50_sor_disconnect(struct drm_encoder *encoder) | 227 | nv50_sor_disconnect(struct drm_encoder *encoder) |
41 | { | 228 | { |
@@ -124,9 +311,16 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) | |||
124 | return; | 311 | return; |
125 | 312 | ||
126 | if (mode == DRM_MODE_DPMS_ON) { | 313 | if (mode == DRM_MODE_DPMS_ON) { |
314 | struct dp_train_func func = { | ||
315 | .link_set = nv50_sor_dp_link_set, | ||
316 | .train_set = nv50_sor_dp_train_set, | ||
317 | .train_adj = nv50_sor_dp_train_adj | ||
318 | }; | ||
319 | u32 rate = nv_encoder->dp.datarate; | ||
127 | u8 status = DP_SET_POWER_D0; | 320 | u8 status = DP_SET_POWER_D0; |
321 | |||
128 | nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); | 322 | nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); |
129 | nouveau_dp_link_train(encoder, nv_encoder->dp.datarate); | 323 | nouveau_dp_link_train(encoder, rate, &func); |
130 | } else { | 324 | } else { |
131 | u8 status = DP_SET_POWER_D3; | 325 | u8 status = DP_SET_POWER_D3; |
132 | nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); | 326 | nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1); |