diff options
Diffstat (limited to 'drivers/video/ffb.c')
-rw-r--r-- | drivers/video/ffb.c | 1092 |
1 files changed, 1092 insertions, 0 deletions
diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c new file mode 100644 index 000000000000..10cd05059fe9 --- /dev/null +++ b/drivers/video/ffb.c | |||
@@ -0,0 +1,1092 @@ | |||
1 | /* ffb.c: Creator/Elite3D frame buffer driver | ||
2 | * | ||
3 | * Copyright (C) 2003 David S. Miller (davem@redhat.com) | ||
4 | * Copyright (C) 1997,1998,1999 Jakub Jelinek (jj@ultra.linux.cz) | ||
5 | * | ||
6 | * Driver layout based loosely on tgafb.c, see that file for credits. | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/string.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/fb.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/timer.h> | ||
19 | |||
20 | #include <asm/io.h> | ||
21 | #include <asm/upa.h> | ||
22 | #include <asm/oplib.h> | ||
23 | #include <asm/fbio.h> | ||
24 | |||
25 | #include "sbuslib.h" | ||
26 | |||
27 | /* | ||
28 | * Local functions. | ||
29 | */ | ||
30 | |||
31 | static int ffb_setcolreg(unsigned, unsigned, unsigned, unsigned, | ||
32 | unsigned, struct fb_info *); | ||
33 | static int ffb_blank(int, struct fb_info *); | ||
34 | static void ffb_init_fix(struct fb_info *); | ||
35 | |||
36 | static void ffb_imageblit(struct fb_info *, const struct fb_image *); | ||
37 | static void ffb_fillrect(struct fb_info *, const struct fb_fillrect *); | ||
38 | static void ffb_copyarea(struct fb_info *, const struct fb_copyarea *); | ||
39 | static int ffb_sync(struct fb_info *); | ||
40 | static int ffb_mmap(struct fb_info *, struct file *, struct vm_area_struct *); | ||
41 | static int ffb_ioctl(struct inode *, struct file *, unsigned int, | ||
42 | unsigned long, struct fb_info *); | ||
43 | static int ffb_pan_display(struct fb_var_screeninfo *, struct fb_info *); | ||
44 | |||
45 | /* | ||
46 | * Frame buffer operations | ||
47 | */ | ||
48 | |||
49 | static struct fb_ops ffb_ops = { | ||
50 | .owner = THIS_MODULE, | ||
51 | .fb_setcolreg = ffb_setcolreg, | ||
52 | .fb_blank = ffb_blank, | ||
53 | .fb_pan_display = ffb_pan_display, | ||
54 | .fb_fillrect = ffb_fillrect, | ||
55 | .fb_copyarea = ffb_copyarea, | ||
56 | .fb_imageblit = ffb_imageblit, | ||
57 | .fb_sync = ffb_sync, | ||
58 | .fb_mmap = ffb_mmap, | ||
59 | .fb_ioctl = ffb_ioctl, | ||
60 | |||
61 | /* XXX Use FFB hw cursor once fb cursor API is better understood... */ | ||
62 | .fb_cursor = soft_cursor, | ||
63 | }; | ||
64 | |||
65 | /* Register layout and definitions */ | ||
66 | #define FFB_SFB8R_VOFF 0x00000000 | ||
67 | #define FFB_SFB8G_VOFF 0x00400000 | ||
68 | #define FFB_SFB8B_VOFF 0x00800000 | ||
69 | #define FFB_SFB8X_VOFF 0x00c00000 | ||
70 | #define FFB_SFB32_VOFF 0x01000000 | ||
71 | #define FFB_SFB64_VOFF 0x02000000 | ||
72 | #define FFB_FBC_REGS_VOFF 0x04000000 | ||
73 | #define FFB_BM_FBC_REGS_VOFF 0x04002000 | ||
74 | #define FFB_DFB8R_VOFF 0x04004000 | ||
75 | #define FFB_DFB8G_VOFF 0x04404000 | ||
76 | #define FFB_DFB8B_VOFF 0x04804000 | ||
77 | #define FFB_DFB8X_VOFF 0x04c04000 | ||
78 | #define FFB_DFB24_VOFF 0x05004000 | ||
79 | #define FFB_DFB32_VOFF 0x06004000 | ||
80 | #define FFB_DFB422A_VOFF 0x07004000 /* DFB 422 mode write to A */ | ||
81 | #define FFB_DFB422AD_VOFF 0x07804000 /* DFB 422 mode with line doubling */ | ||
82 | #define FFB_DFB24B_VOFF 0x08004000 /* DFB 24bit mode write to B */ | ||
83 | #define FFB_DFB422B_VOFF 0x09004000 /* DFB 422 mode write to B */ | ||
84 | #define FFB_DFB422BD_VOFF 0x09804000 /* DFB 422 mode with line doubling */ | ||
85 | #define FFB_SFB16Z_VOFF 0x0a004000 /* 16bit mode Z planes */ | ||
86 | #define FFB_SFB8Z_VOFF 0x0a404000 /* 8bit mode Z planes */ | ||
87 | #define FFB_SFB422_VOFF 0x0ac04000 /* SFB 422 mode write to A/B */ | ||
88 | #define FFB_SFB422D_VOFF 0x0b404000 /* SFB 422 mode with line doubling */ | ||
89 | #define FFB_FBC_KREGS_VOFF 0x0bc04000 | ||
90 | #define FFB_DAC_VOFF 0x0bc06000 | ||
91 | #define FFB_PROM_VOFF 0x0bc08000 | ||
92 | #define FFB_EXP_VOFF 0x0bc18000 | ||
93 | |||
94 | #define FFB_SFB8R_POFF 0x04000000UL | ||
95 | #define FFB_SFB8G_POFF 0x04400000UL | ||
96 | #define FFB_SFB8B_POFF 0x04800000UL | ||
97 | #define FFB_SFB8X_POFF 0x04c00000UL | ||
98 | #define FFB_SFB32_POFF 0x05000000UL | ||
99 | #define FFB_SFB64_POFF 0x06000000UL | ||
100 | #define FFB_FBC_REGS_POFF 0x00600000UL | ||
101 | #define FFB_BM_FBC_REGS_POFF 0x00600000UL | ||
102 | #define FFB_DFB8R_POFF 0x01000000UL | ||
103 | #define FFB_DFB8G_POFF 0x01400000UL | ||
104 | #define FFB_DFB8B_POFF 0x01800000UL | ||
105 | #define FFB_DFB8X_POFF 0x01c00000UL | ||
106 | #define FFB_DFB24_POFF 0x02000000UL | ||
107 | #define FFB_DFB32_POFF 0x03000000UL | ||
108 | #define FFB_FBC_KREGS_POFF 0x00610000UL | ||
109 | #define FFB_DAC_POFF 0x00400000UL | ||
110 | #define FFB_PROM_POFF 0x00000000UL | ||
111 | #define FFB_EXP_POFF 0x00200000UL | ||
112 | #define FFB_DFB422A_POFF 0x09000000UL | ||
113 | #define FFB_DFB422AD_POFF 0x09800000UL | ||
114 | #define FFB_DFB24B_POFF 0x0a000000UL | ||
115 | #define FFB_DFB422B_POFF 0x0b000000UL | ||
116 | #define FFB_DFB422BD_POFF 0x0b800000UL | ||
117 | #define FFB_SFB16Z_POFF 0x0c800000UL | ||
118 | #define FFB_SFB8Z_POFF 0x0c000000UL | ||
119 | #define FFB_SFB422_POFF 0x0d000000UL | ||
120 | #define FFB_SFB422D_POFF 0x0d800000UL | ||
121 | |||
122 | /* Draw operations */ | ||
123 | #define FFB_DRAWOP_DOT 0x00 | ||
124 | #define FFB_DRAWOP_AADOT 0x01 | ||
125 | #define FFB_DRAWOP_BRLINECAP 0x02 | ||
126 | #define FFB_DRAWOP_BRLINEOPEN 0x03 | ||
127 | #define FFB_DRAWOP_DDLINE 0x04 | ||
128 | #define FFB_DRAWOP_AALINE 0x05 | ||
129 | #define FFB_DRAWOP_TRIANGLE 0x06 | ||
130 | #define FFB_DRAWOP_POLYGON 0x07 | ||
131 | #define FFB_DRAWOP_RECTANGLE 0x08 | ||
132 | #define FFB_DRAWOP_FASTFILL 0x09 | ||
133 | #define FFB_DRAWOP_BCOPY 0x0a | ||
134 | #define FFB_DRAWOP_VSCROLL 0x0b | ||
135 | |||
136 | /* Pixel processor control */ | ||
137 | /* Force WID */ | ||
138 | #define FFB_PPC_FW_DISABLE 0x800000 | ||
139 | #define FFB_PPC_FW_ENABLE 0xc00000 | ||
140 | /* Auxiliary clip */ | ||
141 | #define FFB_PPC_ACE_DISABLE 0x040000 | ||
142 | #define FFB_PPC_ACE_AUX_SUB 0x080000 | ||
143 | #define FFB_PPC_ACE_AUX_ADD 0x0c0000 | ||
144 | /* Depth cue */ | ||
145 | #define FFB_PPC_DCE_DISABLE 0x020000 | ||
146 | #define FFB_PPC_DCE_ENABLE 0x030000 | ||
147 | /* Alpha blend */ | ||
148 | #define FFB_PPC_ABE_DISABLE 0x008000 | ||
149 | #define FFB_PPC_ABE_ENABLE 0x00c000 | ||
150 | /* View clip */ | ||
151 | #define FFB_PPC_VCE_DISABLE 0x001000 | ||
152 | #define FFB_PPC_VCE_2D 0x002000 | ||
153 | #define FFB_PPC_VCE_3D 0x003000 | ||
154 | /* Area pattern */ | ||
155 | #define FFB_PPC_APE_DISABLE 0x000800 | ||
156 | #define FFB_PPC_APE_ENABLE 0x000c00 | ||
157 | /* Transparent background */ | ||
158 | #define FFB_PPC_TBE_OPAQUE 0x000200 | ||
159 | #define FFB_PPC_TBE_TRANSPARENT 0x000300 | ||
160 | /* Z source */ | ||
161 | #define FFB_PPC_ZS_VAR 0x000080 | ||
162 | #define FFB_PPC_ZS_CONST 0x0000c0 | ||
163 | /* Y source */ | ||
164 | #define FFB_PPC_YS_VAR 0x000020 | ||
165 | #define FFB_PPC_YS_CONST 0x000030 | ||
166 | /* X source */ | ||
167 | #define FFB_PPC_XS_WID 0x000004 | ||
168 | #define FFB_PPC_XS_VAR 0x000008 | ||
169 | #define FFB_PPC_XS_CONST 0x00000c | ||
170 | /* Color (BGR) source */ | ||
171 | #define FFB_PPC_CS_VAR 0x000002 | ||
172 | #define FFB_PPC_CS_CONST 0x000003 | ||
173 | |||
174 | #define FFB_ROP_NEW 0x83 | ||
175 | #define FFB_ROP_OLD 0x85 | ||
176 | #define FFB_ROP_NEW_XOR_OLD 0x86 | ||
177 | |||
178 | #define FFB_UCSR_FIFO_MASK 0x00000fff | ||
179 | #define FFB_UCSR_FB_BUSY 0x01000000 | ||
180 | #define FFB_UCSR_RP_BUSY 0x02000000 | ||
181 | #define FFB_UCSR_ALL_BUSY (FFB_UCSR_RP_BUSY|FFB_UCSR_FB_BUSY) | ||
182 | #define FFB_UCSR_READ_ERR 0x40000000 | ||
183 | #define FFB_UCSR_FIFO_OVFL 0x80000000 | ||
184 | #define FFB_UCSR_ALL_ERRORS (FFB_UCSR_READ_ERR|FFB_UCSR_FIFO_OVFL) | ||
185 | |||
186 | struct ffb_fbc { | ||
187 | /* Next vertex registers */ | ||
188 | u32 xxx1[3]; | ||
189 | volatile u32 alpha; | ||
190 | volatile u32 red; | ||
191 | volatile u32 green; | ||
192 | volatile u32 blue; | ||
193 | volatile u32 depth; | ||
194 | volatile u32 y; | ||
195 | volatile u32 x; | ||
196 | u32 xxx2[2]; | ||
197 | volatile u32 ryf; | ||
198 | volatile u32 rxf; | ||
199 | u32 xxx3[2]; | ||
200 | |||
201 | volatile u32 dmyf; | ||
202 | volatile u32 dmxf; | ||
203 | u32 xxx4[2]; | ||
204 | volatile u32 ebyi; | ||
205 | volatile u32 ebxi; | ||
206 | u32 xxx5[2]; | ||
207 | volatile u32 by; | ||
208 | volatile u32 bx; | ||
209 | u32 dy; | ||
210 | u32 dx; | ||
211 | volatile u32 bh; | ||
212 | volatile u32 bw; | ||
213 | u32 xxx6[2]; | ||
214 | |||
215 | u32 xxx7[32]; | ||
216 | |||
217 | /* Setup unit vertex state register */ | ||
218 | volatile u32 suvtx; | ||
219 | u32 xxx8[63]; | ||
220 | |||
221 | /* Control registers */ | ||
222 | volatile u32 ppc; | ||
223 | volatile u32 wid; | ||
224 | volatile u32 fg; | ||
225 | volatile u32 bg; | ||
226 | volatile u32 consty; | ||
227 | volatile u32 constz; | ||
228 | volatile u32 xclip; | ||
229 | volatile u32 dcss; | ||
230 | volatile u32 vclipmin; | ||
231 | volatile u32 vclipmax; | ||
232 | volatile u32 vclipzmin; | ||
233 | volatile u32 vclipzmax; | ||
234 | volatile u32 dcsf; | ||
235 | volatile u32 dcsb; | ||
236 | volatile u32 dczf; | ||
237 | volatile u32 dczb; | ||
238 | |||
239 | u32 xxx9; | ||
240 | volatile u32 blendc; | ||
241 | volatile u32 blendc1; | ||
242 | volatile u32 blendc2; | ||
243 | volatile u32 fbramitc; | ||
244 | volatile u32 fbc; | ||
245 | volatile u32 rop; | ||
246 | volatile u32 cmp; | ||
247 | volatile u32 matchab; | ||
248 | volatile u32 matchc; | ||
249 | volatile u32 magnab; | ||
250 | volatile u32 magnc; | ||
251 | volatile u32 fbcfg0; | ||
252 | volatile u32 fbcfg1; | ||
253 | volatile u32 fbcfg2; | ||
254 | volatile u32 fbcfg3; | ||
255 | |||
256 | u32 ppcfg; | ||
257 | volatile u32 pick; | ||
258 | volatile u32 fillmode; | ||
259 | volatile u32 fbramwac; | ||
260 | volatile u32 pmask; | ||
261 | volatile u32 xpmask; | ||
262 | volatile u32 ypmask; | ||
263 | volatile u32 zpmask; | ||
264 | volatile u32 clip0min; | ||
265 | volatile u32 clip0max; | ||
266 | volatile u32 clip1min; | ||
267 | volatile u32 clip1max; | ||
268 | volatile u32 clip2min; | ||
269 | volatile u32 clip2max; | ||
270 | volatile u32 clip3min; | ||
271 | volatile u32 clip3max; | ||
272 | |||
273 | /* New 3dRAM III support regs */ | ||
274 | volatile u32 rawblend2; | ||
275 | volatile u32 rawpreblend; | ||
276 | volatile u32 rawstencil; | ||
277 | volatile u32 rawstencilctl; | ||
278 | volatile u32 threedram1; | ||
279 | volatile u32 threedram2; | ||
280 | volatile u32 passin; | ||
281 | volatile u32 rawclrdepth; | ||
282 | volatile u32 rawpmask; | ||
283 | volatile u32 rawcsrc; | ||
284 | volatile u32 rawmatch; | ||
285 | volatile u32 rawmagn; | ||
286 | volatile u32 rawropblend; | ||
287 | volatile u32 rawcmp; | ||
288 | volatile u32 rawwac; | ||
289 | volatile u32 fbramid; | ||
290 | |||
291 | volatile u32 drawop; | ||
292 | u32 xxx10[2]; | ||
293 | volatile u32 fontlpat; | ||
294 | u32 xxx11; | ||
295 | volatile u32 fontxy; | ||
296 | volatile u32 fontw; | ||
297 | volatile u32 fontinc; | ||
298 | volatile u32 font; | ||
299 | u32 xxx12[3]; | ||
300 | volatile u32 blend2; | ||
301 | volatile u32 preblend; | ||
302 | volatile u32 stencil; | ||
303 | volatile u32 stencilctl; | ||
304 | |||
305 | u32 xxx13[4]; | ||
306 | volatile u32 dcss1; | ||
307 | volatile u32 dcss2; | ||
308 | volatile u32 dcss3; | ||
309 | volatile u32 widpmask; | ||
310 | volatile u32 dcs2; | ||
311 | volatile u32 dcs3; | ||
312 | volatile u32 dcs4; | ||
313 | u32 xxx14; | ||
314 | volatile u32 dcd2; | ||
315 | volatile u32 dcd3; | ||
316 | volatile u32 dcd4; | ||
317 | u32 xxx15; | ||
318 | |||
319 | volatile u32 pattern[32]; | ||
320 | |||
321 | u32 xxx16[256]; | ||
322 | |||
323 | volatile u32 devid; | ||
324 | u32 xxx17[63]; | ||
325 | |||
326 | volatile u32 ucsr; | ||
327 | u32 xxx18[31]; | ||
328 | |||
329 | volatile u32 mer; | ||
330 | }; | ||
331 | |||
332 | struct ffb_dac { | ||
333 | volatile u32 type; | ||
334 | volatile u32 value; | ||
335 | volatile u32 type2; | ||
336 | volatile u32 value2; | ||
337 | }; | ||
338 | |||
339 | struct ffb_par { | ||
340 | spinlock_t lock; | ||
341 | struct ffb_fbc *fbc; | ||
342 | struct ffb_dac *dac; | ||
343 | |||
344 | u32 flags; | ||
345 | #define FFB_FLAG_AFB 0x00000001 | ||
346 | #define FFB_FLAG_BLANKED 0x00000002 | ||
347 | |||
348 | u32 fg_cache __attribute__((aligned (8))); | ||
349 | u32 bg_cache; | ||
350 | u32 rop_cache; | ||
351 | |||
352 | int fifo_cache; | ||
353 | |||
354 | unsigned long physbase; | ||
355 | unsigned long fbsize; | ||
356 | |||
357 | char name[64]; | ||
358 | int prom_node; | ||
359 | int prom_parent_node; | ||
360 | int dac_rev; | ||
361 | int board_type; | ||
362 | struct list_head list; | ||
363 | }; | ||
364 | |||
365 | static void FFBFifo(struct ffb_par *par, int n) | ||
366 | { | ||
367 | struct ffb_fbc *fbc; | ||
368 | int cache = par->fifo_cache; | ||
369 | |||
370 | if (cache - n < 0) { | ||
371 | fbc = par->fbc; | ||
372 | do { cache = (upa_readl(&fbc->ucsr) & FFB_UCSR_FIFO_MASK) - 8; | ||
373 | } while (cache - n < 0); | ||
374 | } | ||
375 | par->fifo_cache = cache - n; | ||
376 | } | ||
377 | |||
378 | static void FFBWait(struct ffb_par *par) | ||
379 | { | ||
380 | struct ffb_fbc *fbc; | ||
381 | int limit = 10000; | ||
382 | |||
383 | fbc = par->fbc; | ||
384 | do { | ||
385 | if ((upa_readl(&fbc->ucsr) & FFB_UCSR_ALL_BUSY) == 0) | ||
386 | break; | ||
387 | if ((upa_readl(&fbc->ucsr) & FFB_UCSR_ALL_ERRORS) != 0) { | ||
388 | upa_writel(FFB_UCSR_ALL_ERRORS, &fbc->ucsr); | ||
389 | } | ||
390 | udelay(10); | ||
391 | } while(--limit > 0); | ||
392 | } | ||
393 | |||
394 | static int ffb_sync(struct fb_info *p) | ||
395 | { | ||
396 | struct ffb_par *par = (struct ffb_par *) p->par; | ||
397 | |||
398 | FFBWait(par); | ||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static __inline__ void ffb_rop(struct ffb_par *par, u32 rop) | ||
403 | { | ||
404 | if (par->rop_cache != rop) { | ||
405 | FFBFifo(par, 1); | ||
406 | upa_writel(rop, &par->fbc->rop); | ||
407 | par->rop_cache = rop; | ||
408 | } | ||
409 | } | ||
410 | |||
411 | static void ffb_switch_from_graph(struct ffb_par *par) | ||
412 | { | ||
413 | struct ffb_fbc *fbc = par->fbc; | ||
414 | struct ffb_dac *dac = par->dac; | ||
415 | unsigned long flags; | ||
416 | |||
417 | spin_lock_irqsave(&par->lock, flags); | ||
418 | FFBWait(par); | ||
419 | par->fifo_cache = 0; | ||
420 | FFBFifo(par, 7); | ||
421 | upa_writel(FFB_PPC_VCE_DISABLE|FFB_PPC_TBE_OPAQUE| | ||
422 | FFB_PPC_APE_DISABLE|FFB_PPC_CS_CONST, | ||
423 | &fbc->ppc); | ||
424 | upa_writel(0x2000707f, &fbc->fbc); | ||
425 | upa_writel(par->rop_cache, &fbc->rop); | ||
426 | upa_writel(0xffffffff, &fbc->pmask); | ||
427 | upa_writel((1 << 16) | (0 << 0), &fbc->fontinc); | ||
428 | upa_writel(par->fg_cache, &fbc->fg); | ||
429 | upa_writel(par->bg_cache, &fbc->bg); | ||
430 | FFBWait(par); | ||
431 | |||
432 | /* Disable cursor. */ | ||
433 | upa_writel(0x100, &dac->type2); | ||
434 | if (par->dac_rev <= 2) | ||
435 | upa_writel(0, &dac->value2); | ||
436 | else | ||
437 | upa_writel(3, &dac->value2); | ||
438 | |||
439 | spin_unlock_irqrestore(&par->lock, flags); | ||
440 | } | ||
441 | |||
442 | static int ffb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | ||
443 | { | ||
444 | struct ffb_par *par = (struct ffb_par *) info->par; | ||
445 | |||
446 | /* We just use this to catch switches out of | ||
447 | * graphics mode. | ||
448 | */ | ||
449 | ffb_switch_from_graph(par); | ||
450 | |||
451 | if (var->xoffset || var->yoffset || var->vmode) | ||
452 | return -EINVAL; | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | /** | ||
457 | * ffb_fillrect - REQUIRED function. Can use generic routines if | ||
458 | * non acclerated hardware and packed pixel based. | ||
459 | * Draws a rectangle on the screen. | ||
460 | * | ||
461 | * @info: frame buffer structure that represents a single frame buffer | ||
462 | * @rect: structure defining the rectagle and operation. | ||
463 | */ | ||
464 | static void ffb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | ||
465 | { | ||
466 | struct ffb_par *par = (struct ffb_par *) info->par; | ||
467 | struct ffb_fbc *fbc = par->fbc; | ||
468 | unsigned long flags; | ||
469 | u32 fg; | ||
470 | |||
471 | if (rect->rop != ROP_COPY && rect->rop != ROP_XOR) | ||
472 | BUG(); | ||
473 | |||
474 | fg = ((u32 *)info->pseudo_palette)[rect->color]; | ||
475 | |||
476 | spin_lock_irqsave(&par->lock, flags); | ||
477 | |||
478 | if (fg != par->fg_cache) { | ||
479 | FFBFifo(par, 1); | ||
480 | upa_writel(fg, &fbc->fg); | ||
481 | par->fg_cache = fg; | ||
482 | } | ||
483 | |||
484 | ffb_rop(par, (rect->rop == ROP_COPY ? | ||
485 | FFB_ROP_NEW : | ||
486 | FFB_ROP_NEW_XOR_OLD)); | ||
487 | |||
488 | FFBFifo(par, 5); | ||
489 | upa_writel(FFB_DRAWOP_RECTANGLE, &fbc->drawop); | ||
490 | upa_writel(rect->dy, &fbc->by); | ||
491 | upa_writel(rect->dx, &fbc->bx); | ||
492 | upa_writel(rect->height, &fbc->bh); | ||
493 | upa_writel(rect->width, &fbc->bw); | ||
494 | |||
495 | spin_unlock_irqrestore(&par->lock, flags); | ||
496 | } | ||
497 | |||
498 | /** | ||
499 | * ffb_copyarea - REQUIRED function. Can use generic routines if | ||
500 | * non acclerated hardware and packed pixel based. | ||
501 | * Copies on area of the screen to another area. | ||
502 | * | ||
503 | * @info: frame buffer structure that represents a single frame buffer | ||
504 | * @area: structure defining the source and destination. | ||
505 | */ | ||
506 | |||
507 | static void | ||
508 | ffb_copyarea(struct fb_info *info, const struct fb_copyarea *area) | ||
509 | { | ||
510 | struct ffb_par *par = (struct ffb_par *) info->par; | ||
511 | struct ffb_fbc *fbc = par->fbc; | ||
512 | unsigned long flags; | ||
513 | |||
514 | if (area->dx != area->sx || | ||
515 | area->dy == area->sy) { | ||
516 | cfb_copyarea(info, area); | ||
517 | return; | ||
518 | } | ||
519 | |||
520 | spin_lock_irqsave(&par->lock, flags); | ||
521 | |||
522 | ffb_rop(par, FFB_ROP_OLD); | ||
523 | |||
524 | FFBFifo(par, 7); | ||
525 | upa_writel(FFB_DRAWOP_VSCROLL, &fbc->drawop); | ||
526 | upa_writel(area->sy, &fbc->by); | ||
527 | upa_writel(area->sx, &fbc->bx); | ||
528 | upa_writel(area->dy, &fbc->dy); | ||
529 | upa_writel(area->dx, &fbc->dx); | ||
530 | upa_writel(area->height, &fbc->bh); | ||
531 | upa_writel(area->width, &fbc->bw); | ||
532 | |||
533 | spin_unlock_irqrestore(&par->lock, flags); | ||
534 | } | ||
535 | |||
536 | /** | ||
537 | * ffb_imageblit - REQUIRED function. Can use generic routines if | ||
538 | * non acclerated hardware and packed pixel based. | ||
539 | * Copies a image from system memory to the screen. | ||
540 | * | ||
541 | * @info: frame buffer structure that represents a single frame buffer | ||
542 | * @image: structure defining the image. | ||
543 | */ | ||
544 | static void ffb_imageblit(struct fb_info *info, const struct fb_image *image) | ||
545 | { | ||
546 | struct ffb_par *par = (struct ffb_par *) info->par; | ||
547 | struct ffb_fbc *fbc = par->fbc; | ||
548 | const u8 *data = image->data; | ||
549 | unsigned long flags; | ||
550 | u32 fg, bg, xy; | ||
551 | u64 fgbg; | ||
552 | int i, width, stride; | ||
553 | |||
554 | if (image->depth > 1) { | ||
555 | cfb_imageblit(info, image); | ||
556 | return; | ||
557 | } | ||
558 | |||
559 | fg = ((u32 *)info->pseudo_palette)[image->fg_color]; | ||
560 | bg = ((u32 *)info->pseudo_palette)[image->bg_color]; | ||
561 | fgbg = ((u64) fg << 32) | (u64) bg; | ||
562 | xy = (image->dy << 16) | image->dx; | ||
563 | width = image->width; | ||
564 | stride = ((width + 7) >> 3); | ||
565 | |||
566 | spin_lock_irqsave(&par->lock, flags); | ||
567 | |||
568 | if (fgbg != *(u64 *)&par->fg_cache) { | ||
569 | FFBFifo(par, 2); | ||
570 | upa_writeq(fgbg, &fbc->fg); | ||
571 | *(u64 *)&par->fg_cache = fgbg; | ||
572 | } | ||
573 | |||
574 | if (width >= 32) { | ||
575 | FFBFifo(par, 1); | ||
576 | upa_writel(32, &fbc->fontw); | ||
577 | } | ||
578 | |||
579 | while (width >= 32) { | ||
580 | const u8 *next_data = data + 4; | ||
581 | |||
582 | FFBFifo(par, 1); | ||
583 | upa_writel(xy, &fbc->fontxy); | ||
584 | xy += (32 << 0); | ||
585 | |||
586 | for (i = 0; i < image->height; i++) { | ||
587 | u32 val = (((u32)data[0] << 24) | | ||
588 | ((u32)data[1] << 16) | | ||
589 | ((u32)data[2] << 8) | | ||
590 | ((u32)data[3] << 0)); | ||
591 | FFBFifo(par, 1); | ||
592 | upa_writel(val, &fbc->font); | ||
593 | |||
594 | data += stride; | ||
595 | } | ||
596 | |||
597 | data = next_data; | ||
598 | width -= 32; | ||
599 | } | ||
600 | |||
601 | if (width) { | ||
602 | FFBFifo(par, 2); | ||
603 | upa_writel(width, &fbc->fontw); | ||
604 | upa_writel(xy, &fbc->fontxy); | ||
605 | |||
606 | for (i = 0; i < image->height; i++) { | ||
607 | u32 val = (((u32)data[0] << 24) | | ||
608 | ((u32)data[1] << 16) | | ||
609 | ((u32)data[2] << 8) | | ||
610 | ((u32)data[3] << 0)); | ||
611 | FFBFifo(par, 1); | ||
612 | upa_writel(val, &fbc->font); | ||
613 | |||
614 | data += stride; | ||
615 | } | ||
616 | } | ||
617 | |||
618 | spin_unlock_irqrestore(&par->lock, flags); | ||
619 | } | ||
620 | |||
621 | static void ffb_fixup_var_rgb(struct fb_var_screeninfo *var) | ||
622 | { | ||
623 | var->red.offset = 0; | ||
624 | var->red.length = 8; | ||
625 | var->green.offset = 8; | ||
626 | var->green.length = 8; | ||
627 | var->blue.offset = 16; | ||
628 | var->blue.length = 8; | ||
629 | var->transp.offset = 0; | ||
630 | var->transp.length = 0; | ||
631 | } | ||
632 | |||
633 | /** | ||
634 | * ffb_setcolreg - Optional function. Sets a color register. | ||
635 | * @regno: boolean, 0 copy local, 1 get_user() function | ||
636 | * @red: frame buffer colormap structure | ||
637 | * @green: The green value which can be up to 16 bits wide | ||
638 | * @blue: The blue value which can be up to 16 bits wide. | ||
639 | * @transp: If supported the alpha value which can be up to 16 bits wide. | ||
640 | * @info: frame buffer info structure | ||
641 | */ | ||
642 | static int ffb_setcolreg(unsigned regno, | ||
643 | unsigned red, unsigned green, unsigned blue, | ||
644 | unsigned transp, struct fb_info *info) | ||
645 | { | ||
646 | u32 value; | ||
647 | |||
648 | if (regno >= 256) | ||
649 | return 1; | ||
650 | |||
651 | red >>= 8; | ||
652 | green >>= 8; | ||
653 | blue >>= 8; | ||
654 | |||
655 | value = (blue << 16) | (green << 8) | red; | ||
656 | ((u32 *)info->pseudo_palette)[regno] = value; | ||
657 | |||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | /** | ||
662 | * ffb_blank - Optional function. Blanks the display. | ||
663 | * @blank_mode: the blank mode we want. | ||
664 | * @info: frame buffer structure that represents a single frame buffer | ||
665 | */ | ||
666 | static int | ||
667 | ffb_blank(int blank, struct fb_info *info) | ||
668 | { | ||
669 | struct ffb_par *par = (struct ffb_par *) info->par; | ||
670 | struct ffb_dac *dac = par->dac; | ||
671 | unsigned long flags; | ||
672 | u32 tmp; | ||
673 | |||
674 | spin_lock_irqsave(&par->lock, flags); | ||
675 | |||
676 | FFBWait(par); | ||
677 | |||
678 | switch (blank) { | ||
679 | case FB_BLANK_UNBLANK: /* Unblanking */ | ||
680 | upa_writel(0x6000, &dac->type); | ||
681 | tmp = (upa_readl(&dac->value) | 0x1); | ||
682 | upa_writel(0x6000, &dac->type); | ||
683 | upa_writel(tmp, &dac->value); | ||
684 | par->flags &= ~FFB_FLAG_BLANKED; | ||
685 | break; | ||
686 | |||
687 | case FB_BLANK_NORMAL: /* Normal blanking */ | ||
688 | case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */ | ||
689 | case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */ | ||
690 | case FB_BLANK_POWERDOWN: /* Poweroff */ | ||
691 | upa_writel(0x6000, &dac->type); | ||
692 | tmp = (upa_readl(&dac->value) & ~0x1); | ||
693 | upa_writel(0x6000, &dac->type); | ||
694 | upa_writel(tmp, &dac->value); | ||
695 | par->flags |= FFB_FLAG_BLANKED; | ||
696 | break; | ||
697 | } | ||
698 | |||
699 | spin_unlock_irqrestore(&par->lock, flags); | ||
700 | |||
701 | return 0; | ||
702 | } | ||
703 | |||
704 | static struct sbus_mmap_map ffb_mmap_map[] = { | ||
705 | { | ||
706 | .voff = FFB_SFB8R_VOFF, | ||
707 | .poff = FFB_SFB8R_POFF, | ||
708 | .size = 0x0400000 | ||
709 | }, | ||
710 | { | ||
711 | .voff = FFB_SFB8G_VOFF, | ||
712 | .poff = FFB_SFB8G_POFF, | ||
713 | .size = 0x0400000 | ||
714 | }, | ||
715 | { | ||
716 | .voff = FFB_SFB8B_VOFF, | ||
717 | .poff = FFB_SFB8B_POFF, | ||
718 | .size = 0x0400000 | ||
719 | }, | ||
720 | { | ||
721 | .voff = FFB_SFB8X_VOFF, | ||
722 | .poff = FFB_SFB8X_POFF, | ||
723 | .size = 0x0400000 | ||
724 | }, | ||
725 | { | ||
726 | .voff = FFB_SFB32_VOFF, | ||
727 | .poff = FFB_SFB32_POFF, | ||
728 | .size = 0x1000000 | ||
729 | }, | ||
730 | { | ||
731 | .voff = FFB_SFB64_VOFF, | ||
732 | .poff = FFB_SFB64_POFF, | ||
733 | .size = 0x2000000 | ||
734 | }, | ||
735 | { | ||
736 | .voff = FFB_FBC_REGS_VOFF, | ||
737 | .poff = FFB_FBC_REGS_POFF, | ||
738 | .size = 0x0002000 | ||
739 | }, | ||
740 | { | ||
741 | .voff = FFB_BM_FBC_REGS_VOFF, | ||
742 | .poff = FFB_BM_FBC_REGS_POFF, | ||
743 | .size = 0x0002000 | ||
744 | }, | ||
745 | { | ||
746 | .voff = FFB_DFB8R_VOFF, | ||
747 | .poff = FFB_DFB8R_POFF, | ||
748 | .size = 0x0400000 | ||
749 | }, | ||
750 | { | ||
751 | .voff = FFB_DFB8G_VOFF, | ||
752 | .poff = FFB_DFB8G_POFF, | ||
753 | .size = 0x0400000 | ||
754 | }, | ||
755 | { | ||
756 | .voff = FFB_DFB8B_VOFF, | ||
757 | .poff = FFB_DFB8B_POFF, | ||
758 | .size = 0x0400000 | ||
759 | }, | ||
760 | { | ||
761 | .voff = FFB_DFB8X_VOFF, | ||
762 | .poff = FFB_DFB8X_POFF, | ||
763 | .size = 0x0400000 | ||
764 | }, | ||
765 | { | ||
766 | .voff = FFB_DFB24_VOFF, | ||
767 | .poff = FFB_DFB24_POFF, | ||
768 | .size = 0x1000000 | ||
769 | }, | ||
770 | { | ||
771 | .voff = FFB_DFB32_VOFF, | ||
772 | .poff = FFB_DFB32_POFF, | ||
773 | .size = 0x1000000 | ||
774 | }, | ||
775 | { | ||
776 | .voff = FFB_FBC_KREGS_VOFF, | ||
777 | .poff = FFB_FBC_KREGS_POFF, | ||
778 | .size = 0x0002000 | ||
779 | }, | ||
780 | { | ||
781 | .voff = FFB_DAC_VOFF, | ||
782 | .poff = FFB_DAC_POFF, | ||
783 | .size = 0x0002000 | ||
784 | }, | ||
785 | { | ||
786 | .voff = FFB_PROM_VOFF, | ||
787 | .poff = FFB_PROM_POFF, | ||
788 | .size = 0x0010000 | ||
789 | }, | ||
790 | { | ||
791 | .voff = FFB_EXP_VOFF, | ||
792 | .poff = FFB_EXP_POFF, | ||
793 | .size = 0x0002000 | ||
794 | }, | ||
795 | { | ||
796 | .voff = FFB_DFB422A_VOFF, | ||
797 | .poff = FFB_DFB422A_POFF, | ||
798 | .size = 0x0800000 | ||
799 | }, | ||
800 | { | ||
801 | .voff = FFB_DFB422AD_VOFF, | ||
802 | .poff = FFB_DFB422AD_POFF, | ||
803 | .size = 0x0800000 | ||
804 | }, | ||
805 | { | ||
806 | .voff = FFB_DFB24B_VOFF, | ||
807 | .poff = FFB_DFB24B_POFF, | ||
808 | .size = 0x1000000 | ||
809 | }, | ||
810 | { | ||
811 | .voff = FFB_DFB422B_VOFF, | ||
812 | .poff = FFB_DFB422B_POFF, | ||
813 | .size = 0x0800000 | ||
814 | }, | ||
815 | { | ||
816 | .voff = FFB_DFB422BD_VOFF, | ||
817 | .poff = FFB_DFB422BD_POFF, | ||
818 | .size = 0x0800000 | ||
819 | }, | ||
820 | { | ||
821 | .voff = FFB_SFB16Z_VOFF, | ||
822 | .poff = FFB_SFB16Z_POFF, | ||
823 | .size = 0x0800000 | ||
824 | }, | ||
825 | { | ||
826 | .voff = FFB_SFB8Z_VOFF, | ||
827 | .poff = FFB_SFB8Z_POFF, | ||
828 | .size = 0x0800000 | ||
829 | }, | ||
830 | { | ||
831 | .voff = FFB_SFB422_VOFF, | ||
832 | .poff = FFB_SFB422_POFF, | ||
833 | .size = 0x0800000 | ||
834 | }, | ||
835 | { | ||
836 | .voff = FFB_SFB422D_VOFF, | ||
837 | .poff = FFB_SFB422D_POFF, | ||
838 | .size = 0x0800000 | ||
839 | }, | ||
840 | { .size = 0 } | ||
841 | }; | ||
842 | |||
843 | static int ffb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma) | ||
844 | { | ||
845 | struct ffb_par *par = (struct ffb_par *)info->par; | ||
846 | |||
847 | return sbusfb_mmap_helper(ffb_mmap_map, | ||
848 | par->physbase, par->fbsize, | ||
849 | 0, vma); | ||
850 | } | ||
851 | |||
852 | static int ffb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | ||
853 | unsigned long arg, struct fb_info *info) | ||
854 | { | ||
855 | struct ffb_par *par = (struct ffb_par *) info->par; | ||
856 | |||
857 | return sbusfb_ioctl_helper(cmd, arg, info, | ||
858 | FBTYPE_CREATOR, 24, par->fbsize); | ||
859 | } | ||
860 | |||
861 | /* | ||
862 | * Initialisation | ||
863 | */ | ||
864 | |||
865 | static void | ||
866 | ffb_init_fix(struct fb_info *info) | ||
867 | { | ||
868 | struct ffb_par *par = (struct ffb_par *)info->par; | ||
869 | const char *ffb_type_name; | ||
870 | |||
871 | if (!(par->flags & FFB_FLAG_AFB)) { | ||
872 | if ((par->board_type & 0x7) == 0x3) | ||
873 | ffb_type_name = "Creator 3D"; | ||
874 | else | ||
875 | ffb_type_name = "Creator"; | ||
876 | } else | ||
877 | ffb_type_name = "Elite 3D"; | ||
878 | |||
879 | strlcpy(info->fix.id, ffb_type_name, sizeof(info->fix.id)); | ||
880 | |||
881 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
882 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
883 | |||
884 | /* Framebuffer length is the same regardless of resolution. */ | ||
885 | info->fix.line_length = 8192; | ||
886 | |||
887 | info->fix.accel = FB_ACCEL_SUN_CREATOR; | ||
888 | } | ||
889 | |||
890 | static int ffb_apply_upa_parent_ranges(int parent, | ||
891 | struct linux_prom64_registers *regs) | ||
892 | { | ||
893 | struct linux_prom64_ranges ranges[PROMREG_MAX]; | ||
894 | char name[128]; | ||
895 | int len, i; | ||
896 | |||
897 | prom_getproperty(parent, "name", name, sizeof(name)); | ||
898 | if (strcmp(name, "upa") != 0) | ||
899 | return 0; | ||
900 | |||
901 | len = prom_getproperty(parent, "ranges", (void *) ranges, sizeof(ranges)); | ||
902 | if (len <= 0) | ||
903 | return 1; | ||
904 | |||
905 | len /= sizeof(struct linux_prom64_ranges); | ||
906 | for (i = 0; i < len; i++) { | ||
907 | struct linux_prom64_ranges *rng = &ranges[i]; | ||
908 | u64 phys_addr = regs->phys_addr; | ||
909 | |||
910 | if (phys_addr >= rng->ot_child_base && | ||
911 | phys_addr < (rng->ot_child_base + rng->or_size)) { | ||
912 | regs->phys_addr -= rng->ot_child_base; | ||
913 | regs->phys_addr += rng->ot_parent_base; | ||
914 | return 0; | ||
915 | } | ||
916 | } | ||
917 | |||
918 | return 1; | ||
919 | } | ||
920 | |||
921 | struct all_info { | ||
922 | struct fb_info info; | ||
923 | struct ffb_par par; | ||
924 | u32 pseudo_palette[256]; | ||
925 | struct list_head list; | ||
926 | }; | ||
927 | static LIST_HEAD(ffb_list); | ||
928 | |||
929 | static void ffb_init_one(int node, int parent) | ||
930 | { | ||
931 | struct linux_prom64_registers regs[2*PROMREG_MAX]; | ||
932 | struct ffb_fbc *fbc; | ||
933 | struct ffb_dac *dac; | ||
934 | struct all_info *all; | ||
935 | |||
936 | if (prom_getproperty(node, "reg", (void *) regs, sizeof(regs)) <= 0) { | ||
937 | printk("ffb: Cannot get reg device node property.\n"); | ||
938 | return; | ||
939 | } | ||
940 | |||
941 | if (ffb_apply_upa_parent_ranges(parent, ®s[0])) { | ||
942 | printk("ffb: Cannot apply parent ranges to regs.\n"); | ||
943 | return; | ||
944 | } | ||
945 | |||
946 | all = kmalloc(sizeof(*all), GFP_KERNEL); | ||
947 | if (!all) { | ||
948 | printk(KERN_ERR "ffb: Cannot allocate memory.\n"); | ||
949 | return; | ||
950 | } | ||
951 | memset(all, 0, sizeof(*all)); | ||
952 | |||
953 | INIT_LIST_HEAD(&all->list); | ||
954 | |||
955 | spin_lock_init(&all->par.lock); | ||
956 | all->par.fbc = (struct ffb_fbc *)(regs[0].phys_addr + FFB_FBC_REGS_POFF); | ||
957 | all->par.dac = (struct ffb_dac *)(regs[0].phys_addr + FFB_DAC_POFF); | ||
958 | all->par.rop_cache = FFB_ROP_NEW; | ||
959 | all->par.physbase = regs[0].phys_addr; | ||
960 | all->par.prom_node = node; | ||
961 | all->par.prom_parent_node = parent; | ||
962 | |||
963 | /* Don't mention copyarea, so SCROLL_REDRAW is always | ||
964 | * used. It is the fastest on this chip. | ||
965 | */ | ||
966 | all->info.flags = (FBINFO_DEFAULT | | ||
967 | /* FBINFO_HWACCEL_COPYAREA | */ | ||
968 | FBINFO_HWACCEL_FILLRECT | | ||
969 | FBINFO_HWACCEL_IMAGEBLIT); | ||
970 | all->info.fbops = &ffb_ops; | ||
971 | all->info.screen_base = (char *) all->par.physbase + FFB_DFB24_POFF; | ||
972 | all->info.par = &all->par; | ||
973 | all->info.pseudo_palette = all->pseudo_palette; | ||
974 | |||
975 | sbusfb_fill_var(&all->info.var, all->par.prom_node, 32); | ||
976 | all->par.fbsize = PAGE_ALIGN(all->info.var.xres * | ||
977 | all->info.var.yres * | ||
978 | 4); | ||
979 | ffb_fixup_var_rgb(&all->info.var); | ||
980 | |||
981 | all->info.var.accel_flags = FB_ACCELF_TEXT; | ||
982 | |||
983 | prom_getstring(node, "name", all->par.name, sizeof(all->par.name)); | ||
984 | if (!strcmp(all->par.name, "SUNW,afb")) | ||
985 | all->par.flags |= FFB_FLAG_AFB; | ||
986 | |||
987 | all->par.board_type = prom_getintdefault(node, "board_type", 0); | ||
988 | |||
989 | fbc = all->par.fbc; | ||
990 | if((upa_readl(&fbc->ucsr) & FFB_UCSR_ALL_ERRORS) != 0) | ||
991 | upa_writel(FFB_UCSR_ALL_ERRORS, &fbc->ucsr); | ||
992 | |||
993 | ffb_switch_from_graph(&all->par); | ||
994 | |||
995 | dac = all->par.dac; | ||
996 | upa_writel(0x8000, &dac->type); | ||
997 | all->par.dac_rev = upa_readl(&dac->value) >> 0x1c; | ||
998 | |||
999 | /* Elite3D has different DAC revision numbering, and no DAC revisions | ||
1000 | * have the reversed meaning of cursor enable. | ||
1001 | */ | ||
1002 | if (all->par.flags & FFB_FLAG_AFB) | ||
1003 | all->par.dac_rev = 10; | ||
1004 | |||
1005 | /* Unblank it just to be sure. When there are multiple | ||
1006 | * FFB/AFB cards in the system, or it is not the OBP | ||
1007 | * chosen console, it will have video outputs off in | ||
1008 | * the DAC. | ||
1009 | */ | ||
1010 | ffb_blank(0, &all->info); | ||
1011 | |||
1012 | if (fb_alloc_cmap(&all->info.cmap, 256, 0)) { | ||
1013 | printk(KERN_ERR "ffb: Could not allocate color map.\n"); | ||
1014 | kfree(all); | ||
1015 | return; | ||
1016 | } | ||
1017 | |||
1018 | ffb_init_fix(&all->info); | ||
1019 | |||
1020 | if (register_framebuffer(&all->info) < 0) { | ||
1021 | printk(KERN_ERR "ffb: Could not register framebuffer.\n"); | ||
1022 | fb_dealloc_cmap(&all->info.cmap); | ||
1023 | kfree(all); | ||
1024 | return; | ||
1025 | } | ||
1026 | |||
1027 | list_add(&all->list, &ffb_list); | ||
1028 | |||
1029 | printk("ffb: %s at %016lx type %d DAC %d\n", | ||
1030 | ((all->par.flags & FFB_FLAG_AFB) ? "AFB" : "FFB"), | ||
1031 | regs[0].phys_addr, all->par.board_type, all->par.dac_rev); | ||
1032 | } | ||
1033 | |||
1034 | static void ffb_scan_siblings(int root) | ||
1035 | { | ||
1036 | int node, child; | ||
1037 | |||
1038 | child = prom_getchild(root); | ||
1039 | for (node = prom_searchsiblings(child, "SUNW,ffb"); node; | ||
1040 | node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) | ||
1041 | ffb_init_one(node, root); | ||
1042 | for (node = prom_searchsiblings(child, "SUNW,afb"); node; | ||
1043 | node = prom_searchsiblings(prom_getsibling(node), "SUNW,afb")) | ||
1044 | ffb_init_one(node, root); | ||
1045 | } | ||
1046 | |||
1047 | int __init ffb_init(void) | ||
1048 | { | ||
1049 | int root; | ||
1050 | |||
1051 | if (fb_get_options("ffb", NULL)) | ||
1052 | return -ENODEV; | ||
1053 | |||
1054 | ffb_scan_siblings(prom_root_node); | ||
1055 | |||
1056 | root = prom_getchild(prom_root_node); | ||
1057 | for (root = prom_searchsiblings(root, "upa"); root; | ||
1058 | root = prom_searchsiblings(prom_getsibling(root), "upa")) | ||
1059 | ffb_scan_siblings(root); | ||
1060 | |||
1061 | return 0; | ||
1062 | } | ||
1063 | |||
1064 | void __exit ffb_exit(void) | ||
1065 | { | ||
1066 | struct list_head *pos, *tmp; | ||
1067 | |||
1068 | list_for_each_safe(pos, tmp, &ffb_list) { | ||
1069 | struct all_info *all = list_entry(pos, typeof(*all), list); | ||
1070 | |||
1071 | unregister_framebuffer(&all->info); | ||
1072 | fb_dealloc_cmap(&all->info.cmap); | ||
1073 | kfree(all); | ||
1074 | } | ||
1075 | } | ||
1076 | |||
1077 | int __init | ||
1078 | ffb_setup(char *arg) | ||
1079 | { | ||
1080 | /* No cmdline options yet... */ | ||
1081 | return 0; | ||
1082 | } | ||
1083 | |||
1084 | module_init(ffb_init); | ||
1085 | |||
1086 | #ifdef MODULE | ||
1087 | module_exit(ffb_exit); | ||
1088 | #endif | ||
1089 | |||
1090 | MODULE_DESCRIPTION("framebuffer driver for Creator/Elite3D chipsets"); | ||
1091 | MODULE_AUTHOR("David S. Miller <davem@redhat.com>"); | ||
1092 | MODULE_LICENSE("GPL"); | ||