blob: 30e14eb1f51e60e126166fa0c94d3a3cc08741f9 [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;
Antonino A. Daplas3c03ec22007-10-16 01:29:20 -070082static int reverse_i2c __devinitdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#ifdef CONFIG_MTRR
84static int nomtrr __devinitdata = 0;
85#endif
Richard Purdie202d4e62007-03-03 17:43:52 +000086#ifdef CONFIG_PMAC_BACKLIGHT
87static int backlight __devinitdata = 1;
88#else
89static int backlight __devinitdata = 0;
90#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
92static char *mode_option __devinitdata = NULL;
93
94static struct fb_fix_screeninfo __devinitdata nvidiafb_fix = {
95 .type = FB_TYPE_PACKED_PIXELS,
96 .xpanstep = 8,
97 .ypanstep = 1,
98};
99
100static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = {
101 .xres = 640,
102 .yres = 480,
103 .xres_virtual = 640,
104 .yres_virtual = 480,
105 .bits_per_pixel = 8,
106 .red = {0, 8, 0},
107 .green = {0, 8, 0},
108 .blue = {0, 8, 0},
109 .transp = {0, 0, 0},
110 .activate = FB_ACTIVATE_NOW,
111 .height = -1,
112 .width = -1,
113 .pixclock = 39721,
114 .left_margin = 40,
115 .right_margin = 24,
116 .upper_margin = 32,
117 .lower_margin = 11,
118 .hsync_len = 96,
119 .vsync_len = 2,
120 .vmode = FB_VMODE_NONINTERLACED
121};
122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
124 u16 bg, u16 fg, u32 w, u32 h)
125{
James Simmonsf1ab5da2005-06-21 17:17:07 -0700126 u32 *data = (u32 *) data8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 int i, j, k = 0;
128 u32 b, tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
130 w = (w + 1) & ~1;
131
132 for (i = 0; i < h; i++) {
133 b = *data++;
134 reverse_order(&b);
135
136 for (j = 0; j < w / 2; j++) {
137 tmp = 0;
138#if defined (__BIG_ENDIAN)
139 tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
140 b <<= 1;
141 tmp |= (b & (1 << 31)) ? fg : bg;
142 b <<= 1;
143#else
144 tmp = (b & 1) ? fg : bg;
145 b >>= 1;
146 tmp |= (b & 1) ? fg << 16 : bg << 16;
147 b >>= 1;
148#endif
149 NV_WR32(&par->CURSOR[k++], 0, tmp);
150 }
151 k += (MAX_CURS - w) / 2;
152 }
153}
154
155static void nvidia_write_clut(struct nvidia_par *par,
156 u8 regnum, u8 red, u8 green, u8 blue)
157{
158 NVWriteDacMask(par, 0xff);
159 NVWriteDacWriteAddr(par, regnum);
160 NVWriteDacData(par, red);
161 NVWriteDacData(par, green);
162 NVWriteDacData(par, blue);
163}
164
165static void nvidia_read_clut(struct nvidia_par *par,
166 u8 regnum, u8 * red, u8 * green, u8 * blue)
167{
168 NVWriteDacMask(par, 0xff);
169 NVWriteDacReadAddr(par, regnum);
170 *red = NVReadDacData(par);
171 *green = NVReadDacData(par);
172 *blue = NVReadDacData(par);
173}
174
175static int nvidia_panel_tweak(struct nvidia_par *par,
176 struct _riva_hw_state *state)
177{
178 int tweak = 0;
179
180 if (par->paneltweak) {
181 tweak = par->paneltweak;
182 } else {
183 /* begin flat panel hacks */
184 /* This is unfortunate, but some chips need this register
185 tweaked or else you get artifacts where adjacent pixels are
186 swapped. There are no hard rules for what to set here so all
187 we can do is experiment and apply hacks. */
188
189 if(((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) {
190 /* At least one NV34 laptop needs this workaround. */
191 tweak = -1;
192 }
193
194 if((par->Chipset & 0xfff0) == 0x0310) {
195 tweak = 1;
196 }
197 /* end flat panel hacks */
198 }
199
200 return tweak;
201}
202
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700203static void nvidia_screen_off(struct nvidia_par *par, int on)
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800204{
205 unsigned char tmp;
206
207 if (on) {
208 /*
209 * Turn off screen and disable sequencer.
210 */
211 tmp = NVReadSeq(par, 0x01);
212
213 NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */
214 NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */
215 } else {
216 /*
217 * Reenable sequencer, then turn on screen.
218 */
219
220 tmp = NVReadSeq(par, 0x01);
221
222 NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */
223 NVWriteSeq(par, 0x00, 0x03); /* End Reset */
224 }
225}
226
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227static void nvidia_save_vga(struct nvidia_par *par,
228 struct _riva_hw_state *state)
229{
230 int i;
231
232 NVTRACE_ENTER();
233 NVLockUnlock(par, 0);
234
235 NVUnloadStateExt(par, state);
236
237 state->misc_output = NVReadMiscOut(par);
238
239 for (i = 0; i < NUM_CRT_REGS; i++)
240 state->crtc[i] = NVReadCrtc(par, i);
241
242 for (i = 0; i < NUM_ATC_REGS; i++)
243 state->attr[i] = NVReadAttr(par, i);
244
245 for (i = 0; i < NUM_GRC_REGS; i++)
246 state->gra[i] = NVReadGr(par, i);
247
248 for (i = 0; i < NUM_SEQ_REGS; i++)
249 state->seq[i] = NVReadSeq(par, i);
250 NVTRACE_LEAVE();
251}
252
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800253#undef DUMP_REG
254
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800255static void nvidia_write_regs(struct nvidia_par *par,
256 struct _riva_hw_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 int i;
259
260 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
262 NVLoadStateExt(par, state);
263
264 NVWriteMiscOut(par, state->misc_output);
265
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800266 for (i = 1; i < NUM_SEQ_REGS; i++) {
267#ifdef DUMP_REG
268 printk(" SEQ[%02x] = %08x\n", i, state->seq[i]);
269#endif
270 NVWriteSeq(par, i, state->seq[i]);
271 }
272
273 /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 of CRTC[17] */
274 NVWriteCrtc(par, 0x11, state->crtc[0x11] & ~0x80);
275
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 for (i = 0; i < NUM_CRT_REGS; i++) {
277 switch (i) {
278 case 0x19:
279 case 0x20 ... 0x40:
280 break;
281 default:
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800282#ifdef DUMP_REG
283 printk("CRTC[%02x] = %08x\n", i, state->crtc[i]);
284#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 NVWriteCrtc(par, i, state->crtc[i]);
286 }
287 }
288
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800289 for (i = 0; i < NUM_GRC_REGS; i++) {
290#ifdef DUMP_REG
291 printk(" GRA[%02x] = %08x\n", i, state->gra[i]);
292#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 NVWriteGr(par, i, state->gra[i]);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800294 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800296 for (i = 0; i < NUM_ATC_REGS; i++) {
297#ifdef DUMP_REG
298 printk("ATTR[%02x] = %08x\n", i, state->attr[i]);
299#endif
300 NVWriteAttr(par, i, state->attr[i]);
301 }
302
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 NVTRACE_LEAVE();
304}
305
306static int nvidia_calc_regs(struct fb_info *info)
307{
308 struct nvidia_par *par = info->par;
309 struct _riva_hw_state *state = &par->ModeReg;
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700310 int i, depth = fb_get_color_depth(&info->var, &info->fix);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 int h_display = info->var.xres / 8 - 1;
312 int h_start = (info->var.xres + info->var.right_margin) / 8 - 1;
313 int h_end = (info->var.xres + info->var.right_margin +
314 info->var.hsync_len) / 8 - 1;
315 int h_total = (info->var.xres + info->var.right_margin +
316 info->var.hsync_len + info->var.left_margin) / 8 - 5;
317 int h_blank_s = h_display;
318 int h_blank_e = h_total + 4;
319 int v_display = info->var.yres - 1;
320 int v_start = info->var.yres + info->var.lower_margin - 1;
321 int v_end = (info->var.yres + info->var.lower_margin +
322 info->var.vsync_len) - 1;
323 int v_total = (info->var.yres + info->var.lower_margin +
324 info->var.vsync_len + info->var.upper_margin) - 2;
325 int v_blank_s = v_display;
326 int v_blank_e = v_total + 1;
327
328 /*
329 * Set all CRTC values.
330 */
331
332 if (info->var.vmode & FB_VMODE_INTERLACED)
333 v_total |= 1;
334
335 if (par->FlatPanel == 1) {
336 v_start = v_total - 3;
337 v_end = v_total - 2;
338 v_blank_s = v_start;
339 h_start = h_total - 5;
340 h_end = h_total - 2;
341 h_blank_e = h_total + 4;
342 }
343
344 state->crtc[0x0] = Set8Bits(h_total);
345 state->crtc[0x1] = Set8Bits(h_display);
346 state->crtc[0x2] = Set8Bits(h_blank_s);
347 state->crtc[0x3] = SetBitField(h_blank_e, 4: 0, 4:0)
348 | SetBit(7);
349 state->crtc[0x4] = Set8Bits(h_start);
350 state->crtc[0x5] = SetBitField(h_blank_e, 5: 5, 7:7)
351 | SetBitField(h_end, 4: 0, 4:0);
352 state->crtc[0x6] = SetBitField(v_total, 7: 0, 7:0);
353 state->crtc[0x7] = SetBitField(v_total, 8: 8, 0:0)
354 | SetBitField(v_display, 8: 8, 1:1)
355 | SetBitField(v_start, 8: 8, 2:2)
356 | SetBitField(v_blank_s, 8: 8, 3:3)
357 | SetBit(4)
358 | SetBitField(v_total, 9: 9, 5:5)
359 | SetBitField(v_display, 9: 9, 6:6)
360 | SetBitField(v_start, 9: 9, 7:7);
361 state->crtc[0x9] = SetBitField(v_blank_s, 9: 9, 5:5)
362 | SetBit(6)
363 | ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0x00);
364 state->crtc[0x10] = Set8Bits(v_start);
365 state->crtc[0x11] = SetBitField(v_end, 3: 0, 3:0) | SetBit(5);
366 state->crtc[0x12] = Set8Bits(v_display);
367 state->crtc[0x13] = ((info->var.xres_virtual / 8) *
368 (info->var.bits_per_pixel / 8));
369 state->crtc[0x15] = Set8Bits(v_blank_s);
370 state->crtc[0x16] = Set8Bits(v_blank_e);
371
372 state->attr[0x10] = 0x01;
373
374 if (par->Television)
375 state->attr[0x11] = 0x00;
376
377 state->screen = SetBitField(h_blank_e, 6: 6, 4:4)
378 | SetBitField(v_blank_s, 10: 10, 3:3)
379 | SetBitField(v_start, 10: 10, 2:2)
380 | SetBitField(v_display, 10: 10, 1:1)
381 | SetBitField(v_total, 10: 10, 0:0);
382
383 state->horiz = SetBitField(h_total, 8: 8, 0:0)
384 | SetBitField(h_display, 8: 8, 1:1)
385 | SetBitField(h_blank_s, 8: 8, 2:2)
386 | SetBitField(h_start, 8: 8, 3:3);
387
388 state->extra = SetBitField(v_total, 11: 11, 0:0)
389 | SetBitField(v_display, 11: 11, 2:2)
390 | SetBitField(v_start, 11: 11, 4:4)
391 | SetBitField(v_blank_s, 11: 11, 6:6);
392
393 if (info->var.vmode & FB_VMODE_INTERLACED) {
394 h_total = (h_total >> 1) & ~1;
395 state->interlace = Set8Bits(h_total);
396 state->horiz |= SetBitField(h_total, 8: 8, 4:4);
397 } else {
398 state->interlace = 0xff; /* interlace off */
399 }
400
401 /*
402 * Calculate the extended registers.
403 */
404
405 if (depth < 24)
406 i = depth;
407 else
408 i = 32;
409
410 if (par->Architecture >= NV_ARCH_10)
411 par->CURSOR = (volatile u32 __iomem *)(info->screen_base +
412 par->CursorStart);
413
414 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
415 state->misc_output &= ~0x40;
416 else
417 state->misc_output |= 0x40;
418 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
419 state->misc_output &= ~0x80;
420 else
421 state->misc_output |= 0x80;
422
423 NVCalcStateExt(par, state, i, info->var.xres_virtual,
424 info->var.xres, info->var.yres_virtual,
425 1000000000 / info->var.pixclock, info->var.vmode);
426
427 state->scale = NV_RD32(par->PRAMDAC, 0x00000848) & 0xfff000ff;
428 if (par->FlatPanel == 1) {
429 state->pixel |= (1 << 7);
430
431 if (!par->fpScaler || (par->fpWidth <= info->var.xres)
432 || (par->fpHeight <= info->var.yres)) {
433 state->scale |= (1 << 8);
434 }
435
436 if (!par->crtcSync_read) {
437 state->crtcSync = NV_RD32(par->PRAMDAC, 0x0828);
438 par->crtcSync_read = 1;
439 }
440
441 par->PanelTweak = nvidia_panel_tweak(par, state);
442 }
443
444 state->vpll = state->pll;
445 state->vpll2 = state->pll;
446 state->vpllB = state->pllB;
447 state->vpll2B = state->pllB;
448
449 VGA_WR08(par->PCIO, 0x03D4, 0x1C);
450 state->fifo = VGA_RD08(par->PCIO, 0x03D5) & ~(1<<5);
451
452 if (par->CRTCnumber) {
453 state->head = NV_RD32(par->PCRTC0, 0x00000860) & ~0x00001000;
454 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) | 0x00001000;
455 state->crtcOwner = 3;
456 state->pllsel |= 0x20000800;
457 state->vpll = NV_RD32(par->PRAMDAC0, 0x00000508);
458 if (par->twoStagePLL)
459 state->vpllB = NV_RD32(par->PRAMDAC0, 0x00000578);
460 } else if (par->twoHeads) {
461 state->head = NV_RD32(par->PCRTC0, 0x00000860) | 0x00001000;
462 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) & ~0x00001000;
463 state->crtcOwner = 0;
464 state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
465 if (par->twoStagePLL)
466 state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
467 }
468
469 state->cursorConfig = 0x00000100;
470
471 if (info->var.vmode & FB_VMODE_DOUBLE)
472 state->cursorConfig |= (1 << 4);
473
474 if (par->alphaCursor) {
475 if ((par->Chipset & 0x0ff0) != 0x0110)
476 state->cursorConfig |= 0x04011000;
477 else
478 state->cursorConfig |= 0x14011000;
479 state->general |= (1 << 29);
480 } else
481 state->cursorConfig |= 0x02000000;
482
483 if (par->twoHeads) {
484 if ((par->Chipset & 0x0ff0) == 0x0110) {
485 state->dither = NV_RD32(par->PRAMDAC, 0x0528) &
486 ~0x00010000;
487 if (par->FPDither)
488 state->dither |= 0x00010000;
489 } else {
490 state->dither = NV_RD32(par->PRAMDAC, 0x083C) & ~1;
491 if (par->FPDither)
492 state->dither |= 1;
493 }
494 }
495
496 state->timingH = 0;
497 state->timingV = 0;
498 state->displayV = info->var.xres;
499
500 return 0;
501}
502
503static void nvidia_init_vga(struct fb_info *info)
504{
505 struct nvidia_par *par = info->par;
506 struct _riva_hw_state *state = &par->ModeReg;
507 int i;
508
509 for (i = 0; i < 0x10; i++)
510 state->attr[i] = i;
511 state->attr[0x10] = 0x41;
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800512 state->attr[0x11] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 state->attr[0x12] = 0x0f;
514 state->attr[0x13] = 0x00;
515 state->attr[0x14] = 0x00;
516
517 memset(state->crtc, 0x00, NUM_CRT_REGS);
518 state->crtc[0x0a] = 0x20;
519 state->crtc[0x17] = 0xe3;
520 state->crtc[0x18] = 0xff;
521 state->crtc[0x28] = 0x40;
522
523 memset(state->gra, 0x00, NUM_GRC_REGS);
524 state->gra[0x05] = 0x40;
525 state->gra[0x06] = 0x05;
526 state->gra[0x07] = 0x0f;
527 state->gra[0x08] = 0xff;
528
529 state->seq[0x00] = 0x03;
530 state->seq[0x01] = 0x01;
531 state->seq[0x02] = 0x0f;
532 state->seq[0x03] = 0x00;
533 state->seq[0x04] = 0x0e;
534
535 state->misc_output = 0xeb;
536}
537
538static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
539{
540 struct nvidia_par *par = info->par;
541 u8 data[MAX_CURS * MAX_CURS / 8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 int i, set = cursor->set;
James Simmonsf1ab5da2005-06-21 17:17:07 -0700543 u16 fg, bg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544
Antonino A. Daplas7a482422005-09-21 07:30:21 +0800545 if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
James Simmonsf1ab5da2005-06-21 17:17:07 -0700546 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
548 NVShowHideCursor(par, 0);
549
550 if (par->cursor_reset) {
551 set = FB_CUR_SETALL;
552 par->cursor_reset = 0;
553 }
554
555 if (set & FB_CUR_SETSIZE)
556 memset_io(par->CURSOR, 0, MAX_CURS * MAX_CURS * 2);
557
558 if (set & FB_CUR_SETPOS) {
559 u32 xx, yy, temp;
560
561 yy = cursor->image.dy - info->var.yoffset;
562 xx = cursor->image.dx - info->var.xoffset;
563 temp = xx & 0xFFFF;
564 temp |= yy << 16;
565
566 NV_WR32(par->PRAMDAC, 0x0000300, temp);
567 }
568
569 if (set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
570 u32 bg_idx = cursor->image.bg_color;
571 u32 fg_idx = cursor->image.fg_color;
572 u32 s_pitch = (cursor->image.width + 7) >> 3;
573 u32 d_pitch = MAX_CURS / 8;
574 u8 *dat = (u8 *) cursor->image.data;
575 u8 *msk = (u8 *) cursor->mask;
576 u8 *src;
577
578 src = kmalloc(s_pitch * cursor->image.height, GFP_ATOMIC);
579
580 if (src) {
581 switch (cursor->rop) {
582 case ROP_XOR:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700583 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 src[i] = dat[i] ^ msk[i];
585 break;
586 case ROP_COPY:
587 default:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700588 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 src[i] = dat[i] & msk[i];
590 break;
591 }
592
James Simmonsf1ab5da2005-06-21 17:17:07 -0700593 fb_pad_aligned_buffer(data, d_pitch, src, s_pitch,
594 cursor->image.height);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
596 bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
597 ((info->cmap.green[bg_idx] & 0xf8) << 2) |
598 ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | 1 << 15;
599
600 fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
601 ((info->cmap.green[fg_idx] & 0xf8) << 2) |
602 ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
603
604 NVLockUnlock(par, 0);
605
606 nvidiafb_load_cursor_image(par, data, bg, fg,
607 cursor->image.width,
608 cursor->image.height);
609 kfree(src);
610 }
611 }
612
613 if (cursor->enable)
614 NVShowHideCursor(par, 1);
615
616 return 0;
617}
618
619static int nvidiafb_set_par(struct fb_info *info)
620{
621 struct nvidia_par *par = info->par;
622
623 NVTRACE_ENTER();
624
625 NVLockUnlock(par, 1);
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800626 if (!par->FlatPanel || !par->twoHeads)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 par->FPDither = 0;
628
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800629 if (par->FPDither < 0) {
630 if ((par->Chipset & 0x0ff0) == 0x0110)
631 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x0528)
632 & 0x00010000);
633 else
634 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1);
635 printk(KERN_INFO PFX "Flat panel dithering %s\n",
636 par->FPDither ? "enabled" : "disabled");
637 }
638
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700639 info->fix.visual = (info->var.bits_per_pixel == 8) ?
640 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 nvidia_init_vga(info);
643 nvidia_calc_regs(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645 NVLockUnlock(par, 0);
646 if (par->twoHeads) {
647 VGA_WR08(par->PCIO, 0x03D4, 0x44);
648 VGA_WR08(par->PCIO, 0x03D5, par->ModeReg.crtcOwner);
649 NVLockUnlock(par, 0);
650 }
651
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700652 nvidia_screen_off(par, 1);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800653
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800654 nvidia_write_regs(par, &par->ModeReg);
655 NVSetStartAddress(par, 0);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800656
657#if defined (__BIG_ENDIAN)
658 /* turn on LFB swapping */
659 {
660 unsigned char tmp;
661
662 VGA_WR08(par->PCIO, 0x3d4, 0x46);
663 tmp = VGA_RD08(par->PCIO, 0x3d5);
664 tmp |= (1 << 7);
665 VGA_WR08(par->PCIO, 0x3d5, tmp);
666 }
667#endif
668
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 info->fix.line_length = (info->var.xres_virtual *
670 info->var.bits_per_pixel) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 if (info->var.accel_flags) {
672 info->fbops->fb_imageblit = nvidiafb_imageblit;
673 info->fbops->fb_fillrect = nvidiafb_fillrect;
674 info->fbops->fb_copyarea = nvidiafb_copyarea;
675 info->fbops->fb_sync = nvidiafb_sync;
676 info->pixmap.scan_align = 4;
677 info->flags &= ~FBINFO_HWACCEL_DISABLED;
Antonino A. Daplas01b15bd2007-07-17 04:05:25 -0700678 info->flags |= FBINFO_READS_FAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 NVResetGraphics(info);
680 } else {
681 info->fbops->fb_imageblit = cfb_imageblit;
682 info->fbops->fb_fillrect = cfb_fillrect;
683 info->fbops->fb_copyarea = cfb_copyarea;
684 info->fbops->fb_sync = NULL;
685 info->pixmap.scan_align = 1;
686 info->flags |= FBINFO_HWACCEL_DISABLED;
Antonino A. Daplas01b15bd2007-07-17 04:05:25 -0700687 info->flags &= ~FBINFO_READS_FAST;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 }
689
690 par->cursor_reset = 1;
691
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700692 nvidia_screen_off(par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Paul Mackerras70abac62006-10-03 01:15:14 -0700694#ifdef CONFIG_BOOTX_TEXT
695 /* Update debug text engine */
696 btext_update_display(info->fix.smem_start,
697 info->var.xres, info->var.yres,
698 info->var.bits_per_pixel, info->fix.line_length);
699#endif
700
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700701 NVLockUnlock(par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 NVTRACE_LEAVE();
703 return 0;
704}
705
706static int nvidiafb_setcolreg(unsigned regno, unsigned red, unsigned green,
707 unsigned blue, unsigned transp,
708 struct fb_info *info)
709{
710 struct nvidia_par *par = info->par;
711 int i;
712
713 NVTRACE_ENTER();
714 if (regno >= (1 << info->var.green.length))
715 return -EINVAL;
716
717 if (info->var.grayscale) {
718 /* gray = 0.30*R + 0.59*G + 0.11*B */
719 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
720 }
721
722 if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
723 ((u32 *) info->pseudo_palette)[regno] =
724 (regno << info->var.red.offset) |
725 (regno << info->var.green.offset) |
726 (regno << info->var.blue.offset);
727 }
728
729 switch (info->var.bits_per_pixel) {
730 case 8:
731 /* "transparent" stuff is completely ignored. */
732 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
733 break;
734 case 16:
735 if (info->var.green.length == 5) {
736 for (i = 0; i < 8; i++) {
737 nvidia_write_clut(par, regno * 8 + i, red >> 8,
738 green >> 8, blue >> 8);
739 }
740 } else {
741 u8 r, g, b;
742
743 if (regno < 32) {
744 for (i = 0; i < 8; i++) {
745 nvidia_write_clut(par, regno * 8 + i,
746 red >> 8, green >> 8,
747 blue >> 8);
748 }
749 }
750
751 nvidia_read_clut(par, regno * 4, &r, &g, &b);
752
753 for (i = 0; i < 4; i++)
754 nvidia_write_clut(par, regno * 4 + i, r,
755 green >> 8, b);
756 }
757 break;
758 case 32:
759 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
760 break;
761 default:
762 /* do nothing */
763 break;
764 }
765
766 NVTRACE_LEAVE();
767 return 0;
768}
769
770static int nvidiafb_check_var(struct fb_var_screeninfo *var,
771 struct fb_info *info)
772{
773 struct nvidia_par *par = info->par;
774 int memlen, vramlen, mode_valid = 0;
775 int pitch, err = 0;
776
777 NVTRACE_ENTER();
778
779 var->transp.offset = 0;
780 var->transp.length = 0;
781
782 var->xres &= ~7;
783
784 if (var->bits_per_pixel <= 8)
785 var->bits_per_pixel = 8;
786 else if (var->bits_per_pixel <= 16)
787 var->bits_per_pixel = 16;
788 else
789 var->bits_per_pixel = 32;
790
791 switch (var->bits_per_pixel) {
792 case 8:
793 var->red.offset = 0;
794 var->red.length = 8;
795 var->green.offset = 0;
796 var->green.length = 8;
797 var->blue.offset = 0;
798 var->blue.length = 8;
799 var->transp.offset = 0;
800 var->transp.length = 0;
801 break;
802 case 16:
803 var->green.length = (var->green.length < 6) ? 5 : 6;
804 var->red.length = 5;
805 var->blue.length = 5;
806 var->transp.length = 6 - var->green.length;
807 var->blue.offset = 0;
808 var->green.offset = 5;
809 var->red.offset = 5 + var->green.length;
810 var->transp.offset = (5 + var->red.offset) & 15;
811 break;
812 case 32: /* RGBA 8888 */
813 var->red.offset = 16;
814 var->red.length = 8;
815 var->green.offset = 8;
816 var->green.length = 8;
817 var->blue.offset = 0;
818 var->blue.length = 8;
819 var->transp.length = 8;
820 var->transp.offset = 24;
821 break;
822 }
823
824 var->red.msb_right = 0;
825 var->green.msb_right = 0;
826 var->blue.msb_right = 0;
827 var->transp.msb_right = 0;
828
829 if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
830 !info->monspecs.dclkmax || !fb_validate_mode(var, info))
831 mode_valid = 1;
832
833 /* calculate modeline if supported by monitor */
834 if (!mode_valid && info->monspecs.gtf) {
835 if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
836 mode_valid = 1;
837 }
838
839 if (!mode_valid) {
Geert Uytterhoeven9791d762007-02-12 00:55:19 -0800840 const struct fb_videomode *mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
842 mode = fb_find_best_mode(var, &info->modelist);
843 if (mode) {
844 fb_videomode_to_var(var, mode);
845 mode_valid = 1;
846 }
847 }
848
849 if (!mode_valid && info->monspecs.modedb_len)
850 return -EINVAL;
851
852 if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres ||
853 par->fpHeight < var->yres))
854 return -EINVAL;
855
856 if (var->yres_virtual < var->yres)
857 var->yres_virtual = var->yres;
858
859 if (var->xres_virtual < var->xres)
860 var->xres_virtual = var->xres;
861
862 var->xres_virtual = (var->xres_virtual + 63) & ~63;
863
Antonino A. Daplas917bb072005-05-01 08:59:22 -0700864 vramlen = info->screen_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 pitch = ((var->xres_virtual * var->bits_per_pixel) + 7) / 8;
866 memlen = pitch * var->yres_virtual;
867
868 if (memlen > vramlen) {
869 var->yres_virtual = vramlen / pitch;
870
871 if (var->yres_virtual < var->yres) {
872 var->yres_virtual = var->yres;
873 var->xres_virtual = vramlen / var->yres_virtual;
874 var->xres_virtual /= var->bits_per_pixel / 8;
875 var->xres_virtual &= ~63;
876 pitch = (var->xres_virtual *
877 var->bits_per_pixel + 7) / 8;
878 memlen = pitch * var->yres;
879
880 if (var->xres_virtual < var->xres) {
881 printk("nvidiafb: required video memory, "
882 "%d bytes, for %dx%d-%d (virtual) "
883 "is out of range\n",
884 memlen, var->xres_virtual,
885 var->yres_virtual, var->bits_per_pixel);
886 err = -ENOMEM;
887 }
888 }
889 }
890
891 if (var->accel_flags) {
892 if (var->yres_virtual > 0x7fff)
893 var->yres_virtual = 0x7fff;
894 if (var->xres_virtual > 0x7fff)
895 var->xres_virtual = 0x7fff;
896 }
897
898 var->xres_virtual &= ~63;
899
900 NVTRACE_LEAVE();
901
902 return err;
903}
904
905static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
906 struct fb_info *info)
907{
908 struct nvidia_par *par = info->par;
909 u32 total;
910
Antonino A. Daplas3c8d61b2005-11-13 16:06:34 -0800911 total = var->yoffset * info->fix.line_length + var->xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912
913 NVSetStartAddress(par, total);
914
915 return 0;
916}
917
918static int nvidiafb_blank(int blank, struct fb_info *info)
919{
920 struct nvidia_par *par = info->par;
921 unsigned char tmp, vesa;
922
923 tmp = NVReadSeq(par, 0x01) & ~0x20; /* screen on/off */
924 vesa = NVReadCrtc(par, 0x1a) & ~0xc0; /* sync on/off */
925
926 NVTRACE_ENTER();
927
928 if (blank)
929 tmp |= 0x20;
930
931 switch (blank) {
932 case FB_BLANK_UNBLANK:
933 case FB_BLANK_NORMAL:
934 break;
935 case FB_BLANK_VSYNC_SUSPEND:
936 vesa |= 0x80;
937 break;
938 case FB_BLANK_HSYNC_SUSPEND:
939 vesa |= 0x40;
940 break;
941 case FB_BLANK_POWERDOWN:
942 vesa |= 0xc0;
943 break;
944 }
945
946 NVWriteSeq(par, 0x01, tmp);
947 NVWriteCrtc(par, 0x1a, vesa);
948
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 NVTRACE_LEAVE();
950
951 return 0;
952}
953
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -0700954/*
955 * Because the VGA registers are not mapped linearly in its MMIO space,
956 * restrict VGA register saving and restore to x86 only, where legacy VGA IO
957 * access is legal. Consequently, we must also check if the device is the
958 * primary display.
959 */
960#ifdef CONFIG_X86
961static void save_vga_x86(struct nvidia_par *par)
962{
963 struct resource *res= &par->pci_dev->resource[PCI_ROM_RESOURCE];
964
965 if (res && res->flags & IORESOURCE_ROM_SHADOW) {
966 memset(&par->vgastate, 0, sizeof(par->vgastate));
967 par->vgastate.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS |
968 VGA_SAVE_CMAP;
969 save_vga(&par->vgastate);
970 }
971}
972
973static void restore_vga_x86(struct nvidia_par *par)
974{
975 struct resource *res= &par->pci_dev->resource[PCI_ROM_RESOURCE];
976
977 if (res && res->flags & IORESOURCE_ROM_SHADOW)
978 restore_vga(&par->vgastate);
979}
980#else
981#define save_vga_x86(x) do {} while (0)
982#define restore_vga_x86(x) do {} while (0)
983#endif /* X86 */
984
985static int nvidiafb_open(struct fb_info *info, int user)
986{
987 struct nvidia_par *par = info->par;
988
989 mutex_lock(&par->open_lock);
990
991 if (!par->open_count) {
992 save_vga_x86(par);
993 nvidia_save_vga(par, &par->initial_state);
994 }
995
996 par->open_count++;
997 mutex_unlock(&par->open_lock);
998 return 0;
999}
1000
1001static int nvidiafb_release(struct fb_info *info, int user)
1002{
1003 struct nvidia_par *par = info->par;
1004 int err = 0;
1005
1006 mutex_lock(&par->open_lock);
1007
1008 if (!par->open_count) {
1009 err = -EINVAL;
1010 goto done;
1011 }
1012
1013 if (par->open_count == 1) {
1014 nvidia_write_regs(par, &par->initial_state);
1015 restore_vga_x86(par);
1016 }
1017
1018 par->open_count--;
1019done:
1020 mutex_unlock(&par->open_lock);
Antonino A. Daplas2620c6e2007-05-08 00:39:42 -07001021 return err;
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -07001022}
1023
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024static struct fb_ops nvidia_fb_ops = {
1025 .owner = THIS_MODULE,
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -07001026 .fb_open = nvidiafb_open,
1027 .fb_release = nvidiafb_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 .fb_check_var = nvidiafb_check_var,
1029 .fb_set_par = nvidiafb_set_par,
1030 .fb_setcolreg = nvidiafb_setcolreg,
1031 .fb_pan_display = nvidiafb_pan_display,
1032 .fb_blank = nvidiafb_blank,
1033 .fb_fillrect = nvidiafb_fillrect,
1034 .fb_copyarea = nvidiafb_copyarea,
1035 .fb_imageblit = nvidiafb_imageblit,
1036 .fb_cursor = nvidiafb_cursor,
1037 .fb_sync = nvidiafb_sync,
1038};
1039
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001040#ifdef CONFIG_PM
David Brownellc78a7c22006-08-14 23:11:06 -07001041static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001042{
1043 struct fb_info *info = pci_get_drvdata(dev);
1044 struct nvidia_par *par = info->par;
1045
David Brownellc78a7c22006-08-14 23:11:06 -07001046 if (mesg.event == PM_EVENT_PRETHAW)
1047 mesg.event = PM_EVENT_FREEZE;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001048 acquire_console_sem();
David Brownellc78a7c22006-08-14 23:11:06 -07001049 par->pm_state = mesg.event;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001050
David Brownellc78a7c22006-08-14 23:11:06 -07001051 if (mesg.event == PM_EVENT_SUSPEND) {
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001052 fb_set_suspend(info, 1);
1053 nvidiafb_blank(FB_BLANK_POWERDOWN, info);
1054 nvidia_write_regs(par, &par->SavedReg);
1055 pci_save_state(dev);
1056 pci_disable_device(dev);
David Brownellc78a7c22006-08-14 23:11:06 -07001057 pci_set_power_state(dev, pci_choose_state(dev, mesg));
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001058 }
David Brownellc78a7c22006-08-14 23:11:06 -07001059 dev->dev.power.power_state = mesg;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001060
1061 release_console_sem();
1062 return 0;
1063}
1064
1065static int nvidiafb_resume(struct pci_dev *dev)
1066{
1067 struct fb_info *info = pci_get_drvdata(dev);
1068 struct nvidia_par *par = info->par;
1069
1070 acquire_console_sem();
1071 pci_set_power_state(dev, PCI_D0);
1072
1073 if (par->pm_state != PM_EVENT_FREEZE) {
1074 pci_restore_state(dev);
Antonino A. Daplas7b566b12006-10-03 01:14:53 -07001075
1076 if (pci_enable_device(dev))
1077 goto fail;
1078
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001079 pci_set_master(dev);
1080 }
1081
1082 par->pm_state = PM_EVENT_ON;
1083 nvidiafb_set_par(info);
1084 fb_set_suspend (info, 0);
1085 nvidiafb_blank(FB_BLANK_UNBLANK, info);
1086
Antonino A. Daplas7b566b12006-10-03 01:14:53 -07001087fail:
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001088 release_console_sem();
1089 return 0;
1090}
1091#else
1092#define nvidiafb_suspend NULL
1093#define nvidiafb_resume NULL
1094#endif
1095
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096static int __devinit nvidia_set_fbinfo(struct fb_info *info)
1097{
1098 struct fb_monspecs *specs = &info->monspecs;
1099 struct fb_videomode modedb;
1100 struct nvidia_par *par = info->par;
1101 int lpitch;
1102
1103 NVTRACE_ENTER();
1104 info->flags = FBINFO_DEFAULT
1105 | FBINFO_HWACCEL_IMAGEBLIT
1106 | FBINFO_HWACCEL_FILLRECT
1107 | FBINFO_HWACCEL_COPYAREA
1108 | FBINFO_HWACCEL_YPAN;
1109
1110 fb_videomode_to_modelist(info->monspecs.modedb,
1111 info->monspecs.modedb_len, &info->modelist);
1112 fb_var_to_videomode(&modedb, &nvidiafb_default_var);
1113
Antonino A. Daplasade91852006-01-09 20:53:39 -08001114 switch (bpp) {
1115 case 0 ... 8:
1116 bpp = 8;
1117 break;
1118 case 9 ... 16:
1119 bpp = 16;
1120 break;
1121 default:
1122 bpp = 32;
1123 break;
1124 }
1125
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 if (specs->modedb != NULL) {
Geert Uytterhoeven9791d762007-02-12 00:55:19 -08001127 const struct fb_videomode *mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128
Geert Uytterhoeven9791d762007-02-12 00:55:19 -08001129 mode = fb_find_best_display(specs, &info->modelist);
1130 fb_videomode_to_var(&nvidiafb_default_var, mode);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001131 nvidiafb_default_var.bits_per_pixel = bpp;
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001132 } else if (par->fpWidth && par->fpHeight) {
1133 char buf[16];
1134
1135 memset(buf, 0, 16);
Antonino A. Daplas948a95f2005-09-09 13:09:59 -07001136 snprintf(buf, 15, "%dx%dMR", par->fpWidth, par->fpHeight);
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001137 fb_find_mode(&nvidiafb_default_var, info, buf, specs->modedb,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001138 specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 }
1140
1141 if (mode_option)
1142 fb_find_mode(&nvidiafb_default_var, info, mode_option,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001143 specs->modedb, specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144
1145 info->var = nvidiafb_default_var;
1146 info->fix.visual = (info->var.bits_per_pixel == 8) ?
1147 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
1148 info->pseudo_palette = par->pseudo_palette;
1149 fb_alloc_cmap(&info->cmap, 256, 0);
1150 fb_destroy_modedb(info->monspecs.modedb);
1151 info->monspecs.modedb = NULL;
1152
1153 /* maximize virtual vertical length */
1154 lpitch = info->var.xres_virtual *
1155 ((info->var.bits_per_pixel + 7) >> 3);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001156 info->var.yres_virtual = info->screen_size / lpitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
1158 info->pixmap.scan_align = 4;
1159 info->pixmap.buf_align = 4;
James Simmons58a60642005-06-21 17:17:08 -07001160 info->pixmap.access_align = 32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 info->pixmap.size = 8 * 1024;
1162 info->pixmap.flags = FB_PIXMAP_SYSTEM;
1163
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001164 if (!hwcur)
Antonino A. Daplasc465e052005-11-07 01:00:35 -08001165 info->fbops->fb_cursor = NULL;
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001166
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 info->var.accel_flags = (!noaccel);
1168
1169 switch (par->Architecture) {
1170 case NV_ARCH_04:
1171 info->fix.accel = FB_ACCEL_NV4;
1172 break;
1173 case NV_ARCH_10:
1174 info->fix.accel = FB_ACCEL_NV_10;
1175 break;
1176 case NV_ARCH_20:
1177 info->fix.accel = FB_ACCEL_NV_20;
1178 break;
1179 case NV_ARCH_30:
1180 info->fix.accel = FB_ACCEL_NV_30;
1181 break;
1182 case NV_ARCH_40:
1183 info->fix.accel = FB_ACCEL_NV_40;
1184 break;
1185 }
1186
1187 NVTRACE_LEAVE();
1188
1189 return nvidiafb_check_var(&info->var, info);
1190}
1191
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001192static u32 __devinit nvidia_get_chipset(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193{
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001194 struct nvidia_par *par = info->par;
1195 u32 id = (par->pci_dev->vendor << 16) | par->pci_dev->device;
1196
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001197 printk(KERN_INFO PFX "Device ID: %x \n", id);
1198
Antonino A. Daplasac1ae162007-07-17 04:05:30 -07001199 if ((id & 0xfff0) == 0x00f0 ||
1200 (id & 0xfff0) == 0x02e0) {
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001201 /* pci-e */
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001202 id = NV_RD32(par->REGS, 0x1800);
1203
1204 if ((id & 0x0000ffff) == 0x000010DE)
1205 id = 0x10DE0000 | (id >> 16);
1206 else if ((id & 0xffff0000) == 0xDE100000) /* wrong endian */
1207 id = 0x10DE0000 | ((id << 8) & 0x0000ff00) |
1208 ((id >> 8) & 0x000000ff);
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001209 printk(KERN_INFO PFX "Subsystem ID: %x \n", id);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001210 }
1211
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001212 return id;
1213}
1214
1215static u32 __devinit nvidia_get_arch(struct fb_info *info)
1216{
1217 struct nvidia_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 u32 arch = 0;
1219
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001220 switch (par->Chipset & 0x0ff0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 case 0x0100: /* GeForce 256 */
1222 case 0x0110: /* GeForce2 MX */
1223 case 0x0150: /* GeForce2 */
1224 case 0x0170: /* GeForce4 MX */
1225 case 0x0180: /* GeForce4 MX (8x AGP) */
1226 case 0x01A0: /* nForce */
1227 case 0x01F0: /* nForce2 */
1228 arch = NV_ARCH_10;
1229 break;
1230 case 0x0200: /* GeForce3 */
1231 case 0x0250: /* GeForce4 Ti */
1232 case 0x0280: /* GeForce4 Ti (8x AGP) */
1233 arch = NV_ARCH_20;
1234 break;
1235 case 0x0300: /* GeForceFX 5800 */
1236 case 0x0310: /* GeForceFX 5600 */
1237 case 0x0320: /* GeForceFX 5200 */
1238 case 0x0330: /* GeForceFX 5900 */
1239 case 0x0340: /* GeForceFX 5700 */
1240 arch = NV_ARCH_30;
1241 break;
Wink Savillee40c6752006-11-10 12:27:52 -08001242 case 0x0040: /* GeForce 6800 */
1243 case 0x00C0: /* GeForce 6800 */
1244 case 0x0120: /* GeForce 6800 */
Wink Savillee40c6752006-11-10 12:27:52 -08001245 case 0x0140: /* GeForce 6600 */
1246 case 0x0160: /* GeForce 6200 */
1247 case 0x01D0: /* GeForce 7200, 7300, 7400 */
1248 case 0x0090: /* GeForce 7800 */
1249 case 0x0210: /* GeForce 6800 */
1250 case 0x0220: /* GeForce 6200 */
Wink Savillee40c6752006-11-10 12:27:52 -08001251 case 0x0240: /* GeForce 6100 */
1252 case 0x0290: /* GeForce 7900 */
1253 case 0x0390: /* GeForce 7600 */
Antonino A. Daplasac1ae162007-07-17 04:05:30 -07001254 case 0x03D0:
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;
Antonino A. Daplas3c03ec22007-10-16 01:29:20 -07001309 par->reverse_i2c = reverse_i2c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
1311 /* enable IO and mem if not already done */
1312 pci_read_config_word(pd, PCI_COMMAND, &cmd);
1313 cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
1314 pci_write_config_word(pd, PCI_COMMAND, cmd);
1315
1316 nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
1317 nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
1318 nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
1319
1320 par->REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
1321
1322 if (!par->REGS) {
1323 printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
1324 goto err_out_free_base0;
1325 }
1326
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001327 par->Chipset = nvidia_get_chipset(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001328 par->Architecture = nvidia_get_arch(info);
1329
1330 if (par->Architecture == 0) {
1331 printk(KERN_ERR PFX "unknown NV_ARCH\n");
1332 goto err_out_arch;
1333 }
1334
1335 sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
1336
Antonino A. Daplas918799a2006-01-09 20:53:40 -08001337 if (NVCommonSetup(info))
1338 goto err_out_arch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
1340 par->FbAddress = nvidiafb_fix.smem_start;
1341 par->FbMapSize = par->RamAmountKBytes * 1024;
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001342 if (vram && vram * 1024 * 1024 < par->FbMapSize)
1343 par->FbMapSize = vram * 1024 * 1024;
1344
1345 /* Limit amount of vram to 64 MB */
1346 if (par->FbMapSize > 64 * 1024 * 1024)
1347 par->FbMapSize = 64 * 1024 * 1024;
1348
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001349 if(par->Architecture >= NV_ARCH_40)
1350 par->FbUsableSize = par->FbMapSize - (560 * 1024);
1351 else
1352 par->FbUsableSize = par->FbMapSize - (128 * 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 par->ScratchBufferSize = (par->Architecture < NV_ARCH_10) ? 8 * 1024 :
1354 16 * 1024;
1355 par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001356 par->CursorStart = par->FbUsableSize + (32 * 1024);
1357
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001359 info->screen_size = par->FbUsableSize;
1360 nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
1362 if (!info->screen_base) {
1363 printk(KERN_ERR PFX "cannot ioremap FB base\n");
1364 goto err_out_free_base1;
1365 }
1366
1367 par->FbStart = info->screen_base;
1368
1369#ifdef CONFIG_MTRR
1370 if (!nomtrr) {
1371 par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001372 par->RamAmountKBytes * 1024,
1373 MTRR_TYPE_WRCOMB, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 if (par->mtrr.vram < 0) {
1375 printk(KERN_ERR PFX "unable to setup MTRR\n");
1376 } else {
1377 par->mtrr.vram_valid = 1;
1378 /* let there be speed */
1379 printk(KERN_INFO PFX "MTRR set to ON\n");
1380 }
1381 }
1382#endif /* CONFIG_MTRR */
1383
1384 info->fbops = &nvidia_fb_ops;
1385 info->fix = nvidiafb_fix;
1386
1387 if (nvidia_set_fbinfo(info) < 0) {
1388 printk(KERN_ERR PFX "error setting initial video mode\n");
1389 goto err_out_iounmap_fb;
1390 }
1391
1392 nvidia_save_vga(par, &par->SavedReg);
1393
Guido Guentherce38cac2006-07-30 03:04:21 -07001394 pci_set_drvdata(pd, info);
Richard Purdie202d4e62007-03-03 17:43:52 +00001395
1396 if (backlight)
1397 nvidia_bl_init(par);
1398
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 if (register_framebuffer(info) < 0) {
1400 printk(KERN_ERR PFX "error registering nVidia framebuffer\n");
1401 goto err_out_iounmap_fb;
1402 }
1403
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404
1405 printk(KERN_INFO PFX
1406 "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
1407 info->fix.id,
1408 par->FbMapSize / (1024 * 1024), info->fix.smem_start);
Michael Hanselmann5474c122006-06-25 05:47:08 -07001409
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 NVTRACE_LEAVE();
1411 return 0;
1412
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001413err_out_iounmap_fb:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 iounmap(info->screen_base);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001415err_out_free_base1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 fb_destroy_modedb(info->monspecs.modedb);
1417 nvidia_delete_i2c_busses(par);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001418err_out_arch:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 iounmap(par->REGS);
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001420 err_out_free_base0:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 pci_release_regions(pd);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001422err_out_enable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 kfree(info->pixmap.addr);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001424err_out_kfree:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 framebuffer_release(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001426err_out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 return -ENODEV;
1428}
1429
Prarit Bhargava5e14ab82007-02-12 00:55:14 -08001430static void __devexit nvidiafb_remove(struct pci_dev *pd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431{
1432 struct fb_info *info = pci_get_drvdata(pd);
1433 struct nvidia_par *par = info->par;
1434
1435 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
Richard Purdie37ce69a2007-02-10 14:10:33 +00001437 unregister_framebuffer(info);
1438
Michael Hanselmann5474c122006-06-25 05:47:08 -07001439 nvidia_bl_exit(par);
1440
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441#ifdef CONFIG_MTRR
1442 if (par->mtrr.vram_valid)
1443 mtrr_del(par->mtrr.vram, info->fix.smem_start,
1444 info->fix.smem_len);
1445#endif /* CONFIG_MTRR */
1446
1447 iounmap(info->screen_base);
1448 fb_destroy_modedb(info->monspecs.modedb);
1449 nvidia_delete_i2c_busses(par);
1450 iounmap(par->REGS);
1451 pci_release_regions(pd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 kfree(info->pixmap.addr);
1453 framebuffer_release(info);
1454 pci_set_drvdata(pd, NULL);
1455 NVTRACE_LEAVE();
1456}
1457
1458/* ------------------------------------------------------------------------- *
1459 *
1460 * initialization
1461 *
1462 * ------------------------------------------------------------------------- */
1463
1464#ifndef MODULE
1465static int __devinit nvidiafb_setup(char *options)
1466{
1467 char *this_opt;
1468
1469 NVTRACE_ENTER();
1470 if (!options || !*options)
1471 return 0;
1472
1473 while ((this_opt = strsep(&options, ",")) != NULL) {
1474 if (!strncmp(this_opt, "forceCRTC", 9)) {
1475 char *p;
1476
1477 p = this_opt + 9;
1478 if (!*p || !*(++p))
1479 continue;
1480 forceCRTC = *p - '0';
1481 if (forceCRTC < 0 || forceCRTC > 1)
1482 forceCRTC = -1;
1483 } else if (!strncmp(this_opt, "flatpanel", 9)) {
1484 flatpanel = 1;
1485 } else if (!strncmp(this_opt, "hwcur", 5)) {
1486 hwcur = 1;
1487 } else if (!strncmp(this_opt, "noaccel", 6)) {
1488 noaccel = 1;
1489 } else if (!strncmp(this_opt, "noscale", 7)) {
1490 noscale = 1;
Antonino A. Daplas3c03ec22007-10-16 01:29:20 -07001491 } else if (!strncmp(this_opt, "reverse_i2c", 11)) {
1492 reverse_i2c = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 } else if (!strncmp(this_opt, "paneltweak:", 11)) {
1494 paneltweak = simple_strtoul(this_opt+11, NULL, 0);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001495 } else if (!strncmp(this_opt, "vram:", 5)) {
1496 vram = simple_strtoul(this_opt+5, NULL, 0);
Richard Purdie202d4e62007-03-03 17:43:52 +00001497 } else if (!strncmp(this_opt, "backlight:", 10)) {
1498 backlight = simple_strtoul(this_opt+10, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499#ifdef CONFIG_MTRR
1500 } else if (!strncmp(this_opt, "nomtrr", 6)) {
1501 nomtrr = 1;
1502#endif
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001503 } else if (!strncmp(this_opt, "fpdither:", 9)) {
1504 fpdither = simple_strtol(this_opt+9, NULL, 0);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001505 } else if (!strncmp(this_opt, "bpp:", 4)) {
1506 bpp = simple_strtoul(this_opt+4, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 } else
1508 mode_option = this_opt;
1509 }
1510 NVTRACE_LEAVE();
1511 return 0;
1512}
1513#endif /* !MODULE */
1514
1515static struct pci_driver nvidiafb_driver = {
1516 .name = "nvidiafb",
1517 .id_table = nvidiafb_pci_tbl,
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001518 .probe = nvidiafb_probe,
1519 .suspend = nvidiafb_suspend,
1520 .resume = nvidiafb_resume,
Prarit Bhargava5e14ab82007-02-12 00:55:14 -08001521 .remove = __devexit_p(nvidiafb_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522};
1523
1524/* ------------------------------------------------------------------------- *
1525 *
1526 * modularization
1527 *
1528 * ------------------------------------------------------------------------- */
1529
1530static int __devinit nvidiafb_init(void)
1531{
1532#ifndef MODULE
1533 char *option = NULL;
1534
1535 if (fb_get_options("nvidiafb", &option))
1536 return -ENODEV;
1537 nvidiafb_setup(option);
1538#endif
1539 return pci_register_driver(&nvidiafb_driver);
1540}
1541
1542module_init(nvidiafb_init);
1543
1544#ifdef MODULE
1545static void __exit nvidiafb_exit(void)
1546{
1547 pci_unregister_driver(&nvidiafb_driver);
1548}
1549
1550module_exit(nvidiafb_exit);
1551
1552module_param(flatpanel, int, 0);
1553MODULE_PARM_DESC(flatpanel,
1554 "Enables experimental flat panel support for some chipsets. "
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001555 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
1556module_param(fpdither, int, 0);
1557MODULE_PARM_DESC(fpdither,
1558 "Enables dithering of flat panel for 6 bits panels. "
1559 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560module_param(hwcur, int, 0);
1561MODULE_PARM_DESC(hwcur,
1562 "Enables hardware cursor implementation. (0 or 1=enabled) "
1563 "(default=0)");
1564module_param(noaccel, int, 0);
1565MODULE_PARM_DESC(noaccel,
1566 "Disables hardware acceleration. (0 or 1=disable) "
1567 "(default=0)");
1568module_param(noscale, int, 0);
1569MODULE_PARM_DESC(noscale,
1570 "Disables screen scaleing. (0 or 1=disable) "
1571 "(default=0, do scaling)");
1572module_param(paneltweak, int, 0);
1573MODULE_PARM_DESC(paneltweak,
1574 "Tweak display settings for flatpanels. "
1575 "(default=0, no tweaks)");
1576module_param(forceCRTC, int, 0);
1577MODULE_PARM_DESC(forceCRTC,
1578 "Forces usage of a particular CRTC in case autodetection "
1579 "fails. (0 or 1) (default=autodetect)");
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001580module_param(vram, int, 0);
1581MODULE_PARM_DESC(vram,
1582 "amount of framebuffer memory to remap in MiB"
1583 "(default=0 - remap entire memory)");
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001584module_param(mode_option, charp, 0);
1585MODULE_PARM_DESC(mode_option, "Specify initial video mode");
Antonino A. Daplasade91852006-01-09 20:53:39 -08001586module_param(bpp, int, 0);
1587MODULE_PARM_DESC(bpp, "pixel width in bits"
1588 "(default=8)");
Antonino A. Daplas3c03ec22007-10-16 01:29:20 -07001589module_param(reverse_i2c, int, 0);
1590MODULE_PARM_DESC(reverse_i2c, "reverse port assignment of the i2c bus");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591#ifdef CONFIG_MTRR
1592module_param(nomtrr, bool, 0);
1593MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
1594 "(default=0)");
1595#endif
1596
1597MODULE_AUTHOR("Antonino Daplas");
1598MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
1599MODULE_LICENSE("GPL");
1600#endif /* MODULE */
1601