blob: b97ec6901263ab02bef1fd4e77bb8c390b91f2f5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/video/nvidia/nvidia.c - nVidia fb driver
3 *
4 * Copyright 2004 Antonino Daplas <adaplas@pol.net>
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 *
10 */
11
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/errno.h>
15#include <linux/string.h>
16#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/slab.h>
18#include <linux/delay.h>
19#include <linux/fb.h>
20#include <linux/init.h>
21#include <linux/pci.h>
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -080022#include <linux/console.h>
Michael Hanselmann5474c122006-06-25 05:47:08 -070023#include <linux/backlight.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#ifdef CONFIG_MTRR
25#include <asm/mtrr.h>
26#endif
27#ifdef CONFIG_PPC_OF
28#include <asm/prom.h>
29#include <asm/pci-bridge.h>
30#endif
Paul Mackerras70abac62006-10-03 01:15:14 -070031#ifdef CONFIG_BOOTX_TEXT
32#include <asm/btext.h>
33#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
35#include "nv_local.h"
36#include "nv_type.h"
37#include "nv_proto.h"
38#include "nv_dma.h"
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#undef CONFIG_FB_NVIDIA_DEBUG
41#ifdef CONFIG_FB_NVIDIA_DEBUG
42#define NVTRACE printk
43#else
44#define NVTRACE if (0) printk
45#endif
46
47#define NVTRACE_ENTER(...) NVTRACE("%s START\n", __FUNCTION__)
48#define NVTRACE_LEAVE(...) NVTRACE("%s END\n", __FUNCTION__)
49
50#ifdef CONFIG_FB_NVIDIA_DEBUG
51#define assert(expr) \
52 if (!(expr)) { \
53 printk( "Assertion failed! %s,%s,%s,line=%d\n",\
54 #expr,__FILE__,__FUNCTION__,__LINE__); \
55 BUG(); \
56 }
57#else
58#define assert(expr)
59#endif
60
61#define PFX "nvidiafb: "
62
63/* HW cursor parameters */
64#define MAX_CURS 32
65
66static struct pci_device_id nvidiafb_pci_tbl[] = {
Antonino A. Daplas8eec4982006-06-26 00:26:30 -070067 {PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
68 PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0},
69 { 0, }
Linus Torvalds1da177e2005-04-16 15:20:36 -070070};
Linus Torvalds1da177e2005-04-16 15:20:36 -070071MODULE_DEVICE_TABLE(pci, nvidiafb_pci_tbl);
72
73/* command line data, set in nvidiafb_setup() */
74static int flatpanel __devinitdata = -1; /* Autodetect later */
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -080075static int fpdither __devinitdata = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076static int forceCRTC __devinitdata = -1;
77static int hwcur __devinitdata = 0;
78static int noaccel __devinitdata = 0;
79static int noscale __devinitdata = 0;
80static int paneltweak __devinitdata = 0;
Antonino A. Daplas917bb072005-05-01 08:59:22 -070081static int vram __devinitdata = 0;
Antonino A. Daplasade91852006-01-09 20:53:39 -080082static int bpp __devinitdata = 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#ifdef CONFIG_MTRR
84static int nomtrr __devinitdata = 0;
85#endif
Richard Purdie202d4e62007-03-03 17:43:52 +000086#ifdef CONFIG_PMAC_BACKLIGHT
87static int backlight __devinitdata = 1;
88#else
89static int backlight __devinitdata = 0;
90#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
92static char *mode_option __devinitdata = NULL;
93
94static struct fb_fix_screeninfo __devinitdata nvidiafb_fix = {
95 .type = FB_TYPE_PACKED_PIXELS,
96 .xpanstep = 8,
97 .ypanstep = 1,
98};
99
100static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = {
101 .xres = 640,
102 .yres = 480,
103 .xres_virtual = 640,
104 .yres_virtual = 480,
105 .bits_per_pixel = 8,
106 .red = {0, 8, 0},
107 .green = {0, 8, 0},
108 .blue = {0, 8, 0},
109 .transp = {0, 0, 0},
110 .activate = FB_ACTIVATE_NOW,
111 .height = -1,
112 .width = -1,
113 .pixclock = 39721,
114 .left_margin = 40,
115 .right_margin = 24,
116 .upper_margin = 32,
117 .lower_margin = 11,
118 .hsync_len = 96,
119 .vsync_len = 2,
120 .vmode = FB_VMODE_NONINTERLACED
121};
122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
124 u16 bg, u16 fg, u32 w, u32 h)
125{
James Simmonsf1ab5da2005-06-21 17:17:07 -0700126 u32 *data = (u32 *) data8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 int i, j, k = 0;
128 u32 b, tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
130 w = (w + 1) & ~1;
131
132 for (i = 0; i < h; i++) {
133 b = *data++;
134 reverse_order(&b);
135
136 for (j = 0; j < w / 2; j++) {
137 tmp = 0;
138#if defined (__BIG_ENDIAN)
139 tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
140 b <<= 1;
141 tmp |= (b & (1 << 31)) ? fg : bg;
142 b <<= 1;
143#else
144 tmp = (b & 1) ? fg : bg;
145 b >>= 1;
146 tmp |= (b & 1) ? fg << 16 : bg << 16;
147 b >>= 1;
148#endif
149 NV_WR32(&par->CURSOR[k++], 0, tmp);
150 }
151 k += (MAX_CURS - w) / 2;
152 }
153}
154
155static void nvidia_write_clut(struct nvidia_par *par,
156 u8 regnum, u8 red, u8 green, u8 blue)
157{
158 NVWriteDacMask(par, 0xff);
159 NVWriteDacWriteAddr(par, regnum);
160 NVWriteDacData(par, red);
161 NVWriteDacData(par, green);
162 NVWriteDacData(par, blue);
163}
164
165static void nvidia_read_clut(struct nvidia_par *par,
166 u8 regnum, u8 * red, u8 * green, u8 * blue)
167{
168 NVWriteDacMask(par, 0xff);
169 NVWriteDacReadAddr(par, regnum);
170 *red = NVReadDacData(par);
171 *green = NVReadDacData(par);
172 *blue = NVReadDacData(par);
173}
174
175static int nvidia_panel_tweak(struct nvidia_par *par,
176 struct _riva_hw_state *state)
177{
178 int tweak = 0;
179
180 if (par->paneltweak) {
181 tweak = par->paneltweak;
182 } else {
183 /* begin flat panel hacks */
184 /* This is unfortunate, but some chips need this register
185 tweaked or else you get artifacts where adjacent pixels are
186 swapped. There are no hard rules for what to set here so all
187 we can do is experiment and apply hacks. */
188
189 if(((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) {
190 /* At least one NV34 laptop needs this workaround. */
191 tweak = -1;
192 }
193
194 if((par->Chipset & 0xfff0) == 0x0310) {
195 tweak = 1;
196 }
197 /* end flat panel hacks */
198 }
199
200 return tweak;
201}
202
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800203static void nvidia_vga_protect(struct nvidia_par *par, int on)
204{
205 unsigned char tmp;
206
207 if (on) {
208 /*
209 * Turn off screen and disable sequencer.
210 */
211 tmp = NVReadSeq(par, 0x01);
212
213 NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */
214 NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */
215 } else {
216 /*
217 * Reenable sequencer, then turn on screen.
218 */
219
220 tmp = NVReadSeq(par, 0x01);
221
222 NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */
223 NVWriteSeq(par, 0x00, 0x03); /* End Reset */
224 }
225}
226
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227static void nvidia_save_vga(struct nvidia_par *par,
228 struct _riva_hw_state *state)
229{
230 int i;
231
232 NVTRACE_ENTER();
233 NVLockUnlock(par, 0);
234
235 NVUnloadStateExt(par, state);
236
237 state->misc_output = NVReadMiscOut(par);
238
239 for (i = 0; i < NUM_CRT_REGS; i++)
240 state->crtc[i] = NVReadCrtc(par, i);
241
242 for (i = 0; i < NUM_ATC_REGS; i++)
243 state->attr[i] = NVReadAttr(par, i);
244
245 for (i = 0; i < NUM_GRC_REGS; i++)
246 state->gra[i] = NVReadGr(par, i);
247
248 for (i = 0; i < NUM_SEQ_REGS; i++)
249 state->seq[i] = NVReadSeq(par, i);
250 NVTRACE_LEAVE();
251}
252
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800253#undef DUMP_REG
254
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800255static void nvidia_write_regs(struct nvidia_par *par,
256 struct _riva_hw_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 int i;
259
260 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
262 NVLoadStateExt(par, state);
263
264 NVWriteMiscOut(par, state->misc_output);
265
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800266 for (i = 1; i < NUM_SEQ_REGS; i++) {
267#ifdef DUMP_REG
268 printk(" SEQ[%02x] = %08x\n", i, state->seq[i]);
269#endif
270 NVWriteSeq(par, i, state->seq[i]);
271 }
272
273 /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 of CRTC[17] */
274 NVWriteCrtc(par, 0x11, state->crtc[0x11] & ~0x80);
275
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 for (i = 0; i < NUM_CRT_REGS; i++) {
277 switch (i) {
278 case 0x19:
279 case 0x20 ... 0x40:
280 break;
281 default:
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800282#ifdef DUMP_REG
283 printk("CRTC[%02x] = %08x\n", i, state->crtc[i]);
284#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 NVWriteCrtc(par, i, state->crtc[i]);
286 }
287 }
288
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800289 for (i = 0; i < NUM_GRC_REGS; i++) {
290#ifdef DUMP_REG
291 printk(" GRA[%02x] = %08x\n", i, state->gra[i]);
292#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 NVWriteGr(par, i, state->gra[i]);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800294 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800296 for (i = 0; i < NUM_ATC_REGS; i++) {
297#ifdef DUMP_REG
298 printk("ATTR[%02x] = %08x\n", i, state->attr[i]);
299#endif
300 NVWriteAttr(par, i, state->attr[i]);
301 }
302
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 NVTRACE_LEAVE();
304}
305
306static int nvidia_calc_regs(struct fb_info *info)
307{
308 struct nvidia_par *par = info->par;
309 struct _riva_hw_state *state = &par->ModeReg;
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700310 int i, depth = fb_get_color_depth(&info->var, &info->fix);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 int h_display = info->var.xres / 8 - 1;
312 int h_start = (info->var.xres + info->var.right_margin) / 8 - 1;
313 int h_end = (info->var.xres + info->var.right_margin +
314 info->var.hsync_len) / 8 - 1;
315 int h_total = (info->var.xres + info->var.right_margin +
316 info->var.hsync_len + info->var.left_margin) / 8 - 5;
317 int h_blank_s = h_display;
318 int h_blank_e = h_total + 4;
319 int v_display = info->var.yres - 1;
320 int v_start = info->var.yres + info->var.lower_margin - 1;
321 int v_end = (info->var.yres + info->var.lower_margin +
322 info->var.vsync_len) - 1;
323 int v_total = (info->var.yres + info->var.lower_margin +
324 info->var.vsync_len + info->var.upper_margin) - 2;
325 int v_blank_s = v_display;
326 int v_blank_e = v_total + 1;
327
328 /*
329 * Set all CRTC values.
330 */
331
332 if (info->var.vmode & FB_VMODE_INTERLACED)
333 v_total |= 1;
334
335 if (par->FlatPanel == 1) {
336 v_start = v_total - 3;
337 v_end = v_total - 2;
338 v_blank_s = v_start;
339 h_start = h_total - 5;
340 h_end = h_total - 2;
341 h_blank_e = h_total + 4;
342 }
343
344 state->crtc[0x0] = Set8Bits(h_total);
345 state->crtc[0x1] = Set8Bits(h_display);
346 state->crtc[0x2] = Set8Bits(h_blank_s);
347 state->crtc[0x3] = SetBitField(h_blank_e, 4: 0, 4:0)
348 | SetBit(7);
349 state->crtc[0x4] = Set8Bits(h_start);
350 state->crtc[0x5] = SetBitField(h_blank_e, 5: 5, 7:7)
351 | SetBitField(h_end, 4: 0, 4:0);
352 state->crtc[0x6] = SetBitField(v_total, 7: 0, 7:0);
353 state->crtc[0x7] = SetBitField(v_total, 8: 8, 0:0)
354 | SetBitField(v_display, 8: 8, 1:1)
355 | SetBitField(v_start, 8: 8, 2:2)
356 | SetBitField(v_blank_s, 8: 8, 3:3)
357 | SetBit(4)
358 | SetBitField(v_total, 9: 9, 5:5)
359 | SetBitField(v_display, 9: 9, 6:6)
360 | SetBitField(v_start, 9: 9, 7:7);
361 state->crtc[0x9] = SetBitField(v_blank_s, 9: 9, 5:5)
362 | SetBit(6)
363 | ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0x00);
364 state->crtc[0x10] = Set8Bits(v_start);
365 state->crtc[0x11] = SetBitField(v_end, 3: 0, 3:0) | SetBit(5);
366 state->crtc[0x12] = Set8Bits(v_display);
367 state->crtc[0x13] = ((info->var.xres_virtual / 8) *
368 (info->var.bits_per_pixel / 8));
369 state->crtc[0x15] = Set8Bits(v_blank_s);
370 state->crtc[0x16] = Set8Bits(v_blank_e);
371
372 state->attr[0x10] = 0x01;
373
374 if (par->Television)
375 state->attr[0x11] = 0x00;
376
377 state->screen = SetBitField(h_blank_e, 6: 6, 4:4)
378 | SetBitField(v_blank_s, 10: 10, 3:3)
379 | SetBitField(v_start, 10: 10, 2:2)
380 | SetBitField(v_display, 10: 10, 1:1)
381 | SetBitField(v_total, 10: 10, 0:0);
382
383 state->horiz = SetBitField(h_total, 8: 8, 0:0)
384 | SetBitField(h_display, 8: 8, 1:1)
385 | SetBitField(h_blank_s, 8: 8, 2:2)
386 | SetBitField(h_start, 8: 8, 3:3);
387
388 state->extra = SetBitField(v_total, 11: 11, 0:0)
389 | SetBitField(v_display, 11: 11, 2:2)
390 | SetBitField(v_start, 11: 11, 4:4)
391 | SetBitField(v_blank_s, 11: 11, 6:6);
392
393 if (info->var.vmode & FB_VMODE_INTERLACED) {
394 h_total = (h_total >> 1) & ~1;
395 state->interlace = Set8Bits(h_total);
396 state->horiz |= SetBitField(h_total, 8: 8, 4:4);
397 } else {
398 state->interlace = 0xff; /* interlace off */
399 }
400
401 /*
402 * Calculate the extended registers.
403 */
404
405 if (depth < 24)
406 i = depth;
407 else
408 i = 32;
409
410 if (par->Architecture >= NV_ARCH_10)
411 par->CURSOR = (volatile u32 __iomem *)(info->screen_base +
412 par->CursorStart);
413
414 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
415 state->misc_output &= ~0x40;
416 else
417 state->misc_output |= 0x40;
418 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
419 state->misc_output &= ~0x80;
420 else
421 state->misc_output |= 0x80;
422
423 NVCalcStateExt(par, state, i, info->var.xres_virtual,
424 info->var.xres, info->var.yres_virtual,
425 1000000000 / info->var.pixclock, info->var.vmode);
426
427 state->scale = NV_RD32(par->PRAMDAC, 0x00000848) & 0xfff000ff;
428 if (par->FlatPanel == 1) {
429 state->pixel |= (1 << 7);
430
431 if (!par->fpScaler || (par->fpWidth <= info->var.xres)
432 || (par->fpHeight <= info->var.yres)) {
433 state->scale |= (1 << 8);
434 }
435
436 if (!par->crtcSync_read) {
437 state->crtcSync = NV_RD32(par->PRAMDAC, 0x0828);
438 par->crtcSync_read = 1;
439 }
440
441 par->PanelTweak = nvidia_panel_tweak(par, state);
442 }
443
444 state->vpll = state->pll;
445 state->vpll2 = state->pll;
446 state->vpllB = state->pllB;
447 state->vpll2B = state->pllB;
448
449 VGA_WR08(par->PCIO, 0x03D4, 0x1C);
450 state->fifo = VGA_RD08(par->PCIO, 0x03D5) & ~(1<<5);
451
452 if (par->CRTCnumber) {
453 state->head = NV_RD32(par->PCRTC0, 0x00000860) & ~0x00001000;
454 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) | 0x00001000;
455 state->crtcOwner = 3;
456 state->pllsel |= 0x20000800;
457 state->vpll = NV_RD32(par->PRAMDAC0, 0x00000508);
458 if (par->twoStagePLL)
459 state->vpllB = NV_RD32(par->PRAMDAC0, 0x00000578);
460 } else if (par->twoHeads) {
461 state->head = NV_RD32(par->PCRTC0, 0x00000860) | 0x00001000;
462 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) & ~0x00001000;
463 state->crtcOwner = 0;
464 state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
465 if (par->twoStagePLL)
466 state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
467 }
468
469 state->cursorConfig = 0x00000100;
470
471 if (info->var.vmode & FB_VMODE_DOUBLE)
472 state->cursorConfig |= (1 << 4);
473
474 if (par->alphaCursor) {
475 if ((par->Chipset & 0x0ff0) != 0x0110)
476 state->cursorConfig |= 0x04011000;
477 else
478 state->cursorConfig |= 0x14011000;
479 state->general |= (1 << 29);
480 } else
481 state->cursorConfig |= 0x02000000;
482
483 if (par->twoHeads) {
484 if ((par->Chipset & 0x0ff0) == 0x0110) {
485 state->dither = NV_RD32(par->PRAMDAC, 0x0528) &
486 ~0x00010000;
487 if (par->FPDither)
488 state->dither |= 0x00010000;
489 } else {
490 state->dither = NV_RD32(par->PRAMDAC, 0x083C) & ~1;
491 if (par->FPDither)
492 state->dither |= 1;
493 }
494 }
495
496 state->timingH = 0;
497 state->timingV = 0;
498 state->displayV = info->var.xres;
499
500 return 0;
501}
502
503static void nvidia_init_vga(struct fb_info *info)
504{
505 struct nvidia_par *par = info->par;
506 struct _riva_hw_state *state = &par->ModeReg;
507 int i;
508
509 for (i = 0; i < 0x10; i++)
510 state->attr[i] = i;
511 state->attr[0x10] = 0x41;
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800512 state->attr[0x11] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 state->attr[0x12] = 0x0f;
514 state->attr[0x13] = 0x00;
515 state->attr[0x14] = 0x00;
516
517 memset(state->crtc, 0x00, NUM_CRT_REGS);
518 state->crtc[0x0a] = 0x20;
519 state->crtc[0x17] = 0xe3;
520 state->crtc[0x18] = 0xff;
521 state->crtc[0x28] = 0x40;
522
523 memset(state->gra, 0x00, NUM_GRC_REGS);
524 state->gra[0x05] = 0x40;
525 state->gra[0x06] = 0x05;
526 state->gra[0x07] = 0x0f;
527 state->gra[0x08] = 0xff;
528
529 state->seq[0x00] = 0x03;
530 state->seq[0x01] = 0x01;
531 state->seq[0x02] = 0x0f;
532 state->seq[0x03] = 0x00;
533 state->seq[0x04] = 0x0e;
534
535 state->misc_output = 0xeb;
536}
537
538static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
539{
540 struct nvidia_par *par = info->par;
541 u8 data[MAX_CURS * MAX_CURS / 8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 int i, set = cursor->set;
James Simmonsf1ab5da2005-06-21 17:17:07 -0700543 u16 fg, bg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544
Antonino A. Daplas7a482422005-09-21 07:30:21 +0800545 if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
James Simmonsf1ab5da2005-06-21 17:17:07 -0700546 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
548 NVShowHideCursor(par, 0);
549
550 if (par->cursor_reset) {
551 set = FB_CUR_SETALL;
552 par->cursor_reset = 0;
553 }
554
555 if (set & FB_CUR_SETSIZE)
556 memset_io(par->CURSOR, 0, MAX_CURS * MAX_CURS * 2);
557
558 if (set & FB_CUR_SETPOS) {
559 u32 xx, yy, temp;
560
561 yy = cursor->image.dy - info->var.yoffset;
562 xx = cursor->image.dx - info->var.xoffset;
563 temp = xx & 0xFFFF;
564 temp |= yy << 16;
565
566 NV_WR32(par->PRAMDAC, 0x0000300, temp);
567 }
568
569 if (set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
570 u32 bg_idx = cursor->image.bg_color;
571 u32 fg_idx = cursor->image.fg_color;
572 u32 s_pitch = (cursor->image.width + 7) >> 3;
573 u32 d_pitch = MAX_CURS / 8;
574 u8 *dat = (u8 *) cursor->image.data;
575 u8 *msk = (u8 *) cursor->mask;
576 u8 *src;
577
578 src = kmalloc(s_pitch * cursor->image.height, GFP_ATOMIC);
579
580 if (src) {
581 switch (cursor->rop) {
582 case ROP_XOR:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700583 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 src[i] = dat[i] ^ msk[i];
585 break;
586 case ROP_COPY:
587 default:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700588 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 src[i] = dat[i] & msk[i];
590 break;
591 }
592
James Simmonsf1ab5da2005-06-21 17:17:07 -0700593 fb_pad_aligned_buffer(data, d_pitch, src, s_pitch,
594 cursor->image.height);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
596 bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
597 ((info->cmap.green[bg_idx] & 0xf8) << 2) |
598 ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | 1 << 15;
599
600 fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
601 ((info->cmap.green[fg_idx] & 0xf8) << 2) |
602 ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
603
604 NVLockUnlock(par, 0);
605
606 nvidiafb_load_cursor_image(par, data, bg, fg,
607 cursor->image.width,
608 cursor->image.height);
609 kfree(src);
610 }
611 }
612
613 if (cursor->enable)
614 NVShowHideCursor(par, 1);
615
616 return 0;
617}
618
619static int nvidiafb_set_par(struct fb_info *info)
620{
621 struct nvidia_par *par = info->par;
622
623 NVTRACE_ENTER();
624
625 NVLockUnlock(par, 1);
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800626 if (!par->FlatPanel || !par->twoHeads)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 par->FPDither = 0;
628
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800629 if (par->FPDither < 0) {
630 if ((par->Chipset & 0x0ff0) == 0x0110)
631 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x0528)
632 & 0x00010000);
633 else
634 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1);
635 printk(KERN_INFO PFX "Flat panel dithering %s\n",
636 par->FPDither ? "enabled" : "disabled");
637 }
638
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700639 info->fix.visual = (info->var.bits_per_pixel == 8) ?
640 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 nvidia_init_vga(info);
643 nvidia_calc_regs(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645 NVLockUnlock(par, 0);
646 if (par->twoHeads) {
647 VGA_WR08(par->PCIO, 0x03D4, 0x44);
648 VGA_WR08(par->PCIO, 0x03D5, par->ModeReg.crtcOwner);
649 NVLockUnlock(par, 0);
650 }
651
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800652 nvidia_vga_protect(par, 1);
653
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800654 nvidia_write_regs(par, &par->ModeReg);
655 NVSetStartAddress(par, 0);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800656
657#if defined (__BIG_ENDIAN)
658 /* turn on LFB swapping */
659 {
660 unsigned char tmp;
661
662 VGA_WR08(par->PCIO, 0x3d4, 0x46);
663 tmp = VGA_RD08(par->PCIO, 0x3d5);
664 tmp |= (1 << 7);
665 VGA_WR08(par->PCIO, 0x3d5, tmp);
666 }
667#endif
668
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 info->fix.line_length = (info->var.xres_virtual *
670 info->var.bits_per_pixel) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 if (info->var.accel_flags) {
672 info->fbops->fb_imageblit = nvidiafb_imageblit;
673 info->fbops->fb_fillrect = nvidiafb_fillrect;
674 info->fbops->fb_copyarea = nvidiafb_copyarea;
675 info->fbops->fb_sync = nvidiafb_sync;
676 info->pixmap.scan_align = 4;
677 info->flags &= ~FBINFO_HWACCEL_DISABLED;
678 NVResetGraphics(info);
679 } else {
680 info->fbops->fb_imageblit = cfb_imageblit;
681 info->fbops->fb_fillrect = cfb_fillrect;
682 info->fbops->fb_copyarea = cfb_copyarea;
683 info->fbops->fb_sync = NULL;
684 info->pixmap.scan_align = 1;
685 info->flags |= FBINFO_HWACCEL_DISABLED;
686 }
687
688 par->cursor_reset = 1;
689
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800690 nvidia_vga_protect(par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
Paul Mackerras70abac62006-10-03 01:15:14 -0700692#ifdef CONFIG_BOOTX_TEXT
693 /* Update debug text engine */
694 btext_update_display(info->fix.smem_start,
695 info->var.xres, info->var.yres,
696 info->var.bits_per_pixel, info->fix.line_length);
697#endif
698
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 NVTRACE_LEAVE();
700 return 0;
701}
702
703static int nvidiafb_setcolreg(unsigned regno, unsigned red, unsigned green,
704 unsigned blue, unsigned transp,
705 struct fb_info *info)
706{
707 struct nvidia_par *par = info->par;
708 int i;
709
710 NVTRACE_ENTER();
711 if (regno >= (1 << info->var.green.length))
712 return -EINVAL;
713
714 if (info->var.grayscale) {
715 /* gray = 0.30*R + 0.59*G + 0.11*B */
716 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
717 }
718
719 if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
720 ((u32 *) info->pseudo_palette)[regno] =
721 (regno << info->var.red.offset) |
722 (regno << info->var.green.offset) |
723 (regno << info->var.blue.offset);
724 }
725
726 switch (info->var.bits_per_pixel) {
727 case 8:
728 /* "transparent" stuff is completely ignored. */
729 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
730 break;
731 case 16:
732 if (info->var.green.length == 5) {
733 for (i = 0; i < 8; i++) {
734 nvidia_write_clut(par, regno * 8 + i, red >> 8,
735 green >> 8, blue >> 8);
736 }
737 } else {
738 u8 r, g, b;
739
740 if (regno < 32) {
741 for (i = 0; i < 8; i++) {
742 nvidia_write_clut(par, regno * 8 + i,
743 red >> 8, green >> 8,
744 blue >> 8);
745 }
746 }
747
748 nvidia_read_clut(par, regno * 4, &r, &g, &b);
749
750 for (i = 0; i < 4; i++)
751 nvidia_write_clut(par, regno * 4 + i, r,
752 green >> 8, b);
753 }
754 break;
755 case 32:
756 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
757 break;
758 default:
759 /* do nothing */
760 break;
761 }
762
763 NVTRACE_LEAVE();
764 return 0;
765}
766
767static int nvidiafb_check_var(struct fb_var_screeninfo *var,
768 struct fb_info *info)
769{
770 struct nvidia_par *par = info->par;
771 int memlen, vramlen, mode_valid = 0;
772 int pitch, err = 0;
773
774 NVTRACE_ENTER();
775
776 var->transp.offset = 0;
777 var->transp.length = 0;
778
779 var->xres &= ~7;
780
781 if (var->bits_per_pixel <= 8)
782 var->bits_per_pixel = 8;
783 else if (var->bits_per_pixel <= 16)
784 var->bits_per_pixel = 16;
785 else
786 var->bits_per_pixel = 32;
787
788 switch (var->bits_per_pixel) {
789 case 8:
790 var->red.offset = 0;
791 var->red.length = 8;
792 var->green.offset = 0;
793 var->green.length = 8;
794 var->blue.offset = 0;
795 var->blue.length = 8;
796 var->transp.offset = 0;
797 var->transp.length = 0;
798 break;
799 case 16:
800 var->green.length = (var->green.length < 6) ? 5 : 6;
801 var->red.length = 5;
802 var->blue.length = 5;
803 var->transp.length = 6 - var->green.length;
804 var->blue.offset = 0;
805 var->green.offset = 5;
806 var->red.offset = 5 + var->green.length;
807 var->transp.offset = (5 + var->red.offset) & 15;
808 break;
809 case 32: /* RGBA 8888 */
810 var->red.offset = 16;
811 var->red.length = 8;
812 var->green.offset = 8;
813 var->green.length = 8;
814 var->blue.offset = 0;
815 var->blue.length = 8;
816 var->transp.length = 8;
817 var->transp.offset = 24;
818 break;
819 }
820
821 var->red.msb_right = 0;
822 var->green.msb_right = 0;
823 var->blue.msb_right = 0;
824 var->transp.msb_right = 0;
825
826 if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
827 !info->monspecs.dclkmax || !fb_validate_mode(var, info))
828 mode_valid = 1;
829
830 /* calculate modeline if supported by monitor */
831 if (!mode_valid && info->monspecs.gtf) {
832 if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
833 mode_valid = 1;
834 }
835
836 if (!mode_valid) {
Geert Uytterhoeven9791d762007-02-12 00:55:19 -0800837 const struct fb_videomode *mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
839 mode = fb_find_best_mode(var, &info->modelist);
840 if (mode) {
841 fb_videomode_to_var(var, mode);
842 mode_valid = 1;
843 }
844 }
845
846 if (!mode_valid && info->monspecs.modedb_len)
847 return -EINVAL;
848
849 if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres ||
850 par->fpHeight < var->yres))
851 return -EINVAL;
852
853 if (var->yres_virtual < var->yres)
854 var->yres_virtual = var->yres;
855
856 if (var->xres_virtual < var->xres)
857 var->xres_virtual = var->xres;
858
859 var->xres_virtual = (var->xres_virtual + 63) & ~63;
860
Antonino A. Daplas917bb072005-05-01 08:59:22 -0700861 vramlen = info->screen_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 pitch = ((var->xres_virtual * var->bits_per_pixel) + 7) / 8;
863 memlen = pitch * var->yres_virtual;
864
865 if (memlen > vramlen) {
866 var->yres_virtual = vramlen / pitch;
867
868 if (var->yres_virtual < var->yres) {
869 var->yres_virtual = var->yres;
870 var->xres_virtual = vramlen / var->yres_virtual;
871 var->xres_virtual /= var->bits_per_pixel / 8;
872 var->xres_virtual &= ~63;
873 pitch = (var->xres_virtual *
874 var->bits_per_pixel + 7) / 8;
875 memlen = pitch * var->yres;
876
877 if (var->xres_virtual < var->xres) {
878 printk("nvidiafb: required video memory, "
879 "%d bytes, for %dx%d-%d (virtual) "
880 "is out of range\n",
881 memlen, var->xres_virtual,
882 var->yres_virtual, var->bits_per_pixel);
883 err = -ENOMEM;
884 }
885 }
886 }
887
888 if (var->accel_flags) {
889 if (var->yres_virtual > 0x7fff)
890 var->yres_virtual = 0x7fff;
891 if (var->xres_virtual > 0x7fff)
892 var->xres_virtual = 0x7fff;
893 }
894
895 var->xres_virtual &= ~63;
896
897 NVTRACE_LEAVE();
898
899 return err;
900}
901
902static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
903 struct fb_info *info)
904{
905 struct nvidia_par *par = info->par;
906 u32 total;
907
Antonino A. Daplas3c8d61b2005-11-13 16:06:34 -0800908 total = var->yoffset * info->fix.line_length + var->xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
910 NVSetStartAddress(par, total);
911
912 return 0;
913}
914
915static int nvidiafb_blank(int blank, struct fb_info *info)
916{
917 struct nvidia_par *par = info->par;
918 unsigned char tmp, vesa;
919
920 tmp = NVReadSeq(par, 0x01) & ~0x20; /* screen on/off */
921 vesa = NVReadCrtc(par, 0x1a) & ~0xc0; /* sync on/off */
922
923 NVTRACE_ENTER();
924
925 if (blank)
926 tmp |= 0x20;
927
928 switch (blank) {
929 case FB_BLANK_UNBLANK:
930 case FB_BLANK_NORMAL:
931 break;
932 case FB_BLANK_VSYNC_SUSPEND:
933 vesa |= 0x80;
934 break;
935 case FB_BLANK_HSYNC_SUSPEND:
936 vesa |= 0x40;
937 break;
938 case FB_BLANK_POWERDOWN:
939 vesa |= 0xc0;
940 break;
941 }
942
943 NVWriteSeq(par, 0x01, tmp);
944 NVWriteCrtc(par, 0x1a, vesa);
945
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 NVTRACE_LEAVE();
947
948 return 0;
949}
950
951static struct fb_ops nvidia_fb_ops = {
952 .owner = THIS_MODULE,
953 .fb_check_var = nvidiafb_check_var,
954 .fb_set_par = nvidiafb_set_par,
955 .fb_setcolreg = nvidiafb_setcolreg,
956 .fb_pan_display = nvidiafb_pan_display,
957 .fb_blank = nvidiafb_blank,
958 .fb_fillrect = nvidiafb_fillrect,
959 .fb_copyarea = nvidiafb_copyarea,
960 .fb_imageblit = nvidiafb_imageblit,
961 .fb_cursor = nvidiafb_cursor,
962 .fb_sync = nvidiafb_sync,
963};
964
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800965#ifdef CONFIG_PM
David Brownellc78a7c22006-08-14 23:11:06 -0700966static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800967{
968 struct fb_info *info = pci_get_drvdata(dev);
969 struct nvidia_par *par = info->par;
970
David Brownellc78a7c22006-08-14 23:11:06 -0700971 if (mesg.event == PM_EVENT_PRETHAW)
972 mesg.event = PM_EVENT_FREEZE;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800973 acquire_console_sem();
David Brownellc78a7c22006-08-14 23:11:06 -0700974 par->pm_state = mesg.event;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800975
David Brownellc78a7c22006-08-14 23:11:06 -0700976 if (mesg.event == PM_EVENT_SUSPEND) {
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800977 fb_set_suspend(info, 1);
978 nvidiafb_blank(FB_BLANK_POWERDOWN, info);
979 nvidia_write_regs(par, &par->SavedReg);
980 pci_save_state(dev);
981 pci_disable_device(dev);
David Brownellc78a7c22006-08-14 23:11:06 -0700982 pci_set_power_state(dev, pci_choose_state(dev, mesg));
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800983 }
David Brownellc78a7c22006-08-14 23:11:06 -0700984 dev->dev.power.power_state = mesg;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800985
986 release_console_sem();
987 return 0;
988}
989
990static int nvidiafb_resume(struct pci_dev *dev)
991{
992 struct fb_info *info = pci_get_drvdata(dev);
993 struct nvidia_par *par = info->par;
994
995 acquire_console_sem();
996 pci_set_power_state(dev, PCI_D0);
997
998 if (par->pm_state != PM_EVENT_FREEZE) {
999 pci_restore_state(dev);
Antonino A. Daplas7b566b12006-10-03 01:14:53 -07001000
1001 if (pci_enable_device(dev))
1002 goto fail;
1003
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001004 pci_set_master(dev);
1005 }
1006
1007 par->pm_state = PM_EVENT_ON;
1008 nvidiafb_set_par(info);
1009 fb_set_suspend (info, 0);
1010 nvidiafb_blank(FB_BLANK_UNBLANK, info);
1011
Antonino A. Daplas7b566b12006-10-03 01:14:53 -07001012fail:
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001013 release_console_sem();
1014 return 0;
1015}
1016#else
1017#define nvidiafb_suspend NULL
1018#define nvidiafb_resume NULL
1019#endif
1020
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021static int __devinit nvidia_set_fbinfo(struct fb_info *info)
1022{
1023 struct fb_monspecs *specs = &info->monspecs;
1024 struct fb_videomode modedb;
1025 struct nvidia_par *par = info->par;
1026 int lpitch;
1027
1028 NVTRACE_ENTER();
1029 info->flags = FBINFO_DEFAULT
1030 | FBINFO_HWACCEL_IMAGEBLIT
1031 | FBINFO_HWACCEL_FILLRECT
1032 | FBINFO_HWACCEL_COPYAREA
1033 | FBINFO_HWACCEL_YPAN;
1034
1035 fb_videomode_to_modelist(info->monspecs.modedb,
1036 info->monspecs.modedb_len, &info->modelist);
1037 fb_var_to_videomode(&modedb, &nvidiafb_default_var);
1038
Antonino A. Daplasade91852006-01-09 20:53:39 -08001039 switch (bpp) {
1040 case 0 ... 8:
1041 bpp = 8;
1042 break;
1043 case 9 ... 16:
1044 bpp = 16;
1045 break;
1046 default:
1047 bpp = 32;
1048 break;
1049 }
1050
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 if (specs->modedb != NULL) {
Geert Uytterhoeven9791d762007-02-12 00:55:19 -08001052 const struct fb_videomode *mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053
Geert Uytterhoeven9791d762007-02-12 00:55:19 -08001054 mode = fb_find_best_display(specs, &info->modelist);
1055 fb_videomode_to_var(&nvidiafb_default_var, mode);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001056 nvidiafb_default_var.bits_per_pixel = bpp;
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001057 } else if (par->fpWidth && par->fpHeight) {
1058 char buf[16];
1059
1060 memset(buf, 0, 16);
Antonino A. Daplas948a95f2005-09-09 13:09:59 -07001061 snprintf(buf, 15, "%dx%dMR", par->fpWidth, par->fpHeight);
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001062 fb_find_mode(&nvidiafb_default_var, info, buf, specs->modedb,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001063 specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 }
1065
1066 if (mode_option)
1067 fb_find_mode(&nvidiafb_default_var, info, mode_option,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001068 specs->modedb, specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069
1070 info->var = nvidiafb_default_var;
1071 info->fix.visual = (info->var.bits_per_pixel == 8) ?
1072 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
1073 info->pseudo_palette = par->pseudo_palette;
1074 fb_alloc_cmap(&info->cmap, 256, 0);
1075 fb_destroy_modedb(info->monspecs.modedb);
1076 info->monspecs.modedb = NULL;
1077
1078 /* maximize virtual vertical length */
1079 lpitch = info->var.xres_virtual *
1080 ((info->var.bits_per_pixel + 7) >> 3);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001081 info->var.yres_virtual = info->screen_size / lpitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
1083 info->pixmap.scan_align = 4;
1084 info->pixmap.buf_align = 4;
James Simmons58a60642005-06-21 17:17:08 -07001085 info->pixmap.access_align = 32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 info->pixmap.size = 8 * 1024;
1087 info->pixmap.flags = FB_PIXMAP_SYSTEM;
1088
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001089 if (!hwcur)
Antonino A. Daplasc465e052005-11-07 01:00:35 -08001090 info->fbops->fb_cursor = NULL;
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001091
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 info->var.accel_flags = (!noaccel);
1093
1094 switch (par->Architecture) {
1095 case NV_ARCH_04:
1096 info->fix.accel = FB_ACCEL_NV4;
1097 break;
1098 case NV_ARCH_10:
1099 info->fix.accel = FB_ACCEL_NV_10;
1100 break;
1101 case NV_ARCH_20:
1102 info->fix.accel = FB_ACCEL_NV_20;
1103 break;
1104 case NV_ARCH_30:
1105 info->fix.accel = FB_ACCEL_NV_30;
1106 break;
1107 case NV_ARCH_40:
1108 info->fix.accel = FB_ACCEL_NV_40;
1109 break;
1110 }
1111
1112 NVTRACE_LEAVE();
1113
1114 return nvidiafb_check_var(&info->var, info);
1115}
1116
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001117static u32 __devinit nvidia_get_chipset(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118{
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001119 struct nvidia_par *par = info->par;
1120 u32 id = (par->pci_dev->vendor << 16) | par->pci_dev->device;
1121
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001122 printk(KERN_INFO PFX "Device ID: %x \n", id);
1123
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001124 if ((id & 0xfff0) == 0x00f0) {
1125 /* pci-e */
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001126 id = NV_RD32(par->REGS, 0x1800);
1127
1128 if ((id & 0x0000ffff) == 0x000010DE)
1129 id = 0x10DE0000 | (id >> 16);
1130 else if ((id & 0xffff0000) == 0xDE100000) /* wrong endian */
1131 id = 0x10DE0000 | ((id << 8) & 0x0000ff00) |
1132 ((id >> 8) & 0x000000ff);
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001133 printk(KERN_INFO PFX "Subsystem ID: %x \n", id);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001134 }
1135
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001136 return id;
1137}
1138
1139static u32 __devinit nvidia_get_arch(struct fb_info *info)
1140{
1141 struct nvidia_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 u32 arch = 0;
1143
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001144 switch (par->Chipset & 0x0ff0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 case 0x0100: /* GeForce 256 */
1146 case 0x0110: /* GeForce2 MX */
1147 case 0x0150: /* GeForce2 */
1148 case 0x0170: /* GeForce4 MX */
1149 case 0x0180: /* GeForce4 MX (8x AGP) */
1150 case 0x01A0: /* nForce */
1151 case 0x01F0: /* nForce2 */
1152 arch = NV_ARCH_10;
1153 break;
1154 case 0x0200: /* GeForce3 */
1155 case 0x0250: /* GeForce4 Ti */
1156 case 0x0280: /* GeForce4 Ti (8x AGP) */
1157 arch = NV_ARCH_20;
1158 break;
1159 case 0x0300: /* GeForceFX 5800 */
1160 case 0x0310: /* GeForceFX 5600 */
1161 case 0x0320: /* GeForceFX 5200 */
1162 case 0x0330: /* GeForceFX 5900 */
1163 case 0x0340: /* GeForceFX 5700 */
1164 arch = NV_ARCH_30;
1165 break;
Wink Savillee40c6752006-11-10 12:27:52 -08001166 case 0x0040: /* GeForce 6800 */
1167 case 0x00C0: /* GeForce 6800 */
1168 case 0x0120: /* GeForce 6800 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 case 0x0130:
Wink Savillee40c6752006-11-10 12:27:52 -08001170 case 0x0140: /* GeForce 6600 */
1171 case 0x0160: /* GeForce 6200 */
1172 case 0x01D0: /* GeForce 7200, 7300, 7400 */
1173 case 0x0090: /* GeForce 7800 */
1174 case 0x0210: /* GeForce 6800 */
1175 case 0x0220: /* GeForce 6200 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 case 0x0230:
Wink Savillee40c6752006-11-10 12:27:52 -08001177 case 0x0240: /* GeForce 6100 */
1178 case 0x0290: /* GeForce 7900 */
1179 case 0x0390: /* GeForce 7600 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 arch = NV_ARCH_40;
1181 break;
1182 case 0x0020: /* TNT, TNT2 */
1183 arch = NV_ARCH_04;
1184 break;
1185 default: /* unknown architecture */
1186 break;
1187 }
1188
1189 return arch;
1190}
1191
1192static int __devinit nvidiafb_probe(struct pci_dev *pd,
1193 const struct pci_device_id *ent)
1194{
1195 struct nvidia_par *par;
1196 struct fb_info *info;
1197 unsigned short cmd;
1198
1199
1200 NVTRACE_ENTER();
1201 assert(pd != NULL);
1202
1203 info = framebuffer_alloc(sizeof(struct nvidia_par), &pd->dev);
1204
1205 if (!info)
1206 goto err_out;
1207
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001208 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 par->pci_dev = pd;
1210
Jiri Slabyf5610b92007-02-12 00:55:12 -08001211 info->pixmap.addr = kzalloc(8 * 1024, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212
1213 if (info->pixmap.addr == NULL)
1214 goto err_out_kfree;
1215
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 if (pci_enable_device(pd)) {
1217 printk(KERN_ERR PFX "cannot enable PCI device\n");
1218 goto err_out_enable;
1219 }
1220
1221 if (pci_request_regions(pd, "nvidiafb")) {
1222 printk(KERN_ERR PFX "cannot request PCI regions\n");
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001223 goto err_out_enable;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 }
1225
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 par->FlatPanel = flatpanel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 if (flatpanel == 1)
1228 printk(KERN_INFO PFX "flatpanel support enabled\n");
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001229 par->FPDither = fpdither;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230
1231 par->CRTCnumber = forceCRTC;
1232 par->FpScale = (!noscale);
1233 par->paneltweak = paneltweak;
1234
1235 /* enable IO and mem if not already done */
1236 pci_read_config_word(pd, PCI_COMMAND, &cmd);
1237 cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
1238 pci_write_config_word(pd, PCI_COMMAND, cmd);
1239
1240 nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
1241 nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
1242 nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
1243
1244 par->REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
1245
1246 if (!par->REGS) {
1247 printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
1248 goto err_out_free_base0;
1249 }
1250
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001251 par->Chipset = nvidia_get_chipset(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001252 par->Architecture = nvidia_get_arch(info);
1253
1254 if (par->Architecture == 0) {
1255 printk(KERN_ERR PFX "unknown NV_ARCH\n");
1256 goto err_out_arch;
1257 }
1258
1259 sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
1260
Antonino A. Daplas918799a2006-01-09 20:53:40 -08001261 if (NVCommonSetup(info))
1262 goto err_out_arch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264 par->FbAddress = nvidiafb_fix.smem_start;
1265 par->FbMapSize = par->RamAmountKBytes * 1024;
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001266 if (vram && vram * 1024 * 1024 < par->FbMapSize)
1267 par->FbMapSize = vram * 1024 * 1024;
1268
1269 /* Limit amount of vram to 64 MB */
1270 if (par->FbMapSize > 64 * 1024 * 1024)
1271 par->FbMapSize = 64 * 1024 * 1024;
1272
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001273 if(par->Architecture >= NV_ARCH_40)
1274 par->FbUsableSize = par->FbMapSize - (560 * 1024);
1275 else
1276 par->FbUsableSize = par->FbMapSize - (128 * 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 par->ScratchBufferSize = (par->Architecture < NV_ARCH_10) ? 8 * 1024 :
1278 16 * 1024;
1279 par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001280 par->CursorStart = par->FbUsableSize + (32 * 1024);
1281
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001283 info->screen_size = par->FbUsableSize;
1284 nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
1286 if (!info->screen_base) {
1287 printk(KERN_ERR PFX "cannot ioremap FB base\n");
1288 goto err_out_free_base1;
1289 }
1290
1291 par->FbStart = info->screen_base;
1292
1293#ifdef CONFIG_MTRR
1294 if (!nomtrr) {
1295 par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001296 par->RamAmountKBytes * 1024,
1297 MTRR_TYPE_WRCOMB, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 if (par->mtrr.vram < 0) {
1299 printk(KERN_ERR PFX "unable to setup MTRR\n");
1300 } else {
1301 par->mtrr.vram_valid = 1;
1302 /* let there be speed */
1303 printk(KERN_INFO PFX "MTRR set to ON\n");
1304 }
1305 }
1306#endif /* CONFIG_MTRR */
1307
1308 info->fbops = &nvidia_fb_ops;
1309 info->fix = nvidiafb_fix;
1310
1311 if (nvidia_set_fbinfo(info) < 0) {
1312 printk(KERN_ERR PFX "error setting initial video mode\n");
1313 goto err_out_iounmap_fb;
1314 }
1315
1316 nvidia_save_vga(par, &par->SavedReg);
1317
Guido Guentherce38cac2006-07-30 03:04:21 -07001318 pci_set_drvdata(pd, info);
Richard Purdie202d4e62007-03-03 17:43:52 +00001319
1320 if (backlight)
1321 nvidia_bl_init(par);
1322
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 if (register_framebuffer(info) < 0) {
1324 printk(KERN_ERR PFX "error registering nVidia framebuffer\n");
1325 goto err_out_iounmap_fb;
1326 }
1327
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
1329 printk(KERN_INFO PFX
1330 "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
1331 info->fix.id,
1332 par->FbMapSize / (1024 * 1024), info->fix.smem_start);
Michael Hanselmann5474c122006-06-25 05:47:08 -07001333
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 NVTRACE_LEAVE();
1335 return 0;
1336
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001337err_out_iounmap_fb:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 iounmap(info->screen_base);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001339err_out_free_base1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 fb_destroy_modedb(info->monspecs.modedb);
1341 nvidia_delete_i2c_busses(par);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001342err_out_arch:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 iounmap(par->REGS);
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001344 err_out_free_base0:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 pci_release_regions(pd);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001346err_out_enable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 kfree(info->pixmap.addr);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001348err_out_kfree:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 framebuffer_release(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001350err_out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 return -ENODEV;
1352}
1353
Prarit Bhargava5e14ab82007-02-12 00:55:14 -08001354static void __devexit nvidiafb_remove(struct pci_dev *pd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355{
1356 struct fb_info *info = pci_get_drvdata(pd);
1357 struct nvidia_par *par = info->par;
1358
1359 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
Richard Purdie37ce69a2007-02-10 14:10:33 +00001361 unregister_framebuffer(info);
1362
Michael Hanselmann5474c122006-06-25 05:47:08 -07001363 nvidia_bl_exit(par);
1364
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365#ifdef CONFIG_MTRR
1366 if (par->mtrr.vram_valid)
1367 mtrr_del(par->mtrr.vram, info->fix.smem_start,
1368 info->fix.smem_len);
1369#endif /* CONFIG_MTRR */
1370
1371 iounmap(info->screen_base);
1372 fb_destroy_modedb(info->monspecs.modedb);
1373 nvidia_delete_i2c_busses(par);
1374 iounmap(par->REGS);
1375 pci_release_regions(pd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 kfree(info->pixmap.addr);
1377 framebuffer_release(info);
1378 pci_set_drvdata(pd, NULL);
1379 NVTRACE_LEAVE();
1380}
1381
1382/* ------------------------------------------------------------------------- *
1383 *
1384 * initialization
1385 *
1386 * ------------------------------------------------------------------------- */
1387
1388#ifndef MODULE
1389static int __devinit nvidiafb_setup(char *options)
1390{
1391 char *this_opt;
1392
1393 NVTRACE_ENTER();
1394 if (!options || !*options)
1395 return 0;
1396
1397 while ((this_opt = strsep(&options, ",")) != NULL) {
1398 if (!strncmp(this_opt, "forceCRTC", 9)) {
1399 char *p;
1400
1401 p = this_opt + 9;
1402 if (!*p || !*(++p))
1403 continue;
1404 forceCRTC = *p - '0';
1405 if (forceCRTC < 0 || forceCRTC > 1)
1406 forceCRTC = -1;
1407 } else if (!strncmp(this_opt, "flatpanel", 9)) {
1408 flatpanel = 1;
1409 } else if (!strncmp(this_opt, "hwcur", 5)) {
1410 hwcur = 1;
1411 } else if (!strncmp(this_opt, "noaccel", 6)) {
1412 noaccel = 1;
1413 } else if (!strncmp(this_opt, "noscale", 7)) {
1414 noscale = 1;
1415 } else if (!strncmp(this_opt, "paneltweak:", 11)) {
1416 paneltweak = simple_strtoul(this_opt+11, NULL, 0);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001417 } else if (!strncmp(this_opt, "vram:", 5)) {
1418 vram = simple_strtoul(this_opt+5, NULL, 0);
Richard Purdie202d4e62007-03-03 17:43:52 +00001419 } else if (!strncmp(this_opt, "backlight:", 10)) {
1420 backlight = simple_strtoul(this_opt+10, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421#ifdef CONFIG_MTRR
1422 } else if (!strncmp(this_opt, "nomtrr", 6)) {
1423 nomtrr = 1;
1424#endif
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001425 } else if (!strncmp(this_opt, "fpdither:", 9)) {
1426 fpdither = simple_strtol(this_opt+9, NULL, 0);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001427 } else if (!strncmp(this_opt, "bpp:", 4)) {
1428 bpp = simple_strtoul(this_opt+4, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 } else
1430 mode_option = this_opt;
1431 }
1432 NVTRACE_LEAVE();
1433 return 0;
1434}
1435#endif /* !MODULE */
1436
1437static struct pci_driver nvidiafb_driver = {
1438 .name = "nvidiafb",
1439 .id_table = nvidiafb_pci_tbl,
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001440 .probe = nvidiafb_probe,
1441 .suspend = nvidiafb_suspend,
1442 .resume = nvidiafb_resume,
Prarit Bhargava5e14ab82007-02-12 00:55:14 -08001443 .remove = __devexit_p(nvidiafb_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444};
1445
1446/* ------------------------------------------------------------------------- *
1447 *
1448 * modularization
1449 *
1450 * ------------------------------------------------------------------------- */
1451
1452static int __devinit nvidiafb_init(void)
1453{
1454#ifndef MODULE
1455 char *option = NULL;
1456
1457 if (fb_get_options("nvidiafb", &option))
1458 return -ENODEV;
1459 nvidiafb_setup(option);
1460#endif
1461 return pci_register_driver(&nvidiafb_driver);
1462}
1463
1464module_init(nvidiafb_init);
1465
1466#ifdef MODULE
1467static void __exit nvidiafb_exit(void)
1468{
1469 pci_unregister_driver(&nvidiafb_driver);
1470}
1471
1472module_exit(nvidiafb_exit);
1473
1474module_param(flatpanel, int, 0);
1475MODULE_PARM_DESC(flatpanel,
1476 "Enables experimental flat panel support for some chipsets. "
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001477 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
1478module_param(fpdither, int, 0);
1479MODULE_PARM_DESC(fpdither,
1480 "Enables dithering of flat panel for 6 bits panels. "
1481 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482module_param(hwcur, int, 0);
1483MODULE_PARM_DESC(hwcur,
1484 "Enables hardware cursor implementation. (0 or 1=enabled) "
1485 "(default=0)");
1486module_param(noaccel, int, 0);
1487MODULE_PARM_DESC(noaccel,
1488 "Disables hardware acceleration. (0 or 1=disable) "
1489 "(default=0)");
1490module_param(noscale, int, 0);
1491MODULE_PARM_DESC(noscale,
1492 "Disables screen scaleing. (0 or 1=disable) "
1493 "(default=0, do scaling)");
1494module_param(paneltweak, int, 0);
1495MODULE_PARM_DESC(paneltweak,
1496 "Tweak display settings for flatpanels. "
1497 "(default=0, no tweaks)");
1498module_param(forceCRTC, int, 0);
1499MODULE_PARM_DESC(forceCRTC,
1500 "Forces usage of a particular CRTC in case autodetection "
1501 "fails. (0 or 1) (default=autodetect)");
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001502module_param(vram, int, 0);
1503MODULE_PARM_DESC(vram,
1504 "amount of framebuffer memory to remap in MiB"
1505 "(default=0 - remap entire memory)");
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001506module_param(mode_option, charp, 0);
1507MODULE_PARM_DESC(mode_option, "Specify initial video mode");
Antonino A. Daplasade91852006-01-09 20:53:39 -08001508module_param(bpp, int, 0);
1509MODULE_PARM_DESC(bpp, "pixel width in bits"
1510 "(default=8)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511#ifdef CONFIG_MTRR
1512module_param(nomtrr, bool, 0);
1513MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
1514 "(default=0)");
1515#endif
1516
1517MODULE_AUTHOR("Antonino Daplas");
1518MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
1519MODULE_LICENSE("GPL");
1520#endif /* MODULE */
1521