1 | /* |
2 | * Copyright 2008 Stuart Bennett |
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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
18 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF |
19 | * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
20 | * SOFTWARE. |
21 | */ |
22 | |
23 | #ifndef __NOUVEAU_HW_H__ |
24 | #define __NOUVEAU_HW_H__ |
25 | |
26 | #include "disp.h" |
27 | #include "nvreg.h" |
28 | |
29 | #include <subdev/bios/pll.h> |
30 | |
31 | #define MASK(field) ( \ |
32 | (0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field)) |
33 | |
34 | #define XLATE(src, srclowbit, outfield) ( \ |
35 | (((src) >> (srclowbit)) << (0 ? outfield)) & MASK(outfield)) |
36 | |
37 | void NVWriteVgaSeq(struct drm_device *, int head, uint8_t index, uint8_t value); |
38 | uint8_t NVReadVgaSeq(struct drm_device *, int head, uint8_t index); |
39 | void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value); |
40 | uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index); |
41 | void NVSetOwner(struct drm_device *, int owner); |
42 | void NVBlankScreen(struct drm_device *, int head, bool blank); |
43 | int nouveau_hw_get_pllvals(struct drm_device *, enum nvbios_pll_type plltype, |
44 | struct nvkm_pll_vals *pllvals); |
45 | int nouveau_hw_pllvals_to_clk(struct nvkm_pll_vals *pllvals); |
46 | int nouveau_hw_get_clock(struct drm_device *, enum nvbios_pll_type plltype); |
47 | void nouveau_hw_save_vga_fonts(struct drm_device *, bool save); |
48 | void nouveau_hw_save_state(struct drm_device *, int head, |
49 | struct nv04_mode_state *state); |
50 | void nouveau_hw_load_state(struct drm_device *, int head, |
51 | struct nv04_mode_state *state); |
52 | void nouveau_hw_load_state_palette(struct drm_device *, int head, |
53 | struct nv04_mode_state *state); |
54 | |
55 | /* nouveau_calc.c */ |
56 | extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp, |
57 | int *burst, int *lwm); |
58 | |
59 | static inline uint32_t NVReadCRTC(struct drm_device *dev, |
60 | int head, uint32_t reg) |
61 | { |
62 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
63 | uint32_t val; |
64 | if (head) |
65 | reg += NV_PCRTC0_SIZE; |
66 | val = nvif_rd32(device, reg); |
67 | return val; |
68 | } |
69 | |
70 | static inline void NVWriteCRTC(struct drm_device *dev, |
71 | int head, uint32_t reg, uint32_t val) |
72 | { |
73 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
74 | if (head) |
75 | reg += NV_PCRTC0_SIZE; |
76 | nvif_wr32(device, reg, val); |
77 | } |
78 | |
79 | static inline uint32_t NVReadRAMDAC(struct drm_device *dev, |
80 | int head, uint32_t reg) |
81 | { |
82 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
83 | uint32_t val; |
84 | if (head) |
85 | reg += NV_PRAMDAC0_SIZE; |
86 | val = nvif_rd32(device, reg); |
87 | return val; |
88 | } |
89 | |
90 | static inline void NVWriteRAMDAC(struct drm_device *dev, |
91 | int head, uint32_t reg, uint32_t val) |
92 | { |
93 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
94 | if (head) |
95 | reg += NV_PRAMDAC0_SIZE; |
96 | nvif_wr32(device, reg, val); |
97 | } |
98 | |
99 | static inline uint8_t nv_read_tmds(struct drm_device *dev, |
100 | int or, int dl, uint8_t address) |
101 | { |
102 | int ramdac = (or & DCB_OUTPUT_C) >> 2; |
103 | |
104 | NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, |
105 | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | address); |
106 | return NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8); |
107 | } |
108 | |
109 | static inline void nv_write_tmds(struct drm_device *dev, |
110 | int or, int dl, uint8_t address, |
111 | uint8_t data) |
112 | { |
113 | int ramdac = (or & DCB_OUTPUT_C) >> 2; |
114 | |
115 | NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8, data); |
116 | NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, address); |
117 | } |
118 | |
119 | static inline void NVWriteVgaCrtc(struct drm_device *dev, |
120 | int head, uint8_t index, uint8_t value) |
121 | { |
122 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
123 | nvif_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); |
124 | nvif_wr08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value); |
125 | } |
126 | |
127 | static inline uint8_t NVReadVgaCrtc(struct drm_device *dev, |
128 | int head, uint8_t index) |
129 | { |
130 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
131 | uint8_t val; |
132 | nvif_wr08(device, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); |
133 | val = nvif_rd08(device, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE); |
134 | return val; |
135 | } |
136 | |
137 | /* CR57 and CR58 are a fun pair of regs. CR57 provides an index (0-0xf) for CR58 |
138 | * I suspect they in fact do nothing, but are merely a way to carry useful |
139 | * per-head variables around |
140 | * |
141 | * Known uses: |
142 | * CR57 CR58 |
143 | * 0x00 index to the appropriate dcb entry (or 7f for inactive) |
144 | * 0x02 dcb entry's "or" value (or 00 for inactive) |
145 | * 0x03 bit0 set for dual link (LVDS, possibly elsewhere too) |
146 | * 0x08 or 0x09 pxclk in MHz |
147 | * 0x0f laptop panel info - low nibble for PEXTDEV_BOOT_0 strap |
148 | * high nibble for xlat strap value |
149 | */ |
150 | |
151 | static inline void |
152 | NVWriteVgaCrtc5758(struct drm_device *dev, int head, uint8_t index, uint8_t value) |
153 | { |
154 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); |
155 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_58, value); |
156 | } |
157 | |
158 | static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_t index) |
159 | { |
160 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); |
161 | return NVReadVgaCrtc(dev, head, NV_CIO_CRE_58); |
162 | } |
163 | |
164 | static inline uint8_t NVReadPRMVIO(struct drm_device *dev, |
165 | int head, uint32_t reg) |
166 | { |
167 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
168 | struct nouveau_drm *drm = nouveau_drm(dev); |
169 | uint8_t val; |
170 | |
171 | /* Only NV4x have two pvio ranges; other twoHeads cards MUST call |
172 | * NVSetOwner for the relevant head to be programmed */ |
173 | if (head && drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) |
174 | reg += NV_PRMVIO_SIZE; |
175 | |
176 | val = nvif_rd08(device, reg); |
177 | return val; |
178 | } |
179 | |
180 | static inline void NVWritePRMVIO(struct drm_device *dev, |
181 | int head, uint32_t reg, uint8_t value) |
182 | { |
183 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
184 | struct nouveau_drm *drm = nouveau_drm(dev); |
185 | |
186 | /* Only NV4x have two pvio ranges; other twoHeads cards MUST call |
187 | * NVSetOwner for the relevant head to be programmed */ |
188 | if (head && drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) |
189 | reg += NV_PRMVIO_SIZE; |
190 | |
191 | nvif_wr08(device, reg, value); |
192 | } |
193 | |
194 | static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable) |
195 | { |
196 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
197 | nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); |
198 | nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20); |
199 | } |
200 | |
201 | static inline bool NVGetEnablePalette(struct drm_device *dev, int head) |
202 | { |
203 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
204 | nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); |
205 | return !(nvif_rd08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20); |
206 | } |
207 | |
208 | static inline void NVWriteVgaAttr(struct drm_device *dev, |
209 | int head, uint8_t index, uint8_t value) |
210 | { |
211 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
212 | if (NVGetEnablePalette(dev, head)) |
213 | index &= ~0x20; |
214 | else |
215 | index |= 0x20; |
216 | |
217 | nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); |
218 | nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); |
219 | nvif_wr08(device, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value); |
220 | } |
221 | |
222 | static inline uint8_t NVReadVgaAttr(struct drm_device *dev, |
223 | int head, uint8_t index) |
224 | { |
225 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
226 | uint8_t val; |
227 | if (NVGetEnablePalette(dev, head)) |
228 | index &= ~0x20; |
229 | else |
230 | index |= 0x20; |
231 | |
232 | nvif_rd08(device, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); |
233 | nvif_wr08(device, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); |
234 | val = nvif_rd08(device, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE); |
235 | return val; |
236 | } |
237 | |
238 | static inline void NVVgaSeqReset(struct drm_device *dev, int head, bool start) |
239 | { |
240 | NVWriteVgaSeq(dev, head, NV_VIO_SR_RESET_INDEX, start ? 0x1 : 0x3); |
241 | } |
242 | |
243 | static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect) |
244 | { |
245 | uint8_t seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX); |
246 | |
247 | if (protect) { |
248 | NVVgaSeqReset(dev, head, true); |
249 | NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20); |
250 | } else { |
251 | /* Reenable sequencer, then turn on screen */ |
252 | NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); /* reenable display */ |
253 | NVVgaSeqReset(dev, head, false); |
254 | } |
255 | NVSetEnablePalette(dev, head, protect); |
256 | } |
257 | |
258 | static inline bool |
259 | nv_heads_tied(struct drm_device *dev) |
260 | { |
261 | struct nvif_object *device = &nouveau_drm(dev)->client.device.object; |
262 | struct nouveau_drm *drm = nouveau_drm(dev); |
263 | |
264 | if (drm->client.device.info.chipset == 0x11) |
265 | return !!(nvif_rd32(device, NV_PBUS_DEBUG_1) & (1 << 28)); |
266 | |
267 | return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4; |
268 | } |
269 | |
270 | /* makes cr0-7 on the specified head read-only */ |
271 | static inline bool |
272 | nv_lock_vga_crtc_base(struct drm_device *dev, int head, bool lock) |
273 | { |
274 | uint8_t cr11 = NVReadVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX); |
275 | bool waslocked = cr11 & 0x80; |
276 | |
277 | if (lock) |
278 | cr11 |= 0x80; |
279 | else |
280 | cr11 &= ~0x80; |
281 | NVWriteVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX, cr11); |
282 | |
283 | return waslocked; |
284 | } |
285 | |
286 | static inline void |
287 | nv_lock_vga_crtc_shadow(struct drm_device *dev, int head, int lock) |
288 | { |
289 | /* shadow lock: connects 0x60?3d? regs to "real" 0x3d? regs |
290 | * bit7: unlocks HDT, HBS, HBE, HRS, HRE, HEB |
291 | * bit6: seems to have some effect on CR09 (double scan, VBS_9) |
292 | * bit5: unlocks HDE |
293 | * bit4: unlocks VDE |
294 | * bit3: unlocks VDT, OVL, VRS, ?VRE?, VBS, VBE, LSR, EBR |
295 | * bit2: same as bit 1 of 0x60?804 |
296 | * bit0: same as bit 0 of 0x60?804 |
297 | */ |
298 | |
299 | uint8_t cr21 = lock; |
300 | |
301 | if (lock < 0) |
302 | /* 0xfa is generic "unlock all" mask */ |
303 | cr21 = NVReadVgaCrtc(dev, head, NV_CIO_CRE_21) | 0xfa; |
304 | |
305 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_21, cr21); |
306 | } |
307 | |
308 | /* renders the extended crtc regs (cr19+) on all crtcs impervious: |
309 | * immutable and unreadable |
310 | */ |
311 | static inline bool |
312 | NVLockVgaCrtcs(struct drm_device *dev, bool lock) |
313 | { |
314 | struct nouveau_drm *drm = nouveau_drm(dev); |
315 | bool waslocked = !NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX); |
316 | |
317 | NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX, |
318 | lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE); |
319 | /* NV11 has independently lockable extended crtcs, except when tied */ |
320 | if (drm->client.device.info.chipset == 0x11 && !nv_heads_tied(dev)) |
321 | NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX, |
322 | lock ? NV_CIO_SR_LOCK_VALUE : |
323 | NV_CIO_SR_UNLOCK_RW_VALUE); |
324 | |
325 | return waslocked; |
326 | } |
327 | |
328 | /* nv04 cursor max dimensions of 32x32 (A1R5G5B5) */ |
329 | #define NV04_CURSOR_SIZE 32 |
330 | /* limit nv10 cursors to 64x64 (ARGB8) (we could go to 64x255) */ |
331 | #define NV10_CURSOR_SIZE 64 |
332 | |
333 | static inline int nv_cursor_width(struct drm_device *dev) |
334 | { |
335 | struct nouveau_drm *drm = nouveau_drm(dev); |
336 | |
337 | return drm->client.device.info.family >= NV_DEVICE_INFO_V0_CELSIUS ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE; |
338 | } |
339 | |
340 | static inline void |
341 | nv_fix_nv40_hw_cursor(struct drm_device *dev, int head) |
342 | { |
343 | /* on some nv40 (such as the "true" (in the NV_PFB_BOOT_0 sense) nv40, |
344 | * the gf6800gt) a hardware bug requires a write to PRAMDAC_CURSOR_POS |
345 | * for changes to the CRTC CURCTL regs to take effect, whether changing |
346 | * the pixmap location, or just showing/hiding the cursor |
347 | */ |
348 | uint32_t curpos = NVReadRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS); |
349 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS, curpos); |
350 | } |
351 | |
352 | static inline void |
353 | nv_set_crtc_base(struct drm_device *dev, int head, uint32_t offset) |
354 | { |
355 | struct nouveau_drm *drm = nouveau_drm(dev); |
356 | |
357 | NVWriteCRTC(dev, head, NV_PCRTC_START, offset); |
358 | |
359 | if (drm->client.device.info.family == NV_DEVICE_INFO_V0_TNT) { |
360 | /* |
361 | * Hilarious, the 24th bit doesn't want to stick to |
362 | * PCRTC_START... |
363 | */ |
364 | int cre_heb = NVReadVgaCrtc(dev, head, NV_CIO_CRE_HEB__INDEX); |
365 | |
366 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HEB__INDEX, |
367 | (cre_heb & ~0x40) | ((offset >> 18) & 0x40)); |
368 | } |
369 | } |
370 | |
371 | static inline void |
372 | nv_show_cursor(struct drm_device *dev, int head, bool show) |
373 | { |
374 | struct nouveau_drm *drm = nouveau_drm(dev); |
375 | uint8_t *curctl1 = |
376 | &nv04_display(dev)->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX]; |
377 | |
378 | if (show) |
379 | *curctl1 |= MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); |
380 | else |
381 | *curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); |
382 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1); |
383 | |
384 | if (drm->client.device.info.family == NV_DEVICE_INFO_V0_CURIE) |
385 | nv_fix_nv40_hw_cursor(dev, head); |
386 | } |
387 | |
388 | static inline uint32_t |
389 | nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp) |
390 | { |
391 | struct nouveau_drm *drm = nouveau_drm(dev); |
392 | int mask; |
393 | |
394 | if (bpp == 15) |
395 | bpp = 16; |
396 | if (bpp == 24) |
397 | bpp = 8; |
398 | |
399 | /* Alignment requirements taken from the Haiku driver */ |
400 | if (drm->client.device.info.family == NV_DEVICE_INFO_V0_TNT) |
401 | mask = 128 / bpp - 1; |
402 | else |
403 | mask = 512 / bpp - 1; |
404 | |
405 | return (width + mask) & ~mask; |
406 | } |
407 | |
408 | #endif /* __NOUVEAU_HW_H__ */ |
409 | |