blob: 7b5cffb2785127dca434b8e55804198a7e27d3c4 [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
12#include <linux/config.h>
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/errno.h>
16#include <linux/string.h>
17#include <linux/mm.h>
18#include <linux/tty.h>
19#include <linux/slab.h>
20#include <linux/delay.h>
21#include <linux/fb.h>
22#include <linux/init.h>
23#include <linux/pci.h>
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -080024#include <linux/console.h>
Michael Hanselmann5474c122006-06-25 05:47:08 -070025#include <linux/backlight.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#ifdef CONFIG_MTRR
27#include <asm/mtrr.h>
28#endif
29#ifdef CONFIG_PPC_OF
30#include <asm/prom.h>
31#include <asm/pci-bridge.h>
32#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
34#include "nv_local.h"
35#include "nv_type.h"
36#include "nv_proto.h"
37#include "nv_dma.h"
38
39#ifndef CONFIG_PCI /* sanity check */
40#error This driver requires PCI support.
41#endif
42
43#undef CONFIG_FB_NVIDIA_DEBUG
44#ifdef CONFIG_FB_NVIDIA_DEBUG
45#define NVTRACE printk
46#else
47#define NVTRACE if (0) printk
48#endif
49
50#define NVTRACE_ENTER(...) NVTRACE("%s START\n", __FUNCTION__)
51#define NVTRACE_LEAVE(...) NVTRACE("%s END\n", __FUNCTION__)
52
53#ifdef CONFIG_FB_NVIDIA_DEBUG
54#define assert(expr) \
55 if (!(expr)) { \
56 printk( "Assertion failed! %s,%s,%s,line=%d\n",\
57 #expr,__FILE__,__FUNCTION__,__LINE__); \
58 BUG(); \
59 }
60#else
61#define assert(expr)
62#endif
63
64#define PFX "nvidiafb: "
65
66/* HW cursor parameters */
67#define MAX_CURS 32
68
69static struct pci_device_id nvidiafb_pci_tbl[] = {
Antonino A. Daplas8eec4982006-06-26 00:26:30 -070070 {PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
71 PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0},
72 { 0, }
Linus Torvalds1da177e2005-04-16 15:20:36 -070073};
Linus Torvalds1da177e2005-04-16 15:20:36 -070074MODULE_DEVICE_TABLE(pci, nvidiafb_pci_tbl);
75
76/* command line data, set in nvidiafb_setup() */
77static int flatpanel __devinitdata = -1; /* Autodetect later */
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -080078static int fpdither __devinitdata = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079static int forceCRTC __devinitdata = -1;
80static int hwcur __devinitdata = 0;
81static int noaccel __devinitdata = 0;
82static int noscale __devinitdata = 0;
83static int paneltweak __devinitdata = 0;
Antonino A. Daplas917bb072005-05-01 08:59:22 -070084static int vram __devinitdata = 0;
Antonino A. Daplasade91852006-01-09 20:53:39 -080085static int bpp __devinitdata = 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086#ifdef CONFIG_MTRR
87static int nomtrr __devinitdata = 0;
88#endif
89
90static char *mode_option __devinitdata = NULL;
91
92static struct fb_fix_screeninfo __devinitdata nvidiafb_fix = {
93 .type = FB_TYPE_PACKED_PIXELS,
94 .xpanstep = 8,
95 .ypanstep = 1,
96};
97
98static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = {
99 .xres = 640,
100 .yres = 480,
101 .xres_virtual = 640,
102 .yres_virtual = 480,
103 .bits_per_pixel = 8,
104 .red = {0, 8, 0},
105 .green = {0, 8, 0},
106 .blue = {0, 8, 0},
107 .transp = {0, 0, 0},
108 .activate = FB_ACTIVATE_NOW,
109 .height = -1,
110 .width = -1,
111 .pixclock = 39721,
112 .left_margin = 40,
113 .right_margin = 24,
114 .upper_margin = 32,
115 .lower_margin = 11,
116 .hsync_len = 96,
117 .vsync_len = 2,
118 .vmode = FB_VMODE_NONINTERLACED
119};
120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
122 u16 bg, u16 fg, u32 w, u32 h)
123{
James Simmonsf1ab5da2005-06-21 17:17:07 -0700124 u32 *data = (u32 *) data8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 int i, j, k = 0;
126 u32 b, tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 w = (w + 1) & ~1;
129
130 for (i = 0; i < h; i++) {
131 b = *data++;
132 reverse_order(&b);
133
134 for (j = 0; j < w / 2; j++) {
135 tmp = 0;
136#if defined (__BIG_ENDIAN)
137 tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
138 b <<= 1;
139 tmp |= (b & (1 << 31)) ? fg : bg;
140 b <<= 1;
141#else
142 tmp = (b & 1) ? fg : bg;
143 b >>= 1;
144 tmp |= (b & 1) ? fg << 16 : bg << 16;
145 b >>= 1;
146#endif
147 NV_WR32(&par->CURSOR[k++], 0, tmp);
148 }
149 k += (MAX_CURS - w) / 2;
150 }
151}
152
153static void nvidia_write_clut(struct nvidia_par *par,
154 u8 regnum, u8 red, u8 green, u8 blue)
155{
156 NVWriteDacMask(par, 0xff);
157 NVWriteDacWriteAddr(par, regnum);
158 NVWriteDacData(par, red);
159 NVWriteDacData(par, green);
160 NVWriteDacData(par, blue);
161}
162
163static void nvidia_read_clut(struct nvidia_par *par,
164 u8 regnum, u8 * red, u8 * green, u8 * blue)
165{
166 NVWriteDacMask(par, 0xff);
167 NVWriteDacReadAddr(par, regnum);
168 *red = NVReadDacData(par);
169 *green = NVReadDacData(par);
170 *blue = NVReadDacData(par);
171}
172
173static int nvidia_panel_tweak(struct nvidia_par *par,
174 struct _riva_hw_state *state)
175{
176 int tweak = 0;
177
178 if (par->paneltweak) {
179 tweak = par->paneltweak;
180 } else {
181 /* begin flat panel hacks */
182 /* This is unfortunate, but some chips need this register
183 tweaked or else you get artifacts where adjacent pixels are
184 swapped. There are no hard rules for what to set here so all
185 we can do is experiment and apply hacks. */
186
187 if(((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) {
188 /* At least one NV34 laptop needs this workaround. */
189 tweak = -1;
190 }
191
192 if((par->Chipset & 0xfff0) == 0x0310) {
193 tweak = 1;
194 }
195 /* end flat panel hacks */
196 }
197
198 return tweak;
199}
200
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800201static void nvidia_vga_protect(struct nvidia_par *par, int on)
202{
203 unsigned char tmp;
204
205 if (on) {
206 /*
207 * Turn off screen and disable sequencer.
208 */
209 tmp = NVReadSeq(par, 0x01);
210
211 NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */
212 NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */
213 } else {
214 /*
215 * Reenable sequencer, then turn on screen.
216 */
217
218 tmp = NVReadSeq(par, 0x01);
219
220 NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */
221 NVWriteSeq(par, 0x00, 0x03); /* End Reset */
222 }
223}
224
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225static void nvidia_save_vga(struct nvidia_par *par,
226 struct _riva_hw_state *state)
227{
228 int i;
229
230 NVTRACE_ENTER();
231 NVLockUnlock(par, 0);
232
233 NVUnloadStateExt(par, state);
234
235 state->misc_output = NVReadMiscOut(par);
236
237 for (i = 0; i < NUM_CRT_REGS; i++)
238 state->crtc[i] = NVReadCrtc(par, i);
239
240 for (i = 0; i < NUM_ATC_REGS; i++)
241 state->attr[i] = NVReadAttr(par, i);
242
243 for (i = 0; i < NUM_GRC_REGS; i++)
244 state->gra[i] = NVReadGr(par, i);
245
246 for (i = 0; i < NUM_SEQ_REGS; i++)
247 state->seq[i] = NVReadSeq(par, i);
248 NVTRACE_LEAVE();
249}
250
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800251#undef DUMP_REG
252
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800253static void nvidia_write_regs(struct nvidia_par *par,
254 struct _riva_hw_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 int i;
257
258 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
260 NVLoadStateExt(par, state);
261
262 NVWriteMiscOut(par, state->misc_output);
263
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800264 for (i = 1; i < NUM_SEQ_REGS; i++) {
265#ifdef DUMP_REG
266 printk(" SEQ[%02x] = %08x\n", i, state->seq[i]);
267#endif
268 NVWriteSeq(par, i, state->seq[i]);
269 }
270
271 /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 of CRTC[17] */
272 NVWriteCrtc(par, 0x11, state->crtc[0x11] & ~0x80);
273
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 for (i = 0; i < NUM_CRT_REGS; i++) {
275 switch (i) {
276 case 0x19:
277 case 0x20 ... 0x40:
278 break;
279 default:
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800280#ifdef DUMP_REG
281 printk("CRTC[%02x] = %08x\n", i, state->crtc[i]);
282#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 NVWriteCrtc(par, i, state->crtc[i]);
284 }
285 }
286
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800287 for (i = 0; i < NUM_GRC_REGS; i++) {
288#ifdef DUMP_REG
289 printk(" GRA[%02x] = %08x\n", i, state->gra[i]);
290#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 NVWriteGr(par, i, state->gra[i]);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800292 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800294 for (i = 0; i < NUM_ATC_REGS; i++) {
295#ifdef DUMP_REG
296 printk("ATTR[%02x] = %08x\n", i, state->attr[i]);
297#endif
298 NVWriteAttr(par, i, state->attr[i]);
299 }
300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 NVTRACE_LEAVE();
302}
303
304static int nvidia_calc_regs(struct fb_info *info)
305{
306 struct nvidia_par *par = info->par;
307 struct _riva_hw_state *state = &par->ModeReg;
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700308 int i, depth = fb_get_color_depth(&info->var, &info->fix);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 int h_display = info->var.xres / 8 - 1;
310 int h_start = (info->var.xres + info->var.right_margin) / 8 - 1;
311 int h_end = (info->var.xres + info->var.right_margin +
312 info->var.hsync_len) / 8 - 1;
313 int h_total = (info->var.xres + info->var.right_margin +
314 info->var.hsync_len + info->var.left_margin) / 8 - 5;
315 int h_blank_s = h_display;
316 int h_blank_e = h_total + 4;
317 int v_display = info->var.yres - 1;
318 int v_start = info->var.yres + info->var.lower_margin - 1;
319 int v_end = (info->var.yres + info->var.lower_margin +
320 info->var.vsync_len) - 1;
321 int v_total = (info->var.yres + info->var.lower_margin +
322 info->var.vsync_len + info->var.upper_margin) - 2;
323 int v_blank_s = v_display;
324 int v_blank_e = v_total + 1;
325
326 /*
327 * Set all CRTC values.
328 */
329
330 if (info->var.vmode & FB_VMODE_INTERLACED)
331 v_total |= 1;
332
333 if (par->FlatPanel == 1) {
334 v_start = v_total - 3;
335 v_end = v_total - 2;
336 v_blank_s = v_start;
337 h_start = h_total - 5;
338 h_end = h_total - 2;
339 h_blank_e = h_total + 4;
340 }
341
342 state->crtc[0x0] = Set8Bits(h_total);
343 state->crtc[0x1] = Set8Bits(h_display);
344 state->crtc[0x2] = Set8Bits(h_blank_s);
345 state->crtc[0x3] = SetBitField(h_blank_e, 4: 0, 4:0)
346 | SetBit(7);
347 state->crtc[0x4] = Set8Bits(h_start);
348 state->crtc[0x5] = SetBitField(h_blank_e, 5: 5, 7:7)
349 | SetBitField(h_end, 4: 0, 4:0);
350 state->crtc[0x6] = SetBitField(v_total, 7: 0, 7:0);
351 state->crtc[0x7] = SetBitField(v_total, 8: 8, 0:0)
352 | SetBitField(v_display, 8: 8, 1:1)
353 | SetBitField(v_start, 8: 8, 2:2)
354 | SetBitField(v_blank_s, 8: 8, 3:3)
355 | SetBit(4)
356 | SetBitField(v_total, 9: 9, 5:5)
357 | SetBitField(v_display, 9: 9, 6:6)
358 | SetBitField(v_start, 9: 9, 7:7);
359 state->crtc[0x9] = SetBitField(v_blank_s, 9: 9, 5:5)
360 | SetBit(6)
361 | ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0x00);
362 state->crtc[0x10] = Set8Bits(v_start);
363 state->crtc[0x11] = SetBitField(v_end, 3: 0, 3:0) | SetBit(5);
364 state->crtc[0x12] = Set8Bits(v_display);
365 state->crtc[0x13] = ((info->var.xres_virtual / 8) *
366 (info->var.bits_per_pixel / 8));
367 state->crtc[0x15] = Set8Bits(v_blank_s);
368 state->crtc[0x16] = Set8Bits(v_blank_e);
369
370 state->attr[0x10] = 0x01;
371
372 if (par->Television)
373 state->attr[0x11] = 0x00;
374
375 state->screen = SetBitField(h_blank_e, 6: 6, 4:4)
376 | SetBitField(v_blank_s, 10: 10, 3:3)
377 | SetBitField(v_start, 10: 10, 2:2)
378 | SetBitField(v_display, 10: 10, 1:1)
379 | SetBitField(v_total, 10: 10, 0:0);
380
381 state->horiz = SetBitField(h_total, 8: 8, 0:0)
382 | SetBitField(h_display, 8: 8, 1:1)
383 | SetBitField(h_blank_s, 8: 8, 2:2)
384 | SetBitField(h_start, 8: 8, 3:3);
385
386 state->extra = SetBitField(v_total, 11: 11, 0:0)
387 | SetBitField(v_display, 11: 11, 2:2)
388 | SetBitField(v_start, 11: 11, 4:4)
389 | SetBitField(v_blank_s, 11: 11, 6:6);
390
391 if (info->var.vmode & FB_VMODE_INTERLACED) {
392 h_total = (h_total >> 1) & ~1;
393 state->interlace = Set8Bits(h_total);
394 state->horiz |= SetBitField(h_total, 8: 8, 4:4);
395 } else {
396 state->interlace = 0xff; /* interlace off */
397 }
398
399 /*
400 * Calculate the extended registers.
401 */
402
403 if (depth < 24)
404 i = depth;
405 else
406 i = 32;
407
408 if (par->Architecture >= NV_ARCH_10)
409 par->CURSOR = (volatile u32 __iomem *)(info->screen_base +
410 par->CursorStart);
411
412 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
413 state->misc_output &= ~0x40;
414 else
415 state->misc_output |= 0x40;
416 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
417 state->misc_output &= ~0x80;
418 else
419 state->misc_output |= 0x80;
420
421 NVCalcStateExt(par, state, i, info->var.xres_virtual,
422 info->var.xres, info->var.yres_virtual,
423 1000000000 / info->var.pixclock, info->var.vmode);
424
425 state->scale = NV_RD32(par->PRAMDAC, 0x00000848) & 0xfff000ff;
426 if (par->FlatPanel == 1) {
427 state->pixel |= (1 << 7);
428
429 if (!par->fpScaler || (par->fpWidth <= info->var.xres)
430 || (par->fpHeight <= info->var.yres)) {
431 state->scale |= (1 << 8);
432 }
433
434 if (!par->crtcSync_read) {
435 state->crtcSync = NV_RD32(par->PRAMDAC, 0x0828);
436 par->crtcSync_read = 1;
437 }
438
439 par->PanelTweak = nvidia_panel_tweak(par, state);
440 }
441
442 state->vpll = state->pll;
443 state->vpll2 = state->pll;
444 state->vpllB = state->pllB;
445 state->vpll2B = state->pllB;
446
447 VGA_WR08(par->PCIO, 0x03D4, 0x1C);
448 state->fifo = VGA_RD08(par->PCIO, 0x03D5) & ~(1<<5);
449
450 if (par->CRTCnumber) {
451 state->head = NV_RD32(par->PCRTC0, 0x00000860) & ~0x00001000;
452 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) | 0x00001000;
453 state->crtcOwner = 3;
454 state->pllsel |= 0x20000800;
455 state->vpll = NV_RD32(par->PRAMDAC0, 0x00000508);
456 if (par->twoStagePLL)
457 state->vpllB = NV_RD32(par->PRAMDAC0, 0x00000578);
458 } else if (par->twoHeads) {
459 state->head = NV_RD32(par->PCRTC0, 0x00000860) | 0x00001000;
460 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) & ~0x00001000;
461 state->crtcOwner = 0;
462 state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
463 if (par->twoStagePLL)
464 state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
465 }
466
467 state->cursorConfig = 0x00000100;
468
469 if (info->var.vmode & FB_VMODE_DOUBLE)
470 state->cursorConfig |= (1 << 4);
471
472 if (par->alphaCursor) {
473 if ((par->Chipset & 0x0ff0) != 0x0110)
474 state->cursorConfig |= 0x04011000;
475 else
476 state->cursorConfig |= 0x14011000;
477 state->general |= (1 << 29);
478 } else
479 state->cursorConfig |= 0x02000000;
480
481 if (par->twoHeads) {
482 if ((par->Chipset & 0x0ff0) == 0x0110) {
483 state->dither = NV_RD32(par->PRAMDAC, 0x0528) &
484 ~0x00010000;
485 if (par->FPDither)
486 state->dither |= 0x00010000;
487 } else {
488 state->dither = NV_RD32(par->PRAMDAC, 0x083C) & ~1;
489 if (par->FPDither)
490 state->dither |= 1;
491 }
492 }
493
494 state->timingH = 0;
495 state->timingV = 0;
496 state->displayV = info->var.xres;
497
498 return 0;
499}
500
501static void nvidia_init_vga(struct fb_info *info)
502{
503 struct nvidia_par *par = info->par;
504 struct _riva_hw_state *state = &par->ModeReg;
505 int i;
506
507 for (i = 0; i < 0x10; i++)
508 state->attr[i] = i;
509 state->attr[0x10] = 0x41;
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800510 state->attr[0x11] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 state->attr[0x12] = 0x0f;
512 state->attr[0x13] = 0x00;
513 state->attr[0x14] = 0x00;
514
515 memset(state->crtc, 0x00, NUM_CRT_REGS);
516 state->crtc[0x0a] = 0x20;
517 state->crtc[0x17] = 0xe3;
518 state->crtc[0x18] = 0xff;
519 state->crtc[0x28] = 0x40;
520
521 memset(state->gra, 0x00, NUM_GRC_REGS);
522 state->gra[0x05] = 0x40;
523 state->gra[0x06] = 0x05;
524 state->gra[0x07] = 0x0f;
525 state->gra[0x08] = 0xff;
526
527 state->seq[0x00] = 0x03;
528 state->seq[0x01] = 0x01;
529 state->seq[0x02] = 0x0f;
530 state->seq[0x03] = 0x00;
531 state->seq[0x04] = 0x0e;
532
533 state->misc_output = 0xeb;
534}
535
536static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
537{
538 struct nvidia_par *par = info->par;
539 u8 data[MAX_CURS * MAX_CURS / 8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 int i, set = cursor->set;
James Simmonsf1ab5da2005-06-21 17:17:07 -0700541 u16 fg, bg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Antonino A. Daplas7a482422005-09-21 07:30:21 +0800543 if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
James Simmonsf1ab5da2005-06-21 17:17:07 -0700544 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
546 NVShowHideCursor(par, 0);
547
548 if (par->cursor_reset) {
549 set = FB_CUR_SETALL;
550 par->cursor_reset = 0;
551 }
552
553 if (set & FB_CUR_SETSIZE)
554 memset_io(par->CURSOR, 0, MAX_CURS * MAX_CURS * 2);
555
556 if (set & FB_CUR_SETPOS) {
557 u32 xx, yy, temp;
558
559 yy = cursor->image.dy - info->var.yoffset;
560 xx = cursor->image.dx - info->var.xoffset;
561 temp = xx & 0xFFFF;
562 temp |= yy << 16;
563
564 NV_WR32(par->PRAMDAC, 0x0000300, temp);
565 }
566
567 if (set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
568 u32 bg_idx = cursor->image.bg_color;
569 u32 fg_idx = cursor->image.fg_color;
570 u32 s_pitch = (cursor->image.width + 7) >> 3;
571 u32 d_pitch = MAX_CURS / 8;
572 u8 *dat = (u8 *) cursor->image.data;
573 u8 *msk = (u8 *) cursor->mask;
574 u8 *src;
575
576 src = kmalloc(s_pitch * cursor->image.height, GFP_ATOMIC);
577
578 if (src) {
579 switch (cursor->rop) {
580 case ROP_XOR:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700581 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 src[i] = dat[i] ^ msk[i];
583 break;
584 case ROP_COPY:
585 default:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700586 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 src[i] = dat[i] & msk[i];
588 break;
589 }
590
James Simmonsf1ab5da2005-06-21 17:17:07 -0700591 fb_pad_aligned_buffer(data, d_pitch, src, s_pitch,
592 cursor->image.height);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
594 bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
595 ((info->cmap.green[bg_idx] & 0xf8) << 2) |
596 ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | 1 << 15;
597
598 fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
599 ((info->cmap.green[fg_idx] & 0xf8) << 2) |
600 ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
601
602 NVLockUnlock(par, 0);
603
604 nvidiafb_load_cursor_image(par, data, bg, fg,
605 cursor->image.width,
606 cursor->image.height);
607 kfree(src);
608 }
609 }
610
611 if (cursor->enable)
612 NVShowHideCursor(par, 1);
613
614 return 0;
615}
616
617static int nvidiafb_set_par(struct fb_info *info)
618{
619 struct nvidia_par *par = info->par;
620
621 NVTRACE_ENTER();
622
623 NVLockUnlock(par, 1);
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800624 if (!par->FlatPanel || !par->twoHeads)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 par->FPDither = 0;
626
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800627 if (par->FPDither < 0) {
628 if ((par->Chipset & 0x0ff0) == 0x0110)
629 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x0528)
630 & 0x00010000);
631 else
632 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1);
633 printk(KERN_INFO PFX "Flat panel dithering %s\n",
634 par->FPDither ? "enabled" : "disabled");
635 }
636
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700637 info->fix.visual = (info->var.bits_per_pixel == 8) ?
638 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
639
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 nvidia_init_vga(info);
641 nvidia_calc_regs(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
643 NVLockUnlock(par, 0);
644 if (par->twoHeads) {
645 VGA_WR08(par->PCIO, 0x03D4, 0x44);
646 VGA_WR08(par->PCIO, 0x03D5, par->ModeReg.crtcOwner);
647 NVLockUnlock(par, 0);
648 }
649
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800650 nvidia_vga_protect(par, 1);
651
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800652 nvidia_write_regs(par, &par->ModeReg);
653 NVSetStartAddress(par, 0);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800654
655#if defined (__BIG_ENDIAN)
656 /* turn on LFB swapping */
657 {
658 unsigned char tmp;
659
660 VGA_WR08(par->PCIO, 0x3d4, 0x46);
661 tmp = VGA_RD08(par->PCIO, 0x3d5);
662 tmp |= (1 << 7);
663 VGA_WR08(par->PCIO, 0x3d5, tmp);
664 }
665#endif
666
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 info->fix.line_length = (info->var.xres_virtual *
668 info->var.bits_per_pixel) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 if (info->var.accel_flags) {
670 info->fbops->fb_imageblit = nvidiafb_imageblit;
671 info->fbops->fb_fillrect = nvidiafb_fillrect;
672 info->fbops->fb_copyarea = nvidiafb_copyarea;
673 info->fbops->fb_sync = nvidiafb_sync;
674 info->pixmap.scan_align = 4;
675 info->flags &= ~FBINFO_HWACCEL_DISABLED;
676 NVResetGraphics(info);
677 } else {
678 info->fbops->fb_imageblit = cfb_imageblit;
679 info->fbops->fb_fillrect = cfb_fillrect;
680 info->fbops->fb_copyarea = cfb_copyarea;
681 info->fbops->fb_sync = NULL;
682 info->pixmap.scan_align = 1;
683 info->flags |= FBINFO_HWACCEL_DISABLED;
684 }
685
686 par->cursor_reset = 1;
687
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800688 nvidia_vga_protect(par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
690 NVTRACE_LEAVE();
691 return 0;
692}
693
694static int nvidiafb_setcolreg(unsigned regno, unsigned red, unsigned green,
695 unsigned blue, unsigned transp,
696 struct fb_info *info)
697{
698 struct nvidia_par *par = info->par;
699 int i;
700
701 NVTRACE_ENTER();
702 if (regno >= (1 << info->var.green.length))
703 return -EINVAL;
704
705 if (info->var.grayscale) {
706 /* gray = 0.30*R + 0.59*G + 0.11*B */
707 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
708 }
709
710 if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
711 ((u32 *) info->pseudo_palette)[regno] =
712 (regno << info->var.red.offset) |
713 (regno << info->var.green.offset) |
714 (regno << info->var.blue.offset);
715 }
716
717 switch (info->var.bits_per_pixel) {
718 case 8:
719 /* "transparent" stuff is completely ignored. */
720 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
721 break;
722 case 16:
723 if (info->var.green.length == 5) {
724 for (i = 0; i < 8; i++) {
725 nvidia_write_clut(par, regno * 8 + i, red >> 8,
726 green >> 8, blue >> 8);
727 }
728 } else {
729 u8 r, g, b;
730
731 if (regno < 32) {
732 for (i = 0; i < 8; i++) {
733 nvidia_write_clut(par, regno * 8 + i,
734 red >> 8, green >> 8,
735 blue >> 8);
736 }
737 }
738
739 nvidia_read_clut(par, regno * 4, &r, &g, &b);
740
741 for (i = 0; i < 4; i++)
742 nvidia_write_clut(par, regno * 4 + i, r,
743 green >> 8, b);
744 }
745 break;
746 case 32:
747 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
748 break;
749 default:
750 /* do nothing */
751 break;
752 }
753
754 NVTRACE_LEAVE();
755 return 0;
756}
757
758static int nvidiafb_check_var(struct fb_var_screeninfo *var,
759 struct fb_info *info)
760{
761 struct nvidia_par *par = info->par;
762 int memlen, vramlen, mode_valid = 0;
763 int pitch, err = 0;
764
765 NVTRACE_ENTER();
766
767 var->transp.offset = 0;
768 var->transp.length = 0;
769
770 var->xres &= ~7;
771
772 if (var->bits_per_pixel <= 8)
773 var->bits_per_pixel = 8;
774 else if (var->bits_per_pixel <= 16)
775 var->bits_per_pixel = 16;
776 else
777 var->bits_per_pixel = 32;
778
779 switch (var->bits_per_pixel) {
780 case 8:
781 var->red.offset = 0;
782 var->red.length = 8;
783 var->green.offset = 0;
784 var->green.length = 8;
785 var->blue.offset = 0;
786 var->blue.length = 8;
787 var->transp.offset = 0;
788 var->transp.length = 0;
789 break;
790 case 16:
791 var->green.length = (var->green.length < 6) ? 5 : 6;
792 var->red.length = 5;
793 var->blue.length = 5;
794 var->transp.length = 6 - var->green.length;
795 var->blue.offset = 0;
796 var->green.offset = 5;
797 var->red.offset = 5 + var->green.length;
798 var->transp.offset = (5 + var->red.offset) & 15;
799 break;
800 case 32: /* RGBA 8888 */
801 var->red.offset = 16;
802 var->red.length = 8;
803 var->green.offset = 8;
804 var->green.length = 8;
805 var->blue.offset = 0;
806 var->blue.length = 8;
807 var->transp.length = 8;
808 var->transp.offset = 24;
809 break;
810 }
811
812 var->red.msb_right = 0;
813 var->green.msb_right = 0;
814 var->blue.msb_right = 0;
815 var->transp.msb_right = 0;
816
817 if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
818 !info->monspecs.dclkmax || !fb_validate_mode(var, info))
819 mode_valid = 1;
820
821 /* calculate modeline if supported by monitor */
822 if (!mode_valid && info->monspecs.gtf) {
823 if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
824 mode_valid = 1;
825 }
826
827 if (!mode_valid) {
828 struct fb_videomode *mode;
829
830 mode = fb_find_best_mode(var, &info->modelist);
831 if (mode) {
832 fb_videomode_to_var(var, mode);
833 mode_valid = 1;
834 }
835 }
836
837 if (!mode_valid && info->monspecs.modedb_len)
838 return -EINVAL;
839
840 if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres ||
841 par->fpHeight < var->yres))
842 return -EINVAL;
843
844 if (var->yres_virtual < var->yres)
845 var->yres_virtual = var->yres;
846
847 if (var->xres_virtual < var->xres)
848 var->xres_virtual = var->xres;
849
850 var->xres_virtual = (var->xres_virtual + 63) & ~63;
851
Antonino A. Daplas917bb072005-05-01 08:59:22 -0700852 vramlen = info->screen_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 pitch = ((var->xres_virtual * var->bits_per_pixel) + 7) / 8;
854 memlen = pitch * var->yres_virtual;
855
856 if (memlen > vramlen) {
857 var->yres_virtual = vramlen / pitch;
858
859 if (var->yres_virtual < var->yres) {
860 var->yres_virtual = var->yres;
861 var->xres_virtual = vramlen / var->yres_virtual;
862 var->xres_virtual /= var->bits_per_pixel / 8;
863 var->xres_virtual &= ~63;
864 pitch = (var->xres_virtual *
865 var->bits_per_pixel + 7) / 8;
866 memlen = pitch * var->yres;
867
868 if (var->xres_virtual < var->xres) {
869 printk("nvidiafb: required video memory, "
870 "%d bytes, for %dx%d-%d (virtual) "
871 "is out of range\n",
872 memlen, var->xres_virtual,
873 var->yres_virtual, var->bits_per_pixel);
874 err = -ENOMEM;
875 }
876 }
877 }
878
879 if (var->accel_flags) {
880 if (var->yres_virtual > 0x7fff)
881 var->yres_virtual = 0x7fff;
882 if (var->xres_virtual > 0x7fff)
883 var->xres_virtual = 0x7fff;
884 }
885
886 var->xres_virtual &= ~63;
887
888 NVTRACE_LEAVE();
889
890 return err;
891}
892
893static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
894 struct fb_info *info)
895{
896 struct nvidia_par *par = info->par;
897 u32 total;
898
Antonino A. Daplas3c8d61b2005-11-13 16:06:34 -0800899 total = var->yoffset * info->fix.line_length + var->xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
901 NVSetStartAddress(par, total);
902
903 return 0;
904}
905
906static int nvidiafb_blank(int blank, struct fb_info *info)
907{
908 struct nvidia_par *par = info->par;
909 unsigned char tmp, vesa;
910
911 tmp = NVReadSeq(par, 0x01) & ~0x20; /* screen on/off */
912 vesa = NVReadCrtc(par, 0x1a) & ~0xc0; /* sync on/off */
913
914 NVTRACE_ENTER();
915
916 if (blank)
917 tmp |= 0x20;
918
919 switch (blank) {
920 case FB_BLANK_UNBLANK:
921 case FB_BLANK_NORMAL:
922 break;
923 case FB_BLANK_VSYNC_SUSPEND:
924 vesa |= 0x80;
925 break;
926 case FB_BLANK_HSYNC_SUSPEND:
927 vesa |= 0x40;
928 break;
929 case FB_BLANK_POWERDOWN:
930 vesa |= 0xc0;
931 break;
932 }
933
934 NVWriteSeq(par, 0x01, tmp);
935 NVWriteCrtc(par, 0x1a, vesa);
936
Michael Hanselmann5474c122006-06-25 05:47:08 -0700937#ifdef CONFIG_FB_NVIDIA_BACKLIGHT
938 mutex_lock(&info->bl_mutex);
939 if (info->bl_dev) {
940 down(&info->bl_dev->sem);
941 info->bl_dev->props->power = blank;
942 info->bl_dev->props->update_status(info->bl_dev);
943 up(&info->bl_dev->sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 }
Michael Hanselmann5474c122006-06-25 05:47:08 -0700945 mutex_unlock(&info->bl_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946#endif
947
948 NVTRACE_LEAVE();
949
950 return 0;
951}
952
953static struct fb_ops nvidia_fb_ops = {
954 .owner = THIS_MODULE,
955 .fb_check_var = nvidiafb_check_var,
956 .fb_set_par = nvidiafb_set_par,
957 .fb_setcolreg = nvidiafb_setcolreg,
958 .fb_pan_display = nvidiafb_pan_display,
959 .fb_blank = nvidiafb_blank,
960 .fb_fillrect = nvidiafb_fillrect,
961 .fb_copyarea = nvidiafb_copyarea,
962 .fb_imageblit = nvidiafb_imageblit,
963 .fb_cursor = nvidiafb_cursor,
964 .fb_sync = nvidiafb_sync,
965};
966
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800967#ifdef CONFIG_PM
968static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t state)
969{
970 struct fb_info *info = pci_get_drvdata(dev);
971 struct nvidia_par *par = info->par;
972
973 acquire_console_sem();
974 par->pm_state = state.event;
975
976 if (state.event == PM_EVENT_FREEZE) {
977 dev->dev.power.power_state = state;
978 } else {
979 fb_set_suspend(info, 1);
980 nvidiafb_blank(FB_BLANK_POWERDOWN, info);
981 nvidia_write_regs(par, &par->SavedReg);
982 pci_save_state(dev);
983 pci_disable_device(dev);
984 pci_set_power_state(dev, pci_choose_state(dev, state));
985 }
986
987 release_console_sem();
988 return 0;
989}
990
991static int nvidiafb_resume(struct pci_dev *dev)
992{
993 struct fb_info *info = pci_get_drvdata(dev);
994 struct nvidia_par *par = info->par;
995
996 acquire_console_sem();
997 pci_set_power_state(dev, PCI_D0);
998
999 if (par->pm_state != PM_EVENT_FREEZE) {
1000 pci_restore_state(dev);
1001 pci_enable_device(dev);
1002 pci_set_master(dev);
1003 }
1004
1005 par->pm_state = PM_EVENT_ON;
1006 nvidiafb_set_par(info);
1007 fb_set_suspend (info, 0);
1008 nvidiafb_blank(FB_BLANK_UNBLANK, info);
1009
1010 release_console_sem();
1011 return 0;
1012}
1013#else
1014#define nvidiafb_suspend NULL
1015#define nvidiafb_resume NULL
1016#endif
1017
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018static int __devinit nvidia_set_fbinfo(struct fb_info *info)
1019{
1020 struct fb_monspecs *specs = &info->monspecs;
1021 struct fb_videomode modedb;
1022 struct nvidia_par *par = info->par;
1023 int lpitch;
1024
1025 NVTRACE_ENTER();
1026 info->flags = FBINFO_DEFAULT
1027 | FBINFO_HWACCEL_IMAGEBLIT
1028 | FBINFO_HWACCEL_FILLRECT
1029 | FBINFO_HWACCEL_COPYAREA
1030 | FBINFO_HWACCEL_YPAN;
1031
1032 fb_videomode_to_modelist(info->monspecs.modedb,
1033 info->monspecs.modedb_len, &info->modelist);
1034 fb_var_to_videomode(&modedb, &nvidiafb_default_var);
1035
Antonino A. Daplasade91852006-01-09 20:53:39 -08001036 switch (bpp) {
1037 case 0 ... 8:
1038 bpp = 8;
1039 break;
1040 case 9 ... 16:
1041 bpp = 16;
1042 break;
1043 default:
1044 bpp = 32;
1045 break;
1046 }
1047
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 if (specs->modedb != NULL) {
Antonino A. Daplas5ee1ef92005-11-07 01:00:55 -08001049 struct fb_videomode *modedb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
Antonino A. Daplas5ee1ef92005-11-07 01:00:55 -08001051 modedb = fb_find_best_display(specs, &info->modelist);
1052 fb_videomode_to_var(&nvidiafb_default_var, modedb);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001053 nvidiafb_default_var.bits_per_pixel = bpp;
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001054 } else if (par->fpWidth && par->fpHeight) {
1055 char buf[16];
1056
1057 memset(buf, 0, 16);
Antonino A. Daplas948a95f2005-09-09 13:09:59 -07001058 snprintf(buf, 15, "%dx%dMR", par->fpWidth, par->fpHeight);
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001059 fb_find_mode(&nvidiafb_default_var, info, buf, specs->modedb,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001060 specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 }
1062
1063 if (mode_option)
1064 fb_find_mode(&nvidiafb_default_var, info, mode_option,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001065 specs->modedb, specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066
1067 info->var = nvidiafb_default_var;
1068 info->fix.visual = (info->var.bits_per_pixel == 8) ?
1069 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
1070 info->pseudo_palette = par->pseudo_palette;
1071 fb_alloc_cmap(&info->cmap, 256, 0);
1072 fb_destroy_modedb(info->monspecs.modedb);
1073 info->monspecs.modedb = NULL;
1074
1075 /* maximize virtual vertical length */
1076 lpitch = info->var.xres_virtual *
1077 ((info->var.bits_per_pixel + 7) >> 3);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001078 info->var.yres_virtual = info->screen_size / lpitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079
1080 info->pixmap.scan_align = 4;
1081 info->pixmap.buf_align = 4;
James Simmons58a60642005-06-21 17:17:08 -07001082 info->pixmap.access_align = 32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 info->pixmap.size = 8 * 1024;
1084 info->pixmap.flags = FB_PIXMAP_SYSTEM;
1085
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001086 if (!hwcur)
Antonino A. Daplasc465e052005-11-07 01:00:35 -08001087 info->fbops->fb_cursor = NULL;
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001088
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 info->var.accel_flags = (!noaccel);
1090
1091 switch (par->Architecture) {
1092 case NV_ARCH_04:
1093 info->fix.accel = FB_ACCEL_NV4;
1094 break;
1095 case NV_ARCH_10:
1096 info->fix.accel = FB_ACCEL_NV_10;
1097 break;
1098 case NV_ARCH_20:
1099 info->fix.accel = FB_ACCEL_NV_20;
1100 break;
1101 case NV_ARCH_30:
1102 info->fix.accel = FB_ACCEL_NV_30;
1103 break;
1104 case NV_ARCH_40:
1105 info->fix.accel = FB_ACCEL_NV_40;
1106 break;
1107 }
1108
1109 NVTRACE_LEAVE();
1110
1111 return nvidiafb_check_var(&info->var, info);
1112}
1113
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001114static u32 __devinit nvidia_get_chipset(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115{
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001116 struct nvidia_par *par = info->par;
1117 u32 id = (par->pci_dev->vendor << 16) | par->pci_dev->device;
1118
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001119 printk(KERN_INFO PFX "Device ID: %x \n", id);
1120
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001121 if ((id & 0xfff0) == 0x00f0) {
1122 /* pci-e */
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001123 id = NV_RD32(par->REGS, 0x1800);
1124
1125 if ((id & 0x0000ffff) == 0x000010DE)
1126 id = 0x10DE0000 | (id >> 16);
1127 else if ((id & 0xffff0000) == 0xDE100000) /* wrong endian */
1128 id = 0x10DE0000 | ((id << 8) & 0x0000ff00) |
1129 ((id >> 8) & 0x000000ff);
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001130 printk(KERN_INFO PFX "Subsystem ID: %x \n", id);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001131 }
1132
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001133 return id;
1134}
1135
1136static u32 __devinit nvidia_get_arch(struct fb_info *info)
1137{
1138 struct nvidia_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 u32 arch = 0;
1140
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001141 switch (par->Chipset & 0x0ff0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 case 0x0100: /* GeForce 256 */
1143 case 0x0110: /* GeForce2 MX */
1144 case 0x0150: /* GeForce2 */
1145 case 0x0170: /* GeForce4 MX */
1146 case 0x0180: /* GeForce4 MX (8x AGP) */
1147 case 0x01A0: /* nForce */
1148 case 0x01F0: /* nForce2 */
1149 arch = NV_ARCH_10;
1150 break;
1151 case 0x0200: /* GeForce3 */
1152 case 0x0250: /* GeForce4 Ti */
1153 case 0x0280: /* GeForce4 Ti (8x AGP) */
1154 arch = NV_ARCH_20;
1155 break;
1156 case 0x0300: /* GeForceFX 5800 */
1157 case 0x0310: /* GeForceFX 5600 */
1158 case 0x0320: /* GeForceFX 5200 */
1159 case 0x0330: /* GeForceFX 5900 */
1160 case 0x0340: /* GeForceFX 5700 */
1161 arch = NV_ARCH_30;
1162 break;
1163 case 0x0040:
1164 case 0x00C0:
1165 case 0x0120:
1166 case 0x0130:
1167 case 0x0140:
1168 case 0x0160:
1169 case 0x01D0:
1170 case 0x0090:
1171 case 0x0210:
1172 case 0x0220:
1173 case 0x0230:
Antonino A. Daplasfe610672006-06-26 00:26:38 -07001174 case 0x0240:
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001175 case 0x0290:
1176 case 0x0390:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 arch = NV_ARCH_40;
1178 break;
1179 case 0x0020: /* TNT, TNT2 */
1180 arch = NV_ARCH_04;
1181 break;
1182 default: /* unknown architecture */
1183 break;
1184 }
1185
1186 return arch;
1187}
1188
1189static int __devinit nvidiafb_probe(struct pci_dev *pd,
1190 const struct pci_device_id *ent)
1191{
1192 struct nvidia_par *par;
1193 struct fb_info *info;
1194 unsigned short cmd;
1195
1196
1197 NVTRACE_ENTER();
1198 assert(pd != NULL);
1199
1200 info = framebuffer_alloc(sizeof(struct nvidia_par), &pd->dev);
1201
1202 if (!info)
1203 goto err_out;
1204
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001205 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 par->pci_dev = pd;
1207
1208 info->pixmap.addr = kmalloc(8 * 1024, GFP_KERNEL);
1209
1210 if (info->pixmap.addr == NULL)
1211 goto err_out_kfree;
1212
1213 memset(info->pixmap.addr, 0, 8 * 1024);
1214
1215 if (pci_enable_device(pd)) {
1216 printk(KERN_ERR PFX "cannot enable PCI device\n");
1217 goto err_out_enable;
1218 }
1219
1220 if (pci_request_regions(pd, "nvidiafb")) {
1221 printk(KERN_ERR PFX "cannot request PCI regions\n");
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001222 goto err_out_enable;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 }
1224
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 par->FlatPanel = flatpanel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 if (flatpanel == 1)
1227 printk(KERN_INFO PFX "flatpanel support enabled\n");
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001228 par->FPDither = fpdither;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229
1230 par->CRTCnumber = forceCRTC;
1231 par->FpScale = (!noscale);
1232 par->paneltweak = paneltweak;
1233
1234 /* enable IO and mem if not already done */
1235 pci_read_config_word(pd, PCI_COMMAND, &cmd);
1236 cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
1237 pci_write_config_word(pd, PCI_COMMAND, cmd);
1238
1239 nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
1240 nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
1241 nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
1242
1243 par->REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
1244
1245 if (!par->REGS) {
1246 printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
1247 goto err_out_free_base0;
1248 }
1249
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001250 par->Chipset = nvidia_get_chipset(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001251 par->Architecture = nvidia_get_arch(info);
1252
1253 if (par->Architecture == 0) {
1254 printk(KERN_ERR PFX "unknown NV_ARCH\n");
1255 goto err_out_arch;
1256 }
1257
1258 sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
1259
Antonino A. Daplas918799a2006-01-09 20:53:40 -08001260 if (NVCommonSetup(info))
1261 goto err_out_arch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262
1263 par->FbAddress = nvidiafb_fix.smem_start;
1264 par->FbMapSize = par->RamAmountKBytes * 1024;
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001265 if (vram && vram * 1024 * 1024 < par->FbMapSize)
1266 par->FbMapSize = vram * 1024 * 1024;
1267
1268 /* Limit amount of vram to 64 MB */
1269 if (par->FbMapSize > 64 * 1024 * 1024)
1270 par->FbMapSize = 64 * 1024 * 1024;
1271
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001272 if(par->Architecture >= NV_ARCH_40)
1273 par->FbUsableSize = par->FbMapSize - (560 * 1024);
1274 else
1275 par->FbUsableSize = par->FbMapSize - (128 * 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 par->ScratchBufferSize = (par->Architecture < NV_ARCH_10) ? 8 * 1024 :
1277 16 * 1024;
1278 par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001279 par->CursorStart = par->FbUsableSize + (32 * 1024);
1280
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001282 info->screen_size = par->FbUsableSize;
1283 nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
1285 if (!info->screen_base) {
1286 printk(KERN_ERR PFX "cannot ioremap FB base\n");
1287 goto err_out_free_base1;
1288 }
1289
1290 par->FbStart = info->screen_base;
1291
1292#ifdef CONFIG_MTRR
1293 if (!nomtrr) {
1294 par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001295 par->RamAmountKBytes * 1024,
1296 MTRR_TYPE_WRCOMB, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 if (par->mtrr.vram < 0) {
1298 printk(KERN_ERR PFX "unable to setup MTRR\n");
1299 } else {
1300 par->mtrr.vram_valid = 1;
1301 /* let there be speed */
1302 printk(KERN_INFO PFX "MTRR set to ON\n");
1303 }
1304 }
1305#endif /* CONFIG_MTRR */
1306
1307 info->fbops = &nvidia_fb_ops;
1308 info->fix = nvidiafb_fix;
1309
1310 if (nvidia_set_fbinfo(info) < 0) {
1311 printk(KERN_ERR PFX "error setting initial video mode\n");
1312 goto err_out_iounmap_fb;
1313 }
1314
1315 nvidia_save_vga(par, &par->SavedReg);
1316
1317 if (register_framebuffer(info) < 0) {
1318 printk(KERN_ERR PFX "error registering nVidia framebuffer\n");
1319 goto err_out_iounmap_fb;
1320 }
1321
1322 pci_set_drvdata(pd, info);
1323
1324 printk(KERN_INFO PFX
1325 "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
1326 info->fix.id,
1327 par->FbMapSize / (1024 * 1024), info->fix.smem_start);
Michael Hanselmann5474c122006-06-25 05:47:08 -07001328
1329 nvidia_bl_init(par);
1330
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 NVTRACE_LEAVE();
1332 return 0;
1333
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001334err_out_iounmap_fb:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 iounmap(info->screen_base);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001336err_out_free_base1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 fb_destroy_modedb(info->monspecs.modedb);
1338 nvidia_delete_i2c_busses(par);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001339err_out_arch:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 iounmap(par->REGS);
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001341 err_out_free_base0:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 pci_release_regions(pd);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001343err_out_enable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 kfree(info->pixmap.addr);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001345err_out_kfree:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 framebuffer_release(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001347err_out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 return -ENODEV;
1349}
1350
1351static void __exit nvidiafb_remove(struct pci_dev *pd)
1352{
1353 struct fb_info *info = pci_get_drvdata(pd);
1354 struct nvidia_par *par = info->par;
1355
1356 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
Michael Hanselmann5474c122006-06-25 05:47:08 -07001358 nvidia_bl_exit(par);
1359
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 unregister_framebuffer(info);
1361#ifdef CONFIG_MTRR
1362 if (par->mtrr.vram_valid)
1363 mtrr_del(par->mtrr.vram, info->fix.smem_start,
1364 info->fix.smem_len);
1365#endif /* CONFIG_MTRR */
1366
1367 iounmap(info->screen_base);
1368 fb_destroy_modedb(info->monspecs.modedb);
1369 nvidia_delete_i2c_busses(par);
1370 iounmap(par->REGS);
1371 pci_release_regions(pd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 kfree(info->pixmap.addr);
1373 framebuffer_release(info);
1374 pci_set_drvdata(pd, NULL);
1375 NVTRACE_LEAVE();
1376}
1377
1378/* ------------------------------------------------------------------------- *
1379 *
1380 * initialization
1381 *
1382 * ------------------------------------------------------------------------- */
1383
1384#ifndef MODULE
1385static int __devinit nvidiafb_setup(char *options)
1386{
1387 char *this_opt;
1388
1389 NVTRACE_ENTER();
1390 if (!options || !*options)
1391 return 0;
1392
1393 while ((this_opt = strsep(&options, ",")) != NULL) {
1394 if (!strncmp(this_opt, "forceCRTC", 9)) {
1395 char *p;
1396
1397 p = this_opt + 9;
1398 if (!*p || !*(++p))
1399 continue;
1400 forceCRTC = *p - '0';
1401 if (forceCRTC < 0 || forceCRTC > 1)
1402 forceCRTC = -1;
1403 } else if (!strncmp(this_opt, "flatpanel", 9)) {
1404 flatpanel = 1;
1405 } else if (!strncmp(this_opt, "hwcur", 5)) {
1406 hwcur = 1;
1407 } else if (!strncmp(this_opt, "noaccel", 6)) {
1408 noaccel = 1;
1409 } else if (!strncmp(this_opt, "noscale", 7)) {
1410 noscale = 1;
1411 } else if (!strncmp(this_opt, "paneltweak:", 11)) {
1412 paneltweak = simple_strtoul(this_opt+11, NULL, 0);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001413 } else if (!strncmp(this_opt, "vram:", 5)) {
1414 vram = simple_strtoul(this_opt+5, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415#ifdef CONFIG_MTRR
1416 } else if (!strncmp(this_opt, "nomtrr", 6)) {
1417 nomtrr = 1;
1418#endif
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001419 } else if (!strncmp(this_opt, "fpdither:", 9)) {
1420 fpdither = simple_strtol(this_opt+9, NULL, 0);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001421 } else if (!strncmp(this_opt, "bpp:", 4)) {
1422 bpp = simple_strtoul(this_opt+4, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 } else
1424 mode_option = this_opt;
1425 }
1426 NVTRACE_LEAVE();
1427 return 0;
1428}
1429#endif /* !MODULE */
1430
1431static struct pci_driver nvidiafb_driver = {
1432 .name = "nvidiafb",
1433 .id_table = nvidiafb_pci_tbl,
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001434 .probe = nvidiafb_probe,
1435 .suspend = nvidiafb_suspend,
1436 .resume = nvidiafb_resume,
1437 .remove = __exit_p(nvidiafb_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438};
1439
1440/* ------------------------------------------------------------------------- *
1441 *
1442 * modularization
1443 *
1444 * ------------------------------------------------------------------------- */
1445
1446static int __devinit nvidiafb_init(void)
1447{
1448#ifndef MODULE
1449 char *option = NULL;
1450
1451 if (fb_get_options("nvidiafb", &option))
1452 return -ENODEV;
1453 nvidiafb_setup(option);
1454#endif
1455 return pci_register_driver(&nvidiafb_driver);
1456}
1457
1458module_init(nvidiafb_init);
1459
1460#ifdef MODULE
1461static void __exit nvidiafb_exit(void)
1462{
1463 pci_unregister_driver(&nvidiafb_driver);
1464}
1465
1466module_exit(nvidiafb_exit);
1467
1468module_param(flatpanel, int, 0);
1469MODULE_PARM_DESC(flatpanel,
1470 "Enables experimental flat panel support for some chipsets. "
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001471 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
1472module_param(fpdither, int, 0);
1473MODULE_PARM_DESC(fpdither,
1474 "Enables dithering of flat panel for 6 bits panels. "
1475 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476module_param(hwcur, int, 0);
1477MODULE_PARM_DESC(hwcur,
1478 "Enables hardware cursor implementation. (0 or 1=enabled) "
1479 "(default=0)");
1480module_param(noaccel, int, 0);
1481MODULE_PARM_DESC(noaccel,
1482 "Disables hardware acceleration. (0 or 1=disable) "
1483 "(default=0)");
1484module_param(noscale, int, 0);
1485MODULE_PARM_DESC(noscale,
1486 "Disables screen scaleing. (0 or 1=disable) "
1487 "(default=0, do scaling)");
1488module_param(paneltweak, int, 0);
1489MODULE_PARM_DESC(paneltweak,
1490 "Tweak display settings for flatpanels. "
1491 "(default=0, no tweaks)");
1492module_param(forceCRTC, int, 0);
1493MODULE_PARM_DESC(forceCRTC,
1494 "Forces usage of a particular CRTC in case autodetection "
1495 "fails. (0 or 1) (default=autodetect)");
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001496module_param(vram, int, 0);
1497MODULE_PARM_DESC(vram,
1498 "amount of framebuffer memory to remap in MiB"
1499 "(default=0 - remap entire memory)");
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001500module_param(mode_option, charp, 0);
1501MODULE_PARM_DESC(mode_option, "Specify initial video mode");
Antonino A. Daplasade91852006-01-09 20:53:39 -08001502module_param(bpp, int, 0);
1503MODULE_PARM_DESC(bpp, "pixel width in bits"
1504 "(default=8)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505#ifdef CONFIG_MTRR
1506module_param(nomtrr, bool, 0);
1507MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
1508 "(default=0)");
1509#endif
1510
1511MODULE_AUTHOR("Antonino Daplas");
1512MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
1513MODULE_LICENSE("GPL");
1514#endif /* MODULE */
1515