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