]>
Commit | Line | Data |
---|---|---|
75faef78 BS |
1 | /* |
2 | * Copyright 2013 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 | |
7f4b9616 | 23 | * Roy Spliet <rspliet@eclipso.eu> |
75faef78 BS |
24 | */ |
25 | ||
aae95ca7 BS |
26 | #include <subdev/bios.h> |
27 | #include <subdev/bios/bit.h> | |
28 | #include <subdev/bios/pll.h> | |
29 | #include <subdev/bios/rammap.h> | |
7f4b9616 | 30 | #include <subdev/bios/M0205.h> |
aae95ca7 BS |
31 | #include <subdev/bios/timing.h> |
32 | ||
33 | #include <subdev/clock/nva3.h> | |
34 | #include <subdev/clock/pll.h> | |
35 | ||
7f4b9616 RS |
36 | #include <subdev/timer.h> |
37 | ||
38 | #include <engine/fifo.h> | |
39 | ||
aae95ca7 BS |
40 | #include <core/option.h> |
41 | ||
42 | #include "ramfuc.h" | |
43 | ||
75faef78 BS |
44 | #include "nv50.h" |
45 | ||
aae95ca7 BS |
46 | struct nva3_ramfuc { |
47 | struct ramfuc base; | |
7f4b9616 RS |
48 | struct ramfuc_reg r_0x001610; |
49 | struct ramfuc_reg r_0x001700; | |
b6a7907f | 50 | struct ramfuc_reg r_0x002504; |
aae95ca7 BS |
51 | struct ramfuc_reg r_0x004000; |
52 | struct ramfuc_reg r_0x004004; | |
53 | struct ramfuc_reg r_0x004018; | |
54 | struct ramfuc_reg r_0x004128; | |
55 | struct ramfuc_reg r_0x004168; | |
7f4b9616 | 56 | struct ramfuc_reg r_0x100080; |
aae95ca7 BS |
57 | struct ramfuc_reg r_0x100200; |
58 | struct ramfuc_reg r_0x100210; | |
59 | struct ramfuc_reg r_0x100220[9]; | |
b6a7907f | 60 | struct ramfuc_reg r_0x100264; |
aae95ca7 BS |
61 | struct ramfuc_reg r_0x1002d0; |
62 | struct ramfuc_reg r_0x1002d4; | |
63 | struct ramfuc_reg r_0x1002dc; | |
64 | struct ramfuc_reg r_0x10053c; | |
65 | struct ramfuc_reg r_0x1005a0; | |
66 | struct ramfuc_reg r_0x1005a4; | |
b6a7907f | 67 | struct ramfuc_reg r_0x100700; |
aae95ca7 BS |
68 | struct ramfuc_reg r_0x100714; |
69 | struct ramfuc_reg r_0x100718; | |
70 | struct ramfuc_reg r_0x10071c; | |
7f4b9616 | 71 | struct ramfuc_reg r_0x100720; |
aae95ca7 BS |
72 | struct ramfuc_reg r_0x100760; |
73 | struct ramfuc_reg r_0x1007a0; | |
74 | struct ramfuc_reg r_0x1007e0; | |
b6a7907f | 75 | struct ramfuc_reg r_0x100da0; |
aae95ca7 BS |
76 | struct ramfuc_reg r_0x10f804; |
77 | struct ramfuc_reg r_0x1110e0; | |
78 | struct ramfuc_reg r_0x111100; | |
79 | struct ramfuc_reg r_0x111104; | |
7f4b9616 RS |
80 | struct ramfuc_reg r_0x1111e0; |
81 | struct ramfuc_reg r_0x111400; | |
aae95ca7 BS |
82 | struct ramfuc_reg r_0x611200; |
83 | struct ramfuc_reg r_mr[4]; | |
84 | }; | |
85 | ||
7f4b9616 RS |
86 | struct nva3_ltrain { |
87 | enum { | |
88 | NVA3_TRAIN_UNKNOWN, | |
89 | NVA3_TRAIN_UNSUPPORTED, | |
90 | NVA3_TRAIN_ONCE, | |
91 | NVA3_TRAIN_EXEC, | |
92 | NVA3_TRAIN_DONE | |
93 | } state; | |
94 | u32 r_100720; | |
95 | u32 r_1111e0; | |
96 | u32 r_111400; | |
97 | struct nouveau_mem *mem; | |
98 | }; | |
99 | ||
75faef78 BS |
100 | struct nva3_ram { |
101 | struct nouveau_ram base; | |
aae95ca7 | 102 | struct nva3_ramfuc fuc; |
7f4b9616 | 103 | struct nva3_ltrain ltrain; |
75faef78 BS |
104 | }; |
105 | ||
7f4b9616 RS |
106 | void |
107 | nva3_link_train_calc(u32 *vals, struct nva3_ltrain *train) | |
108 | { | |
109 | int i, lo, hi; | |
110 | u8 median[8], bins[4] = {0, 0, 0, 0}, bin = 0, qty = 0; | |
111 | ||
112 | for (i = 0; i < 8; i++) { | |
113 | for (lo = 0; lo < 0x40; lo++) { | |
114 | if (!(vals[lo] & 0x80000000)) | |
115 | continue; | |
116 | if (vals[lo] & (0x101 << i)) | |
117 | break; | |
118 | } | |
119 | ||
120 | if (lo == 0x40) | |
121 | return; | |
122 | ||
123 | for (hi = lo + 1; hi < 0x40; hi++) { | |
124 | if (!(vals[lo] & 0x80000000)) | |
125 | continue; | |
126 | if (!(vals[hi] & (0x101 << i))) { | |
127 | hi--; | |
128 | break; | |
129 | } | |
130 | } | |
131 | ||
132 | median[i] = ((hi - lo) >> 1) + lo; | |
133 | bins[(median[i] & 0xf0) >> 4]++; | |
134 | median[i] += 0x30; | |
135 | } | |
136 | ||
137 | /* Find the best value for 0x1111e0 */ | |
138 | for (i = 0; i < 4; i++) { | |
139 | if (bins[i] > qty) { | |
140 | bin = i + 3; | |
141 | qty = bins[i]; | |
142 | } | |
143 | } | |
144 | ||
145 | train->r_100720 = 0; | |
146 | for (i = 0; i < 8; i++) { | |
147 | median[i] = max(median[i], (u8) (bin << 4)); | |
148 | median[i] = min(median[i], (u8) ((bin << 4) | 0xf)); | |
149 | ||
150 | train->r_100720 |= ((median[i] & 0x0f) << (i << 2)); | |
151 | } | |
152 | ||
153 | train->r_1111e0 = 0x02000000 | (bin * 0x101); | |
154 | train->r_111400 = 0x0; | |
155 | } | |
156 | ||
157 | /* | |
158 | * Link training for (at least) DDR3 | |
159 | */ | |
160 | int | |
161 | nva3_link_train(struct nouveau_fb *pfb) | |
162 | { | |
163 | struct nouveau_bios *bios = nouveau_bios(pfb); | |
164 | struct nva3_ram *ram = (void *)pfb->ram; | |
165 | struct nouveau_clock *clk = nouveau_clock(pfb); | |
166 | struct nva3_ltrain *train = &ram->ltrain; | |
167 | struct nouveau_device *device = nv_device(pfb); | |
168 | struct nva3_ramfuc *fuc = &ram->fuc; | |
169 | u32 *result, r1700; | |
170 | int ret, i; | |
171 | struct nvbios_M0205T M0205T = { 0 }; | |
172 | u8 ver, hdr, cnt, len, snr, ssz; | |
173 | unsigned int clk_current; | |
174 | unsigned long flags; | |
175 | unsigned long *f = &flags; | |
176 | ||
177 | if (nouveau_boolopt(device->cfgopt, "NvMemExec", true) != true) | |
178 | return -ENOSYS; | |
179 | ||
180 | /* XXX: Multiple partitions? */ | |
181 | result = kmalloc(64 * sizeof(u32), GFP_KERNEL); | |
182 | if (!result) | |
183 | return -ENOMEM; | |
184 | ||
185 | train->state = NVA3_TRAIN_EXEC; | |
186 | ||
187 | /* Clock speeds for training and back */ | |
188 | nvbios_M0205Tp(bios, &ver, &hdr, &cnt, &len, &snr, &ssz, &M0205T); | |
189 | if (M0205T.freq == 0) | |
190 | return -ENOENT; | |
191 | ||
192 | clk_current = clk->read(clk, nv_clk_src_mem); | |
193 | ||
194 | ret = nva3_clock_pre(clk, f); | |
195 | if (ret) | |
196 | goto out; | |
197 | ||
198 | /* First: clock up/down */ | |
199 | ret = ram->base.calc(pfb, (u32) M0205T.freq * 1000); | |
200 | if (ret) | |
201 | goto out; | |
202 | ||
203 | /* Do this *after* calc, eliminates write in script */ | |
204 | nv_wr32(pfb, 0x111400, 0x00000000); | |
205 | /* XXX: Magic writes that improve train reliability? */ | |
206 | nv_mask(pfb, 0x100674, 0x0000ffff, 0x00000000); | |
207 | nv_mask(pfb, 0x1005e4, 0x0000ffff, 0x00000000); | |
208 | nv_mask(pfb, 0x100b0c, 0x000000ff, 0x00000000); | |
209 | nv_wr32(pfb, 0x100c04, 0x00000400); | |
210 | ||
211 | /* Now the training script */ | |
212 | r1700 = ram_rd32(fuc, 0x001700); | |
213 | ||
214 | ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); | |
215 | ram_wr32(fuc, 0x611200, 0x3300); | |
216 | ram_wait_vblank(fuc); | |
217 | ram_wait(fuc, 0x611200, 0x00000003, 0x00000000, 500000); | |
218 | ram_mask(fuc, 0x001610, 0x00000083, 0x00000003); | |
219 | ram_mask(fuc, 0x100080, 0x00000020, 0x00000000); | |
220 | ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); | |
221 | ram_wr32(fuc, 0x001700, 0x00000000); | |
222 | ||
223 | ram_train(fuc); | |
224 | ||
225 | /* Reset */ | |
226 | ram_mask(fuc, 0x10f804, 0x80000000, 0x80000000); | |
227 | ram_wr32(fuc, 0x10053c, 0x0); | |
228 | ram_wr32(fuc, 0x100720, train->r_100720); | |
229 | ram_wr32(fuc, 0x1111e0, train->r_1111e0); | |
230 | ram_wr32(fuc, 0x111400, train->r_111400); | |
231 | ram_nuke(fuc, 0x100080); | |
232 | ram_mask(fuc, 0x100080, 0x00000020, 0x00000020); | |
233 | ram_nsec(fuc, 1000); | |
234 | ||
235 | ram_wr32(fuc, 0x001700, r1700); | |
236 | ram_mask(fuc, 0x001610, 0x00000083, 0x00000080); | |
237 | ram_wr32(fuc, 0x611200, 0x3330); | |
238 | ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); | |
239 | ||
240 | ram_exec(fuc, true); | |
241 | ||
242 | ram->base.calc(pfb, clk_current); | |
243 | ram_exec(fuc, true); | |
244 | ||
245 | /* Post-processing, avoids flicker */ | |
246 | nv_mask(pfb, 0x616308, 0x10, 0x10); | |
247 | nv_mask(pfb, 0x616b08, 0x10, 0x10); | |
248 | ||
249 | nva3_clock_post(clk, f); | |
250 | ||
251 | ram_train_result(pfb, result, 64); | |
252 | for (i = 0; i < 64; i++) | |
253 | nv_debug(pfb, "Train: %08x", result[i]); | |
254 | nva3_link_train_calc(result, train); | |
255 | ||
256 | nv_debug(pfb, "Train: %08x %08x %08x", train->r_100720, | |
257 | train->r_1111e0, train->r_111400); | |
258 | ||
259 | kfree(result); | |
260 | ||
261 | train->state = NVA3_TRAIN_DONE; | |
262 | ||
263 | return ret; | |
264 | ||
265 | out: | |
266 | if(ret == -EBUSY) | |
267 | f = NULL; | |
268 | ||
269 | train->state = NVA3_TRAIN_UNSUPPORTED; | |
270 | ||
271 | nva3_clock_post(clk, f); | |
272 | return ret; | |
273 | } | |
274 | ||
275 | int | |
276 | nva3_link_train_init(struct nouveau_fb *pfb) | |
277 | { | |
278 | static const u32 pattern[16] = { | |
279 | 0xaaaaaaaa, 0xcccccccc, 0xdddddddd, 0xeeeeeeee, | |
280 | 0x00000000, 0x11111111, 0x44444444, 0xdddddddd, | |
281 | 0x33333333, 0x55555555, 0x77777777, 0x66666666, | |
282 | 0x99999999, 0x88888888, 0xeeeeeeee, 0xbbbbbbbb, | |
283 | }; | |
284 | struct nouveau_bios *bios = nouveau_bios(pfb); | |
285 | struct nva3_ram *ram = (void *)pfb->ram; | |
286 | struct nva3_ltrain *train = &ram->ltrain; | |
287 | struct nouveau_mem *mem; | |
288 | struct nvbios_M0205E M0205E; | |
289 | u8 ver, hdr, cnt, len; | |
290 | u32 r001700; | |
291 | int ret, i = 0; | |
292 | ||
293 | train->state = NVA3_TRAIN_UNSUPPORTED; | |
294 | ||
295 | /* We support type "5" | |
296 | * XXX: training pattern table appears to be unused for this routine */ | |
297 | if (!nvbios_M0205Ep(bios, i, &ver, &hdr, &cnt, &len, &M0205E)) | |
298 | return -ENOENT; | |
299 | ||
300 | if (M0205E.type != 5) | |
301 | return 0; | |
302 | ||
303 | train->state = NVA3_TRAIN_ONCE; | |
304 | ||
305 | ret = pfb->ram->get(pfb, 0x8000, 0x10000, 0, 0x800, &ram->ltrain.mem); | |
306 | if (ret) | |
307 | return ret; | |
308 | ||
309 | mem = ram->ltrain.mem; | |
310 | ||
311 | nv_wr32(pfb, 0x100538, 0x10000000 | (mem->offset >> 16)); | |
312 | nv_wr32(pfb, 0x1005a8, 0x0000ffff); | |
313 | nv_mask(pfb, 0x10f800, 0x00000001, 0x00000001); | |
314 | ||
315 | for (i = 0; i < 0x30; i++) { | |
316 | nv_wr32(pfb, 0x10f8c0, (i << 8) | i); | |
317 | nv_wr32(pfb, 0x10f900, pattern[i % 16]); | |
318 | } | |
319 | ||
320 | for (i = 0; i < 0x30; i++) { | |
321 | nv_wr32(pfb, 0x10f8e0, (i << 8) | i); | |
322 | nv_wr32(pfb, 0x10f920, pattern[i % 16]); | |
323 | } | |
324 | ||
325 | /* And upload the pattern */ | |
326 | r001700 = nv_rd32(pfb, 0x1700); | |
327 | nv_wr32(pfb, 0x1700, mem->offset >> 16); | |
328 | for (i = 0; i < 16; i++) | |
329 | nv_wr32(pfb, 0x700000 + (i << 2), pattern[i]); | |
330 | for (i = 0; i < 16; i++) | |
331 | nv_wr32(pfb, 0x700100 + (i << 2), pattern[i]); | |
332 | nv_wr32(pfb, 0x1700, r001700); | |
333 | ||
334 | train->r_100720 = nv_rd32(pfb, 0x100720); | |
335 | train->r_1111e0 = nv_rd32(pfb, 0x1111e0); | |
336 | train->r_111400 = nv_rd32(pfb, 0x111400); | |
337 | ||
338 | return 0; | |
339 | } | |
340 | ||
341 | void | |
342 | nva3_link_train_fini(struct nouveau_fb *pfb) | |
343 | { | |
344 | struct nva3_ram *ram = (void *)pfb->ram; | |
345 | ||
346 | if (ram->ltrain.mem) | |
347 | pfb->ram->put(pfb, &ram->ltrain.mem); | |
348 | } | |
349 | ||
bf504b3f RS |
350 | /* |
351 | * RAM reclocking | |
352 | */ | |
353 | #define T(t) cfg->timing_10_##t | |
354 | static int | |
355 | nva3_ram_timing_calc(struct nouveau_fb *pfb, u32 *timing) | |
356 | { | |
357 | struct nva3_ram *ram = (void *)pfb->ram; | |
358 | struct nvbios_ramcfg *cfg = &ram->base.target.bios; | |
359 | int tUNK_base; | |
360 | u32 cur3, cur7, cur8; | |
361 | ||
362 | cur3 = nv_rd32(pfb, 0x10022c); | |
363 | cur7 = nv_rd32(pfb, 0x10023c); | |
364 | cur8 = nv_rd32(pfb, 0x100240); | |
365 | ||
366 | if (T(CWL) == 0) | |
367 | T(CWL) = ((nv_rd32(pfb, 0x100228) & 0x0f000000) >> 24) + 1; | |
368 | ||
369 | tUNK_base = ((cur7 & 0x00ff0000) >> 16) - | |
370 | (cur3 & 0x000000ff) - 1; | |
371 | ||
372 | timing[0] = (T(RP) << 24 | T(RAS) << 16 | T(RFC) << 8 | T(RC)); | |
373 | timing[1] = (T(WR) + 1 + T(CWL)) << 24 | | |
374 | max_t(u8,T(18), 1) << 16 | | |
375 | (T(WTR) + 1 + T(CWL)) << 8 | | |
376 | (5 + T(CL) - T(CWL)); | |
377 | timing[2] = (T(CWL) - 1) << 24 | | |
378 | (T(RRD) << 16) | | |
379 | (T(RCDWR) << 8) | | |
380 | T(RCDRD); | |
381 | timing[3] = (cur3 & 0x00ff0000) | | |
382 | (0x30 + T(CL)) << 24 | | |
383 | (0xb + T(CL)) << 8 | | |
384 | (T(CL) - 1); | |
385 | timing[4] = T(20) << 24 | | |
386 | T(21) << 16 | | |
387 | T(13) << 8 | | |
388 | T(13); | |
389 | timing[5] = T(RFC) << 24 | | |
390 | max_t(u8,T(RCDRD), T(RCDWR)) << 16 | | |
391 | (T(CWL) + 6) << 8 | | |
392 | T(RP); | |
393 | timing[6] = (0x5a + T(CL)) << 16 | | |
394 | (6 - T(CL) + T(CWL)) << 8 | | |
395 | (0x50 + T(CL) - T(CWL)); | |
396 | timing[7] = (cur7 & 0xff000000) | | |
397 | ((tUNK_base + T(CL)) << 16) | | |
398 | 0x202; | |
399 | timing[8] = cur8 & 0xffffff00; | |
400 | ||
401 | nv_debug(pfb, "Entry: 220: %08x %08x %08x %08x\n", | |
402 | timing[0], timing[1], timing[2], timing[3]); | |
403 | nv_debug(pfb, " 230: %08x %08x %08x %08x\n", | |
404 | timing[4], timing[5], timing[6], timing[7]); | |
405 | nv_debug(pfb, " 240: %08x\n", timing[8]); | |
406 | return 0; | |
407 | } | |
408 | #undef T | |
409 | ||
b6a7907f RS |
410 | static void |
411 | nouveau_sddr3_dll_reset(struct nva3_ramfuc *fuc) | |
412 | { | |
413 | ram_mask(fuc, mr[0], 0x100, 0x100); | |
414 | ram_nsec(fuc, 1000); | |
415 | ram_mask(fuc, mr[0], 0x100, 0x000); | |
416 | ram_nsec(fuc, 1000); | |
417 | } | |
418 | ||
419 | static void | |
420 | nouveau_sddr3_dll_disable(struct nva3_ramfuc *fuc, u32 *mr) | |
421 | { | |
422 | u32 mr1_old = ram_rd32(fuc, mr[1]); | |
423 | ||
424 | if (!(mr1_old & 0x1)) { | |
425 | ram_wr32(fuc, 0x1002d4, 0x00000001); | |
426 | ram_wr32(fuc, mr[1], mr[1]); | |
427 | ram_nsec(fuc, 1000); | |
428 | } | |
429 | } | |
430 | ||
431 | static void | |
432 | nva3_ram_lock_pll(struct nva3_ramfuc *fuc, struct nva3_clock_info *mclk) | |
433 | { | |
434 | ram_wr32(fuc, 0x004004, mclk->pll); | |
435 | ram_mask(fuc, 0x004000, 0x00000001, 0x00000001); | |
436 | ram_mask(fuc, 0x004000, 0x00000010, 0x00000000); | |
437 | ram_wait(fuc, 0x004000, 0x00020000, 0x00020000, 64000); | |
438 | ram_mask(fuc, 0x004000, 0x00000010, 0x00000010); | |
439 | } | |
440 | ||
aae95ca7 BS |
441 | static int |
442 | nva3_ram_calc(struct nouveau_fb *pfb, u32 freq) | |
443 | { | |
444 | struct nouveau_bios *bios = nouveau_bios(pfb); | |
445 | struct nva3_ram *ram = (void *)pfb->ram; | |
446 | struct nva3_ramfuc *fuc = &ram->fuc; | |
b6a7907f | 447 | struct nva3_ltrain *train = &ram->ltrain; |
aae95ca7 | 448 | struct nva3_clock_info mclk; |
c378eb74 BS |
449 | struct nouveau_ram_data *next; |
450 | u8 ver, hdr, cnt, len, strap; | |
aae95ca7 | 451 | u32 data; |
b6a7907f | 452 | u32 r004018, r100760, r100da0, r111100, ctrl; |
aae95ca7 | 453 | u32 unk714, unk718, unk71c; |
c378eb74 | 454 | int ret, i; |
bf504b3f | 455 | u32 timing[9]; |
b6a7907f | 456 | bool pll2pll; |
c378eb74 BS |
457 | |
458 | next = &ram->base.target; | |
459 | next->freq = freq; | |
460 | ram->base.next = next; | |
aae95ca7 | 461 | |
7f4b9616 RS |
462 | if (ram->ltrain.state == NVA3_TRAIN_ONCE) |
463 | nva3_link_train(pfb); | |
464 | ||
aae95ca7 | 465 | /* lookup memory config data relevant to the target frequency */ |
c378eb74 | 466 | i = 0; |
b6a7907f RS |
467 | data = nvbios_rammapEm(bios, freq / 1000, &ver, &hdr, &cnt, &len, |
468 | &next->bios); | |
469 | if (!data || ver != 0x10 || hdr < 0x05) { | |
aae95ca7 BS |
470 | nv_error(pfb, "invalid/missing rammap entry\n"); |
471 | return -EINVAL; | |
472 | } | |
473 | ||
474 | /* locate specific data set for the attached memory */ | |
0a8649f1 | 475 | strap = nvbios_ramcfg_index(nv_subdev(pfb)); |
aae95ca7 BS |
476 | if (strap >= cnt) { |
477 | nv_error(pfb, "invalid ramcfg strap\n"); | |
478 | return -EINVAL; | |
479 | } | |
480 | ||
c378eb74 BS |
481 | data = nvbios_rammapSp(bios, data, ver, hdr, cnt, len, strap, |
482 | &ver, &hdr, &next->bios); | |
b6a7907f | 483 | if (!data || ver != 0x10 || hdr < 0x09) { |
aae95ca7 BS |
484 | nv_error(pfb, "invalid/missing ramcfg entry\n"); |
485 | return -EINVAL; | |
486 | } | |
487 | ||
488 | /* lookup memory timings, if bios says they're present */ | |
c378eb74 BS |
489 | if (next->bios.ramcfg_timing != 0xff) { |
490 | data = nvbios_timingEp(bios, next->bios.ramcfg_timing, | |
491 | &ver, &hdr, &cnt, &len, | |
492 | &next->bios); | |
b6a7907f | 493 | if (!data || ver != 0x10 || hdr < 0x17) { |
aae95ca7 BS |
494 | nv_error(pfb, "invalid/missing timing entry\n"); |
495 | return -EINVAL; | |
496 | } | |
aae95ca7 BS |
497 | } |
498 | ||
6a4a47cf | 499 | ret = nva3_pll_info(nouveau_clock(pfb), 0x12, 0x4000, freq, &mclk); |
aae95ca7 BS |
500 | if (ret < 0) { |
501 | nv_error(pfb, "failed mclk calculation\n"); | |
502 | return ret; | |
503 | } | |
504 | ||
bf504b3f RS |
505 | nva3_ram_timing_calc(pfb, timing); |
506 | ||
aae95ca7 | 507 | ret = ram_init(fuc, pfb); |
b6a7907f RS |
508 | if (ret) |
509 | return ret; | |
510 | ||
511 | /* Determine ram-specific MR values */ | |
512 | ram->base.mr[0] = ram_rd32(fuc, mr[0]); | |
513 | ram->base.mr[1] = ram_rd32(fuc, mr[1]); | |
514 | ram->base.mr[2] = ram_rd32(fuc, mr[2]); | |
515 | ||
516 | switch (ram->base.type) { | |
517 | case NV_MEM_TYPE_DDR3: | |
518 | ret = nouveau_sddr3_calc(&ram->base); | |
519 | break; | |
520 | default: | |
521 | ret = -ENOSYS; | |
522 | break; | |
523 | } | |
524 | ||
aae95ca7 BS |
525 | if (ret) |
526 | return ret; | |
527 | ||
528 | /* XXX: where the fuck does 750MHz come from? */ | |
529 | if (freq <= 750000) { | |
530 | r004018 = 0x10000000; | |
531 | r100760 = 0x22222222; | |
b6a7907f | 532 | r100da0 = 0x00000010; |
aae95ca7 BS |
533 | } else { |
534 | r004018 = 0x00000000; | |
535 | r100760 = 0x00000000; | |
b6a7907f | 536 | r100da0 = 0x00000000; |
aae95ca7 BS |
537 | } |
538 | ||
b6a7907f RS |
539 | if (!next->bios.ramcfg_10_DLLoff) |
540 | r004018 |= 0x00004000; | |
541 | ||
542 | /* pll2pll requires to switch to a safe clock first */ | |
aae95ca7 | 543 | ctrl = ram_rd32(fuc, 0x004000); |
b6a7907f | 544 | pll2pll = (!(ctrl & 0x00000008)) && mclk.pll; |
aae95ca7 | 545 | |
b6a7907f | 546 | /* Pre, NVIDIA does this outside the script */ |
c378eb74 | 547 | if (next->bios.ramcfg_10_02_10) { |
aae95ca7 BS |
548 | ram_mask(fuc, 0x111104, 0x00000600, 0x00000000); |
549 | } else { | |
550 | ram_mask(fuc, 0x111100, 0x40000000, 0x40000000); | |
551 | ram_mask(fuc, 0x111104, 0x00000180, 0x00000000); | |
552 | } | |
b6a7907f RS |
553 | /* Always disable this bit during reclock */ |
554 | ram_mask(fuc, 0x100200, 0x00000800, 0x00000000); | |
555 | ||
556 | /* If switching from non-pll to pll, lock before disabling FB */ | |
557 | if (mclk.pll && !pll2pll) { | |
558 | ram_mask(fuc, 0x004128, 0x003f3141, mclk.clk | 0x00000101); | |
559 | nva3_ram_lock_pll(fuc, &mclk); | |
560 | } | |
561 | ||
562 | /* Start with disabling some CRTCs and PFIFO? */ | |
563 | ram_wait_vblank(fuc); | |
564 | ram_wr32(fuc, 0x611200, 0x3300); | |
565 | ram_mask(fuc, 0x002504, 0x1, 0x1); | |
566 | ram_nsec(fuc, 10000); | |
567 | ram_wait(fuc, 0x002504, 0x10, 0x10, 20000); /* XXX: or longer? */ | |
568 | ram_block(fuc); | |
569 | ram_nsec(fuc, 2000); | |
570 | ||
aae95ca7 | 571 | |
c378eb74 | 572 | if (!next->bios.ramcfg_10_02_10) |
b6a7907f | 573 | ram_mask(fuc, 0x111100, 0x04020000, 0x04020000); /*XXX*/ |
aae95ca7 | 574 | |
b6a7907f RS |
575 | /* If we're disabling the DLL, do it now */ |
576 | if (next->bios.ramcfg_10_DLLoff) | |
577 | nouveau_sddr3_dll_disable(fuc, ram->base.mr); | |
578 | ||
579 | /* Brace RAM for impact */ | |
aae95ca7 BS |
580 | ram_wr32(fuc, 0x1002d4, 0x00000001); |
581 | ram_wr32(fuc, 0x1002d0, 0x00000001); | |
582 | ram_wr32(fuc, 0x1002d0, 0x00000001); | |
583 | ram_wr32(fuc, 0x100210, 0x00000000); | |
584 | ram_wr32(fuc, 0x1002dc, 0x00000001); | |
585 | ram_nsec(fuc, 2000); | |
586 | ||
b6a7907f RS |
587 | if (nv_device(pfb)->chipset == 0xa3 && freq <= 500000) |
588 | ram_mask(fuc, 0x100700, 0x00000006, 0x00000006); | |
589 | ||
590 | /* Fiddle with clocks */ | |
591 | /* There's 4 scenario's | |
592 | * pll->pll: first switch to a 324MHz clock, set up new PLL, switch | |
593 | * clk->pll: Set up new PLL, switch | |
594 | * pll->clk: Set up clock, switch | |
595 | * clk->clk: Overwrite ctrl and other bits, switch */ | |
596 | ||
597 | /* Switch to regular clock - 324MHz */ | |
598 | if (pll2pll) { | |
599 | ram_mask(fuc, 0x004000, 0x00000004, 0x00000004); | |
600 | ram_mask(fuc, 0x004168, 0x003f3141, 0x00083101); | |
601 | ram_mask(fuc, 0x004000, 0x00000008, 0x00000008); | |
aae95ca7 BS |
602 | ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); |
603 | ram_wr32(fuc, 0x004018, 0x00001000); | |
b6a7907f RS |
604 | nva3_ram_lock_pll(fuc, &mclk); |
605 | } | |
606 | ||
607 | if (mclk.pll) { | |
608 | ram_mask(fuc, 0x004000, 0x00000105, 0x00000105); | |
609 | ram_wr32(fuc, 0x004018, 0x00001000 | r004018); | |
610 | ram_wr32(fuc, 0x100da0, r100da0); | |
611 | } else { | |
612 | ram_mask(fuc, 0x004168, 0x003f3141, mclk.clk | 0x00000101); | |
613 | ram_mask(fuc, 0x004000, 0x00000108, 0x00000008); | |
aae95ca7 | 614 | ram_mask(fuc, 0x1110e0, 0x00088000, 0x00088000); |
b6a7907f RS |
615 | ram_wr32(fuc, 0x004018, 0x00009000 | r004018); |
616 | ram_wr32(fuc, 0x100da0, r100da0); | |
aae95ca7 | 617 | } |
b6a7907f | 618 | ram_nsec(fuc, 20000); |
aae95ca7 | 619 | |
c378eb74 BS |
620 | if (next->bios.rammap_10_04_08) { |
621 | ram_wr32(fuc, 0x1005a0, next->bios.ramcfg_10_06 << 16 | | |
622 | next->bios.ramcfg_10_05 << 8 | | |
623 | next->bios.ramcfg_10_05); | |
624 | ram_wr32(fuc, 0x1005a4, next->bios.ramcfg_10_08 << 8 | | |
625 | next->bios.ramcfg_10_07); | |
626 | ram_wr32(fuc, 0x10f804, next->bios.ramcfg_10_09_f0 << 20 | | |
627 | next->bios.ramcfg_10_03_0f << 16 | | |
628 | next->bios.ramcfg_10_09_0f | | |
629 | 0x80000000); | |
aae95ca7 BS |
630 | ram_mask(fuc, 0x10053c, 0x00001000, 0x00000000); |
631 | } else { | |
b6a7907f RS |
632 | if (train->state == NVA3_TRAIN_DONE) { |
633 | ram_wr32(fuc, 0x100080, 0x1020); | |
634 | ram_mask(fuc, 0x111400, 0xffffffff, train->r_111400); | |
635 | ram_mask(fuc, 0x1111e0, 0xffffffff, train->r_1111e0); | |
636 | ram_mask(fuc, 0x100720, 0xffffffff, train->r_100720); | |
637 | } | |
aae95ca7 BS |
638 | ram_mask(fuc, 0x10053c, 0x00001000, 0x00001000); |
639 | ram_mask(fuc, 0x10f804, 0x80000000, 0x00000000); | |
640 | ram_mask(fuc, 0x100760, 0x22222222, r100760); | |
641 | ram_mask(fuc, 0x1007a0, 0x22222222, r100760); | |
642 | ram_mask(fuc, 0x1007e0, 0x22222222, r100760); | |
643 | } | |
644 | ||
b6a7907f RS |
645 | if (nv_device(pfb)->chipset == 0xa3 && freq > 500000) { |
646 | ram_mask(fuc, 0x100700, 0x00000006, 0x00000000); | |
647 | } | |
648 | ||
649 | /* Final switch */ | |
aae95ca7 BS |
650 | if (mclk.pll) { |
651 | ram_mask(fuc, 0x1110e0, 0x00088000, 0x00011000); | |
b6a7907f | 652 | ram_mask(fuc, 0x004000, 0x00000008, 0x00000000); |
aae95ca7 BS |
653 | } |
654 | ||
aae95ca7 BS |
655 | ram_wr32(fuc, 0x1002dc, 0x00000000); |
656 | ram_wr32(fuc, 0x1002d4, 0x00000001); | |
657 | ram_wr32(fuc, 0x100210, 0x80000000); | |
b6a7907f | 658 | ram_nsec(fuc, 2000); |
aae95ca7 | 659 | |
b6a7907f RS |
660 | /* Set RAM MR parameters and timings */ |
661 | ram_wr32(fuc, mr[2], ram->base.mr[2]); | |
662 | ram_nsec(fuc, 1000); | |
663 | ram_wr32(fuc, mr[1], ram->base.mr[1]); | |
aae95ca7 | 664 | ram_nsec(fuc, 1000); |
b6a7907f | 665 | ram_wr32(fuc, mr[0], ram->base.mr[0]); |
aae95ca7 BS |
666 | ram_nsec(fuc, 1000); |
667 | ||
bf504b3f RS |
668 | ram_wr32(fuc, 0x100220[3], timing[3]); |
669 | ram_wr32(fuc, 0x100220[1], timing[1]); | |
670 | ram_wr32(fuc, 0x100220[6], timing[6]); | |
671 | ram_wr32(fuc, 0x100220[7], timing[7]); | |
672 | ram_wr32(fuc, 0x100220[2], timing[2]); | |
673 | ram_wr32(fuc, 0x100220[4], timing[4]); | |
674 | ram_wr32(fuc, 0x100220[5], timing[5]); | |
675 | ram_wr32(fuc, 0x100220[0], timing[0]); | |
676 | ram_wr32(fuc, 0x100220[8], timing[8]); | |
677 | ||
678 | /* Misc */ | |
c378eb74 | 679 | ram_mask(fuc, 0x100200, 0x00001000, !next->bios.ramcfg_10_02_08 << 12); |
aae95ca7 | 680 | |
b6a7907f RS |
681 | /* XXX: A lot of "chipset"/"ram type" specific stuff...? */ |
682 | unk714 = ram_rd32(fuc, 0x100714) & ~0xf0000130; | |
683 | unk718 = ram_rd32(fuc, 0x100718) & ~0x00000100; | |
684 | unk71c = ram_rd32(fuc, 0x10071c) & ~0x00000100; | |
685 | r111100 = ram_rd32(fuc, 0x111100) & ~0x3a800000; | |
686 | ||
687 | if (next->bios.ramcfg_10_02_04) { | |
688 | switch (ram->base.type) { | |
689 | case NV_MEM_TYPE_DDR3: | |
690 | if (nv_device(pfb)->chipset != 0xa8) | |
691 | r111100 |= 0x00000004; | |
692 | /* no break */ | |
693 | default: | |
694 | break; | |
695 | } | |
696 | } else { | |
697 | switch (ram->base.type) { | |
698 | case NV_MEM_TYPE_DDR3: | |
699 | if (nv_device(pfb)->chipset == 0xa8) { | |
700 | r111100 |= 0x08000000; | |
701 | } else { | |
702 | r111100 &= ~0x00000004; | |
703 | r111100 |= 0x12800000; | |
704 | } | |
705 | unk714 |= 0x00000010; | |
706 | break; | |
707 | default: | |
708 | break; | |
709 | } | |
710 | } | |
711 | ||
712 | unk714 |= (next->bios.ramcfg_10_04_01) << 8; | |
713 | ||
c378eb74 | 714 | if (next->bios.ramcfg_10_02_20) |
aae95ca7 | 715 | unk714 |= 0xf0000000; |
b6a7907f RS |
716 | if (next->bios.ramcfg_10_02_02) |
717 | unk718 |= 0x00000100; | |
c378eb74 | 718 | if (next->bios.ramcfg_10_02_01) |
aae95ca7 | 719 | unk71c |= 0x00000100; |
b6a7907f RS |
720 | if (next->bios.timing_10_24 != 0xff) { |
721 | unk718 &= ~0xf0000000; | |
722 | unk718 |= next->bios.timing_10_24 << 28; | |
723 | } | |
724 | if (next->bios.ramcfg_10_02_10) | |
725 | r111100 &= ~0x04020000; | |
aae95ca7 | 726 | |
b6a7907f RS |
727 | ram_mask(fuc, 0x100714, 0xffffffff, unk714); |
728 | ram_mask(fuc, 0x10071c, 0xffffffff, unk71c); | |
729 | ram_mask(fuc, 0x100718, 0xffffffff, unk718); | |
730 | ram_mask(fuc, 0x111100, 0xffffffff, r111100); | |
aae95ca7 | 731 | |
b6a7907f RS |
732 | /* Reset DLL */ |
733 | if (!next->bios.ramcfg_10_DLLoff) | |
734 | nouveau_sddr3_dll_reset(fuc); | |
aae95ca7 | 735 | |
b6a7907f | 736 | ram_nsec(fuc, 14000); |
aae95ca7 | 737 | |
b6a7907f | 738 | ram_wr32(fuc, 0x100264, 0x1); |
aae95ca7 | 739 | ram_nsec(fuc, 2000); |
aae95ca7 | 740 | |
b6a7907f RS |
741 | ram_nuke(fuc, 0x100700); |
742 | ram_mask(fuc, 0x100700, 0x01000000, 0x01000000); | |
743 | ram_mask(fuc, 0x100700, 0x01000000, 0x00000000); | |
744 | ||
745 | /* Re-enable FB */ | |
746 | ram_unblock(fuc); | |
747 | ram_wr32(fuc, 0x611200, 0x3330); | |
748 | ||
749 | /* Post fiddlings */ | |
c378eb74 | 750 | if (next->bios.rammap_10_04_02) |
aae95ca7 | 751 | ram_mask(fuc, 0x100200, 0x00000800, 0x00000800); |
c378eb74 | 752 | if (next->bios.ramcfg_10_02_10) { |
aae95ca7 BS |
753 | ram_mask(fuc, 0x111104, 0x00000180, 0x00000180); |
754 | ram_mask(fuc, 0x111100, 0x40000000, 0x00000000); | |
755 | } else { | |
756 | ram_mask(fuc, 0x111104, 0x00000600, 0x00000600); | |
757 | } | |
758 | ||
759 | if (mclk.pll) { | |
760 | ram_mask(fuc, 0x004168, 0x00000001, 0x00000000); | |
761 | ram_mask(fuc, 0x004168, 0x00000100, 0x00000000); | |
762 | } else { | |
763 | ram_mask(fuc, 0x004000, 0x00000001, 0x00000000); | |
764 | ram_mask(fuc, 0x004128, 0x00000001, 0x00000000); | |
765 | ram_mask(fuc, 0x004128, 0x00000100, 0x00000000); | |
766 | } | |
767 | ||
768 | return 0; | |
769 | } | |
770 | ||
771 | static int | |
772 | nva3_ram_prog(struct nouveau_fb *pfb) | |
773 | { | |
774 | struct nouveau_device *device = nv_device(pfb); | |
775 | struct nva3_ram *ram = (void *)pfb->ram; | |
776 | struct nva3_ramfuc *fuc = &ram->fuc; | |
b6a7907f RS |
777 | bool exec = nouveau_boolopt(device->cfgopt, "NvMemExec", true); |
778 | ||
779 | if (exec) { | |
780 | nv_mask(pfb, 0x001534, 0x2, 0x2); | |
781 | ||
782 | ram_exec(fuc, true); | |
783 | ||
784 | /* Post-processing, avoids flicker */ | |
785 | nv_mask(pfb, 0x002504, 0x1, 0x0); | |
786 | nv_mask(pfb, 0x001534, 0x2, 0x0); | |
787 | ||
788 | nv_mask(pfb, 0x616308, 0x10, 0x10); | |
789 | nv_mask(pfb, 0x616b08, 0x10, 0x10); | |
790 | } else { | |
791 | ram_exec(fuc, false); | |
792 | } | |
aae95ca7 BS |
793 | return 0; |
794 | } | |
795 | ||
796 | static void | |
797 | nva3_ram_tidy(struct nouveau_fb *pfb) | |
798 | { | |
799 | struct nva3_ram *ram = (void *)pfb->ram; | |
800 | struct nva3_ramfuc *fuc = &ram->fuc; | |
801 | ram_exec(fuc, false); | |
802 | } | |
803 | ||
804 | static int | |
805 | nva3_ram_init(struct nouveau_object *object) | |
806 | { | |
807 | struct nouveau_fb *pfb = (void *)object->parent; | |
808 | struct nva3_ram *ram = (void *)object; | |
7f4b9616 | 809 | int ret; |
aae95ca7 BS |
810 | |
811 | ret = nouveau_ram_init(&ram->base); | |
812 | if (ret) | |
813 | return ret; | |
814 | ||
7f4b9616 RS |
815 | nva3_link_train_init(pfb); |
816 | ||
817 | return 0; | |
818 | } | |
819 | ||
820 | static int | |
821 | nva3_ram_fini(struct nouveau_object *object, bool suspend) | |
822 | { | |
823 | struct nouveau_fb *pfb = (void *)object->parent; | |
824 | ||
825 | if (!suspend) | |
826 | nva3_link_train_fini(pfb); | |
aae95ca7 BS |
827 | |
828 | return 0; | |
829 | } | |
830 | ||
75faef78 BS |
831 | static int |
832 | nva3_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, | |
833 | struct nouveau_oclass *oclass, void *data, u32 datasize, | |
834 | struct nouveau_object **pobject) | |
835 | { | |
836 | struct nva3_ram *ram; | |
aae95ca7 | 837 | int ret, i; |
75faef78 BS |
838 | |
839 | ret = nv50_ram_create(parent, engine, oclass, &ram); | |
840 | *pobject = nv_object(ram); | |
841 | if (ret) | |
842 | return ret; | |
843 | ||
aae95ca7 BS |
844 | switch (ram->base.type) { |
845 | case NV_MEM_TYPE_DDR3: | |
846 | ram->base.calc = nva3_ram_calc; | |
847 | ram->base.prog = nva3_ram_prog; | |
848 | ram->base.tidy = nva3_ram_tidy; | |
849 | break; | |
850 | default: | |
851 | nv_warn(ram, "reclocking of this ram type unsupported\n"); | |
852 | return 0; | |
853 | } | |
854 | ||
7f4b9616 RS |
855 | ram->fuc.r_0x001610 = ramfuc_reg(0x001610); |
856 | ram->fuc.r_0x001700 = ramfuc_reg(0x001700); | |
b6a7907f | 857 | ram->fuc.r_0x002504 = ramfuc_reg(0x002504); |
aae95ca7 BS |
858 | ram->fuc.r_0x004000 = ramfuc_reg(0x004000); |
859 | ram->fuc.r_0x004004 = ramfuc_reg(0x004004); | |
860 | ram->fuc.r_0x004018 = ramfuc_reg(0x004018); | |
861 | ram->fuc.r_0x004128 = ramfuc_reg(0x004128); | |
862 | ram->fuc.r_0x004168 = ramfuc_reg(0x004168); | |
7f4b9616 | 863 | ram->fuc.r_0x100080 = ramfuc_reg(0x100080); |
aae95ca7 BS |
864 | ram->fuc.r_0x100200 = ramfuc_reg(0x100200); |
865 | ram->fuc.r_0x100210 = ramfuc_reg(0x100210); | |
866 | for (i = 0; i < 9; i++) | |
867 | ram->fuc.r_0x100220[i] = ramfuc_reg(0x100220 + (i * 4)); | |
b6a7907f | 868 | ram->fuc.r_0x100264 = ramfuc_reg(0x100264); |
aae95ca7 BS |
869 | ram->fuc.r_0x1002d0 = ramfuc_reg(0x1002d0); |
870 | ram->fuc.r_0x1002d4 = ramfuc_reg(0x1002d4); | |
871 | ram->fuc.r_0x1002dc = ramfuc_reg(0x1002dc); | |
872 | ram->fuc.r_0x10053c = ramfuc_reg(0x10053c); | |
873 | ram->fuc.r_0x1005a0 = ramfuc_reg(0x1005a0); | |
874 | ram->fuc.r_0x1005a4 = ramfuc_reg(0x1005a4); | |
b6a7907f | 875 | ram->fuc.r_0x100700 = ramfuc_reg(0x100700); |
aae95ca7 BS |
876 | ram->fuc.r_0x100714 = ramfuc_reg(0x100714); |
877 | ram->fuc.r_0x100718 = ramfuc_reg(0x100718); | |
878 | ram->fuc.r_0x10071c = ramfuc_reg(0x10071c); | |
7f4b9616 | 879 | ram->fuc.r_0x100720 = ramfuc_reg(0x100720); |
a4073189 RS |
880 | ram->fuc.r_0x100760 = ramfuc_stride(0x100760, 4, ram->base.part_mask); |
881 | ram->fuc.r_0x1007a0 = ramfuc_stride(0x1007a0, 4, ram->base.part_mask); | |
882 | ram->fuc.r_0x1007e0 = ramfuc_stride(0x1007e0, 4, ram->base.part_mask); | |
b6a7907f | 883 | ram->fuc.r_0x100da0 = ramfuc_stride(0x100da0, 4, ram->base.part_mask); |
aae95ca7 | 884 | ram->fuc.r_0x10f804 = ramfuc_reg(0x10f804); |
a4073189 | 885 | ram->fuc.r_0x1110e0 = ramfuc_stride(0x1110e0, 4, ram->base.part_mask); |
aae95ca7 BS |
886 | ram->fuc.r_0x111100 = ramfuc_reg(0x111100); |
887 | ram->fuc.r_0x111104 = ramfuc_reg(0x111104); | |
7f4b9616 RS |
888 | ram->fuc.r_0x1111e0 = ramfuc_reg(0x1111e0); |
889 | ram->fuc.r_0x111400 = ramfuc_reg(0x111400); | |
aae95ca7 BS |
890 | ram->fuc.r_0x611200 = ramfuc_reg(0x611200); |
891 | ||
892 | if (ram->base.ranks > 1) { | |
893 | ram->fuc.r_mr[0] = ramfuc_reg2(0x1002c0, 0x1002c8); | |
894 | ram->fuc.r_mr[1] = ramfuc_reg2(0x1002c4, 0x1002cc); | |
895 | ram->fuc.r_mr[2] = ramfuc_reg2(0x1002e0, 0x1002e8); | |
896 | ram->fuc.r_mr[3] = ramfuc_reg2(0x1002e4, 0x1002ec); | |
897 | } else { | |
898 | ram->fuc.r_mr[0] = ramfuc_reg(0x1002c0); | |
899 | ram->fuc.r_mr[1] = ramfuc_reg(0x1002c4); | |
900 | ram->fuc.r_mr[2] = ramfuc_reg(0x1002e0); | |
901 | ram->fuc.r_mr[3] = ramfuc_reg(0x1002e4); | |
902 | } | |
903 | ||
75faef78 BS |
904 | return 0; |
905 | } | |
906 | ||
907 | struct nouveau_oclass | |
908 | nva3_ram_oclass = { | |
909 | .ofuncs = &(struct nouveau_ofuncs) { | |
910 | .ctor = nva3_ram_ctor, | |
911 | .dtor = _nouveau_ram_dtor, | |
aae95ca7 | 912 | .init = nva3_ram_init, |
7f4b9616 | 913 | .fini = nva3_ram_fini, |
75faef78 BS |
914 | }, |
915 | }; |