diff options
author | Roy Spliet <r.spliet@student.tudelft.nl> | 2012-01-09 00:23:07 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-03-13 03:07:50 -0400 |
commit | c7c039fd31be82ecb8d48477955e76badd38141a (patch) | |
tree | 0b527eba7fd021ce99c52d9f744d503d0919849c /drivers/gpu/drm/nouveau/nouveau_mem.c | |
parent | 03ddf04bdb9cc4cdf8edb231b78f031647498314 (diff) |
drm/nouveau/pm: implement DDR2/DDR3/GDDR3/GDDR5 MR generation and validation
Roy Spliet:
- Implement according to specs
- Simplify
- Make array for mc latency registers
Martin Peres:
- squash and split all the commits from Roy
- rework following Ben Skeggs comments
- add a form of timings validation
- store the initial timings for later use
Ben Skeggs
- merge slightly modified tidy-up patch with this one
- remove perflvl-dropping logic for the moment
Signed-off-by: Roy Spliet <r.spliet@student.tudelft.nl>
Signed-off-by: Martin Peres <martin.peres@labri.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_mem.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_mem.c | 588 |
1 files changed, 455 insertions, 133 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 3337f2cfb5f6..1cd29c02a7e9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c | |||
@@ -471,50 +471,41 @@ nouveau_mem_gart_init(struct drm_device *dev) | |||
471 | return 0; | 471 | return 0; |
472 | } | 472 | } |
473 | 473 | ||
474 | /* XXX: For now a dummy. More samples required, possibly even a card | 474 | static void |
475 | * Called from nouveau_perf.c */ | 475 | nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, |
476 | void nv30_mem_timing_entry(struct drm_device *dev, | 476 | struct nouveau_pm_tbl_entry *e, |
477 | struct nouveau_pm_tbl_header *hdr, | 477 | struct nouveau_pm_memtiming *t, |
478 | struct nouveau_pm_tbl_entry *e, uint8_t magic_number, | 478 | struct nouveau_pm_memtiming *boot) |
479 | struct nouveau_pm_memtiming *timing) | ||
480 | { | ||
481 | |||
482 | NV_DEBUG(dev, "Timing entry format unknown, " | ||
483 | "please contact nouveau developers"); | ||
484 | } | ||
485 | |||
486 | void nv40_mem_timing_entry(struct drm_device *dev, | ||
487 | struct nouveau_pm_tbl_header *hdr, | ||
488 | struct nouveau_pm_tbl_entry *e, | ||
489 | struct nouveau_pm_memtiming *timing) | ||
490 | { | 479 | { |
491 | 480 | ||
492 | timing->reg_0 = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); | 481 | t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); |
493 | 482 | ||
494 | /* XXX: I don't trust the -1's and +1's... they must come | 483 | /* XXX: I don't trust the -1's and +1's... they must come |
495 | * from somewhere! */ | 484 | * from somewhere! */ |
496 | timing->reg_1 = (e->tWR + 2 + (timing->tCWL - 1)) << 24 | | 485 | t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 | |
497 | 1 << 16 | | 486 | 1 << 16 | |
498 | (e->tWTR + 2 + (timing->tCWL - 1)) << 8 | | 487 | (e->tWTR + 2 + (t->tCWL - 1)) << 8 | |
499 | (e->tCL + 2 - (timing->tCWL - 1)); | 488 | (e->tCL + 2 - (t->tCWL - 1)); |
500 | 489 | ||
501 | timing->reg_2 = 0x20200000 | ((timing->tCWL - 1) << 24 | | 490 | t->reg[2] = 0x20200000 | |
502 | e->tRRD << 16 | e->tRCDWR << 8 | e->tRCDRD); | 491 | ((t->tCWL - 1) << 24 | |
503 | 492 | e->tRRD << 16 | | |
504 | NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", timing->id, | 493 | e->tRCDWR << 8 | |
505 | timing->reg_0, timing->reg_1, timing->reg_2); | 494 | e->tRCDRD); |
495 | |||
496 | NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", t->id, | ||
497 | t->reg[0], t->reg[1], t->reg[2]); | ||
506 | } | 498 | } |
507 | 499 | ||
508 | void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, | 500 | static void |
509 | struct nouveau_pm_tbl_header *hdr, | 501 | nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, |
510 | struct nouveau_pm_tbl_entry *e, | 502 | struct nouveau_pm_tbl_header *hdr, |
511 | struct nouveau_pm_memtiming *timing) | 503 | struct nouveau_pm_tbl_entry *e, |
504 | struct nouveau_pm_memtiming *t, | ||
505 | struct nouveau_pm_memtiming *boot) | ||
512 | { | 506 | { |
513 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 507 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
514 | uint8_t unk18 = 1, | 508 | uint8_t unk18 = 1, unk20 = 0, unk21 = 0, tmp7_3; |
515 | unk20 = 0, | ||
516 | unk21 = 0, | ||
517 | tmp7_3; | ||
518 | 509 | ||
519 | switch (min(hdr->entry_len, (u8) 22)) { | 510 | switch (min(hdr->entry_len, (u8) 22)) { |
520 | case 22: | 511 | case 22: |
@@ -523,132 +514,414 @@ void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, | |||
523 | unk20 = e->tUNK_20; | 514 | unk20 = e->tUNK_20; |
524 | case 20: | 515 | case 20: |
525 | if (e->tCWL > 0) | 516 | if (e->tCWL > 0) |
526 | timing->tCWL = e->tCWL; | 517 | t->tCWL = e->tCWL; |
527 | case 19: | 518 | case 19: |
528 | unk18 = e->tUNK_18; | 519 | unk18 = e->tUNK_18; |
529 | break; | 520 | break; |
530 | } | 521 | } |
531 | 522 | ||
532 | timing->reg_0 = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); | 523 | t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); |
533 | 524 | ||
534 | timing->reg_1 = (e->tWR + 2 + (timing->tCWL - 1)) << 24 | | 525 | t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 | |
535 | max(unk18, (u8) 1) << 16 | | 526 | max(unk18, (u8) 1) << 16 | |
536 | (e->tWTR + 2 + (timing->tCWL - 1)) << 8; | 527 | (e->tWTR + 2 + (t->tCWL - 1)) << 8; |
537 | 528 | ||
538 | timing->reg_2 = ((timing->tCWL - 1) << 24 | e->tRRD << 16 | | 529 | t->reg[2] = ((t->tCWL - 1) << 24 | |
539 | e->tRCDWR << 8 | e->tRCDRD); | 530 | e->tRRD << 16 | |
531 | e->tRCDWR << 8 | | ||
532 | e->tRCDRD); | ||
540 | 533 | ||
541 | timing->reg_4 = e->tUNK_13 << 8 | e->tUNK_13; | 534 | t->reg[4] = e->tUNK_13 << 8 | e->tUNK_13; |
542 | 535 | ||
543 | timing->reg_5 = (e->tRFC << 24 | max(e->tRCDRD, e->tRCDWR) << 16 | | 536 | t->reg[5] = (e->tRFC << 24 | max(e->tRCDRD, e->tRCDWR) << 16 | e->tRP); |
544 | e->tRP); | ||
545 | 537 | ||
546 | timing->reg_8 = (nv_rd32(dev, 0x100240) & 0xffffff00); | 538 | t->reg[8] = boot->reg[8] & 0xffffff00; |
547 | 539 | ||
548 | if (P->version == 1) { | 540 | if (P->version == 1) { |
549 | timing->reg_1 |= (e->tCL + 2 - (timing->tCWL - 1)); | 541 | t->reg[1] |= (e->tCL + 2 - (t->tCWL - 1)); |
550 | 542 | ||
551 | timing->reg_3 = (0x14 + e->tCL) << 24 | | 543 | t->reg[3] = (0x14 + e->tCL) << 24 | |
552 | 0x16 << 16 | | 544 | 0x16 << 16 | |
553 | (e->tCL - 1) << 8 | | 545 | (e->tCL - 1) << 8 | |
554 | (e->tCL - 1); | 546 | (e->tCL - 1); |
555 | 547 | ||
556 | timing->reg_4 |= (nv_rd32(dev, 0x100230) & 0xffff0000); | 548 | t->reg[4] |= boot->reg[4] & 0xffff0000; |
557 | 549 | ||
558 | timing->reg_6 = (0x33 - timing->tCWL) << 16 | | 550 | t->reg[6] = (0x33 - t->tCWL) << 16 | |
559 | timing->tCWL << 8 | | 551 | t->tCWL << 8 | |
560 | (0x2E + e->tCL - timing->tCWL); | 552 | (0x2e + e->tCL - t->tCWL); |
561 | 553 | ||
562 | timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16; | 554 | t->reg[7] = 0x4000202 | (e->tCL - 1) << 16; |
563 | 555 | ||
564 | /* XXX: P.version == 1 only has DDR2 and GDDR3? */ | 556 | /* XXX: P.version == 1 only has DDR2 and GDDR3? */ |
565 | if (dev_priv->vram_type == NV_MEM_TYPE_DDR2) { | 557 | if (dev_priv->vram_type == NV_MEM_TYPE_DDR2) { |
566 | timing->reg_5 |= (e->tCL + 3) << 8; | 558 | t->reg[5] |= (e->tCL + 3) << 8; |
567 | timing->reg_6 |= (timing->tCWL - 2) << 8; | 559 | t->reg[6] |= (t->tCWL - 2) << 8; |
568 | timing->reg_8 |= (e->tCL - 4); | 560 | t->reg[8] |= (e->tCL - 4); |
569 | } else { | 561 | } else { |
570 | timing->reg_5 |= (e->tCL + 2) << 8; | 562 | t->reg[5] |= (e->tCL + 2) << 8; |
571 | timing->reg_6 |= timing->tCWL << 8; | 563 | t->reg[6] |= t->tCWL << 8; |
572 | timing->reg_8 |= (e->tCL - 2); | 564 | t->reg[8] |= (e->tCL - 2); |
573 | } | 565 | } |
574 | } else { | 566 | } else { |
575 | timing->reg_1 |= (5 + e->tCL - (timing->tCWL)); | 567 | t->reg[1] |= (5 + e->tCL - (t->tCWL)); |
576 | 568 | ||
577 | /* XXX: 0xb? 0x30? */ | 569 | /* XXX: 0xb? 0x30? */ |
578 | timing->reg_3 = (0x30 + e->tCL) << 24 | | 570 | t->reg[3] = (0x30 + e->tCL) << 24 | |
579 | (nv_rd32(dev, 0x10022c) & 0x00ff0000) | | 571 | (boot->reg[3] & 0x00ff0000)| |
580 | (0xB + e->tCL) << 8 | | 572 | (0xb + e->tCL) << 8 | |
581 | (e->tCL - 1); | 573 | (e->tCL - 1); |
582 | 574 | ||
583 | timing->reg_4 |= (unk20 << 24 | unk21 << 16); | 575 | t->reg[4] |= (unk20 << 24 | unk21 << 16); |
584 | 576 | ||
585 | /* XXX: +6? */ | 577 | /* XXX: +6? */ |
586 | timing->reg_5 |= (timing->tCWL + 6) << 8; | 578 | t->reg[5] |= (t->tCWL + 6) << 8; |
587 | 579 | ||
588 | timing->reg_6 = (0x5A + e->tCL) << 16 | | 580 | t->reg[6] = (0x5a + e->tCL) << 16 | |
589 | (6 - e->tCL + timing->tCWL) << 8 | | 581 | (6 - e->tCL + t->tCWL) << 8 | |
590 | (0x50 + e->tCL - timing->tCWL); | 582 | (0x50 + e->tCL - t->tCWL); |
591 | 583 | ||
592 | tmp7_3 = (nv_rd32(dev, 0x10023c) & 0xff000000) >> 24; | 584 | tmp7_3 = (boot->reg[7] & 0xff000000) >> 24; |
593 | timing->reg_7 = (tmp7_3 << 24) | | 585 | t->reg[7] = (tmp7_3 << 24) | |
594 | ((tmp7_3 - 6 + e->tCL) << 16) | | 586 | ((tmp7_3 - 6 + e->tCL) << 16) | |
595 | 0x202; | 587 | 0x202; |
596 | } | 588 | } |
597 | 589 | ||
598 | NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", timing->id, | 590 | NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", t->id, |
599 | timing->reg_0, timing->reg_1, | 591 | t->reg[0], t->reg[1], t->reg[2], t->reg[3]); |
600 | timing->reg_2, timing->reg_3); | ||
601 | NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", | 592 | NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", |
602 | timing->reg_4, timing->reg_5, | 593 | t->reg[4], t->reg[5], t->reg[6], t->reg[7]); |
603 | timing->reg_6, timing->reg_7); | 594 | NV_DEBUG(dev, " 240: %08x\n", t->reg[8]); |
604 | NV_DEBUG(dev, " 240: %08x\n", timing->reg_8); | ||
605 | } | 595 | } |
606 | 596 | ||
607 | void nvc0_mem_timing_entry(struct drm_device *dev, | 597 | static void |
608 | struct nouveau_pm_tbl_header *hdr, | 598 | nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, |
609 | struct nouveau_pm_tbl_entry *e, | 599 | struct nouveau_pm_tbl_entry *e, |
610 | struct nouveau_pm_memtiming *timing) | 600 | struct nouveau_pm_memtiming *t, |
601 | struct nouveau_pm_memtiming *boot) | ||
611 | { | 602 | { |
612 | timing->tCWL = e->tCWL; | 603 | if (e->tCWL > 0) |
604 | t->tCWL = e->tCWL; | ||
613 | 605 | ||
614 | timing->reg_0 = (e->tRP << 24 | (e->tRAS & 0x7f) << 17 | | 606 | t->reg[0] = (e->tRP << 24 | (e->tRAS & 0x7f) << 17 | |
615 | e->tRFC << 8 | e->tRC); | 607 | e->tRFC << 8 | e->tRC); |
616 | 608 | ||
617 | timing->reg_1 = (nv_rd32(dev, 0x10f294) & 0xff000000) | | 609 | t->reg[1] = (boot->reg[1] & 0xff000000) | |
618 | (e->tRCDWR & 0x0f) << 20 | | 610 | (e->tRCDWR & 0x0f) << 20 | |
619 | (e->tRCDRD & 0x0f) << 14 | | 611 | (e->tRCDRD & 0x0f) << 14 | |
620 | (e->tCWL << 7) | | 612 | (e->tCWL << 7) | |
621 | (e->tCL & 0x0f); | 613 | (e->tCL & 0x0f); |
622 | 614 | ||
623 | timing->reg_2 = (nv_rd32(dev, 0x10f298) & 0xff0000ff) | | 615 | t->reg[2] = (boot->reg[2] & 0xff0000ff) | |
624 | e->tWR << 16 | e->tWTR << 8; | 616 | e->tWR << 16 | e->tWTR << 8; |
625 | 617 | ||
626 | timing->reg_3 = (e->tUNK_20&0xf) << 9 | | 618 | t->reg[3] = (e->tUNK_20 & 0xf) << 9 | |
627 | (e->tUNK_21 & 0xf) << 5 | | 619 | (e->tUNK_21 & 0xf) << 5 | |
628 | (e->tUNK_13 & 0x1f); | 620 | (e->tUNK_13 & 0x1f); |
629 | 621 | ||
630 | timing->reg_4 = (nv_rd32(dev, 0x10f2a0) & 0xfff00fff) | | 622 | t->reg[4] = (boot->reg[4] & 0xfff00fff) | |
631 | (e->tRRD&0x1f) << 15; | 623 | (e->tRRD&0x1f) << 15; |
632 | 624 | ||
633 | NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id, | 625 | NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", t->id, |
634 | timing->reg_0, timing->reg_1, | 626 | t->reg[0], t->reg[1], t->reg[2], t->reg[3]); |
635 | timing->reg_2, timing->reg_3); | 627 | NV_DEBUG(dev, " 2a0: %08x\n", t->reg[4]); |
636 | NV_DEBUG(dev, " 2a0: %08x\n", | ||
637 | timing->reg_4); | ||
638 | } | 628 | } |
639 | 629 | ||
640 | void | 630 | /** |
641 | nouveau_mem_features_entry(uint8_t p_version, struct nouveau_pm_tbl_header *hdr, | 631 | * MR generation methods |
642 | struct nouveau_pm_tbl_entry *e, | 632 | */ |
643 | struct nouveau_pm_memtiming *timing) | 633 | |
634 | static bool | ||
635 | nouveau_mem_ddr2_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, | ||
636 | struct nouveau_pm_tbl_entry *e, | ||
637 | struct nouveau_pm_memtiming *t, | ||
638 | struct nouveau_pm_memtiming *boot) | ||
639 | { | ||
640 | t->drive_strength = 0; | ||
641 | if (hdr->entry_len < 15) { | ||
642 | t->odt = boot->odt; | ||
643 | } else { | ||
644 | t->odt = e->RAM_FT1 & 0x07; | ||
645 | } | ||
646 | |||
647 | if (e->tCL >= NV_MEM_CL_DDR2_MAX) { | ||
648 | NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); | ||
649 | return false; | ||
650 | } | ||
651 | |||
652 | if (e->tWR >= NV_MEM_WR_DDR2_MAX) { | ||
653 | NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); | ||
654 | return false; | ||
655 | } | ||
656 | |||
657 | if (t->odt > 3) { | ||
658 | NV_WARN(dev, "(%u) Invalid odt value, assuming disabled: %x", | ||
659 | t->id, t->odt); | ||
660 | t->odt = 0; | ||
661 | } | ||
662 | |||
663 | t->mr[0] = (boot->mr[0] & 0x100f) | | ||
664 | (e->tCL) << 4 | | ||
665 | (e->tWR - 1) << 9; | ||
666 | t->mr[1] = (boot->mr[1] & 0x101fbb) | | ||
667 | (t->odt & 0x1) << 2 | | ||
668 | (t->odt & 0x2) << 5; | ||
669 | |||
670 | NV_DEBUG(dev, "(%u) MR: %08x", t->id, t->mr[0]); | ||
671 | return true; | ||
672 | } | ||
673 | |||
674 | uint8_t nv_mem_wr_lut_ddr3[NV_MEM_WR_DDR3_MAX] = { | ||
675 | 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0}; | ||
676 | |||
677 | static bool | ||
678 | nouveau_mem_ddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, | ||
679 | struct nouveau_pm_tbl_entry *e, | ||
680 | struct nouveau_pm_memtiming *t, | ||
681 | struct nouveau_pm_memtiming *boot) | ||
682 | { | ||
683 | u8 cl = e->tCL - 4; | ||
684 | |||
685 | t->drive_strength = 0; | ||
686 | if (hdr->entry_len < 15) { | ||
687 | t->odt = boot->odt; | ||
688 | } else { | ||
689 | t->odt = e->RAM_FT1 & 0x07; | ||
690 | } | ||
691 | |||
692 | if (e->tCL >= NV_MEM_CL_DDR3_MAX || e->tCL < 4) { | ||
693 | NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); | ||
694 | return false; | ||
695 | } | ||
696 | |||
697 | if (e->tWR >= NV_MEM_WR_DDR3_MAX || e->tWR < 4) { | ||
698 | NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); | ||
699 | return false; | ||
700 | } | ||
701 | |||
702 | if (e->tCWL < 5) { | ||
703 | NV_WARN(dev, "(%u) Invalid tCWL: %u", t->id, e->tCWL); | ||
704 | return false; | ||
705 | } | ||
706 | |||
707 | t->mr[0] = (boot->mr[0] & 0x180b) | | ||
708 | /* CAS */ | ||
709 | (cl & 0x7) << 4 | | ||
710 | (cl & 0x8) >> 1 | | ||
711 | (nv_mem_wr_lut_ddr3[e->tWR]) << 9; | ||
712 | t->mr[1] = (boot->mr[1] & 0x101dbb) | | ||
713 | (t->odt & 0x1) << 2 | | ||
714 | (t->odt & 0x2) << 5 | | ||
715 | (t->odt & 0x4) << 7; | ||
716 | t->mr[2] = (boot->mr[2] & 0x20ffb7) | (e->tCWL - 5) << 3; | ||
717 | |||
718 | NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[2]); | ||
719 | return true; | ||
720 | } | ||
721 | |||
722 | uint8_t nv_mem_cl_lut_gddr3[NV_MEM_CL_GDDR3_MAX] = { | ||
723 | 0, 0, 0, 0, 4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11}; | ||
724 | uint8_t nv_mem_wr_lut_gddr3[NV_MEM_WR_GDDR3_MAX] = { | ||
725 | 0, 0, 0, 0, 0, 2, 3, 8, 9, 10, 11, 0, 0, 1, 1, 0, 3}; | ||
726 | |||
727 | static bool | ||
728 | nouveau_mem_gddr3_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, | ||
729 | struct nouveau_pm_tbl_entry *e, | ||
730 | struct nouveau_pm_memtiming *t, | ||
731 | struct nouveau_pm_memtiming *boot) | ||
644 | { | 732 | { |
645 | if (p_version == 1) { | 733 | if (hdr->entry_len < 15) { |
646 | /* XXX: Todo */ | 734 | t->drive_strength = boot->drive_strength; |
647 | } else if (p_version == 2) { | 735 | t->odt = boot->odt; |
648 | timing->odt = e->RAM_FT1 & 0x1; | 736 | } else { |
649 | timing->dll_disable = (e->RAM_FT1 & 0x2) >> 1; | 737 | t->drive_strength = (e->RAM_FT1 & 0x30) >> 4; |
650 | timing->ron_pull = (e->RAM_FT1 & 0x4) >> 2; | 738 | t->odt = e->RAM_FT1 & 0x07; |
651 | } | 739 | } |
740 | |||
741 | if (e->tCL >= NV_MEM_CL_GDDR3_MAX) { | ||
742 | NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); | ||
743 | return false; | ||
744 | } | ||
745 | |||
746 | if (e->tWR >= NV_MEM_WR_GDDR3_MAX) { | ||
747 | NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); | ||
748 | return false; | ||
749 | } | ||
750 | |||
751 | if (t->odt > 3) { | ||
752 | NV_WARN(dev, "(%u) Invalid odt value, assuming autocal: %x", | ||
753 | t->id, t->odt); | ||
754 | t->odt = 0; | ||
755 | } | ||
756 | |||
757 | t->mr[0] = (boot->mr[0] & 0xe0b) | | ||
758 | /* CAS */ | ||
759 | ((nv_mem_cl_lut_gddr3[e->tCL] & 0x7) << 4) | | ||
760 | ((nv_mem_cl_lut_gddr3[e->tCL] & 0x8) >> 2); | ||
761 | t->mr[1] = (boot->mr[1] & 0x100f40) | t->drive_strength | | ||
762 | (t->odt << 2) | | ||
763 | (nv_mem_wr_lut_gddr3[e->tWR] & 0xf) << 4; | ||
764 | |||
765 | NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]); | ||
766 | return true; | ||
767 | } | ||
768 | |||
769 | static bool | ||
770 | nouveau_mem_gddr5_mr(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr, | ||
771 | struct nouveau_pm_tbl_entry *e, | ||
772 | struct nouveau_pm_memtiming *t, | ||
773 | struct nouveau_pm_memtiming *boot) | ||
774 | { | ||
775 | if (hdr->entry_len < 15) { | ||
776 | t->drive_strength = boot->drive_strength; | ||
777 | t->odt = boot->odt; | ||
778 | } else { | ||
779 | t->drive_strength = (e->RAM_FT1 & 0x30) >> 4; | ||
780 | t->odt = e->RAM_FT1 & 0x03; | ||
781 | } | ||
782 | |||
783 | if (e->tCL >= NV_MEM_CL_GDDR5_MAX) { | ||
784 | NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); | ||
785 | return false; | ||
786 | } | ||
787 | |||
788 | if (e->tWR >= NV_MEM_WR_GDDR5_MAX) { | ||
789 | NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); | ||
790 | return false; | ||
791 | } | ||
792 | |||
793 | if (t->odt > 3) { | ||
794 | NV_WARN(dev, "(%u) Invalid odt value, assuming autocal: %x", | ||
795 | t->id, t->odt); | ||
796 | t->odt = 0; | ||
797 | } | ||
798 | |||
799 | t->mr[0] = (boot->mr[0] & 0x007) | | ||
800 | ((e->tCL - 5) << 3) | | ||
801 | ((e->tWR - 4) << 8); | ||
802 | t->mr[1] = (boot->mr[1] & 0x1007f0) | | ||
803 | t->drive_strength | | ||
804 | (t->odt << 2); | ||
805 | |||
806 | NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]); | ||
807 | return true; | ||
808 | } | ||
809 | |||
810 | static void | ||
811 | nouveau_mem_copy_current_timings(struct drm_device *dev, | ||
812 | struct nouveau_pm_memtiming *t) | ||
813 | { | ||
814 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
815 | u32 timing_base, timing_regs, mr_base; | ||
816 | int i; | ||
817 | |||
818 | if (dev_priv->card_type >= 0xC0) { | ||
819 | timing_base = 0x10f290; | ||
820 | mr_base = 0x10f300; | ||
821 | } else { | ||
822 | timing_base = 0x100220; | ||
823 | mr_base = 0x1002c0; | ||
824 | } | ||
825 | |||
826 | t->id = -1; | ||
827 | |||
828 | switch (dev_priv->card_type) { | ||
829 | case NV_50: | ||
830 | timing_regs = 9; | ||
831 | break; | ||
832 | case NV_C0: | ||
833 | case NV_D0: | ||
834 | timing_regs = 5; | ||
835 | break; | ||
836 | case NV_30: | ||
837 | case NV_40: | ||
838 | timing_regs = 3; | ||
839 | break; | ||
840 | default: | ||
841 | timing_regs = 0; | ||
842 | return; | ||
843 | } | ||
844 | for(i = 0; i < timing_regs; i++) | ||
845 | t->reg[i] = nv_rd32(dev, timing_base + (0x04 * i)); | ||
846 | |||
847 | t->tCWL = 0; | ||
848 | if (dev_priv->card_type < NV_C0) { | ||
849 | t->tCWL = ((nv_rd32(dev, 0x100228) & 0x0f000000) >> 24) + 1; | ||
850 | } | ||
851 | |||
852 | t->mr[0] = nv_rd32(dev, mr_base); | ||
853 | t->mr[1] = nv_rd32(dev, mr_base + 0x04); | ||
854 | t->mr[2] = nv_rd32(dev, mr_base + 0x20); | ||
855 | t->mr[3] = nv_rd32(dev, mr_base + 0x24); | ||
856 | |||
857 | t->odt = 0; | ||
858 | t->drive_strength = 0; | ||
859 | |||
860 | switch (dev_priv->vram_type) { | ||
861 | case NV_MEM_TYPE_DDR3: | ||
862 | t->odt |= (t->mr[1] & 0x200) >> 7; | ||
863 | case NV_MEM_TYPE_DDR2: | ||
864 | t->odt |= (t->mr[1] & 0x04) >> 2 | | ||
865 | (t->mr[1] & 0x40) >> 5; | ||
866 | break; | ||
867 | case NV_MEM_TYPE_GDDR3: | ||
868 | case NV_MEM_TYPE_GDDR5: | ||
869 | t->drive_strength = t->mr[1] & 0x03; | ||
870 | t->odt = (t->mr[1] & 0x0c) >> 2; | ||
871 | break; | ||
872 | default: | ||
873 | break; | ||
874 | } | ||
875 | } | ||
876 | |||
877 | static bool | ||
878 | nouveau_mem_compare_timings(struct drm_device *dev, | ||
879 | struct nouveau_pm_memtiming *t1, | ||
880 | struct nouveau_pm_memtiming *t2) | ||
881 | { | ||
882 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
883 | |||
884 | switch (dev_priv->card_type) { | ||
885 | case 0x50: | ||
886 | if (t1->reg[8] != t2->reg[8] || | ||
887 | t1->reg[7] != t2->reg[7] || | ||
888 | t1->reg[6] != t2->reg[6] || | ||
889 | t1->reg[5] != t2->reg[5]) | ||
890 | return false; | ||
891 | case 0xC0: | ||
892 | if (t1->reg[4] != t2->reg[4] || | ||
893 | t1->reg[3] != t2->reg[3]) | ||
894 | return false; | ||
895 | case 0x40: | ||
896 | if (t1->reg[2] != t2->reg[2] || | ||
897 | t1->reg[1] != t2->reg[1] || | ||
898 | t1->reg[0] != t2->reg[0]) | ||
899 | return false; | ||
900 | break; | ||
901 | default: | ||
902 | return false; | ||
903 | } | ||
904 | |||
905 | /* RSpliet: may generate many false negatives */ | ||
906 | switch (dev_priv->vram_type) { | ||
907 | case NV_MEM_TYPE_GDDR3: | ||
908 | case NV_MEM_TYPE_GDDR5: | ||
909 | if (t1->mr[0] == t2->mr[0] || | ||
910 | t1->mr[1] != t2->mr[1]) | ||
911 | return true; | ||
912 | break; | ||
913 | case NV_MEM_TYPE_DDR3: | ||
914 | if (t1->mr[2] == t2->mr[2]) | ||
915 | return true; | ||
916 | case NV_MEM_TYPE_DDR2: | ||
917 | if (t1->mr[0] == t2->mr[0]) | ||
918 | return true; | ||
919 | break; | ||
920 | default: | ||
921 | return false; | ||
922 | } | ||
923 | |||
924 | return false; | ||
652 | } | 925 | } |
653 | 926 | ||
654 | /** | 927 | /** |
@@ -665,10 +938,24 @@ nouveau_mem_timing_init(struct drm_device *dev) | |||
665 | struct nvbios *bios = &dev_priv->vbios; | 938 | struct nvbios *bios = &dev_priv->vbios; |
666 | struct bit_entry P; | 939 | struct bit_entry P; |
667 | struct nouveau_pm_tbl_header *hdr = NULL; | 940 | struct nouveau_pm_tbl_header *hdr = NULL; |
668 | uint8_t tCWL; | 941 | bool valid_generation = false; |
669 | u8 *entry; | 942 | u8 *entry; |
670 | int i; | 943 | int i; |
671 | 944 | ||
945 | memtimings->nr_timing = 0; | ||
946 | memtimings->nr_timing_valid = 0; | ||
947 | memtimings->supported = 0; | ||
948 | |||
949 | if (dev_priv->card_type < NV_40) { | ||
950 | NV_ERROR(dev, "Timing entry format unknown for card_type %x. " | ||
951 | "please contact nouveau developers", | ||
952 | dev_priv->card_type); | ||
953 | return; | ||
954 | } | ||
955 | |||
956 | /* Copy the current timings */ | ||
957 | nouveau_mem_copy_current_timings(dev, &memtimings->boot); | ||
958 | |||
672 | if (bios->type == NVBIOS_BIT) { | 959 | if (bios->type == NVBIOS_BIT) { |
673 | if (bit_table(dev, 'P', &P)) | 960 | if (bit_table(dev, 'P', &P)) |
674 | return; | 961 | return; |
@@ -710,12 +997,6 @@ nouveau_mem_timing_init(struct drm_device *dev) | |||
710 | if (!memtimings->timing) | 997 | if (!memtimings->timing) |
711 | return; | 998 | return; |
712 | 999 | ||
713 | /* Get tCWL from the timing reg for NV_40 and NV_50 | ||
714 | * Used in calculations later... source unknown */ | ||
715 | tCWL = 0; | ||
716 | if (dev_priv->card_type < NV_C0) | ||
717 | tCWL = ((nv_rd32(dev, 0x100228) & 0x0f000000) >> 24) + 1; | ||
718 | |||
719 | entry = (u8 *) hdr + hdr->header_len; | 1000 | entry = (u8 *) hdr + hdr->header_len; |
720 | for (i = 0; i < hdr->entry_cnt; i++, entry += hdr->entry_len) { | 1001 | for (i = 0; i < hdr->entry_cnt; i++, entry += hdr->entry_len) { |
721 | struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i]; | 1002 | struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i]; |
@@ -723,29 +1004,70 @@ nouveau_mem_timing_init(struct drm_device *dev) | |||
723 | (struct nouveau_pm_tbl_entry *) entry; | 1004 | (struct nouveau_pm_tbl_entry *) entry; |
724 | if (entry[0] == 0) | 1005 | if (entry[0] == 0) |
725 | continue; | 1006 | continue; |
1007 | memtimings->nr_timing_valid++; | ||
726 | 1008 | ||
727 | timing->id = i; | 1009 | timing->id = i; |
728 | timing->WR = entry[0]; | 1010 | timing->tCWL = memtimings->boot.tCWL; |
729 | timing->CL = entry[2]; | ||
730 | timing->tCWL = tCWL; | ||
731 | |||
732 | nouveau_mem_features_entry(P.version, hdr, entry_struct, | ||
733 | &pm->memtimings.timing[i]); | ||
734 | 1011 | ||
735 | if (dev_priv->card_type <= NV_40) { | 1012 | /* generate the timngs */ |
1013 | if (dev_priv->card_type == NV_40) { | ||
736 | nv40_mem_timing_entry(dev, hdr, entry_struct, | 1014 | nv40_mem_timing_entry(dev, hdr, entry_struct, |
737 | &pm->memtimings.timing[i]); | 1015 | &pm->memtimings.timing[i], |
1016 | &memtimings->boot); | ||
738 | } else if (dev_priv->card_type == NV_50) { | 1017 | } else if (dev_priv->card_type == NV_50) { |
739 | nv50_mem_timing_entry(dev, &P, hdr, entry_struct, | 1018 | nv50_mem_timing_entry(dev, &P, hdr, entry_struct, |
740 | &pm->memtimings.timing[i]); | 1019 | &pm->memtimings.timing[i], |
1020 | &memtimings->boot); | ||
741 | } else if (dev_priv->card_type == NV_C0) { | 1021 | } else if (dev_priv->card_type == NV_C0) { |
742 | nvc0_mem_timing_entry(dev, hdr, entry_struct, | 1022 | nvc0_mem_timing_entry(dev, hdr, entry_struct, |
743 | &pm->memtimings.timing[i]); | 1023 | &pm->memtimings.timing[i], |
1024 | &memtimings->boot); | ||
1025 | } | ||
1026 | |||
1027 | /* generate the MR/EMR/... */ | ||
1028 | switch (dev_priv->vram_type) { | ||
1029 | case NV_MEM_TYPE_GDDR3: | ||
1030 | nouveau_mem_gddr3_mr(dev, hdr, entry_struct, timing, | ||
1031 | &memtimings->boot); | ||
1032 | break; | ||
1033 | case NV_MEM_TYPE_GDDR5: | ||
1034 | nouveau_mem_gddr5_mr(dev, hdr, entry_struct, timing, | ||
1035 | &memtimings->boot); | ||
1036 | break; | ||
1037 | case NV_MEM_TYPE_DDR2: | ||
1038 | nouveau_mem_ddr2_mr(dev, hdr, entry_struct, timing, | ||
1039 | &memtimings->boot); | ||
1040 | break; | ||
1041 | case NV_MEM_TYPE_DDR3: | ||
1042 | nouveau_mem_ddr3_mr(dev, hdr, entry_struct, timing, | ||
1043 | &memtimings->boot); | ||
1044 | break; | ||
1045 | default: | ||
1046 | valid_generation = false; | ||
1047 | break; | ||
1048 | } | ||
1049 | |||
1050 | /* some kind of validation */ | ||
1051 | if (nouveau_mem_compare_timings(dev, timing, | ||
1052 | &memtimings->boot)) { | ||
1053 | NV_DEBUG(dev, "Copy boot timings from entry %d\n", | ||
1054 | timing->id); | ||
1055 | memtimings->boot = *timing; | ||
1056 | valid_generation = true; | ||
744 | } | 1057 | } |
745 | } | 1058 | } |
746 | 1059 | ||
747 | memtimings->nr_timing = hdr->entry_cnt; | 1060 | memtimings->nr_timing = hdr->entry_cnt; |
748 | memtimings->supported = (P.version == 1); | 1061 | memtimings->supported = (P.version == 1) && valid_generation; |
1062 | |||
1063 | /* if there are no timing entries that cannot | ||
1064 | * re-generate the current timings | ||
1065 | */ | ||
1066 | if (memtimings->nr_timing_valid > 0 && !valid_generation) { | ||
1067 | NV_INFO(dev, | ||
1068 | "Memory timings management may not be working." | ||
1069 | " please report to nouveau devs\n"); | ||
1070 | } | ||
749 | } | 1071 | } |
750 | 1072 | ||
751 | void | 1073 | void |