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