aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'linux-core/nv50_crtc.c')
-rw-r--r--linux-core/nv50_crtc.c533
1 files changed, 0 insertions, 533 deletions
diff --git a/linux-core/nv50_crtc.c b/linux-core/nv50_crtc.c
deleted file mode 100644
index f56aa339..00000000
--- a/linux-core/nv50_crtc.c
+++ /dev/null
@@ -1,533 +0,0 @@
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
32static 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
45static 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
68static 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 & DRM_MODE_FLAG_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 & DRM_MODE_FLAG_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 & DRM_MODE_FLAG_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 & DRM_MODE_FLAG_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
135static 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
168static 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
220static 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
233static void nv50_crtc_calc_scale(struct nv50_crtc *crtc, uint32_t *outX, uint32_t *outY)
234{
235 uint32_t hor_scale, ver_scale;
236
237 /* max res is 8192, which is 2^13, which leaves 19 bits */
238 hor_scale = (crtc->native_mode->hdisplay << 19)/crtc->mode->hdisplay;
239 ver_scale = (crtc->native_mode->vdisplay << 19)/crtc->mode->vdisplay;
240
241 if (ver_scale > hor_scale) {
242 *outX = (crtc->mode->hdisplay * hor_scale) >> 19;
243 *outY = (crtc->mode->vdisplay * hor_scale) >> 19;
244 } else {
245 *outX = (crtc->mode->hdisplay * ver_scale) >> 19;
246 *outY = (crtc->mode->vdisplay * ver_scale) >> 19;
247 }
248}
249
250static int nv50_crtc_set_scale(struct nv50_crtc *crtc)
251{
252 struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
253 uint32_t offset = crtc->index * 0x400;
254 uint32_t outX, outY;
255
256 NV50_DEBUG("\n");
257
258 switch (crtc->requested_scaling_mode) {
259 case SCALE_ASPECT:
260 nv50_crtc_calc_scale(crtc, &outX, &outY);
261 break;
262 case SCALE_FULLSCREEN:
263 outX = crtc->native_mode->hdisplay;
264 outY = crtc->native_mode->vdisplay;
265 break;
266 case SCALE_NOSCALE:
267 case SCALE_NON_GPU:
268 default:
269 outX = crtc->mode->hdisplay;
270 outY = crtc->mode->vdisplay;
271 break;
272 }
273
274 /* Got a better name for SCALER_ACTIVE? */
275 /* One day i've got to really figure out why this is needed. */
276 if ((crtc->mode->flags & DRM_MODE_FLAG_DBLSCAN) || (crtc->mode->flags & DRM_MODE_FLAG_INTERLACE) ||
277 crtc->mode->hdisplay != outX || crtc->mode->vdisplay != outY) {
278 OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_ACTIVE);
279 } else {
280 OUT_MODE(NV50_CRTC0_SCALE_CTRL + offset, NV50_CRTC0_SCALE_CTRL_SCALER_INACTIVE);
281 }
282
283 OUT_MODE(NV50_CRTC0_SCALE_RES1 + offset, outY << 16 | outX);
284 OUT_MODE(NV50_CRTC0_SCALE_RES2 + offset, outY << 16 | outX);
285
286 /* processed */
287 crtc->scaling_mode = crtc->requested_scaling_mode;
288
289 return 0;
290}
291
292static int nv50_crtc_calc_clock(struct nv50_crtc *crtc,
293 uint32_t *bestN1, uint32_t *bestN2, uint32_t *bestM1, uint32_t *bestM2, uint32_t *bestlog2P)
294{
295 struct nouveau_hw_mode *hw_mode;
296 struct pll_lims limits;
297 int clk, vco2, crystal;
298 int minvco1, minvco2, minU1, maxU1, minU2, maxU2, minM1, maxM1;
299 int maxvco1, maxvco2, minN1, maxN1, minM2, maxM2, minN2, maxN2;
300 bool fixedgain2;
301 int M1, N1, M2, N2, log2P;
302 int clkP, calcclk1, calcclk2, calcclkout;
303 int delta, bestdelta = INT_MAX;
304 int bestclk = 0;
305
306 NV50_DEBUG("\n");
307
308 if (crtc->use_native_mode)
309 hw_mode = crtc->native_mode;
310 else
311 hw_mode = crtc->mode;
312
313 clk = hw_mode->clock;
314
315 /* These are in the g80 bios tables, at least in mine. */
316 if (!get_pll_limits(crtc->dev, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index), &limits))
317 return -EINVAL;
318
319 minvco1 = limits.vco1.minfreq, maxvco1 = limits.vco1.maxfreq;
320 minvco2 = limits.vco2.minfreq, maxvco2 = limits.vco2.maxfreq;
321 minU1 = limits.vco1.min_inputfreq, minU2 = limits.vco2.min_inputfreq;
322 maxU1 = limits.vco1.max_inputfreq, maxU2 = limits.vco2.max_inputfreq;
323 minM1 = limits.vco1.min_m, maxM1 = limits.vco1.max_m;
324 minN1 = limits.vco1.min_n, maxN1 = limits.vco1.max_n;
325 minM2 = limits.vco2.min_m, maxM2 = limits.vco2.max_m;
326 minN2 = limits.vco2.min_n, maxN2 = limits.vco2.max_n;
327 crystal = limits.refclk;
328 fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
329
330 vco2 = (maxvco2 - maxvco2/200) / 2;
331 for (log2P = 0; clk && log2P < 6 && clk <= (vco2 >> log2P); log2P++) /* log2P is maximum of 6 */
332 ;
333 clkP = clk << log2P;
334
335 if (maxvco2 < clk + clk/200) /* +0.5% */
336 maxvco2 = clk + clk/200;
337
338 for (M1 = minM1; M1 <= maxM1; M1++) {
339 if (crystal/M1 < minU1)
340 return bestclk;
341 if (crystal/M1 > maxU1)
342 continue;
343
344 for (N1 = minN1; N1 <= maxN1; N1++) {
345 calcclk1 = crystal * N1 / M1;
346 if (calcclk1 < minvco1)
347 continue;
348 if (calcclk1 > maxvco1)
349 break;
350
351 for (M2 = minM2; M2 <= maxM2; M2++) {
352 if (calcclk1/M2 < minU2)
353 break;
354 if (calcclk1/M2 > maxU2)
355 continue;
356
357 /* add calcclk1/2 to round better */
358 N2 = (clkP * M2 + calcclk1/2) / calcclk1;
359 if (N2 < minN2)
360 continue;
361 if (N2 > maxN2)
362 break;
363
364 if (!fixedgain2) {
365 calcclk2 = calcclk1 * N2 / M2;
366 if (calcclk2 < minvco2)
367 break;
368 if (calcclk2 > maxvco2)
369 continue;
370 } else
371 calcclk2 = calcclk1;
372
373 calcclkout = calcclk2 >> log2P;
374 delta = abs(calcclkout - clk);
375 /* we do an exhaustive search rather than terminating
376 * on an optimality condition...
377 */
378 if (delta < bestdelta) {
379 bestdelta = delta;
380 bestclk = calcclkout;
381 *bestN1 = N1;
382 *bestN2 = N2;
383 *bestM1 = M1;
384 *bestM2 = M2;
385 *bestlog2P = log2P;
386 if (delta == 0) /* except this one */
387 return bestclk;
388 }
389 }
390 }
391 }
392
393 return bestclk;
394}
395
396static int nv50_crtc_set_clock(struct nv50_crtc *crtc)
397{
398 struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
399
400 uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1(crtc->index);
401
402 uint32_t N1 = 0, N2 = 0, M1 = 0, M2 = 0, log2P = 0;
403
404 uint32_t reg1 = NV_READ(pll_reg + 4);
405 uint32_t reg2 = NV_READ(pll_reg + 8);
406
407 NV50_DEBUG("\n");
408
409 NV_WRITE(pll_reg, NV50_PDISPLAY_CRTC_CLK_CLK_CTRL1_CONNECTED | 0x10000011);
410
411 /* The other bits are typically empty, but let's be on the safe side. */
412 reg1 &= 0xff00ff00;
413 reg2 &= 0x8000ff00;
414
415 if (!nv50_crtc_calc_clock(crtc, &N1, &N2, &M1, &M2, &log2P))
416 return -EINVAL;
417
418 NV50_DEBUG("N1 %d N2 %d M1 %d M2 %d log2P %d\n", N1, N2, M1, M2, log2P);
419
420 reg1 |= (M1 << 16) | N1;
421 reg2 |= (log2P << 28) | (M2 << 16) | N2;
422
423 NV_WRITE(pll_reg + 4, reg1);
424 NV_WRITE(pll_reg + 8, reg2);
425
426 return 0;
427}
428
429static int nv50_crtc_set_clock_mode(struct nv50_crtc *crtc)
430{
431 struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
432
433 NV50_DEBUG("\n");
434
435 /* This acknowledges a clock request. */
436 NV_WRITE(NV50_PDISPLAY_CRTC_CLK_CLK_CTRL2(crtc->index), 0);
437
438 return 0;
439}
440
441static int nv50_crtc_destroy(struct nv50_crtc *crtc)
442{
443 struct drm_device *dev = crtc->dev;
444 struct drm_nouveau_private *dev_priv = dev->dev_private;
445 struct nv50_display *display = nv50_get_display(dev);
446
447 NV50_DEBUG("\n");
448
449 if (!display || !crtc)
450 return -EINVAL;
451
452 list_del(&crtc->item);
453
454 nv50_fb_destroy(crtc);
455 nv50_lut_destroy(crtc);
456 nv50_cursor_destroy(crtc);
457
458 kfree(crtc->mode);
459 kfree(crtc->native_mode);
460
461 if (dev_priv->free_crtc)
462 dev_priv->free_crtc(crtc);
463
464 return 0;
465}
466
467int nv50_crtc_create(struct drm_device *dev, int index)
468{
469 struct drm_nouveau_private *dev_priv = dev->dev_private;
470 struct nv50_crtc *crtc = NULL;
471 struct nv50_display *display = NULL;
472 int rval = 0;
473
474 NV50_DEBUG("\n");
475
476 /* This allows the public layer to do it's thing. */
477 if (dev_priv->alloc_crtc)
478 crtc = dev_priv->alloc_crtc(dev);
479
480 if (!crtc)
481 return -ENOMEM;
482
483 crtc->dev = dev;
484
485 display = nv50_get_display(dev);
486 if (!display) {
487 rval = -EINVAL;
488 goto out;
489 }
490
491 list_add_tail(&crtc->item, &display->crtcs);
492
493 crtc->index = index;
494
495 crtc->mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
496 crtc->native_mode = kzalloc(sizeof(struct nouveau_hw_mode), GFP_KERNEL);
497
498 crtc->requested_scaling_mode = SCALE_INVALID;
499 crtc->scaling_mode = SCALE_INVALID;
500
501 if (!crtc->mode || !crtc->native_mode) {
502 rval = -ENOMEM;
503 goto out;
504 }
505
506 nv50_fb_create(crtc);
507 nv50_lut_create(crtc);
508 nv50_cursor_create(crtc);
509
510 /* set function pointers */
511 crtc->validate_mode = nv50_crtc_validate_mode;
512 crtc->set_mode = nv50_crtc_set_mode;
513 crtc->execute_mode = nv50_crtc_execute_mode;
514 crtc->set_fb = nv50_crtc_set_fb;
515 crtc->blank = nv50_crtc_blank;
516 crtc->set_dither = nv50_crtc_set_dither;
517 crtc->set_scale = nv50_crtc_set_scale;
518 crtc->set_clock = nv50_crtc_set_clock;
519 crtc->set_clock_mode = nv50_crtc_set_clock_mode;
520 crtc->destroy = nv50_crtc_destroy;
521
522 return 0;
523
524out:
525 if (crtc->mode)
526 kfree(crtc->mode);
527 if (crtc->native_mode)
528 kfree(crtc->native_mode);
529 if (dev_priv->free_crtc)
530 dev_priv->free_crtc(crtc);
531
532 return rval;
533}