Commit | Line | Data |
---|---|---|
6ee73861 BS |
1 | /* |
2 | * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. | |
3 | * Copyright 2005 Stephane Marchesin | |
4 | * | |
5 | * The Weather Channel (TM) funded Tungsten Graphics to develop the | |
6 | * initial release of the Radeon 8500 driver under the XFree86 license. | |
7 | * This notice must be preserved. | |
8 | * | |
9 | * Permission is hereby granted, free of charge, to any person obtaining a | |
10 | * copy of this software and associated documentation files (the "Software"), | |
11 | * to deal in the Software without restriction, including without limitation | |
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
13 | * and/or sell copies of the Software, and to permit persons to whom the | |
14 | * Software is furnished to do so, subject to the following conditions: | |
15 | * | |
16 | * The above copyright notice and this permission notice (including the next | |
17 | * paragraph) shall be included in all copies or substantial portions of the | |
18 | * Software. | |
19 | * | |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
23 | * THE AUTHORS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
26 | * DEALINGS IN THE SOFTWARE. | |
27 | * | |
28 | * Authors: | |
e6084257 RS |
29 | * Ben Skeggs <bskeggs@redhat.com> |
30 | * Roy Spliet <r.spliet@student.tudelft.nl> | |
6ee73861 BS |
31 | */ |
32 | ||
33 | ||
34 | #include "drmP.h" | |
35 | #include "drm.h" | |
36 | #include "drm_sarea.h" | |
6ee73861 | 37 | |
cbab95db FJ |
38 | #include "nouveau_drv.h" |
39 | #include "nouveau_pm.h" | |
6ee73861 | 40 | |
fd99fd61 BS |
41 | static int |
42 | nv40_mem_timing_calc(struct drm_device *dev, u32 freq, | |
43 | struct nouveau_pm_tbl_entry *e, u8 len, | |
44 | struct nouveau_pm_memtiming *boot, | |
45 | struct nouveau_pm_memtiming *t) | |
ddb20055 | 46 | { |
c7c039fd | 47 | t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); |
9a782488 RS |
48 | |
49 | /* XXX: I don't trust the -1's and +1's... they must come | |
50 | * from somewhere! */ | |
c7c039fd RS |
51 | t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 | |
52 | 1 << 16 | | |
53 | (e->tWTR + 2 + (t->tCWL - 1)) << 8 | | |
54 | (e->tCL + 2 - (t->tCWL - 1)); | |
55 | ||
56 | t->reg[2] = 0x20200000 | | |
57 | ((t->tCWL - 1) << 24 | | |
58 | e->tRRD << 16 | | |
59 | e->tRCDWR << 8 | | |
60 | e->tRCDRD); | |
61 | ||
62 | NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", t->id, | |
63 | t->reg[0], t->reg[1], t->reg[2]); | |
fd99fd61 | 64 | return 0; |
9a782488 RS |
65 | } |
66 | ||
fd99fd61 BS |
67 | static int |
68 | nv50_mem_timing_calc(struct drm_device *dev, u32 freq, | |
69 | struct nouveau_pm_tbl_entry *e, u8 len, | |
70 | struct nouveau_pm_memtiming *boot, | |
71 | struct nouveau_pm_memtiming *t) | |
ddb20055 | 72 | { |
fd99fd61 | 73 | struct bit_entry P; |
c7c039fd | 74 | uint8_t unk18 = 1, unk20 = 0, unk21 = 0, tmp7_3; |
9a782488 | 75 | |
fd99fd61 BS |
76 | if (bit_table(dev, 'P', &P)) |
77 | return -EINVAL; | |
78 | ||
79 | switch (min(len, (u8) 22)) { | |
9a782488 RS |
80 | case 22: |
81 | unk21 = e->tUNK_21; | |
82 | case 21: | |
83 | unk20 = e->tUNK_20; | |
84 | case 20: | |
bfb31465 | 85 | if (e->tCWL > 0) |
c7c039fd | 86 | t->tCWL = e->tCWL; |
9a782488 RS |
87 | case 19: |
88 | unk18 = e->tUNK_18; | |
89 | break; | |
90 | } | |
91 | ||
c7c039fd | 92 | t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC); |
9a782488 | 93 | |
c7c039fd | 94 | t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 | |
bfb31465 | 95 | max(unk18, (u8) 1) << 16 | |
c7c039fd | 96 | (e->tWTR + 2 + (t->tCWL - 1)) << 8; |
bfb31465 | 97 | |
c7c039fd RS |
98 | t->reg[2] = ((t->tCWL - 1) << 24 | |
99 | e->tRRD << 16 | | |
100 | e->tRCDWR << 8 | | |
101 | e->tRCDRD); | |
ddb20055 | 102 | |
c7c039fd | 103 | t->reg[4] = e->tUNK_13 << 8 | e->tUNK_13; |
9a782488 | 104 | |
c7c039fd | 105 | t->reg[5] = (e->tRFC << 24 | max(e->tRCDRD, e->tRCDWR) << 16 | e->tRP); |
bfb31465 | 106 | |
c7c039fd | 107 | t->reg[8] = boot->reg[8] & 0xffffff00; |
9a782488 | 108 | |
fd99fd61 | 109 | if (P.version == 1) { |
c7c039fd | 110 | t->reg[1] |= (e->tCL + 2 - (t->tCWL - 1)); |
ddb20055 | 111 | |
c7c039fd RS |
112 | t->reg[3] = (0x14 + e->tCL) << 24 | |
113 | 0x16 << 16 | | |
114 | (e->tCL - 1) << 8 | | |
115 | (e->tCL - 1); | |
ddb20055 | 116 | |
c7c039fd | 117 | t->reg[4] |= boot->reg[4] & 0xffff0000; |
ddb20055 | 118 | |
c7c039fd RS |
119 | t->reg[6] = (0x33 - t->tCWL) << 16 | |
120 | t->tCWL << 8 | | |
121 | (0x2e + e->tCL - t->tCWL); | |
ddb20055 | 122 | |
c7c039fd | 123 | t->reg[7] = 0x4000202 | (e->tCL - 1) << 16; |
bfb31465 RS |
124 | |
125 | /* XXX: P.version == 1 only has DDR2 and GDDR3? */ | |
861d2107 | 126 | if (nvfb_vram_type(dev) == NV_MEM_TYPE_DDR2) { |
c7c039fd RS |
127 | t->reg[5] |= (e->tCL + 3) << 8; |
128 | t->reg[6] |= (t->tCWL - 2) << 8; | |
129 | t->reg[8] |= (e->tCL - 4); | |
bfb31465 | 130 | } else { |
c7c039fd RS |
131 | t->reg[5] |= (e->tCL + 2) << 8; |
132 | t->reg[6] |= t->tCWL << 8; | |
133 | t->reg[8] |= (e->tCL - 2); | |
bfb31465 | 134 | } |
9a782488 | 135 | } else { |
c7c039fd | 136 | t->reg[1] |= (5 + e->tCL - (t->tCWL)); |
bfb31465 RS |
137 | |
138 | /* XXX: 0xb? 0x30? */ | |
c7c039fd RS |
139 | t->reg[3] = (0x30 + e->tCL) << 24 | |
140 | (boot->reg[3] & 0x00ff0000)| | |
141 | (0xb + e->tCL) << 8 | | |
142 | (e->tCL - 1); | |
bfb31465 | 143 | |
c7c039fd | 144 | t->reg[4] |= (unk20 << 24 | unk21 << 16); |
bfb31465 | 145 | |
9a782488 | 146 | /* XXX: +6? */ |
c7c039fd | 147 | t->reg[5] |= (t->tCWL + 6) << 8; |
9a782488 | 148 | |
c7c039fd RS |
149 | t->reg[6] = (0x5a + e->tCL) << 16 | |
150 | (6 - e->tCL + t->tCWL) << 8 | | |
151 | (0x50 + e->tCL - t->tCWL); | |
bfb31465 | 152 | |
c7c039fd RS |
153 | tmp7_3 = (boot->reg[7] & 0xff000000) >> 24; |
154 | t->reg[7] = (tmp7_3 << 24) | | |
155 | ((tmp7_3 - 6 + e->tCL) << 16) | | |
156 | 0x202; | |
9a782488 RS |
157 | } |
158 | ||
c7c039fd RS |
159 | NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", t->id, |
160 | t->reg[0], t->reg[1], t->reg[2], t->reg[3]); | |
9a782488 | 161 | NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", |
c7c039fd RS |
162 | t->reg[4], t->reg[5], t->reg[6], t->reg[7]); |
163 | NV_DEBUG(dev, " 240: %08x\n", t->reg[8]); | |
fd99fd61 | 164 | return 0; |
9a782488 RS |
165 | } |
166 | ||
fd99fd61 BS |
167 | static int |
168 | nvc0_mem_timing_calc(struct drm_device *dev, u32 freq, | |
169 | struct nouveau_pm_tbl_entry *e, u8 len, | |
170 | struct nouveau_pm_memtiming *boot, | |
171 | struct nouveau_pm_memtiming *t) | |
ddb20055 | 172 | { |
c7c039fd RS |
173 | if (e->tCWL > 0) |
174 | t->tCWL = e->tCWL; | |
bfb31465 | 175 | |
c7c039fd RS |
176 | t->reg[0] = (e->tRP << 24 | (e->tRAS & 0x7f) << 17 | |
177 | e->tRFC << 8 | e->tRC); | |
ddb20055 | 178 | |
c7c039fd RS |
179 | t->reg[1] = (boot->reg[1] & 0xff000000) | |
180 | (e->tRCDWR & 0x0f) << 20 | | |
181 | (e->tRCDRD & 0x0f) << 14 | | |
e6084257 | 182 | (t->tCWL << 7) | |
c7c039fd | 183 | (e->tCL & 0x0f); |
ddb20055 | 184 | |
c7c039fd RS |
185 | t->reg[2] = (boot->reg[2] & 0xff0000ff) | |
186 | e->tWR << 16 | e->tWTR << 8; | |
ddb20055 | 187 | |
e6084257 | 188 | t->reg[3] = (e->tUNK_20 & 0x1f) << 9 | |
c7c039fd RS |
189 | (e->tUNK_21 & 0xf) << 5 | |
190 | (e->tUNK_13 & 0x1f); | |
ddb20055 | 191 | |
c7c039fd RS |
192 | t->reg[4] = (boot->reg[4] & 0xfff00fff) | |
193 | (e->tRRD&0x1f) << 15; | |
ddb20055 | 194 | |
c7c039fd RS |
195 | NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", t->id, |
196 | t->reg[0], t->reg[1], t->reg[2], t->reg[3]); | |
197 | NV_DEBUG(dev, " 2a0: %08x\n", t->reg[4]); | |
fd99fd61 | 198 | return 0; |
bfb31465 RS |
199 | } |
200 | ||
c7c039fd RS |
201 | /** |
202 | * MR generation methods | |
203 | */ | |
204 | ||
fd99fd61 BS |
205 | static int |
206 | nouveau_mem_ddr2_mr(struct drm_device *dev, u32 freq, | |
207 | struct nouveau_pm_tbl_entry *e, u8 len, | |
208 | struct nouveau_pm_memtiming *boot, | |
209 | struct nouveau_pm_memtiming *t) | |
c7c039fd RS |
210 | { |
211 | t->drive_strength = 0; | |
fd99fd61 | 212 | if (len < 15) { |
c7c039fd RS |
213 | t->odt = boot->odt; |
214 | } else { | |
215 | t->odt = e->RAM_FT1 & 0x07; | |
216 | } | |
217 | ||
218 | if (e->tCL >= NV_MEM_CL_DDR2_MAX) { | |
219 | NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); | |
fd99fd61 | 220 | return -ERANGE; |
c7c039fd RS |
221 | } |
222 | ||
223 | if (e->tWR >= NV_MEM_WR_DDR2_MAX) { | |
224 | NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); | |
fd99fd61 | 225 | return -ERANGE; |
c7c039fd RS |
226 | } |
227 | ||
228 | if (t->odt > 3) { | |
229 | NV_WARN(dev, "(%u) Invalid odt value, assuming disabled: %x", | |
230 | t->id, t->odt); | |
231 | t->odt = 0; | |
232 | } | |
233 | ||
234 | t->mr[0] = (boot->mr[0] & 0x100f) | | |
235 | (e->tCL) << 4 | | |
236 | (e->tWR - 1) << 9; | |
237 | t->mr[1] = (boot->mr[1] & 0x101fbb) | | |
238 | (t->odt & 0x1) << 2 | | |
239 | (t->odt & 0x2) << 5; | |
240 | ||
241 | NV_DEBUG(dev, "(%u) MR: %08x", t->id, t->mr[0]); | |
fd99fd61 | 242 | return 0; |
c7c039fd RS |
243 | } |
244 | ||
245 | uint8_t nv_mem_wr_lut_ddr3[NV_MEM_WR_DDR3_MAX] = { | |
246 | 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0}; | |
247 | ||
fd99fd61 BS |
248 | static int |
249 | nouveau_mem_ddr3_mr(struct drm_device *dev, u32 freq, | |
250 | struct nouveau_pm_tbl_entry *e, u8 len, | |
251 | struct nouveau_pm_memtiming *boot, | |
252 | struct nouveau_pm_memtiming *t) | |
c7c039fd RS |
253 | { |
254 | u8 cl = e->tCL - 4; | |
255 | ||
256 | t->drive_strength = 0; | |
fd99fd61 | 257 | if (len < 15) { |
c7c039fd RS |
258 | t->odt = boot->odt; |
259 | } else { | |
260 | t->odt = e->RAM_FT1 & 0x07; | |
261 | } | |
262 | ||
263 | if (e->tCL >= NV_MEM_CL_DDR3_MAX || e->tCL < 4) { | |
264 | NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); | |
fd99fd61 | 265 | return -ERANGE; |
c7c039fd RS |
266 | } |
267 | ||
268 | if (e->tWR >= NV_MEM_WR_DDR3_MAX || e->tWR < 4) { | |
269 | NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); | |
fd99fd61 | 270 | return -ERANGE; |
c7c039fd RS |
271 | } |
272 | ||
273 | if (e->tCWL < 5) { | |
274 | NV_WARN(dev, "(%u) Invalid tCWL: %u", t->id, e->tCWL); | |
fd99fd61 | 275 | return -ERANGE; |
c7c039fd RS |
276 | } |
277 | ||
278 | t->mr[0] = (boot->mr[0] & 0x180b) | | |
279 | /* CAS */ | |
280 | (cl & 0x7) << 4 | | |
281 | (cl & 0x8) >> 1 | | |
282 | (nv_mem_wr_lut_ddr3[e->tWR]) << 9; | |
283 | t->mr[1] = (boot->mr[1] & 0x101dbb) | | |
284 | (t->odt & 0x1) << 2 | | |
285 | (t->odt & 0x2) << 5 | | |
286 | (t->odt & 0x4) << 7; | |
287 | t->mr[2] = (boot->mr[2] & 0x20ffb7) | (e->tCWL - 5) << 3; | |
288 | ||
289 | NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[2]); | |
fd99fd61 | 290 | return 0; |
c7c039fd RS |
291 | } |
292 | ||
293 | uint8_t nv_mem_cl_lut_gddr3[NV_MEM_CL_GDDR3_MAX] = { | |
294 | 0, 0, 0, 0, 4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11}; | |
295 | uint8_t nv_mem_wr_lut_gddr3[NV_MEM_WR_GDDR3_MAX] = { | |
296 | 0, 0, 0, 0, 0, 2, 3, 8, 9, 10, 11, 0, 0, 1, 1, 0, 3}; | |
297 | ||
fd99fd61 BS |
298 | static int |
299 | nouveau_mem_gddr3_mr(struct drm_device *dev, u32 freq, | |
300 | struct nouveau_pm_tbl_entry *e, u8 len, | |
301 | struct nouveau_pm_memtiming *boot, | |
302 | struct nouveau_pm_memtiming *t) | |
bfb31465 | 303 | { |
fd99fd61 | 304 | if (len < 15) { |
c7c039fd RS |
305 | t->drive_strength = boot->drive_strength; |
306 | t->odt = boot->odt; | |
307 | } else { | |
308 | t->drive_strength = (e->RAM_FT1 & 0x30) >> 4; | |
309 | t->odt = e->RAM_FT1 & 0x07; | |
bfb31465 | 310 | } |
c7c039fd RS |
311 | |
312 | if (e->tCL >= NV_MEM_CL_GDDR3_MAX) { | |
313 | NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); | |
fd99fd61 | 314 | return -ERANGE; |
c7c039fd RS |
315 | } |
316 | ||
317 | if (e->tWR >= NV_MEM_WR_GDDR3_MAX) { | |
318 | NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); | |
fd99fd61 | 319 | return -ERANGE; |
c7c039fd RS |
320 | } |
321 | ||
322 | if (t->odt > 3) { | |
323 | NV_WARN(dev, "(%u) Invalid odt value, assuming autocal: %x", | |
324 | t->id, t->odt); | |
325 | t->odt = 0; | |
326 | } | |
327 | ||
328 | t->mr[0] = (boot->mr[0] & 0xe0b) | | |
329 | /* CAS */ | |
330 | ((nv_mem_cl_lut_gddr3[e->tCL] & 0x7) << 4) | | |
331 | ((nv_mem_cl_lut_gddr3[e->tCL] & 0x8) >> 2); | |
332 | t->mr[1] = (boot->mr[1] & 0x100f40) | t->drive_strength | | |
333 | (t->odt << 2) | | |
334 | (nv_mem_wr_lut_gddr3[e->tWR] & 0xf) << 4; | |
1a7287ea | 335 | t->mr[2] = boot->mr[2]; |
c7c039fd | 336 | |
1a7287ea BS |
337 | NV_DEBUG(dev, "(%u) MR: %08x %08x %08x", t->id, |
338 | t->mr[0], t->mr[1], t->mr[2]); | |
fd99fd61 | 339 | return 0; |
c7c039fd RS |
340 | } |
341 | ||
fd99fd61 BS |
342 | static int |
343 | nouveau_mem_gddr5_mr(struct drm_device *dev, u32 freq, | |
344 | struct nouveau_pm_tbl_entry *e, u8 len, | |
345 | struct nouveau_pm_memtiming *boot, | |
346 | struct nouveau_pm_memtiming *t) | |
c7c039fd | 347 | { |
fd99fd61 | 348 | if (len < 15) { |
c7c039fd RS |
349 | t->drive_strength = boot->drive_strength; |
350 | t->odt = boot->odt; | |
351 | } else { | |
352 | t->drive_strength = (e->RAM_FT1 & 0x30) >> 4; | |
353 | t->odt = e->RAM_FT1 & 0x03; | |
354 | } | |
355 | ||
356 | if (e->tCL >= NV_MEM_CL_GDDR5_MAX) { | |
357 | NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL); | |
fd99fd61 | 358 | return -ERANGE; |
c7c039fd RS |
359 | } |
360 | ||
361 | if (e->tWR >= NV_MEM_WR_GDDR5_MAX) { | |
362 | NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR); | |
fd99fd61 | 363 | return -ERANGE; |
c7c039fd RS |
364 | } |
365 | ||
366 | if (t->odt > 3) { | |
367 | NV_WARN(dev, "(%u) Invalid odt value, assuming autocal: %x", | |
368 | t->id, t->odt); | |
369 | t->odt = 0; | |
370 | } | |
371 | ||
372 | t->mr[0] = (boot->mr[0] & 0x007) | | |
373 | ((e->tCL - 5) << 3) | | |
374 | ((e->tWR - 4) << 8); | |
375 | t->mr[1] = (boot->mr[1] & 0x1007f0) | | |
376 | t->drive_strength | | |
377 | (t->odt << 2); | |
378 | ||
379 | NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]); | |
fd99fd61 | 380 | return 0; |
c7c039fd RS |
381 | } |
382 | ||
085028ce BS |
383 | int |
384 | nouveau_mem_timing_calc(struct drm_device *dev, u32 freq, | |
385 | struct nouveau_pm_memtiming *t) | |
fd99fd61 BS |
386 | { |
387 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
388 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
085028ce | 389 | struct nouveau_pm_memtiming *boot = &pm->boot.timing; |
fd99fd61 | 390 | struct nouveau_pm_tbl_entry *e; |
070be296 | 391 | u8 ver, len, *ptr, *ramcfg; |
fd99fd61 BS |
392 | int ret; |
393 | ||
394 | ptr = nouveau_perf_timing(dev, freq, &ver, &len); | |
085028ce BS |
395 | if (!ptr || ptr[0] == 0x00) { |
396 | *t = *boot; | |
397 | return 0; | |
398 | } | |
fd99fd61 BS |
399 | e = (struct nouveau_pm_tbl_entry *)ptr; |
400 | ||
085028ce | 401 | t->tCWL = boot->tCWL; |
fd99fd61 | 402 | |
085028ce BS |
403 | switch (dev_priv->card_type) { |
404 | case NV_40: | |
405 | ret = nv40_mem_timing_calc(dev, freq, e, len, boot, t); | |
406 | break; | |
407 | case NV_50: | |
408 | ret = nv50_mem_timing_calc(dev, freq, e, len, boot, t); | |
409 | break; | |
410 | case NV_C0: | |
a94ba1fc | 411 | case NV_D0: |
085028ce BS |
412 | ret = nvc0_mem_timing_calc(dev, freq, e, len, boot, t); |
413 | break; | |
414 | default: | |
415 | ret = -ENODEV; | |
416 | break; | |
417 | } | |
fd99fd61 | 418 | |
861d2107 | 419 | switch (nvfb_vram_type(dev) * !ret) { |
085028ce BS |
420 | case NV_MEM_TYPE_GDDR3: |
421 | ret = nouveau_mem_gddr3_mr(dev, freq, e, len, boot, t); | |
422 | break; | |
423 | case NV_MEM_TYPE_GDDR5: | |
424 | ret = nouveau_mem_gddr5_mr(dev, freq, e, len, boot, t); | |
425 | break; | |
426 | case NV_MEM_TYPE_DDR2: | |
427 | ret = nouveau_mem_ddr2_mr(dev, freq, e, len, boot, t); | |
428 | break; | |
429 | case NV_MEM_TYPE_DDR3: | |
430 | ret = nouveau_mem_ddr3_mr(dev, freq, e, len, boot, t); | |
431 | break; | |
432 | default: | |
433 | ret = -EINVAL; | |
070be296 BS |
434 | break; |
435 | } | |
436 | ||
437 | ramcfg = nouveau_perf_ramcfg(dev, freq, &ver, &len); | |
438 | if (ramcfg) { | |
439 | int dll_off; | |
440 | ||
441 | if (ver == 0x00) | |
442 | dll_off = !!(ramcfg[3] & 0x04); | |
443 | else | |
444 | dll_off = !!(ramcfg[2] & 0x40); | |
445 | ||
861d2107 | 446 | switch (nvfb_vram_type(dev)) { |
070be296 BS |
447 | case NV_MEM_TYPE_GDDR3: |
448 | t->mr[1] &= ~0x00000040; | |
449 | t->mr[1] |= 0x00000040 * dll_off; | |
450 | break; | |
451 | default: | |
452 | t->mr[1] &= ~0x00000001; | |
453 | t->mr[1] |= 0x00000001 * dll_off; | |
454 | break; | |
455 | } | |
fd99fd61 BS |
456 | } |
457 | ||
085028ce | 458 | return ret; |
fd99fd61 BS |
459 | } |
460 | ||
461 | void | |
462 | nouveau_mem_timing_read(struct drm_device *dev, struct nouveau_pm_memtiming *t) | |
c7c039fd RS |
463 | { |
464 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
465 | u32 timing_base, timing_regs, mr_base; | |
466 | int i; | |
467 | ||
468 | if (dev_priv->card_type >= 0xC0) { | |
469 | timing_base = 0x10f290; | |
470 | mr_base = 0x10f300; | |
471 | } else { | |
472 | timing_base = 0x100220; | |
473 | mr_base = 0x1002c0; | |
474 | } | |
475 | ||
476 | t->id = -1; | |
477 | ||
478 | switch (dev_priv->card_type) { | |
479 | case NV_50: | |
480 | timing_regs = 9; | |
481 | break; | |
482 | case NV_C0: | |
483 | case NV_D0: | |
484 | timing_regs = 5; | |
485 | break; | |
486 | case NV_30: | |
487 | case NV_40: | |
488 | timing_regs = 3; | |
489 | break; | |
490 | default: | |
491 | timing_regs = 0; | |
492 | return; | |
493 | } | |
494 | for(i = 0; i < timing_regs; i++) | |
495 | t->reg[i] = nv_rd32(dev, timing_base + (0x04 * i)); | |
496 | ||
497 | t->tCWL = 0; | |
498 | if (dev_priv->card_type < NV_C0) { | |
499 | t->tCWL = ((nv_rd32(dev, 0x100228) & 0x0f000000) >> 24) + 1; | |
e6084257 RS |
500 | } else if (dev_priv->card_type <= NV_D0) { |
501 | t->tCWL = ((nv_rd32(dev, 0x10f294) & 0x00000f80) >> 7); | |
c7c039fd RS |
502 | } |
503 | ||
504 | t->mr[0] = nv_rd32(dev, mr_base); | |
505 | t->mr[1] = nv_rd32(dev, mr_base + 0x04); | |
506 | t->mr[2] = nv_rd32(dev, mr_base + 0x20); | |
507 | t->mr[3] = nv_rd32(dev, mr_base + 0x24); | |
508 | ||
509 | t->odt = 0; | |
510 | t->drive_strength = 0; | |
511 | ||
861d2107 | 512 | switch (nvfb_vram_type(dev)) { |
c7c039fd RS |
513 | case NV_MEM_TYPE_DDR3: |
514 | t->odt |= (t->mr[1] & 0x200) >> 7; | |
515 | case NV_MEM_TYPE_DDR2: | |
516 | t->odt |= (t->mr[1] & 0x04) >> 2 | | |
517 | (t->mr[1] & 0x40) >> 5; | |
518 | break; | |
519 | case NV_MEM_TYPE_GDDR3: | |
520 | case NV_MEM_TYPE_GDDR5: | |
521 | t->drive_strength = t->mr[1] & 0x03; | |
522 | t->odt = (t->mr[1] & 0x0c) >> 2; | |
523 | break; | |
524 | default: | |
525 | break; | |
526 | } | |
527 | } | |
528 | ||
2d85bc88 BS |
529 | int |
530 | nouveau_mem_exec(struct nouveau_mem_exec_func *exec, | |
531 | struct nouveau_pm_level *perflvl) | |
532 | { | |
533 | struct drm_nouveau_private *dev_priv = exec->dev->dev_private; | |
534 | struct nouveau_pm_memtiming *info = &perflvl->timing; | |
535 | u32 tMRD = 1000, tCKSRE = 0, tCKSRX = 0, tXS = 0, tDLLK = 0; | |
536 | u32 mr[3] = { info->mr[0], info->mr[1], info->mr[2] }; | |
537 | u32 mr1_dlloff; | |
538 | ||
861d2107 | 539 | switch (nvfb_vram_type(dev_priv->dev)) { |
2d85bc88 BS |
540 | case NV_MEM_TYPE_DDR2: |
541 | tDLLK = 2000; | |
542 | mr1_dlloff = 0x00000001; | |
543 | break; | |
544 | case NV_MEM_TYPE_DDR3: | |
545 | tDLLK = 12000; | |
78c20186 BS |
546 | tCKSRE = 2000; |
547 | tXS = 1000; | |
2d85bc88 BS |
548 | mr1_dlloff = 0x00000001; |
549 | break; | |
550 | case NV_MEM_TYPE_GDDR3: | |
551 | tDLLK = 40000; | |
552 | mr1_dlloff = 0x00000040; | |
553 | break; | |
554 | default: | |
555 | NV_ERROR(exec->dev, "cannot reclock unsupported memtype\n"); | |
556 | return -ENODEV; | |
557 | } | |
558 | ||
559 | /* fetch current MRs */ | |
861d2107 | 560 | switch (nvfb_vram_type(dev_priv->dev)) { |
1a7287ea | 561 | case NV_MEM_TYPE_GDDR3: |
2d85bc88 BS |
562 | case NV_MEM_TYPE_DDR3: |
563 | mr[2] = exec->mrg(exec, 2); | |
564 | default: | |
565 | mr[1] = exec->mrg(exec, 1); | |
566 | mr[0] = exec->mrg(exec, 0); | |
567 | break; | |
568 | } | |
569 | ||
570 | /* DLL 'on' -> DLL 'off' mode, disable before entering self-refresh */ | |
571 | if (!(mr[1] & mr1_dlloff) && (info->mr[1] & mr1_dlloff)) { | |
572 | exec->precharge(exec); | |
573 | exec->mrs (exec, 1, mr[1] | mr1_dlloff); | |
574 | exec->wait(exec, tMRD); | |
575 | } | |
576 | ||
577 | /* enter self-refresh mode */ | |
578 | exec->precharge(exec); | |
579 | exec->refresh(exec); | |
580 | exec->refresh(exec); | |
581 | exec->refresh_auto(exec, false); | |
582 | exec->refresh_self(exec, true); | |
583 | exec->wait(exec, tCKSRE); | |
584 | ||
585 | /* modify input clock frequency */ | |
586 | exec->clock_set(exec); | |
587 | ||
588 | /* exit self-refresh mode */ | |
589 | exec->wait(exec, tCKSRX); | |
590 | exec->precharge(exec); | |
591 | exec->refresh_self(exec, false); | |
592 | exec->refresh_auto(exec, true); | |
593 | exec->wait(exec, tXS); | |
78c20186 | 594 | exec->wait(exec, tXS); |
2d85bc88 BS |
595 | |
596 | /* update MRs */ | |
597 | if (mr[2] != info->mr[2]) { | |
598 | exec->mrs (exec, 2, info->mr[2]); | |
599 | exec->wait(exec, tMRD); | |
600 | } | |
601 | ||
602 | if (mr[1] != info->mr[1]) { | |
b830973b BS |
603 | /* need to keep DLL off until later, at least on GDDR3 */ |
604 | exec->mrs (exec, 1, info->mr[1] | (mr[1] & mr1_dlloff)); | |
2d85bc88 BS |
605 | exec->wait(exec, tMRD); |
606 | } | |
607 | ||
608 | if (mr[0] != info->mr[0]) { | |
609 | exec->mrs (exec, 0, info->mr[0]); | |
610 | exec->wait(exec, tMRD); | |
611 | } | |
612 | ||
613 | /* update PFB timing registers */ | |
614 | exec->timing_set(exec); | |
615 | ||
b830973b | 616 | /* DLL (enable + ) reset */ |
2d85bc88 | 617 | if (!(info->mr[1] & mr1_dlloff)) { |
b830973b BS |
618 | if (mr[1] & mr1_dlloff) { |
619 | exec->mrs (exec, 1, info->mr[1]); | |
620 | exec->wait(exec, tMRD); | |
621 | } | |
2d85bc88 BS |
622 | exec->mrs (exec, 0, info->mr[0] | 0x00000100); |
623 | exec->wait(exec, tMRD); | |
624 | exec->mrs (exec, 0, info->mr[0] | 0x00000000); | |
625 | exec->wait(exec, tMRD); | |
626 | exec->wait(exec, tDLLK); | |
861d2107 | 627 | if (nvfb_vram_type(dev_priv->dev) == NV_MEM_TYPE_GDDR3) |
2d85bc88 BS |
628 | exec->precharge(exec); |
629 | } | |
630 | ||
631 | return 0; | |
632 | } | |
633 | ||
c70c41e8 BS |
634 | int |
635 | nouveau_mem_vbios_type(struct drm_device *dev) | |
636 | { | |
637 | struct bit_entry M; | |
638 | u8 ramcfg = (nv_rd32(dev, 0x101000) & 0x0000003c) >> 2; | |
639 | if (!bit_table(dev, 'M', &M) || M.version != 2 || M.length < 5) { | |
640 | u8 *table = ROMPTR(dev, M.data[3]); | |
641 | if (table && table[0] == 0x10 && ramcfg < table[3]) { | |
642 | u8 *entry = table + table[1] + (ramcfg * table[2]); | |
643 | switch (entry[0] & 0x0f) { | |
644 | case 0: return NV_MEM_TYPE_DDR2; | |
645 | case 1: return NV_MEM_TYPE_DDR3; | |
646 | case 2: return NV_MEM_TYPE_GDDR3; | |
647 | case 3: return NV_MEM_TYPE_GDDR5; | |
648 | default: | |
649 | break; | |
650 | } | |
651 | ||
652 | } | |
653 | } | |
654 | return NV_MEM_TYPE_UNKNOWN; | |
655 | } |