blob: 35fc57a936983fd089981f56ceabaa447ae3ceae [file] [log] [blame]
Ben Skeggsfade7ad2010-09-27 11:18:14 +10001/*
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#include "drmP.h"
26#include "nouveau_drv.h"
27#include "nouveau_bios.h"
28#include "nouveau_pm.h"
29
Ben Skeggs3b0582d2011-06-17 11:09:40 +100030static u32 read_pll(struct drm_device *dev, u32 pll, int clk);
31static u32 read_clk(struct drm_device *dev, int clk);
32
33static u32
34read_clk(struct drm_device *dev, int clk)
35{
36 u32 sctl, sdiv, sclk;
37
38 if (clk >= 0x40)
39 return 27000;
40
41 sctl = nv_rd32(dev, 0x4120 + (clk * 4));
42 switch (sctl & 0x00003100) {
43 case 0x00000100:
44 return 27000;
45 case 0x00002100:
46 if (sctl & 0x00000040)
47 return 108000;
48 return 100000;
49 case 0x00003100:
50 sdiv = ((sctl & 0x003f0000) >> 16) + 2;
51 if ((sctl & 0x00000030) != 0x00000030)
52 sclk = read_pll(dev, 0x00e820, 0x41);
53 else
54 sclk = read_pll(dev, 0x00e8a0, 0x42);
55
56 return (sclk * 2) / sdiv;
57 default:
58 return 0;
59 }
60}
61
62static u32
63read_pll(struct drm_device *dev, u32 pll, int clk)
64{
65 u32 ctrl = nv_rd32(dev, pll + 0);
66 u32 sclk, P = 1, N = 1, M = 1;
67
68 if (!(ctrl & 0x00000008)) {
69 u32 coef = nv_rd32(dev, pll + 4);
70 M = (coef & 0x000000ff) >> 0;
71 N = (coef & 0x0000ff00) >> 8;
72 P = (coef & 0x003f0000) >> 16;
73 if ((pll & 0x00ff00) == 0x00e800)
74 P = 1;
75
76 sclk = read_clk(dev, 0x00 + clk);
77 } else {
78 sclk = read_clk(dev, 0x10 + clk);
79 }
80
81 return sclk * N / (M * P);
82}
Ben Skeggsfade7ad2010-09-27 11:18:14 +100083
84struct nva3_pm_state {
Ben Skeggsdac55b52011-04-15 11:16:55 +100085 enum pll_types type;
86 u32 src0;
87 u32 src1;
88 u32 ctrl;
89 u32 coef;
90 u32 old_pnm;
91 u32 new_pnm;
92 u32 new_div;
Ben Skeggsfade7ad2010-09-27 11:18:14 +100093};
94
Ben Skeggs215f9022011-04-14 15:02:03 +100095static int
96nva3_pm_pll_offset(u32 id)
97{
98 static const u32 pll_map[] = {
99 0x00, PLL_CORE,
100 0x01, PLL_SHADER,
101 0x02, PLL_MEMORY,
102 0x00, 0x00
103 };
104 const u32 *map = pll_map;
105
106 while (map[1]) {
107 if (id == map[1])
108 return map[0];
109 map += 2;
110 }
111
112 return -ENOENT;
113}
114
Ben Skeggsfade7ad2010-09-27 11:18:14 +1000115int
116nva3_pm_clock_get(struct drm_device *dev, u32 id)
117{
Ben Skeggs3b0582d2011-06-17 11:09:40 +1000118 switch (id) {
119 case PLL_CORE:
120 return read_pll(dev, 0x4200, 0);
121 case PLL_SHADER:
122 return read_pll(dev, 0x4220, 1);
123 case PLL_MEMORY:
124 return read_pll(dev, 0x4000, 2);
125 default:
126 return -ENOENT;
Ben Skeggs215f9022011-04-14 15:02:03 +1000127 }
Ben Skeggsfade7ad2010-09-27 11:18:14 +1000128}
129
130void *
131nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
132 u32 id, int khz)
133{
Ben Skeggsdac55b52011-04-15 11:16:55 +1000134 struct nva3_pm_state *pll;
135 struct pll_lims limits;
Ben Skeggs52eba8d2011-04-28 02:34:21 +1000136 int N, M, P, diff;
Ben Skeggsdac55b52011-04-15 11:16:55 +1000137 int ret, off;
Ben Skeggsfade7ad2010-09-27 11:18:14 +1000138
Ben Skeggsdac55b52011-04-15 11:16:55 +1000139 ret = get_pll_limits(dev, id, &limits);
140 if (ret < 0)
Ben Skeggsfade7ad2010-09-27 11:18:14 +1000141 return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
Ben Skeggsdac55b52011-04-15 11:16:55 +1000142
143 off = nva3_pm_pll_offset(id);
144 if (id < 0)
145 return ERR_PTR(-EINVAL);
146
147
148 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
149 if (!pll)
150 return ERR_PTR(-ENOMEM);
151 pll->type = id;
152 pll->src0 = 0x004120 + (off * 4);
153 pll->src1 = 0x004160 + (off * 4);
154 pll->ctrl = limits.reg + 0;
155 pll->coef = limits.reg + 4;
156
157 /* If target clock is within [-2, 3) MHz of a divisor, we'll
158 * use that instead of calculating MNP values
159 */
Ben Skeggsbfb61f42011-04-20 14:15:49 +1000160 pll->new_div = min((limits.refclk * 2) / (khz - 2999), 16);
Ben Skeggsdac55b52011-04-15 11:16:55 +1000161 if (pll->new_div) {
162 diff = khz - ((limits.refclk * 2) / pll->new_div);
163 if (diff < -2000 || diff >= 3000)
164 pll->new_div = 0;
Ben Skeggsfade7ad2010-09-27 11:18:14 +1000165 }
166
Ben Skeggsdac55b52011-04-15 11:16:55 +1000167 if (!pll->new_div) {
Ben Skeggs52eba8d2011-04-28 02:34:21 +1000168 ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P);
Ben Skeggsdac55b52011-04-15 11:16:55 +1000169 if (ret < 0)
170 return ERR_PTR(ret);
171
172 pll->new_pnm = (P << 16) | (N << 8) | M;
173 pll->new_div = 2 - 1;
174 } else {
175 pll->new_pnm = 0;
176 pll->new_div--;
Ben Skeggsfade7ad2010-09-27 11:18:14 +1000177 }
178
Ben Skeggsdac55b52011-04-15 11:16:55 +1000179 if ((nv_rd32(dev, pll->src1) & 0x00000101) != 0x00000101)
180 pll->old_pnm = nv_rd32(dev, pll->coef);
181 return pll;
Ben Skeggsfade7ad2010-09-27 11:18:14 +1000182}
183
184void
185nva3_pm_clock_set(struct drm_device *dev, void *pre_state)
186{
Ben Skeggsdac55b52011-04-15 11:16:55 +1000187 struct nva3_pm_state *pll = pre_state;
188 u32 ctrl = 0;
Ben Skeggsfade7ad2010-09-27 11:18:14 +1000189
Ben Skeggsdac55b52011-04-15 11:16:55 +1000190 /* For the memory clock, NVIDIA will build a "script" describing
191 * the reclocking process and ask PDAEMON to execute it.
192 */
193 if (pll->type == PLL_MEMORY) {
194 nv_wr32(dev, 0x100210, 0);
195 nv_wr32(dev, 0x1002dc, 1);
196 nv_wr32(dev, 0x004018, 0x00001000);
197 ctrl = 0x18000100;
198 }
199
200 if (pll->old_pnm || !pll->new_pnm) {
201 nv_mask(dev, pll->src1, 0x003c0101, 0x00000101 |
202 (pll->new_div << 18));
203 nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl);
204 nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000);
205 }
206
207 if (pll->new_pnm) {
208 nv_mask(dev, pll->src0, 0x00000101, 0x00000101);
209 nv_wr32(dev, pll->coef, pll->new_pnm);
210 nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl);
211 nv_mask(dev, pll->ctrl, 0x00000010, 0x00000000);
212 nv_mask(dev, pll->ctrl, 0x00020010, 0x00020010);
213 nv_wr32(dev, pll->ctrl, 0x00010015 | ctrl);
214 nv_mask(dev, pll->src1, 0x00000100, 0x00000000);
215 nv_mask(dev, pll->src1, 0x00000001, 0x00000000);
216 if (pll->type == PLL_MEMORY)
217 nv_wr32(dev, 0x4018, 0x10005000);
218 } else {
219 nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000);
220 nv_mask(dev, pll->src0, 0x00000100, 0x00000000);
221 nv_mask(dev, pll->src0, 0x00000001, 0x00000000);
222 if (pll->type == PLL_MEMORY)
223 nv_wr32(dev, 0x4018, 0x1000d000);
224 }
225
226 if (pll->type == PLL_MEMORY) {
227 nv_wr32(dev, 0x1002dc, 0);
228 nv_wr32(dev, 0x100210, 0x80000000);
229 }
230
231 kfree(pll);
Ben Skeggsfade7ad2010-09-27 11:18:14 +1000232}
233