diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/video/fbmem.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/video/fbmem.c')
-rw-r--r-- | drivers/video/fbmem.c | 1371 |
1 files changed, 1371 insertions, 0 deletions
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c new file mode 100644 index 000000000000..25f460ca0daf --- /dev/null +++ b/drivers/video/fbmem.c | |||
@@ -0,0 +1,1371 @@ | |||
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 <linux/config.h> | ||
15 | #include <linux/module.h> | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/smp_lock.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/major.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/mm.h> | ||
25 | #include <linux/mman.h> | ||
26 | #include <linux/tty.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/linux_logo.h> | ||
29 | #include <linux/proc_fs.h> | ||
30 | #include <linux/console.h> | ||
31 | #ifdef CONFIG_KMOD | ||
32 | #include <linux/kmod.h> | ||
33 | #endif | ||
34 | #include <linux/devfs_fs_kernel.h> | ||
35 | #include <linux/err.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <linux/device.h> | ||
38 | #include <linux/efi.h> | ||
39 | |||
40 | #if defined(__mc68000__) || defined(CONFIG_APUS) | ||
41 | #include <asm/setup.h> | ||
42 | #endif | ||
43 | |||
44 | #include <asm/io.h> | ||
45 | #include <asm/uaccess.h> | ||
46 | #include <asm/page.h> | ||
47 | #include <asm/pgtable.h> | ||
48 | |||
49 | #include <linux/fb.h> | ||
50 | |||
51 | /* | ||
52 | * Frame buffer device initialization and setup routines | ||
53 | */ | ||
54 | |||
55 | #define FBPIXMAPSIZE (1024 * 8) | ||
56 | |||
57 | static struct notifier_block *fb_notifier_list; | ||
58 | struct fb_info *registered_fb[FB_MAX]; | ||
59 | int num_registered_fb; | ||
60 | |||
61 | /* | ||
62 | * Helpers | ||
63 | */ | ||
64 | |||
65 | int fb_get_color_depth(struct fb_var_screeninfo *var) | ||
66 | { | ||
67 | if (var->green.length == var->blue.length && | ||
68 | var->green.length == var->red.length && | ||
69 | !var->green.offset && !var->blue.offset && | ||
70 | !var->red.offset) | ||
71 | return var->green.length; | ||
72 | else | ||
73 | return (var->green.length + var->red.length + | ||
74 | var->blue.length); | ||
75 | } | ||
76 | EXPORT_SYMBOL(fb_get_color_depth); | ||
77 | |||
78 | /* | ||
79 | * Drawing helpers. | ||
80 | */ | ||
81 | void fb_iomove_buf_aligned(struct fb_info *info, struct fb_pixmap *buf, | ||
82 | u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, | ||
83 | u32 height) | ||
84 | { | ||
85 | int i; | ||
86 | |||
87 | for (i = height; i--; ) { | ||
88 | buf->outbuf(info, dst, src, s_pitch); | ||
89 | src += s_pitch; | ||
90 | dst += d_pitch; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | void fb_sysmove_buf_aligned(struct fb_info *info, struct fb_pixmap *buf, | ||
95 | u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, | ||
96 | u32 height) | ||
97 | { | ||
98 | int i, j; | ||
99 | |||
100 | for (i = height; i--; ) { | ||
101 | for (j = 0; j < s_pitch; j++) | ||
102 | dst[j] = src[j]; | ||
103 | src += s_pitch; | ||
104 | dst += d_pitch; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | void fb_iomove_buf_unaligned(struct fb_info *info, struct fb_pixmap *buf, | ||
109 | u8 *dst, u32 d_pitch, u8 *src, u32 idx, | ||
110 | u32 height, u32 shift_high, u32 shift_low, | ||
111 | u32 mod) | ||
112 | { | ||
113 | u8 mask = (u8) (0xfff << shift_high), tmp; | ||
114 | int i, j; | ||
115 | |||
116 | for (i = height; i--; ) { | ||
117 | for (j = 0; j < idx; j++) { | ||
118 | tmp = buf->inbuf(info, dst+j); | ||
119 | tmp &= mask; | ||
120 | tmp |= *src >> shift_low; | ||
121 | buf->outbuf(info, dst+j, &tmp, 1); | ||
122 | tmp = *src << shift_high; | ||
123 | buf->outbuf(info, dst+j+1, &tmp, 1); | ||
124 | src++; | ||
125 | } | ||
126 | tmp = buf->inbuf(info, dst+idx); | ||
127 | tmp &= mask; | ||
128 | tmp |= *src >> shift_low; | ||
129 | buf->outbuf(info, dst+idx, &tmp, 1); | ||
130 | if (shift_high < mod) { | ||
131 | tmp = *src << shift_high; | ||
132 | buf->outbuf(info, dst+idx+1, &tmp, 1); | ||
133 | } | ||
134 | src++; | ||
135 | dst += d_pitch; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | void fb_sysmove_buf_unaligned(struct fb_info *info, struct fb_pixmap *buf, | ||
140 | u8 *dst, u32 d_pitch, u8 *src, u32 idx, | ||
141 | u32 height, u32 shift_high, u32 shift_low, | ||
142 | u32 mod) | ||
143 | { | ||
144 | u8 mask = (u8) (0xfff << shift_high), tmp; | ||
145 | int i, j; | ||
146 | |||
147 | for (i = height; i--; ) { | ||
148 | for (j = 0; j < idx; j++) { | ||
149 | tmp = dst[j]; | ||
150 | tmp &= mask; | ||
151 | tmp |= *src >> shift_low; | ||
152 | dst[j] = tmp; | ||
153 | tmp = *src << shift_high; | ||
154 | dst[j+1] = tmp; | ||
155 | src++; | ||
156 | } | ||
157 | tmp = dst[idx]; | ||
158 | tmp &= mask; | ||
159 | tmp |= *src >> shift_low; | ||
160 | dst[idx] = tmp; | ||
161 | if (shift_high < mod) { | ||
162 | tmp = *src << shift_high; | ||
163 | dst[idx+1] = tmp; | ||
164 | } | ||
165 | src++; | ||
166 | dst += d_pitch; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * we need to lock this section since fb_cursor | ||
172 | * may use fb_imageblit() | ||
173 | */ | ||
174 | char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size) | ||
175 | { | ||
176 | u32 align = buf->buf_align - 1, offset; | ||
177 | char *addr = buf->addr; | ||
178 | |||
179 | /* If IO mapped, we need to sync before access, no sharing of | ||
180 | * the pixmap is done | ||
181 | */ | ||
182 | if (buf->flags & FB_PIXMAP_IO) { | ||
183 | if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC)) | ||
184 | info->fbops->fb_sync(info); | ||
185 | return addr; | ||
186 | } | ||
187 | |||
188 | /* See if we fit in the remaining pixmap space */ | ||
189 | offset = buf->offset + align; | ||
190 | offset &= ~align; | ||
191 | if (offset + size > buf->size) { | ||
192 | /* We do not fit. In order to be able to re-use the buffer, | ||
193 | * we must ensure no asynchronous DMA'ing or whatever operation | ||
194 | * is in progress, we sync for that. | ||
195 | */ | ||
196 | if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC)) | ||
197 | info->fbops->fb_sync(info); | ||
198 | offset = 0; | ||
199 | } | ||
200 | buf->offset = offset + size; | ||
201 | addr += offset; | ||
202 | |||
203 | return addr; | ||
204 | } | ||
205 | |||
206 | #ifdef CONFIG_LOGO | ||
207 | #include <linux/linux_logo.h> | ||
208 | |||
209 | static inline unsigned safe_shift(unsigned d, int n) | ||
210 | { | ||
211 | return n < 0 ? d >> -n : d << n; | ||
212 | } | ||
213 | |||
214 | static void fb_set_logocmap(struct fb_info *info, | ||
215 | const struct linux_logo *logo) | ||
216 | { | ||
217 | struct fb_cmap palette_cmap; | ||
218 | u16 palette_green[16]; | ||
219 | u16 palette_blue[16]; | ||
220 | u16 palette_red[16]; | ||
221 | int i, j, n; | ||
222 | const unsigned char *clut = logo->clut; | ||
223 | |||
224 | palette_cmap.start = 0; | ||
225 | palette_cmap.len = 16; | ||
226 | palette_cmap.red = palette_red; | ||
227 | palette_cmap.green = palette_green; | ||
228 | palette_cmap.blue = palette_blue; | ||
229 | palette_cmap.transp = NULL; | ||
230 | |||
231 | for (i = 0; i < logo->clutsize; i += n) { | ||
232 | n = logo->clutsize - i; | ||
233 | /* palette_cmap provides space for only 16 colors at once */ | ||
234 | if (n > 16) | ||
235 | n = 16; | ||
236 | palette_cmap.start = 32 + i; | ||
237 | palette_cmap.len = n; | ||
238 | for (j = 0; j < n; ++j) { | ||
239 | palette_cmap.red[j] = clut[0] << 8 | clut[0]; | ||
240 | palette_cmap.green[j] = clut[1] << 8 | clut[1]; | ||
241 | palette_cmap.blue[j] = clut[2] << 8 | clut[2]; | ||
242 | clut += 3; | ||
243 | } | ||
244 | fb_set_cmap(&palette_cmap, info); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | static void fb_set_logo_truepalette(struct fb_info *info, | ||
249 | const struct linux_logo *logo, | ||
250 | u32 *palette) | ||
251 | { | ||
252 | unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff }; | ||
253 | unsigned char redmask, greenmask, bluemask; | ||
254 | int redshift, greenshift, blueshift; | ||
255 | int i; | ||
256 | const unsigned char *clut = logo->clut; | ||
257 | |||
258 | /* | ||
259 | * We have to create a temporary palette since console palette is only | ||
260 | * 16 colors long. | ||
261 | */ | ||
262 | /* Bug: Doesn't obey msb_right ... (who needs that?) */ | ||
263 | redmask = mask[info->var.red.length < 8 ? info->var.red.length : 8]; | ||
264 | greenmask = mask[info->var.green.length < 8 ? info->var.green.length : 8]; | ||
265 | bluemask = mask[info->var.blue.length < 8 ? info->var.blue.length : 8]; | ||
266 | redshift = info->var.red.offset - (8 - info->var.red.length); | ||
267 | greenshift = info->var.green.offset - (8 - info->var.green.length); | ||
268 | blueshift = info->var.blue.offset - (8 - info->var.blue.length); | ||
269 | |||
270 | for ( i = 0; i < logo->clutsize; i++) { | ||
271 | palette[i+32] = (safe_shift((clut[0] & redmask), redshift) | | ||
272 | safe_shift((clut[1] & greenmask), greenshift) | | ||
273 | safe_shift((clut[2] & bluemask), blueshift)); | ||
274 | clut += 3; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | static void fb_set_logo_directpalette(struct fb_info *info, | ||
279 | const struct linux_logo *logo, | ||
280 | u32 *palette) | ||
281 | { | ||
282 | int redshift, greenshift, blueshift; | ||
283 | int i; | ||
284 | |||
285 | redshift = info->var.red.offset; | ||
286 | greenshift = info->var.green.offset; | ||
287 | blueshift = info->var.blue.offset; | ||
288 | |||
289 | for (i = 32; i < logo->clutsize; i++) | ||
290 | palette[i] = i << redshift | i << greenshift | i << blueshift; | ||
291 | } | ||
292 | |||
293 | static void fb_set_logo(struct fb_info *info, | ||
294 | const struct linux_logo *logo, u8 *dst, | ||
295 | int depth) | ||
296 | { | ||
297 | int i, j, k, fg = 1; | ||
298 | const u8 *src = logo->data; | ||
299 | u8 d, xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0; | ||
300 | |||
301 | if (fb_get_color_depth(&info->var) == 3) | ||
302 | fg = 7; | ||
303 | |||
304 | switch (depth) { | ||
305 | case 4: | ||
306 | for (i = 0; i < logo->height; i++) | ||
307 | for (j = 0; j < logo->width; src++) { | ||
308 | *dst++ = *src >> 4; | ||
309 | j++; | ||
310 | if (j < logo->width) { | ||
311 | *dst++ = *src & 0x0f; | ||
312 | j++; | ||
313 | } | ||
314 | } | ||
315 | break; | ||
316 | case 1: | ||
317 | for (i = 0; i < logo->height; i++) { | ||
318 | for (j = 0; j < logo->width; src++) { | ||
319 | d = *src ^ xor; | ||
320 | for (k = 7; k >= 0; k--) { | ||
321 | *dst++ = ((d >> k) & 1) ? fg : 0; | ||
322 | j++; | ||
323 | } | ||
324 | } | ||
325 | } | ||
326 | break; | ||
327 | } | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors), | ||
332 | * linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on | ||
333 | * the visual format and color depth of the framebuffer, the DAC, the | ||
334 | * pseudo_palette, and the logo data will be adjusted accordingly. | ||
335 | * | ||
336 | * Case 1 - linux_logo_clut224: | ||
337 | * Color exceeds the number of console colors (16), thus we set the hardware DAC | ||
338 | * using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set. | ||
339 | * | ||
340 | * For visuals that require color info from the pseudo_palette, we also construct | ||
341 | * one for temporary use. The "needs_directpalette" or "needs_truepalette" flags | ||
342 | * will be set. | ||
343 | * | ||
344 | * Case 2 - linux_logo_vga16: | ||
345 | * The number of colors just matches the console colors, thus there is no need | ||
346 | * to set the DAC or the pseudo_palette. However, the bitmap is packed, ie, | ||
347 | * each byte contains color information for two pixels (upper and lower nibble). | ||
348 | * To be consistent with fb_imageblit() usage, we therefore separate the two | ||
349 | * nibbles into separate bytes. The "depth" flag will be set to 4. | ||
350 | * | ||
351 | * Case 3 - linux_logo_mono: | ||
352 | * This is similar with Case 2. Each byte contains information for 8 pixels. | ||
353 | * We isolate each bit and expand each into a byte. The "depth" flag will | ||
354 | * be set to 1. | ||
355 | */ | ||
356 | static struct logo_data { | ||
357 | int depth; | ||
358 | int needs_directpalette; | ||
359 | int needs_truepalette; | ||
360 | int needs_cmapreset; | ||
361 | const struct linux_logo *logo; | ||
362 | } fb_logo; | ||
363 | |||
364 | int fb_prepare_logo(struct fb_info *info) | ||
365 | { | ||
366 | int depth = fb_get_color_depth(&info->var); | ||
367 | |||
368 | memset(&fb_logo, 0, sizeof(struct logo_data)); | ||
369 | |||
370 | if (info->flags & FBINFO_MISC_TILEBLITTING) | ||
371 | return 0; | ||
372 | |||
373 | if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { | ||
374 | depth = info->var.blue.length; | ||
375 | if (info->var.red.length < depth) | ||
376 | depth = info->var.red.length; | ||
377 | if (info->var.green.length < depth) | ||
378 | depth = info->var.green.length; | ||
379 | } | ||
380 | |||
381 | if (depth >= 8) { | ||
382 | switch (info->fix.visual) { | ||
383 | case FB_VISUAL_TRUECOLOR: | ||
384 | fb_logo.needs_truepalette = 1; | ||
385 | break; | ||
386 | case FB_VISUAL_DIRECTCOLOR: | ||
387 | fb_logo.needs_directpalette = 1; | ||
388 | fb_logo.needs_cmapreset = 1; | ||
389 | break; | ||
390 | case FB_VISUAL_PSEUDOCOLOR: | ||
391 | fb_logo.needs_cmapreset = 1; | ||
392 | break; | ||
393 | } | ||
394 | } | ||
395 | |||
396 | /* Return if no suitable logo was found */ | ||
397 | fb_logo.logo = fb_find_logo(depth); | ||
398 | |||
399 | if (!fb_logo.logo || fb_logo.logo->height > info->var.yres) { | ||
400 | fb_logo.logo = NULL; | ||
401 | return 0; | ||
402 | } | ||
403 | /* What depth we asked for might be different from what we get */ | ||
404 | if (fb_logo.logo->type == LINUX_LOGO_CLUT224) | ||
405 | fb_logo.depth = 8; | ||
406 | else if (fb_logo.logo->type == LINUX_LOGO_VGA16) | ||
407 | fb_logo.depth = 4; | ||
408 | else | ||
409 | fb_logo.depth = 1; | ||
410 | return fb_logo.logo->height; | ||
411 | } | ||
412 | |||
413 | int fb_show_logo(struct fb_info *info) | ||
414 | { | ||
415 | u32 *palette = NULL, *saved_pseudo_palette = NULL; | ||
416 | unsigned char *logo_new = NULL; | ||
417 | struct fb_image image; | ||
418 | int x; | ||
419 | |||
420 | /* Return if the frame buffer is not mapped or suspended */ | ||
421 | if (fb_logo.logo == NULL || info->state != FBINFO_STATE_RUNNING) | ||
422 | return 0; | ||
423 | |||
424 | image.depth = 8; | ||
425 | image.data = fb_logo.logo->data; | ||
426 | |||
427 | if (fb_logo.needs_cmapreset) | ||
428 | fb_set_logocmap(info, fb_logo.logo); | ||
429 | |||
430 | if (fb_logo.needs_truepalette || | ||
431 | fb_logo.needs_directpalette) { | ||
432 | palette = kmalloc(256 * 4, GFP_KERNEL); | ||
433 | if (palette == NULL) | ||
434 | return 0; | ||
435 | |||
436 | if (fb_logo.needs_truepalette) | ||
437 | fb_set_logo_truepalette(info, fb_logo.logo, palette); | ||
438 | else | ||
439 | fb_set_logo_directpalette(info, fb_logo.logo, palette); | ||
440 | |||
441 | saved_pseudo_palette = info->pseudo_palette; | ||
442 | info->pseudo_palette = palette; | ||
443 | } | ||
444 | |||
445 | if (fb_logo.depth <= 4) { | ||
446 | logo_new = kmalloc(fb_logo.logo->width * fb_logo.logo->height, | ||
447 | GFP_KERNEL); | ||
448 | if (logo_new == NULL) { | ||
449 | kfree(palette); | ||
450 | if (saved_pseudo_palette) | ||
451 | info->pseudo_palette = saved_pseudo_palette; | ||
452 | return 0; | ||
453 | } | ||
454 | image.data = logo_new; | ||
455 | fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth); | ||
456 | } | ||
457 | |||
458 | image.width = fb_logo.logo->width; | ||
459 | image.height = fb_logo.logo->height; | ||
460 | image.dy = 0; | ||
461 | |||
462 | for (x = 0; x < num_online_cpus() * (fb_logo.logo->width + 8) && | ||
463 | x <= info->var.xres-fb_logo.logo->width; x += (fb_logo.logo->width + 8)) { | ||
464 | image.dx = x; | ||
465 | info->fbops->fb_imageblit(info, &image); | ||
466 | } | ||
467 | |||
468 | kfree(palette); | ||
469 | if (saved_pseudo_palette != NULL) | ||
470 | info->pseudo_palette = saved_pseudo_palette; | ||
471 | kfree(logo_new); | ||
472 | return fb_logo.logo->height; | ||
473 | } | ||
474 | #else | ||
475 | int fb_prepare_logo(struct fb_info *info) { return 0; } | ||
476 | int fb_show_logo(struct fb_info *info) { return 0; } | ||
477 | #endif /* CONFIG_LOGO */ | ||
478 | |||
479 | static int fbmem_read_proc(char *buf, char **start, off_t offset, | ||
480 | int len, int *eof, void *private) | ||
481 | { | ||
482 | struct fb_info **fi; | ||
483 | int clen; | ||
484 | |||
485 | clen = 0; | ||
486 | for (fi = registered_fb; fi < ®istered_fb[FB_MAX] && len < 4000; fi++) | ||
487 | if (*fi) | ||
488 | clen += sprintf(buf + clen, "%d %s\n", | ||
489 | (*fi)->node, | ||
490 | (*fi)->fix.id); | ||
491 | *start = buf + offset; | ||
492 | if (clen > offset) | ||
493 | clen -= offset; | ||
494 | else | ||
495 | clen = 0; | ||
496 | return clen < len ? clen : len; | ||
497 | } | ||
498 | |||
499 | static ssize_t | ||
500 | fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
501 | { | ||
502 | unsigned long p = *ppos; | ||
503 | struct inode *inode = file->f_dentry->d_inode; | ||
504 | int fbidx = iminor(inode); | ||
505 | struct fb_info *info = registered_fb[fbidx]; | ||
506 | u32 *buffer, *dst; | ||
507 | u32 __iomem *src; | ||
508 | int c, i, cnt = 0, err = 0; | ||
509 | unsigned long total_size; | ||
510 | |||
511 | if (!info || ! info->screen_base) | ||
512 | return -ENODEV; | ||
513 | |||
514 | if (info->state != FBINFO_STATE_RUNNING) | ||
515 | return -EPERM; | ||
516 | |||
517 | if (info->fbops->fb_read) | ||
518 | return info->fbops->fb_read(file, buf, count, ppos); | ||
519 | |||
520 | total_size = info->screen_size; | ||
521 | if (total_size == 0) | ||
522 | total_size = info->fix.smem_len; | ||
523 | |||
524 | if (p >= total_size) | ||
525 | return 0; | ||
526 | if (count >= total_size) | ||
527 | count = total_size; | ||
528 | if (count + p > total_size) | ||
529 | count = total_size - p; | ||
530 | |||
531 | cnt = 0; | ||
532 | buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, | ||
533 | GFP_KERNEL); | ||
534 | if (!buffer) | ||
535 | return -ENOMEM; | ||
536 | |||
537 | src = (u32 __iomem *) (info->screen_base + p); | ||
538 | |||
539 | if (info->fbops->fb_sync) | ||
540 | info->fbops->fb_sync(info); | ||
541 | |||
542 | while (count) { | ||
543 | c = (count > PAGE_SIZE) ? PAGE_SIZE : count; | ||
544 | dst = buffer; | ||
545 | for (i = c >> 2; i--; ) | ||
546 | *dst++ = fb_readl(src++); | ||
547 | if (c & 3) { | ||
548 | u8 *dst8 = (u8 *) dst; | ||
549 | u8 __iomem *src8 = (u8 __iomem *) src; | ||
550 | |||
551 | for (i = c & 3; i--;) | ||
552 | *dst8++ = fb_readb(src8++); | ||
553 | |||
554 | src = (u32 __iomem *) src8; | ||
555 | } | ||
556 | |||
557 | if (copy_to_user(buf, buffer, c)) { | ||
558 | err = -EFAULT; | ||
559 | break; | ||
560 | } | ||
561 | *ppos += c; | ||
562 | buf += c; | ||
563 | cnt += c; | ||
564 | count -= c; | ||
565 | } | ||
566 | |||
567 | kfree(buffer); | ||
568 | return (err) ? err : cnt; | ||
569 | } | ||
570 | |||
571 | static ssize_t | ||
572 | fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | ||
573 | { | ||
574 | unsigned long p = *ppos; | ||
575 | struct inode *inode = file->f_dentry->d_inode; | ||
576 | int fbidx = iminor(inode); | ||
577 | struct fb_info *info = registered_fb[fbidx]; | ||
578 | u32 *buffer, *src; | ||
579 | u32 __iomem *dst; | ||
580 | int c, i, cnt = 0, err; | ||
581 | unsigned long total_size; | ||
582 | |||
583 | if (!info || !info->screen_base) | ||
584 | return -ENODEV; | ||
585 | |||
586 | if (info->state != FBINFO_STATE_RUNNING) | ||
587 | return -EPERM; | ||
588 | |||
589 | if (info->fbops->fb_write) | ||
590 | return info->fbops->fb_write(file, buf, count, ppos); | ||
591 | |||
592 | total_size = info->screen_size; | ||
593 | if (total_size == 0) | ||
594 | total_size = info->fix.smem_len; | ||
595 | |||
596 | if (p > total_size) | ||
597 | return -ENOSPC; | ||
598 | if (count >= total_size) | ||
599 | count = total_size; | ||
600 | err = 0; | ||
601 | if (count + p > total_size) { | ||
602 | count = total_size - p; | ||
603 | err = -ENOSPC; | ||
604 | } | ||
605 | cnt = 0; | ||
606 | buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, | ||
607 | GFP_KERNEL); | ||
608 | if (!buffer) | ||
609 | return -ENOMEM; | ||
610 | |||
611 | dst = (u32 __iomem *) (info->screen_base + p); | ||
612 | |||
613 | if (info->fbops->fb_sync) | ||
614 | info->fbops->fb_sync(info); | ||
615 | |||
616 | while (count) { | ||
617 | c = (count > PAGE_SIZE) ? PAGE_SIZE : count; | ||
618 | src = buffer; | ||
619 | if (copy_from_user(src, buf, c)) { | ||
620 | err = -EFAULT; | ||
621 | break; | ||
622 | } | ||
623 | for (i = c >> 2; i--; ) | ||
624 | fb_writel(*src++, dst++); | ||
625 | if (c & 3) { | ||
626 | u8 *src8 = (u8 *) src; | ||
627 | u8 __iomem *dst8 = (u8 __iomem *) dst; | ||
628 | |||
629 | for (i = c & 3; i--; ) | ||
630 | fb_writeb(*src8++, dst8++); | ||
631 | |||
632 | dst = (u32 __iomem *) dst8; | ||
633 | } | ||
634 | *ppos += c; | ||
635 | buf += c; | ||
636 | cnt += c; | ||
637 | count -= c; | ||
638 | } | ||
639 | kfree(buffer); | ||
640 | |||
641 | return (err) ? err : cnt; | ||
642 | } | ||
643 | |||
644 | #ifdef CONFIG_KMOD | ||
645 | static void try_to_load(int fb) | ||
646 | { | ||
647 | request_module("fb%d", fb); | ||
648 | } | ||
649 | #endif /* CONFIG_KMOD */ | ||
650 | |||
651 | int | ||
652 | fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) | ||
653 | { | ||
654 | int xoffset = var->xoffset; | ||
655 | int yoffset = var->yoffset; | ||
656 | int err; | ||
657 | |||
658 | if (xoffset < 0 || yoffset < 0 || !info->fbops->fb_pan_display || | ||
659 | xoffset + info->var.xres > info->var.xres_virtual || | ||
660 | yoffset + info->var.yres > info->var.yres_virtual) | ||
661 | return -EINVAL; | ||
662 | if ((err = info->fbops->fb_pan_display(var, info))) | ||
663 | return err; | ||
664 | info->var.xoffset = var->xoffset; | ||
665 | info->var.yoffset = var->yoffset; | ||
666 | if (var->vmode & FB_VMODE_YWRAP) | ||
667 | info->var.vmode |= FB_VMODE_YWRAP; | ||
668 | else | ||
669 | info->var.vmode &= ~FB_VMODE_YWRAP; | ||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | int | ||
674 | fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) | ||
675 | { | ||
676 | int err; | ||
677 | |||
678 | if (var->activate & FB_ACTIVATE_INV_MODE) { | ||
679 | struct fb_videomode mode1, mode2; | ||
680 | int ret = 0; | ||
681 | |||
682 | fb_var_to_videomode(&mode1, var); | ||
683 | fb_var_to_videomode(&mode2, &info->var); | ||
684 | /* make sure we don't delete the videomode of current var */ | ||
685 | ret = fb_mode_is_equal(&mode1, &mode2); | ||
686 | |||
687 | if (!ret) { | ||
688 | struct fb_event event; | ||
689 | |||
690 | event.info = info; | ||
691 | event.data = &mode1; | ||
692 | ret = notifier_call_chain(&fb_notifier_list, | ||
693 | FB_EVENT_MODE_DELETE, &event); | ||
694 | } | ||
695 | |||
696 | if (!ret) | ||
697 | fb_delete_videomode(&mode1, &info->modelist); | ||
698 | |||
699 | return ret; | ||
700 | } | ||
701 | |||
702 | if ((var->activate & FB_ACTIVATE_FORCE) || | ||
703 | memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) { | ||
704 | if (!info->fbops->fb_check_var) { | ||
705 | *var = info->var; | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | if ((err = info->fbops->fb_check_var(var, info))) | ||
710 | return err; | ||
711 | |||
712 | if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { | ||
713 | struct fb_videomode mode; | ||
714 | int err = 0; | ||
715 | |||
716 | info->var = *var; | ||
717 | if (info->fbops->fb_set_par) | ||
718 | info->fbops->fb_set_par(info); | ||
719 | |||
720 | fb_pan_display(info, &info->var); | ||
721 | |||
722 | fb_set_cmap(&info->cmap, info); | ||
723 | |||
724 | fb_var_to_videomode(&mode, &info->var); | ||
725 | |||
726 | if (info->modelist.prev && info->modelist.next && | ||
727 | !list_empty(&info->modelist)) | ||
728 | err = fb_add_videomode(&mode, &info->modelist); | ||
729 | |||
730 | if (!err && info->flags & FBINFO_MISC_USEREVENT) { | ||
731 | struct fb_event event; | ||
732 | |||
733 | info->flags &= ~FBINFO_MISC_USEREVENT; | ||
734 | event.info = info; | ||
735 | notifier_call_chain(&fb_notifier_list, | ||
736 | FB_EVENT_MODE_CHANGE, | ||
737 | &event); | ||
738 | } | ||
739 | } | ||
740 | } | ||
741 | return 0; | ||
742 | } | ||
743 | |||
744 | int | ||
745 | fb_blank(struct fb_info *info, int blank) | ||
746 | { | ||
747 | int ret = -EINVAL; | ||
748 | |||
749 | if (blank > FB_BLANK_POWERDOWN) | ||
750 | blank = FB_BLANK_POWERDOWN; | ||
751 | |||
752 | if (info->fbops->fb_blank) | ||
753 | ret = info->fbops->fb_blank(blank, info); | ||
754 | |||
755 | if (!ret) { | ||
756 | struct fb_event event; | ||
757 | |||
758 | event.info = info; | ||
759 | event.data = ␣ | ||
760 | notifier_call_chain(&fb_notifier_list, FB_EVENT_BLANK, &event); | ||
761 | } | ||
762 | |||
763 | return ret; | ||
764 | } | ||
765 | |||
766 | static int | ||
767 | fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | ||
768 | unsigned long arg) | ||
769 | { | ||
770 | int fbidx = iminor(inode); | ||
771 | struct fb_info *info = registered_fb[fbidx]; | ||
772 | struct fb_ops *fb = info->fbops; | ||
773 | struct fb_var_screeninfo var; | ||
774 | struct fb_fix_screeninfo fix; | ||
775 | struct fb_con2fbmap con2fb; | ||
776 | struct fb_cmap_user cmap; | ||
777 | struct fb_event event; | ||
778 | void __user *argp = (void __user *)arg; | ||
779 | int i; | ||
780 | |||
781 | if (!fb) | ||
782 | return -ENODEV; | ||
783 | switch (cmd) { | ||
784 | case FBIOGET_VSCREENINFO: | ||
785 | return copy_to_user(argp, &info->var, | ||
786 | sizeof(var)) ? -EFAULT : 0; | ||
787 | case FBIOPUT_VSCREENINFO: | ||
788 | if (copy_from_user(&var, argp, sizeof(var))) | ||
789 | return -EFAULT; | ||
790 | acquire_console_sem(); | ||
791 | info->flags |= FBINFO_MISC_USEREVENT; | ||
792 | i = fb_set_var(info, &var); | ||
793 | info->flags &= ~FBINFO_MISC_USEREVENT; | ||
794 | release_console_sem(); | ||
795 | if (i) return i; | ||
796 | if (copy_to_user(argp, &var, sizeof(var))) | ||
797 | return -EFAULT; | ||
798 | return 0; | ||
799 | case FBIOGET_FSCREENINFO: | ||
800 | return copy_to_user(argp, &info->fix, | ||
801 | sizeof(fix)) ? -EFAULT : 0; | ||
802 | case FBIOPUTCMAP: | ||
803 | if (copy_from_user(&cmap, argp, sizeof(cmap))) | ||
804 | return -EFAULT; | ||
805 | return (fb_set_user_cmap(&cmap, info)); | ||
806 | case FBIOGETCMAP: | ||
807 | if (copy_from_user(&cmap, argp, sizeof(cmap))) | ||
808 | return -EFAULT; | ||
809 | return fb_cmap_to_user(&info->cmap, &cmap); | ||
810 | case FBIOPAN_DISPLAY: | ||
811 | if (copy_from_user(&var, argp, sizeof(var))) | ||
812 | return -EFAULT; | ||
813 | acquire_console_sem(); | ||
814 | i = fb_pan_display(info, &var); | ||
815 | release_console_sem(); | ||
816 | if (i) | ||
817 | return i; | ||
818 | if (copy_to_user(argp, &var, sizeof(var))) | ||
819 | return -EFAULT; | ||
820 | return 0; | ||
821 | case FBIO_CURSOR: | ||
822 | return -EINVAL; | ||
823 | case FBIOGET_CON2FBMAP: | ||
824 | if (copy_from_user(&con2fb, argp, sizeof(con2fb))) | ||
825 | return -EFAULT; | ||
826 | if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) | ||
827 | return -EINVAL; | ||
828 | con2fb.framebuffer = -1; | ||
829 | event.info = info; | ||
830 | event.data = &con2fb; | ||
831 | notifier_call_chain(&fb_notifier_list, | ||
832 | FB_EVENT_GET_CONSOLE_MAP, &event); | ||
833 | return copy_to_user(argp, &con2fb, | ||
834 | sizeof(con2fb)) ? -EFAULT : 0; | ||
835 | case FBIOPUT_CON2FBMAP: | ||
836 | if (copy_from_user(&con2fb, argp, sizeof(con2fb))) | ||
837 | return - EFAULT; | ||
838 | if (con2fb.console < 0 || con2fb.console > MAX_NR_CONSOLES) | ||
839 | return -EINVAL; | ||
840 | if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) | ||
841 | return -EINVAL; | ||
842 | #ifdef CONFIG_KMOD | ||
843 | if (!registered_fb[con2fb.framebuffer]) | ||
844 | try_to_load(con2fb.framebuffer); | ||
845 | #endif /* CONFIG_KMOD */ | ||
846 | if (!registered_fb[con2fb.framebuffer]) | ||
847 | return -EINVAL; | ||
848 | event.info = info; | ||
849 | event.data = &con2fb; | ||
850 | return notifier_call_chain(&fb_notifier_list, | ||
851 | FB_EVENT_SET_CONSOLE_MAP, | ||
852 | &event); | ||
853 | case FBIOBLANK: | ||
854 | acquire_console_sem(); | ||
855 | info->flags |= FBINFO_MISC_USEREVENT; | ||
856 | i = fb_blank(info, arg); | ||
857 | info->flags &= ~FBINFO_MISC_USEREVENT; | ||
858 | release_console_sem(); | ||
859 | return i; | ||
860 | default: | ||
861 | if (fb->fb_ioctl == NULL) | ||
862 | return -EINVAL; | ||
863 | return fb->fb_ioctl(inode, file, cmd, arg, info); | ||
864 | } | ||
865 | } | ||
866 | |||
867 | #ifdef CONFIG_COMPAT | ||
868 | static long | ||
869 | fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
870 | { | ||
871 | int fbidx = iminor(file->f_dentry->d_inode); | ||
872 | struct fb_info *info = registered_fb[fbidx]; | ||
873 | struct fb_ops *fb = info->fbops; | ||
874 | long ret; | ||
875 | |||
876 | if (fb->fb_compat_ioctl == NULL) | ||
877 | return -ENOIOCTLCMD; | ||
878 | lock_kernel(); | ||
879 | ret = fb->fb_compat_ioctl(file, cmd, arg, info); | ||
880 | unlock_kernel(); | ||
881 | return ret; | ||
882 | } | ||
883 | #endif | ||
884 | |||
885 | static int | ||
886 | fb_mmap(struct file *file, struct vm_area_struct * vma) | ||
887 | { | ||
888 | int fbidx = iminor(file->f_dentry->d_inode); | ||
889 | struct fb_info *info = registered_fb[fbidx]; | ||
890 | struct fb_ops *fb = info->fbops; | ||
891 | unsigned long off; | ||
892 | #if !defined(__sparc__) || defined(__sparc_v9__) | ||
893 | unsigned long start; | ||
894 | u32 len; | ||
895 | #endif | ||
896 | |||
897 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) | ||
898 | return -EINVAL; | ||
899 | off = vma->vm_pgoff << PAGE_SHIFT; | ||
900 | if (!fb) | ||
901 | return -ENODEV; | ||
902 | if (fb->fb_mmap) { | ||
903 | int res; | ||
904 | lock_kernel(); | ||
905 | res = fb->fb_mmap(info, file, vma); | ||
906 | unlock_kernel(); | ||
907 | return res; | ||
908 | } | ||
909 | |||
910 | #if defined(__sparc__) && !defined(__sparc_v9__) | ||
911 | /* Should never get here, all fb drivers should have their own | ||
912 | mmap routines */ | ||
913 | return -EINVAL; | ||
914 | #else | ||
915 | /* !sparc32... */ | ||
916 | lock_kernel(); | ||
917 | |||
918 | /* frame buffer memory */ | ||
919 | start = info->fix.smem_start; | ||
920 | len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); | ||
921 | if (off >= len) { | ||
922 | /* memory mapped io */ | ||
923 | off -= len; | ||
924 | if (info->var.accel_flags) { | ||
925 | unlock_kernel(); | ||
926 | return -EINVAL; | ||
927 | } | ||
928 | start = info->fix.mmio_start; | ||
929 | len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); | ||
930 | } | ||
931 | unlock_kernel(); | ||
932 | start &= PAGE_MASK; | ||
933 | if ((vma->vm_end - vma->vm_start + off) > len) | ||
934 | return -EINVAL; | ||
935 | off += start; | ||
936 | vma->vm_pgoff = off >> PAGE_SHIFT; | ||
937 | /* This is an IO map - tell maydump to skip this VMA */ | ||
938 | vma->vm_flags |= VM_IO | VM_RESERVED; | ||
939 | #if defined(__sparc_v9__) | ||
940 | if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, | ||
941 | vma->vm_end - vma->vm_start, vma->vm_page_prot)) | ||
942 | return -EAGAIN; | ||
943 | #else | ||
944 | #if defined(__mc68000__) | ||
945 | #if defined(CONFIG_SUN3) | ||
946 | pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE; | ||
947 | #elif defined(CONFIG_MMU) | ||
948 | if (CPU_IS_020_OR_030) | ||
949 | pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE030; | ||
950 | if (CPU_IS_040_OR_060) { | ||
951 | pgprot_val(vma->vm_page_prot) &= _CACHEMASK040; | ||
952 | /* Use no-cache mode, serialized */ | ||
953 | pgprot_val(vma->vm_page_prot) |= _PAGE_NOCACHE_S; | ||
954 | } | ||
955 | #endif | ||
956 | #elif defined(__powerpc__) | ||
957 | vma->vm_page_prot = phys_mem_access_prot(file, off, | ||
958 | vma->vm_end - vma->vm_start, | ||
959 | vma->vm_page_prot); | ||
960 | #elif defined(__alpha__) | ||
961 | /* Caching is off in the I/O space quadrant by design. */ | ||
962 | #elif defined(__i386__) || defined(__x86_64__) | ||
963 | if (boot_cpu_data.x86 > 3) | ||
964 | pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; | ||
965 | #elif defined(__mips__) | ||
966 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
967 | #elif defined(__hppa__) | ||
968 | pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; | ||
969 | #elif defined(__arm__) || defined(__sh__) || defined(__m32r__) | ||
970 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); | ||
971 | #elif defined(__ia64__) | ||
972 | if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start)) | ||
973 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); | ||
974 | else | ||
975 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
976 | #else | ||
977 | #warning What do we have to do here?? | ||
978 | #endif | ||
979 | if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, | ||
980 | vma->vm_end - vma->vm_start, vma->vm_page_prot)) | ||
981 | return -EAGAIN; | ||
982 | #endif /* !__sparc_v9__ */ | ||
983 | return 0; | ||
984 | #endif /* !sparc32 */ | ||
985 | } | ||
986 | |||
987 | static int | ||
988 | fb_open(struct inode *inode, struct file *file) | ||
989 | { | ||
990 | int fbidx = iminor(inode); | ||
991 | struct fb_info *info; | ||
992 | int res = 0; | ||
993 | |||
994 | if (fbidx >= FB_MAX) | ||
995 | return -ENODEV; | ||
996 | #ifdef CONFIG_KMOD | ||
997 | if (!(info = registered_fb[fbidx])) | ||
998 | try_to_load(fbidx); | ||
999 | #endif /* CONFIG_KMOD */ | ||
1000 | if (!(info = registered_fb[fbidx])) | ||
1001 | return -ENODEV; | ||
1002 | if (!try_module_get(info->fbops->owner)) | ||
1003 | return -ENODEV; | ||
1004 | if (info->fbops->fb_open) { | ||
1005 | res = info->fbops->fb_open(info,1); | ||
1006 | if (res) | ||
1007 | module_put(info->fbops->owner); | ||
1008 | } | ||
1009 | return res; | ||
1010 | } | ||
1011 | |||
1012 | static int | ||
1013 | fb_release(struct inode *inode, struct file *file) | ||
1014 | { | ||
1015 | int fbidx = iminor(inode); | ||
1016 | struct fb_info *info; | ||
1017 | |||
1018 | lock_kernel(); | ||
1019 | info = registered_fb[fbidx]; | ||
1020 | if (info->fbops->fb_release) | ||
1021 | info->fbops->fb_release(info,1); | ||
1022 | module_put(info->fbops->owner); | ||
1023 | unlock_kernel(); | ||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | static struct file_operations fb_fops = { | ||
1028 | .owner = THIS_MODULE, | ||
1029 | .read = fb_read, | ||
1030 | .write = fb_write, | ||
1031 | .ioctl = fb_ioctl, | ||
1032 | #ifdef CONFIG_COMPAT | ||
1033 | .compat_ioctl = fb_compat_ioctl, | ||
1034 | #endif | ||
1035 | .mmap = fb_mmap, | ||
1036 | .open = fb_open, | ||
1037 | .release = fb_release, | ||
1038 | #ifdef HAVE_ARCH_FB_UNMAPPED_AREA | ||
1039 | .get_unmapped_area = get_fb_unmapped_area, | ||
1040 | #endif | ||
1041 | }; | ||
1042 | |||
1043 | static struct class_simple *fb_class; | ||
1044 | |||
1045 | /** | ||
1046 | * register_framebuffer - registers a frame buffer device | ||
1047 | * @fb_info: frame buffer info structure | ||
1048 | * | ||
1049 | * Registers a frame buffer device @fb_info. | ||
1050 | * | ||
1051 | * Returns negative errno on error, or zero for success. | ||
1052 | * | ||
1053 | */ | ||
1054 | |||
1055 | int | ||
1056 | register_framebuffer(struct fb_info *fb_info) | ||
1057 | { | ||
1058 | int i; | ||
1059 | struct fb_event event; | ||
1060 | |||
1061 | if (num_registered_fb == FB_MAX) | ||
1062 | return -ENXIO; | ||
1063 | num_registered_fb++; | ||
1064 | for (i = 0 ; i < FB_MAX; i++) | ||
1065 | if (!registered_fb[i]) | ||
1066 | break; | ||
1067 | fb_info->node = i; | ||
1068 | |||
1069 | fb_info->class_device = class_simple_device_add(fb_class, MKDEV(FB_MAJOR, i), | ||
1070 | fb_info->device, "fb%d", i); | ||
1071 | if (IS_ERR(fb_info->class_device)) { | ||
1072 | /* Not fatal */ | ||
1073 | printk(KERN_WARNING "Unable to create class_device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->class_device)); | ||
1074 | fb_info->class_device = NULL; | ||
1075 | } else | ||
1076 | fb_init_class_device(fb_info); | ||
1077 | |||
1078 | if (fb_info->pixmap.addr == NULL) { | ||
1079 | fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); | ||
1080 | if (fb_info->pixmap.addr) { | ||
1081 | fb_info->pixmap.size = FBPIXMAPSIZE; | ||
1082 | fb_info->pixmap.buf_align = 1; | ||
1083 | fb_info->pixmap.scan_align = 1; | ||
1084 | fb_info->pixmap.access_align = 4; | ||
1085 | fb_info->pixmap.flags = FB_PIXMAP_DEFAULT; | ||
1086 | } | ||
1087 | } | ||
1088 | fb_info->pixmap.offset = 0; | ||
1089 | |||
1090 | if (!fb_info->modelist.prev || | ||
1091 | !fb_info->modelist.next || | ||
1092 | list_empty(&fb_info->modelist)) { | ||
1093 | struct fb_videomode mode; | ||
1094 | |||
1095 | INIT_LIST_HEAD(&fb_info->modelist); | ||
1096 | fb_var_to_videomode(&mode, &fb_info->var); | ||
1097 | fb_add_videomode(&mode, &fb_info->modelist); | ||
1098 | } | ||
1099 | |||
1100 | registered_fb[i] = fb_info; | ||
1101 | |||
1102 | devfs_mk_cdev(MKDEV(FB_MAJOR, i), | ||
1103 | S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i); | ||
1104 | event.info = fb_info; | ||
1105 | notifier_call_chain(&fb_notifier_list, | ||
1106 | FB_EVENT_FB_REGISTERED, &event); | ||
1107 | return 0; | ||
1108 | } | ||
1109 | |||
1110 | |||
1111 | /** | ||
1112 | * unregister_framebuffer - releases a frame buffer device | ||
1113 | * @fb_info: frame buffer info structure | ||
1114 | * | ||
1115 | * Unregisters a frame buffer device @fb_info. | ||
1116 | * | ||
1117 | * Returns negative errno on error, or zero for success. | ||
1118 | * | ||
1119 | */ | ||
1120 | |||
1121 | int | ||
1122 | unregister_framebuffer(struct fb_info *fb_info) | ||
1123 | { | ||
1124 | int i; | ||
1125 | |||
1126 | i = fb_info->node; | ||
1127 | if (!registered_fb[i]) | ||
1128 | return -EINVAL; | ||
1129 | devfs_remove("fb/%d", i); | ||
1130 | |||
1131 | if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) | ||
1132 | kfree(fb_info->pixmap.addr); | ||
1133 | fb_destroy_modelist(&fb_info->modelist); | ||
1134 | registered_fb[i]=NULL; | ||
1135 | num_registered_fb--; | ||
1136 | fb_cleanup_class_device(fb_info); | ||
1137 | class_simple_device_remove(MKDEV(FB_MAJOR, i)); | ||
1138 | return 0; | ||
1139 | } | ||
1140 | |||
1141 | /** | ||
1142 | * fb_register_client - register a client notifier | ||
1143 | * @nb: notifier block to callback on events | ||
1144 | */ | ||
1145 | int fb_register_client(struct notifier_block *nb) | ||
1146 | { | ||
1147 | return notifier_chain_register(&fb_notifier_list, nb); | ||
1148 | } | ||
1149 | |||
1150 | /** | ||
1151 | * fb_unregister_client - unregister a client notifier | ||
1152 | * @nb: notifier block to callback on events | ||
1153 | */ | ||
1154 | int fb_unregister_client(struct notifier_block *nb) | ||
1155 | { | ||
1156 | return notifier_chain_unregister(&fb_notifier_list, nb); | ||
1157 | } | ||
1158 | |||
1159 | /** | ||
1160 | * fb_set_suspend - low level driver signals suspend | ||
1161 | * @info: framebuffer affected | ||
1162 | * @state: 0 = resuming, !=0 = suspending | ||
1163 | * | ||
1164 | * This is meant to be used by low level drivers to | ||
1165 | * signal suspend/resume to the core & clients. | ||
1166 | * It must be called with the console semaphore held | ||
1167 | */ | ||
1168 | void fb_set_suspend(struct fb_info *info, int state) | ||
1169 | { | ||
1170 | struct fb_event event; | ||
1171 | |||
1172 | event.info = info; | ||
1173 | if (state) { | ||
1174 | notifier_call_chain(&fb_notifier_list, FB_EVENT_SUSPEND, &event); | ||
1175 | info->state = FBINFO_STATE_SUSPENDED; | ||
1176 | } else { | ||
1177 | info->state = FBINFO_STATE_RUNNING; | ||
1178 | notifier_call_chain(&fb_notifier_list, FB_EVENT_RESUME, &event); | ||
1179 | } | ||
1180 | } | ||
1181 | |||
1182 | /** | ||
1183 | * fbmem_init - init frame buffer subsystem | ||
1184 | * | ||
1185 | * Initialize the frame buffer subsystem. | ||
1186 | * | ||
1187 | * NOTE: This function is _only_ to be called by drivers/char/mem.c. | ||
1188 | * | ||
1189 | */ | ||
1190 | |||
1191 | static int __init | ||
1192 | fbmem_init(void) | ||
1193 | { | ||
1194 | create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL); | ||
1195 | |||
1196 | devfs_mk_dir("fb"); | ||
1197 | if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) | ||
1198 | printk("unable to get major %d for fb devs\n", FB_MAJOR); | ||
1199 | |||
1200 | fb_class = class_simple_create(THIS_MODULE, "graphics"); | ||
1201 | if (IS_ERR(fb_class)) { | ||
1202 | printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class)); | ||
1203 | fb_class = NULL; | ||
1204 | } | ||
1205 | return 0; | ||
1206 | } | ||
1207 | |||
1208 | #ifdef MODULE | ||
1209 | module_init(fbmem_init); | ||
1210 | static void __exit | ||
1211 | fbmem_exit(void) | ||
1212 | { | ||
1213 | class_simple_destroy(fb_class); | ||
1214 | } | ||
1215 | |||
1216 | module_exit(fbmem_exit); | ||
1217 | MODULE_LICENSE("GPL"); | ||
1218 | MODULE_DESCRIPTION("Framebuffer base"); | ||
1219 | #else | ||
1220 | subsys_initcall(fbmem_init); | ||
1221 | #endif | ||
1222 | |||
1223 | int fb_new_modelist(struct fb_info *info) | ||
1224 | { | ||
1225 | struct fb_event event; | ||
1226 | struct fb_var_screeninfo var = info->var; | ||
1227 | struct list_head *pos, *n; | ||
1228 | struct fb_modelist *modelist; | ||
1229 | struct fb_videomode *m, mode; | ||
1230 | int err = 1; | ||
1231 | |||
1232 | list_for_each_safe(pos, n, &info->modelist) { | ||
1233 | modelist = list_entry(pos, struct fb_modelist, list); | ||
1234 | m = &modelist->mode; | ||
1235 | fb_videomode_to_var(&var, m); | ||
1236 | var.activate = FB_ACTIVATE_TEST; | ||
1237 | err = fb_set_var(info, &var); | ||
1238 | fb_var_to_videomode(&mode, &var); | ||
1239 | if (err || !fb_mode_is_equal(m, &mode)) { | ||
1240 | list_del(pos); | ||
1241 | kfree(pos); | ||
1242 | } | ||
1243 | } | ||
1244 | |||
1245 | err = 1; | ||
1246 | |||
1247 | if (!list_empty(&info->modelist)) { | ||
1248 | event.info = info; | ||
1249 | err = notifier_call_chain(&fb_notifier_list, | ||
1250 | FB_EVENT_NEW_MODELIST, | ||
1251 | &event); | ||
1252 | } | ||
1253 | |||
1254 | return err; | ||
1255 | } | ||
1256 | |||
1257 | static char *video_options[FB_MAX]; | ||
1258 | static int ofonly; | ||
1259 | |||
1260 | /** | ||
1261 | * fb_get_options - get kernel boot parameters | ||
1262 | * @name: framebuffer name as it would appear in | ||
1263 | * the boot parameter line | ||
1264 | * (video=<name>:<options>) | ||
1265 | * @option: the option will be stored here | ||
1266 | * | ||
1267 | * NOTE: Needed to maintain backwards compatibility | ||
1268 | */ | ||
1269 | int fb_get_options(char *name, char **option) | ||
1270 | { | ||
1271 | char *opt, *options = NULL; | ||
1272 | int opt_len, retval = 0; | ||
1273 | int name_len = strlen(name), i; | ||
1274 | |||
1275 | if (name_len && ofonly && strncmp(name, "offb", 4)) | ||
1276 | retval = 1; | ||
1277 | |||
1278 | if (name_len && !retval) { | ||
1279 | for (i = 0; i < FB_MAX; i++) { | ||
1280 | if (video_options[i] == NULL) | ||
1281 | continue; | ||
1282 | opt_len = strlen(video_options[i]); | ||
1283 | if (!opt_len) | ||
1284 | continue; | ||
1285 | opt = video_options[i]; | ||
1286 | if (!strncmp(name, opt, name_len) && | ||
1287 | opt[name_len] == ':') | ||
1288 | options = opt + name_len + 1; | ||
1289 | } | ||
1290 | } | ||
1291 | if (options && !strncmp(options, "off", 3)) | ||
1292 | retval = 1; | ||
1293 | |||
1294 | if (option) | ||
1295 | *option = options; | ||
1296 | |||
1297 | return retval; | ||
1298 | } | ||
1299 | |||
1300 | |||
1301 | extern const char *global_mode_option; | ||
1302 | |||
1303 | /** | ||
1304 | * video_setup - process command line options | ||
1305 | * @options: string of options | ||
1306 | * | ||
1307 | * Process command line options for frame buffer subsystem. | ||
1308 | * | ||
1309 | * NOTE: This function is a __setup and __init function. | ||
1310 | * It only stores the options. Drivers have to call | ||
1311 | * fb_get_options() as necessary. | ||
1312 | * | ||
1313 | * Returns zero. | ||
1314 | * | ||
1315 | */ | ||
1316 | int __init video_setup(char *options) | ||
1317 | { | ||
1318 | int i, global = 0; | ||
1319 | |||
1320 | if (!options || !*options) | ||
1321 | global = 1; | ||
1322 | |||
1323 | if (!global && !strncmp(options, "ofonly", 6)) { | ||
1324 | ofonly = 1; | ||
1325 | global = 1; | ||
1326 | } | ||
1327 | |||
1328 | if (!global && !strstr(options, "fb:")) { | ||
1329 | global_mode_option = options; | ||
1330 | global = 1; | ||
1331 | } | ||
1332 | |||
1333 | if (!global) { | ||
1334 | for (i = 0; i < FB_MAX; i++) { | ||
1335 | if (video_options[i] == NULL) { | ||
1336 | video_options[i] = options; | ||
1337 | break; | ||
1338 | } | ||
1339 | |||
1340 | } | ||
1341 | } | ||
1342 | |||
1343 | return 0; | ||
1344 | } | ||
1345 | __setup("video=", video_setup); | ||
1346 | |||
1347 | /* | ||
1348 | * Visible symbols for modules | ||
1349 | */ | ||
1350 | |||
1351 | EXPORT_SYMBOL(register_framebuffer); | ||
1352 | EXPORT_SYMBOL(unregister_framebuffer); | ||
1353 | EXPORT_SYMBOL(num_registered_fb); | ||
1354 | EXPORT_SYMBOL(registered_fb); | ||
1355 | EXPORT_SYMBOL(fb_prepare_logo); | ||
1356 | EXPORT_SYMBOL(fb_show_logo); | ||
1357 | EXPORT_SYMBOL(fb_set_var); | ||
1358 | EXPORT_SYMBOL(fb_blank); | ||
1359 | EXPORT_SYMBOL(fb_pan_display); | ||
1360 | EXPORT_SYMBOL(fb_get_buffer_offset); | ||
1361 | EXPORT_SYMBOL(fb_iomove_buf_unaligned); | ||
1362 | EXPORT_SYMBOL(fb_iomove_buf_aligned); | ||
1363 | EXPORT_SYMBOL(fb_sysmove_buf_unaligned); | ||
1364 | EXPORT_SYMBOL(fb_sysmove_buf_aligned); | ||
1365 | EXPORT_SYMBOL(fb_set_suspend); | ||
1366 | EXPORT_SYMBOL(fb_register_client); | ||
1367 | EXPORT_SYMBOL(fb_unregister_client); | ||
1368 | EXPORT_SYMBOL(fb_get_options); | ||
1369 | EXPORT_SYMBOL(fb_new_modelist); | ||
1370 | |||
1371 | MODULE_LICENSE("GPL"); | ||