diff options
author | Sean Paul <seanpaul@chromium.org> | 2012-10-31 19:21:00 -0400 |
---|---|---|
committer | Jingoo Han <jg1.han@samsung.com> | 2012-11-28 20:33:27 -0500 |
commit | fadec4b7e59e3a89c43dcba1c9812a9f99d2a61a (patch) | |
tree | 3ac223022c0faefc87489b0bd140e37202d2bba2 /drivers/video/exynos | |
parent | ace2d7f2b537a072e39ddb4f51467d32b4900f8c (diff) |
video: exynos_dp: Clean up SW link training
Clean up some of the SW training code to make it more clear and reduce
duplicate code.
[jg1.han@samsung.com: used exynos_dp_write_bytes_to_dpcd()]
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
Diffstat (limited to 'drivers/video/exynos')
-rw-r--r-- | drivers/video/exynos/exynos_dp_core.c | 292 |
1 files changed, 119 insertions, 173 deletions
diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c index e007906694fa..7161f9f86c9f 100644 --- a/drivers/video/exynos/exynos_dp_core.c +++ b/drivers/video/exynos/exynos_dp_core.c | |||
@@ -277,7 +277,7 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp) | |||
277 | 277 | ||
278 | /* Set sink to D0 (Sink Not Ready) mode. */ | 278 | /* Set sink to D0 (Sink Not Ready) mode. */ |
279 | retval = exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE, | 279 | retval = exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE, |
280 | DPCD_SET_POWER_STATE_D0); | 280 | DPCD_SET_POWER_STATE_D0); |
281 | if (retval) | 281 | if (retval) |
282 | return retval; | 282 | return retval; |
283 | 283 | ||
@@ -302,17 +302,18 @@ static int exynos_dp_link_start(struct exynos_dp_device *dp) | |||
302 | exynos_dp_set_training_pattern(dp, TRAINING_PTN1); | 302 | exynos_dp_set_training_pattern(dp, TRAINING_PTN1); |
303 | 303 | ||
304 | /* Set RX training pattern */ | 304 | /* Set RX training pattern */ |
305 | exynos_dp_write_byte_to_dpcd(dp, | 305 | retval = exynos_dp_write_byte_to_dpcd(dp, |
306 | DPCD_ADDR_TRAINING_PATTERN_SET, | 306 | DPCD_ADDR_TRAINING_PATTERN_SET, |
307 | DPCD_SCRAMBLING_DISABLED | | 307 | DPCD_SCRAMBLING_DISABLED | DPCD_TRAINING_PATTERN_1); |
308 | DPCD_TRAINING_PATTERN_1); | 308 | if (retval) |
309 | return retval; | ||
309 | 310 | ||
310 | for (lane = 0; lane < lane_count; lane++) | 311 | for (lane = 0; lane < lane_count; lane++) |
311 | buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 | | 312 | buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 | |
312 | DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0; | 313 | DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0; |
313 | retval = exynos_dp_write_bytes_to_dpcd(dp, | 314 | |
314 | DPCD_ADDR_TRAINING_LANE0_SET, | 315 | retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET, |
315 | lane_count, buf); | 316 | lane_count, buf); |
316 | 317 | ||
317 | return retval; | 318 | return retval; |
318 | } | 319 | } |
@@ -338,18 +339,17 @@ static int exynos_dp_clock_recovery_ok(u8 link_status[2], int lane_count) | |||
338 | return 0; | 339 | return 0; |
339 | } | 340 | } |
340 | 341 | ||
341 | static int exynos_dp_channel_eq_ok(u8 link_align[3], int lane_count) | 342 | static int exynos_dp_channel_eq_ok(u8 link_status[2], u8 link_align, |
343 | int lane_count) | ||
342 | { | 344 | { |
343 | int lane; | 345 | int lane; |
344 | u8 lane_align; | ||
345 | u8 lane_status; | 346 | u8 lane_status; |
346 | 347 | ||
347 | lane_align = link_align[2]; | 348 | if ((link_align & DPCD_INTERLANE_ALIGN_DONE) == 0) |
348 | if ((lane_align & DPCD_INTERLANE_ALIGN_DONE) == 0) | ||
349 | return -EINVAL; | 349 | return -EINVAL; |
350 | 350 | ||
351 | for (lane = 0; lane < lane_count; lane++) { | 351 | for (lane = 0; lane < lane_count; lane++) { |
352 | lane_status = exynos_dp_get_lane_status(link_align, lane); | 352 | lane_status = exynos_dp_get_lane_status(link_status, lane); |
353 | lane_status &= DPCD_CHANNEL_EQ_BITS; | 353 | lane_status &= DPCD_CHANNEL_EQ_BITS; |
354 | if (lane_status != DPCD_CHANNEL_EQ_BITS) | 354 | if (lane_status != DPCD_CHANNEL_EQ_BITS) |
355 | return -EINVAL; | 355 | return -EINVAL; |
@@ -433,22 +433,47 @@ static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp) | |||
433 | dp->link_train.lt_state = FAILED; | 433 | dp->link_train.lt_state = FAILED; |
434 | } | 434 | } |
435 | 435 | ||
436 | static void exynos_dp_get_adjust_training_lane(struct exynos_dp_device *dp, | ||
437 | u8 adjust_request[2]) | ||
438 | { | ||
439 | int lane, lane_count; | ||
440 | u8 voltage_swing, pre_emphasis, training_lane; | ||
441 | |||
442 | lane_count = dp->link_train.lane_count; | ||
443 | for (lane = 0; lane < lane_count; lane++) { | ||
444 | voltage_swing = exynos_dp_get_adjust_request_voltage( | ||
445 | adjust_request, lane); | ||
446 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | ||
447 | adjust_request, lane); | ||
448 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | ||
449 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | ||
450 | |||
451 | if (voltage_swing == VOLTAGE_LEVEL_3) | ||
452 | training_lane |= DPCD_MAX_SWING_REACHED; | ||
453 | if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) | ||
454 | training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; | ||
455 | |||
456 | dp->link_train.training_lane[lane] = training_lane; | ||
457 | } | ||
458 | } | ||
459 | |||
436 | static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) | 460 | static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) |
437 | { | 461 | { |
438 | u8 link_status[2]; | ||
439 | int lane, lane_count, retval; | 462 | int lane, lane_count, retval; |
440 | 463 | u8 voltage_swing, pre_emphasis, training_lane; | |
441 | u8 adjust_request[2]; | 464 | u8 link_status[2], adjust_request[2]; |
442 | u8 voltage_swing; | ||
443 | u8 pre_emphasis; | ||
444 | u8 training_lane; | ||
445 | 465 | ||
446 | usleep_range(100, 101); | 466 | usleep_range(100, 101); |
447 | 467 | ||
448 | lane_count = dp->link_train.lane_count; | 468 | lane_count = dp->link_train.lane_count; |
449 | 469 | ||
450 | retval = exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, | 470 | retval = exynos_dp_read_bytes_from_dpcd(dp, |
451 | 2, link_status); | 471 | DPCD_ADDR_LANE0_1_STATUS, 2, link_status); |
472 | if (retval) | ||
473 | return retval; | ||
474 | |||
475 | retval = exynos_dp_read_bytes_from_dpcd(dp, | ||
476 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request); | ||
452 | if (retval) | 477 | if (retval) |
453 | return retval; | 478 | return retval; |
454 | 479 | ||
@@ -456,43 +481,10 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) | |||
456 | /* set training pattern 2 for EQ */ | 481 | /* set training pattern 2 for EQ */ |
457 | exynos_dp_set_training_pattern(dp, TRAINING_PTN2); | 482 | exynos_dp_set_training_pattern(dp, TRAINING_PTN2); |
458 | 483 | ||
459 | for (lane = 0; lane < lane_count; lane++) { | ||
460 | retval = exynos_dp_read_bytes_from_dpcd(dp, | ||
461 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, | ||
462 | 2, adjust_request); | ||
463 | if (retval) | ||
464 | return retval; | ||
465 | |||
466 | voltage_swing = exynos_dp_get_adjust_request_voltage( | ||
467 | adjust_request, lane); | ||
468 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | ||
469 | adjust_request, lane); | ||
470 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | ||
471 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | ||
472 | |||
473 | if (voltage_swing == VOLTAGE_LEVEL_3) | ||
474 | training_lane |= DPCD_MAX_SWING_REACHED; | ||
475 | if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) | ||
476 | training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; | ||
477 | |||
478 | dp->link_train.training_lane[lane] = training_lane; | ||
479 | |||
480 | exynos_dp_set_lane_link_training(dp, | ||
481 | dp->link_train.training_lane[lane], | ||
482 | lane); | ||
483 | } | ||
484 | |||
485 | retval = exynos_dp_write_byte_to_dpcd(dp, | 484 | retval = exynos_dp_write_byte_to_dpcd(dp, |
486 | DPCD_ADDR_TRAINING_PATTERN_SET, | 485 | DPCD_ADDR_TRAINING_PATTERN_SET, |
487 | DPCD_SCRAMBLING_DISABLED | | 486 | DPCD_SCRAMBLING_DISABLED | |
488 | DPCD_TRAINING_PATTERN_2); | 487 | DPCD_TRAINING_PATTERN_2); |
489 | if (retval) | ||
490 | return retval; | ||
491 | |||
492 | retval = exynos_dp_write_bytes_to_dpcd(dp, | ||
493 | DPCD_ADDR_TRAINING_LANE0_SET, | ||
494 | lane_count, | ||
495 | dp->link_train.training_lane); | ||
496 | if (retval) | 488 | if (retval) |
497 | return retval; | 489 | return retval; |
498 | 490 | ||
@@ -502,162 +494,116 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp) | |||
502 | for (lane = 0; lane < lane_count; lane++) { | 494 | for (lane = 0; lane < lane_count; lane++) { |
503 | training_lane = exynos_dp_get_lane_link_training( | 495 | training_lane = exynos_dp_get_lane_link_training( |
504 | dp, lane); | 496 | dp, lane); |
505 | retval = exynos_dp_read_bytes_from_dpcd(dp, | ||
506 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, | ||
507 | 2, adjust_request); | ||
508 | if (retval) | ||
509 | return retval; | ||
510 | |||
511 | voltage_swing = exynos_dp_get_adjust_request_voltage( | 497 | voltage_swing = exynos_dp_get_adjust_request_voltage( |
512 | adjust_request, lane); | 498 | adjust_request, lane); |
513 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | 499 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( |
514 | adjust_request, lane); | 500 | adjust_request, lane); |
515 | 501 | ||
516 | if (voltage_swing == VOLTAGE_LEVEL_3 || | 502 | if (DPCD_VOLTAGE_SWING_GET(training_lane) == |
517 | pre_emphasis == PRE_EMPHASIS_LEVEL_3) { | 503 | voltage_swing && |
518 | dev_err(dp->dev, "voltage or pre emphasis reached max level\n"); | 504 | DPCD_PRE_EMPHASIS_GET(training_lane) == |
519 | goto reduce_link_rate; | 505 | pre_emphasis) |
520 | } | ||
521 | |||
522 | if ((DPCD_VOLTAGE_SWING_GET(training_lane) == | ||
523 | voltage_swing) && | ||
524 | (DPCD_PRE_EMPHASIS_GET(training_lane) == | ||
525 | pre_emphasis)) { | ||
526 | dp->link_train.cr_loop[lane]++; | 506 | dp->link_train.cr_loop[lane]++; |
527 | if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP) { | ||
528 | dev_err(dp->dev, "CR Max loop\n"); | ||
529 | goto reduce_link_rate; | ||
530 | } | ||
531 | } | ||
532 | |||
533 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | ||
534 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | ||
535 | 507 | ||
536 | if (voltage_swing == VOLTAGE_LEVEL_3) | 508 | if (dp->link_train.cr_loop[lane] == MAX_CR_LOOP || |
537 | training_lane |= DPCD_MAX_SWING_REACHED; | 509 | voltage_swing == VOLTAGE_LEVEL_3 || |
538 | if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) | 510 | pre_emphasis == PRE_EMPHASIS_LEVEL_3) { |
539 | training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; | 511 | dev_err(dp->dev, "CR Max reached (%d,%d,%d)\n", |
512 | dp->link_train.cr_loop[lane], | ||
513 | voltage_swing, pre_emphasis); | ||
514 | exynos_dp_reduce_link_rate(dp); | ||
515 | return -EIO; | ||
516 | } | ||
517 | } | ||
518 | } | ||
540 | 519 | ||
541 | dp->link_train.training_lane[lane] = training_lane; | 520 | exynos_dp_get_adjust_training_lane(dp, adjust_request); |
542 | 521 | ||
543 | exynos_dp_set_lane_link_training(dp, | 522 | for (lane = 0; lane < lane_count; lane++) |
544 | dp->link_train.training_lane[lane], lane); | 523 | exynos_dp_set_lane_link_training(dp, |
545 | } | 524 | dp->link_train.training_lane[lane], lane); |
546 | 525 | ||
547 | retval = exynos_dp_write_bytes_to_dpcd(dp, | 526 | retval = exynos_dp_write_bytes_to_dpcd(dp, |
548 | DPCD_ADDR_TRAINING_LANE0_SET, lane_count, | 527 | DPCD_ADDR_TRAINING_LANE0_SET, lane_count, |
549 | dp->link_train.training_lane); | 528 | dp->link_train.training_lane); |
550 | if (retval) | 529 | if (retval) |
551 | return retval; | 530 | return retval; |
552 | } | ||
553 | 531 | ||
554 | return retval; | 532 | return retval; |
555 | |||
556 | reduce_link_rate: | ||
557 | exynos_dp_reduce_link_rate(dp); | ||
558 | return -EIO; | ||
559 | } | 533 | } |
560 | 534 | ||
561 | static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) | 535 | static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp) |
562 | { | 536 | { |
563 | u8 link_status[2]; | ||
564 | u8 link_align[3]; | ||
565 | int lane, lane_count, retval; | 537 | int lane, lane_count, retval; |
566 | u32 reg; | 538 | u32 reg; |
567 | 539 | u8 link_align, link_status[2], adjust_request[2]; | |
568 | u8 adjust_request[2]; | ||
569 | u8 voltage_swing; | ||
570 | u8 pre_emphasis; | ||
571 | u8 training_lane; | ||
572 | 540 | ||
573 | usleep_range(400, 401); | 541 | usleep_range(400, 401); |
574 | 542 | ||
575 | lane_count = dp->link_train.lane_count; | 543 | lane_count = dp->link_train.lane_count; |
576 | 544 | ||
577 | retval = exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS, | 545 | retval = exynos_dp_read_bytes_from_dpcd(dp, |
578 | 2, link_status); | 546 | DPCD_ADDR_LANE0_1_STATUS, 2, link_status); |
579 | if (retval) | 547 | if (retval) |
580 | return retval; | 548 | return retval; |
581 | 549 | ||
582 | if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) { | 550 | if (exynos_dp_clock_recovery_ok(link_status, lane_count)) { |
583 | link_align[0] = link_status[0]; | 551 | exynos_dp_reduce_link_rate(dp); |
584 | link_align[1] = link_status[1]; | 552 | return -EIO; |
585 | 553 | } | |
586 | exynos_dp_read_byte_from_dpcd(dp, | ||
587 | DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, | ||
588 | &link_align[2]); | ||
589 | |||
590 | for (lane = 0; lane < lane_count; lane++) { | ||
591 | retval = exynos_dp_read_bytes_from_dpcd(dp, | ||
592 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, | ||
593 | 2, adjust_request); | ||
594 | if (retval) | ||
595 | return retval; | ||
596 | 554 | ||
597 | voltage_swing = exynos_dp_get_adjust_request_voltage( | 555 | retval = exynos_dp_read_bytes_from_dpcd(dp, |
598 | adjust_request, lane); | 556 | DPCD_ADDR_ADJUST_REQUEST_LANE0_1, 2, adjust_request); |
599 | pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis( | 557 | if (retval) |
600 | adjust_request, lane); | 558 | return retval; |
601 | training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) | | ||
602 | DPCD_PRE_EMPHASIS_SET(pre_emphasis); | ||
603 | 559 | ||
604 | if (voltage_swing == VOLTAGE_LEVEL_3) | 560 | retval = exynos_dp_read_byte_from_dpcd(dp, |
605 | training_lane |= DPCD_MAX_SWING_REACHED; | 561 | DPCD_ADDR_LANE_ALIGN_STATUS_UPDATED, &link_align); |
606 | if (pre_emphasis == PRE_EMPHASIS_LEVEL_3) | 562 | if (retval) |
607 | training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED; | 563 | return retval; |
608 | 564 | ||
609 | dp->link_train.training_lane[lane] = training_lane; | 565 | exynos_dp_get_adjust_training_lane(dp, adjust_request); |
610 | } | ||
611 | 566 | ||
612 | if (exynos_dp_channel_eq_ok(link_align, lane_count) == 0) { | 567 | if (!exynos_dp_channel_eq_ok(link_status, link_align, lane_count)) { |
613 | /* traing pattern Set to Normal */ | 568 | /* traing pattern Set to Normal */ |
614 | exynos_dp_training_pattern_dis(dp); | 569 | exynos_dp_training_pattern_dis(dp); |
615 | 570 | ||
616 | dev_info(dp->dev, "Link Training success!\n"); | 571 | dev_info(dp->dev, "Link Training success!\n"); |
617 | 572 | ||
618 | exynos_dp_get_link_bandwidth(dp, ®); | 573 | exynos_dp_get_link_bandwidth(dp, ®); |
619 | dp->link_train.link_rate = reg; | 574 | dp->link_train.link_rate = reg; |
620 | dev_dbg(dp->dev, "final bandwidth = %.2x\n", | 575 | dev_dbg(dp->dev, "final bandwidth = %.2x\n", |
621 | dp->link_train.link_rate); | 576 | dp->link_train.link_rate); |
622 | 577 | ||
623 | exynos_dp_get_lane_count(dp, ®); | 578 | exynos_dp_get_lane_count(dp, ®); |
624 | dp->link_train.lane_count = reg; | 579 | dp->link_train.lane_count = reg; |
625 | dev_dbg(dp->dev, "final lane count = %.2x\n", | 580 | dev_dbg(dp->dev, "final lane count = %.2x\n", |
626 | dp->link_train.lane_count); | 581 | dp->link_train.lane_count); |
627 | 582 | ||
628 | /* set enhanced mode if available */ | 583 | /* set enhanced mode if available */ |
629 | exynos_dp_set_enhanced_mode(dp); | 584 | exynos_dp_set_enhanced_mode(dp); |
630 | dp->link_train.lt_state = FINISHED; | 585 | dp->link_train.lt_state = FINISHED; |
631 | } else { | ||
632 | /* not all locked */ | ||
633 | dp->link_train.eq_loop++; | ||
634 | 586 | ||
635 | if (dp->link_train.eq_loop > MAX_EQ_LOOP) { | 587 | return 0; |
636 | dev_err(dp->dev, "EQ Max loop\n"); | 588 | } |
637 | goto reduce_link_rate; | ||
638 | } | ||
639 | 589 | ||
640 | for (lane = 0; lane < lane_count; lane++) | 590 | /* not all locked */ |
641 | exynos_dp_set_lane_link_training(dp, | 591 | dp->link_train.eq_loop++; |
642 | dp->link_train.training_lane[lane], | ||
643 | lane); | ||
644 | 592 | ||
645 | retval = exynos_dp_write_bytes_to_dpcd(dp, | 593 | if (dp->link_train.eq_loop > MAX_EQ_LOOP) { |
646 | DPCD_ADDR_TRAINING_LANE0_SET, | 594 | dev_err(dp->dev, "EQ Max loop\n"); |
647 | lane_count, | 595 | exynos_dp_reduce_link_rate(dp); |
648 | dp->link_train.training_lane); | 596 | return -EIO; |
649 | if (retval) | ||
650 | return retval; | ||
651 | } | ||
652 | } else { | ||
653 | goto reduce_link_rate; | ||
654 | } | 597 | } |
655 | 598 | ||
656 | return 0; | 599 | for (lane = 0; lane < lane_count; lane++) |
600 | exynos_dp_set_lane_link_training(dp, | ||
601 | dp->link_train.training_lane[lane], lane); | ||
657 | 602 | ||
658 | reduce_link_rate: | 603 | retval = exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_TRAINING_LANE0_SET, |
659 | exynos_dp_reduce_link_rate(dp); | 604 | lane_count, dp->link_train.training_lane); |
660 | return -EIO; | 605 | |
606 | return retval; | ||
661 | } | 607 | } |
662 | 608 | ||
663 | static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp, | 609 | static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp, |