diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2011-08-03 19:26:44 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2011-09-20 02:10:21 -0400 |
commit | 27a4598737f8b315ba7827cb84578ba38c9b883c (patch) | |
tree | 005242dd0d4934b0c8886d25bfd4e717861d8b9c /drivers/gpu | |
parent | a002feceb7852964af579ece784b0123869f1e3f (diff) |
drm/nouveau/dp: restructure link training code
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_bios.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_dp.c | 526 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_encoder.h | 17 |
4 files changed, 206 insertions, 352 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 528fb6087814..b6efa8c7cdbf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c | |||
@@ -1179,19 +1179,18 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | |||
1179 | * | 1179 | * |
1180 | */ | 1180 | */ |
1181 | 1181 | ||
1182 | struct bit_displayport_encoder_table *dpe = NULL; | ||
1183 | struct dcb_entry *dcb = bios->display.output; | 1182 | struct dcb_entry *dcb = bios->display.output; |
1184 | struct drm_device *dev = bios->dev; | 1183 | struct drm_device *dev = bios->dev; |
1185 | uint8_t cond = bios->data[offset + 1]; | 1184 | uint8_t cond = bios->data[offset + 1]; |
1186 | int dummy; | 1185 | uint8_t *table, headerlen; |
1187 | 1186 | ||
1188 | BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond); | 1187 | BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond); |
1189 | 1188 | ||
1190 | if (!iexec->execute) | 1189 | if (!iexec->execute) |
1191 | return 3; | 1190 | return 3; |
1192 | 1191 | ||
1193 | dpe = nouveau_bios_dp_table(dev, dcb, &dummy); | 1192 | table = nouveau_bios_dp_table(dev, dcb, &headerlen); |
1194 | if (!dpe) { | 1193 | if (!table) { |
1195 | NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset); | 1194 | NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset); |
1196 | return 3; | 1195 | return 3; |
1197 | } | 1196 | } |
@@ -1208,7 +1207,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | |||
1208 | break; | 1207 | break; |
1209 | case 1: | 1208 | case 1: |
1210 | case 2: | 1209 | case 2: |
1211 | if (!(dpe->unknown & cond)) | 1210 | if (!(table[5] & cond)) |
1212 | iexec->execute = false; | 1211 | iexec->execute = false; |
1213 | break; | 1212 | break; |
1214 | case 5: | 1213 | case 5: |
@@ -4480,7 +4479,7 @@ bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent, | |||
4480 | 4479 | ||
4481 | void * | 4480 | void * |
4482 | nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, | 4481 | nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, |
4483 | int *length) | 4482 | uint8_t *headerlen) |
4484 | { | 4483 | { |
4485 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 4484 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
4486 | struct nvbios *bios = &dev_priv->vbios; | 4485 | struct nvbios *bios = &dev_priv->vbios; |
@@ -4498,7 +4497,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, | |||
4498 | return NULL; | 4497 | return NULL; |
4499 | } | 4498 | } |
4500 | 4499 | ||
4501 | *length = table[4]; | 4500 | *headerlen = table[4]; |
4502 | return bios_output_config_match(dev, dcbent, | 4501 | return bios_output_config_match(dev, dcbent, |
4503 | bios->display.dp_table_ptr + table[1], | 4502 | bios->display.dp_table_ptr + table[1], |
4504 | table[2], table[3], table[0] >= 0x21); | 4503 | table[2], table[3], table[0] >= 0x21); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 6a756a00d007..726d0ac63b9b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include "nouveau_i2c.h" | 28 | #include "nouveau_i2c.h" |
29 | #include "nouveau_connector.h" | 29 | #include "nouveau_connector.h" |
30 | #include "nouveau_encoder.h" | 30 | #include "nouveau_encoder.h" |
31 | #include "nouveau_crtc.h" | ||
31 | 32 | ||
32 | /****************************************************************************** | 33 | /****************************************************************************** |
33 | * aux channel util functions | 34 | * aux channel util functions |
@@ -178,22 +179,6 @@ auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size) | |||
178 | return 0; | 179 | return 0; |
179 | } | 180 | } |
180 | 181 | ||
181 | static int | ||
182 | auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size) | ||
183 | { | ||
184 | struct drm_device *dev = encoder->dev; | ||
185 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
186 | struct nouveau_i2c_chan *auxch; | ||
187 | int ret; | ||
188 | |||
189 | auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); | ||
190 | if (!auxch) | ||
191 | return -ENODEV; | ||
192 | |||
193 | ret = nouveau_dp_auxch(auxch, 8, address, buf, size); | ||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | static u32 | 182 | static u32 |
198 | dp_link_bw_get(struct drm_device *dev, int or, int link) | 183 | dp_link_bw_get(struct drm_device *dev, int or, int link) |
199 | { | 184 | { |
@@ -304,382 +289,269 @@ nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) | |||
304 | unk); | 289 | unk); |
305 | } | 290 | } |
306 | 291 | ||
307 | static int | 292 | /****************************************************************************** |
308 | nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) | 293 | * link training |
309 | { | 294 | *****************************************************************************/ |
310 | struct drm_device *dev = encoder->dev; | 295 | struct dp_state { |
311 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 296 | struct dcb_entry *dcb; |
312 | uint32_t tmp; | 297 | int auxch; |
313 | int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); | 298 | int crtc; |
314 | 299 | int or; | |
315 | tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | 300 | int link; |
316 | tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED | | 301 | int enh_frame; |
317 | NV50_SOR_DP_CTRL_LANE_MASK); | 302 | int link_nr; |
318 | tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16; | 303 | u32 link_bw; |
319 | if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN) | 304 | u8 stat[6]; |
320 | tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED; | 305 | u8 conf[4]; |
321 | nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); | 306 | }; |
322 | |||
323 | return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1); | ||
324 | } | ||
325 | 307 | ||
326 | static int | 308 | static void |
327 | nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd) | 309 | dp_set_link_config(struct drm_device *dev, struct dp_state *dp) |
328 | { | 310 | { |
329 | struct drm_device *dev = encoder->dev; | 311 | int or = dp->or, link = dp->link; |
330 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 312 | u32 clk_sor, dp_ctrl; |
331 | uint32_t tmp; | 313 | u8 sink[2]; |
332 | int reg = 0x614300 + (nv_encoder->or * 0x800); | ||
333 | 314 | ||
334 | tmp = nv_rd32(dev, reg); | 315 | NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); |
335 | tmp &= 0xfff3ffff; | ||
336 | if (cmd == DP_LINK_BW_2_7) | ||
337 | tmp |= 0x00040000; | ||
338 | nv_wr32(dev, reg, tmp); | ||
339 | 316 | ||
340 | return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1); | 317 | switch (dp->link_bw) { |
341 | } | 318 | case 270000: |
319 | clk_sor = 0x00040000; | ||
320 | sink[0] = DP_LINK_BW_2_7; | ||
321 | break; | ||
322 | default: | ||
323 | clk_sor = 0x00000000; | ||
324 | sink[0] = DP_LINK_BW_1_62; | ||
325 | break; | ||
326 | } | ||
342 | 327 | ||
343 | static int | 328 | dp_ctrl = ((1 << dp->link_nr) - 1) << 16; |
344 | nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern) | 329 | sink[1] = dp->link_nr; |
345 | { | 330 | if (dp->enh_frame) { |
346 | struct drm_device *dev = encoder->dev; | 331 | dp_ctrl |= 0x00004000; |
347 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 332 | sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; |
348 | uint32_t tmp; | 333 | } |
349 | uint8_t cmd; | ||
350 | int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); | ||
351 | int ret; | ||
352 | 334 | ||
353 | tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | 335 | nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, clk_sor); |
354 | tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN; | 336 | nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); |
355 | tmp |= (pattern << 24); | ||
356 | nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); | ||
357 | 337 | ||
358 | ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); | 338 | auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); |
359 | if (ret) | ||
360 | return ret; | ||
361 | cmd &= ~DP_TRAINING_PATTERN_MASK; | ||
362 | cmd |= (pattern & DP_TRAINING_PATTERN_MASK); | ||
363 | return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); | ||
364 | } | 339 | } |
365 | 340 | ||
366 | static int | 341 | static void |
367 | nouveau_dp_max_voltage_swing(struct drm_encoder *encoder) | 342 | dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) |
368 | { | 343 | { |
369 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 344 | NV_DEBUG_KMS(dev, "training pattern %d\n", tp); |
370 | struct drm_device *dev = encoder->dev; | 345 | nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); |
371 | struct bit_displayport_encoder_table_entry *dpse; | 346 | auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &tp, 1); |
372 | struct bit_displayport_encoder_table *dpe; | ||
373 | int i, dpe_headerlen, max_vs = 0; | ||
374 | |||
375 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | ||
376 | if (!dpe) | ||
377 | return false; | ||
378 | dpse = (void *)((char *)dpe + dpe_headerlen); | ||
379 | |||
380 | for (i = 0; i < dpe_headerlen; i++, dpse++) { | ||
381 | if (dpse->vs_level > max_vs) | ||
382 | max_vs = dpse->vs_level; | ||
383 | } | ||
384 | |||
385 | return max_vs; | ||
386 | } | 347 | } |
387 | 348 | ||
388 | static int | 349 | static int |
389 | nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs) | 350 | dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) |
390 | { | 351 | { |
391 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 352 | u32 mask = 0, drv = 0, pre = 0, unk = 0; |
392 | struct drm_device *dev = encoder->dev; | 353 | u8 shifts[4] = { 16, 8, 0, 24 }; |
393 | struct bit_displayport_encoder_table_entry *dpse; | 354 | u8 *bios, *last, headerlen; |
394 | struct bit_displayport_encoder_table *dpe; | 355 | int link = dp->link; |
395 | int i, dpe_headerlen, max_pre = 0; | 356 | int or = dp->or; |
357 | int i; | ||
358 | |||
359 | bios = nouveau_bios_dp_table(dev, dp->dcb, &headerlen); | ||
360 | last = bios + headerlen + (bios[4] * 5); | ||
361 | for (i = 0; i < dp->link_nr; i++) { | ||
362 | u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; | ||
363 | u8 *conf = bios + headerlen; | ||
364 | |||
365 | while (conf < last) { | ||
366 | if ((lane & 3) == conf[0] && | ||
367 | (lane >> 2) == conf[1]) | ||
368 | break; | ||
369 | conf += 5; | ||
370 | } | ||
396 | 371 | ||
397 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | 372 | if (conf == last) |
398 | if (!dpe) | 373 | return -EINVAL; |
399 | return false; | ||
400 | dpse = (void *)((char *)dpe + dpe_headerlen); | ||
401 | 374 | ||
402 | for (i = 0; i < dpe_headerlen; i++, dpse++) { | 375 | dp->conf[i] = (conf[1] << 3) | conf[0]; |
403 | if (dpse->vs_level != vs) | 376 | if (conf[0] == DP_TRAIN_VOLTAGE_SWING_1200) |
404 | continue; | 377 | dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED; |
378 | if (conf[1] == DP_TRAIN_PRE_EMPHASIS_9_5) | ||
379 | dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; | ||
405 | 380 | ||
406 | if (dpse->pre_level > max_pre) | 381 | NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]); |
407 | max_pre = dpse->pre_level; | 382 | |
383 | mask |= 0xff << shifts[i]; | ||
384 | drv |= conf[2] << shifts[i]; | ||
385 | pre |= conf[3] << shifts[i]; | ||
386 | unk = (unk & ~0x0000ff00) | (conf[4] << 8); | ||
387 | unk |= 1 << (shifts[i] >> 3); | ||
408 | } | 388 | } |
409 | 389 | ||
410 | return max_pre; | 390 | nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv); |
391 | nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre); | ||
392 | nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk); | ||
393 | |||
394 | return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); | ||
411 | } | 395 | } |
412 | 396 | ||
413 | static bool | 397 | static int |
414 | nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config) | 398 | dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay) |
415 | { | 399 | { |
416 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 400 | int ret; |
417 | struct drm_device *dev = encoder->dev; | ||
418 | struct bit_displayport_encoder_table *dpe; | ||
419 | int ret, i, dpe_headerlen, vs = 0, pre = 0; | ||
420 | uint8_t request[2]; | ||
421 | 401 | ||
422 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | 402 | udelay(delay); |
423 | if (!dpe) | ||
424 | return false; | ||
425 | 403 | ||
426 | ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2); | 404 | ret = auxch_tx(dev, dp->auxch, 9, DP_LANE0_1_STATUS, dp->stat, 6); |
427 | if (ret) | 405 | if (ret) |
428 | return false; | 406 | return ret; |
429 | 407 | ||
430 | NV_DEBUG_KMS(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]); | 408 | NV_DEBUG_KMS(dev, "status %02x %02x %02x %02x %02x %02x\n", |
409 | dp->stat[0], dp->stat[1], dp->stat[2], dp->stat[3], | ||
410 | dp->stat[4], dp->stat[5]); | ||
411 | return 0; | ||
412 | } | ||
431 | 413 | ||
432 | /* Keep all lanes at the same level.. */ | 414 | static int |
433 | for (i = 0; i < nv_encoder->dp.link_nr; i++) { | 415 | dp_link_train_cr(struct drm_device *dev, struct dp_state *dp) |
434 | int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf; | 416 | { |
435 | int lane_vs = lane_req & 3; | 417 | bool cr_done = false, abort = false; |
436 | int lane_pre = (lane_req >> 2) & 3; | 418 | int voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; |
419 | int tries = 0, i; | ||
437 | 420 | ||
438 | if (lane_vs > vs) | 421 | dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_1); |
439 | vs = lane_vs; | ||
440 | if (lane_pre > pre) | ||
441 | pre = lane_pre; | ||
442 | } | ||
443 | 422 | ||
444 | if (vs >= nouveau_dp_max_voltage_swing(encoder)) { | 423 | do { |
445 | vs = nouveau_dp_max_voltage_swing(encoder); | 424 | if (dp_link_train_commit(dev, dp) || |
446 | vs |= 4; | 425 | dp_link_train_update(dev, dp, 100)) |
447 | } | 426 | break; |
448 | 427 | ||
449 | if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) { | 428 | cr_done = true; |
450 | pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3); | 429 | for (i = 0; i < dp->link_nr; i++) { |
451 | pre |= 4; | 430 | u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; |
452 | } | 431 | if (!(lane & DP_LANE_CR_DONE)) { |
432 | cr_done = false; | ||
433 | if (dp->conf[i] & DP_TRAIN_MAX_SWING_REACHED) | ||
434 | abort = true; | ||
435 | break; | ||
436 | } | ||
437 | } | ||
453 | 438 | ||
454 | /* Update the configuration for all lanes.. */ | 439 | if ((dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { |
455 | for (i = 0; i < nv_encoder->dp.link_nr; i++) | 440 | voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; |
456 | config[i] = (pre << 3) | vs; | 441 | tries = 0; |
442 | } | ||
443 | } while (!cr_done && !abort && ++tries < 5); | ||
457 | 444 | ||
458 | return true; | 445 | return cr_done ? 0 : -1; |
459 | } | 446 | } |
460 | 447 | ||
461 | static bool | 448 | static int |
462 | nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config) | 449 | dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) |
463 | { | 450 | { |
464 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 451 | bool eq_done, cr_done = true; |
465 | struct drm_device *dev = encoder->dev; | 452 | int tries = 0, i; |
466 | struct bit_displayport_encoder_table_entry *dpse; | ||
467 | struct bit_displayport_encoder_table *dpe; | ||
468 | int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); | ||
469 | int dpe_headerlen, ret, i; | ||
470 | 453 | ||
471 | NV_DEBUG_KMS(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n", | 454 | dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2); |
472 | config[0], config[1], config[2], config[3]); | ||
473 | 455 | ||
474 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | 456 | do { |
475 | if (!dpe) | 457 | if (dp_link_train_update(dev, dp, 400)) |
476 | return false; | ||
477 | dpse = (void *)((char *)dpe + dpe_headerlen); | ||
478 | |||
479 | for (i = 0; i < dpe->record_nr; i++, dpse++) { | ||
480 | if (dpse->vs_level == (config[0] & 3) && | ||
481 | dpse->pre_level == ((config[0] >> 3) & 3)) | ||
482 | break; | 458 | break; |
483 | } | ||
484 | BUG_ON(i == dpe->record_nr); | ||
485 | |||
486 | for (i = 0; i < nv_encoder->dp.link_nr; i++) { | ||
487 | const int shift[4] = { 16, 8, 0, 24 }; | ||
488 | uint32_t mask = 0xff << shift[i]; | ||
489 | uint32_t reg0, reg1, reg2; | ||
490 | |||
491 | reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask; | ||
492 | reg0 |= (dpse->reg0 << shift[i]); | ||
493 | reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask; | ||
494 | reg1 |= (dpse->reg1 << shift[i]); | ||
495 | reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff; | ||
496 | reg2 |= (dpse->reg2 << 8); | ||
497 | nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0); | ||
498 | nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1); | ||
499 | nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2); | ||
500 | } | ||
501 | 459 | ||
502 | ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4); | 460 | eq_done = !!(dp->stat[2] & DP_INTERLANE_ALIGN_DONE); |
503 | if (ret) | 461 | for (i = 0; i < dp->link_nr && eq_done; i++) { |
504 | return false; | 462 | u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; |
463 | if (!(lane & DP_LANE_CR_DONE)) | ||
464 | cr_done = false; | ||
465 | if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || | ||
466 | !(lane & DP_LANE_SYMBOL_LOCKED)) | ||
467 | eq_done = false; | ||
468 | } | ||
505 | 469 | ||
506 | return true; | 470 | if (dp_link_train_commit(dev, dp)) |
471 | break; | ||
472 | } while (!eq_done && cr_done && ++tries <= 5); | ||
473 | |||
474 | return eq_done ? 0 : -1; | ||
507 | } | 475 | } |
508 | 476 | ||
509 | bool | 477 | bool |
510 | nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) | 478 | nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) |
511 | { | 479 | { |
512 | struct drm_device *dev = encoder->dev; | 480 | struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; |
513 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
514 | struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; | 481 | struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; |
515 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | 482 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); |
516 | struct nouveau_connector *nv_connector; | 483 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); |
517 | struct bit_displayport_encoder_table *dpe; | 484 | struct nouveau_connector *nv_connector = |
518 | int dpe_headerlen; | 485 | nouveau_encoder_connector_get(nv_encoder); |
519 | uint8_t config[4], status[3]; | 486 | struct drm_device *dev = encoder->dev; |
520 | bool cr_done, cr_max_vs, eq_done, hpd_state; | 487 | struct nouveau_i2c_chan *auxch; |
521 | int ret = 0, i, tries, voltage; | 488 | const u32 bw_list[] = { 270000, 162000, 0 }; |
522 | 489 | const u32 *link_bw = bw_list; | |
523 | NV_DEBUG_KMS(dev, "link training!!\n"); | 490 | struct dp_state dp; |
524 | 491 | u8 *bios, headerlen; | |
525 | nv_connector = nouveau_encoder_connector_get(nv_encoder); | 492 | u16 script; |
526 | if (!nv_connector) | ||
527 | return false; | ||
528 | |||
529 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | ||
530 | if (!dpe) { | ||
531 | NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or); | ||
532 | return false; | ||
533 | } | ||
534 | |||
535 | /* disable hotplug detect, this flips around on some panels during | ||
536 | * link training. | ||
537 | */ | ||
538 | hpd_state = pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); | ||
539 | |||
540 | if (dpe->script0) { | ||
541 | NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); | ||
542 | nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0), | ||
543 | nv_encoder->dcb, -1); | ||
544 | } | ||
545 | |||
546 | train: | ||
547 | cr_done = eq_done = false; | ||
548 | |||
549 | /* set link configuration */ | ||
550 | NV_DEBUG_KMS(dev, "\tbegin train: bw %d, lanes %d\n", | ||
551 | nv_encoder->dp.link_bw, nv_encoder->dp.link_nr); | ||
552 | |||
553 | ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw); | ||
554 | if (ret) | ||
555 | return false; | ||
556 | |||
557 | config[0] = nv_encoder->dp.link_nr; | ||
558 | if (nv_encoder->dp.dpcd_version >= 0x11 && | ||
559 | nv_encoder->dp.enhanced_frame) | ||
560 | config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; | ||
561 | 493 | ||
562 | ret = nouveau_dp_lane_count_set(encoder, config[0]); | 494 | auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); |
563 | if (ret) | 495 | if (!auxch) |
564 | return false; | 496 | return false; |
565 | 497 | ||
566 | /* clock recovery */ | 498 | bios = nouveau_bios_dp_table(dev, nv_encoder->dcb, &headerlen); |
567 | NV_DEBUG_KMS(dev, "\tbegin cr\n"); | 499 | if (!bios) |
568 | ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1); | 500 | return -EINVAL; |
569 | if (ret) | ||
570 | goto stop; | ||
571 | |||
572 | tries = 0; | ||
573 | voltage = -1; | ||
574 | memset(config, 0x00, sizeof(config)); | ||
575 | for (;;) { | ||
576 | if (!nouveau_dp_link_train_commit(encoder, config)) | ||
577 | break; | ||
578 | |||
579 | udelay(100); | ||
580 | 501 | ||
581 | ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2); | 502 | dp.dcb = nv_encoder->dcb; |
582 | if (ret) | 503 | dp.crtc = nv_crtc->index; |
583 | break; | 504 | dp.auxch = auxch->rd; |
584 | NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", | 505 | dp.or = nv_encoder->or; |
585 | status[0], status[1]); | 506 | dp.link = !(nv_encoder->dcb->sorconf.link & 1); |
586 | 507 | dp.enh_frame = nv_encoder->dp.enhanced_frame; | |
587 | cr_done = true; | ||
588 | cr_max_vs = false; | ||
589 | for (i = 0; i < nv_encoder->dp.link_nr; i++) { | ||
590 | int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; | ||
591 | |||
592 | if (!(lane & DP_LANE_CR_DONE)) { | ||
593 | cr_done = false; | ||
594 | if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) | ||
595 | cr_max_vs = true; | ||
596 | break; | ||
597 | } | ||
598 | } | ||
599 | |||
600 | if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { | ||
601 | voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK; | ||
602 | tries = 0; | ||
603 | } | ||
604 | |||
605 | if (cr_done || cr_max_vs || (++tries == 5)) | ||
606 | break; | ||
607 | |||
608 | if (!nouveau_dp_link_train_adjust(encoder, config)) | ||
609 | break; | ||
610 | } | ||
611 | |||
612 | if (!cr_done) | ||
613 | goto stop; | ||
614 | |||
615 | /* channel equalisation */ | ||
616 | NV_DEBUG_KMS(dev, "\tbegin eq\n"); | ||
617 | ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2); | ||
618 | if (ret) | ||
619 | goto stop; | ||
620 | 508 | ||
621 | for (tries = 0; tries <= 5; tries++) { | 509 | /* some sinks toggle hotplug in response to some of the actions |
622 | udelay(400); | 510 | * we take during link training (DP_SET_POWER is one), we need |
511 | * to ignore them for the moment to avoid races. | ||
512 | */ | ||
513 | pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); | ||
623 | 514 | ||
624 | ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3); | 515 | /* execute pre-train script from vbios */ |
625 | if (ret) | 516 | nouveau_bios_run_init_table(dev, ROM16(bios[6]), dp.dcb, dp.crtc); |
626 | break; | ||
627 | NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", | ||
628 | status[0], status[1]); | ||
629 | 517 | ||
630 | eq_done = true; | 518 | /* start off at highest link rate supported by encoder and display */ |
631 | if (!(status[2] & DP_INTERLANE_ALIGN_DONE)) | 519 | if (nv_encoder->dp.link_bw == DP_LINK_BW_1_62) |
632 | eq_done = false; | 520 | link_bw++; |
633 | 521 | ||
634 | for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) { | 522 | while (link_bw[0]) { |
635 | int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; | 523 | /* find minimum required lane count at this link rate */ |
524 | dp.link_nr = nv_encoder->dp.link_nr; | ||
525 | while ((dp.link_nr >> 1) * link_bw[0] > datarate) | ||
526 | dp.link_nr >>= 1; | ||
636 | 527 | ||
637 | if (!(lane & DP_LANE_CR_DONE)) { | 528 | /* drop link rate to minimum with this lane count */ |
638 | cr_done = false; | 529 | while ((link_bw[1] * dp.link_nr) > datarate) |
639 | break; | 530 | link_bw++; |
640 | } | 531 | dp.link_bw = link_bw[0]; |
641 | 532 | ||
642 | if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || | 533 | /* program selected link configuration */ |
643 | !(lane & DP_LANE_SYMBOL_LOCKED)) { | 534 | dp_set_link_config(dev, &dp); |
644 | eq_done = false; | ||
645 | break; | ||
646 | } | ||
647 | } | ||
648 | 535 | ||
649 | if (eq_done || !cr_done) | 536 | /* attempt to train the link at this configuration */ |
537 | memset(dp.stat, 0x00, sizeof(dp.stat)); | ||
538 | if (!dp_link_train_cr(dev, &dp) && | ||
539 | !dp_link_train_eq(dev, &dp)) | ||
650 | break; | 540 | break; |
651 | 541 | ||
652 | if (!nouveau_dp_link_train_adjust(encoder, config) || | 542 | /* retry at lower rate */ |
653 | !nouveau_dp_link_train_commit(encoder, config)) | 543 | link_bw++; |
654 | break; | ||
655 | } | 544 | } |
656 | 545 | ||
657 | stop: | 546 | /* finish link training */ |
658 | /* end link training */ | 547 | dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE); |
659 | ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE); | ||
660 | if (ret) | ||
661 | return false; | ||
662 | |||
663 | /* retry at a lower setting, if possible */ | ||
664 | if (!ret && !(eq_done && cr_done)) { | ||
665 | NV_DEBUG_KMS(dev, "\twe failed\n"); | ||
666 | if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62) { | ||
667 | NV_DEBUG_KMS(dev, "retry link training at low rate\n"); | ||
668 | nv_encoder->dp.link_bw = DP_LINK_BW_1_62; | ||
669 | goto train; | ||
670 | } | ||
671 | } | ||
672 | 548 | ||
673 | if (dpe->script1) { | 549 | /* execute post-train script from vbios */ |
674 | NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or); | 550 | nouveau_bios_run_init_table(dev, ROM16(bios[8]), dp.dcb, dp.crtc); |
675 | nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1), | ||
676 | nv_encoder->dcb, -1); | ||
677 | } | ||
678 | 551 | ||
679 | /* re-enable hotplug detect */ | 552 | /* re-enable hotplug detect */ |
680 | pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, hpd_state); | 553 | pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true); |
681 | 554 | return true; | |
682 | return eq_done; | ||
683 | } | 555 | } |
684 | 556 | ||
685 | bool | 557 | bool |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 8cca5ff3add9..3cf8e6a10e9d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -1081,7 +1081,7 @@ extern int get_pll_limits(struct drm_device *, uint32_t limit_match, | |||
1081 | extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk, | 1081 | extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk, |
1082 | struct dcb_entry *, int crtc); | 1082 | struct dcb_entry *, int crtc); |
1083 | extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *, | 1083 | extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *, |
1084 | int *length); | 1084 | u8 *headerlen); |
1085 | extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); | 1085 | extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); |
1086 | extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *); | 1086 | extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *); |
1087 | extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk, | 1087 | extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk, |
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index fc5ee0d68573..deafe7b1f524 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h | |||
@@ -84,21 +84,4 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder); | |||
84 | int nv50_sor_create(struct drm_connector *, struct dcb_entry *); | 84 | int nv50_sor_create(struct drm_connector *, struct dcb_entry *); |
85 | int nv50_dac_create(struct drm_connector *, struct dcb_entry *); | 85 | int nv50_dac_create(struct drm_connector *, struct dcb_entry *); |
86 | 86 | ||
87 | struct bit_displayport_encoder_table { | ||
88 | uint32_t match; | ||
89 | uint8_t record_nr; | ||
90 | uint8_t unknown; | ||
91 | uint16_t script0; | ||
92 | uint16_t script1; | ||
93 | uint16_t unknown_table; | ||
94 | } __attribute__ ((packed)); | ||
95 | |||
96 | struct bit_displayport_encoder_table_entry { | ||
97 | uint8_t vs_level; | ||
98 | uint8_t pre_level; | ||
99 | uint8_t reg0; | ||
100 | uint8_t reg1; | ||
101 | uint8_t reg2; | ||
102 | } __attribute__ ((packed)); | ||
103 | |||
104 | #endif /* __NOUVEAU_ENCODER_H__ */ | 87 | #endif /* __NOUVEAU_ENCODER_H__ */ |