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
19#include <video/nomodeset.h>
20
21#include "fb_internal.h"
22
23    /*
24     *  Frame buffer device initialization and setup routines
25     */
26
27#define FBPIXMAPSIZE	(1024 * 8)
28
29struct class *fb_class;
30
31DEFINE_MUTEX(registration_lock);
32struct fb_info *registered_fb[FB_MAX] __read_mostly;
33int 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
37
38struct fb_info *get_fb_info(unsigned int idx)
39{
40	struct fb_info *fb_info;
41
42	if (idx >= FB_MAX)
43		return ERR_PTR(-ENODEV);
44
45	mutex_lock(&registration_lock);
46	fb_info = registered_fb[idx];
47	if (fb_info)
48		refcount_inc(&fb_info->count);
49	mutex_unlock(&registration_lock);
50
51	return fb_info;
52}
53
54void put_fb_info(struct fb_info *fb_info)
55{
56	if (!refcount_dec_and_test(&fb_info->count))
57		return;
58	if (fb_info->fbops->fb_destroy)
59		fb_info->fbops->fb_destroy(fb_info);
60}
61
62/*
63 * Helpers
64 */
65
66int fb_get_color_depth(struct fb_var_screeninfo *var,
67		       struct fb_fix_screeninfo *fix)
68{
69	int depth = 0;
70
71	if (fix->visual == FB_VISUAL_MONO01 ||
72	    fix->visual == FB_VISUAL_MONO10)
73		depth = 1;
74	else {
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;
80		else
81			depth = var->green.length + var->red.length +
82				var->blue.length;
83	}
84
85	return depth;
86}
87EXPORT_SYMBOL(fb_get_color_depth);
88
89/*
90 * Data padding functions.
91 */
92void fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height)
93{
94	__fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, height);
95}
96EXPORT_SYMBOL(fb_pad_aligned_buffer);
97
98void fb_pad_unaligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 idx, u32 height,
99				u32 shift_high, u32 shift_low, u32 mod)
100{
101	u8 mask = (u8) (0xfff << shift_high), tmp;
102	int i, j;
103
104	for (i = height; i--; ) {
105		for (j = 0; j < idx; j++) {
106			tmp = dst[j];
107			tmp &= mask;
108			tmp |= *src >> shift_low;
109			dst[j] = tmp;
110			tmp = *src << shift_high;
111			dst[j+1] = tmp;
112			src++;
113		}
114		tmp = dst[idx];
115		tmp &= mask;
116		tmp |= *src >> shift_low;
117		dst[idx] = tmp;
118		if (shift_high < mod) {
119			tmp = *src << shift_high;
120			dst[idx+1] = tmp;
121		}
122		src++;
123		dst += d_pitch;
124	}
125}
126EXPORT_SYMBOL(fb_pad_unaligned_buffer);
127
128/*
129 * we need to lock this section since fb_cursor
130 * may use fb_imageblit()
131 */
132char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size)
133{
134	u32 align = buf->buf_align - 1, offset;
135	char *addr = buf->addr;
136
137	/* If IO mapped, we need to sync before access, no sharing of
138	 * the pixmap is done
139	 */
140	if (buf->flags & FB_PIXMAP_IO) {
141		if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
142			info->fbops->fb_sync(info);
143		return addr;
144	}
145
146	/* See if we fit in the remaining pixmap space */
147	offset = buf->offset + align;
148	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.
153		 */
154		if (info->fbops->fb_sync && (buf->flags & FB_PIXMAP_SYNC))
155			info->fbops->fb_sync(info);
156		offset = 0;
157	}
158	buf->offset = offset + size;
159	addr += offset;
160
161	return addr;
162}
163EXPORT_SYMBOL(fb_get_buffer_offset);
164
165int
166fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
167{
168	struct fb_fix_screeninfo *fix = &info->fix;
169	unsigned int yres = info->var.yres;
170	int err = 0;
171
172	if (var->yoffset > 0) {
173		if (var->vmode & FB_VMODE_YWRAP) {
174			if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
175				err = -EINVAL;
176			else
177				yres = 0;
178		} else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
179			err = -EINVAL;
180	}
181
182	if (var->xoffset > 0 && (!fix->xpanstep ||
183				 (var->xoffset % fix->xpanstep)))
184		err = -EINVAL;
185
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)
189		return -EINVAL;
190
191	if ((err = info->fbops->fb_pan_display(var, info)))
192		return err;
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;
197	else
198		info->var.vmode &= ~FB_VMODE_YWRAP;
199	return 0;
200}
201EXPORT_SYMBOL(fb_pan_display);
202
203static int fb_check_caps(struct fb_info *info, struct fb_var_screeninfo *var,
204			 u32 activate)
205{
206	struct fb_blit_caps caps, fbcaps;
207	int err = 0;
208
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);
214
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))
218		err = -EINVAL;
219
220	return err;
221}
222
223int
224fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
225{
226	int ret = 0;
227	u32 activate;
228	struct fb_var_screeninfo old_var;
229	struct fb_videomode mode;
230	struct fb_event event;
231	u32 unused;
232
233	if (var->activate & FB_ACTIVATE_INV_MODE) {
234		struct fb_videomode mode1, mode2;
235
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);
240		if (!ret) {
241			ret = fbcon_mode_deleted(info, &mode1);
242			if (!ret)
243				fb_delete_videomode(&mode1, &info->modelist);
244		}
245
246		return ret ? -EINVAL : 0;
247	}
248
249	if (!(var->activate & FB_ACTIVATE_FORCE) &&
250	    !memcmp(&info->var, var, sizeof(struct fb_var_screeninfo)))
251		return 0;
252
253	activate = var->activate;
254
255	/* When using FOURCC mode, make sure the red, green, blue and
256	 * transp fields are set to 0.
257	 */
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)
266			return -EINVAL;
267	}
268
269	if (!info->fbops->fb_check_var) {
270		*var = info->var;
271		return 0;
272	}
273
274	/* bitfill_aligned() assumes that it's at least 8x8 */
275	if (var->xres < 8 || var->yres < 8)
276		return -EINVAL;
277
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))
281		return -EINVAL;
282
283	ret = info->fbops->fb_check_var(var, info);
284
285	if (ret)
286		return ret;
287
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",
292			info->fix.id,
293			var->xres_virtual, var->yres_virtual,
294			var->xres, var->yres);
295		return -EINVAL;
296	}
297
298	if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
299		return 0;
300
301	if (info->fbops->fb_get_caps) {
302		ret = fb_check_caps(info, var, activate);
303
304		if (ret)
305			return ret;
306	}
307
308	old_var = info->var;
309	info->var = *var;
310
311	if (info->fbops->fb_set_par) {
312		ret = info->fbops->fb_set_par(info);
313
314		if (ret) {
315			info->var = old_var;
316			printk(KERN_WARNING "detected "
317				"fb_set_par error, "
318				"error code: %d\n", ret);
319			return ret;
320		}
321	}
322
323	fb_pan_display(info, &info->var);
324	fb_set_cmap(&info->cmap, info);
325	fb_var_to_videomode(&mode, &info->var);
326
327	if (info->modelist.prev && info->modelist.next &&
328	    !list_empty(&info->modelist))
329		ret = fb_add_videomode(&mode, &info->modelist);
330
331	if (ret)
332		return ret;
333
334	event.info = info;
335	event.data = &mode;
336	fb_notifier_call_chain(FB_EVENT_MODE_CHANGE, &event);
337
338	return 0;
339}
340EXPORT_SYMBOL(fb_set_var);
341
342int
343fb_blank(struct fb_info *info, int blank)
344{
345	struct fb_event event;
346	int ret = -EINVAL;
347
348	if (blank > FB_BLANK_POWERDOWN)
349		blank = FB_BLANK_POWERDOWN;
350
351	event.info = info;
352	event.data = &blank;
353
354	if (info->fbops->fb_blank)
355		ret = info->fbops->fb_blank(blank, info);
356
357	if (!ret)
358		fb_notifier_call_chain(FB_EVENT_BLANK, &event);
359
360	return ret;
361}
362EXPORT_SYMBOL(fb_blank);
363
364static int fb_check_foreignness(struct fb_info *fi)
365{
366	const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN;
367
368	fi->flags &= ~FBINFO_FOREIGN_ENDIAN;
369
370#ifdef __BIG_ENDIAN
371	fi->flags |= foreign_endian ? 0 : FBINFO_BE_MATH;
372#else
373	fi->flags |= foreign_endian ? FBINFO_BE_MATH : 0;
374#endif /* __BIG_ENDIAN */
375
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);
379		return -ENOSYS;
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);
383		return -ENOSYS;
384	}
385
386	return 0;
387}
388
389static int do_register_framebuffer(struct fb_info *fb_info)
390{
391	int i;
392	struct fb_videomode mode;
393
394	if (fb_check_foreignness(fb_info))
395		return -ENOSYS;
396
397	if (num_registered_fb == FB_MAX)
398		return -ENXIO;
399
400	num_registered_fb++;
401	for (i = 0 ; i < FB_MAX; i++)
402		if (!registered_fb[i])
403			break;
404	fb_info->node = i;
405	refcount_set(&fb_info->count, 1);
406	mutex_init(&fb_info->lock);
407	mutex_init(&fb_info->mm_lock);
408
409	fb_device_create(fb_info);
410
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;
419		}
420	}
421	fb_info->pixmap.offset = 0;
422
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);
425
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);
428
429	if (!fb_info->modelist.prev || !fb_info->modelist.next)
430		INIT_LIST_HEAD(&fb_info->modelist);
431
432	if (fb_info->skip_vt_switch)
433		pm_vt_switch_required(fb_info->device, false);
434	else
435		pm_vt_switch_required(fb_info->device, true);
436
437	fb_var_to_videomode(&mode, &fb_info->var);
438	fb_add_videomode(&mode, &fb_info->modelist);
439	registered_fb[i] = fb_info;
440
441#ifdef CONFIG_GUMSTIX_AM200EPD
442	{
443		struct fb_event event;
444		event.info = fb_info;
445		fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
446	}
447#endif
448
449	return fbcon_fb_registered(fb_info);
450}
451
452static void unbind_console(struct fb_info *fb_info)
453{
454	int i = fb_info->node;
455
456	if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
457		return;
458
459	fbcon_fb_unbind(fb_info);
460}
461
462static void unlink_framebuffer(struct fb_info *fb_info)
463{
464	int i;
465
466	i = fb_info->node;
467	if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info))
468		return;
469
470	fb_device_destroy(fb_info);
471	pm_vt_switch_unregister(fb_info->device);
472	unbind_console(fb_info);
473}
474
475static void do_unregister_framebuffer(struct fb_info *fb_info)
476{
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;
482	}
483
484	fb_destroy_modelist(&fb_info->modelist);
485	registered_fb[fb_info->node] = NULL;
486	num_registered_fb--;
487#ifdef CONFIG_GUMSTIX_AM200EPD
488	{
489		struct fb_event event;
490		event.info = fb_info;
491		fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
492	}
493#endif
494	fbcon_fb_unregistered(fb_info);
495
496	/* this may free fb info */
497	put_fb_info(fb_info);
498}
499
500/**
501 *	register_framebuffer - registers a frame buffer device
502 *	@fb_info: frame buffer info structure
503 *
504 *	Registers a frame buffer device @fb_info.
505 *
506 *	Returns negative errno on error, or zero for success.
507 *
508 */
509int
510register_framebuffer(struct fb_info *fb_info)
511{
512	int ret;
513
514	mutex_lock(&registration_lock);
515	ret = do_register_framebuffer(fb_info);
516	mutex_unlock(&registration_lock);
517
518	return ret;
519}
520EXPORT_SYMBOL(register_framebuffer);
521
522/**
523 *	unregister_framebuffer - releases a frame buffer device
524 *	@fb_info: frame buffer info structure
525 *
526 *	Unregisters a frame buffer device @fb_info.
527 *
528 *	Returns negative errno on error, or zero for success.
529 *
530 *      This function will also notify the framebuffer console
531 *      to release the driver.
532 *
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.
537 */
538void
539unregister_framebuffer(struct fb_info *fb_info)
540{
541	mutex_lock(&registration_lock);
542	do_unregister_framebuffer(fb_info);
543	mutex_unlock(&registration_lock);
544}
545EXPORT_SYMBOL(unregister_framebuffer);
546
547/**
548 *	fb_set_suspend - low level driver signals suspend
549 *	@info: framebuffer affected
550 *	@state: 0 = resuming, !=0 = suspending
551 *
552 *	This is meant to be used by low level drivers to
553 * 	signal suspend/resume to the core & clients.
554 *	It must be called with the console semaphore held
555 */
556void fb_set_suspend(struct fb_info *info, int state)
557{
558	WARN_CONSOLE_UNLOCKED();
559
560	if (state) {
561		fbcon_suspended(info);
562		info->state = FBINFO_STATE_SUSPENDED;
563	} else {
564		info->state = FBINFO_STATE_RUNNING;
565		fbcon_resumed(info);
566	}
567}
568EXPORT_SYMBOL(fb_set_suspend);
569
570static int __init fbmem_init(void)
571{
572	int ret;
573
574	fb_class = class_create("graphics");
575	if (IS_ERR(fb_class)) {
576		ret = PTR_ERR(fb_class);
577		pr_err("Unable to create fb class; errno = %d\n", ret);
578		goto err_fb_class;
579	}
580
581	ret = fb_init_procfs();
582	if (ret)
583		goto err_class_destroy;
584
585	ret = fb_register_chrdev();
586	if (ret)
587		goto err_fb_cleanup_procfs;
588
589	fb_console_init();
590
591	return 0;
592
593err_fb_cleanup_procfs:
594	fb_cleanup_procfs();
595err_class_destroy:
596	class_destroy(fb_class);
597err_fb_class:
598	fb_class = NULL;
599	return ret;
600}
601
602#ifdef MODULE
603static void __exit fbmem_exit(void)
604{
605	fb_console_exit();
606	fb_unregister_chrdev();
607	fb_cleanup_procfs();
608	class_destroy(fb_class);
609}
610
611module_init(fbmem_init);
612module_exit(fbmem_exit);
613MODULE_LICENSE("GPL");
614MODULE_DESCRIPTION("Framebuffer base");
615#else
616subsys_initcall(fbmem_init);
617#endif
618
619int fb_new_modelist(struct fb_info *info)
620{
621	struct fb_var_screeninfo var = info->var;
622	struct list_head *pos, *n;
623	struct fb_modelist *modelist;
624	struct fb_videomode *m, mode;
625	int err;
626
627	list_for_each_safe(pos, n, &info->modelist) {
628		modelist = list_entry(pos, struct fb_modelist, list);
629		m = &modelist->mode;
630		fb_videomode_to_var(&var, m);
631		var.activate = FB_ACTIVATE_TEST;
632		err = fb_set_var(info, &var);
633		fb_var_to_videomode(&mode, &var);
634		if (err || !fb_mode_is_equal(m, &mode)) {
635			list_del(pos);
636			kfree(pos);
637		}
638	}
639
640	if (list_empty(&info->modelist))
641		return 1;
642
643	fbcon_new_modelist(info);
644
645	return 0;
646}
647
648bool fb_modesetting_disabled(const char *drvname)
649{
650	bool fwonly = video_firmware_drivers_only();
651
652	if (fwonly)
653		pr_warn("Driver %s not loading because of nomodeset parameter\n",
654			drvname);
655
656	return fwonly;
657}
658EXPORT_SYMBOL(fb_modesetting_disabled);
659
660MODULE_LICENSE("GPL");
661