2 * linux/drivers/video/fbmem.c
4 * Copyright (C) 1994 Martin Schaller
6 * 2001 - Documented with DocBook
7 * - Brad Douglas <brad@neruo.com>
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
14 #include <linux/console.h>
15 #include <linux/export.h>
17 #include <linux/fbcon.h>
19 #include <video/nomodeset.h>
21 #include "fb_internal.h"
24 * Frame buffer device initialization and setup routines
27 #define FBPIXMAPSIZE (1024 * 8)
29 struct class *fb_class
;
31 DEFINE_MUTEX(registration_lock
);
32 struct fb_info
*registered_fb
[FB_MAX
] __read_mostly
;
33 int num_registered_fb __read_mostly
;
34 #define for_each_registered_fb(i) \
35 for (i = 0; i < FB_MAX; i++) \
36 if (!registered_fb[i]) {} else
38 struct fb_info
*get_fb_info(unsigned int idx
)
40 struct fb_info
*fb_info
;
43 return ERR_PTR(-ENODEV
);
45 mutex_lock(®istration_lock
);
46 fb_info
= registered_fb
[idx
];
48 refcount_inc(&fb_info
->count
);
49 mutex_unlock(®istration_lock
);
54 void put_fb_info(struct fb_info
*fb_info
)
56 if (!refcount_dec_and_test(&fb_info
->count
))
58 if (fb_info
->fbops
->fb_destroy
)
59 fb_info
->fbops
->fb_destroy(fb_info
);
66 int fb_get_color_depth(struct fb_var_screeninfo
*var
,
67 struct fb_fix_screeninfo
*fix
)
71 if (fix
->visual
== FB_VISUAL_MONO01
||
72 fix
->visual
== FB_VISUAL_MONO10
)
75 if (var
->green
.length
== var
->blue
.length
&&
76 var
->green
.length
== var
->red
.length
&&
77 var
->green
.offset
== var
->blue
.offset
&&
78 var
->green
.offset
== var
->red
.offset
)
79 depth
= var
->green
.length
;
81 depth
= var
->green
.length
+ var
->red
.length
+
87 EXPORT_SYMBOL(fb_get_color_depth
);
90 * Data padding functions.
92 void fb_pad_aligned_buffer(u8
*dst
, u32 d_pitch
, u8
*src
, u32 s_pitch
, u32 height
)
94 __fb_pad_aligned_buffer(dst
, d_pitch
, src
, s_pitch
, height
);
96 EXPORT_SYMBOL(fb_pad_aligned_buffer
);
98 void fb_pad_unaligned_buffer(u8
*dst
, u32 d_pitch
, u8
*src
, u32 idx
, u32 height
,
99 u32 shift_high
, u32 shift_low
, u32 mod
)
101 u8 mask
= (u8
) (0xfff << shift_high
), tmp
;
104 for (i
= height
; i
--; ) {
105 for (j
= 0; j
< idx
; j
++) {
108 tmp
|= *src
>> shift_low
;
110 tmp
= *src
<< shift_high
;
116 tmp
|= *src
>> shift_low
;
118 if (shift_high
< mod
) {
119 tmp
= *src
<< shift_high
;
126 EXPORT_SYMBOL(fb_pad_unaligned_buffer
);
129 * we need to lock this section since fb_cursor
130 * may use fb_imageblit()
132 char* fb_get_buffer_offset(struct fb_info
*info
, struct fb_pixmap
*buf
, u32 size
)
134 u32 align
= buf
->buf_align
- 1, offset
;
135 char *addr
= buf
->addr
;
137 /* If IO mapped, we need to sync before access, no sharing of
140 if (buf
->flags
& FB_PIXMAP_IO
) {
141 if (info
->fbops
->fb_sync
&& (buf
->flags
& FB_PIXMAP_SYNC
))
142 info
->fbops
->fb_sync(info
);
146 /* See if we fit in the remaining pixmap space */
147 offset
= buf
->offset
+ align
;
149 if (offset
+ size
> buf
->size
) {
150 /* We do not fit. In order to be able to re-use the buffer,
151 * we must ensure no asynchronous DMA'ing or whatever operation
152 * is in progress, we sync for that.
154 if (info
->fbops
->fb_sync
&& (buf
->flags
& FB_PIXMAP_SYNC
))
155 info
->fbops
->fb_sync(info
);
158 buf
->offset
= offset
+ size
;
163 EXPORT_SYMBOL(fb_get_buffer_offset
);
166 fb_pan_display(struct fb_info
*info
, struct fb_var_screeninfo
*var
)
168 struct fb_fix_screeninfo
*fix
= &info
->fix
;
169 unsigned int yres
= info
->var
.yres
;
172 if (var
->yoffset
> 0) {
173 if (var
->vmode
& FB_VMODE_YWRAP
) {
174 if (!fix
->ywrapstep
|| (var
->yoffset
% fix
->ywrapstep
))
178 } else if (!fix
->ypanstep
|| (var
->yoffset
% fix
->ypanstep
))
182 if (var
->xoffset
> 0 && (!fix
->xpanstep
||
183 (var
->xoffset
% fix
->xpanstep
)))
186 if (err
|| !info
->fbops
->fb_pan_display
||
187 var
->yoffset
> info
->var
.yres_virtual
- yres
||
188 var
->xoffset
> info
->var
.xres_virtual
- info
->var
.xres
)
191 if ((err
= info
->fbops
->fb_pan_display(var
, info
)))
193 info
->var
.xoffset
= var
->xoffset
;
194 info
->var
.yoffset
= var
->yoffset
;
195 if (var
->vmode
& FB_VMODE_YWRAP
)
196 info
->var
.vmode
|= FB_VMODE_YWRAP
;
198 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
201 EXPORT_SYMBOL(fb_pan_display
);
203 static int fb_check_caps(struct fb_info
*info
, struct fb_var_screeninfo
*var
,
206 struct fb_blit_caps caps
, fbcaps
;
209 memset(&caps
, 0, sizeof(caps
));
210 memset(&fbcaps
, 0, sizeof(fbcaps
));
211 caps
.flags
= (activate
& FB_ACTIVATE_ALL
) ? 1 : 0;
212 fbcon_get_requirement(info
, &caps
);
213 info
->fbops
->fb_get_caps(info
, &fbcaps
, var
);
215 if (!bitmap_subset(caps
.x
, fbcaps
.x
, FB_MAX_BLIT_WIDTH
) ||
216 !bitmap_subset(caps
.y
, fbcaps
.y
, FB_MAX_BLIT_HEIGHT
) ||
217 (fbcaps
.len
< caps
.len
))
224 fb_set_var(struct fb_info
*info
, struct fb_var_screeninfo
*var
)
228 struct fb_var_screeninfo old_var
;
229 struct fb_videomode mode
;
230 struct fb_event event
;
233 if (var
->activate
& FB_ACTIVATE_INV_MODE
) {
234 struct fb_videomode mode1
, mode2
;
236 fb_var_to_videomode(&mode1
, var
);
237 fb_var_to_videomode(&mode2
, &info
->var
);
238 /* make sure we don't delete the videomode of current var */
239 ret
= fb_mode_is_equal(&mode1
, &mode2
);
241 ret
= fbcon_mode_deleted(info
, &mode1
);
243 fb_delete_videomode(&mode1
, &info
->modelist
);
246 return ret
? -EINVAL
: 0;
249 if (!(var
->activate
& FB_ACTIVATE_FORCE
) &&
250 !memcmp(&info
->var
, var
, sizeof(struct fb_var_screeninfo
)))
253 activate
= var
->activate
;
255 /* When using FOURCC mode, make sure the red, green, blue and
256 * transp fields are set to 0.
258 if ((info
->fix
.capabilities
& FB_CAP_FOURCC
) &&
259 var
->grayscale
> 1) {
260 if (var
->red
.offset
|| var
->green
.offset
||
261 var
->blue
.offset
|| var
->transp
.offset
||
262 var
->red
.length
|| var
->green
.length
||
263 var
->blue
.length
|| var
->transp
.length
||
264 var
->red
.msb_right
|| var
->green
.msb_right
||
265 var
->blue
.msb_right
|| var
->transp
.msb_right
)
269 if (!info
->fbops
->fb_check_var
) {
274 /* bitfill_aligned() assumes that it's at least 8x8 */
275 if (var
->xres
< 8 || var
->yres
< 8)
278 /* Too huge resolution causes multiplication overflow. */
279 if (check_mul_overflow(var
->xres
, var
->yres
, &unused
) ||
280 check_mul_overflow(var
->xres_virtual
, var
->yres_virtual
, &unused
))
283 ret
= info
->fbops
->fb_check_var(var
, info
);
288 /* verify that virtual resolution >= physical resolution */
289 if (var
->xres_virtual
< var
->xres
||
290 var
->yres_virtual
< var
->yres
) {
291 pr_warn("WARNING: fbcon: Driver '%s' missed to adjust virtual screen size (%ux%u vs. %ux%u)\n",
293 var
->xres_virtual
, var
->yres_virtual
,
294 var
->xres
, var
->yres
);
298 if ((var
->activate
& FB_ACTIVATE_MASK
) != FB_ACTIVATE_NOW
)
301 if (info
->fbops
->fb_get_caps
) {
302 ret
= fb_check_caps(info
, var
, activate
);
311 if (info
->fbops
->fb_set_par
) {
312 ret
= info
->fbops
->fb_set_par(info
);
316 printk(KERN_WARNING
"detected "
318 "error code: %d\n", ret
);
323 fb_pan_display(info
, &info
->var
);
324 fb_set_cmap(&info
->cmap
, info
);
325 fb_var_to_videomode(&mode
, &info
->var
);
327 if (info
->modelist
.prev
&& info
->modelist
.next
&&
328 !list_empty(&info
->modelist
))
329 ret
= fb_add_videomode(&mode
, &info
->modelist
);
336 fb_notifier_call_chain(FB_EVENT_MODE_CHANGE
, &event
);
340 EXPORT_SYMBOL(fb_set_var
);
343 fb_blank(struct fb_info
*info
, int blank
)
345 struct fb_event event
;
348 if (blank
> FB_BLANK_POWERDOWN
)
349 blank
= FB_BLANK_POWERDOWN
;
354 if (info
->fbops
->fb_blank
)
355 ret
= info
->fbops
->fb_blank(blank
, info
);
358 fb_notifier_call_chain(FB_EVENT_BLANK
, &event
);
362 EXPORT_SYMBOL(fb_blank
);
364 static int fb_check_foreignness(struct fb_info
*fi
)
366 const bool foreign_endian
= fi
->flags
& FBINFO_FOREIGN_ENDIAN
;
368 fi
->flags
&= ~FBINFO_FOREIGN_ENDIAN
;
371 fi
->flags
|= foreign_endian
? 0 : FBINFO_BE_MATH
;
373 fi
->flags
|= foreign_endian
? FBINFO_BE_MATH
: 0;
374 #endif /* __BIG_ENDIAN */
376 if (fi
->flags
& FBINFO_BE_MATH
&& !fb_be_math(fi
)) {
377 pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
378 "support this framebuffer\n", fi
->fix
.id
);
380 } else if (!(fi
->flags
& FBINFO_BE_MATH
) && fb_be_math(fi
)) {
381 pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
382 "support this framebuffer\n", fi
->fix
.id
);
389 static int do_register_framebuffer(struct fb_info
*fb_info
)
392 struct fb_videomode mode
;
394 if (fb_check_foreignness(fb_info
))
397 if (num_registered_fb
== FB_MAX
)
401 for (i
= 0 ; i
< FB_MAX
; i
++)
402 if (!registered_fb
[i
])
405 refcount_set(&fb_info
->count
, 1);
406 mutex_init(&fb_info
->lock
);
407 mutex_init(&fb_info
->mm_lock
);
409 fb_device_create(fb_info
);
411 if (fb_info
->pixmap
.addr
== NULL
) {
412 fb_info
->pixmap
.addr
= kmalloc(FBPIXMAPSIZE
, GFP_KERNEL
);
413 if (fb_info
->pixmap
.addr
) {
414 fb_info
->pixmap
.size
= FBPIXMAPSIZE
;
415 fb_info
->pixmap
.buf_align
= 1;
416 fb_info
->pixmap
.scan_align
= 1;
417 fb_info
->pixmap
.access_align
= 32;
418 fb_info
->pixmap
.flags
= FB_PIXMAP_DEFAULT
;
421 fb_info
->pixmap
.offset
= 0;
423 if (bitmap_empty(fb_info
->pixmap
.blit_x
, FB_MAX_BLIT_WIDTH
))
424 bitmap_fill(fb_info
->pixmap
.blit_x
, FB_MAX_BLIT_WIDTH
);
426 if (bitmap_empty(fb_info
->pixmap
.blit_y
, FB_MAX_BLIT_HEIGHT
))
427 bitmap_fill(fb_info
->pixmap
.blit_y
, FB_MAX_BLIT_HEIGHT
);
429 if (!fb_info
->modelist
.prev
|| !fb_info
->modelist
.next
)
430 INIT_LIST_HEAD(&fb_info
->modelist
);
432 if (fb_info
->skip_vt_switch
)
433 pm_vt_switch_required(fb_info
->device
, false);
435 pm_vt_switch_required(fb_info
->device
, true);
437 fb_var_to_videomode(&mode
, &fb_info
->var
);
438 fb_add_videomode(&mode
, &fb_info
->modelist
);
439 registered_fb
[i
] = fb_info
;
441 #ifdef CONFIG_GUMSTIX_AM200EPD
443 struct fb_event event
;
444 event
.info
= fb_info
;
445 fb_notifier_call_chain(FB_EVENT_FB_REGISTERED
, &event
);
449 return fbcon_fb_registered(fb_info
);
452 static void unbind_console(struct fb_info
*fb_info
)
454 int i
= fb_info
->node
;
456 if (WARN_ON(i
< 0 || i
>= FB_MAX
|| registered_fb
[i
] != fb_info
))
459 fbcon_fb_unbind(fb_info
);
462 static void unlink_framebuffer(struct fb_info
*fb_info
)
467 if (WARN_ON(i
< 0 || i
>= FB_MAX
|| registered_fb
[i
] != fb_info
))
470 fb_device_destroy(fb_info
);
471 pm_vt_switch_unregister(fb_info
->device
);
472 unbind_console(fb_info
);
475 static void do_unregister_framebuffer(struct fb_info
*fb_info
)
477 unlink_framebuffer(fb_info
);
478 if (fb_info
->pixmap
.addr
&&
479 (fb_info
->pixmap
.flags
& FB_PIXMAP_DEFAULT
)) {
480 kfree(fb_info
->pixmap
.addr
);
481 fb_info
->pixmap
.addr
= NULL
;
484 fb_destroy_modelist(&fb_info
->modelist
);
485 registered_fb
[fb_info
->node
] = NULL
;
487 #ifdef CONFIG_GUMSTIX_AM200EPD
489 struct fb_event event
;
490 event
.info
= fb_info
;
491 fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED
, &event
);
494 fbcon_fb_unregistered(fb_info
);
496 /* this may free fb info */
497 put_fb_info(fb_info
);
501 * register_framebuffer - registers a frame buffer device
502 * @fb_info: frame buffer info structure
504 * Registers a frame buffer device @fb_info.
506 * Returns negative errno on error, or zero for success.
510 register_framebuffer(struct fb_info
*fb_info
)
514 mutex_lock(®istration_lock
);
515 ret
= do_register_framebuffer(fb_info
);
516 mutex_unlock(®istration_lock
);
520 EXPORT_SYMBOL(register_framebuffer
);
523 * unregister_framebuffer - releases a frame buffer device
524 * @fb_info: frame buffer info structure
526 * Unregisters a frame buffer device @fb_info.
528 * Returns negative errno on error, or zero for success.
530 * This function will also notify the framebuffer console
531 * to release the driver.
533 * This is meant to be called within a driver's module_exit()
534 * function. If this is called outside module_exit(), ensure
535 * that the driver implements fb_open() and fb_release() to
536 * check that no processes are using the device.
539 unregister_framebuffer(struct fb_info
*fb_info
)
541 mutex_lock(®istration_lock
);
542 do_unregister_framebuffer(fb_info
);
543 mutex_unlock(®istration_lock
);
545 EXPORT_SYMBOL(unregister_framebuffer
);
547 static void devm_unregister_framebuffer(void *data
)
549 struct fb_info
*info
= data
;
551 unregister_framebuffer(info
);
555 * devm_register_framebuffer - resource-managed frame buffer device registration
556 * @dev: device the framebuffer belongs to
557 * @fb_info: frame buffer info structure
559 * Registers a frame buffer device @fb_info to device @dev.
561 * Returns negative errno on error, or zero for success.
565 devm_register_framebuffer(struct device
*dev
, struct fb_info
*fb_info
)
569 ret
= register_framebuffer(fb_info
);
573 return devm_add_action_or_reset(dev
, devm_unregister_framebuffer
, fb_info
);
575 EXPORT_SYMBOL(devm_register_framebuffer
);
578 * fb_set_suspend - low level driver signals suspend
579 * @info: framebuffer affected
580 * @state: 0 = resuming, !=0 = suspending
582 * This is meant to be used by low level drivers to
583 * signal suspend/resume to the core & clients.
584 * It must be called with the console semaphore held
586 void fb_set_suspend(struct fb_info
*info
, int state
)
588 WARN_CONSOLE_UNLOCKED();
591 fbcon_suspended(info
);
592 info
->state
= FBINFO_STATE_SUSPENDED
;
594 info
->state
= FBINFO_STATE_RUNNING
;
598 EXPORT_SYMBOL(fb_set_suspend
);
600 static int __init
fbmem_init(void)
604 fb_class
= class_create("graphics");
605 if (IS_ERR(fb_class
)) {
606 ret
= PTR_ERR(fb_class
);
607 pr_err("Unable to create fb class; errno = %d\n", ret
);
611 ret
= fb_init_procfs();
613 goto err_class_destroy
;
615 ret
= fb_register_chrdev();
617 goto err_fb_cleanup_procfs
;
623 err_fb_cleanup_procfs
:
626 class_destroy(fb_class
);
633 static void __exit
fbmem_exit(void)
636 fb_unregister_chrdev();
638 class_destroy(fb_class
);
641 module_init(fbmem_init
);
642 module_exit(fbmem_exit
);
643 MODULE_LICENSE("GPL");
644 MODULE_DESCRIPTION("Framebuffer base");
646 subsys_initcall(fbmem_init
);
649 int fb_new_modelist(struct fb_info
*info
)
651 struct fb_var_screeninfo var
= info
->var
;
652 struct list_head
*pos
, *n
;
653 struct fb_modelist
*modelist
;
654 struct fb_videomode
*m
, mode
;
657 list_for_each_safe(pos
, n
, &info
->modelist
) {
658 modelist
= list_entry(pos
, struct fb_modelist
, list
);
660 fb_videomode_to_var(&var
, m
);
661 var
.activate
= FB_ACTIVATE_TEST
;
662 err
= fb_set_var(info
, &var
);
663 fb_var_to_videomode(&mode
, &var
);
664 if (err
|| !fb_mode_is_equal(m
, &mode
)) {
670 if (list_empty(&info
->modelist
))
673 fbcon_new_modelist(info
);
678 bool fb_modesetting_disabled(const char *drvname
)
680 bool fwonly
= video_firmware_drivers_only();
683 pr_warn("Driver %s not loading because of nomodeset parameter\n",
688 EXPORT_SYMBOL(fb_modesetting_disabled
);
690 MODULE_LICENSE("GPL");