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