diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_pm.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_pm.c | 207 |
1 files changed, 145 insertions, 62 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index e025cae5ef23..22789db48969 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "nouveau_bios.h" | 27 | #include "nouveau_bios.h" |
28 | #include "nouveau_hw.h" | 28 | #include "nouveau_hw.h" |
29 | #include "nouveau_pm.h" | 29 | #include "nouveau_pm.h" |
30 | #include "nouveau_hwsq.h" | ||
30 | 31 | ||
31 | enum clk_src { | 32 | enum clk_src { |
32 | clk_src_crystal, | 33 | clk_src_crystal, |
@@ -351,6 +352,9 @@ nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) | |||
351 | } | 352 | } |
352 | 353 | ||
353 | struct nv50_pm_state { | 354 | struct nv50_pm_state { |
355 | struct hwsq_ucode mclk_hwsq; | ||
356 | u32 mscript; | ||
357 | |||
354 | u32 emast; | 358 | u32 emast; |
355 | u32 nctrl; | 359 | u32 nctrl; |
356 | u32 ncoef; | 360 | u32 ncoef; |
@@ -359,10 +363,6 @@ struct nv50_pm_state { | |||
359 | 363 | ||
360 | u32 amast; | 364 | u32 amast; |
361 | u32 pdivs; | 365 | u32 pdivs; |
362 | |||
363 | u32 mscript; | ||
364 | u32 mctrl; | ||
365 | u32 mcoef; | ||
366 | }; | 366 | }; |
367 | 367 | ||
368 | static u32 | 368 | static u32 |
@@ -415,6 +415,80 @@ clk_same(u32 a, u32 b) | |||
415 | return ((a / 1000) == (b / 1000)); | 415 | return ((a / 1000) == (b / 1000)); |
416 | } | 416 | } |
417 | 417 | ||
418 | static int | ||
419 | calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq) | ||
420 | { | ||
421 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
422 | struct pll_lims pll; | ||
423 | u32 mast = nv_rd32(dev, 0x00c040); | ||
424 | u32 ctrl = nv_rd32(dev, 0x004008); | ||
425 | u32 coef = nv_rd32(dev, 0x00400c); | ||
426 | u32 orig = ctrl; | ||
427 | u32 crtc_mask = 0; | ||
428 | int N, M, P; | ||
429 | int ret, i; | ||
430 | |||
431 | /* use pcie refclock if possible, otherwise use mpll */ | ||
432 | ctrl &= ~0x81ff0200; | ||
433 | if (clk_same(freq, read_clk(dev, clk_src_href))) { | ||
434 | ctrl |= 0x00000200 | (pll.log2p_bias << 19); | ||
435 | } else { | ||
436 | ret = calc_pll(dev, 0x4008, &pll, freq, &N, &M, &P); | ||
437 | if (ret == 0) | ||
438 | return -EINVAL; | ||
439 | |||
440 | ctrl |= 0x80000000 | (P << 22) | (P << 16); | ||
441 | ctrl |= pll.log2p_bias << 19; | ||
442 | coef = (N << 8) | M; | ||
443 | } | ||
444 | |||
445 | mast &= ~0xc0000000; /* get MCLK_2 from HREF */ | ||
446 | mast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */ | ||
447 | |||
448 | /* determine active crtcs */ | ||
449 | for (i = 0; i < 2; i++) { | ||
450 | if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK))) | ||
451 | crtc_mask |= (1 << i); | ||
452 | } | ||
453 | |||
454 | /* build the ucode which will reclock the memory for us */ | ||
455 | hwsq_init(hwsq); | ||
456 | if (crtc_mask) { | ||
457 | hwsq_op5f(hwsq, crtc_mask, 0x00); /* wait for scanout */ | ||
458 | hwsq_op5f(hwsq, crtc_mask, 0x01); /* wait for vblank */ | ||
459 | } | ||
460 | if (dev_priv->chipset >= 0x92) | ||
461 | hwsq_wr32(hwsq, 0x611200, 0x00003300); /* disable scanout */ | ||
462 | hwsq_unkn(hwsq, 0xb0); /* disable bus access */ | ||
463 | hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */ | ||
464 | |||
465 | /* prepare memory controller */ | ||
466 | hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ | ||
467 | hwsq_wr32(hwsq, 0x1002d0, 0x00000001); /* force refresh */ | ||
468 | hwsq_wr32(hwsq, 0x100210, 0x00000000); /* stop the automatic refresh */ | ||
469 | hwsq_wr32(hwsq, 0x1002dc, 0x00000001); /* start self refresh mode */ | ||
470 | |||
471 | /* reclock memory */ | ||
472 | hwsq_wr32(hwsq, 0xc040, mast); | ||
473 | hwsq_wr32(hwsq, 0x4008, orig | 0x00000200); /* bypass MPLL */ | ||
474 | hwsq_wr32(hwsq, 0x400c, coef); | ||
475 | hwsq_wr32(hwsq, 0x4008, ctrl); | ||
476 | |||
477 | /* restart memory controller */ | ||
478 | hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */ | ||
479 | hwsq_wr32(hwsq, 0x1002dc, 0x00000000); /* stop self refresh mode */ | ||
480 | hwsq_wr32(hwsq, 0x100210, 0x80000000); /* restart automatic refresh */ | ||
481 | hwsq_unkn(hwsq, 0x07); /* wait for the PLL to stabilize (12us) */ | ||
482 | |||
483 | hwsq_unkn(hwsq, 0x0b); /* may be unnecessary: causes flickering */ | ||
484 | hwsq_unkn(hwsq, 0xd0); /* enable bus access again */ | ||
485 | hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */ | ||
486 | if (dev_priv->chipset >= 0x92) | ||
487 | hwsq_wr32(hwsq, 0x611200, 0x00003330); /* enable scanout */ | ||
488 | hwsq_fini(hwsq); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
418 | void * | 492 | void * |
419 | nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) | 493 | nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) |
420 | { | 494 | { |
@@ -462,23 +536,16 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) | |||
462 | info->scoef = (N << 8) | M; | 536 | info->scoef = (N << 8) | M; |
463 | } | 537 | } |
464 | 538 | ||
465 | /* memory: use pcie refclock if possible, otherwise use mpll */ | 539 | /* memory: build hwsq ucode which we'll use to reclock memory */ |
466 | info->mscript = perflvl->memscript; | 540 | info->mclk_hwsq.len = 0; |
467 | if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) { | ||
468 | info->mctrl = 0x00000200 | (pll.log2p_bias << 19); | ||
469 | info->mcoef = nv_rd32(dev, 0x400c); | ||
470 | } else | ||
471 | if (perflvl->memory) { | 541 | if (perflvl->memory) { |
472 | clk = calc_pll(dev, 0x4008, &pll, perflvl->memory, | 542 | clk = calc_mclk(dev, perflvl->memory, &info->mclk_hwsq); |
473 | &N, &M, &P1); | 543 | if (clk < 0) { |
474 | if (clk == 0) | 544 | ret = clk; |
475 | goto error; | 545 | goto error; |
546 | } | ||
476 | 547 | ||
477 | info->mctrl = 0x80000000 | (P1 << 22) | (P1 << 16); | 548 | info->mscript = perflvl->memscript; |
478 | info->mctrl |= pll.log2p_bias << 19; | ||
479 | info->mcoef = (N << 8) | M; | ||
480 | } else { | ||
481 | info->mctrl = 0x00000000; | ||
482 | } | 549 | } |
483 | 550 | ||
484 | /* vdec: avoid modifying xpll until we know exactly how the other | 551 | /* vdec: avoid modifying xpll until we know exactly how the other |
@@ -537,6 +604,44 @@ error: | |||
537 | return ERR_PTR(ret); | 604 | return ERR_PTR(ret); |
538 | } | 605 | } |
539 | 606 | ||
607 | static int | ||
608 | prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq) | ||
609 | { | ||
610 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
611 | u32 hwsq_data, hwsq_kick; | ||
612 | int i; | ||
613 | |||
614 | if (dev_priv->chipset < 0x90) { | ||
615 | hwsq_data = 0x001400; | ||
616 | hwsq_kick = 0x00000003; | ||
617 | } else { | ||
618 | hwsq_data = 0x080000; | ||
619 | hwsq_kick = 0x00000001; | ||
620 | } | ||
621 | |||
622 | /* upload hwsq ucode */ | ||
623 | nv_mask(dev, 0x001098, 0x00000008, 0x00000000); | ||
624 | nv_wr32(dev, 0x001304, 0x00000000); | ||
625 | for (i = 0; i < hwsq->len / 4; i++) | ||
626 | nv_wr32(dev, hwsq_data + (i * 4), hwsq->ptr.u32[i]); | ||
627 | nv_mask(dev, 0x001098, 0x00000018, 0x00000018); | ||
628 | |||
629 | /* launch, and wait for completion */ | ||
630 | nv_wr32(dev, 0x00130c, hwsq_kick); | ||
631 | if (!nv_wait(dev, 0x001308, 0x00000100, 0x00000000)) { | ||
632 | NV_ERROR(dev, "hwsq ucode exec timed out\n"); | ||
633 | NV_ERROR(dev, "0x001308: 0x%08x\n", nv_rd32(dev, 0x001308)); | ||
634 | for (i = 0; i < hwsq->len / 4; i++) { | ||
635 | NV_ERROR(dev, "0x%06x: 0x%08x\n", 0x1400 + (i * 4), | ||
636 | nv_rd32(dev, 0x001400 + (i * 4))); | ||
637 | } | ||
638 | |||
639 | return -EIO; | ||
640 | } | ||
641 | |||
642 | return 0; | ||
643 | } | ||
644 | |||
540 | int | 645 | int |
541 | nv50_pm_clocks_set(struct drm_device *dev, void *data) | 646 | nv50_pm_clocks_set(struct drm_device *dev, void *data) |
542 | { | 647 | { |
@@ -550,6 +655,28 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) | |||
550 | if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) | 655 | if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) |
551 | goto error; | 656 | goto error; |
552 | 657 | ||
658 | /* memory: it is *very* important we change this first, the ucode | ||
659 | * we build in pre() now has hardcoded 0xc040 values, which can't | ||
660 | * change before we execute it or the engine clocks may end up | ||
661 | * messed up. | ||
662 | */ | ||
663 | if (info->mclk_hwsq.len) { | ||
664 | /* execute some scripts that do ??? from the vbios.. */ | ||
665 | if (!bit_table(dev, 'M', &M) && M.version == 1) { | ||
666 | if (M.length >= 6) | ||
667 | nouveau_bios_init_exec(dev, ROM16(M.data[5])); | ||
668 | if (M.length >= 8) | ||
669 | nouveau_bios_init_exec(dev, ROM16(M.data[7])); | ||
670 | if (M.length >= 10) | ||
671 | nouveau_bios_init_exec(dev, ROM16(M.data[9])); | ||
672 | nouveau_bios_init_exec(dev, info->mscript); | ||
673 | } | ||
674 | |||
675 | ret = prog_mclk(dev, &info->mclk_hwsq); | ||
676 | if (ret) | ||
677 | goto resume; | ||
678 | } | ||
679 | |||
553 | /* reclock vdec/dom6 */ | 680 | /* reclock vdec/dom6 */ |
554 | nv_mask(dev, 0x00c040, 0x00000c00, 0x00000000); | 681 | nv_mask(dev, 0x00c040, 0x00000c00, 0x00000000); |
555 | switch (dev_priv->chipset) { | 682 | switch (dev_priv->chipset) { |
@@ -578,50 +705,6 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data) | |||
578 | nv_wr32(dev, 0x00402c, info->ncoef); | 705 | nv_wr32(dev, 0x00402c, info->ncoef); |
579 | nv_mask(dev, 0x00c040, 0x00100033, info->emast); | 706 | nv_mask(dev, 0x00c040, 0x00100033, info->emast); |
580 | 707 | ||
581 | /* memory */ | ||
582 | if (!info->mctrl) | ||
583 | goto resume; | ||
584 | |||
585 | /* execute some scripts that do ??? from the vbios.. */ | ||
586 | if (!bit_table(dev, 'M', &M) && M.version == 1) { | ||
587 | if (M.length >= 6) | ||
588 | nouveau_bios_init_exec(dev, ROM16(M.data[5])); | ||
589 | if (M.length >= 8) | ||
590 | nouveau_bios_init_exec(dev, ROM16(M.data[7])); | ||
591 | if (M.length >= 10) | ||
592 | nouveau_bios_init_exec(dev, ROM16(M.data[9])); | ||
593 | nouveau_bios_init_exec(dev, info->mscript); | ||
594 | } | ||
595 | |||
596 | /* disable display */ | ||
597 | if (dev_priv->chipset >= 0x92) { | ||
598 | nv_wr32(dev, 0x611200, 0x00003300); | ||
599 | udelay(100); | ||
600 | } | ||
601 | |||
602 | /* prepare ram for reclocking */ | ||
603 | nv_wr32(dev, 0x1002d4, 0x00000001); /* precharge */ | ||
604 | nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */ | ||
605 | nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */ | ||
606 | nv_mask(dev, 0x100210, 0x80000000, 0x00000000); /* no auto-refresh */ | ||
607 | nv_wr32(dev, 0x1002dc, 0x00000001); /* enable self-refresh */ | ||
608 | |||
609 | /* modify mpll */ | ||
610 | nv_mask(dev, 0x00c040, 0x0000c000, 0x0000c000); | ||
611 | nv_mask(dev, 0x004008, 0x01ff0200, 0x00000200 | info->mctrl); | ||
612 | nv_wr32(dev, 0x00400c, info->mcoef); | ||
613 | udelay(100); | ||
614 | nv_mask(dev, 0x004008, 0x81ff0200, info->mctrl); | ||
615 | |||
616 | /* re-enable normal operation of memory controller */ | ||
617 | nv_wr32(dev, 0x1002dc, 0x00000000); | ||
618 | nv_mask(dev, 0x100210, 0x80000000, 0x80000000); | ||
619 | udelay(100); | ||
620 | |||
621 | /* re-enable display */ | ||
622 | if (dev_priv->chipset >= 0x92) | ||
623 | nv_wr32(dev, 0x611200, 0x00003330); | ||
624 | |||
625 | goto resume; | 708 | goto resume; |
626 | error: | 709 | error: |
627 | ret = -EBUSY; | 710 | ret = -EBUSY; |