NV50: A minor change.
[platform/upstream/libdrm.git] / linux-core / nv50_crtc.c
1 /*
2  * Copyright (C) 2008 Maarten Maathuis.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining
6  * a copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sublicense, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial
15  * portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  */
26
27 #include "nv50_crtc.h"
28 #include "nv50_cursor.h"
29 #include "nv50_lut.h"
30 #include "nv50_fb.h"
31
32 static int nv50_crtc_validate_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode)
33 {
34         NV50_DEBUG("\n");
35
36         if (mode->clock > 400000)
37                 return MODE_CLOCK_HIGH;
38
39         if (mode->clock < 25000)
40                 return MODE_CLOCK_LOW;
41
42         return MODE_OK;
43 }
44
45 static int nv50_crtc_set_mode(struct nv50_crtc *crtc, struct nouveau_hw_mode *mode)
46 {
47         struct nouveau_hw_mode *hw_mode = crtc->mode;
48         uint8_t rval;
49
50         NV50_DEBUG("index %d\n", crtc->index);
51
52         if (!mode) {
53                 DRM_ERROR("No mode\n");
54                 return MODE_NOMODE;
55         }
56
57         if ((rval = crtc->validate_mode(crtc, mode))) {
58                 DRM_ERROR("Mode invalid\n");
59                 return rval;
60         }
61
62         /* copy values to mode */
63         *hw_mode = *mode;
64
65         return 0;
66 }
67
68 static int nv50_crtc_execute_mode(struct nv50_crtc *crtc)
69 {
70         struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
71         struct nouveau_hw_mode *hw_mode;
72         uint32_t hsync_dur,  vsync_dur, hsync_start_to_end, vsync_start_to_end;
73         uint32_t hunk1, vunk1, vunk2a, vunk2b;
74         uint32_t offset = crtc->index * 0x400;
75
76         NV50_DEBUG("index %d\n", crtc->index);
77         NV50_DEBUG("%s native mode\n", crtc->use_native_mode ? "using" : "not using");
78
79         if (crtc->use_native_mode)
80                 hw_mode = crtc->native_mode;
81         else
82                 hw_mode = crtc->mode;
83
84         hsync_dur = hw_mode->hsync_end - hw_mode->hsync_start;
85         vsync_dur = hw_mode->vsync_end - hw_mode->vsync_start;
86         hsync_start_to_end = hw_mode->hblank_end - hw_mode->hsync_start;
87         vsync_start_to_end = hw_mode->vblank_end - hw_mode->vsync_start;
88         /* I can't give this a proper name, anyone else can? */
89         hunk1 = hw_mode->htotal - hw_mode->hsync_start + hw_mode->hblank_start;
90         vunk1 = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start;
91         /* Another strange value, this time only for interlaced modes. */
92         vunk2a = 2*hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_start;
93         vunk2b = hw_mode->vtotal - hw_mode->vsync_start + hw_mode->vblank_end;
94
95         if (hw_mode->flags & V_INTERLACE) {
96                 vsync_dur /= 2;
97                 vsync_start_to_end  /= 2;
98                 vunk1 /= 2;
99                 vunk2a /= 2;
100                 vunk2b /= 2;
101                 /* magic */
102                 if (hw_mode->flags & V_DBLSCAN) {
103                         vsync_start_to_end -= 1;
104                         vunk1 -= 1;
105                         vunk2a -= 1;
106                         vunk2b -= 1;
107                 }
108         }
109
110         OUT_MODE(NV50_CRTC0_CLOCK + offset, hw_mode->clock | 0x800000);
111         OUT_MODE(NV50_CRTC0_INTERLACE + offset, (hw_mode->flags & V_INTERLACE) ? 2 : 0);
112         OUT_MODE(NV50_CRTC0_DISPLAY_START + offset, 0);
113         OUT_MODE(NV50_CRTC0_UNK82C + offset, 0);
114         OUT_MODE(NV50_CRTC0_DISPLAY_TOTAL + offset, hw_mode->vtotal << 16 | hw_mode->htotal);
115         OUT_MODE(NV50_CRTC0_SYNC_DURATION + offset, (vsync_dur - 1) << 16 | (hsync_dur - 1));
116         OUT_MODE(NV50_CRTC0_SYNC_START_TO_BLANK_END + offset, (vsync_start_to_end - 1) << 16 | (hsync_start_to_end - 1));
117         OUT_MODE(NV50_CRTC0_MODE_UNK1 + offset, (vunk1 - 1) << 16 | (hunk1 - 1));
118         if (hw_mode->flags & V_INTERLACE) {
119                 OUT_MODE(NV50_CRTC0_MODE_UNK2 + offset, (vunk2b - 1) << 16 | (vunk2a - 1));
120         }
121
122         crtc->set_fb(crtc);
123         crtc->set_dither(crtc);
124
125         /* This is the actual resolution of the mode. */
126         OUT_MODE(NV50_CRTC0_REAL_RES + offset, (crtc->mode->vdisplay << 16) | crtc->mode->hdisplay);
127         OUT_MODE(NV50_CRTC0_SCALE_CENTER_OFFSET + offset, NV50_CRTC_SCALE_CENTER_OFFSET_VAL(0,0));
128
129         /* Maybe move this as well? */
130         crtc->blank(crtc, FALSE);
131
132         return 0;
133 }
134
135 static int nv50_crtc_set_fb(struct nv50_crtc *crtc)
136 {
137         struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
138         uint32_t offset = crtc->index * 0x400;
139
140         NV50_DEBUG("\n");
141
142         OUT_MODE(NV50_CRTC0_FB_SIZE + offset, crtc->fb->height << 16 | crtc->fb->width);
143
144         /* I suspect this flag indicates a linear fb. */
145         OUT_MODE(NV50_CRTC0_FB_PITCH + offset, crtc->fb->pitch | 0x100000);
146
147         switch (crtc->fb->depth) {
148                 case 8:
149                         OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_8BPP); 
150                         break;
151                 case 15:
152                         OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_15BPP);
153                         break;
154                 case 16:
155                         OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_16BPP);
156                         break;
157                 case 24:
158                         OUT_MODE(NV50_CRTC0_DEPTH + offset, NV50_CRTC0_DEPTH_24BPP); 
159                         break;
160         }
161
162         OUT_MODE(NV50_CRTC0_COLOR_CTRL + offset, NV50_CRTC_COLOR_CTRL_MODE_COLOR);
163         OUT_MODE(NV50_CRTC0_FB_POS + offset, (crtc->fb->y << 16) | (crtc->fb->x));
164
165         return 0;
166 }
167
168 static int nv50_crtc_blank(struct nv50_crtc *crtc, bool blanked)
169 {
170         struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
171         uint32_t offset = crtc->index * 0x400;
172
173         NV50_DEBUG("index %d\n", crtc->index);
174         NV50_DEBUG("%s\n", blanked ? "blanked" : "unblanked");
175
176         /* We really need a framebuffer. */
177         if (!crtc->fb->block && !blanked) {
178                 DRM_ERROR("No framebuffer available on crtc %d\n", crtc->index);
179                 return -EINVAL;
180         }
181
182         if (blanked) {
183                 crtc->cursor->hide(crtc);
184
185                 OUT_MODE(NV50_CRTC0_CLUT_MODE + offset, NV50_CRTC0_CLUT_MODE_BLANK);
186                 OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, 0);
187                 if (dev_priv->chipset != 0x50)
188                         OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_BLANK);
189                 OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_BLANK);
190                 if (dev_priv->chipset != 0x50)
191                         OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_BLANK);
192         } else {
193                 OUT_MODE(NV50_CRTC0_FB_OFFSET + offset, crtc->fb->block->start >> 8);
194                 OUT_MODE(0x864 + offset, 0);
195
196                 crtc->cursor->set_offset(crtc);
197
198                 if (dev_priv->chipset != 0x50)
199                         OUT_MODE(NV84_CRTC0_BLANK_UNK2 + offset, NV84_CRTC0_BLANK_UNK2_UNBLANK);
200
201                 if (crtc->cursor->visible)
202                         crtc->cursor->show(crtc);
203                 else
204                         crtc->cursor->hide(crtc);
205
206                 OUT_MODE(NV50_CRTC0_CLUT_MODE + offset, 
207                         crtc->fb->depth == 8 ? NV50_CRTC0_CLUT_MODE_OFF : NV50_CRTC0_CLUT_MODE_ON);
208                 OUT_MODE(NV50_CRTC0_CLUT_OFFSET + offset, crtc->lut->block->start >> 8);
209                 if (dev_priv->chipset != 0x50)
210                         OUT_MODE(NV84_CRTC0_BLANK_UNK1 + offset, NV84_CRTC0_BLANK_UNK1_UNBLANK);
211                 OUT_MODE(NV50_CRTC0_BLANK_CTRL + offset, NV50_CRTC0_BLANK_CTRL_UNBLANK);
212         }
213
214         /* sometimes you need to know if a screen is already blanked. */
215         crtc->blanked = blanked;
216
217         return 0;
218 }
219
220 static int nv50_crtc_set_dither(struct nv50_crtc *crtc)
221 {
222         struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
223         uint32_t offset = crtc->index * 0x400;
224
225         NV50_DEBUG("\n");
226
227         OUT_MODE(NV50_CRTC0_DITHERING_CTRL + offset, crtc->use_dithering ? 
228                         NV50_CRTC0_DITHERING_CTRL_ON : NV50_CRTC0_DITHERING_CTRL_OFF);
229
230         return 0;
231 }
232
233 static void nv50_crtc_calc_scale(struct nv50_crtc *crtc, uint32_t *outX, uint32_t *outY)
234 {
235         float hor_scale, ver_scale;
236
237         hor_scale = (float)crtc->native_mode->hdisplay/(float)crtc->mode->hdisplay;
238         ver_scale = (float)crtc->native_mode->vdisplay/(float)crtc->mode->vdisplay;
239
240         if (ver_scale > hor_scale) {
241                 *outX = crtc->mode->hdisplay * hor_scale;
242                 *outY = crtc->mode->vdisplay * hor_scale;
243         } else {
244                 *outX = crtc->mode->hdisplay * ver_scale;
245                 *outY = crtc->mode->vdisplay * ver_scale;
246         }
247 }
248
249 static int nv50_crtc_set_scale(struct nv50_crtc *crtc)
250 {
251         struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
252         uint32_t offset = crtc->index * 0x400;
253         uint32_t outX, outY;
254
255         NV50_DEBUG("\n");
256
257         switch (crtc->scaling_mode) {
258                 case SCALE_ASPECT:
259                         nv50_crtc_calc_scale(crtc, &outX, &outY);
260                         break;
261                 case SCALE_FULLSCREEN:
262                         outX = crtc->native_mode->hdisplay;
263                         outY = crtc->native_mode->vdisplay;
264                         break;
265                 case SCALE_NOSCALE:
266                 case SCALE_PANEL:
267                 default:
268                         outX = crtc->mode->hdisplay;
269                         outY = crtc->mode->vdisplay;
270                         break;
271         }
272
273         /* Got a better name for SCALER_ACTIVE? */
274         /* One day i've got to really figure out why this is needed. */
275         if ((crtc->mode->flags & V_DBLSCAN) || (crtc->mode->flags & V_INTERLACE) ||
276                 crtc->mode->hdisplay != outX || crtc->mode->vdisplay != outY) {
277                 OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE);
278         } else {
279                 OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE);
280         }
281
282         OUT_MODE(NV50_CRTC0_SCALE_RES1 + offset, outY << 16 | outX);
283         OUT_MODE(NV50_CRTC0_SCALE_RES2 + offset, outY << 16 | outX);
284
285         return 0;
286 }
287
288 static int nv50_crtc_calc_clock(struct nv50_crtc *crtc, 
289         uint32_t *bestN1, uint32_t *bestN2, uint32_t *bestM1, uint32_t *bestM2, uint32_t *bestlog2P)
290 {
291         struct nouveau_hw_mode *hw_mode;
292         struct pll_lims limits;
293         int clk, vco2, crystal;
294         int minvco1, minvco2, minU1, maxU1, minU2, maxU2, minM1, maxM1;
295         int maxvco1, maxvco2, minN1, maxN1, minM2, maxM2, minN2, maxN2;
296         bool fixedgain2;
297         int M1, N1, M2, N2, log2P;
298         int clkP, calcclk1, calcclk2, calcclkout;
299         int delta, bestdelta = INT_MAX;
300         int bestclk = 0;
301
302         NV50_DEBUG("\n");
303
304         if (crtc->use_native_mode)
305                 hw_mode = crtc->native_mode;
306         else
307                 hw_mode = crtc->mode;
308
309         clk = hw_mode->clock;
310
311         /* These are in the g80 bios tables, at least in mine. */
312         if (!get_pll_limits(crtc->dev, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index), &limits))
313                 return -EINVAL;
314
315         minvco1 = limits.vco1.minfreq, maxvco1 = limits.vco1.maxfreq;
316         minvco2 = limits.vco2.minfreq, maxvco2 = limits.vco2.maxfreq;
317         minU1 = limits.vco1.min_inputfreq, minU2 = limits.vco2.min_inputfreq;
318         maxU1 = limits.vco1.max_inputfreq, maxU2 = limits.vco2.max_inputfreq;
319         minM1 = limits.vco1.min_m, maxM1 = limits.vco1.max_m;
320         minN1 = limits.vco1.min_n, maxN1 = limits.vco1.max_n;
321         minM2 = limits.vco2.min_m, maxM2 = limits.vco2.max_m;
322         minN2 = limits.vco2.min_n, maxN2 = limits.vco2.max_n;
323         crystal = limits.refclk;
324         fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
325
326         vco2 = (maxvco2 - maxvco2/200) / 2;
327         for (log2P = 0; clk && log2P < 6 && clk <= (vco2 >> log2P); log2P++) /* log2P is maximum of 6 */
328                 ;
329         clkP = clk << log2P;
330
331         if (maxvco2 < clk + clk/200)    /* +0.5% */
332                 maxvco2 = clk + clk/200;
333
334         for (M1 = minM1; M1 <= maxM1; M1++) {
335                 if (crystal/M1 < minU1)
336                         return bestclk;
337                 if (crystal/M1 > maxU1)
338                         continue;
339
340                 for (N1 = minN1; N1 <= maxN1; N1++) {
341                         calcclk1 = crystal * N1 / M1;
342                         if (calcclk1 < minvco1)
343                                 continue;
344                         if (calcclk1 > maxvco1)
345                                 break;
346
347                         for (M2 = minM2; M2 <= maxM2; M2++) {
348                                 if (calcclk1/M2 < minU2)
349                                         break;
350                                 if (calcclk1/M2 > maxU2)
351                                         continue;
352
353                                 /* add calcclk1/2 to round better */
354                                 N2 = (clkP * M2 + calcclk1/2) / calcclk1;
355                                 if (N2 < minN2)
356                                         continue;
357                                 if (N2 > maxN2)
358                                         break;
359
360                                 if (!fixedgain2) {
361                                         calcclk2 = calcclk1 * N2 / M2;
362                                         if (calcclk2 < minvco2)
363                                                 break;
364                                         if (calcclk2 > maxvco2)
365                                                 continue;
366                                 } else
367                                         calcclk2 = calcclk1;
368
369                                 calcclkout = calcclk2 >> log2P;
370                                 delta = abs(calcclkout - clk);
371                                 /* we do an exhaustive search rather than terminating
372                                  * on an optimality condition...
373                                  */
374                                 if (delta < bestdelta) {
375                                         bestdelta = delta;
376                                         bestclk = calcclkout;
377                                         *bestN1 = N1;
378                                         *bestN2 = N2;
379                                         *bestM1 = M1;
380                                         *bestM2 = M2;
381                                         *bestlog2P = log2P;
382                                         if (delta == 0) /* except this one */
383                                                 return bestclk;
384                                 }
385                         }
386                 }
387         }
388
389         return bestclk;
390 }
391
392 static int nv50_crtc_set_clock(struct nv50_crtc *crtc)
393 {
394         struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
395
396         uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index);
397
398         uint32_t N1 = 0, N2 = 0, M1 = 0, M2 = 0, log2P = 0;
399
400         uint32_t reg1 = NV_READ(pll_reg + 4);
401         uint32_t reg2 = NV_READ(pll_reg + 8);
402
403         NV50_DEBUG("\n");
404
405         NV_WRITE(pll_reg, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED | 0x10000011);
406
407         /* The other bits are typically empty, but let's be on the safe side. */
408         reg1 &= 0xff00ff00;
409         reg2 &= 0x8000ff00;
410
411         if (!nv50_crtc_calc_clock(crtc, &N1, &N2, &M1, &M2, &log2P))
412                 return -EINVAL;
413
414         NV50_DEBUG("N1 %d N2 %d M1 %d M2 %d log2P %d\n", N1, N2, M1, M2, log2P);
415
416         reg1 |= (M1 << 16) | N1;
417         reg2 |= (log2P << 28) | (M2 << 16) | N2;
418
419         NV_WRITE(pll_reg + 4, reg1);
420         NV_WRITE(pll_reg + 8, reg2);
421
422         return 0;
423 }
424
425 static int nv50_crtc_set_clock_mode(struct nv50_crtc *crtc)
426 {
427         struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
428
429         NV50_DEBUG("\n");
430
431         /* This acknowledges a clock request. */
432         NV_WRITE(NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(crtc->index), 0);
433
434         return 0;
435 }
436
437 static int nv50_crtc_destroy(struct nv50_crtc *crtc)
438 {
439         struct drm_device *dev = crtc->dev;
440         struct drm_nouveau_private *dev_priv = dev->dev_private;
441         struct nv50_display *display = nv50_get_display(dev);
442
443         NV50_DEBUG("\n");
444
445         if (!display || !crtc)
446                 return -EINVAL;
447
448         list_del(&crtc->head);
449
450         nv50_fb_destroy(crtc);
451         nv50_lut_destroy(crtc);
452         nv50_cursor_destroy(crtc);
453
454         kfree(crtc->mode);
455         kfree(crtc->native_mode);
456
457         if (dev_priv->free_crtc)
458                 dev_priv->free_crtc(crtc);
459
460         return 0;
461 }
462
463 int nv50_crtc_create(struct drm_device *dev, int index)
464 {
465         struct drm_nouveau_private *dev_priv = dev->dev_private;
466         struct nv50_crtc *crtc = NULL;
467         struct nv50_display *display = NULL;
468         int rval = 0;
469
470         NV50_DEBUG("\n");
471
472         /* This allows the public layer to do it's thing. */
473         if (dev_priv->alloc_crtc)
474                 crtc = dev_priv->alloc_crtc(dev);
475
476         if (!crtc)
477                 return -ENOMEM;
478
479         crtc->dev = dev;
480
481         display = nv50_get_display(dev);
482         if (!display) {
483                 rval = -EINVAL;
484                 goto out;
485         }
486
487         list_add_tail(&crtc->head, &display->crtcs);
488
489         crtc->index = index;
490
491         crtc->mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
492         crtc->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
493
494         if (!crtc->mode || !crtc->native_mode) {
495                 rval = -ENOMEM;
496                 goto out;
497         }
498
499         nv50_fb_create(crtc);
500         nv50_lut_create(crtc);
501         nv50_cursor_create(crtc);
502
503         /* set function pointers */
504         crtc->validate_mode = nv50_crtc_validate_mode;
505         crtc->set_mode = nv50_crtc_set_mode;
506         crtc->execute_mode = nv50_crtc_execute_mode;
507         crtc->set_fb = nv50_crtc_set_fb;
508         crtc->blank = nv50_crtc_blank;
509         crtc->set_dither = nv50_crtc_set_dither;
510         crtc->set_scale = nv50_crtc_set_scale;
511         crtc->set_clock = nv50_crtc_set_clock;
512         crtc->set_clock_mode = nv50_crtc_set_clock_mode;
513         crtc->destroy = nv50_crtc_destroy;
514
515         return 0;
516
517 out:
518         if (crtc->mode)
519                 kfree(crtc->mode);
520         if (crtc->native_mode)
521                 kfree(crtc->native_mode);
522         if (dev_priv->free_crtc)
523                 dev_priv->free_crtc(crtc);
524
525         return rval;
526 }