blob: 731edf7cb0a07036e79d64146f656e0105212ba2 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32#include "nv_local.h"
33#include "nv_type.h"
34#include "nv_proto.h"
35#include "nv_dma.h"
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#undef CONFIG_FB_NVIDIA_DEBUG
38#ifdef CONFIG_FB_NVIDIA_DEBUG
39#define NVTRACE printk
40#else
41#define NVTRACE if (0) printk
42#endif
43
44#define NVTRACE_ENTER(...) NVTRACE("%s START\n", __FUNCTION__)
45#define NVTRACE_LEAVE(...) NVTRACE("%s END\n", __FUNCTION__)
46
47#ifdef CONFIG_FB_NVIDIA_DEBUG
48#define assert(expr) \
49 if (!(expr)) { \
50 printk( "Assertion failed! %s,%s,%s,line=%d\n",\
51 #expr,__FILE__,__FUNCTION__,__LINE__); \
52 BUG(); \
53 }
54#else
55#define assert(expr)
56#endif
57
58#define PFX "nvidiafb: "
59
60/* HW cursor parameters */
61#define MAX_CURS 32
62
63static struct pci_device_id nvidiafb_pci_tbl[] = {
Antonino A. Daplas8eec4982006-06-26 00:26:30 -070064 {PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
65 PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0},
66 { 0, }
Linus Torvalds1da177e2005-04-16 15:20:36 -070067};
Linus Torvalds1da177e2005-04-16 15:20:36 -070068MODULE_DEVICE_TABLE(pci, nvidiafb_pci_tbl);
69
70/* command line data, set in nvidiafb_setup() */
71static int flatpanel __devinitdata = -1; /* Autodetect later */
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -080072static int fpdither __devinitdata = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073static int forceCRTC __devinitdata = -1;
74static int hwcur __devinitdata = 0;
75static int noaccel __devinitdata = 0;
76static int noscale __devinitdata = 0;
77static int paneltweak __devinitdata = 0;
Antonino A. Daplas917bb072005-05-01 08:59:22 -070078static int vram __devinitdata = 0;
Antonino A. Daplasade91852006-01-09 20:53:39 -080079static int bpp __devinitdata = 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080#ifdef CONFIG_MTRR
81static int nomtrr __devinitdata = 0;
82#endif
83
84static char *mode_option __devinitdata = NULL;
85
86static struct fb_fix_screeninfo __devinitdata nvidiafb_fix = {
87 .type = FB_TYPE_PACKED_PIXELS,
88 .xpanstep = 8,
89 .ypanstep = 1,
90};
91
92static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = {
93 .xres = 640,
94 .yres = 480,
95 .xres_virtual = 640,
96 .yres_virtual = 480,
97 .bits_per_pixel = 8,
98 .red = {0, 8, 0},
99 .green = {0, 8, 0},
100 .blue = {0, 8, 0},
101 .transp = {0, 0, 0},
102 .activate = FB_ACTIVATE_NOW,
103 .height = -1,
104 .width = -1,
105 .pixclock = 39721,
106 .left_margin = 40,
107 .right_margin = 24,
108 .upper_margin = 32,
109 .lower_margin = 11,
110 .hsync_len = 96,
111 .vsync_len = 2,
112 .vmode = FB_VMODE_NONINTERLACED
113};
114
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
116 u16 bg, u16 fg, u32 w, u32 h)
117{
James Simmonsf1ab5da2005-06-21 17:17:07 -0700118 u32 *data = (u32 *) data8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 int i, j, k = 0;
120 u32 b, tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
122 w = (w + 1) & ~1;
123
124 for (i = 0; i < h; i++) {
125 b = *data++;
126 reverse_order(&b);
127
128 for (j = 0; j < w / 2; j++) {
129 tmp = 0;
130#if defined (__BIG_ENDIAN)
131 tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
132 b <<= 1;
133 tmp |= (b & (1 << 31)) ? fg : bg;
134 b <<= 1;
135#else
136 tmp = (b & 1) ? fg : bg;
137 b >>= 1;
138 tmp |= (b & 1) ? fg << 16 : bg << 16;
139 b >>= 1;
140#endif
141 NV_WR32(&par->CURSOR[k++], 0, tmp);
142 }
143 k += (MAX_CURS - w) / 2;
144 }
145}
146
147static void nvidia_write_clut(struct nvidia_par *par,
148 u8 regnum, u8 red, u8 green, u8 blue)
149{
150 NVWriteDacMask(par, 0xff);
151 NVWriteDacWriteAddr(par, regnum);
152 NVWriteDacData(par, red);
153 NVWriteDacData(par, green);
154 NVWriteDacData(par, blue);
155}
156
157static void nvidia_read_clut(struct nvidia_par *par,
158 u8 regnum, u8 * red, u8 * green, u8 * blue)
159{
160 NVWriteDacMask(par, 0xff);
161 NVWriteDacReadAddr(par, regnum);
162 *red = NVReadDacData(par);
163 *green = NVReadDacData(par);
164 *blue = NVReadDacData(par);
165}
166
167static int nvidia_panel_tweak(struct nvidia_par *par,
168 struct _riva_hw_state *state)
169{
170 int tweak = 0;
171
172 if (par->paneltweak) {
173 tweak = par->paneltweak;
174 } else {
175 /* begin flat panel hacks */
176 /* This is unfortunate, but some chips need this register
177 tweaked or else you get artifacts where adjacent pixels are
178 swapped. There are no hard rules for what to set here so all
179 we can do is experiment and apply hacks. */
180
181 if(((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) {
182 /* At least one NV34 laptop needs this workaround. */
183 tweak = -1;
184 }
185
186 if((par->Chipset & 0xfff0) == 0x0310) {
187 tweak = 1;
188 }
189 /* end flat panel hacks */
190 }
191
192 return tweak;
193}
194
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800195static void nvidia_vga_protect(struct nvidia_par *par, int on)
196{
197 unsigned char tmp;
198
199 if (on) {
200 /*
201 * Turn off screen and disable sequencer.
202 */
203 tmp = NVReadSeq(par, 0x01);
204
205 NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */
206 NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */
207 } else {
208 /*
209 * Reenable sequencer, then turn on screen.
210 */
211
212 tmp = NVReadSeq(par, 0x01);
213
214 NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */
215 NVWriteSeq(par, 0x00, 0x03); /* End Reset */
216 }
217}
218
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219static void nvidia_save_vga(struct nvidia_par *par,
220 struct _riva_hw_state *state)
221{
222 int i;
223
224 NVTRACE_ENTER();
225 NVLockUnlock(par, 0);
226
227 NVUnloadStateExt(par, state);
228
229 state->misc_output = NVReadMiscOut(par);
230
231 for (i = 0; i < NUM_CRT_REGS; i++)
232 state->crtc[i] = NVReadCrtc(par, i);
233
234 for (i = 0; i < NUM_ATC_REGS; i++)
235 state->attr[i] = NVReadAttr(par, i);
236
237 for (i = 0; i < NUM_GRC_REGS; i++)
238 state->gra[i] = NVReadGr(par, i);
239
240 for (i = 0; i < NUM_SEQ_REGS; i++)
241 state->seq[i] = NVReadSeq(par, i);
242 NVTRACE_LEAVE();
243}
244
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800245#undef DUMP_REG
246
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800247static void nvidia_write_regs(struct nvidia_par *par,
248 struct _riva_hw_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 int i;
251
252 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
254 NVLoadStateExt(par, state);
255
256 NVWriteMiscOut(par, state->misc_output);
257
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800258 for (i = 1; i < NUM_SEQ_REGS; i++) {
259#ifdef DUMP_REG
260 printk(" SEQ[%02x] = %08x\n", i, state->seq[i]);
261#endif
262 NVWriteSeq(par, i, state->seq[i]);
263 }
264
265 /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 of CRTC[17] */
266 NVWriteCrtc(par, 0x11, state->crtc[0x11] & ~0x80);
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 for (i = 0; i < NUM_CRT_REGS; i++) {
269 switch (i) {
270 case 0x19:
271 case 0x20 ... 0x40:
272 break;
273 default:
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800274#ifdef DUMP_REG
275 printk("CRTC[%02x] = %08x\n", i, state->crtc[i]);
276#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 NVWriteCrtc(par, i, state->crtc[i]);
278 }
279 }
280
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800281 for (i = 0; i < NUM_GRC_REGS; i++) {
282#ifdef DUMP_REG
283 printk(" GRA[%02x] = %08x\n", i, state->gra[i]);
284#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 NVWriteGr(par, i, state->gra[i]);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800286 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800288 for (i = 0; i < NUM_ATC_REGS; i++) {
289#ifdef DUMP_REG
290 printk("ATTR[%02x] = %08x\n", i, state->attr[i]);
291#endif
292 NVWriteAttr(par, i, state->attr[i]);
293 }
294
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 NVTRACE_LEAVE();
296}
297
298static int nvidia_calc_regs(struct fb_info *info)
299{
300 struct nvidia_par *par = info->par;
301 struct _riva_hw_state *state = &par->ModeReg;
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700302 int i, depth = fb_get_color_depth(&info->var, &info->fix);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 int h_display = info->var.xres / 8 - 1;
304 int h_start = (info->var.xres + info->var.right_margin) / 8 - 1;
305 int h_end = (info->var.xres + info->var.right_margin +
306 info->var.hsync_len) / 8 - 1;
307 int h_total = (info->var.xres + info->var.right_margin +
308 info->var.hsync_len + info->var.left_margin) / 8 - 5;
309 int h_blank_s = h_display;
310 int h_blank_e = h_total + 4;
311 int v_display = info->var.yres - 1;
312 int v_start = info->var.yres + info->var.lower_margin - 1;
313 int v_end = (info->var.yres + info->var.lower_margin +
314 info->var.vsync_len) - 1;
315 int v_total = (info->var.yres + info->var.lower_margin +
316 info->var.vsync_len + info->var.upper_margin) - 2;
317 int v_blank_s = v_display;
318 int v_blank_e = v_total + 1;
319
320 /*
321 * Set all CRTC values.
322 */
323
324 if (info->var.vmode & FB_VMODE_INTERLACED)
325 v_total |= 1;
326
327 if (par->FlatPanel == 1) {
328 v_start = v_total - 3;
329 v_end = v_total - 2;
330 v_blank_s = v_start;
331 h_start = h_total - 5;
332 h_end = h_total - 2;
333 h_blank_e = h_total + 4;
334 }
335
336 state->crtc[0x0] = Set8Bits(h_total);
337 state->crtc[0x1] = Set8Bits(h_display);
338 state->crtc[0x2] = Set8Bits(h_blank_s);
339 state->crtc[0x3] = SetBitField(h_blank_e, 4: 0, 4:0)
340 | SetBit(7);
341 state->crtc[0x4] = Set8Bits(h_start);
342 state->crtc[0x5] = SetBitField(h_blank_e, 5: 5, 7:7)
343 | SetBitField(h_end, 4: 0, 4:0);
344 state->crtc[0x6] = SetBitField(v_total, 7: 0, 7:0);
345 state->crtc[0x7] = SetBitField(v_total, 8: 8, 0:0)
346 | SetBitField(v_display, 8: 8, 1:1)
347 | SetBitField(v_start, 8: 8, 2:2)
348 | SetBitField(v_blank_s, 8: 8, 3:3)
349 | SetBit(4)
350 | SetBitField(v_total, 9: 9, 5:5)
351 | SetBitField(v_display, 9: 9, 6:6)
352 | SetBitField(v_start, 9: 9, 7:7);
353 state->crtc[0x9] = SetBitField(v_blank_s, 9: 9, 5:5)
354 | SetBit(6)
355 | ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0x00);
356 state->crtc[0x10] = Set8Bits(v_start);
357 state->crtc[0x11] = SetBitField(v_end, 3: 0, 3:0) | SetBit(5);
358 state->crtc[0x12] = Set8Bits(v_display);
359 state->crtc[0x13] = ((info->var.xres_virtual / 8) *
360 (info->var.bits_per_pixel / 8));
361 state->crtc[0x15] = Set8Bits(v_blank_s);
362 state->crtc[0x16] = Set8Bits(v_blank_e);
363
364 state->attr[0x10] = 0x01;
365
366 if (par->Television)
367 state->attr[0x11] = 0x00;
368
369 state->screen = SetBitField(h_blank_e, 6: 6, 4:4)
370 | SetBitField(v_blank_s, 10: 10, 3:3)
371 | SetBitField(v_start, 10: 10, 2:2)
372 | SetBitField(v_display, 10: 10, 1:1)
373 | SetBitField(v_total, 10: 10, 0:0);
374
375 state->horiz = SetBitField(h_total, 8: 8, 0:0)
376 | SetBitField(h_display, 8: 8, 1:1)
377 | SetBitField(h_blank_s, 8: 8, 2:2)
378 | SetBitField(h_start, 8: 8, 3:3);
379
380 state->extra = SetBitField(v_total, 11: 11, 0:0)
381 | SetBitField(v_display, 11: 11, 2:2)
382 | SetBitField(v_start, 11: 11, 4:4)
383 | SetBitField(v_blank_s, 11: 11, 6:6);
384
385 if (info->var.vmode & FB_VMODE_INTERLACED) {
386 h_total = (h_total >> 1) & ~1;
387 state->interlace = Set8Bits(h_total);
388 state->horiz |= SetBitField(h_total, 8: 8, 4:4);
389 } else {
390 state->interlace = 0xff; /* interlace off */
391 }
392
393 /*
394 * Calculate the extended registers.
395 */
396
397 if (depth < 24)
398 i = depth;
399 else
400 i = 32;
401
402 if (par->Architecture >= NV_ARCH_10)
403 par->CURSOR = (volatile u32 __iomem *)(info->screen_base +
404 par->CursorStart);
405
406 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
407 state->misc_output &= ~0x40;
408 else
409 state->misc_output |= 0x40;
410 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
411 state->misc_output &= ~0x80;
412 else
413 state->misc_output |= 0x80;
414
415 NVCalcStateExt(par, state, i, info->var.xres_virtual,
416 info->var.xres, info->var.yres_virtual,
417 1000000000 / info->var.pixclock, info->var.vmode);
418
419 state->scale = NV_RD32(par->PRAMDAC, 0x00000848) & 0xfff000ff;
420 if (par->FlatPanel == 1) {
421 state->pixel |= (1 << 7);
422
423 if (!par->fpScaler || (par->fpWidth <= info->var.xres)
424 || (par->fpHeight <= info->var.yres)) {
425 state->scale |= (1 << 8);
426 }
427
428 if (!par->crtcSync_read) {
429 state->crtcSync = NV_RD32(par->PRAMDAC, 0x0828);
430 par->crtcSync_read = 1;
431 }
432
433 par->PanelTweak = nvidia_panel_tweak(par, state);
434 }
435
436 state->vpll = state->pll;
437 state->vpll2 = state->pll;
438 state->vpllB = state->pllB;
439 state->vpll2B = state->pllB;
440
441 VGA_WR08(par->PCIO, 0x03D4, 0x1C);
442 state->fifo = VGA_RD08(par->PCIO, 0x03D5) & ~(1<<5);
443
444 if (par->CRTCnumber) {
445 state->head = NV_RD32(par->PCRTC0, 0x00000860) & ~0x00001000;
446 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) | 0x00001000;
447 state->crtcOwner = 3;
448 state->pllsel |= 0x20000800;
449 state->vpll = NV_RD32(par->PRAMDAC0, 0x00000508);
450 if (par->twoStagePLL)
451 state->vpllB = NV_RD32(par->PRAMDAC0, 0x00000578);
452 } else if (par->twoHeads) {
453 state->head = NV_RD32(par->PCRTC0, 0x00000860) | 0x00001000;
454 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) & ~0x00001000;
455 state->crtcOwner = 0;
456 state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
457 if (par->twoStagePLL)
458 state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
459 }
460
461 state->cursorConfig = 0x00000100;
462
463 if (info->var.vmode & FB_VMODE_DOUBLE)
464 state->cursorConfig |= (1 << 4);
465
466 if (par->alphaCursor) {
467 if ((par->Chipset & 0x0ff0) != 0x0110)
468 state->cursorConfig |= 0x04011000;
469 else
470 state->cursorConfig |= 0x14011000;
471 state->general |= (1 << 29);
472 } else
473 state->cursorConfig |= 0x02000000;
474
475 if (par->twoHeads) {
476 if ((par->Chipset & 0x0ff0) == 0x0110) {
477 state->dither = NV_RD32(par->PRAMDAC, 0x0528) &
478 ~0x00010000;
479 if (par->FPDither)
480 state->dither |= 0x00010000;
481 } else {
482 state->dither = NV_RD32(par->PRAMDAC, 0x083C) & ~1;
483 if (par->FPDither)
484 state->dither |= 1;
485 }
486 }
487
488 state->timingH = 0;
489 state->timingV = 0;
490 state->displayV = info->var.xres;
491
492 return 0;
493}
494
495static void nvidia_init_vga(struct fb_info *info)
496{
497 struct nvidia_par *par = info->par;
498 struct _riva_hw_state *state = &par->ModeReg;
499 int i;
500
501 for (i = 0; i < 0x10; i++)
502 state->attr[i] = i;
503 state->attr[0x10] = 0x41;
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800504 state->attr[0x11] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 state->attr[0x12] = 0x0f;
506 state->attr[0x13] = 0x00;
507 state->attr[0x14] = 0x00;
508
509 memset(state->crtc, 0x00, NUM_CRT_REGS);
510 state->crtc[0x0a] = 0x20;
511 state->crtc[0x17] = 0xe3;
512 state->crtc[0x18] = 0xff;
513 state->crtc[0x28] = 0x40;
514
515 memset(state->gra, 0x00, NUM_GRC_REGS);
516 state->gra[0x05] = 0x40;
517 state->gra[0x06] = 0x05;
518 state->gra[0x07] = 0x0f;
519 state->gra[0x08] = 0xff;
520
521 state->seq[0x00] = 0x03;
522 state->seq[0x01] = 0x01;
523 state->seq[0x02] = 0x0f;
524 state->seq[0x03] = 0x00;
525 state->seq[0x04] = 0x0e;
526
527 state->misc_output = 0xeb;
528}
529
530static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
531{
532 struct nvidia_par *par = info->par;
533 u8 data[MAX_CURS * MAX_CURS / 8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 int i, set = cursor->set;
James Simmonsf1ab5da2005-06-21 17:17:07 -0700535 u16 fg, bg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Antonino A. Daplas7a482422005-09-21 07:30:21 +0800537 if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
James Simmonsf1ab5da2005-06-21 17:17:07 -0700538 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
540 NVShowHideCursor(par, 0);
541
542 if (par->cursor_reset) {
543 set = FB_CUR_SETALL;
544 par->cursor_reset = 0;
545 }
546
547 if (set & FB_CUR_SETSIZE)
548 memset_io(par->CURSOR, 0, MAX_CURS * MAX_CURS * 2);
549
550 if (set & FB_CUR_SETPOS) {
551 u32 xx, yy, temp;
552
553 yy = cursor->image.dy - info->var.yoffset;
554 xx = cursor->image.dx - info->var.xoffset;
555 temp = xx & 0xFFFF;
556 temp |= yy << 16;
557
558 NV_WR32(par->PRAMDAC, 0x0000300, temp);
559 }
560
561 if (set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
562 u32 bg_idx = cursor->image.bg_color;
563 u32 fg_idx = cursor->image.fg_color;
564 u32 s_pitch = (cursor->image.width + 7) >> 3;
565 u32 d_pitch = MAX_CURS / 8;
566 u8 *dat = (u8 *) cursor->image.data;
567 u8 *msk = (u8 *) cursor->mask;
568 u8 *src;
569
570 src = kmalloc(s_pitch * cursor->image.height, GFP_ATOMIC);
571
572 if (src) {
573 switch (cursor->rop) {
574 case ROP_XOR:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700575 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 src[i] = dat[i] ^ msk[i];
577 break;
578 case ROP_COPY:
579 default:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700580 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 src[i] = dat[i] & msk[i];
582 break;
583 }
584
James Simmonsf1ab5da2005-06-21 17:17:07 -0700585 fb_pad_aligned_buffer(data, d_pitch, src, s_pitch,
586 cursor->image.height);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
588 bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
589 ((info->cmap.green[bg_idx] & 0xf8) << 2) |
590 ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | 1 << 15;
591
592 fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
593 ((info->cmap.green[fg_idx] & 0xf8) << 2) |
594 ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
595
596 NVLockUnlock(par, 0);
597
598 nvidiafb_load_cursor_image(par, data, bg, fg,
599 cursor->image.width,
600 cursor->image.height);
601 kfree(src);
602 }
603 }
604
605 if (cursor->enable)
606 NVShowHideCursor(par, 1);
607
608 return 0;
609}
610
611static int nvidiafb_set_par(struct fb_info *info)
612{
613 struct nvidia_par *par = info->par;
614
615 NVTRACE_ENTER();
616
617 NVLockUnlock(par, 1);
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800618 if (!par->FlatPanel || !par->twoHeads)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 par->FPDither = 0;
620
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800621 if (par->FPDither < 0) {
622 if ((par->Chipset & 0x0ff0) == 0x0110)
623 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x0528)
624 & 0x00010000);
625 else
626 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1);
627 printk(KERN_INFO PFX "Flat panel dithering %s\n",
628 par->FPDither ? "enabled" : "disabled");
629 }
630
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700631 info->fix.visual = (info->var.bits_per_pixel == 8) ?
632 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
633
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 nvidia_init_vga(info);
635 nvidia_calc_regs(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 NVLockUnlock(par, 0);
638 if (par->twoHeads) {
639 VGA_WR08(par->PCIO, 0x03D4, 0x44);
640 VGA_WR08(par->PCIO, 0x03D5, par->ModeReg.crtcOwner);
641 NVLockUnlock(par, 0);
642 }
643
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800644 nvidia_vga_protect(par, 1);
645
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800646 nvidia_write_regs(par, &par->ModeReg);
647 NVSetStartAddress(par, 0);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800648
649#if defined (__BIG_ENDIAN)
650 /* turn on LFB swapping */
651 {
652 unsigned char tmp;
653
654 VGA_WR08(par->PCIO, 0x3d4, 0x46);
655 tmp = VGA_RD08(par->PCIO, 0x3d5);
656 tmp |= (1 << 7);
657 VGA_WR08(par->PCIO, 0x3d5, tmp);
658 }
659#endif
660
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 info->fix.line_length = (info->var.xres_virtual *
662 info->var.bits_per_pixel) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 if (info->var.accel_flags) {
664 info->fbops->fb_imageblit = nvidiafb_imageblit;
665 info->fbops->fb_fillrect = nvidiafb_fillrect;
666 info->fbops->fb_copyarea = nvidiafb_copyarea;
667 info->fbops->fb_sync = nvidiafb_sync;
668 info->pixmap.scan_align = 4;
669 info->flags &= ~FBINFO_HWACCEL_DISABLED;
670 NVResetGraphics(info);
671 } else {
672 info->fbops->fb_imageblit = cfb_imageblit;
673 info->fbops->fb_fillrect = cfb_fillrect;
674 info->fbops->fb_copyarea = cfb_copyarea;
675 info->fbops->fb_sync = NULL;
676 info->pixmap.scan_align = 1;
677 info->flags |= FBINFO_HWACCEL_DISABLED;
678 }
679
680 par->cursor_reset = 1;
681
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800682 nvidia_vga_protect(par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
684 NVTRACE_LEAVE();
685 return 0;
686}
687
688static int nvidiafb_setcolreg(unsigned regno, unsigned red, unsigned green,
689 unsigned blue, unsigned transp,
690 struct fb_info *info)
691{
692 struct nvidia_par *par = info->par;
693 int i;
694
695 NVTRACE_ENTER();
696 if (regno >= (1 << info->var.green.length))
697 return -EINVAL;
698
699 if (info->var.grayscale) {
700 /* gray = 0.30*R + 0.59*G + 0.11*B */
701 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
702 }
703
704 if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
705 ((u32 *) info->pseudo_palette)[regno] =
706 (regno << info->var.red.offset) |
707 (regno << info->var.green.offset) |
708 (regno << info->var.blue.offset);
709 }
710
711 switch (info->var.bits_per_pixel) {
712 case 8:
713 /* "transparent" stuff is completely ignored. */
714 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
715 break;
716 case 16:
717 if (info->var.green.length == 5) {
718 for (i = 0; i < 8; i++) {
719 nvidia_write_clut(par, regno * 8 + i, red >> 8,
720 green >> 8, blue >> 8);
721 }
722 } else {
723 u8 r, g, b;
724
725 if (regno < 32) {
726 for (i = 0; i < 8; i++) {
727 nvidia_write_clut(par, regno * 8 + i,
728 red >> 8, green >> 8,
729 blue >> 8);
730 }
731 }
732
733 nvidia_read_clut(par, regno * 4, &r, &g, &b);
734
735 for (i = 0; i < 4; i++)
736 nvidia_write_clut(par, regno * 4 + i, r,
737 green >> 8, b);
738 }
739 break;
740 case 32:
741 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
742 break;
743 default:
744 /* do nothing */
745 break;
746 }
747
748 NVTRACE_LEAVE();
749 return 0;
750}
751
752static int nvidiafb_check_var(struct fb_var_screeninfo *var,
753 struct fb_info *info)
754{
755 struct nvidia_par *par = info->par;
756 int memlen, vramlen, mode_valid = 0;
757 int pitch, err = 0;
758
759 NVTRACE_ENTER();
760
761 var->transp.offset = 0;
762 var->transp.length = 0;
763
764 var->xres &= ~7;
765
766 if (var->bits_per_pixel <= 8)
767 var->bits_per_pixel = 8;
768 else if (var->bits_per_pixel <= 16)
769 var->bits_per_pixel = 16;
770 else
771 var->bits_per_pixel = 32;
772
773 switch (var->bits_per_pixel) {
774 case 8:
775 var->red.offset = 0;
776 var->red.length = 8;
777 var->green.offset = 0;
778 var->green.length = 8;
779 var->blue.offset = 0;
780 var->blue.length = 8;
781 var->transp.offset = 0;
782 var->transp.length = 0;
783 break;
784 case 16:
785 var->green.length = (var->green.length < 6) ? 5 : 6;
786 var->red.length = 5;
787 var->blue.length = 5;
788 var->transp.length = 6 - var->green.length;
789 var->blue.offset = 0;
790 var->green.offset = 5;
791 var->red.offset = 5 + var->green.length;
792 var->transp.offset = (5 + var->red.offset) & 15;
793 break;
794 case 32: /* RGBA 8888 */
795 var->red.offset = 16;
796 var->red.length = 8;
797 var->green.offset = 8;
798 var->green.length = 8;
799 var->blue.offset = 0;
800 var->blue.length = 8;
801 var->transp.length = 8;
802 var->transp.offset = 24;
803 break;
804 }
805
806 var->red.msb_right = 0;
807 var->green.msb_right = 0;
808 var->blue.msb_right = 0;
809 var->transp.msb_right = 0;
810
811 if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
812 !info->monspecs.dclkmax || !fb_validate_mode(var, info))
813 mode_valid = 1;
814
815 /* calculate modeline if supported by monitor */
816 if (!mode_valid && info->monspecs.gtf) {
817 if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
818 mode_valid = 1;
819 }
820
821 if (!mode_valid) {
822 struct fb_videomode *mode;
823
824 mode = fb_find_best_mode(var, &info->modelist);
825 if (mode) {
826 fb_videomode_to_var(var, mode);
827 mode_valid = 1;
828 }
829 }
830
831 if (!mode_valid && info->monspecs.modedb_len)
832 return -EINVAL;
833
834 if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres ||
835 par->fpHeight < var->yres))
836 return -EINVAL;
837
838 if (var->yres_virtual < var->yres)
839 var->yres_virtual = var->yres;
840
841 if (var->xres_virtual < var->xres)
842 var->xres_virtual = var->xres;
843
844 var->xres_virtual = (var->xres_virtual + 63) & ~63;
845
Antonino A. Daplas917bb072005-05-01 08:59:22 -0700846 vramlen = info->screen_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 pitch = ((var->xres_virtual * var->bits_per_pixel) + 7) / 8;
848 memlen = pitch * var->yres_virtual;
849
850 if (memlen > vramlen) {
851 var->yres_virtual = vramlen / pitch;
852
853 if (var->yres_virtual < var->yres) {
854 var->yres_virtual = var->yres;
855 var->xres_virtual = vramlen / var->yres_virtual;
856 var->xres_virtual /= var->bits_per_pixel / 8;
857 var->xres_virtual &= ~63;
858 pitch = (var->xres_virtual *
859 var->bits_per_pixel + 7) / 8;
860 memlen = pitch * var->yres;
861
862 if (var->xres_virtual < var->xres) {
863 printk("nvidiafb: required video memory, "
864 "%d bytes, for %dx%d-%d (virtual) "
865 "is out of range\n",
866 memlen, var->xres_virtual,
867 var->yres_virtual, var->bits_per_pixel);
868 err = -ENOMEM;
869 }
870 }
871 }
872
873 if (var->accel_flags) {
874 if (var->yres_virtual > 0x7fff)
875 var->yres_virtual = 0x7fff;
876 if (var->xres_virtual > 0x7fff)
877 var->xres_virtual = 0x7fff;
878 }
879
880 var->xres_virtual &= ~63;
881
882 NVTRACE_LEAVE();
883
884 return err;
885}
886
887static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
888 struct fb_info *info)
889{
890 struct nvidia_par *par = info->par;
891 u32 total;
892
Antonino A. Daplas3c8d61b2005-11-13 16:06:34 -0800893 total = var->yoffset * info->fix.line_length + var->xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
895 NVSetStartAddress(par, total);
896
897 return 0;
898}
899
900static int nvidiafb_blank(int blank, struct fb_info *info)
901{
902 struct nvidia_par *par = info->par;
903 unsigned char tmp, vesa;
904
905 tmp = NVReadSeq(par, 0x01) & ~0x20; /* screen on/off */
906 vesa = NVReadCrtc(par, 0x1a) & ~0xc0; /* sync on/off */
907
908 NVTRACE_ENTER();
909
910 if (blank)
911 tmp |= 0x20;
912
913 switch (blank) {
914 case FB_BLANK_UNBLANK:
915 case FB_BLANK_NORMAL:
916 break;
917 case FB_BLANK_VSYNC_SUSPEND:
918 vesa |= 0x80;
919 break;
920 case FB_BLANK_HSYNC_SUSPEND:
921 vesa |= 0x40;
922 break;
923 case FB_BLANK_POWERDOWN:
924 vesa |= 0xc0;
925 break;
926 }
927
928 NVWriteSeq(par, 0x01, tmp);
929 NVWriteCrtc(par, 0x1a, vesa);
930
Michael Hanselmanne01af032006-07-10 04:44:45 -0700931 nvidia_bl_set_power(info, blank);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
933 NVTRACE_LEAVE();
934
935 return 0;
936}
937
938static struct fb_ops nvidia_fb_ops = {
939 .owner = THIS_MODULE,
940 .fb_check_var = nvidiafb_check_var,
941 .fb_set_par = nvidiafb_set_par,
942 .fb_setcolreg = nvidiafb_setcolreg,
943 .fb_pan_display = nvidiafb_pan_display,
944 .fb_blank = nvidiafb_blank,
945 .fb_fillrect = nvidiafb_fillrect,
946 .fb_copyarea = nvidiafb_copyarea,
947 .fb_imageblit = nvidiafb_imageblit,
948 .fb_cursor = nvidiafb_cursor,
949 .fb_sync = nvidiafb_sync,
950};
951
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800952#ifdef CONFIG_PM
David Brownellc78a7c22006-08-14 23:11:06 -0700953static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800954{
955 struct fb_info *info = pci_get_drvdata(dev);
956 struct nvidia_par *par = info->par;
957
David Brownellc78a7c22006-08-14 23:11:06 -0700958 if (mesg.event == PM_EVENT_PRETHAW)
959 mesg.event = PM_EVENT_FREEZE;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800960 acquire_console_sem();
David Brownellc78a7c22006-08-14 23:11:06 -0700961 par->pm_state = mesg.event;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800962
David Brownellc78a7c22006-08-14 23:11:06 -0700963 if (mesg.event == PM_EVENT_SUSPEND) {
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800964 fb_set_suspend(info, 1);
965 nvidiafb_blank(FB_BLANK_POWERDOWN, info);
966 nvidia_write_regs(par, &par->SavedReg);
967 pci_save_state(dev);
968 pci_disable_device(dev);
David Brownellc78a7c22006-08-14 23:11:06 -0700969 pci_set_power_state(dev, pci_choose_state(dev, mesg));
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800970 }
David Brownellc78a7c22006-08-14 23:11:06 -0700971 dev->dev.power.power_state = mesg;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800972
973 release_console_sem();
974 return 0;
975}
976
977static int nvidiafb_resume(struct pci_dev *dev)
978{
979 struct fb_info *info = pci_get_drvdata(dev);
980 struct nvidia_par *par = info->par;
981
982 acquire_console_sem();
983 pci_set_power_state(dev, PCI_D0);
984
985 if (par->pm_state != PM_EVENT_FREEZE) {
986 pci_restore_state(dev);
Antonino A. Daplas7b566b12006-10-03 01:14:53 -0700987
988 if (pci_enable_device(dev))
989 goto fail;
990
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800991 pci_set_master(dev);
992 }
993
994 par->pm_state = PM_EVENT_ON;
995 nvidiafb_set_par(info);
996 fb_set_suspend (info, 0);
997 nvidiafb_blank(FB_BLANK_UNBLANK, info);
998
Antonino A. Daplas7b566b12006-10-03 01:14:53 -0700999fail:
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001000 release_console_sem();
1001 return 0;
1002}
1003#else
1004#define nvidiafb_suspend NULL
1005#define nvidiafb_resume NULL
1006#endif
1007
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008static int __devinit nvidia_set_fbinfo(struct fb_info *info)
1009{
1010 struct fb_monspecs *specs = &info->monspecs;
1011 struct fb_videomode modedb;
1012 struct nvidia_par *par = info->par;
1013 int lpitch;
1014
1015 NVTRACE_ENTER();
1016 info->flags = FBINFO_DEFAULT
1017 | FBINFO_HWACCEL_IMAGEBLIT
1018 | FBINFO_HWACCEL_FILLRECT
1019 | FBINFO_HWACCEL_COPYAREA
1020 | FBINFO_HWACCEL_YPAN;
1021
1022 fb_videomode_to_modelist(info->monspecs.modedb,
1023 info->monspecs.modedb_len, &info->modelist);
1024 fb_var_to_videomode(&modedb, &nvidiafb_default_var);
1025
Antonino A. Daplasade91852006-01-09 20:53:39 -08001026 switch (bpp) {
1027 case 0 ... 8:
1028 bpp = 8;
1029 break;
1030 case 9 ... 16:
1031 bpp = 16;
1032 break;
1033 default:
1034 bpp = 32;
1035 break;
1036 }
1037
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 if (specs->modedb != NULL) {
Antonino A. Daplas5ee1ef962005-11-07 01:00:55 -08001039 struct fb_videomode *modedb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040
Antonino A. Daplas5ee1ef962005-11-07 01:00:55 -08001041 modedb = fb_find_best_display(specs, &info->modelist);
1042 fb_videomode_to_var(&nvidiafb_default_var, modedb);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001043 nvidiafb_default_var.bits_per_pixel = bpp;
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001044 } else if (par->fpWidth && par->fpHeight) {
1045 char buf[16];
1046
1047 memset(buf, 0, 16);
Antonino A. Daplas948a95f2005-09-09 13:09:59 -07001048 snprintf(buf, 15, "%dx%dMR", par->fpWidth, par->fpHeight);
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001049 fb_find_mode(&nvidiafb_default_var, info, buf, specs->modedb,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001050 specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 }
1052
1053 if (mode_option)
1054 fb_find_mode(&nvidiafb_default_var, info, mode_option,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001055 specs->modedb, specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
1057 info->var = nvidiafb_default_var;
1058 info->fix.visual = (info->var.bits_per_pixel == 8) ?
1059 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
1060 info->pseudo_palette = par->pseudo_palette;
1061 fb_alloc_cmap(&info->cmap, 256, 0);
1062 fb_destroy_modedb(info->monspecs.modedb);
1063 info->monspecs.modedb = NULL;
1064
1065 /* maximize virtual vertical length */
1066 lpitch = info->var.xres_virtual *
1067 ((info->var.bits_per_pixel + 7) >> 3);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001068 info->var.yres_virtual = info->screen_size / lpitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069
1070 info->pixmap.scan_align = 4;
1071 info->pixmap.buf_align = 4;
James Simmons58a60642005-06-21 17:17:08 -07001072 info->pixmap.access_align = 32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 info->pixmap.size = 8 * 1024;
1074 info->pixmap.flags = FB_PIXMAP_SYSTEM;
1075
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001076 if (!hwcur)
Antonino A. Daplasc465e052005-11-07 01:00:35 -08001077 info->fbops->fb_cursor = NULL;
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001078
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 info->var.accel_flags = (!noaccel);
1080
1081 switch (par->Architecture) {
1082 case NV_ARCH_04:
1083 info->fix.accel = FB_ACCEL_NV4;
1084 break;
1085 case NV_ARCH_10:
1086 info->fix.accel = FB_ACCEL_NV_10;
1087 break;
1088 case NV_ARCH_20:
1089 info->fix.accel = FB_ACCEL_NV_20;
1090 break;
1091 case NV_ARCH_30:
1092 info->fix.accel = FB_ACCEL_NV_30;
1093 break;
1094 case NV_ARCH_40:
1095 info->fix.accel = FB_ACCEL_NV_40;
1096 break;
1097 }
1098
1099 NVTRACE_LEAVE();
1100
1101 return nvidiafb_check_var(&info->var, info);
1102}
1103
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001104static u32 __devinit nvidia_get_chipset(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105{
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001106 struct nvidia_par *par = info->par;
1107 u32 id = (par->pci_dev->vendor << 16) | par->pci_dev->device;
1108
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001109 printk(KERN_INFO PFX "Device ID: %x \n", id);
1110
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001111 if ((id & 0xfff0) == 0x00f0) {
1112 /* pci-e */
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001113 id = NV_RD32(par->REGS, 0x1800);
1114
1115 if ((id & 0x0000ffff) == 0x000010DE)
1116 id = 0x10DE0000 | (id >> 16);
1117 else if ((id & 0xffff0000) == 0xDE100000) /* wrong endian */
1118 id = 0x10DE0000 | ((id << 8) & 0x0000ff00) |
1119 ((id >> 8) & 0x000000ff);
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001120 printk(KERN_INFO PFX "Subsystem ID: %x \n", id);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001121 }
1122
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001123 return id;
1124}
1125
1126static u32 __devinit nvidia_get_arch(struct fb_info *info)
1127{
1128 struct nvidia_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 u32 arch = 0;
1130
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001131 switch (par->Chipset & 0x0ff0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 case 0x0100: /* GeForce 256 */
1133 case 0x0110: /* GeForce2 MX */
1134 case 0x0150: /* GeForce2 */
1135 case 0x0170: /* GeForce4 MX */
1136 case 0x0180: /* GeForce4 MX (8x AGP) */
1137 case 0x01A0: /* nForce */
1138 case 0x01F0: /* nForce2 */
1139 arch = NV_ARCH_10;
1140 break;
1141 case 0x0200: /* GeForce3 */
1142 case 0x0250: /* GeForce4 Ti */
1143 case 0x0280: /* GeForce4 Ti (8x AGP) */
1144 arch = NV_ARCH_20;
1145 break;
1146 case 0x0300: /* GeForceFX 5800 */
1147 case 0x0310: /* GeForceFX 5600 */
1148 case 0x0320: /* GeForceFX 5200 */
1149 case 0x0330: /* GeForceFX 5900 */
1150 case 0x0340: /* GeForceFX 5700 */
1151 arch = NV_ARCH_30;
1152 break;
1153 case 0x0040:
1154 case 0x00C0:
1155 case 0x0120:
1156 case 0x0130:
1157 case 0x0140:
1158 case 0x0160:
1159 case 0x01D0:
1160 case 0x0090:
1161 case 0x0210:
1162 case 0x0220:
1163 case 0x0230:
Antonino A. Daplasfe610672006-06-26 00:26:38 -07001164 case 0x0240:
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001165 case 0x0290:
1166 case 0x0390:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 arch = NV_ARCH_40;
1168 break;
1169 case 0x0020: /* TNT, TNT2 */
1170 arch = NV_ARCH_04;
1171 break;
1172 default: /* unknown architecture */
1173 break;
1174 }
1175
1176 return arch;
1177}
1178
1179static int __devinit nvidiafb_probe(struct pci_dev *pd,
1180 const struct pci_device_id *ent)
1181{
1182 struct nvidia_par *par;
1183 struct fb_info *info;
1184 unsigned short cmd;
1185
1186
1187 NVTRACE_ENTER();
1188 assert(pd != NULL);
1189
1190 info = framebuffer_alloc(sizeof(struct nvidia_par), &pd->dev);
1191
1192 if (!info)
1193 goto err_out;
1194
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001195 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 par->pci_dev = pd;
1197
1198 info->pixmap.addr = kmalloc(8 * 1024, GFP_KERNEL);
1199
1200 if (info->pixmap.addr == NULL)
1201 goto err_out_kfree;
1202
1203 memset(info->pixmap.addr, 0, 8 * 1024);
1204
1205 if (pci_enable_device(pd)) {
1206 printk(KERN_ERR PFX "cannot enable PCI device\n");
1207 goto err_out_enable;
1208 }
1209
1210 if (pci_request_regions(pd, "nvidiafb")) {
1211 printk(KERN_ERR PFX "cannot request PCI regions\n");
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001212 goto err_out_enable;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 }
1214
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 par->FlatPanel = flatpanel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 if (flatpanel == 1)
1217 printk(KERN_INFO PFX "flatpanel support enabled\n");
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001218 par->FPDither = fpdither;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219
1220 par->CRTCnumber = forceCRTC;
1221 par->FpScale = (!noscale);
1222 par->paneltweak = paneltweak;
1223
1224 /* enable IO and mem if not already done */
1225 pci_read_config_word(pd, PCI_COMMAND, &cmd);
1226 cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
1227 pci_write_config_word(pd, PCI_COMMAND, cmd);
1228
1229 nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
1230 nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
1231 nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
1232
1233 par->REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
1234
1235 if (!par->REGS) {
1236 printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
1237 goto err_out_free_base0;
1238 }
1239
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001240 par->Chipset = nvidia_get_chipset(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001241 par->Architecture = nvidia_get_arch(info);
1242
1243 if (par->Architecture == 0) {
1244 printk(KERN_ERR PFX "unknown NV_ARCH\n");
1245 goto err_out_arch;
1246 }
1247
1248 sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
1249
Antonino A. Daplas918799a2006-01-09 20:53:40 -08001250 if (NVCommonSetup(info))
1251 goto err_out_arch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252
1253 par->FbAddress = nvidiafb_fix.smem_start;
1254 par->FbMapSize = par->RamAmountKBytes * 1024;
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001255 if (vram && vram * 1024 * 1024 < par->FbMapSize)
1256 par->FbMapSize = vram * 1024 * 1024;
1257
1258 /* Limit amount of vram to 64 MB */
1259 if (par->FbMapSize > 64 * 1024 * 1024)
1260 par->FbMapSize = 64 * 1024 * 1024;
1261
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001262 if(par->Architecture >= NV_ARCH_40)
1263 par->FbUsableSize = par->FbMapSize - (560 * 1024);
1264 else
1265 par->FbUsableSize = par->FbMapSize - (128 * 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 par->ScratchBufferSize = (par->Architecture < NV_ARCH_10) ? 8 * 1024 :
1267 16 * 1024;
1268 par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001269 par->CursorStart = par->FbUsableSize + (32 * 1024);
1270
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001272 info->screen_size = par->FbUsableSize;
1273 nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
1275 if (!info->screen_base) {
1276 printk(KERN_ERR PFX "cannot ioremap FB base\n");
1277 goto err_out_free_base1;
1278 }
1279
1280 par->FbStart = info->screen_base;
1281
1282#ifdef CONFIG_MTRR
1283 if (!nomtrr) {
1284 par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001285 par->RamAmountKBytes * 1024,
1286 MTRR_TYPE_WRCOMB, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 if (par->mtrr.vram < 0) {
1288 printk(KERN_ERR PFX "unable to setup MTRR\n");
1289 } else {
1290 par->mtrr.vram_valid = 1;
1291 /* let there be speed */
1292 printk(KERN_INFO PFX "MTRR set to ON\n");
1293 }
1294 }
1295#endif /* CONFIG_MTRR */
1296
1297 info->fbops = &nvidia_fb_ops;
1298 info->fix = nvidiafb_fix;
1299
1300 if (nvidia_set_fbinfo(info) < 0) {
1301 printk(KERN_ERR PFX "error setting initial video mode\n");
1302 goto err_out_iounmap_fb;
1303 }
1304
1305 nvidia_save_vga(par, &par->SavedReg);
1306
Guido Guentherce38cac2006-07-30 03:04:21 -07001307 pci_set_drvdata(pd, info);
1308 nvidia_bl_init(par);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 if (register_framebuffer(info) < 0) {
1310 printk(KERN_ERR PFX "error registering nVidia framebuffer\n");
1311 goto err_out_iounmap_fb;
1312 }
1313
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314
1315 printk(KERN_INFO PFX
1316 "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
1317 info->fix.id,
1318 par->FbMapSize / (1024 * 1024), info->fix.smem_start);
Michael Hanselmann5474c122006-06-25 05:47:08 -07001319
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 NVTRACE_LEAVE();
1321 return 0;
1322
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001323err_out_iounmap_fb:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 iounmap(info->screen_base);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001325err_out_free_base1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 fb_destroy_modedb(info->monspecs.modedb);
1327 nvidia_delete_i2c_busses(par);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001328err_out_arch:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 iounmap(par->REGS);
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001330 err_out_free_base0:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 pci_release_regions(pd);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001332err_out_enable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 kfree(info->pixmap.addr);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001334err_out_kfree:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 framebuffer_release(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001336err_out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 return -ENODEV;
1338}
1339
1340static void __exit nvidiafb_remove(struct pci_dev *pd)
1341{
1342 struct fb_info *info = pci_get_drvdata(pd);
1343 struct nvidia_par *par = info->par;
1344
1345 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
Michael Hanselmann5474c122006-06-25 05:47:08 -07001347 nvidia_bl_exit(par);
1348
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 unregister_framebuffer(info);
1350#ifdef CONFIG_MTRR
1351 if (par->mtrr.vram_valid)
1352 mtrr_del(par->mtrr.vram, info->fix.smem_start,
1353 info->fix.smem_len);
1354#endif /* CONFIG_MTRR */
1355
1356 iounmap(info->screen_base);
1357 fb_destroy_modedb(info->monspecs.modedb);
1358 nvidia_delete_i2c_busses(par);
1359 iounmap(par->REGS);
1360 pci_release_regions(pd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 kfree(info->pixmap.addr);
1362 framebuffer_release(info);
1363 pci_set_drvdata(pd, NULL);
1364 NVTRACE_LEAVE();
1365}
1366
1367/* ------------------------------------------------------------------------- *
1368 *
1369 * initialization
1370 *
1371 * ------------------------------------------------------------------------- */
1372
1373#ifndef MODULE
1374static int __devinit nvidiafb_setup(char *options)
1375{
1376 char *this_opt;
1377
1378 NVTRACE_ENTER();
1379 if (!options || !*options)
1380 return 0;
1381
1382 while ((this_opt = strsep(&options, ",")) != NULL) {
1383 if (!strncmp(this_opt, "forceCRTC", 9)) {
1384 char *p;
1385
1386 p = this_opt + 9;
1387 if (!*p || !*(++p))
1388 continue;
1389 forceCRTC = *p - '0';
1390 if (forceCRTC < 0 || forceCRTC > 1)
1391 forceCRTC = -1;
1392 } else if (!strncmp(this_opt, "flatpanel", 9)) {
1393 flatpanel = 1;
1394 } else if (!strncmp(this_opt, "hwcur", 5)) {
1395 hwcur = 1;
1396 } else if (!strncmp(this_opt, "noaccel", 6)) {
1397 noaccel = 1;
1398 } else if (!strncmp(this_opt, "noscale", 7)) {
1399 noscale = 1;
1400 } else if (!strncmp(this_opt, "paneltweak:", 11)) {
1401 paneltweak = simple_strtoul(this_opt+11, NULL, 0);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001402 } else if (!strncmp(this_opt, "vram:", 5)) {
1403 vram = simple_strtoul(this_opt+5, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404#ifdef CONFIG_MTRR
1405 } else if (!strncmp(this_opt, "nomtrr", 6)) {
1406 nomtrr = 1;
1407#endif
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001408 } else if (!strncmp(this_opt, "fpdither:", 9)) {
1409 fpdither = simple_strtol(this_opt+9, NULL, 0);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001410 } else if (!strncmp(this_opt, "bpp:", 4)) {
1411 bpp = simple_strtoul(this_opt+4, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 } else
1413 mode_option = this_opt;
1414 }
1415 NVTRACE_LEAVE();
1416 return 0;
1417}
1418#endif /* !MODULE */
1419
1420static struct pci_driver nvidiafb_driver = {
1421 .name = "nvidiafb",
1422 .id_table = nvidiafb_pci_tbl,
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001423 .probe = nvidiafb_probe,
1424 .suspend = nvidiafb_suspend,
1425 .resume = nvidiafb_resume,
1426 .remove = __exit_p(nvidiafb_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427};
1428
1429/* ------------------------------------------------------------------------- *
1430 *
1431 * modularization
1432 *
1433 * ------------------------------------------------------------------------- */
1434
1435static int __devinit nvidiafb_init(void)
1436{
1437#ifndef MODULE
1438 char *option = NULL;
1439
1440 if (fb_get_options("nvidiafb", &option))
1441 return -ENODEV;
1442 nvidiafb_setup(option);
1443#endif
1444 return pci_register_driver(&nvidiafb_driver);
1445}
1446
1447module_init(nvidiafb_init);
1448
1449#ifdef MODULE
1450static void __exit nvidiafb_exit(void)
1451{
1452 pci_unregister_driver(&nvidiafb_driver);
1453}
1454
1455module_exit(nvidiafb_exit);
1456
1457module_param(flatpanel, int, 0);
1458MODULE_PARM_DESC(flatpanel,
1459 "Enables experimental flat panel support for some chipsets. "
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001460 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
1461module_param(fpdither, int, 0);
1462MODULE_PARM_DESC(fpdither,
1463 "Enables dithering of flat panel for 6 bits panels. "
1464 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465module_param(hwcur, int, 0);
1466MODULE_PARM_DESC(hwcur,
1467 "Enables hardware cursor implementation. (0 or 1=enabled) "
1468 "(default=0)");
1469module_param(noaccel, int, 0);
1470MODULE_PARM_DESC(noaccel,
1471 "Disables hardware acceleration. (0 or 1=disable) "
1472 "(default=0)");
1473module_param(noscale, int, 0);
1474MODULE_PARM_DESC(noscale,
1475 "Disables screen scaleing. (0 or 1=disable) "
1476 "(default=0, do scaling)");
1477module_param(paneltweak, int, 0);
1478MODULE_PARM_DESC(paneltweak,
1479 "Tweak display settings for flatpanels. "
1480 "(default=0, no tweaks)");
1481module_param(forceCRTC, int, 0);
1482MODULE_PARM_DESC(forceCRTC,
1483 "Forces usage of a particular CRTC in case autodetection "
1484 "fails. (0 or 1) (default=autodetect)");
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001485module_param(vram, int, 0);
1486MODULE_PARM_DESC(vram,
1487 "amount of framebuffer memory to remap in MiB"
1488 "(default=0 - remap entire memory)");
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001489module_param(mode_option, charp, 0);
1490MODULE_PARM_DESC(mode_option, "Specify initial video mode");
Antonino A. Daplasade91852006-01-09 20:53:39 -08001491module_param(bpp, int, 0);
1492MODULE_PARM_DESC(bpp, "pixel width in bits"
1493 "(default=8)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494#ifdef CONFIG_MTRR
1495module_param(nomtrr, bool, 0);
1496MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
1497 "(default=0)");
1498#endif
1499
1500MODULE_AUTHOR("Antonino Daplas");
1501MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
1502MODULE_LICENSE("GPL");
1503#endif /* MODULE */
1504