nv50: clip SIFC and add {Prepare,Finish}Access hooks.
[nouveau] / src / nv50_crtc.c
1 /*
2  * Copyright 2007 NVIDIA, Corporation
3  * Copyright 2008 Maarten Maathuis
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
20  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23
24 #include "nouveau_modeset.h"
25 #include "nouveau_crtc.h"
26 #include "nouveau_output.h"
27 #include "nouveau_connector.h"
28
29 /* Don't call the directly, only load state should do this on the long run*/
30 static void 
31 NV50CheckWriteVClk(ScrnInfoPtr pScrn)
32 {
33         NVPtr pNv = NVPTR(pScrn);
34         int t_start = GetTimeInMillis();
35
36         while (NVRead(pNv, NV50_DISPLAY_CTRL_STATE) & NV50_DISPLAY_CTRL_STATE_PENDING) {
37                 /* An educated guess. */
38                 const uint32_t supervisor = NVRead(pNv, NV50_DISPLAY_SUPERVISOR);
39
40                 /* Just in case something goes bad, at least you can blindly restart your machine. */
41                 if ((GetTimeInMillis() - t_start) > 5000) {
42                         xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "NV50CheckWriteVClk() timed out.\n");
43                         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "A reboot is probably required now.\n");
44                         break;
45                 }
46
47                 /* Simply acknowledge it, maybe we should do more? */
48                 if (supervisor & NV50_DISPLAY_SUPERVISOR_CRTCn) {
49                         NVWrite(pNv, NV50_DISPLAY_SUPERVISOR, supervisor & NV50_DISPLAY_SUPERVISOR_CRTCn);
50                 }
51
52                 if (supervisor & NV50_DISPLAY_SUPERVISOR_CLK_MASK) {
53                         if (supervisor & NV50_DISPLAY_SUPERVISOR_CLK_UPDATE) {
54                                 const uint32_t clockvar = NVRead(pNv, NV50_DISPLAY_UNK30_CTRL);
55                                 int i;
56
57                                 for(i = 0; i < 2; i++) {
58                                         nouveauCrtcPtr crtc = pNv->crtc[i];
59                                         uint32_t mask = 0;
60
61                                         if (crtc->index == 1)
62                                                 mask = NV50_DISPLAY_UNK30_CTRL_UPDATE_VCLK1;
63                                         else
64                                                 mask = NV50_DISPLAY_UNK30_CTRL_UPDATE_VCLK0;
65
66                                         if (clockvar & mask)
67                                                 crtc->SetPixelClock(crtc, crtc->pixel_clock);
68                                         /* Always do something if the supervisor wants a clock change. */
69                                         /* This is needed because you get a deadlock if you don't kick the NV50_CRTC0_CLK_CTRL2 register. */
70                                         if (crtc->modeset_lock) {
71                                                 crtc->SetClockMode(crtc, crtc->pixel_clock);
72
73                                                 nouveauOutputPtr output;
74                                                 for (output = pNv->output; output != NULL; output = output->next) {
75                                                         if (output->crtc == crtc)
76                                                                 output->SetClockMode(output, crtc->pixel_clock);
77                                                 }
78                                         }
79                                 }
80                         }
81
82                         NVWrite(pNv, NV50_DISPLAY_SUPERVISOR, 1 << (ffs(supervisor & NV50_DISPLAY_SUPERVISOR_CLK_MASK) - 1));
83                         NVWrite(pNv, NV50_DISPLAY_UNK30_CTRL, NV50_DISPLAY_UNK30_CTRL_PENDING);
84                 }
85         }
86 }
87
88 void NV50DisplayCommand(ScrnInfoPtr pScrn, uint32_t addr, uint32_t value)
89 {
90         DDXMMIOH("NV50DisplayCommand: head %d addr 0x%X value 0x%X\n", 0, addr, value);
91         NVPtr pNv = NVPTR(pScrn);
92         NVWrite(pNv, NV50_DISPLAY_CTRL_VAL, value);
93         NVWrite(pNv, NV50_DISPLAY_CTRL_STATE, addr | 0x10000 | NV50_DISPLAY_CTRL_STATE_ENABLE | NV50_DISPLAY_CTRL_STATE_PENDING);
94         NV50CheckWriteVClk(pScrn);
95 }
96
97 void NV50CrtcCommand(nouveauCrtcPtr crtc, uint32_t addr, uint32_t value)
98 {
99         ScrnInfoPtr pScrn = crtc->scrn;
100
101         /* This head dependent offset is only for crtc commands. */
102         NV50DisplayCommand(pScrn, addr + 0x400 * crtc->index, value);
103 }
104
105 static Bool
106 NV50CrtcModeValid(nouveauCrtcPtr crtc, DisplayModePtr mode)
107 {
108         return TRUE;
109 }
110
111 static void
112 NV50CrtcModeSet(nouveauCrtcPtr crtc, DisplayModePtr mode)
113 {
114         ScrnInfoPtr pScrn = crtc->scrn;
115         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcModeSet is called for %s.\n", crtc->index ? "CRTC1" : "CRTC0");
116
117         /* Anyone know a more appropriate name? */
118         DisplayModePtr desired_mode = crtc->use_native_mode ? crtc->native_mode : mode;
119
120         /* Save the pixel clock for posterity. */
121         crtc->pixel_clock = desired_mode->Clock;
122         crtc->cur_mode = mode;
123
124         uint32_t hsync_dur = desired_mode->CrtcHSyncEnd - desired_mode->CrtcHSyncStart;
125         uint32_t vsync_dur = desired_mode->CrtcVSyncEnd - desired_mode->CrtcVSyncStart;
126         uint32_t hsync_start_to_end = desired_mode->CrtcHBlankEnd - desired_mode->CrtcHSyncStart;
127         uint32_t vsync_start_to_end = desired_mode->CrtcVBlankEnd - desired_mode->CrtcVSyncStart;
128         /* I can't give this a proper name, anyone else can? */
129         uint32_t hunk1 = desired_mode->CrtcHTotal - desired_mode->CrtcHSyncStart + desired_mode->CrtcHBlankStart;
130         uint32_t vunk1 = desired_mode->CrtcVTotal - desired_mode->CrtcVSyncStart + desired_mode->CrtcVBlankStart;
131         /* Another strange value, this time only for interlaced modes. */
132         uint32_t vunk2a = 2*desired_mode->CrtcVTotal - desired_mode->CrtcVSyncStart + desired_mode->CrtcVBlankStart;
133         uint32_t vunk2b = desired_mode->CrtcVTotal - desired_mode->CrtcVSyncStart + desired_mode->CrtcVBlankEnd;
134
135         if (desired_mode->Flags & V_INTERLACE) {
136                 vsync_dur /= 2;
137                 vsync_start_to_end  /= 2;
138                 vunk1 /= 2;
139                 vunk2a /= 2;
140                 vunk2b /= 2;
141                 /* magic */
142                 if (desired_mode->Flags & V_DBLSCAN) {
143                         vsync_start_to_end -= 1;
144                         vunk1 -= 1;
145                         vunk2a -= 1;
146                         vunk2b -= 1;
147                 }
148         }
149
150         /* NV50CrtcCommand includes head offset */
151         /* This is the native mode when DFP && !SCALE_PANEL */
152         NV50CrtcCommand(crtc, NV50_CRTC0_CLOCK, desired_mode->Clock | 0x800000);
153         NV50CrtcCommand(crtc, NV50_CRTC0_INTERLACE, (desired_mode->Flags & V_INTERLACE) ? 2 : 0);
154         NV50CrtcCommand(crtc, NV50_CRTC0_DISPLAY_START, 0);
155         NV50CrtcCommand(crtc, NV50_CRTC0_UNK82C, 0);
156         NV50CrtcCommand(crtc, NV50_CRTC0_DISPLAY_TOTAL, desired_mode->CrtcVTotal << 16 | desired_mode->CrtcHTotal);
157         NV50CrtcCommand(crtc, NV50_CRTC0_SYNC_DURATION, (vsync_dur - 1) << 16 | (hsync_dur - 1));
158         NV50CrtcCommand(crtc, NV50_CRTC0_SYNC_START_TO_BLANK_END, (vsync_start_to_end - 1) << 16 | (hsync_start_to_end - 1));
159         NV50CrtcCommand(crtc, NV50_CRTC0_MODE_UNK1, (vunk1 - 1) << 16 | (hunk1 - 1));
160         if (desired_mode->Flags & V_INTERLACE) {
161                 NV50CrtcCommand(crtc, NV50_CRTC0_MODE_UNK2, (vunk2b - 1) << 16 | (vunk2a - 1));
162         }
163         NV50CrtcCommand(crtc, NV50_CRTC0_FB_SIZE, pScrn->virtualY << 16 | pScrn->virtualX);
164
165         /* Maybe move this calculation elsewhere? */
166         crtc->fb_pitch = pScrn->displayWidth * (pScrn->bitsPerPixel / 8);
167         NV50CrtcCommand(crtc, NV50_CRTC0_FB_PITCH, crtc->fb_pitch | 0x100000);
168
169         switch (pScrn->depth) {
170                 case 8:
171                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_8BPP); 
172                         break;
173                 case 15:
174                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_15BPP);
175                         break;
176                 case 16:
177                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_16BPP);
178                         break;
179                 case 24:
180                         NV50CrtcCommand(crtc, NV50_CRTC0_DEPTH, NV50_CRTC0_DEPTH_24BPP); 
181                         break;
182         }
183         crtc->SetDither(crtc);
184         NV50CrtcCommand(crtc, NV50_CRTC0_COLOR_CTRL, NV50_CRTC_COLOR_CTRL_MODE_COLOR);
185         NV50CrtcCommand(crtc, NV50_CRTC0_FB_POS, (crtc->y << 16) | (crtc->x));
186         /* This is the actual resolution of the mode. */
187         NV50CrtcCommand(crtc, NV50_CRTC0_REAL_RES, (mode->VDisplay << 16) | mode->HDisplay);
188         NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_CENTER_OFFSET, NV50_CRTC_SCALE_CENTER_OFFSET_VAL(0,0));
189
190         /* Maybe move this as well? */
191         crtc->Blank(crtc, FALSE);
192 }
193
194 static void
195 NV50CrtcSetPixelClock(nouveauCrtcPtr crtc, int clock)
196 {
197         ScrnInfoPtr pScrn = crtc->scrn;
198         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetPixelClock is called for %s.\n", crtc->index ? "CRTC1" : "CRTC0");
199
200         NVPtr pNv = NVPTR(pScrn);
201
202         /* I don't know why exactly, but these were in my table. */
203         uint32_t pll_reg = crtc->index ? NV50_CRTC1_CLK_CTRL1 : NV50_CRTC0_CLK_CTRL1;
204
205         int NM1 = 0xbeef, NM2 = 0xdead, log2P;
206         struct pll_lims pll_lim;
207         get_pll_limits(pScrn, pll_reg, &pll_lim);
208         /* NV5x hardware doesn't seem to support a single vco mode, otherwise the blob is hiding it well. */
209         getMNP_double(pScrn, &pll_lim, clock, &NM1, &NM2, &log2P);
210
211         uint32_t reg1 = NVRead(pNv, pll_reg + 4);
212         uint32_t reg2 = NVRead(pNv, pll_reg + 8);
213
214         /* bit0: The blob (and bios) seem to have this on (almost) always.
215          *          I'm hoping this (experiment) will fix my image stability issues.
216          */
217         NVWrite(pNv, NV50_CRTC0_CLK_CTRL1 + crtc->index * 0x800, NV50_CRTC_CLK_CTRL1_CONNECTED | 0x10000011);
218
219         /* Eventually we should learn ourselves what all the bits should be. */
220         reg1 &= 0xff00ff00;
221         reg2 &= 0x8000ff00;
222
223         uint8_t N1 = (NM1 >> 8) & 0xFF;
224         uint8_t M1 = NM1 & 0xFF;
225         uint8_t N2 = (NM2 >> 8) & 0xFF;
226         uint8_t M2 = NM2 & 0xFF;
227
228         reg1 |= (M1 << 16) | N1;
229         reg2 |= (log2P << 28) | (M2 << 16) | N2;
230
231         NVWrite(pNv, pll_reg + 4, reg1);
232         NVWrite(pNv, pll_reg + 8, reg2);
233 }
234
235 static void
236 NV50CrtcSetClockMode(nouveauCrtcPtr crtc, int clock)
237 {
238         ScrnInfoPtr pScrn = crtc->scrn;
239         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetClockMode is called for %s.\n", crtc->index ? "CRTC1" : "CRTC0");
240
241         NVPtr pNv = NVPTR(pScrn);
242
243         /* There seem to be a few indicator bits, which are similar to the SOR_CTRL bits. */
244         NVWrite(pNv, NV50_CRTC0_CLK_CTRL2 + crtc->index * 0x800, 0);
245 }
246
247 static void
248 NV50CrtcSetFB(nouveauCrtcPtr crtc, struct nouveau_bo * buffer)
249 {
250         /* For the moment the actual hardware settings stays in ModeSet(). */
251         crtc->front_buffer = buffer;
252 }
253
254 static void 
255 NV50CrtcSetFBOffset(nouveauCrtcPtr crtc, uint32_t x, uint32_t y)
256 {
257         /* For the moment the actual hardware settings stays in ModeSet(). */
258         crtc->x = x;
259         crtc->y = y;
260 }
261
262 static void 
263 NV50CrtcBlank(nouveauCrtcPtr crtc, Bool blanked)
264 {
265         ScrnInfoPtr pScrn = crtc->scrn;
266         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcBlank is called (%s) for %s.\n", blanked ? "blanked" : "unblanked", crtc->index ? "CRTC1" : "CRTC0");
267
268         NVPtr pNv = NVPTR(pScrn);
269
270         if (blanked) {
271                 crtc->HideCursor(crtc, TRUE);
272
273                 NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_MODE, NV50_CRTC0_CLUT_MODE_BLANK);
274                 NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_OFFSET, 0);
275                 if (pNv->NVArch != 0x50)
276                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK1, NV84_CRTC0_BLANK_UNK1_BLANK);
277                 NV50CrtcCommand(crtc, NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_BLANK);
278                 if (pNv->NVArch != 0x50)
279                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK2, NV84_CRTC0_BLANK_UNK2_BLANK);
280         } else {
281                 NV50CrtcCommand(crtc, NV50_CRTC0_FB_OFFSET, crtc->front_buffer->offset >> 8);
282                 NV50CrtcCommand(crtc, 0x864, 0);
283                 NVWrite(pNv, NV50_DISPLAY_UNK_380, 0);
284                 /* RAM is clamped to 256 MiB. */
285                 NVWrite(pNv, NV50_DISPLAY_RAM_AMOUNT, pNv->RamAmountKBytes * 1024 - 1);
286                 NVWrite(pNv, NV50_DISPLAY_UNK_388, 0x150000);
287                 NVWrite(pNv, NV50_DISPLAY_UNK_38C, 0);
288                 if (crtc->index == 1)
289                         NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR_OFFSET, pNv->Cursor2->offset >> 8);
290                 else
291                         NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR_OFFSET, pNv->Cursor->offset >> 8);
292                 if(pNv->NVArch != 0x50)
293                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK2, NV84_CRTC0_BLANK_UNK2_UNBLANK);
294
295                 if (crtc->cursor_visible)
296                         crtc->ShowCursor(crtc, TRUE);
297
298                 NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_MODE, 
299                         pScrn->depth == 8 ? NV50_CRTC0_CLUT_MODE_OFF : NV50_CRTC0_CLUT_MODE_ON);
300                 /* Each CRTC has it's own CLUT. */
301                 if (crtc->index == 1)
302                         NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_OFFSET, pNv->CLUT1->offset >> 8);
303                 else
304                         NV50CrtcCommand(crtc, NV50_CRTC0_CLUT_OFFSET, pNv->CLUT0->offset >> 8);
305                 if (pNv->NVArch != 0x50)
306                         NV50CrtcCommand(crtc, NV84_CRTC0_BLANK_UNK1, NV84_CRTC0_BLANK_UNK1_UNBLANK);
307                 NV50CrtcCommand(crtc, NV50_CRTC0_BLANK_CTRL, NV50_CRTC0_BLANK_CTRL_UNBLANK);
308         }
309 }
310
311 static void
312 NV50CrtcSetDither(nouveauCrtcPtr crtc)
313 {
314         ScrnInfoPtr pScrn = crtc->scrn;
315         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetDither is called (%s).\n", !crtc->modeset_lock ? "update" : "no update");
316
317         NV50CrtcCommand(crtc, NV50_CRTC0_DITHERING_CTRL, crtc->dithering ? 
318                         NV50_CRTC0_DITHERING_CTRL_ON : NV50_CRTC0_DITHERING_CTRL_OFF);
319
320         if (!crtc->modeset_lock)
321                 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
322 }
323
324 static void
325 ComputeAspectScale(DisplayModePtr mode, DisplayModePtr adjusted_mode, int *outX, int *outY)
326 {
327         float scaleX, scaleY, scale;
328
329         scaleX = adjusted_mode->HDisplay / (float)mode->HDisplay;
330         scaleY = adjusted_mode->VDisplay / (float)mode->VDisplay;
331
332         if (scaleX > scaleY)
333                 scale = scaleY;
334         else
335                 scale = scaleX;
336
337         *outX = mode->HDisplay * scale;
338         *outY = mode->VDisplay * scale;
339 }
340
341 static void
342 NV50CrtcSetScaleMode(nouveauCrtcPtr crtc, int scale)
343 {
344         ScrnInfoPtr pScrn = crtc->scrn;
345         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcSetScale is called with mode %d for %s.\n", scale, crtc->index ? "CRTC1" : "CRTC0");
346
347         uint32_t scale_val = 0;
348         int outX = 0, outY = 0;
349
350         switch(scale) {
351                 case SCALE_ASPECT:
352                         ComputeAspectScale(crtc->cur_mode, crtc->native_mode, &outX, &outY);
353                         break;
354                 case SCALE_FULLSCREEN:
355                         outX = crtc->native_mode->HDisplay;
356                         outY = crtc->native_mode->VDisplay;
357                         break;
358                 case SCALE_NOSCALE:
359                 case SCALE_PANEL:
360                 default:
361                         outX = crtc->cur_mode->HDisplay;
362                         outY = crtc->cur_mode->VDisplay;
363                         break;
364         }
365
366         /* Got a better name for SCALER_ACTIVE? */
367         if ((crtc->cur_mode->Flags & V_DBLSCAN) || (crtc->cur_mode->Flags & V_INTERLACE) ||
368                 crtc->cur_mode->HDisplay != outX || crtc->cur_mode->VDisplay != outY) {
369                 scale_val = NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE;
370         } else {
371                 scale_val = NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE;
372         }
373
374         NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_CTRL, scale_val);
375         NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_RES1, outY << 16 | outX);
376         NV50CrtcCommand(crtc, NV50_CRTC0_SCALE_RES2, outY << 16 | outX);
377 }
378
379 /*
380  * Cursor stuff.
381  */
382
383 static void
384 NV50CrtcShowCursor(nouveauCrtcPtr crtc, Bool forced_lock)
385 {
386         ScrnInfoPtr pScrn = crtc->scrn;
387         //xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcShowCursor is called for %s.\n", crtc->index ? "CRTC1" : "CRTC0");
388
389         if (!crtc->modeset_lock)
390                 crtc->cursor_visible = TRUE;
391
392         NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR_CTRL, NV50_CRTC0_CURSOR_CTRL_SHOW);
393
394         /* Calling this during modeset will lock things up. */
395         if (!crtc->modeset_lock && !forced_lock)
396                 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
397 }
398
399 static void
400 NV50CrtcHideCursor(nouveauCrtcPtr crtc, Bool forced_lock)
401 {
402         ScrnInfoPtr pScrn = crtc->scrn;
403         //xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcHideCursor is called for %s.\n", crtc->index ? "CRTC1" : "CRTC0");
404
405         if (!crtc->modeset_lock)
406                 crtc->cursor_visible = FALSE;
407
408         NV50CrtcCommand(crtc, NV50_CRTC0_CURSOR_CTRL, NV50_CRTC0_CURSOR_CTRL_HIDE);
409
410         /* Calling this during modeset will lock things up. */
411         if (!crtc->modeset_lock && !forced_lock)
412                 NV50DisplayCommand(pScrn, NV50_UPDATE_DISPLAY, 0);
413 }
414
415 static void
416 NV50CrtcSetCursorPosition(nouveauCrtcPtr crtc, int x, int y)
417 {
418         ScrnInfoPtr pScrn = crtc->scrn;
419         NVPtr pNv = NVPTR(pScrn);
420         NVWrite(pNv, NV50_CRTC0_CURSOR_POS + crtc->index * 0x1000, (y & 0xFFFF) << 16 | (x & 0xFFFF));
421
422         /* This is needed to allow the cursor to move. */
423         NVWrite(pNv, NV50_CRTC0_CURSOR_POS_CTRL + crtc->index * 0x1000, 0);
424 }
425
426 static void
427 NV50CrtcLoadCursor(nouveauCrtcPtr crtc, Bool argb, uint32_t *src)
428 {
429         if (!argb) /* FIXME */
430                 return;
431
432         NVPtr pNv = NVPTR(crtc->scrn);
433         uint32_t *dst = NULL;
434
435         if (crtc->index == 1)
436                 dst = (uint32_t *) pNv->Cursor2->map;
437         else
438                 dst = (uint32_t *) pNv->Cursor->map;
439
440         /* Assume cursor is 64x64 */
441         memcpy(dst, src, 64 * 64 * 4);
442 }
443
444 /*
445  * Gamma stuff.
446  */
447
448 /*
449  * The indices are a bit strange, but i'll assume it's correct (taken from nv).
450  * The LUT resolution seems to be 14 bits on NV50 as opposed to the 8 bits of previous hardware.
451  */
452 #define NV50_LUT_INDEX(val, w) ((val << (8 - w)) | (val >> ((w << 1) - 8)))
453 static void
454 NV50CrtcGammaSet(nouveauCrtcPtr crtc, uint16_t *red, uint16_t *green, uint16_t *blue, int size)
455 {
456         ScrnInfoPtr pScrn = crtc->scrn;
457         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "NV50CrtcGammaSet is called for %s.\n", crtc->index ? "CRTC1" : "CRTC0");
458
459         NVPtr pNv = NVPTR(pScrn);
460         uint32_t index, i;
461         void * CLUT = NULL;
462
463         /* Each CRTC has it's own CLUT. */
464         if (crtc->index == 1)
465                 CLUT = pNv->CLUT1->map;
466         else
467                 CLUT = pNv->CLUT0->map;
468
469         volatile struct {
470                 unsigned short red, green, blue, unused;
471         } *lut = (void *) CLUT;
472
473         switch (pScrn->depth) {
474         case 15:
475                 /* R5G5B5 */
476                 for (i = 0; i < 32; i++) {
477                         index = NV50_LUT_INDEX(i, 5);
478                         lut[index].red = red[i] >> 2;
479                         lut[index].green = green[i] >> 2;
480                         lut[index].blue = blue[i] >> 2;
481                 }
482                 break;
483         case 16:
484                 /* R5G6B5 */
485                 for (i = 0; i < 32; i++) {
486                         index = NV50_LUT_INDEX(i, 5);
487                         lut[index].red = red[i] >> 2;
488                         lut[index].blue = blue[i] >> 2;
489                 }
490
491                 /* Green has an extra bit. */
492                 for (i = 0; i < 64; i++) {
493                         index = NV50_LUT_INDEX(i, 6);
494                         lut[index].green = green[i] >> 2;
495                 }
496                 break;
497         default:
498                 /* R8G8B8 */
499                 for (i = 0; i < 256; i++) {
500                         lut[i].red = red[i] >> 2;
501                         lut[i].green = green[i] >> 2;
502                         lut[i].blue = blue[i] >> 2;
503                 }
504                 break;
505         }
506 }
507
508 void
509 NV50CrtcInit(ScrnInfoPtr pScrn)
510 {
511         int i;
512         NVPtr pNv = NVPTR(pScrn);
513
514         for (i=0; i < 2; i++) {
515                 nouveauCrtcPtr crtc = xnfcalloc(sizeof(nouveauCrtcRec), 1);
516                 crtc->scrn = pScrn;
517                 crtc->index = i;
518
519                 /* Function pointers. */
520                 crtc->ModeValid = NV50CrtcModeValid;
521                 crtc->ModeSet = NV50CrtcModeSet;
522                 crtc->SetPixelClock = NV50CrtcSetPixelClock;
523                 crtc->SetClockMode = NV50CrtcSetClockMode;
524
525                 crtc->SetFB = NV50CrtcSetFB;
526                 crtc->SetFBOffset = NV50CrtcSetFBOffset;
527
528                 crtc->Blank = NV50CrtcBlank;
529                 crtc->SetDither = NV50CrtcSetDither;
530
531                 crtc->SetScaleMode = NV50CrtcSetScaleMode;
532
533                 crtc->ShowCursor = NV50CrtcShowCursor;
534                 crtc->HideCursor = NV50CrtcHideCursor;
535                 crtc->SetCursorPosition = NV50CrtcSetCursorPosition;
536                 crtc->LoadCursor = NV50CrtcLoadCursor;
537
538                 crtc->GammaSet = NV50CrtcGammaSet;
539
540                 pNv->crtc[i] = crtc;
541         }
542 }
543
544 void
545 NV50CrtcDestroy(ScrnInfoPtr pScrn)
546 {
547         int i;
548         NVPtr pNv = NVPTR(pScrn);
549
550         for (i=0; i < 2; i++) {
551                 nouveauCrtcPtr crtc = pNv->crtc[i];
552
553                 xfree(crtc->name);
554                 xfree(crtc);
555                 pNv->crtc[i] = NULL;
556         }
557 }