aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Peres <martin.peres@ensi-bourges.fr>2011-11-07 17:38:50 -0500
committerBen Skeggs <bskeggs@redhat.com>2011-12-21 04:01:44 -0500
commiteeb7a50bddb281d7beecb0ad73c9f1233e9932c2 (patch)
tree64e74c8fd82a07cc58c56cd3fe7ea492048cbbcf
parentabbd3f8e3bea4b2b0490260e67357067a2dc2039 (diff)
drm/nv50/pm: introduce hwsq-based memory reclocking
More work needs to be done on supporting the different memory types. v2 (Ben Skeggs): - fixed up conflicts from not having pausing patch first - restructured code somewhat to fit with how all the other code works - fixed bug where incorrect mpll_ctrl could get set sometimes - removed stuff that's cargo-culted from the binary driver - merged nv92+ display disable into hwsq - fixed incorrect opcode 0x5f magic at end of ucode Signed-off-by: Martin Peres <martin.peres@ensi-bourges.fr> Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_hwsq.h98
-rw-r--r--drivers/gpu/drm/nouveau/nv50_pm.c207
2 files changed, 243 insertions, 62 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_hwsq.h b/drivers/gpu/drm/nouveau/nouveau_hwsq.h
new file mode 100644
index 000000000000..d59a3b3ff644
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_hwsq.h
@@ -0,0 +1,98 @@
1/*
2 * Copyright 2010 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25#ifndef __NOUVEAU_HWSQ_H__
26#define __NOUVEAU_HWSQ_H__
27
28struct hwsq_ucode {
29 u8 data[0x200];
30 union {
31 u8 *u08;
32 u16 *u16;
33 u32 *u32;
34 } ptr;
35 u16 len;
36
37 u32 reg;
38 u32 val;
39};
40
41static inline void
42hwsq_init(struct hwsq_ucode *hwsq)
43{
44 hwsq->ptr.u08 = hwsq->data;
45 hwsq->reg = 0xffffffff;
46 hwsq->val = 0xffffffff;
47}
48
49static inline void
50hwsq_fini(struct hwsq_ucode *hwsq)
51{
52 do {
53 *hwsq->ptr.u08++ = 0x7f;
54 hwsq->len = hwsq->ptr.u08 - hwsq->data;
55 } while (hwsq->len & 3);
56 hwsq->ptr.u08 = hwsq->data;
57}
58
59static inline void
60hwsq_unkn(struct hwsq_ucode *hwsq, u8 v0)
61{
62 *hwsq->ptr.u08++ = v0;
63}
64
65static inline void
66hwsq_op5f(struct hwsq_ucode *hwsq, u8 v0, u8 v1)
67{
68 *hwsq->ptr.u08++ = 0x5f;
69 *hwsq->ptr.u08++ = v0;
70 *hwsq->ptr.u08++ = v1;
71}
72
73static inline void
74hwsq_wr32(struct hwsq_ucode *hwsq, u32 reg, u32 val)
75{
76 if (val != hwsq->val) {
77 if ((val & 0xffff0000) == (hwsq->val & 0xffff0000)) {
78 *hwsq->ptr.u08++ = 0x42;
79 *hwsq->ptr.u16++ = (val & 0x0000ffff);
80 } else {
81 *hwsq->ptr.u08++ = 0xe2;
82 *hwsq->ptr.u32++ = val;
83 }
84
85 hwsq->val = val;
86 }
87
88 if ((reg & 0xffff0000) == (hwsq->reg & 0xffff0000)) {
89 *hwsq->ptr.u08++ = 0x40;
90 *hwsq->ptr.u16++ = (reg & 0x0000ffff);
91 } else {
92 *hwsq->ptr.u08++ = 0xe0;
93 *hwsq->ptr.u32++ = reg;
94 }
95 hwsq->reg = reg;
96}
97
98#endif
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
31enum clk_src { 32enum 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
353struct nv50_pm_state { 354struct 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
368static u32 368static 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
418static int
419calc_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
418void * 492void *
419nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) 493nv50_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
607static int
608prog_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
540int 645int
541nv50_pm_clocks_set(struct drm_device *dev, void *data) 646nv50_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;
626error: 709error:
627 ret = -EBUSY; 710 ret = -EBUSY;