diff options
Diffstat (limited to 'drivers/video/asiliantfb.c')
-rw-r--r-- | drivers/video/asiliantfb.c | 617 |
1 files changed, 617 insertions, 0 deletions
diff --git a/drivers/video/asiliantfb.c b/drivers/video/asiliantfb.c new file mode 100644 index 000000000000..f4729f4df8ce --- /dev/null +++ b/drivers/video/asiliantfb.c | |||
@@ -0,0 +1,617 @@ | |||
1 | /* | ||
2 | * drivers/video/asiliantfb.c | ||
3 | * frame buffer driver for Asiliant 69000 chip | ||
4 | * Copyright (C) 2001-2003 Saito.K & Jeanne | ||
5 | * | ||
6 | * from driver/video/chipsfb.c and, | ||
7 | * | ||
8 | * drivers/video/asiliantfb.c -- frame buffer device for | ||
9 | * Asiliant 69030 chip (formerly Intel, formerly Chips & Technologies) | ||
10 | * Author: apc@agelectronics.co.uk | ||
11 | * Copyright (C) 2000 AG Electronics | ||
12 | * Note: the data sheets don't seem to be available from Asiliant. | ||
13 | * They are available by searching developer.intel.com, but are not otherwise | ||
14 | * linked to. | ||
15 | * | ||
16 | * This driver should be portable with minimal effort to the 69000 display | ||
17 | * chip, and to the twin-display mode of the 69030. | ||
18 | * Contains code from Thomas Hhenleitner <th@visuelle-maschinen.de> (thanks) | ||
19 | * | ||
20 | * Derived from the CT65550 driver chipsfb.c: | ||
21 | * Copyright (C) 1998 Paul Mackerras | ||
22 | * ...which was derived from the Powermac "chips" driver: | ||
23 | * Copyright (C) 1997 Fabio Riccardi. | ||
24 | * And from the frame buffer device for Open Firmware-initialized devices: | ||
25 | * Copyright (C) 1997 Geert Uytterhoeven. | ||
26 | * | ||
27 | * This file is subject to the terms and conditions of the GNU General Public | ||
28 | * License. See the file COPYING in the main directory of this archive for | ||
29 | * more details. | ||
30 | */ | ||
31 | |||
32 | #include <linux/config.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/string.h> | ||
37 | #include <linux/mm.h> | ||
38 | #include <linux/tty.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/vmalloc.h> | ||
41 | #include <linux/delay.h> | ||
42 | #include <linux/interrupt.h> | ||
43 | #include <linux/fb.h> | ||
44 | #include <linux/init.h> | ||
45 | #include <linux/pci.h> | ||
46 | #include <asm/io.h> | ||
47 | |||
48 | /* Built in clock of the 69030 */ | ||
49 | static const unsigned Fref = 14318180; | ||
50 | |||
51 | #define mmio_base (p->screen_base + 0x400000) | ||
52 | |||
53 | #define mm_write_ind(num, val, ap, dp) do { \ | ||
54 | writeb((num), mmio_base + (ap)); writeb((val), mmio_base + (dp)); \ | ||
55 | } while (0) | ||
56 | |||
57 | static void mm_write_xr(struct fb_info *p, u8 reg, u8 data) | ||
58 | { | ||
59 | mm_write_ind(reg, data, 0x7ac, 0x7ad); | ||
60 | } | ||
61 | #define write_xr(num, val) mm_write_xr(p, num, val) | ||
62 | |||
63 | static void mm_write_fr(struct fb_info *p, u8 reg, u8 data) | ||
64 | { | ||
65 | mm_write_ind(reg, data, 0x7a0, 0x7a1); | ||
66 | } | ||
67 | #define write_fr(num, val) mm_write_fr(p, num, val) | ||
68 | |||
69 | static void mm_write_cr(struct fb_info *p, u8 reg, u8 data) | ||
70 | { | ||
71 | mm_write_ind(reg, data, 0x7a8, 0x7a9); | ||
72 | } | ||
73 | #define write_cr(num, val) mm_write_cr(p, num, val) | ||
74 | |||
75 | static void mm_write_gr(struct fb_info *p, u8 reg, u8 data) | ||
76 | { | ||
77 | mm_write_ind(reg, data, 0x79c, 0x79d); | ||
78 | } | ||
79 | #define write_gr(num, val) mm_write_gr(p, num, val) | ||
80 | |||
81 | static void mm_write_sr(struct fb_info *p, u8 reg, u8 data) | ||
82 | { | ||
83 | mm_write_ind(reg, data, 0x788, 0x789); | ||
84 | } | ||
85 | #define write_sr(num, val) mm_write_sr(p, num, val) | ||
86 | |||
87 | static void mm_write_ar(struct fb_info *p, u8 reg, u8 data) | ||
88 | { | ||
89 | readb(mmio_base + 0x7b4); | ||
90 | mm_write_ind(reg, data, 0x780, 0x780); | ||
91 | } | ||
92 | #define write_ar(num, val) mm_write_ar(p, num, val) | ||
93 | |||
94 | static int asiliantfb_pci_init(struct pci_dev *dp, const struct pci_device_id *); | ||
95 | static int asiliantfb_check_var(struct fb_var_screeninfo *var, | ||
96 | struct fb_info *info); | ||
97 | static int asiliantfb_set_par(struct fb_info *info); | ||
98 | static int asiliantfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
99 | u_int transp, struct fb_info *info); | ||
100 | |||
101 | static struct fb_ops asiliantfb_ops = { | ||
102 | .owner = THIS_MODULE, | ||
103 | .fb_check_var = asiliantfb_check_var, | ||
104 | .fb_set_par = asiliantfb_set_par, | ||
105 | .fb_setcolreg = asiliantfb_setcolreg, | ||
106 | .fb_fillrect = cfb_fillrect, | ||
107 | .fb_copyarea = cfb_copyarea, | ||
108 | .fb_imageblit = cfb_imageblit, | ||
109 | .fb_cursor = soft_cursor, | ||
110 | }; | ||
111 | |||
112 | /* Calculate the ratios for the dot clocks without using a single long long | ||
113 | * value */ | ||
114 | static void asiliant_calc_dclk2(u32 *ppixclock, u8 *dclk2_m, u8 *dclk2_n, u8 *dclk2_div) | ||
115 | { | ||
116 | unsigned pixclock = *ppixclock; | ||
117 | unsigned Ftarget = 1000000 * (1000000 / pixclock); | ||
118 | unsigned n; | ||
119 | unsigned best_error = 0xffffffff; | ||
120 | unsigned best_m = 0xffffffff, | ||
121 | best_n = 0xffffffff; | ||
122 | unsigned ratio; | ||
123 | unsigned remainder; | ||
124 | unsigned char divisor = 0; | ||
125 | |||
126 | /* Calculate the frequency required. This is hard enough. */ | ||
127 | ratio = 1000000 / pixclock; | ||
128 | remainder = 1000000 % pixclock; | ||
129 | Ftarget = 1000000 * ratio + (1000000 * remainder) / pixclock; | ||
130 | |||
131 | while (Ftarget < 100000000) { | ||
132 | divisor += 0x10; | ||
133 | Ftarget <<= 1; | ||
134 | } | ||
135 | |||
136 | ratio = Ftarget / Fref; | ||
137 | remainder = Ftarget % Fref; | ||
138 | |||
139 | /* This expresses the constraint that 150kHz <= Fref/n <= 5Mhz, | ||
140 | * together with 3 <= n <= 257. */ | ||
141 | for (n = 3; n <= 257; n++) { | ||
142 | unsigned m = n * ratio + (n * remainder) / Fref; | ||
143 | |||
144 | /* 3 <= m <= 257 */ | ||
145 | if (m >= 3 && m <= 257) { | ||
146 | unsigned new_error = ((Ftarget * n) - (Fref * m)) >= 0 ? | ||
147 | ((Ftarget * n) - (Fref * m)) : ((Fref * m) - (Ftarget * n)); | ||
148 | if (new_error < best_error) { | ||
149 | best_n = n; | ||
150 | best_m = m; | ||
151 | best_error = new_error; | ||
152 | } | ||
153 | } | ||
154 | /* But if VLD = 4, then 4m <= 1028 */ | ||
155 | else if (m <= 1028) { | ||
156 | /* remember there are still only 8-bits of precision in m, so | ||
157 | * avoid over-optimistic error calculations */ | ||
158 | unsigned new_error = ((Ftarget * n) - (Fref * (m & ~3))) >= 0 ? | ||
159 | ((Ftarget * n) - (Fref * (m & ~3))) : ((Fref * (m & ~3)) - (Ftarget * n)); | ||
160 | if (new_error < best_error) { | ||
161 | best_n = n; | ||
162 | best_m = m; | ||
163 | best_error = new_error; | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | if (best_m > 257) | ||
168 | best_m >>= 2; /* divide m by 4, and leave VCO loop divide at 4 */ | ||
169 | else | ||
170 | divisor |= 4; /* or set VCO loop divide to 1 */ | ||
171 | *dclk2_m = best_m - 2; | ||
172 | *dclk2_n = best_n - 2; | ||
173 | *dclk2_div = divisor; | ||
174 | *ppixclock = pixclock; | ||
175 | return; | ||
176 | } | ||
177 | |||
178 | static void asiliant_set_timing(struct fb_info *p) | ||
179 | { | ||
180 | unsigned hd = p->var.xres / 8; | ||
181 | unsigned hs = (p->var.xres + p->var.right_margin) / 8; | ||
182 | unsigned he = (p->var.xres + p->var.right_margin + p->var.hsync_len) / 8; | ||
183 | unsigned ht = (p->var.left_margin + p->var.xres + p->var.right_margin + p->var.hsync_len) / 8; | ||
184 | unsigned vd = p->var.yres; | ||
185 | unsigned vs = p->var.yres + p->var.lower_margin; | ||
186 | unsigned ve = p->var.yres + p->var.lower_margin + p->var.vsync_len; | ||
187 | unsigned vt = p->var.upper_margin + p->var.yres + p->var.lower_margin + p->var.vsync_len; | ||
188 | unsigned wd = (p->var.xres_virtual * ((p->var.bits_per_pixel+7)/8)) / 8; | ||
189 | |||
190 | if ((p->var.xres == 640) && (p->var.yres == 480) && (p->var.pixclock == 39722)) { | ||
191 | write_fr(0x01, 0x02); /* LCD */ | ||
192 | } else { | ||
193 | write_fr(0x01, 0x01); /* CRT */ | ||
194 | } | ||
195 | |||
196 | write_cr(0x11, (ve - 1) & 0x0f); | ||
197 | write_cr(0x00, (ht - 5) & 0xff); | ||
198 | write_cr(0x01, hd - 1); | ||
199 | write_cr(0x02, hd); | ||
200 | write_cr(0x03, ((ht - 1) & 0x1f) | 0x80); | ||
201 | write_cr(0x04, hs); | ||
202 | write_cr(0x05, (((ht - 1) & 0x20) <<2) | (he & 0x1f)); | ||
203 | write_cr(0x3c, (ht - 1) & 0xc0); | ||
204 | write_cr(0x06, (vt - 2) & 0xff); | ||
205 | write_cr(0x30, (vt - 2) >> 8); | ||
206 | write_cr(0x07, 0x00); | ||
207 | write_cr(0x08, 0x00); | ||
208 | write_cr(0x09, 0x00); | ||
209 | write_cr(0x10, (vs - 1) & 0xff); | ||
210 | write_cr(0x32, ((vs - 1) >> 8) & 0xf); | ||
211 | write_cr(0x11, ((ve - 1) & 0x0f) | 0x80); | ||
212 | write_cr(0x12, (vd - 1) & 0xff); | ||
213 | write_cr(0x31, ((vd - 1) & 0xf00) >> 8); | ||
214 | write_cr(0x13, wd & 0xff); | ||
215 | write_cr(0x41, (wd & 0xf00) >> 8); | ||
216 | write_cr(0x15, (vs - 1) & 0xff); | ||
217 | write_cr(0x33, ((vs - 1) >> 8) & 0xf); | ||
218 | write_cr(0x38, ((ht - 5) & 0x100) >> 8); | ||
219 | write_cr(0x16, (vt - 1) & 0xff); | ||
220 | write_cr(0x18, 0x00); | ||
221 | |||
222 | if (p->var.xres == 640) { | ||
223 | writeb(0xc7, mmio_base + 0x784); /* set misc output reg */ | ||
224 | } else { | ||
225 | writeb(0x07, mmio_base + 0x784); /* set misc output reg */ | ||
226 | } | ||
227 | } | ||
228 | |||
229 | static int asiliantfb_check_var(struct fb_var_screeninfo *var, | ||
230 | struct fb_info *p) | ||
231 | { | ||
232 | unsigned long Ftarget, ratio, remainder; | ||
233 | |||
234 | ratio = 1000000 / var->pixclock; | ||
235 | remainder = 1000000 % var->pixclock; | ||
236 | Ftarget = 1000000 * ratio + (1000000 * remainder) / var->pixclock; | ||
237 | |||
238 | /* First check the constraint that the maximum post-VCO divisor is 32, | ||
239 | * and the maximum Fvco is 220MHz */ | ||
240 | if (Ftarget > 220000000 || Ftarget < 3125000) { | ||
241 | printk(KERN_ERR "asiliantfb dotclock must be between 3.125 and 220MHz\n"); | ||
242 | return -ENXIO; | ||
243 | } | ||
244 | var->xres_virtual = var->xres; | ||
245 | var->yres_virtual = var->yres; | ||
246 | |||
247 | if (var->bits_per_pixel == 24) { | ||
248 | var->red.offset = 16; | ||
249 | var->green.offset = 8; | ||
250 | var->blue.offset = 0; | ||
251 | var->red.length = var->blue.length = var->green.length = 8; | ||
252 | } else if (var->bits_per_pixel == 16) { | ||
253 | switch (var->red.offset) { | ||
254 | case 11: | ||
255 | var->green.length = 6; | ||
256 | break; | ||
257 | case 10: | ||
258 | var->green.length = 5; | ||
259 | break; | ||
260 | default: | ||
261 | return -EINVAL; | ||
262 | } | ||
263 | var->green.offset = 5; | ||
264 | var->blue.offset = 0; | ||
265 | var->red.length = var->blue.length = 5; | ||
266 | } else if (var->bits_per_pixel == 8) { | ||
267 | var->red.offset = var->green.offset = var->blue.offset = 0; | ||
268 | var->red.length = var->green.length = var->blue.length = 8; | ||
269 | } | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int asiliantfb_set_par(struct fb_info *p) | ||
274 | { | ||
275 | u8 dclk2_m; /* Holds m-2 value for register */ | ||
276 | u8 dclk2_n; /* Holds n-2 value for register */ | ||
277 | u8 dclk2_div; /* Holds divisor bitmask */ | ||
278 | |||
279 | /* Set pixclock */ | ||
280 | asiliant_calc_dclk2(&p->var.pixclock, &dclk2_m, &dclk2_n, &dclk2_div); | ||
281 | |||
282 | /* Set color depth */ | ||
283 | if (p->var.bits_per_pixel == 24) { | ||
284 | write_xr(0x81, 0x16); /* 24 bit packed color mode */ | ||
285 | write_xr(0x82, 0x00); /* Disable palettes */ | ||
286 | write_xr(0x20, 0x20); /* 24 bit blitter mode */ | ||
287 | } else if (p->var.bits_per_pixel == 16) { | ||
288 | if (p->var.red.offset == 11) | ||
289 | write_xr(0x81, 0x15); /* 16 bit color mode */ | ||
290 | else | ||
291 | write_xr(0x81, 0x14); /* 15 bit color mode */ | ||
292 | write_xr(0x82, 0x00); /* Disable palettes */ | ||
293 | write_xr(0x20, 0x10); /* 16 bit blitter mode */ | ||
294 | } else if (p->var.bits_per_pixel == 8) { | ||
295 | write_xr(0x0a, 0x02); /* Linear */ | ||
296 | write_xr(0x81, 0x12); /* 8 bit color mode */ | ||
297 | write_xr(0x82, 0x00); /* Graphics gamma enable */ | ||
298 | write_xr(0x20, 0x00); /* 8 bit blitter mode */ | ||
299 | } | ||
300 | p->fix.line_length = p->var.xres * (p->var.bits_per_pixel >> 3); | ||
301 | p->fix.visual = (p->var.bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; | ||
302 | write_xr(0xc4, dclk2_m); | ||
303 | write_xr(0xc5, dclk2_n); | ||
304 | write_xr(0xc7, dclk2_div); | ||
305 | /* Set up the CR registers */ | ||
306 | asiliant_set_timing(p); | ||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static int asiliantfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
311 | u_int transp, struct fb_info *p) | ||
312 | { | ||
313 | if (regno > 255) | ||
314 | return 1; | ||
315 | red >>= 8; | ||
316 | green >>= 8; | ||
317 | blue >>= 8; | ||
318 | |||
319 | /* Set hardware palete */ | ||
320 | writeb(regno, mmio_base + 0x790); | ||
321 | udelay(1); | ||
322 | writeb(red, mmio_base + 0x791); | ||
323 | writeb(green, mmio_base + 0x791); | ||
324 | writeb(blue, mmio_base + 0x791); | ||
325 | |||
326 | switch(p->var.bits_per_pixel) { | ||
327 | case 15: | ||
328 | if (regno < 16) { | ||
329 | ((u32 *)(p->pseudo_palette))[regno] = | ||
330 | ((red & 0xf8) << 7) | | ||
331 | ((green & 0xf8) << 2) | | ||
332 | ((blue & 0xf8) >> 3); | ||
333 | } | ||
334 | break; | ||
335 | case 16: | ||
336 | if (regno < 16) { | ||
337 | ((u32 *)(p->pseudo_palette))[regno] = | ||
338 | ((red & 0xf8) << 8) | | ||
339 | ((green & 0xfc) << 3) | | ||
340 | ((blue & 0xf8) >> 3); | ||
341 | } | ||
342 | break; | ||
343 | case 24: | ||
344 | if (regno < 24) { | ||
345 | ((u32 *)(p->pseudo_palette))[regno] = | ||
346 | (red << 16) | | ||
347 | (green << 8) | | ||
348 | (blue); | ||
349 | } | ||
350 | break; | ||
351 | } | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | struct chips_init_reg { | ||
356 | unsigned char addr; | ||
357 | unsigned char data; | ||
358 | }; | ||
359 | |||
360 | #define N_ELTS(x) (sizeof(x) / sizeof(x[0])) | ||
361 | |||
362 | static struct chips_init_reg chips_init_sr[] = | ||
363 | { | ||
364 | {0x00, 0x03}, /* Reset register */ | ||
365 | {0x01, 0x01}, /* Clocking mode */ | ||
366 | {0x02, 0x0f}, /* Plane mask */ | ||
367 | {0x04, 0x0e} /* Memory mode */ | ||
368 | }; | ||
369 | |||
370 | static struct chips_init_reg chips_init_gr[] = | ||
371 | { | ||
372 | {0x03, 0x00}, /* Data rotate */ | ||
373 | {0x05, 0x00}, /* Graphics mode */ | ||
374 | {0x06, 0x01}, /* Miscellaneous */ | ||
375 | {0x08, 0x00} /* Bit mask */ | ||
376 | }; | ||
377 | |||
378 | static struct chips_init_reg chips_init_ar[] = | ||
379 | { | ||
380 | {0x10, 0x01}, /* Mode control */ | ||
381 | {0x11, 0x00}, /* Overscan */ | ||
382 | {0x12, 0x0f}, /* Memory plane enable */ | ||
383 | {0x13, 0x00} /* Horizontal pixel panning */ | ||
384 | }; | ||
385 | |||
386 | static struct chips_init_reg chips_init_cr[] = | ||
387 | { | ||
388 | {0x0c, 0x00}, /* Start address high */ | ||
389 | {0x0d, 0x00}, /* Start address low */ | ||
390 | {0x40, 0x00}, /* Extended Start Address */ | ||
391 | {0x41, 0x00}, /* Extended Start Address */ | ||
392 | {0x14, 0x00}, /* Underline location */ | ||
393 | {0x17, 0xe3}, /* CRT mode control */ | ||
394 | {0x70, 0x00} /* Interlace control */ | ||
395 | }; | ||
396 | |||
397 | |||
398 | static struct chips_init_reg chips_init_fr[] = | ||
399 | { | ||
400 | {0x01, 0x02}, | ||
401 | {0x03, 0x08}, | ||
402 | {0x08, 0xcc}, | ||
403 | {0x0a, 0x08}, | ||
404 | {0x18, 0x00}, | ||
405 | {0x1e, 0x80}, | ||
406 | {0x40, 0x83}, | ||
407 | {0x41, 0x00}, | ||
408 | {0x48, 0x13}, | ||
409 | {0x4d, 0x60}, | ||
410 | {0x4e, 0x0f}, | ||
411 | |||
412 | {0x0b, 0x01}, | ||
413 | |||
414 | {0x21, 0x51}, | ||
415 | {0x22, 0x1d}, | ||
416 | {0x23, 0x5f}, | ||
417 | {0x20, 0x4f}, | ||
418 | {0x34, 0x00}, | ||
419 | {0x24, 0x51}, | ||
420 | {0x25, 0x00}, | ||
421 | {0x27, 0x0b}, | ||
422 | {0x26, 0x00}, | ||
423 | {0x37, 0x80}, | ||
424 | {0x33, 0x0b}, | ||
425 | {0x35, 0x11}, | ||
426 | {0x36, 0x02}, | ||
427 | {0x31, 0xea}, | ||
428 | {0x32, 0x0c}, | ||
429 | {0x30, 0xdf}, | ||
430 | {0x10, 0x0c}, | ||
431 | {0x11, 0xe0}, | ||
432 | {0x12, 0x50}, | ||
433 | {0x13, 0x00}, | ||
434 | {0x16, 0x03}, | ||
435 | {0x17, 0xbd}, | ||
436 | {0x1a, 0x00}, | ||
437 | }; | ||
438 | |||
439 | |||
440 | static struct chips_init_reg chips_init_xr[] = | ||
441 | { | ||
442 | {0xce, 0x00}, /* set default memory clock */ | ||
443 | {0xcc, 200 }, /* MCLK ratio M */ | ||
444 | {0xcd, 18 }, /* MCLK ratio N */ | ||
445 | {0xce, 0x90}, /* MCLK divisor = 2 */ | ||
446 | |||
447 | {0xc4, 209 }, | ||
448 | {0xc5, 118 }, | ||
449 | {0xc7, 32 }, | ||
450 | {0xcf, 0x06}, | ||
451 | {0x09, 0x01}, /* IO Control - CRT controller extensions */ | ||
452 | {0x0a, 0x02}, /* Frame buffer mapping */ | ||
453 | {0x0b, 0x01}, /* PCI burst write */ | ||
454 | {0x40, 0x03}, /* Memory access control */ | ||
455 | {0x80, 0x82}, /* Pixel pipeline configuration 0 */ | ||
456 | {0x81, 0x12}, /* Pixel pipeline configuration 1 */ | ||
457 | {0x82, 0x08}, /* Pixel pipeline configuration 2 */ | ||
458 | |||
459 | {0xd0, 0x0f}, | ||
460 | {0xd1, 0x01}, | ||
461 | }; | ||
462 | |||
463 | static void __devinit chips_hw_init(struct fb_info *p) | ||
464 | { | ||
465 | int i; | ||
466 | |||
467 | for (i = 0; i < N_ELTS(chips_init_xr); ++i) | ||
468 | write_xr(chips_init_xr[i].addr, chips_init_xr[i].data); | ||
469 | write_xr(0x81, 0x12); | ||
470 | write_xr(0x82, 0x08); | ||
471 | write_xr(0x20, 0x00); | ||
472 | for (i = 0; i < N_ELTS(chips_init_sr); ++i) | ||
473 | write_sr(chips_init_sr[i].addr, chips_init_sr[i].data); | ||
474 | for (i = 0; i < N_ELTS(chips_init_gr); ++i) | ||
475 | write_gr(chips_init_gr[i].addr, chips_init_gr[i].data); | ||
476 | for (i = 0; i < N_ELTS(chips_init_ar); ++i) | ||
477 | write_ar(chips_init_ar[i].addr, chips_init_ar[i].data); | ||
478 | /* Enable video output in attribute index register */ | ||
479 | writeb(0x20, mmio_base + 0x780); | ||
480 | for (i = 0; i < N_ELTS(chips_init_cr); ++i) | ||
481 | write_cr(chips_init_cr[i].addr, chips_init_cr[i].data); | ||
482 | for (i = 0; i < N_ELTS(chips_init_fr); ++i) | ||
483 | write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); | ||
484 | } | ||
485 | |||
486 | static struct fb_fix_screeninfo asiliantfb_fix __devinitdata = { | ||
487 | .id = "Asiliant 69000", | ||
488 | .type = FB_TYPE_PACKED_PIXELS, | ||
489 | .visual = FB_VISUAL_PSEUDOCOLOR, | ||
490 | .accel = FB_ACCEL_NONE, | ||
491 | .line_length = 640, | ||
492 | .smem_len = 0x200000, /* 2MB */ | ||
493 | }; | ||
494 | |||
495 | static struct fb_var_screeninfo asiliantfb_var __devinitdata = { | ||
496 | .xres = 640, | ||
497 | .yres = 480, | ||
498 | .xres_virtual = 640, | ||
499 | .yres_virtual = 480, | ||
500 | .bits_per_pixel = 8, | ||
501 | .red = { .length = 8 }, | ||
502 | .green = { .length = 8 }, | ||
503 | .blue = { .length = 8 }, | ||
504 | .height = -1, | ||
505 | .width = -1, | ||
506 | .vmode = FB_VMODE_NONINTERLACED, | ||
507 | .pixclock = 39722, | ||
508 | .left_margin = 48, | ||
509 | .right_margin = 16, | ||
510 | .upper_margin = 33, | ||
511 | .lower_margin = 10, | ||
512 | .hsync_len = 96, | ||
513 | .vsync_len = 2, | ||
514 | }; | ||
515 | |||
516 | static void __devinit init_asiliant(struct fb_info *p, unsigned long addr) | ||
517 | { | ||
518 | p->fix = asiliantfb_fix; | ||
519 | p->fix.smem_start = addr; | ||
520 | p->var = asiliantfb_var; | ||
521 | p->fbops = &asiliantfb_ops; | ||
522 | p->flags = FBINFO_DEFAULT; | ||
523 | |||
524 | fb_alloc_cmap(&p->cmap, 256, 0); | ||
525 | |||
526 | if (register_framebuffer(p) < 0) { | ||
527 | printk(KERN_ERR "C&T 69000 framebuffer failed to register\n"); | ||
528 | return; | ||
529 | } | ||
530 | |||
531 | printk(KERN_INFO "fb%d: Asiliant 69000 frame buffer (%dK RAM detected)\n", | ||
532 | p->node, p->fix.smem_len / 1024); | ||
533 | |||
534 | writeb(0xff, mmio_base + 0x78c); | ||
535 | chips_hw_init(p); | ||
536 | } | ||
537 | |||
538 | static int __devinit | ||
539 | asiliantfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) | ||
540 | { | ||
541 | unsigned long addr, size; | ||
542 | struct fb_info *p; | ||
543 | |||
544 | if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) | ||
545 | return -ENODEV; | ||
546 | addr = pci_resource_start(dp, 0); | ||
547 | size = pci_resource_len(dp, 0); | ||
548 | if (addr == 0) | ||
549 | return -ENODEV; | ||
550 | if (!request_mem_region(addr, size, "asiliantfb")) | ||
551 | return -EBUSY; | ||
552 | |||
553 | p = framebuffer_alloc(sizeof(u32) * 256, &dp->dev); | ||
554 | if (!p) { | ||
555 | release_mem_region(addr, size); | ||
556 | return -ENOMEM; | ||
557 | } | ||
558 | p->pseudo_palette = p->par; | ||
559 | p->par = NULL; | ||
560 | |||
561 | p->screen_base = ioremap(addr, 0x800000); | ||
562 | if (p->screen_base == NULL) { | ||
563 | release_mem_region(addr, size); | ||
564 | framebuffer_release(p); | ||
565 | return -ENOMEM; | ||
566 | } | ||
567 | |||
568 | pci_write_config_dword(dp, 4, 0x02800083); | ||
569 | writeb(3, p->screen_base + 0x400784); | ||
570 | |||
571 | init_asiliant(p, addr); | ||
572 | |||
573 | pci_set_drvdata(dp, p); | ||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | static void __devexit asiliantfb_remove(struct pci_dev *dp) | ||
578 | { | ||
579 | struct fb_info *p = pci_get_drvdata(dp); | ||
580 | |||
581 | unregister_framebuffer(p); | ||
582 | iounmap(p->screen_base); | ||
583 | release_mem_region(pci_resource_start(dp, 0), pci_resource_len(dp, 0)); | ||
584 | pci_set_drvdata(dp, NULL); | ||
585 | framebuffer_release(p); | ||
586 | } | ||
587 | |||
588 | static struct pci_device_id asiliantfb_pci_tbl[] __devinitdata = { | ||
589 | { PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_69000, PCI_ANY_ID, PCI_ANY_ID }, | ||
590 | { 0 } | ||
591 | }; | ||
592 | |||
593 | MODULE_DEVICE_TABLE(pci, asiliantfb_pci_tbl); | ||
594 | |||
595 | static struct pci_driver asiliantfb_driver = { | ||
596 | .name = "asiliantfb", | ||
597 | .id_table = asiliantfb_pci_tbl, | ||
598 | .probe = asiliantfb_pci_init, | ||
599 | .remove = __devexit_p(asiliantfb_remove), | ||
600 | }; | ||
601 | |||
602 | static int __init asiliantfb_init(void) | ||
603 | { | ||
604 | if (fb_get_options("asiliantfb", NULL)) | ||
605 | return -ENODEV; | ||
606 | |||
607 | return pci_register_driver(&asiliantfb_driver); | ||
608 | } | ||
609 | |||
610 | module_init(asiliantfb_init); | ||
611 | |||
612 | static void __exit asiliantfb_exit(void) | ||
613 | { | ||
614 | pci_unregister_driver(&asiliantfb_driver); | ||
615 | } | ||
616 | |||
617 | MODULE_LICENSE("GPL"); | ||