Commit | Line | Data |
---|---|---|
354d0781 BS |
1 | /* |
2 | * Copyright 2011 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 | ||
77145f1c BS |
25 | #include "nouveau_drm.h" |
26 | #include "nouveau_bios.h" | |
354d0781 BS |
27 | #include "nouveau_pm.h" |
28 | ||
77145f1c BS |
29 | #include <subdev/bios/pll.h> |
30 | #include <subdev/bios.h> | |
31 | #include <subdev/clock.h> | |
32 | #include <subdev/timer.h> | |
33 | #include <subdev/fb.h> | |
34 | ||
354d0781 BS |
35 | static u32 read_div(struct drm_device *, int, u32, u32); |
36 | static u32 read_pll(struct drm_device *, u32); | |
37 | ||
38 | static u32 | |
39 | read_vco(struct drm_device *dev, u32 dsrc) | |
40 | { | |
77145f1c BS |
41 | struct nouveau_device *device = nouveau_dev(dev); |
42 | u32 ssrc = nv_rd32(device, dsrc); | |
354d0781 BS |
43 | if (!(ssrc & 0x00000100)) |
44 | return read_pll(dev, 0x00e800); | |
45 | return read_pll(dev, 0x00e820); | |
46 | } | |
47 | ||
48 | static u32 | |
49 | read_pll(struct drm_device *dev, u32 pll) | |
50 | { | |
77145f1c BS |
51 | struct nouveau_device *device = nouveau_dev(dev); |
52 | u32 ctrl = nv_rd32(device, pll + 0); | |
53 | u32 coef = nv_rd32(device, pll + 4); | |
354d0781 BS |
54 | u32 P = (coef & 0x003f0000) >> 16; |
55 | u32 N = (coef & 0x0000ff00) >> 8; | |
56 | u32 M = (coef & 0x000000ff) >> 0; | |
57 | u32 sclk, doff; | |
58 | ||
8ce51fcf BS |
59 | if (!(ctrl & 0x00000001)) |
60 | return 0; | |
61 | ||
354d0781 BS |
62 | switch (pll & 0xfff000) { |
63 | case 0x00e000: | |
64 | sclk = 27000; | |
65 | P = 1; | |
66 | break; | |
67 | case 0x137000: | |
68 | doff = (pll - 0x137000) / 0x20; | |
69 | sclk = read_div(dev, doff, 0x137120, 0x137140); | |
70 | break; | |
71 | case 0x132000: | |
72 | switch (pll) { | |
73 | case 0x132000: | |
74 | sclk = read_pll(dev, 0x132020); | |
75 | break; | |
76 | case 0x132020: | |
77 | sclk = read_div(dev, 0, 0x137320, 0x137330); | |
78 | break; | |
79 | default: | |
80 | return 0; | |
81 | } | |
82 | break; | |
83 | default: | |
84 | return 0; | |
85 | } | |
86 | ||
87 | return sclk * N / M / P; | |
88 | } | |
89 | ||
90 | static u32 | |
91 | read_div(struct drm_device *dev, int doff, u32 dsrc, u32 dctl) | |
92 | { | |
77145f1c BS |
93 | struct nouveau_device *device = nouveau_dev(dev); |
94 | u32 ssrc = nv_rd32(device, dsrc + (doff * 4)); | |
95 | u32 sctl = nv_rd32(device, dctl + (doff * 4)); | |
354d0781 BS |
96 | |
97 | switch (ssrc & 0x00000003) { | |
98 | case 0: | |
99 | if ((ssrc & 0x00030000) != 0x00030000) | |
100 | return 27000; | |
101 | return 108000; | |
102 | case 2: | |
103 | return 100000; | |
104 | case 3: | |
105 | if (sctl & 0x80000000) { | |
8ce51fcf | 106 | u32 sclk = read_vco(dev, dsrc + (doff * 4)); |
354d0781 BS |
107 | u32 sdiv = (sctl & 0x0000003f) + 2; |
108 | return (sclk * 2) / sdiv; | |
109 | } | |
110 | ||
8ce51fcf | 111 | return read_vco(dev, dsrc + (doff * 4)); |
354d0781 BS |
112 | default: |
113 | return 0; | |
114 | } | |
115 | } | |
116 | ||
117 | static u32 | |
118 | read_mem(struct drm_device *dev) | |
119 | { | |
77145f1c BS |
120 | struct nouveau_device *device = nouveau_dev(dev); |
121 | u32 ssel = nv_rd32(device, 0x1373f0); | |
354d0781 BS |
122 | if (ssel & 0x00000001) |
123 | return read_div(dev, 0, 0x137300, 0x137310); | |
124 | return read_pll(dev, 0x132000); | |
125 | } | |
126 | ||
127 | static u32 | |
128 | read_clk(struct drm_device *dev, int clk) | |
129 | { | |
77145f1c BS |
130 | struct nouveau_device *device = nouveau_dev(dev); |
131 | u32 sctl = nv_rd32(device, 0x137250 + (clk * 4)); | |
132 | u32 ssel = nv_rd32(device, 0x137100); | |
354d0781 BS |
133 | u32 sclk, sdiv; |
134 | ||
135 | if (ssel & (1 << clk)) { | |
136 | if (clk < 7) | |
137 | sclk = read_pll(dev, 0x137000 + (clk * 0x20)); | |
138 | else | |
139 | sclk = read_pll(dev, 0x1370e0); | |
140 | sdiv = ((sctl & 0x00003f00) >> 8) + 2; | |
141 | } else { | |
142 | sclk = read_div(dev, clk, 0x137160, 0x1371d0); | |
143 | sdiv = ((sctl & 0x0000003f) >> 0) + 2; | |
144 | } | |
145 | ||
146 | if (sctl & 0x80000000) | |
147 | return (sclk * 2) / sdiv; | |
148 | return sclk; | |
149 | } | |
150 | ||
151 | int | |
152 | nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl) | |
153 | { | |
154 | perflvl->shader = read_clk(dev, 0x00); | |
155 | perflvl->core = perflvl->shader / 2; | |
156 | perflvl->memory = read_mem(dev); | |
9698b9a6 BS |
157 | perflvl->rop = read_clk(dev, 0x01); |
158 | perflvl->hub07 = read_clk(dev, 0x02); | |
159 | perflvl->hub06 = read_clk(dev, 0x07); | |
160 | perflvl->hub01 = read_clk(dev, 0x08); | |
161 | perflvl->copy = read_clk(dev, 0x09); | |
162 | perflvl->daemon = read_clk(dev, 0x0c); | |
354d0781 BS |
163 | perflvl->vdec = read_clk(dev, 0x0e); |
164 | return 0; | |
165 | } | |
045da4e5 BS |
166 | |
167 | struct nvc0_pm_clock { | |
168 | u32 freq; | |
169 | u32 ssel; | |
170 | u32 mdiv; | |
171 | u32 dsrc; | |
172 | u32 ddiv; | |
173 | u32 coef; | |
174 | }; | |
175 | ||
176 | struct nvc0_pm_state { | |
a1da205f | 177 | struct nouveau_pm_level *perflvl; |
045da4e5 | 178 | struct nvc0_pm_clock eng[16]; |
6b91d6b0 | 179 | struct nvc0_pm_clock mem; |
045da4e5 BS |
180 | }; |
181 | ||
182 | static u32 | |
183 | calc_div(struct drm_device *dev, int clk, u32 ref, u32 freq, u32 *ddiv) | |
184 | { | |
185 | u32 div = min((ref * 2) / freq, (u32)65); | |
186 | if (div < 2) | |
187 | div = 2; | |
188 | ||
189 | *ddiv = div - 2; | |
190 | return (ref * 2) / div; | |
191 | } | |
192 | ||
193 | static u32 | |
194 | calc_src(struct drm_device *dev, int clk, u32 freq, u32 *dsrc, u32 *ddiv) | |
195 | { | |
196 | u32 sclk; | |
197 | ||
198 | /* use one of the fixed frequencies if possible */ | |
199 | *ddiv = 0x00000000; | |
200 | switch (freq) { | |
201 | case 27000: | |
202 | case 108000: | |
203 | *dsrc = 0x00000000; | |
204 | if (freq == 108000) | |
205 | *dsrc |= 0x00030000; | |
206 | return freq; | |
207 | case 100000: | |
208 | *dsrc = 0x00000002; | |
209 | return freq; | |
210 | default: | |
211 | *dsrc = 0x00000003; | |
212 | break; | |
213 | } | |
214 | ||
215 | /* otherwise, calculate the closest divider */ | |
216 | sclk = read_vco(dev, clk); | |
217 | if (clk < 7) | |
218 | sclk = calc_div(dev, clk, sclk, freq, ddiv); | |
219 | return sclk; | |
220 | } | |
221 | ||
222 | static u32 | |
223 | calc_pll(struct drm_device *dev, int clk, u32 freq, u32 *coef) | |
224 | { | |
77145f1c BS |
225 | struct nouveau_device *device = nouveau_dev(dev); |
226 | struct nouveau_bios *bios = nouveau_bios(device); | |
70790f4f | 227 | struct nvbios_pll limits; |
045da4e5 BS |
228 | int N, M, P, ret; |
229 | ||
77145f1c | 230 | ret = nvbios_pll_parse(bios, 0x137000 + (clk * 0x20), &limits); |
045da4e5 BS |
231 | if (ret) |
232 | return 0; | |
233 | ||
234 | limits.refclk = read_div(dev, clk, 0x137120, 0x137140); | |
235 | if (!limits.refclk) | |
236 | return 0; | |
237 | ||
238 | ret = nva3_calc_pll(dev, &limits, freq, &N, NULL, &M, &P); | |
239 | if (ret <= 0) | |
240 | return 0; | |
241 | ||
242 | *coef = (P << 16) | (N << 8) | M; | |
243 | return ret; | |
244 | } | |
245 | ||
246 | /* A (likely rather simplified and incomplete) view of the clock tree | |
247 | * | |
248 | * Key: | |
249 | * | |
250 | * S: source select | |
251 | * D: divider | |
252 | * P: pll | |
253 | * F: switch | |
254 | * | |
255 | * Engine clocks: | |
256 | * | |
257 | * 137250(D) ---- 137100(F0) ---- 137160(S)/1371d0(D) ------------------- ref | |
258 | * (F1) ---- 1370X0(P) ---- 137120(S)/137140(D) ---- ref | |
259 | * | |
260 | * Not all registers exist for all clocks. For example: clocks >= 8 don't | |
261 | * have their own PLL (all tied to clock 7's PLL when in PLL mode), nor do | |
262 | * they have the divider at 1371d0, though the source selection at 137160 | |
263 | * still exists. You must use the divider at 137250 for these instead. | |
264 | * | |
265 | * Memory clock: | |
266 | * | |
267 | * TBD, read_mem() above is likely very wrong... | |
268 | * | |
269 | */ | |
270 | ||
271 | static int | |
272 | calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq) | |
273 | { | |
274 | u32 src0, div0, div1D, div1P = 0; | |
275 | u32 clk0, clk1 = 0; | |
276 | ||
277 | /* invalid clock domain */ | |
278 | if (!freq) | |
279 | return 0; | |
280 | ||
281 | /* first possible path, using only dividers */ | |
282 | clk0 = calc_src(dev, clk, freq, &src0, &div0); | |
283 | clk0 = calc_div(dev, clk, clk0, freq, &div1D); | |
284 | ||
285 | /* see if we can get any closer using PLLs */ | |
1ae73f2f | 286 | if (clk0 != freq && (0x00004387 & (1 << clk))) { |
045da4e5 BS |
287 | if (clk < 7) |
288 | clk1 = calc_pll(dev, clk, freq, &info->coef); | |
289 | else | |
290 | clk1 = read_pll(dev, 0x1370e0); | |
291 | clk1 = calc_div(dev, clk, clk1, freq, &div1P); | |
292 | } | |
293 | ||
294 | /* select the method which gets closest to target freq */ | |
295 | if (abs((int)freq - clk0) <= abs((int)freq - clk1)) { | |
296 | info->dsrc = src0; | |
297 | if (div0) { | |
298 | info->ddiv |= 0x80000000; | |
299 | info->ddiv |= div0 << 8; | |
300 | info->ddiv |= div0; | |
301 | } | |
302 | if (div1D) { | |
303 | info->mdiv |= 0x80000000; | |
304 | info->mdiv |= div1D; | |
305 | } | |
306 | info->ssel = 0; | |
307 | info->freq = clk0; | |
308 | } else { | |
309 | if (div1P) { | |
310 | info->mdiv |= 0x80000000; | |
311 | info->mdiv |= div1P << 8; | |
312 | } | |
313 | info->ssel = (1 << clk); | |
314 | info->freq = clk1; | |
315 | } | |
316 | ||
317 | return 0; | |
318 | } | |
319 | ||
6b91d6b0 BS |
320 | static int |
321 | calc_mem(struct drm_device *dev, struct nvc0_pm_clock *info, u32 freq) | |
322 | { | |
77145f1c BS |
323 | struct nouveau_device *device = nouveau_dev(dev); |
324 | struct nouveau_bios *bios = nouveau_bios(device); | |
70790f4f | 325 | struct nvbios_pll pll; |
6b91d6b0 BS |
326 | int N, M, P, ret; |
327 | u32 ctrl; | |
328 | ||
329 | /* mclk pll input freq comes from another pll, make sure it's on */ | |
77145f1c | 330 | ctrl = nv_rd32(device, 0x132020); |
6b91d6b0 BS |
331 | if (!(ctrl & 0x00000001)) { |
332 | /* if not, program it to 567MHz. nfi where this value comes | |
333 | * from - it looks like it's in the pll limits table for | |
334 | * 132000 but the binary driver ignores all my attempts to | |
335 | * change this value. | |
336 | */ | |
77145f1c BS |
337 | nv_wr32(device, 0x137320, 0x00000103); |
338 | nv_wr32(device, 0x137330, 0x81200606); | |
339 | nv_wait(device, 0x132020, 0x00010000, 0x00010000); | |
340 | nv_wr32(device, 0x132024, 0x0001150f); | |
341 | nv_mask(device, 0x132020, 0x00000001, 0x00000001); | |
342 | nv_wait(device, 0x137390, 0x00020000, 0x00020000); | |
343 | nv_mask(device, 0x132020, 0x00000004, 0x00000004); | |
6b91d6b0 BS |
344 | } |
345 | ||
346 | /* for the moment, until the clock tree is better understood, use | |
347 | * pll mode for all clock frequencies | |
348 | */ | |
77145f1c | 349 | ret = nvbios_pll_parse(bios, 0x132000, &pll); |
6b91d6b0 BS |
350 | if (ret == 0) { |
351 | pll.refclk = read_pll(dev, 0x132020); | |
352 | if (pll.refclk) { | |
353 | ret = nva3_calc_pll(dev, &pll, freq, &N, NULL, &M, &P); | |
354 | if (ret > 0) { | |
355 | info->coef = (P << 16) | (N << 8) | M; | |
356 | return 0; | |
357 | } | |
358 | } | |
359 | } | |
360 | ||
361 | return -EINVAL; | |
362 | } | |
363 | ||
045da4e5 BS |
364 | void * |
365 | nvc0_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl) | |
366 | { | |
77145f1c | 367 | struct nouveau_device *device = nouveau_dev(dev); |
045da4e5 BS |
368 | struct nvc0_pm_state *info; |
369 | int ret; | |
370 | ||
371 | info = kzalloc(sizeof(*info), GFP_KERNEL); | |
372 | if (!info) | |
373 | return ERR_PTR(-ENOMEM); | |
374 | ||
375 | /* NFI why this is still in the performance table, the ROPCs appear | |
376 | * to get their clock from clock 2 ("hub07", actually hub05 on this | |
377 | * chip, but, anyway...) as well. nvatiming confirms hub05 and ROP | |
378 | * are always the same freq with the binary driver even when the | |
379 | * performance table says they should differ. | |
380 | */ | |
77145f1c | 381 | if (device->chipset == 0xd9) |
045da4e5 BS |
382 | perflvl->rop = 0; |
383 | ||
384 | if ((ret = calc_clk(dev, 0x00, &info->eng[0x00], perflvl->shader)) || | |
385 | (ret = calc_clk(dev, 0x01, &info->eng[0x01], perflvl->rop)) || | |
386 | (ret = calc_clk(dev, 0x02, &info->eng[0x02], perflvl->hub07)) || | |
387 | (ret = calc_clk(dev, 0x07, &info->eng[0x07], perflvl->hub06)) || | |
388 | (ret = calc_clk(dev, 0x08, &info->eng[0x08], perflvl->hub01)) || | |
389 | (ret = calc_clk(dev, 0x09, &info->eng[0x09], perflvl->copy)) || | |
390 | (ret = calc_clk(dev, 0x0c, &info->eng[0x0c], perflvl->daemon)) || | |
391 | (ret = calc_clk(dev, 0x0e, &info->eng[0x0e], perflvl->vdec))) { | |
392 | kfree(info); | |
393 | return ERR_PTR(ret); | |
394 | } | |
395 | ||
6b91d6b0 BS |
396 | if (perflvl->memory) { |
397 | ret = calc_mem(dev, &info->mem, perflvl->memory); | |
398 | if (ret) { | |
399 | kfree(info); | |
400 | return ERR_PTR(ret); | |
401 | } | |
402 | } | |
403 | ||
a1da205f | 404 | info->perflvl = perflvl; |
045da4e5 BS |
405 | return info; |
406 | } | |
407 | ||
408 | static void | |
409 | prog_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info) | |
410 | { | |
77145f1c BS |
411 | struct nouveau_device *device = nouveau_dev(dev); |
412 | ||
045da4e5 BS |
413 | /* program dividers at 137160/1371d0 first */ |
414 | if (clk < 7 && !info->ssel) { | |
77145f1c BS |
415 | nv_mask(device, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv); |
416 | nv_wr32(device, 0x137160 + (clk * 0x04), info->dsrc); | |
045da4e5 BS |
417 | } |
418 | ||
419 | /* switch clock to non-pll mode */ | |
77145f1c BS |
420 | nv_mask(device, 0x137100, (1 << clk), 0x00000000); |
421 | nv_wait(device, 0x137100, (1 << clk), 0x00000000); | |
045da4e5 BS |
422 | |
423 | /* reprogram pll */ | |
424 | if (clk < 7) { | |
425 | /* make sure it's disabled first... */ | |
426 | u32 base = 0x137000 + (clk * 0x20); | |
77145f1c | 427 | u32 ctrl = nv_rd32(device, base + 0x00); |
045da4e5 | 428 | if (ctrl & 0x00000001) { |
77145f1c BS |
429 | nv_mask(device, base + 0x00, 0x00000004, 0x00000000); |
430 | nv_mask(device, base + 0x00, 0x00000001, 0x00000000); | |
045da4e5 BS |
431 | } |
432 | /* program it to new values, if necessary */ | |
433 | if (info->ssel) { | |
77145f1c BS |
434 | nv_wr32(device, base + 0x04, info->coef); |
435 | nv_mask(device, base + 0x00, 0x00000001, 0x00000001); | |
436 | nv_wait(device, base + 0x00, 0x00020000, 0x00020000); | |
437 | nv_mask(device, base + 0x00, 0x00020004, 0x00000004); | |
045da4e5 BS |
438 | } |
439 | } | |
440 | ||
441 | /* select pll/non-pll mode, and program final clock divider */ | |
77145f1c BS |
442 | nv_mask(device, 0x137100, (1 << clk), info->ssel); |
443 | nv_wait(device, 0x137100, (1 << clk), info->ssel); | |
444 | nv_mask(device, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv); | |
045da4e5 BS |
445 | } |
446 | ||
a1da205f BS |
447 | static void |
448 | mclk_precharge(struct nouveau_mem_exec_func *exec) | |
449 | { | |
450 | } | |
451 | ||
452 | static void | |
453 | mclk_refresh(struct nouveau_mem_exec_func *exec) | |
454 | { | |
455 | } | |
456 | ||
457 | static void | |
458 | mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable) | |
459 | { | |
77145f1c BS |
460 | struct nouveau_device *device = nouveau_dev(exec->dev); |
461 | nv_wr32(device, 0x10f210, enable ? 0x80000000 : 0x00000000); | |
a1da205f BS |
462 | } |
463 | ||
464 | static void | |
465 | mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable) | |
466 | { | |
467 | } | |
468 | ||
469 | static void | |
470 | mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec) | |
471 | { | |
472 | udelay((nsec + 500) / 1000); | |
473 | } | |
474 | ||
475 | static u32 | |
476 | mclk_mrg(struct nouveau_mem_exec_func *exec, int mr) | |
477 | { | |
77145f1c BS |
478 | struct nouveau_device *device = nouveau_dev(exec->dev); |
479 | struct nouveau_fb *pfb = nouveau_fb(device); | |
480 | if (pfb->ram.type != NV_MEM_TYPE_GDDR5) { | |
a1da205f | 481 | if (mr <= 1) |
77145f1c BS |
482 | return nv_rd32(device, 0x10f300 + ((mr - 0) * 4)); |
483 | return nv_rd32(device, 0x10f320 + ((mr - 2) * 4)); | |
a1da205f BS |
484 | } else { |
485 | if (mr == 0) | |
77145f1c | 486 | return nv_rd32(device, 0x10f300 + (mr * 4)); |
a1da205f BS |
487 | else |
488 | if (mr <= 7) | |
77145f1c BS |
489 | return nv_rd32(device, 0x10f32c + (mr * 4)); |
490 | return nv_rd32(device, 0x10f34c); | |
a1da205f BS |
491 | } |
492 | } | |
493 | ||
494 | static void | |
495 | mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data) | |
496 | { | |
77145f1c BS |
497 | struct nouveau_device *device = nouveau_dev(exec->dev); |
498 | struct nouveau_fb *pfb = nouveau_fb(device); | |
499 | if (pfb->ram.type != NV_MEM_TYPE_GDDR5) { | |
a1da205f | 500 | if (mr <= 1) { |
77145f1c BS |
501 | nv_wr32(device, 0x10f300 + ((mr - 0) * 4), data); |
502 | if (pfb->ram.ranks > 1) | |
503 | nv_wr32(device, 0x10f308 + ((mr - 0) * 4), data); | |
a1da205f BS |
504 | } else |
505 | if (mr <= 3) { | |
77145f1c BS |
506 | nv_wr32(device, 0x10f320 + ((mr - 2) * 4), data); |
507 | if (pfb->ram.ranks > 1) | |
508 | nv_wr32(device, 0x10f328 + ((mr - 2) * 4), data); | |
a1da205f BS |
509 | } |
510 | } else { | |
77145f1c BS |
511 | if (mr == 0) nv_wr32(device, 0x10f300 + (mr * 4), data); |
512 | else if (mr <= 7) nv_wr32(device, 0x10f32c + (mr * 4), data); | |
513 | else if (mr == 15) nv_wr32(device, 0x10f34c, data); | |
a1da205f BS |
514 | } |
515 | } | |
516 | ||
517 | static void | |
518 | mclk_clock_set(struct nouveau_mem_exec_func *exec) | |
519 | { | |
77145f1c | 520 | struct nouveau_device *device = nouveau_dev(exec->dev); |
9d6ba0b5 | 521 | struct nvc0_pm_state *info = exec->priv; |
77145f1c | 522 | u32 ctrl = nv_rd32(device, 0x132000); |
9d6ba0b5 | 523 | |
77145f1c BS |
524 | nv_wr32(device, 0x137360, 0x00000001); |
525 | nv_wr32(device, 0x137370, 0x00000000); | |
526 | nv_wr32(device, 0x137380, 0x00000000); | |
9d6ba0b5 | 527 | if (ctrl & 0x00000001) |
77145f1c | 528 | nv_wr32(device, 0x132000, (ctrl &= ~0x00000001)); |
9d6ba0b5 | 529 | |
77145f1c BS |
530 | nv_wr32(device, 0x132004, info->mem.coef); |
531 | nv_wr32(device, 0x132000, (ctrl |= 0x00000001)); | |
532 | nv_wait(device, 0x137390, 0x00000002, 0x00000002); | |
533 | nv_wr32(device, 0x132018, 0x00005000); | |
9d6ba0b5 | 534 | |
77145f1c BS |
535 | nv_wr32(device, 0x137370, 0x00000001); |
536 | nv_wr32(device, 0x137380, 0x00000001); | |
537 | nv_wr32(device, 0x137360, 0x00000000); | |
a1da205f BS |
538 | } |
539 | ||
540 | static void | |
541 | mclk_timing_set(struct nouveau_mem_exec_func *exec) | |
542 | { | |
77145f1c | 543 | struct nouveau_device *device = nouveau_dev(exec->dev); |
a1da205f BS |
544 | struct nvc0_pm_state *info = exec->priv; |
545 | struct nouveau_pm_level *perflvl = info->perflvl; | |
546 | int i; | |
547 | ||
548 | for (i = 0; i < 5; i++) | |
77145f1c | 549 | nv_wr32(device, 0x10f290 + (i * 4), perflvl->timing.reg[i]); |
a1da205f BS |
550 | } |
551 | ||
552 | static void | |
553 | prog_mem(struct drm_device *dev, struct nvc0_pm_state *info) | |
554 | { | |
77145f1c | 555 | struct nouveau_device *device = nouveau_dev(dev); |
a1da205f BS |
556 | struct nouveau_mem_exec_func exec = { |
557 | .dev = dev, | |
558 | .precharge = mclk_precharge, | |
559 | .refresh = mclk_refresh, | |
560 | .refresh_auto = mclk_refresh_auto, | |
561 | .refresh_self = mclk_refresh_self, | |
562 | .wait = mclk_wait, | |
563 | .mrg = mclk_mrg, | |
564 | .mrs = mclk_mrs, | |
565 | .clock_set = mclk_clock_set, | |
566 | .timing_set = mclk_timing_set, | |
567 | .priv = info | |
568 | }; | |
569 | ||
77145f1c BS |
570 | if (device->chipset < 0xd0) |
571 | nv_wr32(device, 0x611200, 0x00003300); | |
9d6ba0b5 | 572 | else |
77145f1c | 573 | nv_wr32(device, 0x62c000, 0x03030000); |
a1da205f BS |
574 | |
575 | nouveau_mem_exec(&exec, info->perflvl); | |
576 | ||
77145f1c BS |
577 | if (device->chipset < 0xd0) |
578 | nv_wr32(device, 0x611200, 0x00003330); | |
9d6ba0b5 | 579 | else |
77145f1c | 580 | nv_wr32(device, 0x62c000, 0x03030300); |
a1da205f | 581 | } |
045da4e5 BS |
582 | int |
583 | nvc0_pm_clocks_set(struct drm_device *dev, void *data) | |
584 | { | |
585 | struct nvc0_pm_state *info = data; | |
586 | int i; | |
587 | ||
9d6ba0b5 | 588 | if (info->mem.coef) |
a1da205f BS |
589 | prog_mem(dev, info); |
590 | ||
045da4e5 BS |
591 | for (i = 0; i < 16; i++) { |
592 | if (!info->eng[i].freq) | |
593 | continue; | |
594 | prog_clk(dev, i, &info->eng[i]); | |
595 | } | |
596 | ||
597 | kfree(info); | |
598 | return 0; | |
599 | } |