diff options
Diffstat (limited to 'drivers/video/fbmem.c')
-rw-r--r-- | drivers/video/fbmem.c | 299 |
1 files changed, 281 insertions, 18 deletions
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index e2667ddab3f1..9f180096c896 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/config.h> | 14 | #include <linux/config.h> |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | 16 | ||
17 | #include <linux/compat.h> | ||
17 | #include <linux/types.h> | 18 | #include <linux/types.h> |
18 | #include <linux/errno.h> | 19 | #include <linux/errno.h> |
19 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
@@ -323,9 +324,103 @@ static struct logo_data { | |||
323 | const struct linux_logo *logo; | 324 | const struct linux_logo *logo; |
324 | } fb_logo; | 325 | } fb_logo; |
325 | 326 | ||
326 | int fb_prepare_logo(struct fb_info *info) | 327 | static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height) |
328 | { | ||
329 | u32 size = width * height, i; | ||
330 | |||
331 | out += size - 1; | ||
332 | |||
333 | for (i = size; i--; ) | ||
334 | *out-- = *in++; | ||
335 | } | ||
336 | |||
337 | static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height) | ||
338 | { | ||
339 | int i, j, w = width - 1; | ||
340 | |||
341 | for (i = 0; i < height; i++) | ||
342 | for (j = 0; j < width; j++) | ||
343 | out[height * j + w - i] = *in++; | ||
344 | } | ||
345 | |||
346 | static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height) | ||
347 | { | ||
348 | int i, j, w = width - 1; | ||
349 | |||
350 | for (i = 0; i < height; i++) | ||
351 | for (j = 0; j < width; j++) | ||
352 | out[height * (w - j) + i] = *in++; | ||
353 | } | ||
354 | |||
355 | static void fb_rotate_logo(struct fb_info *info, u8 *dst, | ||
356 | struct fb_image *image, int rotate) | ||
357 | { | ||
358 | u32 tmp; | ||
359 | |||
360 | if (rotate == FB_ROTATE_UD) { | ||
361 | image->dx = info->var.xres - image->width; | ||
362 | image->dy = info->var.yres - image->height; | ||
363 | fb_rotate_logo_ud(image->data, dst, image->width, | ||
364 | image->height); | ||
365 | } else if (rotate == FB_ROTATE_CW) { | ||
366 | tmp = image->width; | ||
367 | image->width = image->height; | ||
368 | image->height = tmp; | ||
369 | image->dx = info->var.xres - image->height; | ||
370 | fb_rotate_logo_cw(image->data, dst, image->width, | ||
371 | image->height); | ||
372 | } else if (rotate == FB_ROTATE_CCW) { | ||
373 | tmp = image->width; | ||
374 | image->width = image->height; | ||
375 | image->height = tmp; | ||
376 | image->dy = info->var.yres - image->width; | ||
377 | fb_rotate_logo_ccw(image->data, dst, image->width, | ||
378 | image->height); | ||
379 | } | ||
380 | |||
381 | image->data = dst; | ||
382 | } | ||
383 | |||
384 | static void fb_do_show_logo(struct fb_info *info, struct fb_image *image, | ||
385 | int rotate) | ||
386 | { | ||
387 | int x; | ||
388 | |||
389 | if (rotate == FB_ROTATE_UR) { | ||
390 | for (x = 0; x < num_online_cpus() && | ||
391 | x * (fb_logo.logo->width + 8) <= | ||
392 | info->var.xres - fb_logo.logo->width; x++) { | ||
393 | info->fbops->fb_imageblit(info, image); | ||
394 | image->dx += fb_logo.logo->width + 8; | ||
395 | } | ||
396 | } else if (rotate == FB_ROTATE_UD) { | ||
397 | for (x = 0; x < num_online_cpus() && | ||
398 | x * (fb_logo.logo->width + 8) <= | ||
399 | info->var.xres - fb_logo.logo->width; x++) { | ||
400 | info->fbops->fb_imageblit(info, image); | ||
401 | image->dx -= fb_logo.logo->width + 8; | ||
402 | } | ||
403 | } else if (rotate == FB_ROTATE_CW) { | ||
404 | for (x = 0; x < num_online_cpus() && | ||
405 | x * (fb_logo.logo->width + 8) <= | ||
406 | info->var.yres - fb_logo.logo->width; x++) { | ||
407 | info->fbops->fb_imageblit(info, image); | ||
408 | image->dy += fb_logo.logo->width + 8; | ||
409 | } | ||
410 | } else if (rotate == FB_ROTATE_CCW) { | ||
411 | for (x = 0; x < num_online_cpus() && | ||
412 | x * (fb_logo.logo->width + 8) <= | ||
413 | info->var.yres - fb_logo.logo->width; x++) { | ||
414 | info->fbops->fb_imageblit(info, image); | ||
415 | image->dy -= fb_logo.logo->width + 8; | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | int fb_prepare_logo(struct fb_info *info, int rotate) | ||
327 | { | 421 | { |
328 | int depth = fb_get_color_depth(&info->var, &info->fix); | 422 | int depth = fb_get_color_depth(&info->var, &info->fix); |
423 | int yres; | ||
329 | 424 | ||
330 | memset(&fb_logo, 0, sizeof(struct logo_data)); | 425 | memset(&fb_logo, 0, sizeof(struct logo_data)); |
331 | 426 | ||
@@ -358,10 +453,16 @@ int fb_prepare_logo(struct fb_info *info) | |||
358 | /* Return if no suitable logo was found */ | 453 | /* Return if no suitable logo was found */ |
359 | fb_logo.logo = fb_find_logo(depth); | 454 | fb_logo.logo = fb_find_logo(depth); |
360 | 455 | ||
361 | if (!fb_logo.logo || fb_logo.logo->height > info->var.yres) { | 456 | if (rotate == FB_ROTATE_UR || rotate == FB_ROTATE_UD) |
457 | yres = info->var.yres; | ||
458 | else | ||
459 | yres = info->var.xres; | ||
460 | |||
461 | if (fb_logo.logo && fb_logo.logo->height > yres) { | ||
362 | fb_logo.logo = NULL; | 462 | fb_logo.logo = NULL; |
363 | return 0; | 463 | return 0; |
364 | } | 464 | } |
465 | |||
365 | /* What depth we asked for might be different from what we get */ | 466 | /* What depth we asked for might be different from what we get */ |
366 | if (fb_logo.logo->type == LINUX_LOGO_CLUT224) | 467 | if (fb_logo.logo->type == LINUX_LOGO_CLUT224) |
367 | fb_logo.depth = 8; | 468 | fb_logo.depth = 8; |
@@ -372,12 +473,11 @@ int fb_prepare_logo(struct fb_info *info) | |||
372 | return fb_logo.logo->height; | 473 | return fb_logo.logo->height; |
373 | } | 474 | } |
374 | 475 | ||
375 | int fb_show_logo(struct fb_info *info) | 476 | int fb_show_logo(struct fb_info *info, int rotate) |
376 | { | 477 | { |
377 | u32 *palette = NULL, *saved_pseudo_palette = NULL; | 478 | u32 *palette = NULL, *saved_pseudo_palette = NULL; |
378 | unsigned char *logo_new = NULL; | 479 | unsigned char *logo_new = NULL, *logo_rotate = NULL; |
379 | struct fb_image image; | 480 | struct fb_image image; |
380 | int x; | ||
381 | 481 | ||
382 | /* Return if the frame buffer is not mapped or suspended */ | 482 | /* Return if the frame buffer is not mapped or suspended */ |
383 | if (fb_logo.logo == NULL || info->state != FBINFO_STATE_RUNNING) | 483 | if (fb_logo.logo == NULL || info->state != FBINFO_STATE_RUNNING) |
@@ -417,25 +517,30 @@ int fb_show_logo(struct fb_info *info) | |||
417 | fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth); | 517 | fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth); |
418 | } | 518 | } |
419 | 519 | ||
520 | image.dx = 0; | ||
521 | image.dy = 0; | ||
420 | image.width = fb_logo.logo->width; | 522 | image.width = fb_logo.logo->width; |
421 | image.height = fb_logo.logo->height; | 523 | image.height = fb_logo.logo->height; |
422 | image.dy = 0; | ||
423 | 524 | ||
424 | for (x = 0; x < num_online_cpus() * (fb_logo.logo->width + 8) && | 525 | if (rotate) { |
425 | x <= info->var.xres-fb_logo.logo->width; x += (fb_logo.logo->width + 8)) { | 526 | logo_rotate = kmalloc(fb_logo.logo->width * |
426 | image.dx = x; | 527 | fb_logo.logo->height, GFP_KERNEL); |
427 | info->fbops->fb_imageblit(info, &image); | 528 | if (logo_rotate) |
529 | fb_rotate_logo(info, logo_rotate, &image, rotate); | ||
428 | } | 530 | } |
429 | 531 | ||
532 | fb_do_show_logo(info, &image, rotate); | ||
533 | |||
430 | kfree(palette); | 534 | kfree(palette); |
431 | if (saved_pseudo_palette != NULL) | 535 | if (saved_pseudo_palette != NULL) |
432 | info->pseudo_palette = saved_pseudo_palette; | 536 | info->pseudo_palette = saved_pseudo_palette; |
433 | kfree(logo_new); | 537 | kfree(logo_new); |
538 | kfree(logo_rotate); | ||
434 | return fb_logo.logo->height; | 539 | return fb_logo.logo->height; |
435 | } | 540 | } |
436 | #else | 541 | #else |
437 | int fb_prepare_logo(struct fb_info *info) { return 0; } | 542 | int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; } |
438 | int fb_show_logo(struct fb_info *info) { return 0; } | 543 | int fb_show_logo(struct fb_info *info, int rotate) { return 0; } |
439 | #endif /* CONFIG_LOGO */ | 544 | #endif /* CONFIG_LOGO */ |
440 | 545 | ||
441 | static int fbmem_read_proc(char *buf, char **start, off_t offset, | 546 | static int fbmem_read_proc(char *buf, char **start, off_t offset, |
@@ -829,18 +934,154 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | |||
829 | } | 934 | } |
830 | 935 | ||
831 | #ifdef CONFIG_COMPAT | 936 | #ifdef CONFIG_COMPAT |
937 | struct fb_fix_screeninfo32 { | ||
938 | char id[16]; | ||
939 | compat_caddr_t smem_start; | ||
940 | u32 smem_len; | ||
941 | u32 type; | ||
942 | u32 type_aux; | ||
943 | u32 visual; | ||
944 | u16 xpanstep; | ||
945 | u16 ypanstep; | ||
946 | u16 ywrapstep; | ||
947 | u32 line_length; | ||
948 | compat_caddr_t mmio_start; | ||
949 | u32 mmio_len; | ||
950 | u32 accel; | ||
951 | u16 reserved[3]; | ||
952 | }; | ||
953 | |||
954 | struct fb_cmap32 { | ||
955 | u32 start; | ||
956 | u32 len; | ||
957 | compat_caddr_t red; | ||
958 | compat_caddr_t green; | ||
959 | compat_caddr_t blue; | ||
960 | compat_caddr_t transp; | ||
961 | }; | ||
962 | |||
963 | static int fb_getput_cmap(struct inode *inode, struct file *file, | ||
964 | unsigned int cmd, unsigned long arg) | ||
965 | { | ||
966 | struct fb_cmap_user __user *cmap; | ||
967 | struct fb_cmap32 __user *cmap32; | ||
968 | __u32 data; | ||
969 | int err; | ||
970 | |||
971 | cmap = compat_alloc_user_space(sizeof(*cmap)); | ||
972 | cmap32 = compat_ptr(arg); | ||
973 | |||
974 | if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32))) | ||
975 | return -EFAULT; | ||
976 | |||
977 | if (get_user(data, &cmap32->red) || | ||
978 | put_user(compat_ptr(data), &cmap->red) || | ||
979 | get_user(data, &cmap32->green) || | ||
980 | put_user(compat_ptr(data), &cmap->green) || | ||
981 | get_user(data, &cmap32->blue) || | ||
982 | put_user(compat_ptr(data), &cmap->blue) || | ||
983 | get_user(data, &cmap32->transp) || | ||
984 | put_user(compat_ptr(data), &cmap->transp)) | ||
985 | return -EFAULT; | ||
986 | |||
987 | err = fb_ioctl(inode, file, cmd, (unsigned long) cmap); | ||
988 | |||
989 | if (!err) { | ||
990 | if (copy_in_user(&cmap32->start, | ||
991 | &cmap->start, | ||
992 | 2 * sizeof(__u32))) | ||
993 | err = -EFAULT; | ||
994 | } | ||
995 | return err; | ||
996 | } | ||
997 | |||
998 | static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, | ||
999 | struct fb_fix_screeninfo32 __user *fix32) | ||
1000 | { | ||
1001 | __u32 data; | ||
1002 | int err; | ||
1003 | |||
1004 | err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id)); | ||
1005 | |||
1006 | data = (__u32) (unsigned long) fix->smem_start; | ||
1007 | err |= put_user(data, &fix32->smem_start); | ||
1008 | |||
1009 | err |= put_user(fix->smem_len, &fix32->smem_len); | ||
1010 | err |= put_user(fix->type, &fix32->type); | ||
1011 | err |= put_user(fix->type_aux, &fix32->type_aux); | ||
1012 | err |= put_user(fix->visual, &fix32->visual); | ||
1013 | err |= put_user(fix->xpanstep, &fix32->xpanstep); | ||
1014 | err |= put_user(fix->ypanstep, &fix32->ypanstep); | ||
1015 | err |= put_user(fix->ywrapstep, &fix32->ywrapstep); | ||
1016 | err |= put_user(fix->line_length, &fix32->line_length); | ||
1017 | |||
1018 | data = (__u32) (unsigned long) fix->mmio_start; | ||
1019 | err |= put_user(data, &fix32->mmio_start); | ||
1020 | |||
1021 | err |= put_user(fix->mmio_len, &fix32->mmio_len); | ||
1022 | err |= put_user(fix->accel, &fix32->accel); | ||
1023 | err |= copy_to_user(fix32->reserved, fix->reserved, | ||
1024 | sizeof(fix->reserved)); | ||
1025 | |||
1026 | return err; | ||
1027 | } | ||
1028 | |||
1029 | static int fb_get_fscreeninfo(struct inode *inode, struct file *file, | ||
1030 | unsigned int cmd, unsigned long arg) | ||
1031 | { | ||
1032 | mm_segment_t old_fs; | ||
1033 | struct fb_fix_screeninfo fix; | ||
1034 | struct fb_fix_screeninfo32 __user *fix32; | ||
1035 | int err; | ||
1036 | |||
1037 | fix32 = compat_ptr(arg); | ||
1038 | |||
1039 | old_fs = get_fs(); | ||
1040 | set_fs(KERNEL_DS); | ||
1041 | err = fb_ioctl(inode, file, cmd, (unsigned long) &fix); | ||
1042 | set_fs(old_fs); | ||
1043 | |||
1044 | if (!err) | ||
1045 | err = do_fscreeninfo_to_user(&fix, fix32); | ||
1046 | |||
1047 | return err; | ||
1048 | } | ||
1049 | |||
832 | static long | 1050 | static long |
833 | fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 1051 | fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
834 | { | 1052 | { |
835 | int fbidx = iminor(file->f_dentry->d_inode); | 1053 | struct inode *inode = file->f_dentry->d_inode; |
1054 | int fbidx = iminor(inode); | ||
836 | struct fb_info *info = registered_fb[fbidx]; | 1055 | struct fb_info *info = registered_fb[fbidx]; |
837 | struct fb_ops *fb = info->fbops; | 1056 | struct fb_ops *fb = info->fbops; |
838 | long ret; | 1057 | long ret = -ENOIOCTLCMD; |
839 | 1058 | ||
840 | if (fb->fb_compat_ioctl == NULL) | ||
841 | return -ENOIOCTLCMD; | ||
842 | lock_kernel(); | 1059 | lock_kernel(); |
843 | ret = fb->fb_compat_ioctl(file, cmd, arg, info); | 1060 | switch(cmd) { |
1061 | case FBIOGET_VSCREENINFO: | ||
1062 | case FBIOPUT_VSCREENINFO: | ||
1063 | case FBIOPAN_DISPLAY: | ||
1064 | case FBIOGET_CON2FBMAP: | ||
1065 | case FBIOPUT_CON2FBMAP: | ||
1066 | arg = (unsigned long) compat_ptr(arg); | ||
1067 | case FBIOBLANK: | ||
1068 | ret = fb_ioctl(inode, file, cmd, arg); | ||
1069 | break; | ||
1070 | |||
1071 | case FBIOGET_FSCREENINFO: | ||
1072 | ret = fb_get_fscreeninfo(inode, file, cmd, arg); | ||
1073 | break; | ||
1074 | |||
1075 | case FBIOGETCMAP: | ||
1076 | case FBIOPUTCMAP: | ||
1077 | ret = fb_getput_cmap(inode, file, cmd, arg); | ||
1078 | break; | ||
1079 | |||
1080 | default: | ||
1081 | if (fb->fb_compat_ioctl) | ||
1082 | ret = fb->fb_compat_ioctl(file, cmd, arg, info); | ||
1083 | break; | ||
1084 | } | ||
844 | unlock_kernel(); | 1085 | unlock_kernel(); |
845 | return ret; | 1086 | return ret; |
846 | } | 1087 | } |
@@ -1215,6 +1456,28 @@ int fb_new_modelist(struct fb_info *info) | |||
1215 | return err; | 1456 | return err; |
1216 | } | 1457 | } |
1217 | 1458 | ||
1459 | /** | ||
1460 | * fb_con_duit - user<->fbcon passthrough | ||
1461 | * @info: struct fb_info | ||
1462 | * @event: notification event to be passed to fbcon | ||
1463 | * @data: private data | ||
1464 | * | ||
1465 | * DESCRIPTION | ||
1466 | * This function is an fbcon-user event passing channel | ||
1467 | * which bypasses fbdev. This is hopefully temporary | ||
1468 | * until a user interface for fbcon is created | ||
1469 | */ | ||
1470 | int fb_con_duit(struct fb_info *info, int event, void *data) | ||
1471 | { | ||
1472 | struct fb_event evnt; | ||
1473 | |||
1474 | evnt.info = info; | ||
1475 | evnt.data = data; | ||
1476 | |||
1477 | return notifier_call_chain(&fb_notifier_list, event, &evnt); | ||
1478 | } | ||
1479 | EXPORT_SYMBOL(fb_con_duit); | ||
1480 | |||
1218 | static char *video_options[FB_MAX]; | 1481 | static char *video_options[FB_MAX]; |
1219 | static int ofonly; | 1482 | static int ofonly; |
1220 | 1483 | ||