Rev 489 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
466 | giacomo | 1 | /* |
2 | * linux/drivers/video/fbmem.c |
||
3 | * |
||
4 | * Copyright (C) 1994 Martin Schaller |
||
5 | * |
||
6 | * 2001 - Documented with DocBook |
||
7 | * - Brad Douglas <brad@neruo.com> |
||
8 | * |
||
9 | * This file is subject to the terms and conditions of the GNU General Public |
||
10 | * License. See the file COPYING in the main directory of this archive |
||
11 | * for more details. |
||
12 | */ |
||
13 | |||
14 | #include <linuxcomp.h> |
||
15 | |||
16 | #include <linux/config.h> |
||
17 | #include <linux/module.h> |
||
18 | |||
19 | #include <linux/types.h> |
||
20 | #include <linux/errno.h> |
||
21 | #include <linux/sched.h> |
||
22 | #include <linux/smp_lock.h> |
||
23 | #include <linux/kernel.h> |
||
24 | #include <linux/major.h> |
||
25 | #include <linux/slab.h> |
||
26 | #include <linux/mm.h> |
||
27 | #include <linux/mman.h> |
||
28 | #include <linux/tty.h> |
||
29 | #include <linux/init.h> |
||
30 | #include <linux/linux_logo.h> |
||
31 | #include <linux/proc_fs.h> |
||
32 | #ifdef CONFIG_KMOD |
||
33 | #include <linux/kmod.h> |
||
34 | #endif |
||
35 | #include <linux/devfs_fs_kernel.h> |
||
36 | |||
37 | #if defined(__mc68000__) || defined(CONFIG_APUS) |
||
38 | #include <asm/setup.h> |
||
39 | #endif |
||
40 | |||
41 | #include <asm/io.h> |
||
42 | #include <asm/uaccess.h> |
||
43 | #include <asm/page.h> |
||
44 | #include <asm/pgtable.h> |
||
45 | |||
46 | #include <linux/fb.h> |
||
47 | |||
48 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE |
||
49 | #include "console/fbcon.h" |
||
50 | #endif |
||
51 | /* |
||
52 | * Frame buffer device initialization and setup routines |
||
53 | */ |
||
54 | |||
55 | extern int acornfb_init(void); |
||
56 | extern int acornfb_setup(char*); |
||
57 | extern int amifb_init(void); |
||
58 | extern int amifb_setup(char*); |
||
59 | extern int anakinfb_init(void); |
||
60 | extern int atafb_init(void); |
||
61 | extern int atafb_setup(char*); |
||
62 | extern int macfb_init(void); |
||
63 | extern int macfb_setup(char*); |
||
64 | extern int cyberfb_init(void); |
||
65 | extern int cyberfb_setup(char*); |
||
66 | extern int pm2fb_init(void); |
||
67 | extern int pm2fb_setup(char*); |
||
68 | extern int pm3fb_init(void); |
||
69 | extern int pm3fb_setup(char*); |
||
70 | extern int clps711xfb_init(void); |
||
71 | extern int cyber2000fb_init(void); |
||
72 | extern int cyber2000fb_setup(char*); |
||
73 | extern int retz3fb_init(void); |
||
74 | extern int retz3fb_setup(char*); |
||
75 | extern int clgenfb_init(void); |
||
76 | extern int clgenfb_setup(char*); |
||
77 | extern int hitfb_init(void); |
||
78 | extern int vfb_init(void); |
||
79 | extern int vfb_setup(char*); |
||
80 | extern int offb_init(void); |
||
81 | extern int atyfb_init(void); |
||
82 | extern int atyfb_setup(char*); |
||
83 | extern int aty128fb_init(void); |
||
84 | extern int aty128fb_setup(char*); |
||
85 | extern int neofb_init(void); |
||
86 | extern int neofb_setup(char*); |
||
87 | extern int igafb_init(void); |
||
88 | extern int igafb_setup(char*); |
||
89 | extern int imsttfb_init(void); |
||
90 | extern int imsttfb_setup(char*); |
||
91 | extern int dnfb_init(void); |
||
92 | extern int tgafb_init(void); |
||
93 | extern int tgafb_setup(char*); |
||
94 | extern int virgefb_init(void); |
||
95 | extern int virgefb_setup(char*); |
||
96 | extern int resolver_video_setup(char*); |
||
97 | extern int s3triofb_init(void); |
||
98 | extern int vesafb_init(void); |
||
99 | extern int vesafb_setup(char*); |
||
100 | extern int vga16fb_init(void); |
||
101 | extern int vga16fb_setup(char*); |
||
102 | extern int hgafb_init(void); |
||
103 | extern int hgafb_setup(char*); |
||
104 | extern int matroxfb_init(void); |
||
105 | extern int matroxfb_setup(char*); |
||
106 | extern int hpfb_init(void); |
||
107 | extern int control_init(void); |
||
108 | extern int control_setup(char*); |
||
109 | extern int platinum_init(void); |
||
110 | extern int platinum_setup(char*); |
||
111 | extern int valkyriefb_init(void); |
||
112 | extern int valkyriefb_setup(char*); |
||
113 | extern int chips_init(void); |
||
114 | extern int g364fb_init(void); |
||
115 | extern int sa1100fb_init(void); |
||
116 | extern int fm2fb_init(void); |
||
117 | extern int fm2fb_setup(char*); |
||
118 | extern int q40fb_init(void); |
||
119 | extern int sun3fb_init(void); |
||
120 | extern int sun3fb_setup(char *); |
||
121 | extern int sgivwfb_init(void); |
||
122 | extern int sgivwfb_setup(char*); |
||
123 | extern int rivafb_init(void); |
||
124 | extern int rivafb_setup(char*); |
||
125 | extern int tdfxfb_init(void); |
||
126 | extern int tdfxfb_setup(char*); |
||
127 | extern int tridentfb_init(void); |
||
128 | extern int tridentfb_setup(char*); |
||
129 | extern int sisfb_init(void); |
||
130 | extern int sisfb_setup(char*); |
||
131 | extern int stifb_init(void); |
||
132 | extern int stifb_setup(char*); |
||
133 | extern int pmagbafb_init(void); |
||
134 | extern int pmagbbfb_init(void); |
||
135 | extern int maxinefb_init(void); |
||
136 | extern int tx3912fb_init(void); |
||
137 | extern int tx3912fb_setup(char*); |
||
138 | extern int radeonfb_init(void); |
||
139 | extern int radeonfb_setup(char*); |
||
140 | extern int e1355fb_init(void); |
||
141 | extern int e1355fb_setup(char*); |
||
142 | extern int pvr2fb_init(void); |
||
143 | extern int pvr2fb_setup(char*); |
||
144 | extern int sstfb_init(void); |
||
145 | extern int sstfb_setup(char*); |
||
146 | extern int i810fb_init(void); |
||
147 | extern int i810fb_setup(char*); |
||
148 | extern int ffb_init(void); |
||
149 | extern int ffb_setup(char*); |
||
150 | extern int cg6_init(void); |
||
151 | extern int cg6_setup(char*); |
||
152 | extern int cg3_init(void); |
||
153 | extern int cg3_setup(char*); |
||
154 | extern int bw2_init(void); |
||
155 | extern int bw2_setup(char*); |
||
156 | extern int cg14_init(void); |
||
157 | extern int cg14_setup(char*); |
||
158 | extern int p9100_init(void); |
||
159 | extern int p9100_setup(char*); |
||
160 | extern int tcx_init(void); |
||
161 | extern int tcx_setup(char*); |
||
162 | extern int leo_init(void); |
||
163 | extern int leo_setup(char*); |
||
164 | |||
165 | static struct { |
||
166 | const char *name; |
||
167 | int (*init)(void); |
||
168 | int (*setup)(char*); |
||
169 | } fb_drivers[] __initdata = { |
||
170 | |||
171 | /* |
||
172 | * Chipset specific drivers that use resource management |
||
173 | */ |
||
174 | #ifdef CONFIG_FB_RETINAZ3 |
||
175 | { "retz3fb", retz3fb_init, retz3fb_setup }, |
||
176 | #endif |
||
177 | #ifdef CONFIG_FB_AMIGA |
||
178 | { "amifb", amifb_init, amifb_setup }, |
||
179 | #endif |
||
180 | #ifdef CONFIG_FB_ANAKIN |
||
181 | { "anakinfb", anakinfb_init, NULL }, |
||
182 | #endif |
||
183 | #ifdef CONFIG_FB_CLPS711X |
||
184 | { "clps711xfb", clps711xfb_init, NULL }, |
||
185 | #endif |
||
186 | #ifdef CONFIG_FB_CYBER |
||
187 | { "cyberfb", cyberfb_init, cyberfb_setup }, |
||
188 | #endif |
||
189 | #ifdef CONFIG_FB_CYBER2000 |
||
190 | { "cyber2000fb", cyber2000fb_init, cyber2000fb_setup }, |
||
191 | #endif |
||
192 | #ifdef CONFIG_FB_PM2 |
||
193 | { "pm2fb", pm2fb_init, pm2fb_setup }, |
||
194 | #endif |
||
195 | #ifdef CONFIG_FB_PM3 |
||
196 | { "pm3fb", pm3fb_init, pm3fb_setup }, |
||
197 | #endif |
||
198 | #ifdef CONFIG_FB_CLGEN |
||
199 | { "clgenfb", clgenfb_init, clgenfb_setup }, |
||
200 | #endif |
||
201 | #ifdef CONFIG_FB_ATY |
||
202 | { "atyfb", atyfb_init, atyfb_setup }, |
||
203 | #endif |
||
204 | #ifdef CONFIG_FB_MATROX |
||
205 | { "matroxfb", matroxfb_init, matroxfb_setup }, |
||
206 | #endif |
||
207 | #ifdef CONFIG_FB_ATY128 |
||
208 | { "aty128fb", aty128fb_init, aty128fb_setup }, |
||
209 | #endif |
||
210 | #ifdef CONFIG_FB_NEOMAGIC |
||
211 | { "neofb", neofb_init, neofb_setup }, |
||
212 | #endif |
||
213 | #ifdef CONFIG_FB_VIRGE |
||
214 | { "virgefb", virgefb_init, virgefb_setup }, |
||
215 | #endif |
||
216 | #ifdef CONFIG_FB_RIVA |
||
217 | { "rivafb", rivafb_init, rivafb_setup }, |
||
218 | #endif |
||
219 | #ifdef CONFIG_FB_3DFX |
||
220 | { "tdfxfb", tdfxfb_init, tdfxfb_setup }, |
||
221 | #endif |
||
222 | #ifdef CONFIG_FB_RADEON |
||
223 | { "radeonfb", radeonfb_init, radeonfb_setup }, |
||
224 | #endif |
||
225 | #ifdef CONFIG_FB_CONTROL |
||
226 | { "controlfb", control_init, control_setup }, |
||
227 | #endif |
||
228 | #ifdef CONFIG_FB_PLATINUM |
||
229 | { "platinumfb", platinum_init, platinum_setup }, |
||
230 | #endif |
||
231 | #ifdef CONFIG_FB_VALKYRIE |
||
232 | { "valkyriefb", valkyriefb_init, valkyriefb_setup }, |
||
233 | #endif |
||
234 | #ifdef CONFIG_FB_CT65550 |
||
235 | { "chipsfb", chips_init, NULL }, |
||
236 | #endif |
||
237 | #ifdef CONFIG_FB_IMSTT |
||
238 | { "imsttfb", imsttfb_init, imsttfb_setup }, |
||
239 | #endif |
||
240 | #ifdef CONFIG_FB_S3TRIO |
||
241 | { "s3triofb", s3triofb_init, NULL }, |
||
242 | #endif |
||
243 | #ifdef CONFIG_FB_FM2 |
||
244 | { "fm2fb", fm2fb_init, fm2fb_setup }, |
||
245 | #endif |
||
246 | #ifdef CONFIG_FB_SIS |
||
247 | { "sisfb", sisfb_init, sisfb_setup }, |
||
248 | #endif |
||
249 | #ifdef CONFIG_FB_TRIDENT |
||
250 | { "tridentfb", tridentfb_init, tridentfb_setup }, |
||
251 | #endif |
||
252 | #ifdef CONFIG_FB_I810 |
||
253 | { "i810fb", i810fb_init, i810fb_setup }, |
||
254 | #endif |
||
255 | #ifdef CONFIG_FB_STI |
||
256 | { "stifb", stifb_init, stifb_setup }, |
||
257 | #endif |
||
258 | #ifdef CONFIG_FB_FFB |
||
259 | { "ffb", ffb_init, ffb_setup }, |
||
260 | #endif |
||
261 | #ifdef CONFIG_FB_CG6 |
||
262 | { "cg6fb", cg6_init, cg6_setup }, |
||
263 | #endif |
||
264 | #ifdef CONFIG_FB_CG3 |
||
265 | { "cg3fb", cg3_init, cg3_setup }, |
||
266 | #endif |
||
267 | #ifdef CONFIG_FB_BW2 |
||
268 | { "bw2fb", bw2_init, bw2_setup }, |
||
269 | #endif |
||
270 | #ifdef CONFIG_FB_CG14 |
||
271 | { "cg14fb", cg14_init, cg14_setup }, |
||
272 | #endif |
||
273 | #ifdef CONFIG_FB_P9100 |
||
274 | { "p9100fb", p9100_init, p9100_setup }, |
||
275 | #endif |
||
276 | #ifdef CONFIG_FB_TCX |
||
277 | { "tcxfb", tcx_init, tcx_setup }, |
||
278 | #endif |
||
279 | #ifdef CONFIG_FB_LEO |
||
280 | { "leofb", leo_init, leo_setup }, |
||
281 | #endif |
||
282 | |||
283 | /* |
||
284 | * Generic drivers that are used as fallbacks |
||
285 | * |
||
286 | * These depend on resource management and must be initialized |
||
287 | * _after_ all other frame buffer devices that use resource |
||
288 | * management! |
||
289 | */ |
||
290 | |||
291 | #ifdef CONFIG_FB_OF |
||
292 | { "offb", offb_init, NULL }, |
||
293 | #endif |
||
294 | #ifdef CONFIG_FB_VESA |
||
295 | { "vesafb", vesafb_init, vesafb_setup }, |
||
296 | #endif |
||
297 | |||
298 | /* |
||
299 | * Chipset specific drivers that don't use resource management (yet) |
||
300 | */ |
||
301 | |||
302 | #ifdef CONFIG_FB_SGIVW |
||
303 | { "sgivwfb", sgivwfb_init, sgivwfb_setup }, |
||
304 | #endif |
||
305 | #ifdef CONFIG_FB_ACORN |
||
306 | { "acornfb", acornfb_init, acornfb_setup }, |
||
307 | #endif |
||
308 | #ifdef CONFIG_FB_ATARI |
||
309 | { "atafb", atafb_init, atafb_setup }, |
||
310 | #endif |
||
311 | #ifdef CONFIG_FB_MAC |
||
312 | { "macfb", macfb_init, macfb_setup }, |
||
313 | #endif |
||
314 | #ifdef CONFIG_FB_HGA |
||
315 | { "hgafb", hgafb_init, hgafb_setup }, |
||
316 | #endif |
||
317 | #ifdef CONFIG_FB_IGA |
||
318 | { "igafb", igafb_init, igafb_setup }, |
||
319 | #endif |
||
320 | #ifdef CONFIG_APOLLO |
||
321 | { "apollofb", dnfb_init, NULL }, |
||
322 | #endif |
||
323 | #ifdef CONFIG_FB_Q40 |
||
324 | { "q40fb", q40fb_init, NULL }, |
||
325 | #endif |
||
326 | #ifdef CONFIG_FB_TGA |
||
327 | { "tgafb", tgafb_init, tgafb_setup }, |
||
328 | #endif |
||
329 | #ifdef CONFIG_FB_HP300 |
||
330 | { "hpfb", hpfb_init, NULL }, |
||
331 | #endif |
||
332 | #ifdef CONFIG_FB_G364 |
||
333 | { "g364fb", g364fb_init, NULL }, |
||
334 | #endif |
||
335 | #ifdef CONFIG_FB_SA1100 |
||
336 | { "sa1100fb", sa1100fb_init, NULL }, |
||
337 | #endif |
||
338 | #ifdef CONFIG_FB_SUN3 |
||
339 | { "sun3fb", sun3fb_init, sun3fb_setup }, |
||
340 | #endif |
||
341 | #ifdef CONFIG_FB_HIT |
||
342 | { "hitfb", hitfb_init, NULL }, |
||
343 | #endif |
||
344 | #ifdef CONFIG_FB_TX3912 |
||
345 | { "tx3912fb", tx3912fb_init, tx3912fb_setup }, |
||
346 | #endif |
||
347 | #ifdef CONFIG_FB_E1355 |
||
348 | { "e1355fb", e1355fb_init, e1355fb_setup }, |
||
349 | #endif |
||
350 | #ifdef CONFIG_FB_PVR2 |
||
351 | { "pvr2fb", pvr2fb_init, pvr2fb_setup }, |
||
352 | #endif |
||
353 | #ifdef CONFIG_FB_PMAG_BA |
||
354 | { "pmagbafb", pmagbafb_init, NULL }, |
||
355 | #endif |
||
356 | #ifdef CONFIG_FB_PMAGB_B |
||
357 | { "pmagbbfb", pmagbbfb_init, NULL }, |
||
358 | #endif |
||
359 | #ifdef CONFIG_FB_MAXINE |
||
360 | { "maxinefb", maxinefb_init, NULL }, |
||
361 | #endif |
||
362 | #ifdef CONFIG_FB_VOODOO1 |
||
363 | { "sstfb", sstfb_init, sstfb_setup }, |
||
364 | #endif |
||
365 | /* |
||
366 | * Generic drivers that don't use resource management (yet) |
||
367 | */ |
||
368 | |||
369 | #ifdef CONFIG_FB_VGA16 |
||
370 | { "vga16fb", vga16fb_init, vga16fb_setup }, |
||
371 | #endif |
||
372 | |||
373 | #ifdef CONFIG_GSP_RESOLVER |
||
374 | /* Not a real frame buffer device... */ |
||
375 | { "resolverfb", NULL, resolver_video_setup }, |
||
376 | #endif |
||
377 | |||
378 | #ifdef CONFIG_FB_VIRTUAL |
||
379 | /* |
||
380 | * Vfb must be last to avoid that it becomes your primary display if |
||
381 | * other display devices are present |
||
382 | */ |
||
383 | { "vfb", vfb_init, vfb_setup }, |
||
384 | #endif |
||
385 | }; |
||
386 | |||
387 | #define NUM_FB_DRIVERS (sizeof(fb_drivers)/sizeof(*fb_drivers)) |
||
388 | #define FBPIXMAPSIZE 8192 |
||
389 | |||
468 | giacomo | 390 | //extern const char *global_mode_option; |
466 | giacomo | 391 | |
392 | static initcall_t pref_init_funcs[FB_MAX]; |
||
393 | static int num_pref_init_funcs __initdata = 0; |
||
394 | struct fb_info *registered_fb[FB_MAX]; |
||
395 | int num_registered_fb; |
||
396 | |||
397 | #ifdef CONFIG_FB_OF |
||
398 | static int ofonly __initdata = 0; |
||
399 | #endif |
||
400 | |||
401 | /* |
||
402 | * Drawing helpers. |
||
403 | */ |
||
404 | u8 sys_inbuf(u8 *src) |
||
405 | { |
||
406 | return *src; |
||
407 | } |
||
408 | |||
409 | void sys_outbuf(u8 *src, u8 *dst, unsigned int size) |
||
410 | { |
||
411 | memcpy(dst, src, size); |
||
412 | } |
||
413 | |||
414 | void move_buf_aligned(struct fb_info *info, u8 *dst, u8 *src, u32 d_pitch, |
||
415 | u32 s_pitch, u32 height) |
||
416 | { |
||
417 | int i; |
||
418 | |||
419 | for (i = height; i--; ) { |
||
420 | info->pixmap.outbuf(src, dst, s_pitch); |
||
421 | src += s_pitch; |
||
422 | dst += d_pitch; |
||
423 | } |
||
424 | } |
||
425 | |||
426 | void move_buf_unaligned(struct fb_info *info, u8 *dst, u8 *src, u32 d_pitch, |
||
427 | u32 height, u32 mask, u32 shift_high, u32 shift_low, |
||
428 | u32 mod, u32 idx) |
||
429 | { |
||
430 | int i, j; |
||
431 | u8 tmp; |
||
432 | |||
433 | for (i = height; i--; ) { |
||
434 | for (j = 0; j < idx; j++) { |
||
435 | tmp = info->pixmap.inbuf(dst+j); |
||
436 | tmp &= mask; |
||
437 | tmp |= *src >> shift_low; |
||
438 | info->pixmap.outbuf(&tmp, dst+j, 1); |
||
439 | tmp = *src << shift_high; |
||
440 | info->pixmap.outbuf(&tmp, dst+j+1, 1); |
||
441 | src++; |
||
442 | } |
||
443 | tmp = info->pixmap.inbuf(dst+idx); |
||
444 | tmp &= mask; |
||
445 | tmp |= *src >> shift_low; |
||
446 | info->pixmap.outbuf(&tmp, dst+idx, 1); |
||
447 | if (shift_high < mod) { |
||
448 | tmp = *src << shift_high; |
||
449 | info->pixmap.outbuf(&tmp, dst+idx+1, 1); |
||
450 | } |
||
451 | src++; |
||
452 | dst += d_pitch; |
||
453 | } |
||
454 | } |
||
455 | |||
456 | /* |
||
457 | * we need to lock this section since fb_cursor |
||
458 | * may use fb_imageblit() |
||
459 | */ |
||
460 | u32 fb_get_buffer_offset(struct fb_info *info, u32 size) |
||
461 | { |
||
462 | u32 align = info->pixmap.buf_align - 1; |
||
463 | u32 offset, count = 1000; |
||
464 | |||
465 | spin_lock(&info->pixmap.lock); |
||
466 | offset = info->pixmap.offset + align; |
||
467 | offset &= ~align; |
||
468 | if (offset + size > info->pixmap.size) { |
||
469 | while (atomic_read(&info->pixmap.count) && count--); |
||
470 | if (info->fbops->fb_sync && |
||
471 | info->pixmap.flags & FB_PIXMAP_SYNC) |
||
472 | info->fbops->fb_sync(info); |
||
473 | offset = 0; |
||
474 | } |
||
475 | info->pixmap.offset = offset + size; |
||
476 | atomic_inc(&info->pixmap.count); |
||
477 | smp_mb__after_atomic_inc(); |
||
478 | spin_unlock(&info->pixmap.lock); |
||
479 | return offset; |
||
480 | } |
||
481 | |||
482 | #ifdef CONFIG_LOGO |
||
483 | #include <linux/linux_logo.h> |
||
484 | |||
485 | static inline unsigned safe_shift(unsigned d, int n) |
||
486 | { |
||
487 | return n < 0 ? d >> -n : d << n; |
||
488 | } |
||
489 | |||
490 | static void __init fb_set_logocmap(struct fb_info *info, |
||
491 | const struct linux_logo *logo) |
||
492 | { |
||
493 | struct fb_cmap palette_cmap; |
||
494 | u16 palette_green[16]; |
||
495 | u16 palette_blue[16]; |
||
496 | u16 palette_red[16]; |
||
497 | int i, j, n; |
||
498 | const unsigned char *clut = logo->clut; |
||
499 | |||
500 | palette_cmap.start = 0; |
||
501 | palette_cmap.len = 16; |
||
502 | palette_cmap.red = palette_red; |
||
503 | palette_cmap.green = palette_green; |
||
504 | palette_cmap.blue = palette_blue; |
||
505 | palette_cmap.transp = NULL; |
||
506 | |||
507 | for (i = 0; i < logo->clutsize; i += n) { |
||
508 | n = logo->clutsize - i; |
||
509 | /* palette_cmap provides space for only 16 colors at once */ |
||
510 | if (n > 16) |
||
511 | n = 16; |
||
512 | palette_cmap.start = 32 + i; |
||
513 | palette_cmap.len = n; |
||
514 | for (j = 0; j < n; ++j) { |
||
515 | palette_cmap.red[j] = clut[0] << 8 | clut[0]; |
||
516 | palette_cmap.green[j] = clut[1] << 8 | clut[1]; |
||
517 | palette_cmap.blue[j] = clut[2] << 8 | clut[2]; |
||
518 | clut += 3; |
||
519 | } |
||
520 | fb_set_cmap(&palette_cmap, 1, info); |
||
521 | } |
||
522 | } |
||
523 | |||
524 | static void __init fb_set_logo_truepalette(struct fb_info *info, |
||
525 | const struct linux_logo *logo, |
||
526 | u32 *palette) |
||
527 | { |
||
528 | unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff }; |
||
529 | unsigned char redmask, greenmask, bluemask; |
||
530 | int redshift, greenshift, blueshift; |
||
531 | int i; |
||
532 | const unsigned char *clut = logo->clut; |
||
533 | |||
534 | /* |
||
535 | * We have to create a temporary palette since console palette is only |
||
536 | * 16 colors long. |
||
537 | */ |
||
538 | /* Bug: Doesn't obey msb_right ... (who needs that?) */ |
||
539 | redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8]; |
||
540 | greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8]; |
||
541 | bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8]; |
||
542 | redshift = info->var.red.offset - (8 - info->var.red.length); |
||
543 | greenshift = info->var.green.offset - (8 - info->var.green.length); |
||
544 | blueshift = info->var.blue.offset - (8 - info->var.blue.length); |
||
545 | |||
546 | for ( i = 0; i < logo->clutsize; i++) { |
||
547 | palette[i+32] = (safe_shift((clut[0] & redmask), redshift) | |
||
548 | safe_shift((clut[1] & greenmask), greenshift) | |
||
549 | safe_shift((clut[2] & bluemask), blueshift)); |
||
550 | clut += 3; |
||
551 | } |
||
552 | } |
||
553 | |||
554 | static void __init fb_set_logo_directpalette(struct fb_info *info, |
||
555 | const struct linux_logo *logo, |
||
556 | u32 *palette) |
||
557 | { |
||
558 | int redshift, greenshift, blueshift; |
||
559 | int i; |
||
560 | |||
561 | redshift = info->var.red.offset; |
||
562 | greenshift = info->var.green.offset; |
||
563 | blueshift = info->var.blue.offset; |
||
564 | |||
565 | for (i = 32; i < logo->clutsize; i++) |
||
566 | palette[i] = i << redshift | i << greenshift | i << blueshift; |
||
567 | } |
||
568 | |||
569 | static void __init fb_set_logo(struct fb_info *info, |
||
570 | const struct linux_logo *logo, u8 *dst, |
||
571 | int depth) |
||
572 | { |
||
573 | int i, j, shift; |
||
574 | const u8 *src = logo->data; |
||
575 | u8 d, xor = 0; |
||
576 | |||
577 | switch (depth) { |
||
578 | case 4: |
||
579 | for (i = 0; i < logo->height; i++) |
||
580 | for (j = 0; j < logo->width; src++) { |
||
581 | *dst++ = *src >> 4; |
||
582 | j++; |
||
583 | if (j < logo->width) { |
||
584 | *dst++ = *src & 0x0f; |
||
585 | j++; |
||
586 | } |
||
587 | } |
||
588 | break; |
||
589 | case ~1: |
||
590 | xor = 0xff; |
||
591 | case 1: |
||
592 | for (i = 0; i < logo->height; i++) { |
||
593 | shift = 7; |
||
594 | d = *src++ ^ xor; |
||
595 | for (j = 0; j < logo->width; j++) { |
||
596 | *dst++ = (d >> shift) & 1; |
||
597 | shift = (shift-1) & 7; |
||
598 | if (shift == 7) |
||
599 | d = *src++ ^ xor; |
||
600 | } |
||
601 | } |
||
602 | break; |
||
603 | } |
||
604 | } |
||
605 | |||
606 | /* |
||
607 | * Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors), |
||
608 | * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on |
||
609 | * the visual format and color depth of the framebuffer, the DAC, the |
||
610 | * pseudo_palette, and the logo data will be adjusted accordingly. |
||
611 | * |
||
612 | * Case 1 - linux_logo_clut224: |
||
613 | * Color exceeds the number of console colors (16), thus we set the hardware DAC |
||
614 | * using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set. |
||
615 | * |
||
616 | * For visuals that require color info from the pseudo_palette, we also construct |
||
617 | * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags |
||
618 | * will be set. |
||
619 | * |
||
620 | * Case 2 - linux_logo_vga16: |
||
621 | * The number of colors just matches the console colors, thus there is no need |
||
622 | * to set the DAC or the pseudo_palette. However, the bitmap is packed, ie, |
||
623 | * each byte contains color information for two pixels (upper and lower nibble). |
||
624 | * To be consistent with fb_imageblit() usage, we therefore separate the two |
||
625 | * nibbles into separate bytes. The "depth" flag will be set to 4. |
||
626 | * |
||
627 | * Case 3 - linux_logo_mono: |
||
628 | * This is similar with Case 2. Each byte contains information for 8 pixels. |
||
629 | * We isolate each bit and expand each into a byte. The "depth" flag will |
||
630 | * be set to 1. |
||
631 | */ |
||
632 | static struct logo_data { |
||
633 | int depth; |
||
634 | int needs_directpalette; |
||
635 | int needs_truepalette; |
||
636 | int needs_cmapreset; |
||
637 | const struct linux_logo *logo; |
||
638 | } fb_logo; |
||
639 | |||
640 | int fb_prepare_logo(struct fb_info *info) |
||
641 | { |
||
642 | memset(&fb_logo, 0, sizeof(struct logo_data)); |
||
643 | |||
644 | switch (info->fix.visual) { |
||
645 | case FB_VISUAL_TRUECOLOR: |
||
646 | if (info->var.bits_per_pixel >= 8) |
||
647 | fb_logo.needs_truepalette = 1; |
||
648 | break; |
||
649 | case FB_VISUAL_DIRECTCOLOR: |
||
650 | if (info->var.bits_per_pixel >= 24) { |
||
651 | fb_logo.needs_directpalette = 1; |
||
652 | fb_logo.needs_cmapreset = 1; |
||
653 | } |
||
654 | break; |
||
655 | case FB_VISUAL_PSEUDOCOLOR: |
||
656 | fb_logo.needs_cmapreset = 1; |
||
657 | break; |
||
658 | } |
||
659 | |||
660 | /* Return if no suitable logo was found */ |
||
661 | fb_logo.logo = fb_find_logo(info->var.bits_per_pixel); |
||
662 | |||
663 | if (!fb_logo.logo || fb_logo.logo->height > info->var.yres) { |
||
664 | fb_logo.logo = NULL; |
||
665 | return 0; |
||
666 | } |
||
667 | /* What depth we asked for might be different from what we get */ |
||
668 | if (fb_logo.logo->type == LINUX_LOGO_CLUT224) |
||
669 | fb_logo.depth = 8; |
||
670 | else if (fb_logo.logo->type == LINUX_LOGO_VGA16) |
||
671 | fb_logo.depth = 4; |
||
672 | else |
||
673 | fb_logo.depth = 1; |
||
674 | return fb_logo.logo->height; |
||
675 | } |
||
676 | |||
677 | int fb_show_logo(struct fb_info *info) |
||
678 | { |
||
679 | u32 *palette = NULL, *saved_pseudo_palette = NULL; |
||
680 | unsigned char *logo_new = NULL; |
||
681 | struct fb_image image; |
||
682 | int x; |
||
683 | |||
684 | /* Return if the frame buffer is not mapped */ |
||
685 | if (fb_logo.logo == NULL) |
||
686 | return 0; |
||
687 | |||
688 | image.depth = fb_logo.depth; |
||
689 | image.data = fb_logo.logo->data; |
||
690 | |||
691 | if (fb_logo.needs_cmapreset) |
||
692 | fb_set_logocmap(info, fb_logo.logo); |
||
693 | |||
694 | if (fb_logo.needs_truepalette || |
||
695 | fb_logo.needs_directpalette) { |
||
696 | palette = kmalloc(256 * 4, GFP_KERNEL); |
||
697 | if (palette == NULL) |
||
698 | return 0; |
||
699 | |||
700 | if (fb_logo.needs_truepalette) |
||
701 | fb_set_logo_truepalette(info, fb_logo.logo, palette); |
||
702 | else |
||
703 | fb_set_logo_directpalette(info, fb_logo.logo, palette); |
||
704 | |||
705 | saved_pseudo_palette = info->pseudo_palette; |
||
706 | info->pseudo_palette = palette; |
||
707 | } |
||
708 | |||
709 | if (fb_logo.depth == 4) { |
||
710 | logo_new = kmalloc(fb_logo.logo->width * fb_logo.logo->height, |
||
711 | GFP_KERNEL); |
||
712 | if (logo_new == NULL) { |
||
713 | if (palette) |
||
714 | kfree(palette); |
||
715 | if (saved_pseudo_palette) |
||
716 | info->pseudo_palette = saved_pseudo_palette; |
||
717 | return 0; |
||
718 | } |
||
719 | image.data = logo_new; |
||
720 | fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth); |
||
721 | } |
||
722 | |||
723 | image.width = fb_logo.logo->width; |
||
724 | image.height = fb_logo.logo->height; |
||
725 | image.dy = 0; |
||
726 | |||
727 | for (x = 0; x < num_online_cpus() * (fb_logo.logo->width + 8) && |
||
728 | x <= info->var.xres-fb_logo.logo->width; x += (fb_logo.logo->width + 8)) { |
||
729 | image.dx = x; |
||
730 | info->fbops->fb_imageblit(info, &image); |
||
731 | //atomic_dec(&info->pixmap.count); |
||
732 | //smp_mb__after_atomic_dec(); |
||
733 | } |
||
734 | |||
735 | if (palette != NULL) |
||
736 | kfree(palette); |
||
737 | if (saved_pseudo_palette != NULL) |
||
738 | info->pseudo_palette = saved_pseudo_palette; |
||
739 | if (logo_new != NULL) |
||
740 | kfree(logo_new); |
||
741 | return fb_logo.logo->height; |
||
742 | } |
||
743 | #else |
||
744 | int fb_prepare_logo(struct fb_info *info) { return 0; } |
||
745 | int fb_show_logo(struct fb_info *info) { return 0; } |
||
746 | #endif /* CONFIG_LOGO */ |
||
747 | |||
748 | static int fbmem_read_proc(char *buf, char **start, off_t offset, |
||
749 | int len, int *eof, void *private) |
||
750 | { |
||
751 | struct fb_info **fi; |
||
752 | int clen; |
||
753 | |||
754 | clen = 0; |
||
755 | for (fi = registered_fb; fi < ®istered_fb[FB_MAX] && len < 4000; fi++) |
||
756 | if (*fi) |
||
757 | clen += sprintf26(buf + clen, "%d %s\n", |
||
758 | (*fi)->node, |
||
759 | (*fi)->fix.id); |
||
760 | *start = buf + offset; |
||
761 | if (clen > offset) |
||
762 | clen -= offset; |
||
763 | else |
||
764 | clen = 0; |
||
765 | return clen < len ? clen : len; |
||
766 | } |
||
767 | |||
768 | static ssize_t |
||
769 | fb_read(struct file *file, char *buf, size_t count, loff_t *ppos) |
||
770 | { |
||
771 | unsigned long p = *ppos; |
||
772 | struct inode *inode = file->f_dentry->d_inode; |
||
773 | int fbidx = iminor(inode); |
||
774 | struct fb_info *info = registered_fb[fbidx]; |
||
775 | |||
776 | if (!info || ! info->screen_base) |
||
777 | return -ENODEV; |
||
778 | |||
779 | if (info->fbops->fb_read) |
||
780 | return info->fbops->fb_read(file, buf, count, ppos); |
||
781 | |||
782 | if (p >= info->fix.smem_len) |
||
783 | return 0; |
||
784 | if (count >= info->fix.smem_len) |
||
785 | count = info->fix.smem_len; |
||
786 | if (count + p > info->fix.smem_len) |
||
787 | count = info->fix.smem_len - p; |
||
788 | if (info->fbops->fb_sync) |
||
789 | info->fbops->fb_sync(info); |
||
790 | if (count) { |
||
791 | char *base_addr; |
||
792 | |||
793 | base_addr = info->screen_base; |
||
794 | count -= copy_to_user(buf, base_addr+p, count); |
||
795 | if (!count) |
||
796 | return -EFAULT; |
||
797 | *ppos += count; |
||
798 | } |
||
799 | return count; |
||
800 | } |
||
801 | |||
802 | static ssize_t |
||
803 | fb_write(struct file *file, const char *buf, size_t count, loff_t *ppos) |
||
804 | { |
||
805 | unsigned long p = *ppos; |
||
806 | struct inode *inode = file->f_dentry->d_inode; |
||
807 | int fbidx = iminor(inode); |
||
808 | struct fb_info *info = registered_fb[fbidx]; |
||
809 | int err; |
||
810 | |||
811 | if (!info || !info->screen_base) |
||
812 | return -ENODEV; |
||
813 | |||
814 | if (info->fbops->fb_write) |
||
815 | return info->fbops->fb_write(file, buf, count, ppos); |
||
816 | |||
817 | if (p > info->fix.smem_len) |
||
818 | return -ENOSPC; |
||
819 | if (count >= info->fix.smem_len) |
||
820 | count = info->fix.smem_len; |
||
821 | err = 0; |
||
822 | if (count + p > info->fix.smem_len) { |
||
823 | count = info->fix.smem_len - p; |
||
824 | err = -ENOSPC; |
||
825 | } |
||
826 | if (info->fbops->fb_sync) |
||
827 | info->fbops->fb_sync(info); |
||
828 | if (count) { |
||
829 | char *base_addr; |
||
830 | |||
831 | base_addr = info->screen_base; |
||
832 | count -= copy_from_user(base_addr+p, buf, count); |
||
833 | *ppos += count; |
||
834 | err = -EFAULT; |
||
835 | } |
||
836 | if (count) |
||
837 | return count; |
||
838 | return err; |
||
839 | } |
||
840 | |||
841 | #ifdef CONFIG_KMOD |
||
842 | static void try_to_load(int fb) |
||
843 | { |
||
844 | request_module("fb%d", fb); |
||
845 | } |
||
846 | #endif /* CONFIG_KMOD */ |
||
847 | |||
848 | int |
||
849 | fb_cursor(struct fb_info *info, struct fb_cursor *sprite) |
||
850 | { |
||
851 | struct fb_cursor cursor; |
||
852 | int err; |
||
853 | |||
854 | if (copy_from_user(&cursor, sprite, sizeof(struct fb_cursor))) |
||
855 | return -EFAULT; |
||
856 | |||
857 | if (cursor.set & FB_CUR_SETCUR) |
||
858 | info->cursor.enable = 1; |
||
859 | |||
860 | if (cursor.set & FB_CUR_SETCMAP) { |
||
861 | err = fb_copy_cmap(&cursor.image.cmap, &sprite->image.cmap, 1); |
||
862 | if (err) |
||
863 | return err; |
||
864 | } |
||
865 | |||
866 | if (cursor.set & FB_CUR_SETSHAPE) { |
||
867 | int size = ((cursor.image.width + 7) >> 3) * cursor.image.height; |
||
868 | if ((cursor.image.height != info->cursor.image.height) || |
||
869 | (cursor.image.width != info->cursor.image.width)) |
||
870 | cursor.set |= FB_CUR_SETSIZE; |
||
871 | |||
872 | cursor.image.data = kmalloc(size, GFP_KERNEL); |
||
873 | if (!cursor.image.data) |
||
874 | return -ENOMEM; |
||
875 | |||
876 | cursor.mask = kmalloc(size, GFP_KERNEL); |
||
877 | if (!cursor.mask) { |
||
878 | kfree(cursor.image.data); |
||
879 | return -ENOMEM; |
||
880 | } |
||
881 | |||
882 | if (copy_from_user(&cursor.image.data, sprite->image.data, size) || |
||
883 | copy_from_user(cursor.mask, sprite->mask, size)) { |
||
884 | kfree(cursor.image.data); |
||
885 | kfree(cursor.mask); |
||
886 | return -EFAULT; |
||
887 | } |
||
888 | } |
||
889 | info->cursor.set = cursor.set; |
||
890 | info->cursor.rop = cursor.rop; |
||
891 | err = info->fbops->fb_cursor(info, &cursor); |
||
892 | return err; |
||
893 | } |
||
894 | |||
895 | int |
||
896 | fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) |
||
897 | { |
||
898 | int xoffset = var->xoffset; |
||
899 | int yoffset = var->yoffset; |
||
900 | int err; |
||
901 | |||
902 | if (xoffset < 0 || yoffset < 0 || !info->fbops->fb_pan_display || |
||
903 | xoffset + info->var.xres > info->var.xres_virtual || |
||
904 | yoffset + info->var.yres > info->var.yres_virtual) |
||
905 | return -EINVAL; |
||
906 | if ((err = info->fbops->fb_pan_display(var, info))) |
||
907 | return err; |
||
908 | info->var.xoffset = var->xoffset; |
||
909 | info->var.yoffset = var->yoffset; |
||
910 | if (var->vmode & FB_VMODE_YWRAP) |
||
911 | info->var.vmode |= FB_VMODE_YWRAP; |
||
912 | else |
||
913 | info->var.vmode &= ~FB_VMODE_YWRAP; |
||
914 | return 0; |
||
915 | } |
||
916 | |||
917 | int |
||
918 | fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) |
||
919 | { |
||
920 | int err; |
||
921 | |||
922 | if (memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) { |
||
923 | if (!info->fbops->fb_check_var) { |
||
924 | *var = info->var; |
||
925 | return 0; |
||
926 | } |
||
927 | |||
928 | if ((err = info->fbops->fb_check_var(var, info))) |
||
929 | return err; |
||
930 | |||
931 | if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { |
||
932 | info->var = *var; |
||
933 | |||
934 | if (info->fbops->fb_set_par) |
||
935 | info->fbops->fb_set_par(info); |
||
936 | |||
937 | fb_pan_display(info, &info->var); |
||
938 | |||
939 | fb_set_cmap(&info->cmap, 1, info); |
||
940 | } |
||
941 | } |
||
942 | return 0; |
||
943 | } |
||
944 | |||
945 | int |
||
946 | fb_blank(struct fb_info *info, int blank) |
||
947 | { |
||
948 | /* ??? Variable sized stack allocation. */ |
||
949 | u16 black[info->cmap.len]; |
||
950 | struct fb_cmap cmap; |
||
951 | |||
952 | if (info->fbops->fb_blank && !info->fbops->fb_blank(blank, info)) |
||
953 | return 0; |
||
954 | if (blank) { |
||
955 | memset(black, 0, info->cmap.len * sizeof(u16)); |
||
956 | cmap.red = cmap.green = cmap.blue = black; |
||
957 | cmap.transp = info->cmap.transp ? black : NULL; |
||
958 | cmap.start = info->cmap.start; |
||
959 | cmap.len = info->cmap.len; |
||
960 | } else |
||
961 | cmap = info->cmap; |
||
962 | return fb_set_cmap(&cmap, 1, info); |
||
963 | } |
||
964 | |||
484 | giacomo | 965 | int debx,deby,debb; |
966 | |||
472 | giacomo | 967 | int |
466 | giacomo | 968 | fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, |
969 | unsigned long arg) |
||
970 | { |
||
971 | int fbidx = iminor(inode); |
||
972 | struct fb_info *info = registered_fb[fbidx]; |
||
973 | struct fb_ops *fb = info->fbops; |
||
974 | struct fb_var_screeninfo var; |
||
975 | struct fb_fix_screeninfo fix; |
||
976 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE |
||
977 | struct fb_con2fbmap con2fb; |
||
978 | #endif |
||
979 | struct fb_cmap cmap; |
||
980 | int i; |
||
981 | |||
982 | if (!fb) |
||
983 | return -ENODEV; |
||
984 | switch (cmd) { |
||
985 | case FBIOGET_VSCREENINFO: |
||
986 | return copy_to_user((void *) arg, &info->var, |
||
987 | sizeof(var)) ? -EFAULT : 0; |
||
988 | case FBIOPUT_VSCREENINFO: |
||
989 | if (copy_from_user(&var, (void *) arg, sizeof(var))) |
||
990 | return -EFAULT; |
||
991 | i = fb_set_var(info, &var); |
||
992 | if (i) return i; |
||
993 | if (copy_to_user((void *) arg, &var, sizeof(var))) |
||
994 | return -EFAULT; |
||
995 | return 0; |
||
996 | case FBIOGET_FSCREENINFO: |
||
997 | return copy_to_user((void *) arg, &info->fix, |
||
998 | sizeof(fix)) ? -EFAULT : 0; |
||
999 | case FBIOPUTCMAP: |
||
1000 | if (copy_from_user(&cmap, (void *) arg, sizeof(cmap))) |
||
1001 | return -EFAULT; |
||
1002 | return (fb_set_cmap(&cmap, 0, info)); |
||
1003 | case FBIOGETCMAP: |
||
1004 | if (copy_from_user(&cmap, (void *) arg, sizeof(cmap))) |
||
1005 | return -EFAULT; |
||
1006 | return (fb_copy_cmap(&info->cmap, &cmap, 0)); |
||
1007 | case FBIOPAN_DISPLAY: |
||
1008 | if (copy_from_user(&var, (void *) arg, sizeof(var))) |
||
1009 | return -EFAULT; |
||
1010 | if ((i = fb_pan_display(info, &var))) |
||
1011 | return i; |
||
1012 | if (copy_to_user((void *) arg, &var, sizeof(var))) |
||
1013 | return -EFAULT; |
||
1014 | return 0; |
||
1015 | case FBIO_CURSOR: |
||
1016 | return (fb_cursor(info, (struct fb_cursor *) arg)); |
||
1017 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE |
||
1018 | case FBIOGET_CON2FBMAP: |
||
1019 | if (copy_from_user(&con2fb, (void *)arg, sizeof(con2fb))) |
||
1020 | return -EFAULT; |
||
1021 | if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) |
||
1022 | return -EINVAL; |
||
1023 | con2fb.framebuffer = con2fb_map[con2fb.console-1]; |
||
1024 | return copy_to_user((void *)arg, &con2fb, |
||
1025 | sizeof(con2fb)) ? -EFAULT : 0; |
||
1026 | case FBIOPUT_CON2FBMAP: |
||
1027 | if (copy_from_user(&con2fb, (void *)arg, sizeof(con2fb))) |
||
1028 | return - EFAULT; |
||
1029 | if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES) |
||
1030 | return -EINVAL; |
||
1031 | if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) |
||
1032 | return -EINVAL; |
||
1033 | #ifdef CONFIG_KMOD |
||
1034 | if (!registered_fb[con2fb.framebuffer]) |
||
1035 | try_to_load(con2fb.framebuffer); |
||
1036 | #endif /* CONFIG_KMOD */ |
||
1037 | if (!registered_fb[con2fb.framebuffer]) |
||
1038 | return -EINVAL; |
||
1039 | if (con2fb.console != 0) |
||
1040 | set_con2fb_map(con2fb.console-1, con2fb.framebuffer); |
||
1041 | else |
||
1042 | fb_console_init(); |
||
1043 | return 0; |
||
1044 | #endif /* CONFIG_FRAMEBUFFER_CONSOLE */ |
||
1045 | case FBIOBLANK: |
||
483 | giacomo | 1046 | return fb_blank(info, arg); |
466 | giacomo | 1047 | default: |
1048 | if (fb->fb_ioctl == NULL) |
||
1049 | return -EINVAL; |
||
1050 | return fb->fb_ioctl(inode, file, cmd, arg, info); |
||
1051 | } |
||
1052 | } |
||
1053 | |||
1054 | static int |
||
1055 | fb_mmap(struct file *file, struct vm_area_struct * vma) |
||
1056 | { |
||
1057 | int fbidx = iminor(file->f_dentry->d_inode); |
||
1058 | struct fb_info *info = registered_fb[fbidx]; |
||
1059 | struct fb_ops *fb = info->fbops; |
||
1060 | unsigned long off; |
||
1061 | #if !defined(__sparc__) || defined(__sparc_v9__) |
||
1062 | unsigned long start; |
||
1063 | u32 len; |
||
1064 | #endif |
||
1065 | |||
1066 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) |
||
1067 | return -EINVAL; |
||
1068 | off = vma->vm_pgoff << PAGE_SHIFT; |
||
1069 | if (!fb) |
||
1070 | return -ENODEV; |
||
1071 | if (fb->fb_mmap) { |
||
1072 | int res; |
||
1073 | lock_kernel(); |
||
1074 | res = fb->fb_mmap(info, file, vma); |
||
1075 | unlock_kernel(); |
||
1076 | return res; |
||
1077 | } |
||
1078 | |||
1079 | #if defined(__sparc__) && !defined(__sparc_v9__) |
||
1080 | /* Should never get here, all fb drivers should have their own |
||
1081 | mmap routines */ |
||
1082 | return -EINVAL; |
||
1083 | #else |
||
1084 | /* !sparc32... */ |
||
1085 | lock_kernel(); |
||
1086 | |||
1087 | /* frame buffer memory */ |
||
1088 | start = info->fix.smem_start; |
||
1089 | len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); |
||
1090 | if (off >= len) { |
||
1091 | /* memory mapped io */ |
||
1092 | off -= len; |
||
1093 | if (info->var.accel_flags) { |
||
1094 | unlock_kernel(); |
||
1095 | return -EINVAL; |
||
1096 | } |
||
1097 | start = info->fix.mmio_start; |
||
1098 | len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); |
||
1099 | } |
||
1100 | unlock_kernel(); |
||
1101 | start &= PAGE_MASK; |
||
1102 | if ((vma->vm_end - vma->vm_start + off) > len) |
||
1103 | return -EINVAL; |
||
1104 | off += start; |
||
1105 | vma->vm_pgoff = off >> PAGE_SHIFT; |
||
1106 | /* This is an IO map - tell maydump to skip this VMA */ |
||
1107 | vma->vm_flags |= VM_IO; |
||
1108 | #if defined(__sparc_v9__) |
||
1109 | vma->vm_flags |= (VM_SHM | VM_LOCKED); |
||
1110 | if (io_remap_page_range(vma, vma->vm_start, off, |
||
1111 | vma->vm_end - vma->vm_start, vma->vm_page_prot, 0)) |
||
1112 | return -EAGAIN; |
||
1113 | #else |
||
1114 | #if defined(__mc68000__) |
||
1115 | #if defined(CONFIG_SUN3) |
||
1116 | pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE; |
||
1117 | #elif defined(CONFIG_MMU) |
||
1118 | if (CPU_IS_020_OR_030) |
||
1119 | pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030; |
||
1120 | if (CPU_IS_040_OR_060) { |
||
1121 | pgprot_val(vma->vm_page_prot) &= _CACHEMASK040; |
||
1122 | /* Use no-cache mode, serialized */ |
||
1123 | pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S; |
||
1124 | } |
||
1125 | #endif |
||
1126 | #elif defined(__powerpc__) |
||
1127 | pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED; |
||
1128 | #elif defined(__alpha__) |
||
1129 | /* Caching is off in the I/O space quadrant by design. */ |
||
1130 | #elif defined(__i386__) || defined(__x86_64__) |
||
468 | giacomo | 1131 | //if (boot_cpu_data.x86 > 3) |
1132 | // pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; |
||
466 | giacomo | 1133 | #elif defined(__mips__) |
1134 | pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; |
||
1135 | pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; |
||
1136 | #elif defined(__sh__) |
||
1137 | pgprot_val(vma->vm_page_prot) &= ~_PAGE_CACHABLE; |
||
1138 | #elif defined(__hppa__) |
||
1139 | pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; |
||
1140 | #elif defined(__ia64__) || defined(__arm__) |
||
1141 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); |
||
1142 | #else |
||
1143 | #warning What do we have to do here?? |
||
1144 | #endif |
||
1145 | if (io_remap_page_range(vma, vma->vm_start, off, |
||
1146 | vma->vm_end - vma->vm_start, vma->vm_page_prot)) |
||
1147 | return -EAGAIN; |
||
1148 | #endif /* !__sparc_v9__ */ |
||
1149 | return 0; |
||
1150 | #endif /* !sparc32 */ |
||
1151 | } |
||
1152 | |||
470 | giacomo | 1153 | static int |
466 | giacomo | 1154 | fb_open(struct inode *inode, struct file *file) |
1155 | { |
||
1156 | int fbidx = iminor(inode); |
||
1157 | struct fb_info *info; |
||
477 | giacomo | 1158 | struct fb_fillrect rect; |
476 | giacomo | 1159 | int res = 0; |
466 | giacomo | 1160 | |
1161 | if (fbidx >= FB_MAX) |
||
1162 | return -ENODEV; |
||
1163 | #ifdef CONFIG_KMOD |
||
1164 | if (!(info = registered_fb[fbidx])) |
||
1165 | try_to_load(fbidx); |
||
1166 | #endif /* CONFIG_KMOD */ |
||
1167 | if (!(info = registered_fb[fbidx])) |
||
1168 | return -ENODEV; |
||
1169 | if (!try_module_get(info->fbops->owner)) |
||
1170 | return -ENODEV; |
||
1171 | if (info->fbops->fb_open) { |
||
1172 | res = info->fbops->fb_open(info,1); |
||
1173 | if (res) |
||
1174 | module_put(info->fbops->owner); |
||
1175 | } |
||
475 | giacomo | 1176 | |
484 | giacomo | 1177 | if (info->fbops->fb_set_par) |
1178 | info->fbops->fb_set_par(info); |
||
476 | giacomo | 1179 | fb_pan_display(info, &info->var); |
1180 | |||
477 | giacomo | 1181 | rect.dx = 0; |
1182 | rect.dy = 0; |
||
1183 | rect.width = info->var.xres; |
||
1184 | rect.height = info->var.yres; |
||
1185 | rect.color = 0; |
||
1186 | rect.rop = ROP_COPY; |
||
1187 | |||
1188 | cfb_fillrect(info,&rect); |
||
1189 | |||
476 | giacomo | 1190 | if (info->var.bits_per_pixel >= 8) { |
1191 | |||
1192 | fb_prepare_logo(info); |
||
1193 | fb_show_logo(info); |
||
1194 | |||
1195 | } |
||
475 | giacomo | 1196 | |
466 | giacomo | 1197 | return res; |
1198 | } |
||
1199 | |||
470 | giacomo | 1200 | static int |
466 | giacomo | 1201 | fb_release(struct inode *inode, struct file *file) |
1202 | { |
||
1203 | int fbidx = iminor(inode); |
||
1204 | struct fb_info *info; |
||
1205 | |||
1206 | lock_kernel(); |
||
1207 | info = registered_fb[fbidx]; |
||
1208 | if (info->fbops->fb_release) |
||
1209 | info->fbops->fb_release(info,1); |
||
1210 | module_put(info->fbops->owner); |
||
1211 | unlock_kernel(); |
||
475 | giacomo | 1212 | |
466 | giacomo | 1213 | return 0; |
1214 | } |
||
1215 | |||
1216 | static struct file_operations fb_fops = { |
||
1217 | .owner = THIS_MODULE, |
||
1218 | .read = fb_read, |
||
1219 | .write = fb_write, |
||
1220 | .ioctl = fb_ioctl, |
||
1221 | .mmap = fb_mmap, |
||
1222 | .open = fb_open, |
||
1223 | .release = fb_release, |
||
1224 | #ifdef HAVE_ARCH_FB_UNMAPPED_AREA |
||
1225 | .get_unmapped_area = get_fb_unmapped_area, |
||
1226 | #endif |
||
1227 | }; |
||
1228 | |||
1229 | /** |
||
1230 | * register_framebuffer - registers a frame buffer device |
||
1231 | * @fb_info: frame buffer info structure |
||
1232 | * |
||
1233 | * Registers a frame buffer device @fb_info. |
||
1234 | * |
||
1235 | * Returns negative errno on error, or zero for success. |
||
1236 | * |
||
1237 | */ |
||
1238 | |||
1239 | int |
||
1240 | register_framebuffer(struct fb_info *fb_info) |
||
1241 | { |
||
1242 | int i; |
||
1243 | |||
1244 | if (num_registered_fb == FB_MAX) |
||
1245 | return -ENXIO; |
||
1246 | num_registered_fb++; |
||
1247 | for (i = 0 ; i < FB_MAX; i++) |
||
1248 | if (!registered_fb[i]) |
||
1249 | break; |
||
1250 | fb_info->node = i; |
||
1251 | |||
1252 | if (fb_info->pixmap.addr == NULL) { |
||
1253 | fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); |
||
1254 | if (fb_info->pixmap.addr) { |
||
1255 | fb_info->pixmap.size = FBPIXMAPSIZE; |
||
1256 | fb_info->pixmap.buf_align = 1; |
||
1257 | fb_info->pixmap.scan_align = 1; |
||
1258 | fb_info->pixmap.flags = FB_PIXMAP_DEFAULT; |
||
1259 | } |
||
1260 | } |
||
1261 | fb_info->pixmap.offset = 0; |
||
1262 | if (fb_info->pixmap.outbuf == NULL) |
||
1263 | fb_info->pixmap.outbuf = sys_outbuf; |
||
1264 | if (fb_info->pixmap.inbuf == NULL) |
||
1265 | fb_info->pixmap.inbuf = sys_inbuf; |
||
1266 | spin_lock_init(&fb_info->pixmap.lock); |
||
1267 | |||
1268 | registered_fb[i] = fb_info; |
||
1269 | |||
1270 | devfs_mk_cdev(MKDEV(FB_MAJOR, i), |
||
1271 | S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i); |
||
1272 | return 0; |
||
1273 | } |
||
1274 | |||
1275 | |||
1276 | /** |
||
1277 | * unregister_framebuffer - releases a frame buffer device |
||
1278 | * @fb_info: frame buffer info structure |
||
1279 | * |
||
1280 | * Unregisters a frame buffer device @fb_info. |
||
1281 | * |
||
1282 | * Returns negative errno on error, or zero for success. |
||
1283 | * |
||
1284 | */ |
||
1285 | |||
1286 | int |
||
1287 | unregister_framebuffer(struct fb_info *fb_info) |
||
1288 | { |
||
1289 | int i; |
||
1290 | |||
1291 | i = fb_info->node; |
||
1292 | if (!registered_fb[i]) |
||
1293 | return -EINVAL; |
||
1294 | devfs_remove("fb/%d", i); |
||
1295 | |||
1296 | if (fb_info->pixmap.addr) |
||
1297 | kfree(fb_info->pixmap.addr); |
||
1298 | registered_fb[i]=NULL; |
||
1299 | num_registered_fb--; |
||
1300 | return 0; |
||
1301 | } |
||
1302 | |||
1303 | |||
1304 | /** |
||
1305 | * fbmem_init - init frame buffer subsystem |
||
1306 | * |
||
1307 | * Initialize the frame buffer subsystem. |
||
1308 | * |
||
1309 | * NOTE: This function is _only_ to be called by drivers/char/mem.c. |
||
1310 | * |
||
1311 | */ |
||
1312 | |||
1313 | void __init |
||
1314 | fbmem_init(void) |
||
1315 | { |
||
1316 | int i; |
||
1317 | |||
1318 | create_proc_read_entry("fb", 0, 0, fbmem_read_proc, NULL); |
||
1319 | |||
1320 | devfs_mk_dir("fb"); |
||
1321 | if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) |
||
1322 | printk("unable to get major %d for fb devs\n", FB_MAJOR); |
||
1323 | |||
1324 | #ifdef CONFIG_FB_OF |
||
1325 | if (ofonly) { |
||
1326 | offb_init(); |
||
1327 | return; |
||
1328 | } |
||
1329 | #endif |
||
1330 | |||
1331 | /* |
||
1332 | * Probe for all builtin frame buffer devices |
||
1333 | */ |
||
1334 | for (i = 0; i < num_pref_init_funcs; i++) |
||
1335 | pref_init_funcs[i](); |
||
1336 | |||
1337 | for (i = 0; i < NUM_FB_DRIVERS; i++) |
||
1338 | if (fb_drivers[i].init) |
||
1339 | fb_drivers[i].init(); |
||
1340 | } |
||
1341 | |||
469 | giacomo | 1342 | extern int linuxcomp_setfd(struct inode *i, int i_rdev); |
466 | giacomo | 1343 | |
469 | giacomo | 1344 | /* Shark Inode emulation */ |
1022 | mauro | 1345 | int fb_open_inode(int num) |
1346 | { |
||
1347 | struct inode *i; |
||
469 | giacomo | 1348 | |
1022 | mauro | 1349 | i = (struct inode *)kmalloc(sizeof(struct inode),GFP_KERNEL); |
1350 | |||
1351 | linuxcomp_setfd(i,num); |
||
1352 | |||
1353 | if (fb_open(i,NULL)) { |
||
1354 | kfree(i); |
||
1355 | return -1; |
||
1356 | } |
||
1357 | |||
1358 | kfree(i); |
||
1359 | return 0; |
||
470 | giacomo | 1360 | } |
1361 | |||
1362 | /* Shark Inode emulation */ |
||
1022 | mauro | 1363 | int fb_close_inode(int num) |
1364 | { |
||
1365 | struct inode *i; |
||
470 | giacomo | 1366 | |
1022 | mauro | 1367 | i = (struct inode *)kmalloc(sizeof(struct inode),GFP_KERNEL); |
469 | giacomo | 1368 | |
1022 | mauro | 1369 | linuxcomp_setfd(i,num); |
469 | giacomo | 1370 | |
1022 | mauro | 1371 | if(fb_release(i,NULL)) { |
1372 | kfree(i); |
||
1373 | return -1; |
||
1374 | } |
||
469 | giacomo | 1375 | |
1022 | mauro | 1376 | kfree(i); |
469 | giacomo | 1377 | |
1022 | mauro | 1378 | return 0; |
1379 | } |
||
470 | giacomo | 1380 | |
1022 | mauro | 1381 | int fb_set_mode_inode(int num, unsigned char *modeopt) |
1382 | { |
||
1383 | struct fb_var_screeninfo var; |
||
1384 | struct fb_info *info = registered_fb[num]; |
||
1385 | struct fb_fillrect rect; |
||
469 | giacomo | 1386 | |
1022 | mauro | 1387 | fb_find_mode(&var,info,modeopt,NULL,0,NULL,0); |
469 | giacomo | 1388 | |
1022 | mauro | 1389 | var.xoffset = 0; |
1390 | var.yoffset = 0; |
||
1391 | var.xres_virtual = var.xres; |
||
1392 | var.yres_virtual = var.yres; |
||
472 | giacomo | 1393 | |
1022 | mauro | 1394 | if (info->fbops->fb_check_var) |
1395 | info->fbops->fb_check_var(&var, info); |
||
472 | giacomo | 1396 | |
1022 | mauro | 1397 | memcpy(&(info->var),&var,sizeof(struct fb_var_screeninfo)); |
483 | giacomo | 1398 | |
1022 | mauro | 1399 | if (info->fbops->fb_set_par) |
1400 | info->fbops->fb_set_par(info); |
||
1401 | fb_pan_display(info, &info->var); |
||
472 | giacomo | 1402 | |
1022 | mauro | 1403 | if (info->var.bits_per_pixel > 8) |
1404 | info->fix.visual = FB_VISUAL_TRUECOLOR; |
||
484 | giacomo | 1405 | |
1022 | mauro | 1406 | rect.dx = 0; |
1407 | rect.dy = 0; |
||
1408 | rect.width = info->var.xres; |
||
1409 | rect.height = info->var.yres; |
||
1410 | rect.color = 0; |
||
1411 | rect.rop = ROP_COPY; |
||
484 | giacomo | 1412 | |
1022 | mauro | 1413 | cfb_fillrect(info,&rect); |
484 | giacomo | 1414 | |
1022 | mauro | 1415 | return 0; |
472 | giacomo | 1416 | } |
1417 | |||
466 | giacomo | 1418 | /** |
1419 | * video_setup - process command line options |
||
1420 | * @options: string of options |
||
1421 | * |
||
1422 | * Process command line options for frame buffer subsystem. |
||
1423 | * |
||
1424 | * NOTE: This function is a __setup and __init function. |
||
1425 | * |
||
1426 | * Returns zero. |
||
1427 | * |
||
1428 | */ |
||
1429 | |||
1430 | int __init video_setup(char *options) |
||
1431 | { |
||
1432 | int i, j; |
||
1433 | |||
1434 | if (!options || !*options) |
||
1435 | return 0; |
||
1436 | |||
1437 | #ifdef CONFIG_FB_OF |
||
1438 | if (!strcmp(options, "ofonly")) { |
||
1439 | ofonly = 1; |
||
1440 | return 0; |
||
1441 | } |
||
1442 | #endif |
||
1443 | |||
1444 | if (num_pref_init_funcs == FB_MAX) |
||
1445 | return 0; |
||
1446 | |||
1447 | for (i = 0; i < NUM_FB_DRIVERS; i++) { |
||
1448 | j = strlen(fb_drivers[i].name); |
||
1449 | if (!strncmp(options, fb_drivers[i].name, j) && |
||
1450 | options[j] == ':') { |
||
1451 | if (!strcmp(options+j+1, "off")) |
||
1452 | fb_drivers[i].init = NULL; |
||
1453 | else { |
||
1454 | if (fb_drivers[i].init) { |
||
1455 | pref_init_funcs[num_pref_init_funcs++] = |
||
1456 | fb_drivers[i].init; |
||
1457 | fb_drivers[i].init = NULL; |
||
1458 | } |
||
1459 | if (fb_drivers[i].setup) |
||
1460 | fb_drivers[i].setup(options+j+1); |
||
1461 | } |
||
1462 | return 0; |
||
1463 | } |
||
1464 | } |
||
1465 | |||
1466 | /* |
||
1467 | * If we get here no fb was specified. |
||
1468 | * We consider the argument to be a global video mode option. |
||
1469 | */ |
||
468 | giacomo | 1470 | //global_mode_option = options; |
466 | giacomo | 1471 | return 0; |
1472 | } |
||
1473 | |||
1474 | __setup("video=", video_setup); |
||
1475 | |||
1476 | /* |
||
1477 | * Visible symbols for modules |
||
1478 | */ |
||
1479 | |||
1480 | EXPORT_SYMBOL(register_framebuffer); |
||
1481 | EXPORT_SYMBOL(unregister_framebuffer); |
||
1482 | EXPORT_SYMBOL(num_registered_fb); |
||
1483 | EXPORT_SYMBOL(registered_fb); |
||
1484 | EXPORT_SYMBOL(fb_prepare_logo); |
||
1485 | EXPORT_SYMBOL(fb_show_logo); |
||
1486 | EXPORT_SYMBOL(fb_set_var); |
||
1487 | EXPORT_SYMBOL(fb_blank); |
||
1488 | EXPORT_SYMBOL(fb_pan_display); |
||
1489 | EXPORT_SYMBOL(fb_get_buffer_offset); |
||
1490 | EXPORT_SYMBOL(move_buf_unaligned); |
||
1491 | EXPORT_SYMBOL(move_buf_aligned); |
||
1492 | |||
1493 | MODULE_LICENSE("GPL"); |