blob: f85edf084da3e0fc3f54c34a7333a637bfabbe39 [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. Daplasb9b26962007-05-08 00:38:23 -0700203static void nvidia_screen_off(struct nvidia_par *par, int on)
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800204{
205 unsigned char tmp;
206
207 if (on) {
208 /*
209 * Turn off screen and disable sequencer.
210 */
211 tmp = NVReadSeq(par, 0x01);
212
213 NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */
214 NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */
215 } else {
216 /*
217 * Reenable sequencer, then turn on screen.
218 */
219
220 tmp = NVReadSeq(par, 0x01);
221
222 NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */
223 NVWriteSeq(par, 0x00, 0x03); /* End Reset */
224 }
225}
226
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227static void nvidia_save_vga(struct nvidia_par *par,
228 struct _riva_hw_state *state)
229{
230 int i;
231
232 NVTRACE_ENTER();
233 NVLockUnlock(par, 0);
234
235 NVUnloadStateExt(par, state);
236
237 state->misc_output = NVReadMiscOut(par);
238
239 for (i = 0; i < NUM_CRT_REGS; i++)
240 state->crtc[i] = NVReadCrtc(par, i);
241
242 for (i = 0; i < NUM_ATC_REGS; i++)
243 state->attr[i] = NVReadAttr(par, i);
244
245 for (i = 0; i < NUM_GRC_REGS; i++)
246 state->gra[i] = NVReadGr(par, i);
247
248 for (i = 0; i < NUM_SEQ_REGS; i++)
249 state->seq[i] = NVReadSeq(par, i);
250 NVTRACE_LEAVE();
251}
252
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800253#undef DUMP_REG
254
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800255static void nvidia_write_regs(struct nvidia_par *par,
256 struct _riva_hw_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 int i;
259
260 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
262 NVLoadStateExt(par, state);
263
264 NVWriteMiscOut(par, state->misc_output);
265
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800266 for (i = 1; i < NUM_SEQ_REGS; i++) {
267#ifdef DUMP_REG
268 printk(" SEQ[%02x] = %08x\n", i, state->seq[i]);
269#endif
270 NVWriteSeq(par, i, state->seq[i]);
271 }
272
273 /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 of CRTC[17] */
274 NVWriteCrtc(par, 0x11, state->crtc[0x11] & ~0x80);
275
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 for (i = 0; i < NUM_CRT_REGS; i++) {
277 switch (i) {
278 case 0x19:
279 case 0x20 ... 0x40:
280 break;
281 default:
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800282#ifdef DUMP_REG
283 printk("CRTC[%02x] = %08x\n", i, state->crtc[i]);
284#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 NVWriteCrtc(par, i, state->crtc[i]);
286 }
287 }
288
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800289 for (i = 0; i < NUM_GRC_REGS; i++) {
290#ifdef DUMP_REG
291 printk(" GRA[%02x] = %08x\n", i, state->gra[i]);
292#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 NVWriteGr(par, i, state->gra[i]);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800294 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800296 for (i = 0; i < NUM_ATC_REGS; i++) {
297#ifdef DUMP_REG
298 printk("ATTR[%02x] = %08x\n", i, state->attr[i]);
299#endif
300 NVWriteAttr(par, i, state->attr[i]);
301 }
302
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 NVTRACE_LEAVE();
304}
305
306static int nvidia_calc_regs(struct fb_info *info)
307{
308 struct nvidia_par *par = info->par;
309 struct _riva_hw_state *state = &par->ModeReg;
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700310 int i, depth = fb_get_color_depth(&info->var, &info->fix);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 int h_display = info->var.xres / 8 - 1;
312 int h_start = (info->var.xres + info->var.right_margin) / 8 - 1;
313 int h_end = (info->var.xres + info->var.right_margin +
314 info->var.hsync_len) / 8 - 1;
315 int h_total = (info->var.xres + info->var.right_margin +
316 info->var.hsync_len + info->var.left_margin) / 8 - 5;
317 int h_blank_s = h_display;
318 int h_blank_e = h_total + 4;
319 int v_display = info->var.yres - 1;
320 int v_start = info->var.yres + info->var.lower_margin - 1;
321 int v_end = (info->var.yres + info->var.lower_margin +
322 info->var.vsync_len) - 1;
323 int v_total = (info->var.yres + info->var.lower_margin +
324 info->var.vsync_len + info->var.upper_margin) - 2;
325 int v_blank_s = v_display;
326 int v_blank_e = v_total + 1;
327
328 /*
329 * Set all CRTC values.
330 */
331
332 if (info->var.vmode & FB_VMODE_INTERLACED)
333 v_total |= 1;
334
335 if (par->FlatPanel == 1) {
336 v_start = v_total - 3;
337 v_end = v_total - 2;
338 v_blank_s = v_start;
339 h_start = h_total - 5;
340 h_end = h_total - 2;
341 h_blank_e = h_total + 4;
342 }
343
344 state->crtc[0x0] = Set8Bits(h_total);
345 state->crtc[0x1] = Set8Bits(h_display);
346 state->crtc[0x2] = Set8Bits(h_blank_s);
347 state->crtc[0x3] = SetBitField(h_blank_e, 4: 0, 4:0)
348 | SetBit(7);
349 state->crtc[0x4] = Set8Bits(h_start);
350 state->crtc[0x5] = SetBitField(h_blank_e, 5: 5, 7:7)
351 | SetBitField(h_end, 4: 0, 4:0);
352 state->crtc[0x6] = SetBitField(v_total, 7: 0, 7:0);
353 state->crtc[0x7] = SetBitField(v_total, 8: 8, 0:0)
354 | SetBitField(v_display, 8: 8, 1:1)
355 | SetBitField(v_start, 8: 8, 2:2)
356 | SetBitField(v_blank_s, 8: 8, 3:3)
357 | SetBit(4)
358 | SetBitField(v_total, 9: 9, 5:5)
359 | SetBitField(v_display, 9: 9, 6:6)
360 | SetBitField(v_start, 9: 9, 7:7);
361 state->crtc[0x9] = SetBitField(v_blank_s, 9: 9, 5:5)
362 | SetBit(6)
363 | ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0x00);
364 state->crtc[0x10] = Set8Bits(v_start);
365 state->crtc[0x11] = SetBitField(v_end, 3: 0, 3:0) | SetBit(5);
366 state->crtc[0x12] = Set8Bits(v_display);
367 state->crtc[0x13] = ((info->var.xres_virtual / 8) *
368 (info->var.bits_per_pixel / 8));
369 state->crtc[0x15] = Set8Bits(v_blank_s);
370 state->crtc[0x16] = Set8Bits(v_blank_e);
371
372 state->attr[0x10] = 0x01;
373
374 if (par->Television)
375 state->attr[0x11] = 0x00;
376
377 state->screen = SetBitField(h_blank_e, 6: 6, 4:4)
378 | SetBitField(v_blank_s, 10: 10, 3:3)
379 | SetBitField(v_start, 10: 10, 2:2)
380 | SetBitField(v_display, 10: 10, 1:1)
381 | SetBitField(v_total, 10: 10, 0:0);
382
383 state->horiz = SetBitField(h_total, 8: 8, 0:0)
384 | SetBitField(h_display, 8: 8, 1:1)
385 | SetBitField(h_blank_s, 8: 8, 2:2)
386 | SetBitField(h_start, 8: 8, 3:3);
387
388 state->extra = SetBitField(v_total, 11: 11, 0:0)
389 | SetBitField(v_display, 11: 11, 2:2)
390 | SetBitField(v_start, 11: 11, 4:4)
391 | SetBitField(v_blank_s, 11: 11, 6:6);
392
393 if (info->var.vmode & FB_VMODE_INTERLACED) {
394 h_total = (h_total >> 1) & ~1;
395 state->interlace = Set8Bits(h_total);
396 state->horiz |= SetBitField(h_total, 8: 8, 4:4);
397 } else {
398 state->interlace = 0xff; /* interlace off */
399 }
400
401 /*
402 * Calculate the extended registers.
403 */
404
405 if (depth < 24)
406 i = depth;
407 else
408 i = 32;
409
410 if (par->Architecture >= NV_ARCH_10)
411 par->CURSOR = (volatile u32 __iomem *)(info->screen_base +
412 par->CursorStart);
413
414 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
415 state->misc_output &= ~0x40;
416 else
417 state->misc_output |= 0x40;
418 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
419 state->misc_output &= ~0x80;
420 else
421 state->misc_output |= 0x80;
422
423 NVCalcStateExt(par, state, i, info->var.xres_virtual,
424 info->var.xres, info->var.yres_virtual,
425 1000000000 / info->var.pixclock, info->var.vmode);
426
427 state->scale = NV_RD32(par->PRAMDAC, 0x00000848) & 0xfff000ff;
428 if (par->FlatPanel == 1) {
429 state->pixel |= (1 << 7);
430
431 if (!par->fpScaler || (par->fpWidth <= info->var.xres)
432 || (par->fpHeight <= info->var.yres)) {
433 state->scale |= (1 << 8);
434 }
435
436 if (!par->crtcSync_read) {
437 state->crtcSync = NV_RD32(par->PRAMDAC, 0x0828);
438 par->crtcSync_read = 1;
439 }
440
441 par->PanelTweak = nvidia_panel_tweak(par, state);
442 }
443
444 state->vpll = state->pll;
445 state->vpll2 = state->pll;
446 state->vpllB = state->pllB;
447 state->vpll2B = state->pllB;
448
449 VGA_WR08(par->PCIO, 0x03D4, 0x1C);
450 state->fifo = VGA_RD08(par->PCIO, 0x03D5) & ~(1<<5);
451
452 if (par->CRTCnumber) {
453 state->head = NV_RD32(par->PCRTC0, 0x00000860) & ~0x00001000;
454 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) | 0x00001000;
455 state->crtcOwner = 3;
456 state->pllsel |= 0x20000800;
457 state->vpll = NV_RD32(par->PRAMDAC0, 0x00000508);
458 if (par->twoStagePLL)
459 state->vpllB = NV_RD32(par->PRAMDAC0, 0x00000578);
460 } else if (par->twoHeads) {
461 state->head = NV_RD32(par->PCRTC0, 0x00000860) | 0x00001000;
462 state->head2 = NV_RD32(par->PCRTC0, 0x00002860) & ~0x00001000;
463 state->crtcOwner = 0;
464 state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
465 if (par->twoStagePLL)
466 state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
467 }
468
469 state->cursorConfig = 0x00000100;
470
471 if (info->var.vmode & FB_VMODE_DOUBLE)
472 state->cursorConfig |= (1 << 4);
473
474 if (par->alphaCursor) {
475 if ((par->Chipset & 0x0ff0) != 0x0110)
476 state->cursorConfig |= 0x04011000;
477 else
478 state->cursorConfig |= 0x14011000;
479 state->general |= (1 << 29);
480 } else
481 state->cursorConfig |= 0x02000000;
482
483 if (par->twoHeads) {
484 if ((par->Chipset & 0x0ff0) == 0x0110) {
485 state->dither = NV_RD32(par->PRAMDAC, 0x0528) &
486 ~0x00010000;
487 if (par->FPDither)
488 state->dither |= 0x00010000;
489 } else {
490 state->dither = NV_RD32(par->PRAMDAC, 0x083C) & ~1;
491 if (par->FPDither)
492 state->dither |= 1;
493 }
494 }
495
496 state->timingH = 0;
497 state->timingV = 0;
498 state->displayV = info->var.xres;
499
500 return 0;
501}
502
503static void nvidia_init_vga(struct fb_info *info)
504{
505 struct nvidia_par *par = info->par;
506 struct _riva_hw_state *state = &par->ModeReg;
507 int i;
508
509 for (i = 0; i < 0x10; i++)
510 state->attr[i] = i;
511 state->attr[0x10] = 0x41;
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800512 state->attr[0x11] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 state->attr[0x12] = 0x0f;
514 state->attr[0x13] = 0x00;
515 state->attr[0x14] = 0x00;
516
517 memset(state->crtc, 0x00, NUM_CRT_REGS);
518 state->crtc[0x0a] = 0x20;
519 state->crtc[0x17] = 0xe3;
520 state->crtc[0x18] = 0xff;
521 state->crtc[0x28] = 0x40;
522
523 memset(state->gra, 0x00, NUM_GRC_REGS);
524 state->gra[0x05] = 0x40;
525 state->gra[0x06] = 0x05;
526 state->gra[0x07] = 0x0f;
527 state->gra[0x08] = 0xff;
528
529 state->seq[0x00] = 0x03;
530 state->seq[0x01] = 0x01;
531 state->seq[0x02] = 0x0f;
532 state->seq[0x03] = 0x00;
533 state->seq[0x04] = 0x0e;
534
535 state->misc_output = 0xeb;
536}
537
538static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
539{
540 struct nvidia_par *par = info->par;
541 u8 data[MAX_CURS * MAX_CURS / 8];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 int i, set = cursor->set;
James Simmonsf1ab5da2005-06-21 17:17:07 -0700543 u16 fg, bg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544
Antonino A. Daplas7a482422005-09-21 07:30:21 +0800545 if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
James Simmonsf1ab5da2005-06-21 17:17:07 -0700546 return -ENXIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
548 NVShowHideCursor(par, 0);
549
550 if (par->cursor_reset) {
551 set = FB_CUR_SETALL;
552 par->cursor_reset = 0;
553 }
554
555 if (set & FB_CUR_SETSIZE)
556 memset_io(par->CURSOR, 0, MAX_CURS * MAX_CURS * 2);
557
558 if (set & FB_CUR_SETPOS) {
559 u32 xx, yy, temp;
560
561 yy = cursor->image.dy - info->var.yoffset;
562 xx = cursor->image.dx - info->var.xoffset;
563 temp = xx & 0xFFFF;
564 temp |= yy << 16;
565
566 NV_WR32(par->PRAMDAC, 0x0000300, temp);
567 }
568
569 if (set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
570 u32 bg_idx = cursor->image.bg_color;
571 u32 fg_idx = cursor->image.fg_color;
572 u32 s_pitch = (cursor->image.width + 7) >> 3;
573 u32 d_pitch = MAX_CURS / 8;
574 u8 *dat = (u8 *) cursor->image.data;
575 u8 *msk = (u8 *) cursor->mask;
576 u8 *src;
577
578 src = kmalloc(s_pitch * cursor->image.height, GFP_ATOMIC);
579
580 if (src) {
581 switch (cursor->rop) {
582 case ROP_XOR:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700583 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 src[i] = dat[i] ^ msk[i];
585 break;
586 case ROP_COPY:
587 default:
James Simmonsf1ab5da2005-06-21 17:17:07 -0700588 for (i = 0; i < s_pitch * cursor->image.height; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 src[i] = dat[i] & msk[i];
590 break;
591 }
592
James Simmonsf1ab5da2005-06-21 17:17:07 -0700593 fb_pad_aligned_buffer(data, d_pitch, src, s_pitch,
594 cursor->image.height);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
596 bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
597 ((info->cmap.green[bg_idx] & 0xf8) << 2) |
598 ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | 1 << 15;
599
600 fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
601 ((info->cmap.green[fg_idx] & 0xf8) << 2) |
602 ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
603
604 NVLockUnlock(par, 0);
605
606 nvidiafb_load_cursor_image(par, data, bg, fg,
607 cursor->image.width,
608 cursor->image.height);
609 kfree(src);
610 }
611 }
612
613 if (cursor->enable)
614 NVShowHideCursor(par, 1);
615
616 return 0;
617}
618
619static int nvidiafb_set_par(struct fb_info *info)
620{
621 struct nvidia_par *par = info->par;
622
623 NVTRACE_ENTER();
624
625 NVLockUnlock(par, 1);
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800626 if (!par->FlatPanel || !par->twoHeads)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 par->FPDither = 0;
628
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -0800629 if (par->FPDither < 0) {
630 if ((par->Chipset & 0x0ff0) == 0x0110)
631 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x0528)
632 & 0x00010000);
633 else
634 par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1);
635 printk(KERN_INFO PFX "Flat panel dithering %s\n",
636 par->FPDither ? "enabled" : "disabled");
637 }
638
Antonino A. Daplasb8c90942005-09-09 13:04:37 -0700639 info->fix.visual = (info->var.bits_per_pixel == 8) ?
640 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 nvidia_init_vga(info);
643 nvidia_calc_regs(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645 NVLockUnlock(par, 0);
646 if (par->twoHeads) {
647 VGA_WR08(par->PCIO, 0x03D4, 0x44);
648 VGA_WR08(par->PCIO, 0x03D5, par->ModeReg.crtcOwner);
649 NVLockUnlock(par, 0);
650 }
651
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700652 nvidia_screen_off(par, 1);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800653
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -0800654 nvidia_write_regs(par, &par->ModeReg);
655 NVSetStartAddress(par, 0);
Benjamin Herrenschmidt85f15032005-11-07 01:00:30 -0800656
657#if defined (__BIG_ENDIAN)
658 /* turn on LFB swapping */
659 {
660 unsigned char tmp;
661
662 VGA_WR08(par->PCIO, 0x3d4, 0x46);
663 tmp = VGA_RD08(par->PCIO, 0x3d5);
664 tmp |= (1 << 7);
665 VGA_WR08(par->PCIO, 0x3d5, tmp);
666 }
667#endif
668
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 info->fix.line_length = (info->var.xres_virtual *
670 info->var.bits_per_pixel) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 if (info->var.accel_flags) {
672 info->fbops->fb_imageblit = nvidiafb_imageblit;
673 info->fbops->fb_fillrect = nvidiafb_fillrect;
674 info->fbops->fb_copyarea = nvidiafb_copyarea;
675 info->fbops->fb_sync = nvidiafb_sync;
676 info->pixmap.scan_align = 4;
677 info->flags &= ~FBINFO_HWACCEL_DISABLED;
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
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700690 nvidia_screen_off(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
Antonino A. Daplasb9b26962007-05-08 00:38:23 -0700699 NVLockUnlock(par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 NVTRACE_LEAVE();
701 return 0;
702}
703
704static int nvidiafb_setcolreg(unsigned regno, unsigned red, unsigned green,
705 unsigned blue, unsigned transp,
706 struct fb_info *info)
707{
708 struct nvidia_par *par = info->par;
709 int i;
710
711 NVTRACE_ENTER();
712 if (regno >= (1 << info->var.green.length))
713 return -EINVAL;
714
715 if (info->var.grayscale) {
716 /* gray = 0.30*R + 0.59*G + 0.11*B */
717 red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
718 }
719
720 if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
721 ((u32 *) info->pseudo_palette)[regno] =
722 (regno << info->var.red.offset) |
723 (regno << info->var.green.offset) |
724 (regno << info->var.blue.offset);
725 }
726
727 switch (info->var.bits_per_pixel) {
728 case 8:
729 /* "transparent" stuff is completely ignored. */
730 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
731 break;
732 case 16:
733 if (info->var.green.length == 5) {
734 for (i = 0; i < 8; i++) {
735 nvidia_write_clut(par, regno * 8 + i, red >> 8,
736 green >> 8, blue >> 8);
737 }
738 } else {
739 u8 r, g, b;
740
741 if (regno < 32) {
742 for (i = 0; i < 8; i++) {
743 nvidia_write_clut(par, regno * 8 + i,
744 red >> 8, green >> 8,
745 blue >> 8);
746 }
747 }
748
749 nvidia_read_clut(par, regno * 4, &r, &g, &b);
750
751 for (i = 0; i < 4; i++)
752 nvidia_write_clut(par, regno * 4 + i, r,
753 green >> 8, b);
754 }
755 break;
756 case 32:
757 nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
758 break;
759 default:
760 /* do nothing */
761 break;
762 }
763
764 NVTRACE_LEAVE();
765 return 0;
766}
767
768static int nvidiafb_check_var(struct fb_var_screeninfo *var,
769 struct fb_info *info)
770{
771 struct nvidia_par *par = info->par;
772 int memlen, vramlen, mode_valid = 0;
773 int pitch, err = 0;
774
775 NVTRACE_ENTER();
776
777 var->transp.offset = 0;
778 var->transp.length = 0;
779
780 var->xres &= ~7;
781
782 if (var->bits_per_pixel <= 8)
783 var->bits_per_pixel = 8;
784 else if (var->bits_per_pixel <= 16)
785 var->bits_per_pixel = 16;
786 else
787 var->bits_per_pixel = 32;
788
789 switch (var->bits_per_pixel) {
790 case 8:
791 var->red.offset = 0;
792 var->red.length = 8;
793 var->green.offset = 0;
794 var->green.length = 8;
795 var->blue.offset = 0;
796 var->blue.length = 8;
797 var->transp.offset = 0;
798 var->transp.length = 0;
799 break;
800 case 16:
801 var->green.length = (var->green.length < 6) ? 5 : 6;
802 var->red.length = 5;
803 var->blue.length = 5;
804 var->transp.length = 6 - var->green.length;
805 var->blue.offset = 0;
806 var->green.offset = 5;
807 var->red.offset = 5 + var->green.length;
808 var->transp.offset = (5 + var->red.offset) & 15;
809 break;
810 case 32: /* RGBA 8888 */
811 var->red.offset = 16;
812 var->red.length = 8;
813 var->green.offset = 8;
814 var->green.length = 8;
815 var->blue.offset = 0;
816 var->blue.length = 8;
817 var->transp.length = 8;
818 var->transp.offset = 24;
819 break;
820 }
821
822 var->red.msb_right = 0;
823 var->green.msb_right = 0;
824 var->blue.msb_right = 0;
825 var->transp.msb_right = 0;
826
827 if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
828 !info->monspecs.dclkmax || !fb_validate_mode(var, info))
829 mode_valid = 1;
830
831 /* calculate modeline if supported by monitor */
832 if (!mode_valid && info->monspecs.gtf) {
833 if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
834 mode_valid = 1;
835 }
836
837 if (!mode_valid) {
Geert Uytterhoeven9791d762007-02-12 00:55:19 -0800838 const struct fb_videomode *mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
840 mode = fb_find_best_mode(var, &info->modelist);
841 if (mode) {
842 fb_videomode_to_var(var, mode);
843 mode_valid = 1;
844 }
845 }
846
847 if (!mode_valid && info->monspecs.modedb_len)
848 return -EINVAL;
849
850 if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres ||
851 par->fpHeight < var->yres))
852 return -EINVAL;
853
854 if (var->yres_virtual < var->yres)
855 var->yres_virtual = var->yres;
856
857 if (var->xres_virtual < var->xres)
858 var->xres_virtual = var->xres;
859
860 var->xres_virtual = (var->xres_virtual + 63) & ~63;
861
Antonino A. Daplas917bb072005-05-01 08:59:22 -0700862 vramlen = info->screen_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 pitch = ((var->xres_virtual * var->bits_per_pixel) + 7) / 8;
864 memlen = pitch * var->yres_virtual;
865
866 if (memlen > vramlen) {
867 var->yres_virtual = vramlen / pitch;
868
869 if (var->yres_virtual < var->yres) {
870 var->yres_virtual = var->yres;
871 var->xres_virtual = vramlen / var->yres_virtual;
872 var->xres_virtual /= var->bits_per_pixel / 8;
873 var->xres_virtual &= ~63;
874 pitch = (var->xres_virtual *
875 var->bits_per_pixel + 7) / 8;
876 memlen = pitch * var->yres;
877
878 if (var->xres_virtual < var->xres) {
879 printk("nvidiafb: required video memory, "
880 "%d bytes, for %dx%d-%d (virtual) "
881 "is out of range\n",
882 memlen, var->xres_virtual,
883 var->yres_virtual, var->bits_per_pixel);
884 err = -ENOMEM;
885 }
886 }
887 }
888
889 if (var->accel_flags) {
890 if (var->yres_virtual > 0x7fff)
891 var->yres_virtual = 0x7fff;
892 if (var->xres_virtual > 0x7fff)
893 var->xres_virtual = 0x7fff;
894 }
895
896 var->xres_virtual &= ~63;
897
898 NVTRACE_LEAVE();
899
900 return err;
901}
902
903static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
904 struct fb_info *info)
905{
906 struct nvidia_par *par = info->par;
907 u32 total;
908
Antonino A. Daplas3c8d61b2005-11-13 16:06:34 -0800909 total = var->yoffset * info->fix.line_length + var->xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
911 NVSetStartAddress(par, total);
912
913 return 0;
914}
915
916static int nvidiafb_blank(int blank, struct fb_info *info)
917{
918 struct nvidia_par *par = info->par;
919 unsigned char tmp, vesa;
920
921 tmp = NVReadSeq(par, 0x01) & ~0x20; /* screen on/off */
922 vesa = NVReadCrtc(par, 0x1a) & ~0xc0; /* sync on/off */
923
924 NVTRACE_ENTER();
925
926 if (blank)
927 tmp |= 0x20;
928
929 switch (blank) {
930 case FB_BLANK_UNBLANK:
931 case FB_BLANK_NORMAL:
932 break;
933 case FB_BLANK_VSYNC_SUSPEND:
934 vesa |= 0x80;
935 break;
936 case FB_BLANK_HSYNC_SUSPEND:
937 vesa |= 0x40;
938 break;
939 case FB_BLANK_POWERDOWN:
940 vesa |= 0xc0;
941 break;
942 }
943
944 NVWriteSeq(par, 0x01, tmp);
945 NVWriteCrtc(par, 0x1a, vesa);
946
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 NVTRACE_LEAVE();
948
949 return 0;
950}
951
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -0700952/*
953 * Because the VGA registers are not mapped linearly in its MMIO space,
954 * restrict VGA register saving and restore to x86 only, where legacy VGA IO
955 * access is legal. Consequently, we must also check if the device is the
956 * primary display.
957 */
958#ifdef CONFIG_X86
959static void save_vga_x86(struct nvidia_par *par)
960{
961 struct resource *res= &par->pci_dev->resource[PCI_ROM_RESOURCE];
962
963 if (res && res->flags & IORESOURCE_ROM_SHADOW) {
964 memset(&par->vgastate, 0, sizeof(par->vgastate));
965 par->vgastate.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS |
966 VGA_SAVE_CMAP;
967 save_vga(&par->vgastate);
968 }
969}
970
971static void restore_vga_x86(struct nvidia_par *par)
972{
973 struct resource *res= &par->pci_dev->resource[PCI_ROM_RESOURCE];
974
975 if (res && res->flags & IORESOURCE_ROM_SHADOW)
976 restore_vga(&par->vgastate);
977}
978#else
979#define save_vga_x86(x) do {} while (0)
980#define restore_vga_x86(x) do {} while (0)
981#endif /* X86 */
982
983static int nvidiafb_open(struct fb_info *info, int user)
984{
985 struct nvidia_par *par = info->par;
986
987 mutex_lock(&par->open_lock);
988
989 if (!par->open_count) {
990 save_vga_x86(par);
991 nvidia_save_vga(par, &par->initial_state);
992 }
993
994 par->open_count++;
995 mutex_unlock(&par->open_lock);
996 return 0;
997}
998
999static int nvidiafb_release(struct fb_info *info, int user)
1000{
1001 struct nvidia_par *par = info->par;
1002 int err = 0;
1003
1004 mutex_lock(&par->open_lock);
1005
1006 if (!par->open_count) {
1007 err = -EINVAL;
1008 goto done;
1009 }
1010
1011 if (par->open_count == 1) {
1012 nvidia_write_regs(par, &par->initial_state);
1013 restore_vga_x86(par);
1014 }
1015
1016 par->open_count--;
1017done:
1018 mutex_unlock(&par->open_lock);
Antonino A. Daplas2620c6e2007-05-08 00:39:42 -07001019 return err;
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -07001020}
1021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022static struct fb_ops nvidia_fb_ops = {
1023 .owner = THIS_MODULE,
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -07001024 .fb_open = nvidiafb_open,
1025 .fb_release = nvidiafb_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 .fb_check_var = nvidiafb_check_var,
1027 .fb_set_par = nvidiafb_set_par,
1028 .fb_setcolreg = nvidiafb_setcolreg,
1029 .fb_pan_display = nvidiafb_pan_display,
1030 .fb_blank = nvidiafb_blank,
1031 .fb_fillrect = nvidiafb_fillrect,
1032 .fb_copyarea = nvidiafb_copyarea,
1033 .fb_imageblit = nvidiafb_imageblit,
1034 .fb_cursor = nvidiafb_cursor,
1035 .fb_sync = nvidiafb_sync,
1036};
1037
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001038#ifdef CONFIG_PM
David Brownellc78a7c22006-08-14 23:11:06 -07001039static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001040{
1041 struct fb_info *info = pci_get_drvdata(dev);
1042 struct nvidia_par *par = info->par;
1043
David Brownellc78a7c22006-08-14 23:11:06 -07001044 if (mesg.event == PM_EVENT_PRETHAW)
1045 mesg.event = PM_EVENT_FREEZE;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001046 acquire_console_sem();
David Brownellc78a7c22006-08-14 23:11:06 -07001047 par->pm_state = mesg.event;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001048
David Brownellc78a7c22006-08-14 23:11:06 -07001049 if (mesg.event == PM_EVENT_SUSPEND) {
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001050 fb_set_suspend(info, 1);
1051 nvidiafb_blank(FB_BLANK_POWERDOWN, info);
1052 nvidia_write_regs(par, &par->SavedReg);
1053 pci_save_state(dev);
1054 pci_disable_device(dev);
David Brownellc78a7c22006-08-14 23:11:06 -07001055 pci_set_power_state(dev, pci_choose_state(dev, mesg));
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001056 }
David Brownellc78a7c22006-08-14 23:11:06 -07001057 dev->dev.power.power_state = mesg;
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001058
1059 release_console_sem();
1060 return 0;
1061}
1062
1063static int nvidiafb_resume(struct pci_dev *dev)
1064{
1065 struct fb_info *info = pci_get_drvdata(dev);
1066 struct nvidia_par *par = info->par;
1067
1068 acquire_console_sem();
1069 pci_set_power_state(dev, PCI_D0);
1070
1071 if (par->pm_state != PM_EVENT_FREEZE) {
1072 pci_restore_state(dev);
Antonino A. Daplas7b566b12006-10-03 01:14:53 -07001073
1074 if (pci_enable_device(dev))
1075 goto fail;
1076
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001077 pci_set_master(dev);
1078 }
1079
1080 par->pm_state = PM_EVENT_ON;
1081 nvidiafb_set_par(info);
1082 fb_set_suspend (info, 0);
1083 nvidiafb_blank(FB_BLANK_UNBLANK, info);
1084
Antonino A. Daplas7b566b12006-10-03 01:14:53 -07001085fail:
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001086 release_console_sem();
1087 return 0;
1088}
1089#else
1090#define nvidiafb_suspend NULL
1091#define nvidiafb_resume NULL
1092#endif
1093
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094static int __devinit nvidia_set_fbinfo(struct fb_info *info)
1095{
1096 struct fb_monspecs *specs = &info->monspecs;
1097 struct fb_videomode modedb;
1098 struct nvidia_par *par = info->par;
1099 int lpitch;
1100
1101 NVTRACE_ENTER();
1102 info->flags = FBINFO_DEFAULT
1103 | FBINFO_HWACCEL_IMAGEBLIT
1104 | FBINFO_HWACCEL_FILLRECT
1105 | FBINFO_HWACCEL_COPYAREA
1106 | FBINFO_HWACCEL_YPAN;
1107
1108 fb_videomode_to_modelist(info->monspecs.modedb,
1109 info->monspecs.modedb_len, &info->modelist);
1110 fb_var_to_videomode(&modedb, &nvidiafb_default_var);
1111
Antonino A. Daplasade91852006-01-09 20:53:39 -08001112 switch (bpp) {
1113 case 0 ... 8:
1114 bpp = 8;
1115 break;
1116 case 9 ... 16:
1117 bpp = 16;
1118 break;
1119 default:
1120 bpp = 32;
1121 break;
1122 }
1123
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 if (specs->modedb != NULL) {
Geert Uytterhoeven9791d762007-02-12 00:55:19 -08001125 const struct fb_videomode *mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126
Geert Uytterhoeven9791d762007-02-12 00:55:19 -08001127 mode = fb_find_best_display(specs, &info->modelist);
1128 fb_videomode_to_var(&nvidiafb_default_var, mode);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001129 nvidiafb_default_var.bits_per_pixel = bpp;
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001130 } else if (par->fpWidth && par->fpHeight) {
1131 char buf[16];
1132
1133 memset(buf, 0, 16);
Antonino A. Daplas948a95f2005-09-09 13:09:59 -07001134 snprintf(buf, 15, "%dx%dMR", par->fpWidth, par->fpHeight);
Antonino Daplasdb6778d2005-08-08 14:22:43 +08001135 fb_find_mode(&nvidiafb_default_var, info, buf, specs->modedb,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001136 specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 }
1138
1139 if (mode_option)
1140 fb_find_mode(&nvidiafb_default_var, info, mode_option,
Antonino A. Daplasade91852006-01-09 20:53:39 -08001141 specs->modedb, specs->modedb_len, &modedb, bpp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142
1143 info->var = nvidiafb_default_var;
1144 info->fix.visual = (info->var.bits_per_pixel == 8) ?
1145 FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
1146 info->pseudo_palette = par->pseudo_palette;
1147 fb_alloc_cmap(&info->cmap, 256, 0);
1148 fb_destroy_modedb(info->monspecs.modedb);
1149 info->monspecs.modedb = NULL;
1150
1151 /* maximize virtual vertical length */
1152 lpitch = info->var.xres_virtual *
1153 ((info->var.bits_per_pixel + 7) >> 3);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001154 info->var.yres_virtual = info->screen_size / lpitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155
1156 info->pixmap.scan_align = 4;
1157 info->pixmap.buf_align = 4;
James Simmons58a60642005-06-21 17:17:08 -07001158 info->pixmap.access_align = 32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 info->pixmap.size = 8 * 1024;
1160 info->pixmap.flags = FB_PIXMAP_SYSTEM;
1161
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001162 if (!hwcur)
Antonino A. Daplasc465e052005-11-07 01:00:35 -08001163 info->fbops->fb_cursor = NULL;
Antonino A. Daplas7a482422005-09-21 07:30:21 +08001164
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 info->var.accel_flags = (!noaccel);
1166
1167 switch (par->Architecture) {
1168 case NV_ARCH_04:
1169 info->fix.accel = FB_ACCEL_NV4;
1170 break;
1171 case NV_ARCH_10:
1172 info->fix.accel = FB_ACCEL_NV_10;
1173 break;
1174 case NV_ARCH_20:
1175 info->fix.accel = FB_ACCEL_NV_20;
1176 break;
1177 case NV_ARCH_30:
1178 info->fix.accel = FB_ACCEL_NV_30;
1179 break;
1180 case NV_ARCH_40:
1181 info->fix.accel = FB_ACCEL_NV_40;
1182 break;
1183 }
1184
1185 NVTRACE_LEAVE();
1186
1187 return nvidiafb_check_var(&info->var, info);
1188}
1189
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001190static u32 __devinit nvidia_get_chipset(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191{
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001192 struct nvidia_par *par = info->par;
1193 u32 id = (par->pci_dev->vendor << 16) | par->pci_dev->device;
1194
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001195 printk(KERN_INFO PFX "Device ID: %x \n", id);
1196
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001197 if ((id & 0xfff0) == 0x00f0) {
1198 /* pci-e */
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001199 id = NV_RD32(par->REGS, 0x1800);
1200
1201 if ((id & 0x0000ffff) == 0x000010DE)
1202 id = 0x10DE0000 | (id >> 16);
1203 else if ((id & 0xffff0000) == 0xDE100000) /* wrong endian */
1204 id = 0x10DE0000 | ((id << 8) & 0x0000ff00) |
1205 ((id >> 8) & 0x000000ff);
Antonino A. Daplas8eec4982006-06-26 00:26:30 -07001206 printk(KERN_INFO PFX "Subsystem ID: %x \n", id);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001207 }
1208
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001209 return id;
1210}
1211
1212static u32 __devinit nvidia_get_arch(struct fb_info *info)
1213{
1214 struct nvidia_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 u32 arch = 0;
1216
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001217 switch (par->Chipset & 0x0ff0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 case 0x0100: /* GeForce 256 */
1219 case 0x0110: /* GeForce2 MX */
1220 case 0x0150: /* GeForce2 */
1221 case 0x0170: /* GeForce4 MX */
1222 case 0x0180: /* GeForce4 MX (8x AGP) */
1223 case 0x01A0: /* nForce */
1224 case 0x01F0: /* nForce2 */
1225 arch = NV_ARCH_10;
1226 break;
1227 case 0x0200: /* GeForce3 */
1228 case 0x0250: /* GeForce4 Ti */
1229 case 0x0280: /* GeForce4 Ti (8x AGP) */
1230 arch = NV_ARCH_20;
1231 break;
1232 case 0x0300: /* GeForceFX 5800 */
1233 case 0x0310: /* GeForceFX 5600 */
1234 case 0x0320: /* GeForceFX 5200 */
1235 case 0x0330: /* GeForceFX 5900 */
1236 case 0x0340: /* GeForceFX 5700 */
1237 arch = NV_ARCH_30;
1238 break;
Wink Savillee40c6752006-11-10 12:27:52 -08001239 case 0x0040: /* GeForce 6800 */
1240 case 0x00C0: /* GeForce 6800 */
1241 case 0x0120: /* GeForce 6800 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 case 0x0130:
Wink Savillee40c6752006-11-10 12:27:52 -08001243 case 0x0140: /* GeForce 6600 */
1244 case 0x0160: /* GeForce 6200 */
1245 case 0x01D0: /* GeForce 7200, 7300, 7400 */
Michal Piotrowskibc0ca062007-05-09 02:35:34 -07001246 case 0x02E0: /* GeForce 7300 GT */
Wink Savillee40c6752006-11-10 12:27:52 -08001247 case 0x0090: /* GeForce 7800 */
1248 case 0x0210: /* GeForce 6800 */
1249 case 0x0220: /* GeForce 6200 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 case 0x0230:
Wink Savillee40c6752006-11-10 12:27:52 -08001251 case 0x0240: /* GeForce 6100 */
1252 case 0x0290: /* GeForce 7900 */
1253 case 0x0390: /* GeForce 7600 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 arch = NV_ARCH_40;
1255 break;
1256 case 0x0020: /* TNT, TNT2 */
1257 arch = NV_ARCH_04;
1258 break;
1259 default: /* unknown architecture */
1260 break;
1261 }
1262
1263 return arch;
1264}
1265
1266static int __devinit nvidiafb_probe(struct pci_dev *pd,
1267 const struct pci_device_id *ent)
1268{
1269 struct nvidia_par *par;
1270 struct fb_info *info;
1271 unsigned short cmd;
1272
1273
1274 NVTRACE_ENTER();
1275 assert(pd != NULL);
1276
1277 info = framebuffer_alloc(sizeof(struct nvidia_par), &pd->dev);
1278
1279 if (!info)
1280 goto err_out;
1281
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001282 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 par->pci_dev = pd;
Antonino A. Daplas7dfe50b2007-05-08 00:38:33 -07001284 mutex_init(&par->open_lock);
Jiri Slabyf5610b92007-02-12 00:55:12 -08001285 info->pixmap.addr = kzalloc(8 * 1024, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286
1287 if (info->pixmap.addr == NULL)
1288 goto err_out_kfree;
1289
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 if (pci_enable_device(pd)) {
1291 printk(KERN_ERR PFX "cannot enable PCI device\n");
1292 goto err_out_enable;
1293 }
1294
1295 if (pci_request_regions(pd, "nvidiafb")) {
1296 printk(KERN_ERR PFX "cannot request PCI regions\n");
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001297 goto err_out_enable;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 }
1299
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 par->FlatPanel = flatpanel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 if (flatpanel == 1)
1302 printk(KERN_INFO PFX "flatpanel support enabled\n");
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001303 par->FPDither = fpdither;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
1305 par->CRTCnumber = forceCRTC;
1306 par->FpScale = (!noscale);
1307 par->paneltweak = paneltweak;
1308
1309 /* enable IO and mem if not already done */
1310 pci_read_config_word(pd, PCI_COMMAND, &cmd);
1311 cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
1312 pci_write_config_word(pd, PCI_COMMAND, cmd);
1313
1314 nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
1315 nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
1316 nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
1317
1318 par->REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
1319
1320 if (!par->REGS) {
1321 printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
1322 goto err_out_free_base0;
1323 }
1324
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001325 par->Chipset = nvidia_get_chipset(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001326 par->Architecture = nvidia_get_arch(info);
1327
1328 if (par->Architecture == 0) {
1329 printk(KERN_ERR PFX "unknown NV_ARCH\n");
1330 goto err_out_arch;
1331 }
1332
1333 sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
1334
Antonino A. Daplas918799a2006-01-09 20:53:40 -08001335 if (NVCommonSetup(info))
1336 goto err_out_arch;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
1338 par->FbAddress = nvidiafb_fix.smem_start;
1339 par->FbMapSize = par->RamAmountKBytes * 1024;
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001340 if (vram && vram * 1024 * 1024 < par->FbMapSize)
1341 par->FbMapSize = vram * 1024 * 1024;
1342
1343 /* Limit amount of vram to 64 MB */
1344 if (par->FbMapSize > 64 * 1024 * 1024)
1345 par->FbMapSize = 64 * 1024 * 1024;
1346
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001347 if(par->Architecture >= NV_ARCH_40)
1348 par->FbUsableSize = par->FbMapSize - (560 * 1024);
1349 else
1350 par->FbUsableSize = par->FbMapSize - (128 * 1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 par->ScratchBufferSize = (par->Architecture < NV_ARCH_10) ? 8 * 1024 :
1352 16 * 1024;
1353 par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
Benjamin Herrenschmidt0137ecf2006-01-09 20:51:27 -08001354 par->CursorStart = par->FbUsableSize + (32 * 1024);
1355
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001357 info->screen_size = par->FbUsableSize;
1358 nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359
1360 if (!info->screen_base) {
1361 printk(KERN_ERR PFX "cannot ioremap FB base\n");
1362 goto err_out_free_base1;
1363 }
1364
1365 par->FbStart = info->screen_base;
1366
1367#ifdef CONFIG_MTRR
1368 if (!nomtrr) {
1369 par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001370 par->RamAmountKBytes * 1024,
1371 MTRR_TYPE_WRCOMB, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 if (par->mtrr.vram < 0) {
1373 printk(KERN_ERR PFX "unable to setup MTRR\n");
1374 } else {
1375 par->mtrr.vram_valid = 1;
1376 /* let there be speed */
1377 printk(KERN_INFO PFX "MTRR set to ON\n");
1378 }
1379 }
1380#endif /* CONFIG_MTRR */
1381
1382 info->fbops = &nvidia_fb_ops;
1383 info->fix = nvidiafb_fix;
1384
1385 if (nvidia_set_fbinfo(info) < 0) {
1386 printk(KERN_ERR PFX "error setting initial video mode\n");
1387 goto err_out_iounmap_fb;
1388 }
1389
1390 nvidia_save_vga(par, &par->SavedReg);
1391
Guido Guentherce38cac2006-07-30 03:04:21 -07001392 pci_set_drvdata(pd, info);
Richard Purdie202d4e62007-03-03 17:43:52 +00001393
1394 if (backlight)
1395 nvidia_bl_init(par);
1396
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 if (register_framebuffer(info) < 0) {
1398 printk(KERN_ERR PFX "error registering nVidia framebuffer\n");
1399 goto err_out_iounmap_fb;
1400 }
1401
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
1403 printk(KERN_INFO PFX
1404 "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
1405 info->fix.id,
1406 par->FbMapSize / (1024 * 1024), info->fix.smem_start);
Michael Hanselmann5474c122006-06-25 05:47:08 -07001407
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 NVTRACE_LEAVE();
1409 return 0;
1410
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001411err_out_iounmap_fb:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 iounmap(info->screen_base);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001413err_out_free_base1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 fb_destroy_modedb(info->monspecs.modedb);
1415 nvidia_delete_i2c_busses(par);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001416err_out_arch:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 iounmap(par->REGS);
Antonino A. Daplasa06630f2006-06-26 00:27:04 -07001418 err_out_free_base0:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 pci_release_regions(pd);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001420err_out_enable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 kfree(info->pixmap.addr);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001422err_out_kfree:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 framebuffer_release(info);
Antonino A. Daplasc549dc62006-01-09 20:53:33 -08001424err_out:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 return -ENODEV;
1426}
1427
Prarit Bhargava5e14ab82007-02-12 00:55:14 -08001428static void __devexit nvidiafb_remove(struct pci_dev *pd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429{
1430 struct fb_info *info = pci_get_drvdata(pd);
1431 struct nvidia_par *par = info->par;
1432
1433 NVTRACE_ENTER();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434
Richard Purdie37ce69a2007-02-10 14:10:33 +00001435 unregister_framebuffer(info);
1436
Michael Hanselmann5474c122006-06-25 05:47:08 -07001437 nvidia_bl_exit(par);
1438
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439#ifdef CONFIG_MTRR
1440 if (par->mtrr.vram_valid)
1441 mtrr_del(par->mtrr.vram, info->fix.smem_start,
1442 info->fix.smem_len);
1443#endif /* CONFIG_MTRR */
1444
1445 iounmap(info->screen_base);
1446 fb_destroy_modedb(info->monspecs.modedb);
1447 nvidia_delete_i2c_busses(par);
1448 iounmap(par->REGS);
1449 pci_release_regions(pd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 kfree(info->pixmap.addr);
1451 framebuffer_release(info);
1452 pci_set_drvdata(pd, NULL);
1453 NVTRACE_LEAVE();
1454}
1455
1456/* ------------------------------------------------------------------------- *
1457 *
1458 * initialization
1459 *
1460 * ------------------------------------------------------------------------- */
1461
1462#ifndef MODULE
1463static int __devinit nvidiafb_setup(char *options)
1464{
1465 char *this_opt;
1466
1467 NVTRACE_ENTER();
1468 if (!options || !*options)
1469 return 0;
1470
1471 while ((this_opt = strsep(&options, ",")) != NULL) {
1472 if (!strncmp(this_opt, "forceCRTC", 9)) {
1473 char *p;
1474
1475 p = this_opt + 9;
1476 if (!*p || !*(++p))
1477 continue;
1478 forceCRTC = *p - '0';
1479 if (forceCRTC < 0 || forceCRTC > 1)
1480 forceCRTC = -1;
1481 } else if (!strncmp(this_opt, "flatpanel", 9)) {
1482 flatpanel = 1;
1483 } else if (!strncmp(this_opt, "hwcur", 5)) {
1484 hwcur = 1;
1485 } else if (!strncmp(this_opt, "noaccel", 6)) {
1486 noaccel = 1;
1487 } else if (!strncmp(this_opt, "noscale", 7)) {
1488 noscale = 1;
1489 } else if (!strncmp(this_opt, "paneltweak:", 11)) {
1490 paneltweak = simple_strtoul(this_opt+11, NULL, 0);
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001491 } else if (!strncmp(this_opt, "vram:", 5)) {
1492 vram = simple_strtoul(this_opt+5, NULL, 0);
Richard Purdie202d4e62007-03-03 17:43:52 +00001493 } else if (!strncmp(this_opt, "backlight:", 10)) {
1494 backlight = simple_strtoul(this_opt+10, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495#ifdef CONFIG_MTRR
1496 } else if (!strncmp(this_opt, "nomtrr", 6)) {
1497 nomtrr = 1;
1498#endif
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001499 } else if (!strncmp(this_opt, "fpdither:", 9)) {
1500 fpdither = simple_strtol(this_opt+9, NULL, 0);
Antonino A. Daplasade91852006-01-09 20:53:39 -08001501 } else if (!strncmp(this_opt, "bpp:", 4)) {
1502 bpp = simple_strtoul(this_opt+4, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 } else
1504 mode_option = this_opt;
1505 }
1506 NVTRACE_LEAVE();
1507 return 0;
1508}
1509#endif /* !MODULE */
1510
1511static struct pci_driver nvidiafb_driver = {
1512 .name = "nvidiafb",
1513 .id_table = nvidiafb_pci_tbl,
Antonino A. Daplas7a07cd72006-03-27 01:17:22 -08001514 .probe = nvidiafb_probe,
1515 .suspend = nvidiafb_suspend,
1516 .resume = nvidiafb_resume,
Prarit Bhargava5e14ab82007-02-12 00:55:14 -08001517 .remove = __devexit_p(nvidiafb_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518};
1519
1520/* ------------------------------------------------------------------------- *
1521 *
1522 * modularization
1523 *
1524 * ------------------------------------------------------------------------- */
1525
1526static int __devinit nvidiafb_init(void)
1527{
1528#ifndef MODULE
1529 char *option = NULL;
1530
1531 if (fb_get_options("nvidiafb", &option))
1532 return -ENODEV;
1533 nvidiafb_setup(option);
1534#endif
1535 return pci_register_driver(&nvidiafb_driver);
1536}
1537
1538module_init(nvidiafb_init);
1539
1540#ifdef MODULE
1541static void __exit nvidiafb_exit(void)
1542{
1543 pci_unregister_driver(&nvidiafb_driver);
1544}
1545
1546module_exit(nvidiafb_exit);
1547
1548module_param(flatpanel, int, 0);
1549MODULE_PARM_DESC(flatpanel,
1550 "Enables experimental flat panel support for some chipsets. "
Benjamin Herrenschmidtb8c49ef2005-11-07 01:00:32 -08001551 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
1552module_param(fpdither, int, 0);
1553MODULE_PARM_DESC(fpdither,
1554 "Enables dithering of flat panel for 6 bits panels. "
1555 "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556module_param(hwcur, int, 0);
1557MODULE_PARM_DESC(hwcur,
1558 "Enables hardware cursor implementation. (0 or 1=enabled) "
1559 "(default=0)");
1560module_param(noaccel, int, 0);
1561MODULE_PARM_DESC(noaccel,
1562 "Disables hardware acceleration. (0 or 1=disable) "
1563 "(default=0)");
1564module_param(noscale, int, 0);
1565MODULE_PARM_DESC(noscale,
1566 "Disables screen scaleing. (0 or 1=disable) "
1567 "(default=0, do scaling)");
1568module_param(paneltweak, int, 0);
1569MODULE_PARM_DESC(paneltweak,
1570 "Tweak display settings for flatpanels. "
1571 "(default=0, no tweaks)");
1572module_param(forceCRTC, int, 0);
1573MODULE_PARM_DESC(forceCRTC,
1574 "Forces usage of a particular CRTC in case autodetection "
1575 "fails. (0 or 1) (default=autodetect)");
Antonino A. Daplas917bb072005-05-01 08:59:22 -07001576module_param(vram, int, 0);
1577MODULE_PARM_DESC(vram,
1578 "amount of framebuffer memory to remap in MiB"
1579 "(default=0 - remap entire memory)");
Antonino A. Daplasc439e342006-01-09 20:53:02 -08001580module_param(mode_option, charp, 0);
1581MODULE_PARM_DESC(mode_option, "Specify initial video mode");
Antonino A. Daplasade91852006-01-09 20:53:39 -08001582module_param(bpp, int, 0);
1583MODULE_PARM_DESC(bpp, "pixel width in bits"
1584 "(default=8)");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585#ifdef CONFIG_MTRR
1586module_param(nomtrr, bool, 0);
1587MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
1588 "(default=0)");
1589#endif
1590
1591MODULE_AUTHOR("Antonino Daplas");
1592MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
1593MODULE_LICENSE("GPL");
1594#endif /* MODULE */
1595