1
2
3
4
5
6
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/fb.h>
10#include <linux/console.h>
11#include <linux/pci.h>
12#include <linux/pci_ids.h>
13#include <linux/delay.h>
14#include <linux/string.h>
15
16#define PCI_DEVICE_ID_IBM_GXT4500P 0x21c
17#define PCI_DEVICE_ID_IBM_GXT6000P 0x170
18
19
20
21
22#define CFG_ENDIAN0 0x40
23
24
25#define STATUS 0x1000
26#define CTRL_REG0 0x1004
27#define CR0_HALT_DMA 0x4
28#define CR0_RASTER_RESET 0x8
29#define CR0_GEOM_RESET 0x10
30#define CR0_MEM_CTRLER_RESET 0x20
31
32
33#define FB_AB_CTRL 0x1100
34#define FB_CD_CTRL 0x1104
35#define FB_WID_CTRL 0x1108
36#define FB_Z_CTRL 0x110c
37#define FB_VGA_CTRL 0x1110
38#define REFRESH_AB_CTRL 0x1114
39#define REFRESH_CD_CTRL 0x1118
40#define FB_OVL_CTRL 0x111c
41#define FB_CTRL_TYPE 0x80000000
42#define FB_CTRL_WIDTH_MASK 0x007f0000
43#define FB_CTRL_WIDTH_SHIFT 16
44#define FB_CTRL_START_SEG_MASK 0x00003fff
45
46#define REFRESH_START 0x1098
47#define REFRESH_SIZE 0x109c
48
49
50#define DFA_FB_A 0x11e0
51#define DFA_FB_B 0x11e4
52#define DFA_FB_C 0x11e8
53#define DFA_FB_D 0x11ec
54#define DFA_FB_ENABLE 0x80000000
55#define DFA_FB_BASE_MASK 0x03f00000
56#define DFA_FB_STRIDE_1k 0x00000000
57#define DFA_FB_STRIDE_2k 0x00000010
58#define DFA_FB_STRIDE_4k 0x00000020
59#define DFA_PIX_8BIT 0x00000000
60#define DFA_PIX_16BIT_565 0x00000001
61#define DFA_PIX_16BIT_1555 0x00000002
62#define DFA_PIX_24BIT 0x00000004
63#define DFA_PIX_32BIT 0x00000005
64
65
66static const unsigned char pixsize[] = {
67 1, 2, 2, 2, 4, 4
68};
69
70
71#define DTG_CONTROL 0x1900
72#define DTG_CTL_SCREEN_REFRESH 2
73#define DTG_CTL_ENABLE 1
74#define DTG_HORIZ_EXTENT 0x1904
75#define DTG_HORIZ_DISPLAY 0x1908
76#define DTG_HSYNC_START 0x190c
77#define DTG_HSYNC_END 0x1910
78#define DTG_HSYNC_END_COMP 0x1914
79#define DTG_VERT_EXTENT 0x1918
80#define DTG_VERT_DISPLAY 0x191c
81#define DTG_VSYNC_START 0x1920
82#define DTG_VSYNC_END 0x1924
83#define DTG_VERT_SHORT 0x1928
84
85
86#define DISP_CTL 0x402c
87#define DISP_CTL_OFF 2
88#define SYNC_CTL 0x4034
89#define SYNC_CTL_SYNC_ON_RGB 1
90#define SYNC_CTL_SYNC_OFF 2
91#define SYNC_CTL_HSYNC_INV 8
92#define SYNC_CTL_VSYNC_INV 0x10
93#define SYNC_CTL_HSYNC_OFF 0x20
94#define SYNC_CTL_VSYNC_OFF 0x40
95
96#define PLL_M 0x4040
97#define PLL_N 0x4044
98#define PLL_POSTDIV 0x4048
99#define PLL_C 0x404c
100
101
102#define CURSOR_X 0x4078
103#define CURSOR_Y 0x407c
104#define CURSOR_HOTSPOT 0x4080
105#define CURSOR_MODE 0x4084
106#define CURSOR_MODE_OFF 0
107#define CURSOR_MODE_4BPP 1
108#define CURSOR_PIXMAP 0x5000
109#define CURSOR_CMAP 0x7400
110
111
112#define WAT_FMT 0x4100
113#define WAT_FMT_24BIT 0
114#define WAT_FMT_16BIT_565 1
115#define WAT_FMT_16BIT_1555 2
116#define WAT_FMT_32BIT 3
117#define WAT_FMT_8BIT_332 9
118#define WAT_FMT_8BIT 0xa
119#define WAT_FMT_NO_CMAP 4
120#define WAT_CMAP_OFFSET 0x4104
121#define WAT_CTRL 0x4108
122#define WAT_CTRL_SEL_B 1
123#define WAT_CTRL_NO_INC 2
124#define WAT_GAMMA_CTRL 0x410c
125#define WAT_GAMMA_DISABLE 1
126#define WAT_OVL_CTRL 0x430c
127
128
129static const unsigned char watfmt[] = {
130 WAT_FMT_8BIT, WAT_FMT_16BIT_565, WAT_FMT_16BIT_1555, 0,
131 WAT_FMT_24BIT, WAT_FMT_32BIT
132};
133
134
135#define CMAP 0x6000
136
137#define readreg(par, reg) readl((par)->regs + (reg))
138#define writereg(par, reg, val) writel((val), (par)->regs + (reg))
139
140struct gxt4500_par {
141 void __iomem *regs;
142
143 int pixfmt;
144
145
146 int refclk_ps;
147 int pll_m;
148 int pll_n;
149 int pll_pd1;
150 int pll_pd2;
151
152 u32 pseudo_palette[16];
153};
154
155
156static char *mode_option;
157
158
159static const struct fb_videomode defaultmode __devinitdata = {
160 .refresh = 60,
161 .xres = 1280,
162 .yres = 1024,
163 .pixclock = 9295,
164 .left_margin = 248,
165 .right_margin = 48,
166 .upper_margin = 38,
167 .lower_margin = 1,
168 .hsync_len = 112,
169 .vsync_len = 3,
170 .vmode = FB_VMODE_NONINTERLACED
171};
172
173
174enum gxt_cards {
175 GXT4500P,
176 GXT6000P
177};
178
179
180static const struct cardinfo {
181 int refclk_ps;
182 const char *cardname;
183} cardinfo[] = {
184 [GXT4500P] = { .refclk_ps = 9259, .cardname = "IBM GXT4500P" },
185 [GXT6000P] = { .refclk_ps = 40000, .cardname = "IBM GXT6000P" },
186};
187
188
189
190
191
192
193
194
195
196
197
198static const unsigned char mdivtab[] = {
199 0x3f, 0x00, 0x20, 0x10, 0x28, 0x14, 0x2a, 0x15, 0x0a,
200 0x25, 0x32, 0x19, 0x0c, 0x26, 0x13, 0x09, 0x04, 0x22, 0x11,
201 0x08, 0x24, 0x12, 0x29, 0x34, 0x1a, 0x2d, 0x36, 0x1b, 0x0d,
202 0x06, 0x23, 0x31, 0x38, 0x1c, 0x2e, 0x17, 0x0b, 0x05, 0x02,
203 0x21, 0x30, 0x18, 0x2c, 0x16, 0x2b, 0x35, 0x3a, 0x1d, 0x0e,
204 0x27, 0x33, 0x39, 0x3c, 0x1e, 0x2f, 0x37, 0x3b, 0x3d, 0x3e,
205 0x1f, 0x0f, 0x07, 0x03, 0x01,
206};
207
208static const unsigned char ndivtab[] = {
209 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0x78, 0xbc, 0x5e,
210 0x2f, 0x17, 0x0b, 0x85, 0xc2, 0xe1, 0x70, 0x38, 0x9c, 0x4e,
211 0xa7, 0xd3, 0xe9, 0xf4, 0xfa, 0xfd, 0xfe, 0x7f, 0xbf, 0xdf,
212 0xef, 0x77, 0x3b, 0x1d, 0x8e, 0xc7, 0xe3, 0x71, 0xb8, 0xdc,
213 0x6e, 0xb7, 0x5b, 0x2d, 0x16, 0x8b, 0xc5, 0xe2, 0xf1, 0xf8,
214 0xfc, 0x7e, 0x3f, 0x9f, 0xcf, 0x67, 0xb3, 0xd9, 0x6c, 0xb6,
215 0xdb, 0x6d, 0x36, 0x9b, 0x4d, 0x26, 0x13, 0x89, 0xc4, 0x62,
216 0xb1, 0xd8, 0xec, 0xf6, 0xfb, 0x7d, 0xbe, 0x5f, 0xaf, 0x57,
217 0x2b, 0x95, 0x4a, 0x25, 0x92, 0x49, 0xa4, 0x52, 0x29, 0x94,
218 0xca, 0x65, 0xb2, 0x59, 0x2c, 0x96, 0xcb, 0xe5, 0xf2, 0x79,
219 0x3c, 0x1e, 0x0f, 0x07, 0x83, 0x41, 0x20, 0x90, 0x48, 0x24,
220 0x12, 0x09, 0x84, 0x42, 0xa1, 0x50, 0x28, 0x14, 0x8a, 0x45,
221 0xa2, 0xd1, 0xe8, 0x74, 0xba, 0xdd, 0xee, 0xf7, 0x7b, 0x3d,
222 0x9e, 0x4f, 0x27, 0x93, 0xc9, 0xe4, 0x72, 0x39, 0x1c, 0x0e,
223 0x87, 0xc3, 0x61, 0x30, 0x18, 0x8c, 0xc6, 0x63, 0x31, 0x98,
224 0xcc, 0xe6, 0x73, 0xb9, 0x5c, 0x2e, 0x97, 0x4b, 0xa5, 0xd2,
225 0x69,
226};
227
228static int calc_pll(int period_ps, struct gxt4500_par *par)
229{
230 int m, n, pdiv1, pdiv2, postdiv;
231 int pll_period, best_error, t, intf;
232
233
234 if (period_ps < 3333 || period_ps > 200000)
235 return -1;
236
237 best_error = 1000000;
238 for (pdiv1 = 1; pdiv1 <= 8; ++pdiv1) {
239 for (pdiv2 = 1; pdiv2 <= pdiv1; ++pdiv2) {
240 postdiv = pdiv1 * pdiv2;
241 pll_period = DIV_ROUND_UP(period_ps, postdiv);
242
243 if (pll_period < 1666 || pll_period > 2857)
244 continue;
245 for (m = 1; m <= 64; ++m) {
246 intf = m * par->refclk_ps;
247 if (intf > 500000)
248 break;
249 n = intf * postdiv / period_ps;
250 if (n < 3 || n > 160)
251 continue;
252 t = par->refclk_ps * m * postdiv / n;
253 t -= period_ps;
254 if (t >= 0 && t < best_error) {
255 par->pll_m = m;
256 par->pll_n = n;
257 par->pll_pd1 = pdiv1;
258 par->pll_pd2 = pdiv2;
259 best_error = t;
260 }
261 }
262 }
263 }
264 if (best_error == 1000000)
265 return -1;
266 return 0;
267}
268
269static int calc_pixclock(struct gxt4500_par *par)
270{
271 return par->refclk_ps * par->pll_m * par->pll_pd1 * par->pll_pd2
272 / par->pll_n;
273}
274
275static int gxt4500_var_to_par(struct fb_var_screeninfo *var,
276 struct gxt4500_par *par)
277{
278 if (var->xres + var->xoffset > var->xres_virtual ||
279 var->yres + var->yoffset > var->yres_virtual ||
280 var->xres_virtual > 4096)
281 return -EINVAL;
282 if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
283 return -EINVAL;
284
285 if (calc_pll(var->pixclock, par) < 0)
286 return -EINVAL;
287
288 switch (var->bits_per_pixel) {
289 case 32:
290 if (var->transp.length)
291 par->pixfmt = DFA_PIX_32BIT;
292 else
293 par->pixfmt = DFA_PIX_24BIT;
294 break;
295 case 24:
296 par->pixfmt = DFA_PIX_24BIT;
297 break;
298 case 16:
299 if (var->green.length == 5)
300 par->pixfmt = DFA_PIX_16BIT_1555;
301 else
302 par->pixfmt = DFA_PIX_16BIT_565;
303 break;
304 case 8:
305 par->pixfmt = DFA_PIX_8BIT;
306 break;
307 default:
308 return -EINVAL;
309 }
310
311 return 0;
312}
313
314static const struct fb_bitfield eightbits = {0, 8};
315static const struct fb_bitfield nobits = {0, 0};
316
317static void gxt4500_unpack_pixfmt(struct fb_var_screeninfo *var,
318 int pixfmt)
319{
320 var->bits_per_pixel = pixsize[pixfmt] * 8;
321 var->red = eightbits;
322 var->green = eightbits;
323 var->blue = eightbits;
324 var->transp = nobits;
325
326 switch (pixfmt) {
327 case DFA_PIX_16BIT_565:
328 var->red.length = 5;
329 var->green.length = 6;
330 var->blue.length = 5;
331 break;
332 case DFA_PIX_16BIT_1555:
333 var->red.length = 5;
334 var->green.length = 5;
335 var->blue.length = 5;
336 var->transp.length = 1;
337 break;
338 case DFA_PIX_32BIT:
339 var->transp.length = 8;
340 break;
341 }
342 if (pixfmt != DFA_PIX_8BIT) {
343 var->green.offset = var->red.length;
344 var->blue.offset = var->green.offset + var->green.length;
345 if (var->transp.length)
346 var->transp.offset =
347 var->blue.offset + var->blue.length;
348 }
349}
350
351static int gxt4500_check_var(struct fb_var_screeninfo *var,
352 struct fb_info *info)
353{
354 struct gxt4500_par par;
355 int err;
356
357 par = *(struct gxt4500_par *)info->par;
358 err = gxt4500_var_to_par(var, &par);
359 if (!err) {
360 var->pixclock = calc_pixclock(&par);
361 gxt4500_unpack_pixfmt(var, par.pixfmt);
362 }
363 return err;
364}
365
366static int gxt4500_set_par(struct fb_info *info)
367{
368 struct gxt4500_par *par = info->par;
369 struct fb_var_screeninfo *var = &info->var;
370 int err;
371 u32 ctrlreg, tmp;
372 unsigned int dfa_ctl, pixfmt, stride;
373 unsigned int wid_tiles, i;
374 unsigned int prefetch_pix, htot;
375 struct gxt4500_par save_par;
376
377 save_par = *par;
378 err = gxt4500_var_to_par(var, par);
379 if (err) {
380 *par = save_par;
381 return err;
382 }
383
384
385 ctrlreg = readreg(par, DTG_CONTROL);
386 ctrlreg &= ~(DTG_CTL_ENABLE | DTG_CTL_SCREEN_REFRESH);
387 writereg(par, DTG_CONTROL, ctrlreg);
388
389
390 tmp = readreg(par, PLL_C) & ~0x7f;
391 if (par->pll_n < 38)
392 tmp |= 0x29;
393 if (par->pll_n < 69)
394 tmp |= 0x35;
395 else if (par->pll_n < 100)
396 tmp |= 0x76;
397 else
398 tmp |= 0x7e;
399 writereg(par, PLL_C, tmp);
400 writereg(par, PLL_M, mdivtab[par->pll_m - 1]);
401 writereg(par, PLL_N, ndivtab[par->pll_n - 2]);
402 tmp = ((8 - par->pll_pd2) << 3) | (8 - par->pll_pd1);
403 if (par->pll_pd1 == 8 || par->pll_pd2 == 8) {
404
405 writereg(par, PLL_POSTDIV, tmp | 0x9);
406 udelay(1);
407 }
408 writereg(par, PLL_POSTDIV, tmp);
409 msleep(20);
410
411
412 writereg(par, CURSOR_MODE, CURSOR_MODE_OFF);
413
414
415 writereg(par, CTRL_REG0, CR0_RASTER_RESET | (CR0_RASTER_RESET << 16));
416 udelay(10);
417 writereg(par, CTRL_REG0, CR0_RASTER_RESET << 16);
418
419
420 htot = var->xres + var->left_margin + var->right_margin +
421 var->hsync_len;
422 writereg(par, DTG_HORIZ_EXTENT, htot - 1);
423 writereg(par, DTG_HORIZ_DISPLAY, var->xres - 1);
424 writereg(par, DTG_HSYNC_START, var->xres + var->right_margin - 1);
425 writereg(par, DTG_HSYNC_END,
426 var->xres + var->right_margin + var->hsync_len - 1);
427 writereg(par, DTG_HSYNC_END_COMP,
428 var->xres + var->right_margin + var->hsync_len - 1);
429 writereg(par, DTG_VERT_EXTENT,
430 var->yres + var->upper_margin + var->lower_margin +
431 var->vsync_len - 1);
432 writereg(par, DTG_VERT_DISPLAY, var->yres - 1);
433 writereg(par, DTG_VSYNC_START, var->yres + var->lower_margin - 1);
434 writereg(par, DTG_VSYNC_END,
435 var->yres + var->lower_margin + var->vsync_len - 1);
436 prefetch_pix = 3300000 / var->pixclock;
437 if (prefetch_pix >= htot)
438 prefetch_pix = htot - 1;
439 writereg(par, DTG_VERT_SHORT, htot - prefetch_pix - 1);
440 ctrlreg |= DTG_CTL_ENABLE | DTG_CTL_SCREEN_REFRESH;
441 writereg(par, DTG_CONTROL, ctrlreg);
442
443
444 if (var->xres_virtual > 2048) {
445 stride = 4096;
446 dfa_ctl = DFA_FB_STRIDE_4k;
447 } else if (var->xres_virtual > 1024) {
448 stride = 2048;
449 dfa_ctl = DFA_FB_STRIDE_2k;
450 } else {
451 stride = 1024;
452 dfa_ctl = DFA_FB_STRIDE_1k;
453 }
454
455
456 wid_tiles = (var->xres_virtual + 63) >> 6;
457
458
459 writereg(par, FB_AB_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0);
460 writereg(par, REFRESH_AB_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0);
461 writereg(par, FB_CD_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0);
462 writereg(par, REFRESH_CD_CTRL, FB_CTRL_TYPE | (wid_tiles << 16) | 0);
463 writereg(par, REFRESH_START, (var->xoffset << 16) | var->yoffset);
464 writereg(par, REFRESH_SIZE, (var->xres << 16) | var->yres);
465
466
467
468 pixfmt = par->pixfmt;
469 dfa_ctl |= DFA_FB_ENABLE | pixfmt;
470 writereg(par, DFA_FB_A, dfa_ctl);
471
472
473
474
475
476
477 for (i = 0; i < 32; ++i) {
478 writereg(par, WAT_FMT + (i << 4), watfmt[pixfmt]);
479 writereg(par, WAT_CMAP_OFFSET + (i << 4), 0);
480 writereg(par, WAT_CTRL + (i << 4), 0);
481 writereg(par, WAT_GAMMA_CTRL + (i << 4), WAT_GAMMA_DISABLE);
482 }
483
484
485 ctrlreg = readreg(par, SYNC_CTL) &
486 ~(SYNC_CTL_SYNC_ON_RGB | SYNC_CTL_HSYNC_INV |
487 SYNC_CTL_VSYNC_INV);
488 if (var->sync & FB_SYNC_ON_GREEN)
489 ctrlreg |= SYNC_CTL_SYNC_ON_RGB;
490 if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
491 ctrlreg |= SYNC_CTL_HSYNC_INV;
492 if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
493 ctrlreg |= SYNC_CTL_VSYNC_INV;
494 writereg(par, SYNC_CTL, ctrlreg);
495
496 info->fix.line_length = stride * pixsize[pixfmt];
497 info->fix.visual = (pixfmt == DFA_PIX_8BIT)? FB_VISUAL_PSEUDOCOLOR:
498 FB_VISUAL_DIRECTCOLOR;
499
500 return 0;
501}
502
503static int gxt4500_setcolreg(unsigned int reg, unsigned int red,
504 unsigned int green, unsigned int blue,
505 unsigned int transp, struct fb_info *info)
506{
507 u32 cmap_entry;
508 struct gxt4500_par *par = info->par;
509
510 if (reg > 1023)
511 return 1;
512 cmap_entry = ((transp & 0xff00) << 16) | ((red & 0xff00) << 8) |
513 (green & 0xff00) | (blue >> 8);
514 writereg(par, CMAP + reg * 4, cmap_entry);
515
516 if (reg < 16 && par->pixfmt != DFA_PIX_8BIT) {
517 u32 *pal = info->pseudo_palette;
518 u32 val = reg;
519 switch (par->pixfmt) {
520 case DFA_PIX_16BIT_565:
521 val |= (reg << 11) | (reg << 6);
522 break;
523 case DFA_PIX_16BIT_1555:
524 val |= (reg << 10) | (reg << 5);
525 break;
526 case DFA_PIX_32BIT:
527 val |= (reg << 24);
528
529 case DFA_PIX_24BIT:
530 val |= (reg << 16) | (reg << 8);
531 break;
532 }
533 pal[reg] = val;
534 }
535
536 return 0;
537}
538
539static int gxt4500_pan_display(struct fb_var_screeninfo *var,
540 struct fb_info *info)
541{
542 struct gxt4500_par *par = info->par;
543
544 if (var->xoffset & 7)
545 return -EINVAL;
546 if (var->xoffset + var->xres > var->xres_virtual ||
547 var->yoffset + var->yres > var->yres_virtual)
548 return -EINVAL;
549
550 writereg(par, REFRESH_START, (var->xoffset << 16) | var->yoffset);
551 return 0;
552}
553
554static int gxt4500_blank(int blank, struct fb_info *info)
555{
556 struct gxt4500_par *par = info->par;
557 int ctrl, dctl;
558
559 ctrl = readreg(par, SYNC_CTL);
560 ctrl &= ~(SYNC_CTL_SYNC_OFF | SYNC_CTL_HSYNC_OFF | SYNC_CTL_VSYNC_OFF);
561 dctl = readreg(par, DISP_CTL);
562 dctl |= DISP_CTL_OFF;
563 switch (blank) {
564 case FB_BLANK_UNBLANK:
565 dctl &= ~DISP_CTL_OFF;
566 break;
567 case FB_BLANK_POWERDOWN:
568 ctrl |= SYNC_CTL_SYNC_OFF;
569 break;
570 case FB_BLANK_HSYNC_SUSPEND:
571 ctrl |= SYNC_CTL_HSYNC_OFF;
572 break;
573 case FB_BLANK_VSYNC_SUSPEND:
574 ctrl |= SYNC_CTL_VSYNC_OFF;
575 break;
576 default: ;
577 }
578 writereg(par, SYNC_CTL, ctrl);
579 writereg(par, DISP_CTL, dctl);
580
581 return 0;
582}
583
584static const struct fb_fix_screeninfo gxt4500_fix __devinitdata = {
585 .id = "IBM GXT4500P",
586 .type = FB_TYPE_PACKED_PIXELS,
587 .visual = FB_VISUAL_PSEUDOCOLOR,
588 .xpanstep = 8,
589 .ypanstep = 1,
590 .mmio_len = 0x20000,
591};
592
593static struct fb_ops gxt4500_ops = {
594 .owner = THIS_MODULE,
595 .fb_check_var = gxt4500_check_var,
596 .fb_set_par = gxt4500_set_par,
597 .fb_setcolreg = gxt4500_setcolreg,
598 .fb_pan_display = gxt4500_pan_display,
599 .fb_blank = gxt4500_blank,
600 .fb_fillrect = cfb_fillrect,
601 .fb_copyarea = cfb_copyarea,
602 .fb_imageblit = cfb_imageblit,
603};
604
605
606static int __devinit gxt4500_probe(struct pci_dev *pdev,
607 const struct pci_device_id *ent)
608{
609 int err;
610 unsigned long reg_phys, fb_phys;
611 struct gxt4500_par *par;
612 struct fb_info *info;
613 struct fb_var_screeninfo var;
614 enum gxt_cards cardtype;
615
616 err = pci_enable_device(pdev);
617 if (err) {
618 dev_err(&pdev->dev, "gxt4500: cannot enable PCI device: %d\n",
619 err);
620 return err;
621 }
622
623 reg_phys = pci_resource_start(pdev, 0);
624 if (!request_mem_region(reg_phys, pci_resource_len(pdev, 0),
625 "gxt4500 regs")) {
626 dev_err(&pdev->dev, "gxt4500: cannot get registers\n");
627 goto err_nodev;
628 }
629
630 fb_phys = pci_resource_start(pdev, 1);
631 if (!request_mem_region(fb_phys, pci_resource_len(pdev, 1),
632 "gxt4500 FB")) {
633 dev_err(&pdev->dev, "gxt4500: cannot get framebuffer\n");
634 goto err_free_regs;
635 }
636
637 info = framebuffer_alloc(sizeof(struct gxt4500_par), &pdev->dev);
638 if (!info) {
639 dev_err(&pdev->dev, "gxt4500: cannot alloc FB info record\n");
640 goto err_free_fb;
641 }
642 par = info->par;
643 cardtype = ent->driver_data;
644 par->refclk_ps = cardinfo[cardtype].refclk_ps;
645 info->fix = gxt4500_fix;
646 strlcpy(info->fix.id, cardinfo[cardtype].cardname,
647 sizeof(info->fix.id));
648 info->pseudo_palette = par->pseudo_palette;
649
650 info->fix.mmio_start = reg_phys;
651 par->regs = pci_ioremap_bar(pdev, 0);
652 if (!par->regs) {
653 dev_err(&pdev->dev, "gxt4500: cannot map registers\n");
654 goto err_free_all;
655 }
656
657 info->fix.smem_start = fb_phys;
658 info->fix.smem_len = pci_resource_len(pdev, 1);
659 info->screen_base = pci_ioremap_bar(pdev, 1);
660 if (!info->screen_base) {
661 dev_err(&pdev->dev, "gxt4500: cannot map framebuffer\n");
662 goto err_unmap_regs;
663 }
664
665 pci_set_drvdata(pdev, info);
666
667
668 pci_write_config_dword(pdev, CFG_ENDIAN0, 0x333300);
669
670 info->fbops = &gxt4500_ops;
671 info->flags = FBINFO_FLAG_DEFAULT;
672
673 err = fb_alloc_cmap(&info->cmap, 256, 0);
674 if (err) {
675 dev_err(&pdev->dev, "gxt4500: cannot allocate cmap\n");
676 goto err_unmap_all;
677 }
678
679 gxt4500_blank(FB_BLANK_UNBLANK, info);
680
681 if (!fb_find_mode(&var, info, mode_option, NULL, 0, &defaultmode, 8)) {
682 dev_err(&pdev->dev, "gxt4500: cannot find valid video mode\n");
683 goto err_free_cmap;
684 }
685 info->var = var;
686 if (gxt4500_set_par(info)) {
687 printk(KERN_ERR "gxt4500: cannot set video mode\n");
688 goto err_free_cmap;
689 }
690
691 if (register_framebuffer(info) < 0) {
692 dev_err(&pdev->dev, "gxt4500: cannot register framebuffer\n");
693 goto err_free_cmap;
694 }
695 printk(KERN_INFO "fb%d: %s frame buffer device\n",
696 info->node, info->fix.id);
697
698 return 0;
699
700 err_free_cmap:
701 fb_dealloc_cmap(&info->cmap);
702 err_unmap_all:
703 iounmap(info->screen_base);
704 err_unmap_regs:
705 iounmap(par->regs);
706 err_free_all:
707 framebuffer_release(info);
708 err_free_fb:
709 release_mem_region(fb_phys, pci_resource_len(pdev, 1));
710 err_free_regs:
711 release_mem_region(reg_phys, pci_resource_len(pdev, 0));
712 err_nodev:
713 return -ENODEV;
714}
715
716static void __devexit gxt4500_remove(struct pci_dev *pdev)
717{
718 struct fb_info *info = pci_get_drvdata(pdev);
719 struct gxt4500_par *par;
720
721 if (!info)
722 return;
723 par = info->par;
724 unregister_framebuffer(info);
725 fb_dealloc_cmap(&info->cmap);
726 iounmap(par->regs);
727 iounmap(info->screen_base);
728 release_mem_region(pci_resource_start(pdev, 0),
729 pci_resource_len(pdev, 0));
730 release_mem_region(pci_resource_start(pdev, 1),
731 pci_resource_len(pdev, 1));
732 framebuffer_release(info);
733}
734
735
736static const struct pci_device_id gxt4500_pci_tbl[] = {
737 { PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT4500P),
738 .driver_data = GXT4500P },
739 { PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_GXT6000P),
740 .driver_data = GXT6000P },
741 { 0 }
742};
743
744MODULE_DEVICE_TABLE(pci, gxt4500_pci_tbl);
745
746static struct pci_driver gxt4500_driver = {
747 .name = "gxt4500",
748 .id_table = gxt4500_pci_tbl,
749 .probe = gxt4500_probe,
750 .remove = __devexit_p(gxt4500_remove),
751};
752
753static int __devinit gxt4500_init(void)
754{
755#ifndef MODULE
756 if (fb_get_options("gxt4500", &mode_option))
757 return -ENODEV;
758#endif
759
760 return pci_register_driver(&gxt4500_driver);
761}
762module_init(gxt4500_init);
763
764static void __exit gxt4500_exit(void)
765{
766 pci_unregister_driver(&gxt4500_driver);
767}
768module_exit(gxt4500_exit);
769
770MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
771MODULE_DESCRIPTION("FBDev driver for IBM GXT4500P/6000P");
772MODULE_LICENSE("GPL");
773module_param(mode_option, charp, 0);
774MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\"");
775