aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2012-03-09 01:22:56 -0500
committerBen Skeggs <bskeggs@redhat.com>2012-03-13 03:15:03 -0400
commit8663bc7cde00c8e832d985354f8a6d57a52f7d92 (patch)
treee11120d8757c5c80b346ab819a6cfced10c69d43 /drivers
parent8c1dcb6573ae71ffae392edf5f458543d310607e (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')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c233
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h18
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c4
-rw-r--r--drivers/gpu/drm/nouveau/nv50_sor.c196
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
164static u32
165dp_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
173static int
174dp_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
185void
186nouveau_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
274u8 * 164u8 *
275nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) 165nouveau_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 *****************************************************************************/
320struct dp_state { 210struct 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 {
335static void 222static void
336dp_set_link_config(struct drm_device *dev, struct dp_state *dp) 223dp_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
390static void 242static void
391dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) 243dp_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
405static const u8 nv50_lane_map[] = { 16, 8, 0, 24 };
406static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 };
407
408static int 257static int
409dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) 258dp_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
600bool 403bool
601nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) 404nouveau_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 */
1163void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); 1163void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
1164 1164
1165/* nouveau_dp.c */
1166int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
1167 uint8_t *data, int data_nr);
1168bool nouveau_dp_detect(struct drm_encoder *);
1169bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate);
1170void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32);
1171u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **);
1172
1173/* nv04_fb.c */ 1165/* nv04_fb.c */
1174extern int nv04_fb_vram_init(struct drm_device *); 1166extern int nv04_fb_vram_init(struct drm_device *);
1175extern int nv04_fb_init(struct drm_device *); 1167extern 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
35struct 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
35struct nouveau_encoder { 43struct 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 */
90int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
91 uint8_t *data, int data_nr);
92bool nouveau_dp_detect(struct drm_encoder *);
93bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate,
94 struct dp_train_func *);
95u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **);
96
81struct nouveau_connector * 97struct nouveau_connector *
82nouveau_encoder_connector_get(struct nouveau_encoder *encoder); 98nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
83int nv50_sor_create(struct drm_connector *, struct dcb_entry *); 99int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
100void nv50_sor_dp_calc_tu(struct drm_device *, int, int, u32, u32);
84int nv50_dac_create(struct drm_connector *, struct dcb_entry *); 101int 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
39static u32
40nv50_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
50static void
51nv50_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
57static void
58nv50_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
84static void
85nv50_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
124static void
125nv50_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
139void
140nv50_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}
39static void 226static void
40nv50_sor_disconnect(struct drm_encoder *encoder) 227nv50_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);