blob: 8c3a25360232d73e245b43452342ca43177a3a4a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/video/nvidia/nvidia.c - nVidia fb driver
3 *
4 * Copyright 2004 Antonino Daplas <adaplas@pol.net>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 *
10 */
11
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/errno.h>
15#include <linux/string.h>
16#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/slab.h>
18#include <linux/delay.h>
19#include <linux/fb.h>
20#include <linux/init.h>
21#include <linux/pci.h>
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -080022#include <linux/console.h>
Michael Hanselmann5474c122006-06-25 05:47:08 -070023#include <linux/backlight.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#ifdef CONFIG_MTRR
25#include <asm/mtrr.h>
26#endif
27#ifdef CONFIG_PPC_OF
28#include <asm/prom.h>
29#include <asm/pci-bridge.h>
30#endif
Paul Mackerras70abac62006-10-03 01:15:14 -070031#ifdef CONFIG_BOOTX_TEXT
32#include <asm/btext.h>
33#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
35#include "nv_local.h"
36#include "nv_type.h"
37#include "nv_proto.h"
38#include "nv_dma.h"
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#ifdef CONFIG_FB_NVIDIA_DEBUG
41#define NVTRACE printk
42#else
43#define NVTRACE if (0) printk
44#endif
45
46#define NVTRACE_ENTER(...) NVTRACE("%s START\n", __FUNCTION__)
47#define NVTRACE_LEAVE(...) NVTRACE("%s END\n", __FUNCTION__)
48
49#ifdef CONFIG_FB_NVIDIA_DEBUG
50#define assert(expr) \
51 if (!(expr)) { \
52 printk( "Assertion failed! %s,%s,%s,line=%d\n",\
53 #expr,__FILE__,__FUNCTION__,__LINE__); \
54 BUG(); \
55 }
56#else
57#define assert(expr)
58#endif
59
60#define PFX "nvidiafb: "
61
62/* HW cursor parameters */
63#define MAX_CURS 32
64
65static struct pci_device_id nvidiafb_pci_tbl[] = {
Antonino A. Daplas8eec4982006-06-26 00:26:30 -070066 {PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
67 PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0},
68 { 0, }
Linus Torvalds1da177e2005-04-16 15:20:36 -070069};
Linus Torvalds1da177e2005-04-16 15:20:36 -070070MODULE_DEVICE_TABLE(pci, nvidiafb_pci_tbl);
71
72/* command line data, set in nvidiafb_setup() */
73static int flatpanel __devinitdata = -1; /* Autodetect later */
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -080074static int fpdither __devinitdata = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static int forceCRTC __devinitdata = -1;
76static int hwcur __devinitdata = 0;
77static int noaccel __devinitdata = 0;
78static int noscale __devinitdata = 0;
79static int paneltweak __devinitdata = 0;
Antonino A. Daplas917bb072005-05-01 08:59:22 -070080static int vram __devinitdata = 0;
Antonino A. Daplasade91852006-01-09 20:53:39 -080081static int bpp __devinitdata = 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082#ifdef CONFIG_MTRR
83static int nomtrr __devinitdata = 0;
84#endif
Richard Purdie202d4e62007-03-03 17:43:52 +000085#ifdef CONFIG_PMAC_BACKLIGHT
86static int backlight __devinitdata = 1;
87#else
88static int backlight __devinitdata = 0;
89#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
91static char *mode_option __devinitdata = NULL;
92
93static struct fb_fix_screeninfo __devinitdata nvidiafb_fix = {
94 .type = FB_TYPE_PACKED_PIXELS,
95 .xpanstep = 8,
96 .ypanstep = 1,
97};
98
99static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = {
100 .xres = 640,
101 .yres = 480,
102 .xres_virtual = 640,
103 .yres_virtual = 480,
104 .bits_per_pixel = 8,
105 .red = {0, 8, 0},
106 .green = {0, 8, 0},
107 .blue = {0, 8, 0},
108 .transp = {0, 0, 0},
109 .activate = FB_ACTIVATE_NOW,
110 .height = -1,
111 .width = -1,
112 .pixclock = 39721,
113 .left_margin = 40,
114 .right_margin = 24,
115 .upper_margin = 32,
116 .lower_margin = 11,
117 .hsync_len = 96,
118 .vsync_len = 2,
119 .vmode = FB_VMODE_NONINTERLACED
120};
121
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
123 u16 bg, u16 fg, u32 w, u32 h)
124{
James Simmonsf1ab5da2005-06-21 17:17:07 -0700125 u32 *data = (u32 *) data8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 int i, j, k = 0;
127 u32 b, tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128
129 w = (w + 1) & ~1;
130
131 for (i = 0; i < h; i++) {
132 b = *data++;
133 reverse_order(&b);
134
135 for (j = 0; j < w / 2; j++) {
136 tmp = 0;
137#if defined (__BIG_ENDIAN)
138 tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
139 b <<= 1;
140 tmp |= (b & (1 << 31)) ? fg : bg;
141 b <<= 1;
142#else
143 tmp = (b & 1) ? fg : bg;
144 b >>= 1;
145 tmp |= (b & 1) ? fg << 16 : bg << 16;
146 b >>= 1;
147#endif
148 NV_WR32(&par->CURSOR[k++], 0, tmp);
149 }
150 k += (MAX_CURS - w) / 2;
151 }
152}
153
154static void nvidia_write_clut(struct nvidia_par *par,
155 u8 regnum, u8 red, u8 green, u8 blue)
156{
157 NVWriteDacMask(par, 0xff);
158 NVWriteDacWriteAddr(par, regnum);
159 NVWriteDacData(par, red);
160 NVWriteDacData(par, green);
161 NVWriteDacData(par, blue);
162}
163
164static void nvidia_read_clut(struct nvidia_par *par,
165 u8 regnum, u8 * red, u8 * green, u8 * blue)
166{
167 NVWriteDacMask(par, 0xff);
168 NVWriteDacReadAddr(par, regnum);
169 *red = NVReadDacData(par);
170 *green = NVReadDacData(par);
171 *blue = NVReadDacData(par);
172}
173
174static int nvidia_panel_tweak(struct nvidia_par *par,
175 struct _riva_hw_state *state)
176{
177 int tweak = 0;
178
179 if (par->paneltweak) {
180 tweak = par->paneltweak;
181 } else {
182 /* begin flat panel hacks */
183 /* This is unfortunate, but some chips need this register
184 tweaked or else you get artifacts where adjacent pixels are
185 swapped. There are no hard rules for what to set here so all
186 we can do is experiment and apply hacks. */
187
188 if(((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) {
189 /* At least one NV34 laptop needs this workaround. */
190 tweak = -1;
191 }
192
193 if((par->Chipset & 0xfff0) == 0x0310) {
194 tweak = 1;
195 }
196 /* end flat panel hacks */
197 }
198
199 return tweak;
200}
201
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700202static void nvidia_screen_off(struct nvidia_par *par, int on)
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800203{
204 unsigned char tmp;
205
206 if (on) {
207 /*
208 * Turn off screen and disable sequencer.
209 */
210 tmp = NVReadSeq(par, 0x01);
211
212 NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */
213 NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */
214 } else {
215 /*
216 * Reenable sequencer, then turn on screen.
217 */
218
219 tmp = NVReadSeq(par, 0x01);
220
221 NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */
222 NVWriteSeq(par, 0x00, 0x03); /* End Reset */
223 }
224}
225
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226static void nvidia_save_vga(struct nvidia_par *par,
227 struct _riva_hw_state *state)
228{
229 int i;
230
231 NVTRACE_ENTER();
232 NVLockUnlock(par, 0);
233
234 NVUnloadStateExt(par, state);
235
236 state->misc_output = NVReadMiscOut(par);
237
238 for (i = 0; i < NUM_CRT_REGS; i++)
239 state->crtc[i] = NVReadCrtc(par, i);
240
241 for (i = 0; i < NUM_ATC_REGS; i++)
242 state->attr[i] = NVReadAttr(par, i);
243
244 for (i = 0; i < NUM_GRC_REGS; i++)
245 state->gra[i] = NVReadGr(par, i);
246
247 for (i = 0; i < NUM_SEQ_REGS; i++)
248 state->seq[i] = NVReadSeq(par, i);
249 NVTRACE_LEAVE();
250}
251
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800252#undef DUMP_REG
253
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800254static void nvidia_write_regs(struct nvidia_par *par,
255 struct _riva_hw_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 int i;
258
259 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
261 NVLoadStateExt(par, state);
262
263 NVWriteMiscOut(par, state->misc_output);
264
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800265 for (i = 1; i < NUM_SEQ_REGS; i++) {
266#ifdef DUMP_REG
267 printk(" SEQ[%02x] = %08x\n", i, state->seq[i]);
268#endif
269 NVWriteSeq(par, i, state->seq[i]);
270 }
271
272 /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 of CRTC[17] */
273 NVWriteCrtc(par, 0x11, state->crtc[0x11] & ~0x80);
274
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 for (i = 0; i < NUM_CRT_REGS; i++) {
276 switch (i) {
277 case 0x19:
278 case 0x20 ... 0x40:
279 break;
280 default:
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800281#ifdef DUMP_REG
282 printk("CRTC[%02x] = %08x\n", i, state->crtc[i]);
283#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 NVWriteCrtc(par, i, state->crtc[i]);
285 }
286 }
287
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800288 for (i = 0; i < NUM_GRC_REGS; i++) {
289#ifdef DUMP_REG
290 printk(" GRA[%02x] = %08x\n", i, state->gra[i]);
291#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 NVWriteGr(par, i, state->gra[i]);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800293 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800295 for (i = 0; i < NUM_ATC_REGS; i++) {
296#ifdef DUMP_REG
297 printk("ATTR[%02x] = %08x\n", i, state->attr[i]);
298#endif
299 NVWriteAttr(par, i, state->attr[i]);
300 }
301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 NVTRACE_LEAVE();
303}
304
305static int nvidia_calc_regs(struct fb_info *info)
306{
307 struct nvidia_par *par = info->par;
308 struct _riva_hw_state *state = &par->ModeReg;
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700309 int i, depth = fb_get_color_depth(&info->var, &info->fix);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 int h_display = info->var.xres / 8 - 1;
311 int h_start = (info->var.xres + info->var.right_margin) / 8 - 1;
312 int h_end = (info->var.xres + info->var.right_margin +
313 info->var.hsync_len) / 8 - 1;
314 int h_total = (info->var.xres + info->var.right_margin +
315 info->var.hsync_len + info->var.left_margin) / 8 - 5;
316 int h_blank_s = h_display;
317 int h_blank_e = h_total + 4;
318 int v_display = info->var.yres - 1;
319 int v_start = info->var.yres + info->var.lower_margin - 1;
320 int v_end = (info->var.yres + info->var.lower_margin +
321 info->var.vsync_len) - 1;
322 int v_total = (info->var.yres + info->var.lower_margin +
323 info->var.vsync_len + info->var.upper_margin) - 2;
324 int v_blank_s = v_display;
325 int v_blank_e = v_total + 1;
326
327 /*
328 * Set all CRTC values.
329 */
330
331 if (info->var.vmode & FB_VMODE_INTERLACED)
332 v_total |= 1;
333
334 if (par->FlatPanel == 1) {
335 v_start = v_total - 3;
336 v_end = v_total - 2;
337 v_blank_s = v_start;
338 h_start = h_total - 5;
339 h_end = h_total - 2;
340 h_blank_e = h_total + 4;
341 }
342
343 state->crtc[0x0] = Set8Bits(h_total);
344 state->crtc[0x1] = Set8Bits(h_display);
345 state->crtc[0x2] = Set8Bits(h_blank_s);
346 state->crtc[0x3] = SetBitField(h_blank_e, 4: 0, 4:0)
347 | SetBit(7);
348 state->crtc[0x4] = Set8Bits(h_start);
349 state->crtc[0x5] = SetBitField(h_blank_e, 5: 5, 7:7)
350 | SetBitField(h_end, 4: 0, 4:0);
351 state->crtc[0x6] = SetBitField(v_total, 7: 0, 7:0);
352 state->crtc[0x7] = SetBitField(v_total, 8: 8, 0:0)
353 | SetBitField(v_display, 8: 8, 1:1)
354 | SetBitField(v_start, 8: 8, 2:2)
355 | SetBitField(v_blank_s, 8: 8, 3:3)
356 | SetBit(4)
357 | SetBitField(v_total, 9: 9, 5:5)
358 | SetBitField(v_display, 9: 9, 6:6)
359 | SetBitField(v_start, 9: 9, 7:7);
360 state->crtc[0x9] = SetBitField(v_blank_s, 9: 9, 5:5)
361 | SetBit(6)
362 | ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0x00);
363 state->crtc[0x10] = Set8Bits(v_start);
364 state->crtc[0x11] = SetBitField(v_end, 3: 0, 3:0) | SetBit(5);
365 state->crtc[0x12] = Set8Bits(v_display);
366 state->crtc[0x13] = ((info->var.xres_virtual / 8) *
367 (info->var.bits_per_pixel / 8));
368 state->crtc[0x15] = Set8Bits(v_blank_s);
369 state->crtc[0x16] = Set8Bits(v_blank_e);
370
371 state->attr[0x10] = 0x01;
372
373 if (par->Television)
374 state->attr[0x11] = 0x00;
375
376 state->screen = SetBitField(h_blank_e, 6: 6, 4:4)
377 | SetBitField(v_blank_s, 10: 10, 3:3)
378 | SetBitField(v_start, 10: 10, 2:2)
379 | SetBitField(v_display, 10: 10, 1:1)
380 | SetBitField(v_total, 10: 10, 0:0);
381
382 state->horiz = SetBitField(h_total, 8: 8, 0:0)
383 | SetBitField(h_display, 8: 8, 1:1)
384 | SetBitField(h_blank_s, 8: 8, 2:2)
385 | SetBitField(h_start, 8: 8, 3:3);
386
387 state->extra = SetBitField(v_total, 11: 11, 0:0)
388 | SetBitField(v_display, 11: 11, 2:2)
389 | SetBitField(v_start, 11: 11, 4:4)
390 | SetBitField(v_blank_s, 11: 11, 6:6);
391
392 if (info->var.vmode & FB_VMODE_INTERLACED) {
393 h_total = (h_total >> 1) & ~1;
394 state->interlace = Set8Bits(h_total);
395 state->horiz |= SetBitField(h_total, 8: 8, 4:4);
396 } else {
397 state->interlace = 0xff; /* interlace off */
398 }
399
400 /*
401 * Calculate the extended registers.
402 */
403
404 if (depth < 24)
405 i = depth;
406 else
407 i = 32;
408
409 if (par->Architecture >= NV_ARCH_10)
410 par->CURSOR = (volatile u32 __iomem *)(info->screen_base +
411 par->CursorStart);
412
413 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
414 state->misc_output &= ~0x40;
415 else
416 state->misc_output |= 0x40;
417 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
418 state->misc_output &= ~0x80;
419 else
420 state->misc_output |= 0x80;
421
422 NVCalcStateExt(par, state, i, info->var.xres_virtual,
423 info->var.xres, info->var.yres_virtual,
424 1000000000 / info->var.pixclock, info->var.vmode);
425
426 state->scale = NV_RD32(par->PRAMDAC, 0x00000848) & 0xfff000ff;
427 if (par->FlatPanel == 1) {
428 state->pixel |= (1 << 7);
429
430 if (!par->fpScaler || (par->fpWidth <= info->var.xres)
431 || (par->fpHeight <= info->var.yres)) {
432 state->scale |= (1 << 8);
433 }
434
435 if (!par->crtcSync_read) {
436 state->crtcSync = NV_RD32(par->PRAMDAC, 0x0828);
437 par->crtcSync_read = 1;
438 }
439
440 par->PanelTweak = nvidia_panel_tweak(par, state);
441 }
442
443 state->vpll = state->pll;
444 state->vpll2 = state->pll;
445 state->vpllB = state->pllB;
446 state->vpll2B = state->pllB;
447
448 VGA_WR08(par->PCIO, 0x03D4, 0x1C);
449 state->fifo = VGA_RD08(par->PCIO, 0x03D5) & ~(1<<5);
450
451 if (par->CRTCnumber) {
452 state->head = NV_RD32(par->PCRTC0, 0x00000860) & ~0x00001000;
453 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) | 0x00001000;
454 state->crtcOwner = 3;
455 state->pllsel |= 0x20000800;
456 state->vpll = NV_RD32(par->PRAMDAC0, 0x00000508);
457 if (par->twoStagePLL)
458 state->vpllB = NV_RD32(par->PRAMDAC0, 0x00000578);
459 } else if (par->twoHeads) {
460 state->head = NV_RD32(par->PCRTC0, 0x00000860) | 0x00001000;
461 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) & ~0x00001000;
462 state->crtcOwner = 0;
463 state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
464 if (par->twoStagePLL)
465 state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
466 }
467
468 state->cursorConfig = 0x00000100;
469
470 if (info->var.vmode & FB_VMODE_DOUBLE)
471 state->cursorConfig |= (1 << 4);
472
473 if (par->alphaCursor) {
474 if ((par->Chipset & 0x0ff0) != 0x0110)
475 state->cursorConfig |= 0x04011000;
476 else
477 state->cursorConfig |= 0x14011000;
478 state->general |= (1 << 29);
479 } else
480 state->cursorConfig |= 0x02000000;
481
482 if (par->twoHeads) {
483 if ((par->Chipset & 0x0ff0) == 0x0110) {
484 state->dither = NV_RD32(par->PRAMDAC, 0x0528) &
485 ~0x00010000;
486 if (par->FPDither)
487 state->dither |= 0x00010000;
488 } else {
489 state->dither = NV_RD32(par->PRAMDAC, 0x083C) & ~1;
490 if (par->FPDither)
491 state->dither |= 1;
492 }
493 }
494
495 state->timingH = 0;
496 state->timingV = 0;
497 state->displayV = info->var.xres;
498
499 return 0;
500}
501
502static void nvidia_init_vga(struct fb_info *info)
503{
504 struct nvidia_par *par = info->par;
505 struct _riva_hw_state *state = &par->ModeReg;
506 int i;
507
508 for (i = 0; i < 0x10; i++)
509 state->attr[i] = i;
510 state->attr[0x10] = 0x41;
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800511 state->attr[0x11] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 state->attr[0x12] = 0x0f;
513 state->attr[0x13] = 0x00;
514 state->attr[0x14] = 0x00;
515
516 memset(state->crtc, 0x00, NUM_CRT_REGS);
517 state->crtc[0x0a] = 0x20;
518 state->crtc[0x17] = 0xe3;
519 state->crtc[0x18] = 0xff;
520 state->crtc[0x28] = 0x40;
521
522 memset(state->gra, 0x00, NUM_GRC_REGS);
523 state->gra[0x05] = 0x40;
524 state->gra[0x06] = 0x05;
525 state->gra[0x07] = 0x0f;
526 state->gra[0x08] = 0xff;
527
528 state->seq[0x00] = 0x03;
529 state->seq[0x01] = 0x01;
530 state->seq[0x02] = 0x0f;
531 state->seq[0x03] = 0x00;
532 state->seq[0x04] = 0x0e;
533
534 state->misc_output = 0xeb;
535}
536
537static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
538{
539 struct nvidia_par *par = info->par;
540 u8 data[MAX_CURS * MAX_CURS / 8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 int i, set = cursor->set;
James Simmonsf1ab5da2005-06-21 17:17:07 -0700542 u16 fg, bg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
Antonino A. Daplas7a482422005-09-21 07:30:21 +0800544 if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
James Simmonsf1ab5da2005-06-21 17:17:07 -0700545 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
547 NVShowHideCursor(par, 0);
548
549 if (par->cursor_reset) {
550 set = FB_CUR_SETALL;
551 par->cursor_reset = 0;
552 }
553
554 if (set & FB_CUR_SETSIZE)
555 memset_io(par->CURSOR, 0, MAX_CURS * MAX_CURS * 2);
556
557 if (set & FB_CUR_SETPOS) {
558 u32 xx, yy, temp;
559
560 yy = cursor->image.dy - info->var.yoffset;
561 xx = cursor->image.dx - info->var.xoffset;
562 temp = xx & 0xFFFF;
563 temp |= yy << 16;
564
565 NV_WR32(par->PRAMDAC, 0x0000300, temp);
566 }
567
568 if (set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
569 u32 bg_idx = cursor->image.bg_color;
570 u32 fg_idx = cursor->image.fg_color;
571 u32 s_pitch = (cursor->image.width + 7) >> 3;
572 u32 d_pitch = MAX_CURS / 8;
573 u8 *dat = (u8 *) cursor->image.data;
574 u8 *msk = (u8 *) cursor->mask;
575 u8 *src;
576
577 src = kmalloc(s_pitch * cursor->image.height, GFP_ATOMIC);
578
579 if (src) {
580 switch (cursor->rop) {
581 case ROP_XOR:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700582 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 src[i] = dat[i] ^ msk[i];
584 break;
585 case ROP_COPY:
586 default:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700587 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 src[i] = dat[i] & msk[i];
589 break;
590 }
591
James Simmonsf1ab5da2005-06-21 17:17:07 -0700592 fb_pad_aligned_buffer(data, d_pitch, src, s_pitch,
593 cursor->image.height);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
595 bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
596 ((info->cmap.green[bg_idx] & 0xf8) << 2) |
597 ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | 1 << 15;
598
599 fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
600 ((info->cmap.green[fg_idx] & 0xf8) << 2) |
601 ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
602
603 NVLockUnlock(par, 0);
604
605 nvidiafb_load_cursor_image(par, data, bg, fg,
606 cursor->image.width,
607 cursor->image.height);
608 kfree(src);
609 }
610 }
611
612 if (cursor->enable)
613 NVShowHideCursor(par, 1);
614
615 return 0;
616}
617
618static int nvidiafb_set_par(struct fb_info *info)
619{
620 struct nvidia_par *par = info->par;
621
622 NVTRACE_ENTER();
623
624 NVLockUnlock(par, 1);
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800625 if (!par->FlatPanel || !par->twoHeads)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 par->FPDither = 0;
627
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800628 if (par->FPDither < 0) {
629 if ((par->Chipset & 0x0ff0) == 0x0110)
630 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x0528)
631 & 0x00010000);
632 else
633 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1);
634 printk(KERN_INFO PFX "Flat panel dithering %s\n",
635 par->FPDither ? "enabled" : "disabled");
636 }
637
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700638 info->fix.visual = (info->var.bits_per_pixel == 8) ?
639 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
640
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 nvidia_init_vga(info);
642 nvidia_calc_regs(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643
644 NVLockUnlock(par, 0);
645 if (par->twoHeads) {
646 VGA_WR08(par->PCIO, 0x03D4, 0x44);
647 VGA_WR08(par->PCIO, 0x03D5, par->ModeReg.crtcOwner);
648 NVLockUnlock(par, 0);
649 }
650
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700651 nvidia_screen_off(par, 1);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800652
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800653 nvidia_write_regs(par, &par->ModeReg);
654 NVSetStartAddress(par, 0);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800655
656#if defined (__BIG_ENDIAN)
657 /* turn on LFB swapping */
658 {
659 unsigned char tmp;
660
661 VGA_WR08(par->PCIO, 0x3d4, 0x46);
662 tmp = VGA_RD08(par->PCIO, 0x3d5);
663 tmp |= (1 << 7);
664 VGA_WR08(par->PCIO, 0x3d5, tmp);
665 }
666#endif
667
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 info->fix.line_length = (info->var.xres_virtual *
669 info->var.bits_per_pixel) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 if (info->var.accel_flags) {
671 info->fbops->fb_imageblit = nvidiafb_imageblit;
672 info->fbops->fb_fillrect = nvidiafb_fillrect;
673 info->fbops->fb_copyarea = nvidiafb_copyarea;
674 info->fbops->fb_sync = nvidiafb_sync;
675 info->pixmap.scan_align = 4;
676 info->flags &= ~FBINFO_HWACCEL_DISABLED;
Antonino A. Daplas01b15bd2007-07-17 04:05:25 -0700677 info->flags |= FBINFO_READS_FAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 NVResetGraphics(info);
679 } else {
680 info->fbops->fb_imageblit = cfb_imageblit;
681 info->fbops->fb_fillrect = cfb_fillrect;
682 info->fbops->fb_copyarea = cfb_copyarea;
683 info->fbops->fb_sync = NULL;
684 info->pixmap.scan_align = 1;
685 info->flags |= FBINFO_HWACCEL_DISABLED;
Antonino A. Daplas01b15bd2007-07-17 04:05:25 -0700686 info->flags &= ~FBINFO_READS_FAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 }
688
689 par->cursor_reset = 1;
690
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700691 nvidia_screen_off(par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
Paul Mackerras70abac62006-10-03 01:15:14 -0700693#ifdef CONFIG_BOOTX_TEXT
694 /* Update debug text engine */
695 btext_update_display(info->fix.smem_start,
696 info->var.xres, info->var.yres,
697 info->var.bits_per_pixel, info->fix.line_length);
698#endif
699
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700700 NVLockUnlock(par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 NVTRACE_LEAVE();
702 return 0;
703}
704
705static int nvidiafb_setcolreg(unsigned regno, unsigned red, unsigned green,
706 unsigned blue, unsigned transp,
707 struct fb_info *info)
708{
709 struct nvidia_par *par = info->par;
710 int i;
711
712 NVTRACE_ENTER();
713 if (regno >= (1 << info->var.green.length))
714 return -EINVAL;
715
716 if (info->var.grayscale) {
717 /* gray = 0.30*R + 0.59*G + 0.11*B */
718 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
719 }
720
721 if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
722 ((u32 *) info->pseudo_palette)[regno] =
723 (regno << info->var.red.offset) |
724 (regno << info->var.green.offset) |
725 (regno << info->var.blue.offset);
726 }
727
728 switch (info->var.bits_per_pixel) {
729 case 8:
730 /* "transparent" stuff is completely ignored. */
731 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
732 break;
733 case 16:
734 if (info->var.green.length == 5) {
735 for (i = 0; i < 8; i++) {
736 nvidia_write_clut(par, regno * 8 + i, red >> 8,
737 green >> 8, blue >> 8);
738 }
739 } else {
740 u8 r, g, b;
741
742 if (regno < 32) {
743 for (i = 0; i < 8; i++) {
744 nvidia_write_clut(par, regno * 8 + i,
745 red >> 8, green >> 8,
746 blue >> 8);
747 }
748 }
749
750 nvidia_read_clut(par, regno * 4, &r, &g, &b);
751
752 for (i = 0; i < 4; i++)
753 nvidia_write_clut(par, regno * 4 + i, r,
754 green >> 8, b);
755 }
756 break;
757 case 32:
758 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
759 break;
760 default:
761 /* do nothing */
762 break;
763 }
764
765 NVTRACE_LEAVE();
766 return 0;
767}
768
769static int nvidiafb_check_var(struct fb_var_screeninfo *var,
770 struct fb_info *info)
771{
772 struct nvidia_par *par = info->par;
773 int memlen, vramlen, mode_valid = 0;
774 int pitch, err = 0;
775
776 NVTRACE_ENTER();
777
778 var->transp.offset = 0;
779 var->transp.length = 0;
780
781 var->xres &= ~7;
782
783 if (var->bits_per_pixel <= 8)
784 var->bits_per_pixel = 8;
785 else if (var->bits_per_pixel <= 16)
786 var->bits_per_pixel = 16;
787 else
788 var->bits_per_pixel = 32;
789
790 switch (var->bits_per_pixel) {
791 case 8:
792 var->red.offset = 0;
793 var->red.length = 8;
794 var->green.offset = 0;
795 var->green.length = 8;
796 var->blue.offset = 0;
797 var->blue.length = 8;
798 var->transp.offset = 0;
799 var->transp.length = 0;
800 break;
801 case 16:
802 var->green.length = (var->green.length < 6) ? 5 : 6;
803 var->red.length = 5;
804 var->blue.length = 5;
805 var->transp.length = 6 - var->green.length;
806 var->blue.offset = 0;
807 var->green.offset = 5;
808 var->red.offset = 5 + var->green.length;
809 var->transp.offset = (5 + var->red.offset) & 15;
810 break;
811 case 32: /* RGBA 8888 */
812 var->red.offset = 16;
813 var->red.length = 8;
814 var->green.offset = 8;
815 var->green.length = 8;
816 var->blue.offset = 0;
817 var->blue.length = 8;
818 var->transp.length = 8;
819 var->transp.offset = 24;
820 break;
821 }
822
823 var->red.msb_right = 0;
824 var->green.msb_right = 0;
825 var->blue.msb_right = 0;
826 var->transp.msb_right = 0;
827
828 if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
829 !info->monspecs.dclkmax || !fb_validate_mode(var, info))
830 mode_valid = 1;
831
832 /* calculate modeline if supported by monitor */
833 if (!mode_valid && info->monspecs.gtf) {
834 if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
835 mode_valid = 1;
836 }
837
838 if (!mode_valid) {
Geert Uytterhoeven9791d762007-02-12 00:55:19 -0800839 const struct fb_videomode *mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
841 mode = fb_find_best_mode(var, &info->modelist);
842 if (mode) {
843 fb_videomode_to_var(var, mode);
844 mode_valid = 1;
845 }
846 }
847
848 if (!mode_valid && info->monspecs.modedb_len)
849 return -EINVAL;
850
851 if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres ||
852 par->fpHeight < var->yres))
853 return -EINVAL;
854
855 if (var->yres_virtual < var->yres)
856 var->yres_virtual = var->yres;
857
858 if (var->xres_virtual < var->xres)
859 var->xres_virtual = var->xres;
860
861 var->xres_virtual = (var->xres_virtual + 63) & ~63;
862
Antonino A. Daplas917bb072005-05-01 08:59:22 -0700863 vramlen = info->screen_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 pitch = ((var->xres_virtual * var->bits_per_pixel) + 7) / 8;
865 memlen = pitch * var->yres_virtual;
866
867 if (memlen > vramlen) {
868 var->yres_virtual = vramlen / pitch;
869
870 if (var->yres_virtual < var->yres) {
871 var->yres_virtual = var->yres;
872 var->xres_virtual = vramlen / var->yres_virtual;
873 var->xres_virtual /= var->bits_per_pixel / 8;
874 var->xres_virtual &= ~63;
875 pitch = (var->xres_virtual *
876 var->bits_per_pixel + 7) / 8;
877 memlen = pitch * var->yres;
878
879 if (var->xres_virtual < var->xres) {
880 printk("nvidiafb: required video memory, "
881 "%d bytes, for %dx%d-%d (virtual) "
882 "is out of range\n",
883 memlen, var->xres_virtual,
884 var->yres_virtual, var->bits_per_pixel);
885 err = -ENOMEM;
886 }
887 }
888 }
889
890 if (var->accel_flags) {
891 if (var->yres_virtual > 0x7fff)
892 var->yres_virtual = 0x7fff;
893 if (var->xres_virtual > 0x7fff)
894 var->xres_virtual = 0x7fff;
895 }
896
897 var->xres_virtual &= ~63;
898
899 NVTRACE_LEAVE();
900
901 return err;
902}
903
904static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
905 struct fb_info *info)
906{
907 struct nvidia_par *par = info->par;
908 u32 total;
909
Antonino A. Daplas3c8d61b2005-11-13 16:06:34 -0800910 total = var->yoffset * info->fix.line_length + var->xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
912 NVSetStartAddress(par, total);
913
914 return 0;
915}
916
917static int nvidiafb_blank(int blank, struct fb_info *info)
918{
919 struct nvidia_par *par = info->par;
920 unsigned char tmp, vesa;
921
922 tmp = NVReadSeq(par, 0x01) & ~0x20; /* screen on/off */
923 vesa = NVReadCrtc(par, 0x1a) & ~0xc0; /* sync on/off */
924
925 NVTRACE_ENTER();
926
927 if (blank)
928 tmp |= 0x20;
929
930 switch (blank) {
931 case FB_BLANK_UNBLANK:
932 case FB_BLANK_NORMAL:
933 break;
934 case FB_BLANK_VSYNC_SUSPEND:
935 vesa |= 0x80;
936 break;
937 case FB_BLANK_HSYNC_SUSPEND:
938 vesa |= 0x40;
939 break;
940 case FB_BLANK_POWERDOWN:
941 vesa |= 0xc0;
942 break;
943 }
944
945 NVWriteSeq(par, 0x01, tmp);
946 NVWriteCrtc(par, 0x1a, vesa);
947
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 NVTRACE_LEAVE();
949
950 return 0;
951}
952
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -0700953/*
954 * Because the VGA registers are not mapped linearly in its MMIO space,
955 * restrict VGA register saving and restore to x86 only, where legacy VGA IO
956 * access is legal. Consequently, we must also check if the device is the
957 * primary display.
958 */
959#ifdef CONFIG_X86
960static void save_vga_x86(struct nvidia_par *par)
961{
962 struct resource *res= &par->pci_dev->resource[PCI_ROM_RESOURCE];
963
964 if (res && res->flags & IORESOURCE_ROM_SHADOW) {
965 memset(&par->vgastate, 0, sizeof(par->vgastate));
966 par->vgastate.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS |
967 VGA_SAVE_CMAP;
968 save_vga(&par->vgastate);
969 }
970}
971
972static void restore_vga_x86(struct nvidia_par *par)
973{
974 struct resource *res= &par->pci_dev->resource[PCI_ROM_RESOURCE];
975
976 if (res && res->flags & IORESOURCE_ROM_SHADOW)
977 restore_vga(&par->vgastate);
978}
979#else
980#define save_vga_x86(x) do {} while (0)
981#define restore_vga_x86(x) do {} while (0)
982#endif /* X86 */
983
984static int nvidiafb_open(struct fb_info *info, int user)
985{
986 struct nvidia_par *par = info->par;
987
988 mutex_lock(&par->open_lock);
989
990 if (!par->open_count) {
991 save_vga_x86(par);
992 nvidia_save_vga(par, &par->initial_state);
993 }
994
995 par->open_count++;
996 mutex_unlock(&par->open_lock);
997 return 0;
998}
999
1000static int nvidiafb_release(struct fb_info *info, int user)
1001{
1002 struct nvidia_par *par = info->par;
1003 int err = 0;
1004
1005 mutex_lock(&par->open_lock);
1006
1007 if (!par->open_count) {
1008 err = -EINVAL;
1009 goto done;
1010 }
1011
1012 if (par->open_count == 1) {
1013 nvidia_write_regs(par, &par->initial_state);
1014 restore_vga_x86(par);
1015 }
1016
1017 par->open_count--;
1018done:
1019 mutex_unlock(&par->open_lock);
Antonino A. Daplas2620c6e2007-05-08 00:39:42 -07001020 return err;
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -07001021}
1022
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023static struct fb_ops nvidia_fb_ops = {
1024 .owner = THIS_MODULE,
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -07001025 .fb_open = nvidiafb_open,
1026 .fb_release = nvidiafb_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 .fb_check_var = nvidiafb_check_var,
1028 .fb_set_par = nvidiafb_set_par,
1029 .fb_setcolreg = nvidiafb_setcolreg,
1030 .fb_pan_display = nvidiafb_pan_display,
1031 .fb_blank = nvidiafb_blank,
1032 .fb_fillrect = nvidiafb_fillrect,
1033 .fb_copyarea = nvidiafb_copyarea,
1034 .fb_imageblit = nvidiafb_imageblit,
1035 .fb_cursor = nvidiafb_cursor,
1036 .fb_sync = nvidiafb_sync,
1037};
1038
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001039#ifdef CONFIG_PM
David Brownellc78a7c22006-08-14 23:11:06 -07001040static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001041{
1042 struct fb_info *info = pci_get_drvdata(dev);
1043 struct nvidia_par *par = info->par;
1044
David Brownellc78a7c22006-08-14 23:11:06 -07001045 if (mesg.event == PM_EVENT_PRETHAW)
1046 mesg.event = PM_EVENT_FREEZE;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001047 acquire_console_sem();
David Brownellc78a7c22006-08-14 23:11:06 -07001048 par->pm_state = mesg.event;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001049
David Brownellc78a7c22006-08-14 23:11:06 -07001050 if (mesg.event == PM_EVENT_SUSPEND) {
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001051 fb_set_suspend(info, 1);
1052 nvidiafb_blank(FB_BLANK_POWERDOWN, info);
1053 nvidia_write_regs(par, &par->SavedReg);
1054 pci_save_state(dev);
1055 pci_disable_device(dev);
David Brownellc78a7c22006-08-14 23:11:06 -07001056 pci_set_power_state(dev, pci_choose_state(dev, mesg));
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001057 }
David Brownellc78a7c22006-08-14 23:11:06 -07001058 dev->dev.power.power_state = mesg;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001059
1060 release_console_sem();
1061 return 0;
1062}
1063
1064static int nvidiafb_resume(struct pci_dev *dev)
1065{
1066 struct fb_info *info = pci_get_drvdata(dev);
1067 struct nvidia_par *par = info->par;
1068
1069 acquire_console_sem();
1070 pci_set_power_state(dev, PCI_D0);
1071
1072 if (par->pm_state != PM_EVENT_FREEZE) {
1073 pci_restore_state(dev);
Antonino A. Daplas7b566b12006-10-03 01:14:53 -07001074
1075 if (pci_enable_device(dev))
1076 goto fail;
1077
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001078 pci_set_master(dev);
1079 }
1080
1081 par->pm_state = PM_EVENT_ON;
1082 nvidiafb_set_par(info);
1083 fb_set_suspend (info, 0);
1084 nvidiafb_blank(FB_BLANK_UNBLANK, info);
1085
Antonino A. Daplas7b566b12006-10-03 01:14:53 -07001086fail:
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001087 release_console_sem();
1088 return 0;
1089}
1090#else
1091#define nvidiafb_suspend NULL
1092#define nvidiafb_resume NULL
1093#endif
1094
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095static int __devinit nvidia_set_fbinfo(struct fb_info *info)
1096{
1097 struct fb_monspecs *specs = &info->monspecs;
1098 struct fb_videomode modedb;
1099 struct nvidia_par *par = info->par;
1100 int lpitch;
1101
1102 NVTRACE_ENTER();
1103 info->flags = FBINFO_DEFAULT
1104 | FBINFO_HWACCEL_IMAGEBLIT
1105 | FBINFO_HWACCEL_FILLRECT
1106 | FBINFO_HWACCEL_COPYAREA
1107 | FBINFO_HWACCEL_YPAN;
1108
1109 fb_videomode_to_modelist(info->monspecs.modedb,
1110 info->monspecs.modedb_len, &info->modelist);
1111 fb_var_to_videomode(&modedb, &nvidiafb_default_var);
1112
Antonino A. Daplasade91852006-01-09 20:53:39 -08001113 switch (bpp) {
1114 case 0 ... 8:
1115 bpp = 8;
1116 break;
1117 case 9 ... 16:
1118 bpp = 16;
1119 break;
1120 default:
1121 bpp = 32;
1122 break;
1123 }
1124
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 if (specs->modedb != NULL) {
Geert Uytterhoeven9791d762007-02-12 00:55:19 -08001126 const struct fb_videomode *mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127
Geert Uytterhoeven9791d762007-02-12 00:55:19 -08001128 mode = fb_find_best_display(specs, &info->modelist);
1129 fb_videomode_to_var(&nvidiafb_default_var, mode);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001130 nvidiafb_default_var.bits_per_pixel = bpp;
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001131 } else if (par->fpWidth && par->fpHeight) {
1132 char buf[16];
1133
1134 memset(buf, 0, 16);
Antonino A. Daplas948a95f2005-09-09 13:09:59 -07001135 snprintf(buf, 15, "%dx%dMR", par->fpWidth, par->fpHeight);
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001136 fb_find_mode(&nvidiafb_default_var, info, buf, specs->modedb,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001137 specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 }
1139
1140 if (mode_option)
1141 fb_find_mode(&nvidiafb_default_var, info, mode_option,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001142 specs->modedb, specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143
1144 info->var = nvidiafb_default_var;
1145 info->fix.visual = (info->var.bits_per_pixel == 8) ?
1146 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
1147 info->pseudo_palette = par->pseudo_palette;
1148 fb_alloc_cmap(&info->cmap, 256, 0);
1149 fb_destroy_modedb(info->monspecs.modedb);
1150 info->monspecs.modedb = NULL;
1151
1152 /* maximize virtual vertical length */
1153 lpitch = info->var.xres_virtual *
1154 ((info->var.bits_per_pixel + 7) >> 3);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001155 info->var.yres_virtual = info->screen_size / lpitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156
1157 info->pixmap.scan_align = 4;
1158 info->pixmap.buf_align = 4;
James Simmons58a60642005-06-21 17:17:08 -07001159 info->pixmap.access_align = 32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 info->pixmap.size = 8 * 1024;
1161 info->pixmap.flags = FB_PIXMAP_SYSTEM;
1162
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001163 if (!hwcur)
Antonino A. Daplasc465e052005-11-07 01:00:35 -08001164 info->fbops->fb_cursor = NULL;
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001165
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 info->var.accel_flags = (!noaccel);
1167
1168 switch (par->Architecture) {
1169 case NV_ARCH_04:
1170 info->fix.accel = FB_ACCEL_NV4;
1171 break;
1172 case NV_ARCH_10:
1173 info->fix.accel = FB_ACCEL_NV_10;
1174 break;
1175 case NV_ARCH_20:
1176 info->fix.accel = FB_ACCEL_NV_20;
1177 break;
1178 case NV_ARCH_30:
1179 info->fix.accel = FB_ACCEL_NV_30;
1180 break;
1181 case NV_ARCH_40:
1182 info->fix.accel = FB_ACCEL_NV_40;
1183 break;
1184 }
1185
1186 NVTRACE_LEAVE();
1187
1188 return nvidiafb_check_var(&info->var, info);
1189}
1190
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001191static u32 __devinit nvidia_get_chipset(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192{
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001193 struct nvidia_par *par = info->par;
1194 u32 id = (par->pci_dev->vendor << 16) | par->pci_dev->device;
1195
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001196 printk(KERN_INFO PFX "Device ID: %x \n", id);
1197
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001198 if ((id & 0xfff0) == 0x00f0) {
1199 /* pci-e */
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001200 id = NV_RD32(par->REGS, 0x1800);
1201
1202 if ((id & 0x0000ffff) == 0x000010DE)
1203 id = 0x10DE0000 | (id >> 16);
1204 else if ((id & 0xffff0000) == 0xDE100000) /* wrong endian */
1205 id = 0x10DE0000 | ((id << 8) & 0x0000ff00) |
1206 ((id >> 8) & 0x000000ff);
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001207 printk(KERN_INFO PFX "Subsystem ID: %x \n", id);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001208 }
1209
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001210 return id;
1211}
1212
1213static u32 __devinit nvidia_get_arch(struct fb_info *info)
1214{
1215 struct nvidia_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 u32 arch = 0;
1217
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001218 switch (par->Chipset & 0x0ff0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 case 0x0100: /* GeForce 256 */
1220 case 0x0110: /* GeForce2 MX */
1221 case 0x0150: /* GeForce2 */
1222 case 0x0170: /* GeForce4 MX */
1223 case 0x0180: /* GeForce4 MX (8x AGP) */
1224 case 0x01A0: /* nForce */
1225 case 0x01F0: /* nForce2 */
1226 arch = NV_ARCH_10;
1227 break;
1228 case 0x0200: /* GeForce3 */
1229 case 0x0250: /* GeForce4 Ti */
1230 case 0x0280: /* GeForce4 Ti (8x AGP) */
1231 arch = NV_ARCH_20;
1232 break;
1233 case 0x0300: /* GeForceFX 5800 */
1234 case 0x0310: /* GeForceFX 5600 */
1235 case 0x0320: /* GeForceFX 5200 */
1236 case 0x0330: /* GeForceFX 5900 */
1237 case 0x0340: /* GeForceFX 5700 */
1238 arch = NV_ARCH_30;
1239 break;
Wink Savillee40c6752006-11-10 12:27:52 -08001240 case 0x0040: /* GeForce 6800 */
1241 case 0x00C0: /* GeForce 6800 */
1242 case 0x0120: /* GeForce 6800 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 case 0x0130:
Wink Savillee40c6752006-11-10 12:27:52 -08001244 case 0x0140: /* GeForce 6600 */
1245 case 0x0160: /* GeForce 6200 */
1246 case 0x01D0: /* GeForce 7200, 7300, 7400 */
Michal Piotrowskibc0ca062007-05-09 02:35:34 -07001247 case 0x02E0: /* GeForce 7300 GT */
Wink Savillee40c6752006-11-10 12:27:52 -08001248 case 0x0090: /* GeForce 7800 */
1249 case 0x0210: /* GeForce 6800 */
1250 case 0x0220: /* GeForce 6200 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 case 0x0230:
Wink Savillee40c6752006-11-10 12:27:52 -08001252 case 0x0240: /* GeForce 6100 */
1253 case 0x0290: /* GeForce 7900 */
1254 case 0x0390: /* GeForce 7600 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 arch = NV_ARCH_40;
1256 break;
1257 case 0x0020: /* TNT, TNT2 */
1258 arch = NV_ARCH_04;
1259 break;
1260 default: /* unknown architecture */
1261 break;
1262 }
1263
1264 return arch;
1265}
1266
1267static int __devinit nvidiafb_probe(struct pci_dev *pd,
1268 const struct pci_device_id *ent)
1269{
1270 struct nvidia_par *par;
1271 struct fb_info *info;
1272 unsigned short cmd;
1273
1274
1275 NVTRACE_ENTER();
1276 assert(pd != NULL);
1277
1278 info = framebuffer_alloc(sizeof(struct nvidia_par), &pd->dev);
1279
1280 if (!info)
1281 goto err_out;
1282
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001283 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 par->pci_dev = pd;
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -07001285 mutex_init(&par->open_lock);
Jiri Slabyf5610b92007-02-12 00:55:12 -08001286 info->pixmap.addr = kzalloc(8 * 1024, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
1288 if (info->pixmap.addr == NULL)
1289 goto err_out_kfree;
1290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 if (pci_enable_device(pd)) {
1292 printk(KERN_ERR PFX "cannot enable PCI device\n");
1293 goto err_out_enable;
1294 }
1295
1296 if (pci_request_regions(pd, "nvidiafb")) {
1297 printk(KERN_ERR PFX "cannot request PCI regions\n");
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001298 goto err_out_enable;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 }
1300
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 par->FlatPanel = flatpanel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 if (flatpanel == 1)
1303 printk(KERN_INFO PFX "flatpanel support enabled\n");
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001304 par->FPDither = fpdither;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
1306 par->CRTCnumber = forceCRTC;
1307 par->FpScale = (!noscale);
1308 par->paneltweak = paneltweak;
1309
1310 /* enable IO and mem if not already done */
1311 pci_read_config_word(pd, PCI_COMMAND, &cmd);
1312 cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
1313 pci_write_config_word(pd, PCI_COMMAND, cmd);
1314
1315 nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
1316 nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
1317 nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
1318
1319 par->REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
1320
1321 if (!par->REGS) {
1322 printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
1323 goto err_out_free_base0;
1324 }
1325
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001326 par->Chipset = nvidia_get_chipset(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001327 par->Architecture = nvidia_get_arch(info);
1328
1329 if (par->Architecture == 0) {
1330 printk(KERN_ERR PFX "unknown NV_ARCH\n");
1331 goto err_out_arch;
1332 }
1333
1334 sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
1335
Antonino A. Daplas918799a2006-01-09 20:53:40 -08001336 if (NVCommonSetup(info))
1337 goto err_out_arch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338
1339 par->FbAddress = nvidiafb_fix.smem_start;
1340 par->FbMapSize = par->RamAmountKBytes * 1024;
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001341 if (vram && vram * 1024 * 1024 < par->FbMapSize)
1342 par->FbMapSize = vram * 1024 * 1024;
1343
1344 /* Limit amount of vram to 64 MB */
1345 if (par->FbMapSize > 64 * 1024 * 1024)
1346 par->FbMapSize = 64 * 1024 * 1024;
1347
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001348 if(par->Architecture >= NV_ARCH_40)
1349 par->FbUsableSize = par->FbMapSize - (560 * 1024);
1350 else
1351 par->FbUsableSize = par->FbMapSize - (128 * 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 par->ScratchBufferSize = (par->Architecture < NV_ARCH_10) ? 8 * 1024 :
1353 16 * 1024;
1354 par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001355 par->CursorStart = par->FbUsableSize + (32 * 1024);
1356
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001358 info->screen_size = par->FbUsableSize;
1359 nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
1361 if (!info->screen_base) {
1362 printk(KERN_ERR PFX "cannot ioremap FB base\n");
1363 goto err_out_free_base1;
1364 }
1365
1366 par->FbStart = info->screen_base;
1367
1368#ifdef CONFIG_MTRR
1369 if (!nomtrr) {
1370 par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001371 par->RamAmountKBytes * 1024,
1372 MTRR_TYPE_WRCOMB, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 if (par->mtrr.vram < 0) {
1374 printk(KERN_ERR PFX "unable to setup MTRR\n");
1375 } else {
1376 par->mtrr.vram_valid = 1;
1377 /* let there be speed */
1378 printk(KERN_INFO PFX "MTRR set to ON\n");
1379 }
1380 }
1381#endif /* CONFIG_MTRR */
1382
1383 info->fbops = &nvidia_fb_ops;
1384 info->fix = nvidiafb_fix;
1385
1386 if (nvidia_set_fbinfo(info) < 0) {
1387 printk(KERN_ERR PFX "error setting initial video mode\n");
1388 goto err_out_iounmap_fb;
1389 }
1390
1391 nvidia_save_vga(par, &par->SavedReg);
1392
Guido Guentherce38cac2006-07-30 03:04:21 -07001393 pci_set_drvdata(pd, info);
Richard Purdie202d4e62007-03-03 17:43:52 +00001394
1395 if (backlight)
1396 nvidia_bl_init(par);
1397
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 if (register_framebuffer(info) < 0) {
1399 printk(KERN_ERR PFX "error registering nVidia framebuffer\n");
1400 goto err_out_iounmap_fb;
1401 }
1402
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403
1404 printk(KERN_INFO PFX
1405 "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
1406 info->fix.id,
1407 par->FbMapSize / (1024 * 1024), info->fix.smem_start);
Michael Hanselmann5474c122006-06-25 05:47:08 -07001408
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 NVTRACE_LEAVE();
1410 return 0;
1411
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001412err_out_iounmap_fb:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 iounmap(info->screen_base);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001414err_out_free_base1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 fb_destroy_modedb(info->monspecs.modedb);
1416 nvidia_delete_i2c_busses(par);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001417err_out_arch:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 iounmap(par->REGS);
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001419 err_out_free_base0:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 pci_release_regions(pd);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001421err_out_enable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 kfree(info->pixmap.addr);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001423err_out_kfree:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 framebuffer_release(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001425err_out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 return -ENODEV;
1427}
1428
Prarit Bhargava5e14ab82007-02-12 00:55:14 -08001429static void __devexit nvidiafb_remove(struct pci_dev *pd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430{
1431 struct fb_info *info = pci_get_drvdata(pd);
1432 struct nvidia_par *par = info->par;
1433
1434 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435
Richard Purdie37ce69a2007-02-10 14:10:33 +00001436 unregister_framebuffer(info);
1437
Michael Hanselmann5474c122006-06-25 05:47:08 -07001438 nvidia_bl_exit(par);
1439
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440#ifdef CONFIG_MTRR
1441 if (par->mtrr.vram_valid)
1442 mtrr_del(par->mtrr.vram, info->fix.smem_start,
1443 info->fix.smem_len);
1444#endif /* CONFIG_MTRR */
1445
1446 iounmap(info->screen_base);
1447 fb_destroy_modedb(info->monspecs.modedb);
1448 nvidia_delete_i2c_busses(par);
1449 iounmap(par->REGS);
1450 pci_release_regions(pd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 kfree(info->pixmap.addr);
1452 framebuffer_release(info);
1453 pci_set_drvdata(pd, NULL);
1454 NVTRACE_LEAVE();
1455}
1456
1457/* ------------------------------------------------------------------------- *
1458 *
1459 * initialization
1460 *
1461 * ------------------------------------------------------------------------- */
1462
1463#ifndef MODULE
1464static int __devinit nvidiafb_setup(char *options)
1465{
1466 char *this_opt;
1467
1468 NVTRACE_ENTER();
1469 if (!options || !*options)
1470 return 0;
1471
1472 while ((this_opt = strsep(&options, ",")) != NULL) {
1473 if (!strncmp(this_opt, "forceCRTC", 9)) {
1474 char *p;
1475
1476 p = this_opt + 9;
1477 if (!*p || !*(++p))
1478 continue;
1479 forceCRTC = *p - '0';
1480 if (forceCRTC < 0 || forceCRTC > 1)
1481 forceCRTC = -1;
1482 } else if (!strncmp(this_opt, "flatpanel", 9)) {
1483 flatpanel = 1;
1484 } else if (!strncmp(this_opt, "hwcur", 5)) {
1485 hwcur = 1;
1486 } else if (!strncmp(this_opt, "noaccel", 6)) {
1487 noaccel = 1;
1488 } else if (!strncmp(this_opt, "noscale", 7)) {
1489 noscale = 1;
1490 } else if (!strncmp(this_opt, "paneltweak:", 11)) {
1491 paneltweak = simple_strtoul(this_opt+11, NULL, 0);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001492 } else if (!strncmp(this_opt, "vram:", 5)) {
1493 vram = simple_strtoul(this_opt+5, NULL, 0);
Richard Purdie202d4e62007-03-03 17:43:52 +00001494 } else if (!strncmp(this_opt, "backlight:", 10)) {
1495 backlight = simple_strtoul(this_opt+10, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496#ifdef CONFIG_MTRR
1497 } else if (!strncmp(this_opt, "nomtrr", 6)) {
1498 nomtrr = 1;
1499#endif
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001500 } else if (!strncmp(this_opt, "fpdither:", 9)) {
1501 fpdither = simple_strtol(this_opt+9, NULL, 0);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001502 } else if (!strncmp(this_opt, "bpp:", 4)) {
1503 bpp = simple_strtoul(this_opt+4, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 } else
1505 mode_option = this_opt;
1506 }
1507 NVTRACE_LEAVE();
1508 return 0;
1509}
1510#endif /* !MODULE */
1511
1512static struct pci_driver nvidiafb_driver = {
1513 .name = "nvidiafb",
1514 .id_table = nvidiafb_pci_tbl,
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001515 .probe = nvidiafb_probe,
1516 .suspend = nvidiafb_suspend,
1517 .resume = nvidiafb_resume,
Prarit Bhargava5e14ab82007-02-12 00:55:14 -08001518 .remove = __devexit_p(nvidiafb_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519};
1520
1521/* ------------------------------------------------------------------------- *
1522 *
1523 * modularization
1524 *
1525 * ------------------------------------------------------------------------- */
1526
1527static int __devinit nvidiafb_init(void)
1528{
1529#ifndef MODULE
1530 char *option = NULL;
1531
1532 if (fb_get_options("nvidiafb", &option))
1533 return -ENODEV;
1534 nvidiafb_setup(option);
1535#endif
1536 return pci_register_driver(&nvidiafb_driver);
1537}
1538
1539module_init(nvidiafb_init);
1540
1541#ifdef MODULE
1542static void __exit nvidiafb_exit(void)
1543{
1544 pci_unregister_driver(&nvidiafb_driver);
1545}
1546
1547module_exit(nvidiafb_exit);
1548
1549module_param(flatpanel, int, 0);
1550MODULE_PARM_DESC(flatpanel,
1551 "Enables experimental flat panel support for some chipsets. "
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001552 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
1553module_param(fpdither, int, 0);
1554MODULE_PARM_DESC(fpdither,
1555 "Enables dithering of flat panel for 6 bits panels. "
1556 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557module_param(hwcur, int, 0);
1558MODULE_PARM_DESC(hwcur,
1559 "Enables hardware cursor implementation. (0 or 1=enabled) "
1560 "(default=0)");
1561module_param(noaccel, int, 0);
1562MODULE_PARM_DESC(noaccel,
1563 "Disables hardware acceleration. (0 or 1=disable) "
1564 "(default=0)");
1565module_param(noscale, int, 0);
1566MODULE_PARM_DESC(noscale,
1567 "Disables screen scaleing. (0 or 1=disable) "
1568 "(default=0, do scaling)");
1569module_param(paneltweak, int, 0);
1570MODULE_PARM_DESC(paneltweak,
1571 "Tweak display settings for flatpanels. "
1572 "(default=0, no tweaks)");
1573module_param(forceCRTC, int, 0);
1574MODULE_PARM_DESC(forceCRTC,
1575 "Forces usage of a particular CRTC in case autodetection "
1576 "fails. (0 or 1) (default=autodetect)");
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001577module_param(vram, int, 0);
1578MODULE_PARM_DESC(vram,
1579 "amount of framebuffer memory to remap in MiB"
1580 "(default=0 - remap entire memory)");
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001581module_param(mode_option, charp, 0);
1582MODULE_PARM_DESC(mode_option, "Specify initial video mode");
Antonino A. Daplasade91852006-01-09 20:53:39 -08001583module_param(bpp, int, 0);
1584MODULE_PARM_DESC(bpp, "pixel width in bits"
1585 "(default=8)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586#ifdef CONFIG_MTRR
1587module_param(nomtrr, bool, 0);
1588MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
1589 "(default=0)");
1590#endif
1591
1592MODULE_AUTHOR("Antonino Daplas");
1593MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
1594MODULE_LICENSE("GPL");
1595#endif /* MODULE */
1596