blob: 9f2066f0745adbd62921e2cebb284dcc7a99869f [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
37#ifndef CONFIG_PCI /* sanity check */
38#error This driver requires PCI support.
39#endif
40
41#undef CONFIG_FB_NVIDIA_DEBUG
42#ifdef CONFIG_FB_NVIDIA_DEBUG
43#define NVTRACE printk
44#else
45#define NVTRACE if (0) printk
46#endif
47
48#define NVTRACE_ENTER(...) NVTRACE("%s START\n", __FUNCTION__)
49#define NVTRACE_LEAVE(...) NVTRACE("%s END\n", __FUNCTION__)
50
51#ifdef CONFIG_FB_NVIDIA_DEBUG
52#define assert(expr) \
53 if (!(expr)) { \
54 printk( "Assertion failed! %s,%s,%s,line=%d\n",\
55 #expr,__FILE__,__FUNCTION__,__LINE__); \
56 BUG(); \
57 }
58#else
59#define assert(expr)
60#endif
61
62#define PFX "nvidiafb: "
63
64/* HW cursor parameters */
65#define MAX_CURS 32
66
67static struct pci_device_id nvidiafb_pci_tbl[] = {
Antonino A. Daplas8eec4982006-06-26 00:26:30 -070068 {PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
69 PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0},
70 { 0, }
Linus Torvalds1da177e2005-04-16 15:20:36 -070071};
Linus Torvalds1da177e2005-04-16 15:20:36 -070072MODULE_DEVICE_TABLE(pci, nvidiafb_pci_tbl);
73
74/* command line data, set in nvidiafb_setup() */
75static int flatpanel __devinitdata = -1; /* Autodetect later */
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -080076static int fpdither __devinitdata = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077static int forceCRTC __devinitdata = -1;
78static int hwcur __devinitdata = 0;
79static int noaccel __devinitdata = 0;
80static int noscale __devinitdata = 0;
81static int paneltweak __devinitdata = 0;
Antonino A. Daplas917bb072005-05-01 08:59:22 -070082static int vram __devinitdata = 0;
Antonino A. Daplasade91852006-01-09 20:53:39 -080083static int bpp __devinitdata = 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084#ifdef CONFIG_MTRR
85static int nomtrr __devinitdata = 0;
86#endif
87
88static char *mode_option __devinitdata = NULL;
89
90static struct fb_fix_screeninfo __devinitdata nvidiafb_fix = {
91 .type = FB_TYPE_PACKED_PIXELS,
92 .xpanstep = 8,
93 .ypanstep = 1,
94};
95
96static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = {
97 .xres = 640,
98 .yres = 480,
99 .xres_virtual = 640,
100 .yres_virtual = 480,
101 .bits_per_pixel = 8,
102 .red = {0, 8, 0},
103 .green = {0, 8, 0},
104 .blue = {0, 8, 0},
105 .transp = {0, 0, 0},
106 .activate = FB_ACTIVATE_NOW,
107 .height = -1,
108 .width = -1,
109 .pixclock = 39721,
110 .left_margin = 40,
111 .right_margin = 24,
112 .upper_margin = 32,
113 .lower_margin = 11,
114 .hsync_len = 96,
115 .vsync_len = 2,
116 .vmode = FB_VMODE_NONINTERLACED
117};
118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
120 u16 bg, u16 fg, u32 w, u32 h)
121{
James Simmonsf1ab5da2005-06-21 17:17:07 -0700122 u32 *data = (u32 *) data8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 int i, j, k = 0;
124 u32 b, tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 w = (w + 1) & ~1;
127
128 for (i = 0; i < h; i++) {
129 b = *data++;
130 reverse_order(&b);
131
132 for (j = 0; j < w / 2; j++) {
133 tmp = 0;
134#if defined (__BIG_ENDIAN)
135 tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
136 b <<= 1;
137 tmp |= (b & (1 << 31)) ? fg : bg;
138 b <<= 1;
139#else
140 tmp = (b & 1) ? fg : bg;
141 b >>= 1;
142 tmp |= (b & 1) ? fg << 16 : bg << 16;
143 b >>= 1;
144#endif
145 NV_WR32(&par->CURSOR[k++], 0, tmp);
146 }
147 k += (MAX_CURS - w) / 2;
148 }
149}
150
151static void nvidia_write_clut(struct nvidia_par *par,
152 u8 regnum, u8 red, u8 green, u8 blue)
153{
154 NVWriteDacMask(par, 0xff);
155 NVWriteDacWriteAddr(par, regnum);
156 NVWriteDacData(par, red);
157 NVWriteDacData(par, green);
158 NVWriteDacData(par, blue);
159}
160
161static void nvidia_read_clut(struct nvidia_par *par,
162 u8 regnum, u8 * red, u8 * green, u8 * blue)
163{
164 NVWriteDacMask(par, 0xff);
165 NVWriteDacReadAddr(par, regnum);
166 *red = NVReadDacData(par);
167 *green = NVReadDacData(par);
168 *blue = NVReadDacData(par);
169}
170
171static int nvidia_panel_tweak(struct nvidia_par *par,
172 struct _riva_hw_state *state)
173{
174 int tweak = 0;
175
176 if (par->paneltweak) {
177 tweak = par->paneltweak;
178 } else {
179 /* begin flat panel hacks */
180 /* This is unfortunate, but some chips need this register
181 tweaked or else you get artifacts where adjacent pixels are
182 swapped. There are no hard rules for what to set here so all
183 we can do is experiment and apply hacks. */
184
185 if(((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) {
186 /* At least one NV34 laptop needs this workaround. */
187 tweak = -1;
188 }
189
190 if((par->Chipset & 0xfff0) == 0x0310) {
191 tweak = 1;
192 }
193 /* end flat panel hacks */
194 }
195
196 return tweak;
197}
198
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800199static void nvidia_vga_protect(struct nvidia_par *par, int on)
200{
201 unsigned char tmp;
202
203 if (on) {
204 /*
205 * Turn off screen and disable sequencer.
206 */
207 tmp = NVReadSeq(par, 0x01);
208
209 NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */
210 NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */
211 } else {
212 /*
213 * Reenable sequencer, then turn on screen.
214 */
215
216 tmp = NVReadSeq(par, 0x01);
217
218 NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */
219 NVWriteSeq(par, 0x00, 0x03); /* End Reset */
220 }
221}
222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223static void nvidia_save_vga(struct nvidia_par *par,
224 struct _riva_hw_state *state)
225{
226 int i;
227
228 NVTRACE_ENTER();
229 NVLockUnlock(par, 0);
230
231 NVUnloadStateExt(par, state);
232
233 state->misc_output = NVReadMiscOut(par);
234
235 for (i = 0; i < NUM_CRT_REGS; i++)
236 state->crtc[i] = NVReadCrtc(par, i);
237
238 for (i = 0; i < NUM_ATC_REGS; i++)
239 state->attr[i] = NVReadAttr(par, i);
240
241 for (i = 0; i < NUM_GRC_REGS; i++)
242 state->gra[i] = NVReadGr(par, i);
243
244 for (i = 0; i < NUM_SEQ_REGS; i++)
245 state->seq[i] = NVReadSeq(par, i);
246 NVTRACE_LEAVE();
247}
248
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800249#undef DUMP_REG
250
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800251static void nvidia_write_regs(struct nvidia_par *par,
252 struct _riva_hw_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 int i;
255
256 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
258 NVLoadStateExt(par, state);
259
260 NVWriteMiscOut(par, state->misc_output);
261
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800262 for (i = 1; i < NUM_SEQ_REGS; i++) {
263#ifdef DUMP_REG
264 printk(" SEQ[%02x] = %08x\n", i, state->seq[i]);
265#endif
266 NVWriteSeq(par, i, state->seq[i]);
267 }
268
269 /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 of CRTC[17] */
270 NVWriteCrtc(par, 0x11, state->crtc[0x11] & ~0x80);
271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 for (i = 0; i < NUM_CRT_REGS; i++) {
273 switch (i) {
274 case 0x19:
275 case 0x20 ... 0x40:
276 break;
277 default:
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800278#ifdef DUMP_REG
279 printk("CRTC[%02x] = %08x\n", i, state->crtc[i]);
280#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 NVWriteCrtc(par, i, state->crtc[i]);
282 }
283 }
284
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800285 for (i = 0; i < NUM_GRC_REGS; i++) {
286#ifdef DUMP_REG
287 printk(" GRA[%02x] = %08x\n", i, state->gra[i]);
288#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 NVWriteGr(par, i, state->gra[i]);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800290 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800292 for (i = 0; i < NUM_ATC_REGS; i++) {
293#ifdef DUMP_REG
294 printk("ATTR[%02x] = %08x\n", i, state->attr[i]);
295#endif
296 NVWriteAttr(par, i, state->attr[i]);
297 }
298
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 NVTRACE_LEAVE();
300}
301
302static int nvidia_calc_regs(struct fb_info *info)
303{
304 struct nvidia_par *par = info->par;
305 struct _riva_hw_state *state = &par->ModeReg;
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700306 int i, depth = fb_get_color_depth(&info->var, &info->fix);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 int h_display = info->var.xres / 8 - 1;
308 int h_start = (info->var.xres + info->var.right_margin) / 8 - 1;
309 int h_end = (info->var.xres + info->var.right_margin +
310 info->var.hsync_len) / 8 - 1;
311 int h_total = (info->var.xres + info->var.right_margin +
312 info->var.hsync_len + info->var.left_margin) / 8 - 5;
313 int h_blank_s = h_display;
314 int h_blank_e = h_total + 4;
315 int v_display = info->var.yres - 1;
316 int v_start = info->var.yres + info->var.lower_margin - 1;
317 int v_end = (info->var.yres + info->var.lower_margin +
318 info->var.vsync_len) - 1;
319 int v_total = (info->var.yres + info->var.lower_margin +
320 info->var.vsync_len + info->var.upper_margin) - 2;
321 int v_blank_s = v_display;
322 int v_blank_e = v_total + 1;
323
324 /*
325 * Set all CRTC values.
326 */
327
328 if (info->var.vmode & FB_VMODE_INTERLACED)
329 v_total |= 1;
330
331 if (par->FlatPanel == 1) {
332 v_start = v_total - 3;
333 v_end = v_total - 2;
334 v_blank_s = v_start;
335 h_start = h_total - 5;
336 h_end = h_total - 2;
337 h_blank_e = h_total + 4;
338 }
339
340 state->crtc[0x0] = Set8Bits(h_total);
341 state->crtc[0x1] = Set8Bits(h_display);
342 state->crtc[0x2] = Set8Bits(h_blank_s);
343 state->crtc[0x3] = SetBitField(h_blank_e, 4: 0, 4:0)
344 | SetBit(7);
345 state->crtc[0x4] = Set8Bits(h_start);
346 state->crtc[0x5] = SetBitField(h_blank_e, 5: 5, 7:7)
347 | SetBitField(h_end, 4: 0, 4:0);
348 state->crtc[0x6] = SetBitField(v_total, 7: 0, 7:0);
349 state->crtc[0x7] = SetBitField(v_total, 8: 8, 0:0)
350 | SetBitField(v_display, 8: 8, 1:1)
351 | SetBitField(v_start, 8: 8, 2:2)
352 | SetBitField(v_blank_s, 8: 8, 3:3)
353 | SetBit(4)
354 | SetBitField(v_total, 9: 9, 5:5)
355 | SetBitField(v_display, 9: 9, 6:6)
356 | SetBitField(v_start, 9: 9, 7:7);
357 state->crtc[0x9] = SetBitField(v_blank_s, 9: 9, 5:5)
358 | SetBit(6)
359 | ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0x00);
360 state->crtc[0x10] = Set8Bits(v_start);
361 state->crtc[0x11] = SetBitField(v_end, 3: 0, 3:0) | SetBit(5);
362 state->crtc[0x12] = Set8Bits(v_display);
363 state->crtc[0x13] = ((info->var.xres_virtual / 8) *
364 (info->var.bits_per_pixel / 8));
365 state->crtc[0x15] = Set8Bits(v_blank_s);
366 state->crtc[0x16] = Set8Bits(v_blank_e);
367
368 state->attr[0x10] = 0x01;
369
370 if (par->Television)
371 state->attr[0x11] = 0x00;
372
373 state->screen = SetBitField(h_blank_e, 6: 6, 4:4)
374 | SetBitField(v_blank_s, 10: 10, 3:3)
375 | SetBitField(v_start, 10: 10, 2:2)
376 | SetBitField(v_display, 10: 10, 1:1)
377 | SetBitField(v_total, 10: 10, 0:0);
378
379 state->horiz = SetBitField(h_total, 8: 8, 0:0)
380 | SetBitField(h_display, 8: 8, 1:1)
381 | SetBitField(h_blank_s, 8: 8, 2:2)
382 | SetBitField(h_start, 8: 8, 3:3);
383
384 state->extra = SetBitField(v_total, 11: 11, 0:0)
385 | SetBitField(v_display, 11: 11, 2:2)
386 | SetBitField(v_start, 11: 11, 4:4)
387 | SetBitField(v_blank_s, 11: 11, 6:6);
388
389 if (info->var.vmode & FB_VMODE_INTERLACED) {
390 h_total = (h_total >> 1) & ~1;
391 state->interlace = Set8Bits(h_total);
392 state->horiz |= SetBitField(h_total, 8: 8, 4:4);
393 } else {
394 state->interlace = 0xff; /* interlace off */
395 }
396
397 /*
398 * Calculate the extended registers.
399 */
400
401 if (depth < 24)
402 i = depth;
403 else
404 i = 32;
405
406 if (par->Architecture >= NV_ARCH_10)
407 par->CURSOR = (volatile u32 __iomem *)(info->screen_base +
408 par->CursorStart);
409
410 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
411 state->misc_output &= ~0x40;
412 else
413 state->misc_output |= 0x40;
414 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
415 state->misc_output &= ~0x80;
416 else
417 state->misc_output |= 0x80;
418
419 NVCalcStateExt(par, state, i, info->var.xres_virtual,
420 info->var.xres, info->var.yres_virtual,
421 1000000000 / info->var.pixclock, info->var.vmode);
422
423 state->scale = NV_RD32(par->PRAMDAC, 0x00000848) & 0xfff000ff;
424 if (par->FlatPanel == 1) {
425 state->pixel |= (1 << 7);
426
427 if (!par->fpScaler || (par->fpWidth <= info->var.xres)
428 || (par->fpHeight <= info->var.yres)) {
429 state->scale |= (1 << 8);
430 }
431
432 if (!par->crtcSync_read) {
433 state->crtcSync = NV_RD32(par->PRAMDAC, 0x0828);
434 par->crtcSync_read = 1;
435 }
436
437 par->PanelTweak = nvidia_panel_tweak(par, state);
438 }
439
440 state->vpll = state->pll;
441 state->vpll2 = state->pll;
442 state->vpllB = state->pllB;
443 state->vpll2B = state->pllB;
444
445 VGA_WR08(par->PCIO, 0x03D4, 0x1C);
446 state->fifo = VGA_RD08(par->PCIO, 0x03D5) & ~(1<<5);
447
448 if (par->CRTCnumber) {
449 state->head = NV_RD32(par->PCRTC0, 0x00000860) & ~0x00001000;
450 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) | 0x00001000;
451 state->crtcOwner = 3;
452 state->pllsel |= 0x20000800;
453 state->vpll = NV_RD32(par->PRAMDAC0, 0x00000508);
454 if (par->twoStagePLL)
455 state->vpllB = NV_RD32(par->PRAMDAC0, 0x00000578);
456 } else if (par->twoHeads) {
457 state->head = NV_RD32(par->PCRTC0, 0x00000860) | 0x00001000;
458 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) & ~0x00001000;
459 state->crtcOwner = 0;
460 state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
461 if (par->twoStagePLL)
462 state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
463 }
464
465 state->cursorConfig = 0x00000100;
466
467 if (info->var.vmode & FB_VMODE_DOUBLE)
468 state->cursorConfig |= (1 << 4);
469
470 if (par->alphaCursor) {
471 if ((par->Chipset & 0x0ff0) != 0x0110)
472 state->cursorConfig |= 0x04011000;
473 else
474 state->cursorConfig |= 0x14011000;
475 state->general |= (1 << 29);
476 } else
477 state->cursorConfig |= 0x02000000;
478
479 if (par->twoHeads) {
480 if ((par->Chipset & 0x0ff0) == 0x0110) {
481 state->dither = NV_RD32(par->PRAMDAC, 0x0528) &
482 ~0x00010000;
483 if (par->FPDither)
484 state->dither |= 0x00010000;
485 } else {
486 state->dither = NV_RD32(par->PRAMDAC, 0x083C) & ~1;
487 if (par->FPDither)
488 state->dither |= 1;
489 }
490 }
491
492 state->timingH = 0;
493 state->timingV = 0;
494 state->displayV = info->var.xres;
495
496 return 0;
497}
498
499static void nvidia_init_vga(struct fb_info *info)
500{
501 struct nvidia_par *par = info->par;
502 struct _riva_hw_state *state = &par->ModeReg;
503 int i;
504
505 for (i = 0; i < 0x10; i++)
506 state->attr[i] = i;
507 state->attr[0x10] = 0x41;
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800508 state->attr[0x11] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 state->attr[0x12] = 0x0f;
510 state->attr[0x13] = 0x00;
511 state->attr[0x14] = 0x00;
512
513 memset(state->crtc, 0x00, NUM_CRT_REGS);
514 state->crtc[0x0a] = 0x20;
515 state->crtc[0x17] = 0xe3;
516 state->crtc[0x18] = 0xff;
517 state->crtc[0x28] = 0x40;
518
519 memset(state->gra, 0x00, NUM_GRC_REGS);
520 state->gra[0x05] = 0x40;
521 state->gra[0x06] = 0x05;
522 state->gra[0x07] = 0x0f;
523 state->gra[0x08] = 0xff;
524
525 state->seq[0x00] = 0x03;
526 state->seq[0x01] = 0x01;
527 state->seq[0x02] = 0x0f;
528 state->seq[0x03] = 0x00;
529 state->seq[0x04] = 0x0e;
530
531 state->misc_output = 0xeb;
532}
533
534static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
535{
536 struct nvidia_par *par = info->par;
537 u8 data[MAX_CURS * MAX_CURS / 8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 int i, set = cursor->set;
James Simmonsf1ab5da2005-06-21 17:17:07 -0700539 u16 fg, bg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
Antonino A. Daplas7a482422005-09-21 07:30:21 +0800541 if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
James Simmonsf1ab5da2005-06-21 17:17:07 -0700542 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
544 NVShowHideCursor(par, 0);
545
546 if (par->cursor_reset) {
547 set = FB_CUR_SETALL;
548 par->cursor_reset = 0;
549 }
550
551 if (set & FB_CUR_SETSIZE)
552 memset_io(par->CURSOR, 0, MAX_CURS * MAX_CURS * 2);
553
554 if (set & FB_CUR_SETPOS) {
555 u32 xx, yy, temp;
556
557 yy = cursor->image.dy - info->var.yoffset;
558 xx = cursor->image.dx - info->var.xoffset;
559 temp = xx & 0xFFFF;
560 temp |= yy << 16;
561
562 NV_WR32(par->PRAMDAC, 0x0000300, temp);
563 }
564
565 if (set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
566 u32 bg_idx = cursor->image.bg_color;
567 u32 fg_idx = cursor->image.fg_color;
568 u32 s_pitch = (cursor->image.width + 7) >> 3;
569 u32 d_pitch = MAX_CURS / 8;
570 u8 *dat = (u8 *) cursor->image.data;
571 u8 *msk = (u8 *) cursor->mask;
572 u8 *src;
573
574 src = kmalloc(s_pitch * cursor->image.height, GFP_ATOMIC);
575
576 if (src) {
577 switch (cursor->rop) {
578 case ROP_XOR:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700579 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 src[i] = dat[i] ^ msk[i];
581 break;
582 case ROP_COPY:
583 default:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700584 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 src[i] = dat[i] & msk[i];
586 break;
587 }
588
James Simmonsf1ab5da2005-06-21 17:17:07 -0700589 fb_pad_aligned_buffer(data, d_pitch, src, s_pitch,
590 cursor->image.height);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
592 bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
593 ((info->cmap.green[bg_idx] & 0xf8) << 2) |
594 ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | 1 << 15;
595
596 fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
597 ((info->cmap.green[fg_idx] & 0xf8) << 2) |
598 ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
599
600 NVLockUnlock(par, 0);
601
602 nvidiafb_load_cursor_image(par, data, bg, fg,
603 cursor->image.width,
604 cursor->image.height);
605 kfree(src);
606 }
607 }
608
609 if (cursor->enable)
610 NVShowHideCursor(par, 1);
611
612 return 0;
613}
614
615static int nvidiafb_set_par(struct fb_info *info)
616{
617 struct nvidia_par *par = info->par;
618
619 NVTRACE_ENTER();
620
621 NVLockUnlock(par, 1);
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800622 if (!par->FlatPanel || !par->twoHeads)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 par->FPDither = 0;
624
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800625 if (par->FPDither < 0) {
626 if ((par->Chipset & 0x0ff0) == 0x0110)
627 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x0528)
628 & 0x00010000);
629 else
630 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1);
631 printk(KERN_INFO PFX "Flat panel dithering %s\n",
632 par->FPDither ? "enabled" : "disabled");
633 }
634
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700635 info->fix.visual = (info->var.bits_per_pixel == 8) ?
636 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
637
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 nvidia_init_vga(info);
639 nvidia_calc_regs(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
641 NVLockUnlock(par, 0);
642 if (par->twoHeads) {
643 VGA_WR08(par->PCIO, 0x03D4, 0x44);
644 VGA_WR08(par->PCIO, 0x03D5, par->ModeReg.crtcOwner);
645 NVLockUnlock(par, 0);
646 }
647
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800648 nvidia_vga_protect(par, 1);
649
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800650 nvidia_write_regs(par, &par->ModeReg);
651 NVSetStartAddress(par, 0);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800652
653#if defined (__BIG_ENDIAN)
654 /* turn on LFB swapping */
655 {
656 unsigned char tmp;
657
658 VGA_WR08(par->PCIO, 0x3d4, 0x46);
659 tmp = VGA_RD08(par->PCIO, 0x3d5);
660 tmp |= (1 << 7);
661 VGA_WR08(par->PCIO, 0x3d5, tmp);
662 }
663#endif
664
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 info->fix.line_length = (info->var.xres_virtual *
666 info->var.bits_per_pixel) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 if (info->var.accel_flags) {
668 info->fbops->fb_imageblit = nvidiafb_imageblit;
669 info->fbops->fb_fillrect = nvidiafb_fillrect;
670 info->fbops->fb_copyarea = nvidiafb_copyarea;
671 info->fbops->fb_sync = nvidiafb_sync;
672 info->pixmap.scan_align = 4;
673 info->flags &= ~FBINFO_HWACCEL_DISABLED;
674 NVResetGraphics(info);
675 } else {
676 info->fbops->fb_imageblit = cfb_imageblit;
677 info->fbops->fb_fillrect = cfb_fillrect;
678 info->fbops->fb_copyarea = cfb_copyarea;
679 info->fbops->fb_sync = NULL;
680 info->pixmap.scan_align = 1;
681 info->flags |= FBINFO_HWACCEL_DISABLED;
682 }
683
684 par->cursor_reset = 1;
685
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800686 nvidia_vga_protect(par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
688 NVTRACE_LEAVE();
689 return 0;
690}
691
692static int nvidiafb_setcolreg(unsigned regno, unsigned red, unsigned green,
693 unsigned blue, unsigned transp,
694 struct fb_info *info)
695{
696 struct nvidia_par *par = info->par;
697 int i;
698
699 NVTRACE_ENTER();
700 if (regno >= (1 << info->var.green.length))
701 return -EINVAL;
702
703 if (info->var.grayscale) {
704 /* gray = 0.30*R + 0.59*G + 0.11*B */
705 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
706 }
707
708 if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
709 ((u32 *) info->pseudo_palette)[regno] =
710 (regno << info->var.red.offset) |
711 (regno << info->var.green.offset) |
712 (regno << info->var.blue.offset);
713 }
714
715 switch (info->var.bits_per_pixel) {
716 case 8:
717 /* "transparent" stuff is completely ignored. */
718 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
719 break;
720 case 16:
721 if (info->var.green.length == 5) {
722 for (i = 0; i < 8; i++) {
723 nvidia_write_clut(par, regno * 8 + i, red >> 8,
724 green >> 8, blue >> 8);
725 }
726 } else {
727 u8 r, g, b;
728
729 if (regno < 32) {
730 for (i = 0; i < 8; i++) {
731 nvidia_write_clut(par, regno * 8 + i,
732 red >> 8, green >> 8,
733 blue >> 8);
734 }
735 }
736
737 nvidia_read_clut(par, regno * 4, &r, &g, &b);
738
739 for (i = 0; i < 4; i++)
740 nvidia_write_clut(par, regno * 4 + i, r,
741 green >> 8, b);
742 }
743 break;
744 case 32:
745 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
746 break;
747 default:
748 /* do nothing */
749 break;
750 }
751
752 NVTRACE_LEAVE();
753 return 0;
754}
755
756static int nvidiafb_check_var(struct fb_var_screeninfo *var,
757 struct fb_info *info)
758{
759 struct nvidia_par *par = info->par;
760 int memlen, vramlen, mode_valid = 0;
761 int pitch, err = 0;
762
763 NVTRACE_ENTER();
764
765 var->transp.offset = 0;
766 var->transp.length = 0;
767
768 var->xres &= ~7;
769
770 if (var->bits_per_pixel <= 8)
771 var->bits_per_pixel = 8;
772 else if (var->bits_per_pixel <= 16)
773 var->bits_per_pixel = 16;
774 else
775 var->bits_per_pixel = 32;
776
777 switch (var->bits_per_pixel) {
778 case 8:
779 var->red.offset = 0;
780 var->red.length = 8;
781 var->green.offset = 0;
782 var->green.length = 8;
783 var->blue.offset = 0;
784 var->blue.length = 8;
785 var->transp.offset = 0;
786 var->transp.length = 0;
787 break;
788 case 16:
789 var->green.length = (var->green.length < 6) ? 5 : 6;
790 var->red.length = 5;
791 var->blue.length = 5;
792 var->transp.length = 6 - var->green.length;
793 var->blue.offset = 0;
794 var->green.offset = 5;
795 var->red.offset = 5 + var->green.length;
796 var->transp.offset = (5 + var->red.offset) & 15;
797 break;
798 case 32: /* RGBA 8888 */
799 var->red.offset = 16;
800 var->red.length = 8;
801 var->green.offset = 8;
802 var->green.length = 8;
803 var->blue.offset = 0;
804 var->blue.length = 8;
805 var->transp.length = 8;
806 var->transp.offset = 24;
807 break;
808 }
809
810 var->red.msb_right = 0;
811 var->green.msb_right = 0;
812 var->blue.msb_right = 0;
813 var->transp.msb_right = 0;
814
815 if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
816 !info->monspecs.dclkmax || !fb_validate_mode(var, info))
817 mode_valid = 1;
818
819 /* calculate modeline if supported by monitor */
820 if (!mode_valid && info->monspecs.gtf) {
821 if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
822 mode_valid = 1;
823 }
824
825 if (!mode_valid) {
826 struct fb_videomode *mode;
827
828 mode = fb_find_best_mode(var, &info->modelist);
829 if (mode) {
830 fb_videomode_to_var(var, mode);
831 mode_valid = 1;
832 }
833 }
834
835 if (!mode_valid && info->monspecs.modedb_len)
836 return -EINVAL;
837
838 if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres ||
839 par->fpHeight < var->yres))
840 return -EINVAL;
841
842 if (var->yres_virtual < var->yres)
843 var->yres_virtual = var->yres;
844
845 if (var->xres_virtual < var->xres)
846 var->xres_virtual = var->xres;
847
848 var->xres_virtual = (var->xres_virtual + 63) & ~63;
849
Antonino A. Daplas917bb072005-05-01 08:59:22 -0700850 vramlen = info->screen_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 pitch = ((var->xres_virtual * var->bits_per_pixel) + 7) / 8;
852 memlen = pitch * var->yres_virtual;
853
854 if (memlen > vramlen) {
855 var->yres_virtual = vramlen / pitch;
856
857 if (var->yres_virtual < var->yres) {
858 var->yres_virtual = var->yres;
859 var->xres_virtual = vramlen / var->yres_virtual;
860 var->xres_virtual /= var->bits_per_pixel / 8;
861 var->xres_virtual &= ~63;
862 pitch = (var->xres_virtual *
863 var->bits_per_pixel + 7) / 8;
864 memlen = pitch * var->yres;
865
866 if (var->xres_virtual < var->xres) {
867 printk("nvidiafb: required video memory, "
868 "%d bytes, for %dx%d-%d (virtual) "
869 "is out of range\n",
870 memlen, var->xres_virtual,
871 var->yres_virtual, var->bits_per_pixel);
872 err = -ENOMEM;
873 }
874 }
875 }
876
877 if (var->accel_flags) {
878 if (var->yres_virtual > 0x7fff)
879 var->yres_virtual = 0x7fff;
880 if (var->xres_virtual > 0x7fff)
881 var->xres_virtual = 0x7fff;
882 }
883
884 var->xres_virtual &= ~63;
885
886 NVTRACE_LEAVE();
887
888 return err;
889}
890
891static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
892 struct fb_info *info)
893{
894 struct nvidia_par *par = info->par;
895 u32 total;
896
Antonino A. Daplas3c8d61b2005-11-13 16:06:34 -0800897 total = var->yoffset * info->fix.line_length + var->xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
899 NVSetStartAddress(par, total);
900
901 return 0;
902}
903
904static int nvidiafb_blank(int blank, struct fb_info *info)
905{
906 struct nvidia_par *par = info->par;
907 unsigned char tmp, vesa;
908
909 tmp = NVReadSeq(par, 0x01) & ~0x20; /* screen on/off */
910 vesa = NVReadCrtc(par, 0x1a) & ~0xc0; /* sync on/off */
911
912 NVTRACE_ENTER();
913
914 if (blank)
915 tmp |= 0x20;
916
917 switch (blank) {
918 case FB_BLANK_UNBLANK:
919 case FB_BLANK_NORMAL:
920 break;
921 case FB_BLANK_VSYNC_SUSPEND:
922 vesa |= 0x80;
923 break;
924 case FB_BLANK_HSYNC_SUSPEND:
925 vesa |= 0x40;
926 break;
927 case FB_BLANK_POWERDOWN:
928 vesa |= 0xc0;
929 break;
930 }
931
932 NVWriteSeq(par, 0x01, tmp);
933 NVWriteCrtc(par, 0x1a, vesa);
934
Michael Hanselmanne01af032006-07-10 04:44:45 -0700935 nvidia_bl_set_power(info, blank);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
937 NVTRACE_LEAVE();
938
939 return 0;
940}
941
942static struct fb_ops nvidia_fb_ops = {
943 .owner = THIS_MODULE,
944 .fb_check_var = nvidiafb_check_var,
945 .fb_set_par = nvidiafb_set_par,
946 .fb_setcolreg = nvidiafb_setcolreg,
947 .fb_pan_display = nvidiafb_pan_display,
948 .fb_blank = nvidiafb_blank,
949 .fb_fillrect = nvidiafb_fillrect,
950 .fb_copyarea = nvidiafb_copyarea,
951 .fb_imageblit = nvidiafb_imageblit,
952 .fb_cursor = nvidiafb_cursor,
953 .fb_sync = nvidiafb_sync,
954};
955
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800956#ifdef CONFIG_PM
957static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t state)
958{
959 struct fb_info *info = pci_get_drvdata(dev);
960 struct nvidia_par *par = info->par;
961
962 acquire_console_sem();
963 par->pm_state = state.event;
964
965 if (state.event == PM_EVENT_FREEZE) {
966 dev->dev.power.power_state = state;
967 } else {
968 fb_set_suspend(info, 1);
969 nvidiafb_blank(FB_BLANK_POWERDOWN, info);
970 nvidia_write_regs(par, &par->SavedReg);
971 pci_save_state(dev);
972 pci_disable_device(dev);
973 pci_set_power_state(dev, pci_choose_state(dev, state));
974 }
975
976 release_console_sem();
977 return 0;
978}
979
980static int nvidiafb_resume(struct pci_dev *dev)
981{
982 struct fb_info *info = pci_get_drvdata(dev);
983 struct nvidia_par *par = info->par;
984
985 acquire_console_sem();
986 pci_set_power_state(dev, PCI_D0);
987
988 if (par->pm_state != PM_EVENT_FREEZE) {
989 pci_restore_state(dev);
990 pci_enable_device(dev);
991 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
999 release_console_sem();
1000 return 0;
1001}
1002#else
1003#define nvidiafb_suspend NULL
1004#define nvidiafb_resume NULL
1005#endif
1006
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007static int __devinit nvidia_set_fbinfo(struct fb_info *info)
1008{
1009 struct fb_monspecs *specs = &info->monspecs;
1010 struct fb_videomode modedb;
1011 struct nvidia_par *par = info->par;
1012 int lpitch;
1013
1014 NVTRACE_ENTER();
1015 info->flags = FBINFO_DEFAULT
1016 | FBINFO_HWACCEL_IMAGEBLIT
1017 | FBINFO_HWACCEL_FILLRECT
1018 | FBINFO_HWACCEL_COPYAREA
1019 | FBINFO_HWACCEL_YPAN;
1020
1021 fb_videomode_to_modelist(info->monspecs.modedb,
1022 info->monspecs.modedb_len, &info->modelist);
1023 fb_var_to_videomode(&modedb, &nvidiafb_default_var);
1024
Antonino A. Daplasade91852006-01-09 20:53:39 -08001025 switch (bpp) {
1026 case 0 ... 8:
1027 bpp = 8;
1028 break;
1029 case 9 ... 16:
1030 bpp = 16;
1031 break;
1032 default:
1033 bpp = 32;
1034 break;
1035 }
1036
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 if (specs->modedb != NULL) {
Antonino A. Daplas5ee1ef92005-11-07 01:00:55 -08001038 struct fb_videomode *modedb;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039
Antonino A. Daplas5ee1ef92005-11-07 01:00:55 -08001040 modedb = fb_find_best_display(specs, &info->modelist);
1041 fb_videomode_to_var(&nvidiafb_default_var, modedb);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001042 nvidiafb_default_var.bits_per_pixel = bpp;
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001043 } else if (par->fpWidth && par->fpHeight) {
1044 char buf[16];
1045
1046 memset(buf, 0, 16);
Antonino A. Daplas948a95f2005-09-09 13:09:59 -07001047 snprintf(buf, 15, "%dx%dMR", par->fpWidth, par->fpHeight);
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001048 fb_find_mode(&nvidiafb_default_var, info, buf, specs->modedb,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001049 specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 }
1051
1052 if (mode_option)
1053 fb_find_mode(&nvidiafb_default_var, info, mode_option,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001054 specs->modedb, specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055
1056 info->var = nvidiafb_default_var;
1057 info->fix.visual = (info->var.bits_per_pixel == 8) ?
1058 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
1059 info->pseudo_palette = par->pseudo_palette;
1060 fb_alloc_cmap(&info->cmap, 256, 0);
1061 fb_destroy_modedb(info->monspecs.modedb);
1062 info->monspecs.modedb = NULL;
1063
1064 /* maximize virtual vertical length */
1065 lpitch = info->var.xres_virtual *
1066 ((info->var.bits_per_pixel + 7) >> 3);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001067 info->var.yres_virtual = info->screen_size / lpitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
1069 info->pixmap.scan_align = 4;
1070 info->pixmap.buf_align = 4;
James Simmons58a60642005-06-21 17:17:08 -07001071 info->pixmap.access_align = 32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 info->pixmap.size = 8 * 1024;
1073 info->pixmap.flags = FB_PIXMAP_SYSTEM;
1074
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001075 if (!hwcur)
Antonino A. Daplasc465e052005-11-07 01:00:35 -08001076 info->fbops->fb_cursor = NULL;
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 info->var.accel_flags = (!noaccel);
1079
1080 switch (par->Architecture) {
1081 case NV_ARCH_04:
1082 info->fix.accel = FB_ACCEL_NV4;
1083 break;
1084 case NV_ARCH_10:
1085 info->fix.accel = FB_ACCEL_NV_10;
1086 break;
1087 case NV_ARCH_20:
1088 info->fix.accel = FB_ACCEL_NV_20;
1089 break;
1090 case NV_ARCH_30:
1091 info->fix.accel = FB_ACCEL_NV_30;
1092 break;
1093 case NV_ARCH_40:
1094 info->fix.accel = FB_ACCEL_NV_40;
1095 break;
1096 }
1097
1098 NVTRACE_LEAVE();
1099
1100 return nvidiafb_check_var(&info->var, info);
1101}
1102
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001103static u32 __devinit nvidia_get_chipset(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104{
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001105 struct nvidia_par *par = info->par;
1106 u32 id = (par->pci_dev->vendor << 16) | par->pci_dev->device;
1107
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001108 printk(KERN_INFO PFX "Device ID: %x \n", id);
1109
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001110 if ((id & 0xfff0) == 0x00f0) {
1111 /* pci-e */
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001112 id = NV_RD32(par->REGS, 0x1800);
1113
1114 if ((id & 0x0000ffff) == 0x000010DE)
1115 id = 0x10DE0000 | (id >> 16);
1116 else if ((id & 0xffff0000) == 0xDE100000) /* wrong endian */
1117 id = 0x10DE0000 | ((id << 8) & 0x0000ff00) |
1118 ((id >> 8) & 0x000000ff);
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001119 printk(KERN_INFO PFX "Subsystem ID: %x \n", id);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001120 }
1121
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001122 return id;
1123}
1124
1125static u32 __devinit nvidia_get_arch(struct fb_info *info)
1126{
1127 struct nvidia_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 u32 arch = 0;
1129
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001130 switch (par->Chipset & 0x0ff0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 case 0x0100: /* GeForce 256 */
1132 case 0x0110: /* GeForce2 MX */
1133 case 0x0150: /* GeForce2 */
1134 case 0x0170: /* GeForce4 MX */
1135 case 0x0180: /* GeForce4 MX (8x AGP) */
1136 case 0x01A0: /* nForce */
1137 case 0x01F0: /* nForce2 */
1138 arch = NV_ARCH_10;
1139 break;
1140 case 0x0200: /* GeForce3 */
1141 case 0x0250: /* GeForce4 Ti */
1142 case 0x0280: /* GeForce4 Ti (8x AGP) */
1143 arch = NV_ARCH_20;
1144 break;
1145 case 0x0300: /* GeForceFX 5800 */
1146 case 0x0310: /* GeForceFX 5600 */
1147 case 0x0320: /* GeForceFX 5200 */
1148 case 0x0330: /* GeForceFX 5900 */
1149 case 0x0340: /* GeForceFX 5700 */
1150 arch = NV_ARCH_30;
1151 break;
1152 case 0x0040:
1153 case 0x00C0:
1154 case 0x0120:
1155 case 0x0130:
1156 case 0x0140:
1157 case 0x0160:
1158 case 0x01D0:
1159 case 0x0090:
1160 case 0x0210:
1161 case 0x0220:
1162 case 0x0230:
Antonino A. Daplasfe610672006-06-26 00:26:38 -07001163 case 0x0240:
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001164 case 0x0290:
1165 case 0x0390:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 arch = NV_ARCH_40;
1167 break;
1168 case 0x0020: /* TNT, TNT2 */
1169 arch = NV_ARCH_04;
1170 break;
1171 default: /* unknown architecture */
1172 break;
1173 }
1174
1175 return arch;
1176}
1177
1178static int __devinit nvidiafb_probe(struct pci_dev *pd,
1179 const struct pci_device_id *ent)
1180{
1181 struct nvidia_par *par;
1182 struct fb_info *info;
1183 unsigned short cmd;
1184
1185
1186 NVTRACE_ENTER();
1187 assert(pd != NULL);
1188
1189 info = framebuffer_alloc(sizeof(struct nvidia_par), &pd->dev);
1190
1191 if (!info)
1192 goto err_out;
1193
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001194 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 par->pci_dev = pd;
1196
1197 info->pixmap.addr = kmalloc(8 * 1024, GFP_KERNEL);
1198
1199 if (info->pixmap.addr == NULL)
1200 goto err_out_kfree;
1201
1202 memset(info->pixmap.addr, 0, 8 * 1024);
1203
1204 if (pci_enable_device(pd)) {
1205 printk(KERN_ERR PFX "cannot enable PCI device\n");
1206 goto err_out_enable;
1207 }
1208
1209 if (pci_request_regions(pd, "nvidiafb")) {
1210 printk(KERN_ERR PFX "cannot request PCI regions\n");
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001211 goto err_out_enable;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 }
1213
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 par->FlatPanel = flatpanel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 if (flatpanel == 1)
1216 printk(KERN_INFO PFX "flatpanel support enabled\n");
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001217 par->FPDither = fpdither;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
1219 par->CRTCnumber = forceCRTC;
1220 par->FpScale = (!noscale);
1221 par->paneltweak = paneltweak;
1222
1223 /* enable IO and mem if not already done */
1224 pci_read_config_word(pd, PCI_COMMAND, &cmd);
1225 cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
1226 pci_write_config_word(pd, PCI_COMMAND, cmd);
1227
1228 nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
1229 nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
1230 nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
1231
1232 par->REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
1233
1234 if (!par->REGS) {
1235 printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
1236 goto err_out_free_base0;
1237 }
1238
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001239 par->Chipset = nvidia_get_chipset(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001240 par->Architecture = nvidia_get_arch(info);
1241
1242 if (par->Architecture == 0) {
1243 printk(KERN_ERR PFX "unknown NV_ARCH\n");
1244 goto err_out_arch;
1245 }
1246
1247 sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
1248
Antonino A. Daplas918799a2006-01-09 20:53:40 -08001249 if (NVCommonSetup(info))
1250 goto err_out_arch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251
1252 par->FbAddress = nvidiafb_fix.smem_start;
1253 par->FbMapSize = par->RamAmountKBytes * 1024;
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001254 if (vram && vram * 1024 * 1024 < par->FbMapSize)
1255 par->FbMapSize = vram * 1024 * 1024;
1256
1257 /* Limit amount of vram to 64 MB */
1258 if (par->FbMapSize > 64 * 1024 * 1024)
1259 par->FbMapSize = 64 * 1024 * 1024;
1260
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001261 if(par->Architecture >= NV_ARCH_40)
1262 par->FbUsableSize = par->FbMapSize - (560 * 1024);
1263 else
1264 par->FbUsableSize = par->FbMapSize - (128 * 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 par->ScratchBufferSize = (par->Architecture < NV_ARCH_10) ? 8 * 1024 :
1266 16 * 1024;
1267 par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001268 par->CursorStart = par->FbUsableSize + (32 * 1024);
1269
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001271 info->screen_size = par->FbUsableSize;
1272 nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
1274 if (!info->screen_base) {
1275 printk(KERN_ERR PFX "cannot ioremap FB base\n");
1276 goto err_out_free_base1;
1277 }
1278
1279 par->FbStart = info->screen_base;
1280
1281#ifdef CONFIG_MTRR
1282 if (!nomtrr) {
1283 par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001284 par->RamAmountKBytes * 1024,
1285 MTRR_TYPE_WRCOMB, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 if (par->mtrr.vram < 0) {
1287 printk(KERN_ERR PFX "unable to setup MTRR\n");
1288 } else {
1289 par->mtrr.vram_valid = 1;
1290 /* let there be speed */
1291 printk(KERN_INFO PFX "MTRR set to ON\n");
1292 }
1293 }
1294#endif /* CONFIG_MTRR */
1295
1296 info->fbops = &nvidia_fb_ops;
1297 info->fix = nvidiafb_fix;
1298
1299 if (nvidia_set_fbinfo(info) < 0) {
1300 printk(KERN_ERR PFX "error setting initial video mode\n");
1301 goto err_out_iounmap_fb;
1302 }
1303
1304 nvidia_save_vga(par, &par->SavedReg);
1305
1306 if (register_framebuffer(info) < 0) {
1307 printk(KERN_ERR PFX "error registering nVidia framebuffer\n");
1308 goto err_out_iounmap_fb;
1309 }
1310
1311 pci_set_drvdata(pd, info);
1312
1313 printk(KERN_INFO PFX
1314 "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
1315 info->fix.id,
1316 par->FbMapSize / (1024 * 1024), info->fix.smem_start);
Michael Hanselmann5474c122006-06-25 05:47:08 -07001317
1318 nvidia_bl_init(par);
1319
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