File Index
Files and directories
Sign in
FILE

fbmem.c

File preview with syntax highlighting.

linux-6.19.14/drivers/video/fbdev/core/fbmem.c C
  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/console.h>
 15#include <linux/export.h>
 16#include <linux/fb.h>
 17#include <linux/fbcon.h>
 18#include <linux/lcd.h>
 19#include <linux/leds.h>
 20
 21#include <video/nomodeset.h>
 22
 23#include "fb_internal.h"
 24
 25    /*
 26     *  Frame buffer device initialization and setup routines
 27     */
 28
 29#define FBPIXMAPSIZE	(1024 * 8)
 30
 31struct class *fb_class;
 32
 33DEFINE_MUTEX(registration_lock);
 34struct fb_info *registered_fb[FB_MAX] __read_mostly;
 35int num_registered_fb __read_mostly;
 36#define for_each_registered_fb(i)		\
 37	for (i = 0; i < FB_MAX; i++)		\
 38		if (!registered_fb[i]) {} else
 39
 40struct fb_info *get_fb_info(unsigned int idx)
 41{
 42	struct fb_info *fb_info;
 43
 44	if (idx >= FB_MAX)
 45		return ERR_PTR(-ENODEV);
 46
 47	mutex_lock(&registration_lock);
 48	fb_info = registered_fb[idx];
 49	if (fb_info)
 50		refcount_inc(&fb_info->count);
 51	mutex_unlock(&registration_lock);
 52
 53	return fb_info;
 54}
 55
 56void put_fb_info(struct fb_info *fb_info)
 57{
 58	if (!refcount_dec_and_test(&fb_info->count))
 59		return;
 60	if (fb_info->fbops->fb_destroy)
 61		fb_info->fbops->fb_destroy(fb_info);
 62}
 63
 64/*
 65 * Helpers
 66 */
 67
 68int fb_get_color_depth(struct fb_var_screeninfo *var,
 69		       struct fb_fix_screeninfo *fix)
 70{
 71	int depth = 0;
 72
 73	if (fix->visual == FB_VISUAL_MONO01 ||
 74	    fix->visual == FB_VISUAL_MONO10)
 75		depth = 1;
 76	else {
 77		if (var->green.length == var->blue.length &&
 78		    var->green.length == var->red.length &&
 79		    var->green.offset == var->blue.offset &&
 80		    var->green.offset == var->red.offset)
 81			depth = var->green.length;
 82		else
 83			depth = var->green.length + var->red.length +
 84				var->blue.length;
 85	}
 86
 87	return depth;
 88}
 89EXPORT_SYMBOL(fb_get_color_depth);
 90
 91/*
 92 * Data padding functions.
 93 */
 94void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height)
 95{
 96	__fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height);
 97}
 98EXPORT_SYMBOL(fb_pad_aligned_buffer);
 99
100void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 idx, u32 height,
101				u32 shift_high, u32 shift_low, u32 mod)
102{
103	u8 mask = (u8) (0xfff << shift_high), tmp;
104	int i, j;
105
106	for (i = height; i--; ) {
107		for (j = 0; j < idx; j++) {
108			tmp = dst[j];
109			tmp &= mask;
110			tmp |= *src >> shift_low;
111			dst[j] = tmp;
112			tmp = *src << shift_high;
113			dst[j+1] = tmp;
114			src++;
115		}
116		tmp = dst[idx];
117		tmp &= mask;
118		tmp |= *src >> shift_low;
119		dst[idx] = tmp;
120		if (shift_high < mod) {
121			tmp = *src << shift_high;
122			dst[idx+1] = tmp;
123		}
124		src++;
125		dst += d_pitch;
126	}
127}
128EXPORT_SYMBOL(fb_pad_unaligned_buffer);
129
130/*
131 * we need to lock this section since fb_cursor
132 * may use fb_imageblit()
133 */
134char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size)
135{
136	u32 align = buf->buf_align - 1, offset;
137	char *addr = buf->addr;
138
139	/* If IO mapped, we need to sync before access, no sharing of
140	 * the pixmap is done
141	 */
142	if (buf->flags & FB_PIXMAP_IO) {
143		if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
144			info->fbops->fb_sync(info);
145		return addr;
146	}
147
148	/* See if we fit in the remaining pixmap space */
149	offset = buf->offset + align;
150	offset &= ~align;
151	if (offset + size > buf->size) {
152		/* We do not fit. In order to be able to re-use the buffer,
153		 * we must ensure no asynchronous DMA'ing or whatever operation
154		 * is in progress, we sync for that.
155		 */
156		if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
157			info->fbops->fb_sync(info);
158		offset = 0;
159	}
160	buf->offset = offset + size;
161	addr += offset;
162
163	return addr;
164}
165EXPORT_SYMBOL(fb_get_buffer_offset);
166
167int
168fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
169{
170	struct fb_fix_screeninfo *fix = &info->fix;
171	unsigned int yres = info->var.yres;
172	int err = 0;
173
174	if (var->yoffset > 0) {
175		if (var->vmode & FB_VMODE_YWRAP) {
176			if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
177				err = -EINVAL;
178			else
179				yres = 0;
180		} else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
181			err = -EINVAL;
182	}
183
184	if (var->xoffset > 0 && (!fix->xpanstep ||
185				 (var->xoffset % fix->xpanstep)))
186		err = -EINVAL;
187
188	if (err || !info->fbops->fb_pan_display ||
189	    var->yoffset > info->var.yres_virtual - yres ||
190	    var->xoffset > info->var.xres_virtual - info->var.xres)
191		return -EINVAL;
192
193	if ((err = info->fbops->fb_pan_display(var, info)))
194		return err;
195	info->var.xoffset = var->xoffset;
196	info->var.yoffset = var->yoffset;
197	if (var->vmode & FB_VMODE_YWRAP)
198		info->var.vmode |= FB_VMODE_YWRAP;
199	else
200		info->var.vmode &= ~FB_VMODE_YWRAP;
201	return 0;
202}
203EXPORT_SYMBOL(fb_pan_display);
204
205static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
206			 u32 activate)
207{
208	struct fb_blit_caps caps, fbcaps;
209	int err = 0;
210
211	memset(&caps, 0, sizeof(caps));
212	memset(&fbcaps, 0, sizeof(fbcaps));
213	caps.flags = (activate & FB_ACTIVATE_ALL) ? 1 : 0;
214	fbcon_get_requirement(info, &caps);
215	info->fbops->fb_get_caps(info, &fbcaps, var);
216
217	if (!bitmap_subset(caps.x, fbcaps.x, FB_MAX_BLIT_WIDTH) ||
218	    !bitmap_subset(caps.y, fbcaps.y, FB_MAX_BLIT_HEIGHT) ||
219	    (fbcaps.len < caps.len))
220		err = -EINVAL;
221
222	return err;
223}
224
225static void fb_lcd_notify_mode_change(struct fb_info *info,
226				      struct fb_videomode *mode)
227{
228	lcd_notify_mode_change_all(info->device, mode->xres, mode->yres);
229}
230
231int
232fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
233{
234	int ret = 0;
235	u32 activate;
236	struct fb_var_screeninfo old_var;
237	struct fb_videomode mode;
238	u32 unused;
239
240	if (var->activate & FB_ACTIVATE_INV_MODE) {
241		struct fb_videomode mode1, mode2;
242
243		fb_var_to_videomode(&mode1, var);
244		fb_var_to_videomode(&mode2, &info->var);
245		/* make sure we don't delete the videomode of current var */
246		ret = fb_mode_is_equal(&mode1, &mode2);
247		if (!ret) {
248			ret = fbcon_mode_deleted(info, &mode1);
249			if (!ret)
250				fb_delete_videomode(&mode1, &info->modelist);
251		}
252
253		return ret ? -EINVAL : 0;
254	}
255
256	if (!(var->activate & FB_ACTIVATE_FORCE) &&
257	    !memcmp(&info->var, var, sizeof(struct fb_var_screeninfo)))
258		return 0;
259
260	activate = var->activate;
261
262	/* When using FOURCC mode, make sure the red, green, blue and
263	 * transp fields are set to 0.
264	 */
265	if ((info->fix.capabilities & FB_CAP_FOURCC) &&
266	    var->grayscale > 1) {
267		if (var->red.offset     || var->green.offset    ||
268		    var->blue.offset    || var->transp.offset   ||
269		    var->red.length     || var->green.length    ||
270		    var->blue.length    || var->transp.length   ||
271		    var->red.msb_right  || var->green.msb_right ||
272		    var->blue.msb_right || var->transp.msb_right)
273			return -EINVAL;
274	}
275
276	if (!info->fbops->fb_check_var) {
277		*var = info->var;
278		return 0;
279	}
280
281	/* bitfill_aligned() assumes that it's at least 8x8 */
282	if (var->xres < 8 || var->yres < 8)
283		return -EINVAL;
284
285	/* Too huge resolution causes multiplication overflow. */
286	if (check_mul_overflow(var->xres, var->yres, &unused) ||
287	    check_mul_overflow(var->xres_virtual, var->yres_virtual, &unused))
288		return -EINVAL;
289
290	ret = info->fbops->fb_check_var(var, info);
291
292	if (ret)
293		return ret;
294
295	/* verify that virtual resolution >= physical resolution */
296	if (var->xres_virtual < var->xres ||
297	    var->yres_virtual < var->yres) {
298		pr_warn("WARNING: fbcon: Driver '%s' missed to adjust virtual screen size (%ux%u vs. %ux%u)\n",
299			info->fix.id,
300			var->xres_virtual, var->yres_virtual,
301			var->xres, var->yres);
302		return -EINVAL;
303	}
304
305	if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
306		return 0;
307
308	if (info->fbops->fb_get_caps) {
309		ret = fb_check_caps(info, var, activate);
310
311		if (ret)
312			return ret;
313	}
314
315	old_var = info->var;
316	info->var = *var;
317
318	if (info->fbops->fb_set_par) {
319		ret = info->fbops->fb_set_par(info);
320
321		if (ret) {
322			info->var = old_var;
323			printk(KERN_WARNING "detected "
324				"fb_set_par error, "
325				"error code: %d\n", ret);
326			return ret;
327		}
328	}
329
330	fb_pan_display(info, &info->var);
331	fb_set_cmap(&info->cmap, info);
332	fb_var_to_videomode(&mode, &info->var);
333
334	if (info->modelist.prev && info->modelist.next &&
335	    !list_empty(&info->modelist))
336		ret = fb_add_videomode(&mode, &info->modelist);
337
338	if (ret) {
339		info->var = old_var;
340		return ret;
341	}
342
343	fb_lcd_notify_mode_change(info, &mode);
344
345	return 0;
346}
347EXPORT_SYMBOL(fb_set_var);
348
349static void fb_lcd_notify_blank(struct fb_info *info)
350{
351	int power;
352
353	switch (info->blank) {
354	case FB_BLANK_UNBLANK:
355		power = LCD_POWER_ON;
356		break;
357	/* deprecated; TODO: should become 'off' */
358	case FB_BLANK_NORMAL:
359		power = LCD_POWER_REDUCED;
360		break;
361	case FB_BLANK_VSYNC_SUSPEND:
362		power = LCD_POWER_REDUCED_VSYNC_SUSPEND;
363		break;
364	/* 'off' */
365	case FB_BLANK_HSYNC_SUSPEND:
366	case FB_BLANK_POWERDOWN:
367	default:
368		power = LCD_POWER_OFF;
369		break;
370	}
371
372	lcd_notify_blank_all(info->device, power);
373}
374
375static void fb_ledtrig_backlight_notify_blank(struct fb_info *info)
376{
377	if (info->blank == FB_BLANK_UNBLANK)
378		ledtrig_backlight_blank(false);
379	else
380		ledtrig_backlight_blank(true);
381}
382
383int fb_blank(struct fb_info *info, int blank)
384{
385	int old_blank = info->blank;
386	int ret;
387
388	if (!info->fbops->fb_blank)
389		return -EINVAL;
390
391	if (blank > FB_BLANK_POWERDOWN)
392		blank = FB_BLANK_POWERDOWN;
393
394	info->blank = blank;
395
396	ret = info->fbops->fb_blank(blank, info);
397	if (ret)
398		goto err;
399
400	fb_bl_notify_blank(info, old_blank);
401	fb_lcd_notify_blank(info);
402	fb_ledtrig_backlight_notify_blank(info);
403
404	return 0;
405
406err:
407	info->blank = old_blank;
408	return ret;
409}
410EXPORT_SYMBOL(fb_blank);
411
412static int fb_check_foreignness(struct fb_info *fi)
413{
414	const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
415
416	fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
417
418#ifdef __BIG_ENDIAN
419	fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
420#else
421	fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
422#endif /* __BIG_ENDIAN */
423
424	if (fi->flags & FBINFO_BE_MATH && !fb_be_math(fi)) {
425		pr_err("%s: enable CONFIG_FB_BIG_ENDIAN to "
426		       "support this framebuffer\n", fi->fix.id);
427		return -ENOSYS;
428	} else if (!(fi->flags & FBINFO_BE_MATH) && fb_be_math(fi)) {
429		pr_err("%s: enable CONFIG_FB_LITTLE_ENDIAN to "
430		       "support this framebuffer\n", fi->fix.id);
431		return -ENOSYS;
432	}
433
434	return 0;
435}
436
437static int do_register_framebuffer(struct fb_info *fb_info)
438{
439	int i, err = 0;
440	struct fb_videomode mode;
441
442	if (fb_check_foreignness(fb_info))
443		return -ENOSYS;
444
445	if (num_registered_fb == FB_MAX)
446		return -ENXIO;
447
448	for (i = 0 ; i < FB_MAX; i++)
449		if (!registered_fb[i])
450			break;
451
452	if (i >= FB_MAX)
453		return -ENXIO;
454
455	if (!fb_info->modelist.prev || !fb_info->modelist.next)
456		INIT_LIST_HEAD(&fb_info->modelist);
457
458	fb_var_to_videomode(&mode, &fb_info->var);
459	err = fb_add_videomode(&mode, &fb_info->modelist);
460	if (err < 0)
461		return err;
462
463	fb_info->node = i;
464	refcount_set(&fb_info->count, 1);
465	mutex_init(&fb_info->lock);
466	mutex_init(&fb_info->mm_lock);
467
468	/*
469	 * With an fb_blank callback present, we assume that the
470	 * display is blank, so that fb_blank() enables it on the
471	 * first modeset.
472	 */
473	if (fb_info->fbops->fb_blank)
474		fb_info->blank = FB_BLANK_POWERDOWN;
475
476	fb_device_create(fb_info);
477
478	if (fb_info->pixmap.addr == NULL) {
479		fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
480		if (fb_info->pixmap.addr) {
481			fb_info->pixmap.size = FBPIXMAPSIZE;
482			fb_info->pixmap.buf_align = 1;
483			fb_info->pixmap.scan_align = 1;
484			fb_info->pixmap.access_align = 32;
485			fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
486		}
487	}
488	fb_info->pixmap.offset = 0;
489
490	if (bitmap_empty(fb_info->pixmap.blit_x, FB_MAX_BLIT_WIDTH))
491		bitmap_fill(fb_info->pixmap.blit_x, FB_MAX_BLIT_WIDTH);
492
493	if (bitmap_empty(fb_info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT))
494		bitmap_fill(fb_info->pixmap.blit_y, FB_MAX_BLIT_HEIGHT);
495
496	if (fb_info->skip_vt_switch)
497		pm_vt_switch_required(fb_info->device, false);
498	else
499		pm_vt_switch_required(fb_info->device, true);
500
501	num_registered_fb++;
502	registered_fb[i] = fb_info;
503
504#ifdef CONFIG_GUMSTIX_AM200EPD
505	{
506		struct fb_event event;
507		event.info = fb_info;
508		fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
509	}
510#endif
511
512	return fbcon_fb_registered(fb_info);
513}
514
515static void unbind_console(struct fb_info *fb_info)
516{
517	int i = fb_info->node;
518
519	if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
520		return;
521
522	fbcon_fb_unbind(fb_info);
523}
524
525static void unlink_framebuffer(struct fb_info *fb_info)
526{
527	int i;
528
529	i = fb_info->node;
530	if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
531		return;
532
533	fb_device_destroy(fb_info);
534	pm_vt_switch_unregister(fb_info->device);
535	unbind_console(fb_info);
536}
537
538static void do_unregister_framebuffer(struct fb_info *fb_info)
539{
540	unlink_framebuffer(fb_info);
541	if (fb_info->pixmap.addr &&
542	    (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) {
543		kfree(fb_info->pixmap.addr);
544		fb_info->pixmap.addr = NULL;
545	}
546
547	fbcon_delete_modelist(&fb_info->modelist);
548	fb_destroy_modelist(&fb_info->modelist);
549	registered_fb[fb_info->node] = NULL;
550	num_registered_fb--;
551#ifdef CONFIG_GUMSTIX_AM200EPD
552	{
553		struct fb_event event;
554		event.info = fb_info;
555		fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
556	}
557#endif
558	fbcon_fb_unregistered(fb_info);
559
560	/* this may free fb info */
561	put_fb_info(fb_info);
562}
563
564/**
565 *	register_framebuffer - registers a frame buffer device
566 *	@fb_info: frame buffer info structure
567 *
568 *	Registers a frame buffer device @fb_info.
569 *
570 *	Returns negative errno on error, or zero for success.
571 *
572 */
573int
574register_framebuffer(struct fb_info *fb_info)
575{
576	int ret;
577
578	mutex_lock(&registration_lock);
579	ret = do_register_framebuffer(fb_info);
580	mutex_unlock(&registration_lock);
581
582	return ret;
583}
584EXPORT_SYMBOL(register_framebuffer);
585
586/**
587 *	unregister_framebuffer - releases a frame buffer device
588 *	@fb_info: frame buffer info structure
589 *
590 *	Unregisters a frame buffer device @fb_info.
591 *
592 *	Returns negative errno on error, or zero for success.
593 *
594 *      This function will also notify the framebuffer console
595 *      to release the driver.
596 *
597 *      This is meant to be called within a driver's module_exit()
598 *      function. If this is called outside module_exit(), ensure
599 *      that the driver implements fb_open() and fb_release() to
600 *      check that no processes are using the device.
601 */
602void
603unregister_framebuffer(struct fb_info *fb_info)
604{
605	mutex_lock(&registration_lock);
606	do_unregister_framebuffer(fb_info);
607	mutex_unlock(&registration_lock);
608}
609EXPORT_SYMBOL(unregister_framebuffer);
610
611static void devm_unregister_framebuffer(void *data)
612{
613	struct fb_info *info = data;
614
615	unregister_framebuffer(info);
616}
617
618/**
619 *	devm_register_framebuffer - resource-managed frame buffer device registration
620 *	@dev: device the framebuffer belongs to
621 *	@fb_info: frame buffer info structure
622 *
623 *	Registers a frame buffer device @fb_info to device @dev.
624 *
625 *	Returns negative errno on error, or zero for success.
626 *
627 */
628int
629devm_register_framebuffer(struct device *dev, struct fb_info *fb_info)
630{
631	int ret;
632
633	ret = register_framebuffer(fb_info);
634	if (ret)
635		return ret;
636
637	return devm_add_action_or_reset(dev, devm_unregister_framebuffer, fb_info);
638}
639EXPORT_SYMBOL(devm_register_framebuffer);
640
641/**
642 *	fb_set_suspend - low level driver signals suspend
643 *	@info: framebuffer affected
644 *	@state: 0 = resuming, !=0 = suspending
645 *
646 *	This is meant to be used by low level drivers to
647 * 	signal suspend/resume to the core & clients.
648 *	It must be called with the console semaphore held
649 */
650void fb_set_suspend(struct fb_info *info, int state)
651{
652	WARN_CONSOLE_UNLOCKED();
653
654	if (state) {
655		fbcon_suspended(info);
656		info->state = FBINFO_STATE_SUSPENDED;
657	} else {
658		info->state = FBINFO_STATE_RUNNING;
659		fbcon_resumed(info);
660	}
661}
662EXPORT_SYMBOL(fb_set_suspend);
663
664static int __init fbmem_init(void)
665{
666	int ret;
667
668	fb_class = class_create("graphics");
669	if (IS_ERR(fb_class)) {
670		ret = PTR_ERR(fb_class);
671		pr_err("Unable to create fb class; errno = %d\n", ret);
672		goto err_fb_class;
673	}
674
675	ret = fb_init_procfs();
676	if (ret)
677		goto err_class_destroy;
678
679	ret = fb_register_chrdev();
680	if (ret)
681		goto err_fb_cleanup_procfs;
682
683	fb_console_init();
684
685	return 0;
686
687err_fb_cleanup_procfs:
688	fb_cleanup_procfs();
689err_class_destroy:
690	class_destroy(fb_class);
691err_fb_class:
692	fb_class = NULL;
693	return ret;
694}
695
696#ifdef MODULE
697static void __exit fbmem_exit(void)
698{
699	fb_console_exit();
700	fb_unregister_chrdev();
701	fb_cleanup_procfs();
702	class_destroy(fb_class);
703}
704
705module_init(fbmem_init);
706module_exit(fbmem_exit);
707MODULE_LICENSE("GPL");
708MODULE_DESCRIPTION("Framebuffer base");
709#else
710subsys_initcall(fbmem_init);
711#endif
712
713int fb_new_modelist(struct fb_info *info)
714{
715	struct fb_var_screeninfo var = info->var;
716	struct list_head *pos, *n;
717	struct fb_modelist *modelist;
718	struct fb_videomode *m, mode;
719	int err;
720
721	list_for_each_safe(pos, n, &info->modelist) {
722		modelist = list_entry(pos, struct fb_modelist, list);
723		m = &modelist->mode;
724		fb_videomode_to_var(&var, m);
725		var.activate = FB_ACTIVATE_TEST;
726		err = fb_set_var(info, &var);
727		fb_var_to_videomode(&mode, &var);
728		if (err || !fb_mode_is_equal(m, &mode)) {
729			list_del(pos);
730			kfree(pos);
731		}
732	}
733
734	if (list_empty(&info->modelist))
735		return 1;
736
737	fbcon_new_modelist(info);
738
739	return 0;
740}
741
742bool fb_modesetting_disabled(const char *drvname)
743{
744	bool fwonly = video_firmware_drivers_only();
745
746	if (fwonly)
747		pr_warn("Driver %s not loading because of nomodeset parameter\n",
748			drvname);
749
750	return fwonly;
751}
752EXPORT_SYMBOL(fb_modesetting_disabled);
753
754MODULE_LICENSE("GPL");
~/ Read-only file preview.