[go: nahoru, domu]

1/*
2 * fbsysfs.c - framebuffer device class and attributes
3 *
4 * Copyright (c) 2004 James Simmons <jsimmons@infradead.org>
5 *
6 *	This program is free software you can redistribute it and/or
7 *	modify it under the terms of the GNU General Public License
8 *	as published by the Free Software Foundation; either version
9 *	2 of the License, or (at your option) any later version.
10 */
11
12/*
13 * Note:  currently there's only stubs for framebuffer_alloc and
14 * framebuffer_release here.  The reson for that is that until all drivers
15 * are converted to use it a sysfsification will open OOPSable races.
16 */
17
18#include <linux/kernel.h>
19#include <linux/slab.h>
20#include <linux/fb.h>
21#include <linux/console.h>
22#include <linux/module.h>
23
24#define FB_SYSFS_FLAG_ATTR 1
25
26/**
27 * framebuffer_alloc - creates a new frame buffer info structure
28 *
29 * @size: size of driver private data, can be zero
30 * @dev: pointer to the device for this fb, this can be NULL
31 *
32 * Creates a new frame buffer info structure. Also reserves @size bytes
33 * for driver private data (info->par). info->par (if any) will be
34 * aligned to sizeof(long).
35 *
36 * Returns the new structure, or NULL if an error occurred.
37 *
38 */
39struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
40{
41#define BYTES_PER_LONG (BITS_PER_LONG/8)
42#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
43	int fb_info_size = sizeof(struct fb_info);
44	struct fb_info *info;
45	char *p;
46
47	if (size)
48		fb_info_size += PADDING;
49
50	p = kzalloc(fb_info_size + size, GFP_KERNEL);
51
52	if (!p)
53		return NULL;
54
55	info = (struct fb_info *) p;
56
57	if (size)
58		info->par = p + fb_info_size;
59
60	info->device = dev;
61
62#ifdef CONFIG_FB_BACKLIGHT
63	mutex_init(&info->bl_curve_mutex);
64#endif
65
66	return info;
67#undef PADDING
68#undef BYTES_PER_LONG
69}
70EXPORT_SYMBOL(framebuffer_alloc);
71
72/**
73 * framebuffer_release - marks the structure available for freeing
74 *
75 * @info: frame buffer info structure
76 *
77 * Drop the reference count of the device embedded in the
78 * framebuffer info structure.
79 *
80 */
81void framebuffer_release(struct fb_info *info)
82{
83	if (!info)
84		return;
85	kfree(info->apertures);
86	kfree(info);
87}
88EXPORT_SYMBOL(framebuffer_release);
89
90static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
91{
92	int err;
93
94	var->activate |= FB_ACTIVATE_FORCE;
95	console_lock();
96	fb_info->flags |= FBINFO_MISC_USEREVENT;
97	err = fb_set_var(fb_info, var);
98	fb_info->flags &= ~FBINFO_MISC_USEREVENT;
99	console_unlock();
100	if (err)
101		return err;
102	return 0;
103}
104
105static int mode_string(char *buf, unsigned int offset,
106		       const struct fb_videomode *mode)
107{
108	char m = 'U';
109	char v = 'p';
110
111	if (mode->flag & FB_MODE_IS_DETAILED)
112		m = 'D';
113	if (mode->flag & FB_MODE_IS_VESA)
114		m = 'V';
115	if (mode->flag & FB_MODE_IS_STANDARD)
116		m = 'S';
117
118	if (mode->vmode & FB_VMODE_INTERLACED)
119		v = 'i';
120	if (mode->vmode & FB_VMODE_DOUBLE)
121		v = 'd';
122
123	return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
124	                m, mode->xres, mode->yres, v, mode->refresh);
125}
126
127static ssize_t store_mode(struct device *device, struct device_attribute *attr,
128			  const char *buf, size_t count)
129{
130	struct fb_info *fb_info = dev_get_drvdata(device);
131	char mstr[100];
132	struct fb_var_screeninfo var;
133	struct fb_modelist *modelist;
134	struct fb_videomode *mode;
135	struct list_head *pos;
136	size_t i;
137	int err;
138
139	memset(&var, 0, sizeof(var));
140
141	list_for_each(pos, &fb_info->modelist) {
142		modelist = list_entry(pos, struct fb_modelist, list);
143		mode = &modelist->mode;
144		i = mode_string(mstr, 0, mode);
145		if (strncmp(mstr, buf, max(count, i)) == 0) {
146
147			var = fb_info->var;
148			fb_videomode_to_var(&var, mode);
149			if ((err = activate(fb_info, &var)))
150				return err;
151			fb_info->mode = mode;
152			return count;
153		}
154	}
155	return -EINVAL;
156}
157
158static ssize_t show_mode(struct device *device, struct device_attribute *attr,
159			 char *buf)
160{
161	struct fb_info *fb_info = dev_get_drvdata(device);
162
163	if (!fb_info->mode)
164		return 0;
165
166	return mode_string(buf, 0, fb_info->mode);
167}
168
169static ssize_t store_modes(struct device *device,
170			   struct device_attribute *attr,
171			   const char *buf, size_t count)
172{
173	struct fb_info *fb_info = dev_get_drvdata(device);
174	LIST_HEAD(old_list);
175	int i = count / sizeof(struct fb_videomode);
176
177	if (i * sizeof(struct fb_videomode) != count)
178		return -EINVAL;
179
180	console_lock();
181	if (!lock_fb_info(fb_info)) {
182		console_unlock();
183		return -ENODEV;
184	}
185
186	list_splice(&fb_info->modelist, &old_list);
187	fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
188				 &fb_info->modelist);
189	if (fb_new_modelist(fb_info)) {
190		fb_destroy_modelist(&fb_info->modelist);
191		list_splice(&old_list, &fb_info->modelist);
192	} else
193		fb_destroy_modelist(&old_list);
194
195	unlock_fb_info(fb_info);
196	console_unlock();
197
198	return 0;
199}
200
201static ssize_t show_modes(struct device *device, struct device_attribute *attr,
202			  char *buf)
203{
204	struct fb_info *fb_info = dev_get_drvdata(device);
205	unsigned int i;
206	struct list_head *pos;
207	struct fb_modelist *modelist;
208	const struct fb_videomode *mode;
209
210	i = 0;
211	list_for_each(pos, &fb_info->modelist) {
212		modelist = list_entry(pos, struct fb_modelist, list);
213		mode = &modelist->mode;
214		i += mode_string(buf, i, mode);
215	}
216	return i;
217}
218
219static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
220			 const char *buf, size_t count)
221{
222	struct fb_info *fb_info = dev_get_drvdata(device);
223	struct fb_var_screeninfo var;
224	char ** last = NULL;
225	int err;
226
227	var = fb_info->var;
228	var.bits_per_pixel = simple_strtoul(buf, last, 0);
229	if ((err = activate(fb_info, &var)))
230		return err;
231	return count;
232}
233
234static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
235			char *buf)
236{
237	struct fb_info *fb_info = dev_get_drvdata(device);
238	return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel);
239}
240
241static ssize_t store_rotate(struct device *device,
242			    struct device_attribute *attr,
243			    const char *buf, size_t count)
244{
245	struct fb_info *fb_info = dev_get_drvdata(device);
246	struct fb_var_screeninfo var;
247	char **last = NULL;
248	int err;
249
250	var = fb_info->var;
251	var.rotate = simple_strtoul(buf, last, 0);
252
253	if ((err = activate(fb_info, &var)))
254		return err;
255
256	return count;
257}
258
259
260static ssize_t show_rotate(struct device *device,
261			   struct device_attribute *attr, char *buf)
262{
263	struct fb_info *fb_info = dev_get_drvdata(device);
264
265	return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.rotate);
266}
267
268static ssize_t store_virtual(struct device *device,
269			     struct device_attribute *attr,
270			     const char *buf, size_t count)
271{
272	struct fb_info *fb_info = dev_get_drvdata(device);
273	struct fb_var_screeninfo var;
274	char *last = NULL;
275	int err;
276
277	var = fb_info->var;
278	var.xres_virtual = simple_strtoul(buf, &last, 0);
279	last++;
280	if (last - buf >= count)
281		return -EINVAL;
282	var.yres_virtual = simple_strtoul(last, &last, 0);
283
284	if ((err = activate(fb_info, &var)))
285		return err;
286	return count;
287}
288
289static ssize_t show_virtual(struct device *device,
290			    struct device_attribute *attr, char *buf)
291{
292	struct fb_info *fb_info = dev_get_drvdata(device);
293	return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xres_virtual,
294			fb_info->var.yres_virtual);
295}
296
297static ssize_t show_stride(struct device *device,
298			   struct device_attribute *attr, char *buf)
299{
300	struct fb_info *fb_info = dev_get_drvdata(device);
301	return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length);
302}
303
304static ssize_t store_blank(struct device *device,
305			   struct device_attribute *attr,
306			   const char *buf, size_t count)
307{
308	struct fb_info *fb_info = dev_get_drvdata(device);
309	char *last = NULL;
310	int err;
311
312	console_lock();
313	fb_info->flags |= FBINFO_MISC_USEREVENT;
314	err = fb_blank(fb_info, simple_strtoul(buf, &last, 0));
315	fb_info->flags &= ~FBINFO_MISC_USEREVENT;
316	console_unlock();
317	if (err < 0)
318		return err;
319	return count;
320}
321
322static ssize_t show_blank(struct device *device,
323			  struct device_attribute *attr, char *buf)
324{
325//	struct fb_info *fb_info = dev_get_drvdata(device);
326	return 0;
327}
328
329static ssize_t store_console(struct device *device,
330			     struct device_attribute *attr,
331			     const char *buf, size_t count)
332{
333//	struct fb_info *fb_info = dev_get_drvdata(device);
334	return 0;
335}
336
337static ssize_t show_console(struct device *device,
338			    struct device_attribute *attr, char *buf)
339{
340//	struct fb_info *fb_info = dev_get_drvdata(device);
341	return 0;
342}
343
344static ssize_t store_cursor(struct device *device,
345			    struct device_attribute *attr,
346			    const char *buf, size_t count)
347{
348//	struct fb_info *fb_info = dev_get_drvdata(device);
349	return 0;
350}
351
352static ssize_t show_cursor(struct device *device,
353			   struct device_attribute *attr, char *buf)
354{
355//	struct fb_info *fb_info = dev_get_drvdata(device);
356	return 0;
357}
358
359static ssize_t store_pan(struct device *device,
360			 struct device_attribute *attr,
361			 const char *buf, size_t count)
362{
363	struct fb_info *fb_info = dev_get_drvdata(device);
364	struct fb_var_screeninfo var;
365	char *last = NULL;
366	int err;
367
368	var = fb_info->var;
369	var.xoffset = simple_strtoul(buf, &last, 0);
370	last++;
371	if (last - buf >= count)
372		return -EINVAL;
373	var.yoffset = simple_strtoul(last, &last, 0);
374
375	console_lock();
376	err = fb_pan_display(fb_info, &var);
377	console_unlock();
378
379	if (err < 0)
380		return err;
381	return count;
382}
383
384static ssize_t show_pan(struct device *device,
385			struct device_attribute *attr, char *buf)
386{
387	struct fb_info *fb_info = dev_get_drvdata(device);
388	return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xoffset,
389			fb_info->var.yoffset);
390}
391
392static ssize_t show_name(struct device *device,
393			 struct device_attribute *attr, char *buf)
394{
395	struct fb_info *fb_info = dev_get_drvdata(device);
396
397	return snprintf(buf, PAGE_SIZE, "%s\n", fb_info->fix.id);
398}
399
400static ssize_t store_fbstate(struct device *device,
401			     struct device_attribute *attr,
402			     const char *buf, size_t count)
403{
404	struct fb_info *fb_info = dev_get_drvdata(device);
405	u32 state;
406	char *last = NULL;
407
408	state = simple_strtoul(buf, &last, 0);
409
410	console_lock();
411	if (!lock_fb_info(fb_info)) {
412		console_unlock();
413		return -ENODEV;
414	}
415
416	fb_set_suspend(fb_info, (int)state);
417
418	unlock_fb_info(fb_info);
419	console_unlock();
420
421	return count;
422}
423
424static ssize_t show_fbstate(struct device *device,
425			    struct device_attribute *attr, char *buf)
426{
427	struct fb_info *fb_info = dev_get_drvdata(device);
428	return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state);
429}
430
431#ifdef CONFIG_FB_BACKLIGHT
432static ssize_t store_bl_curve(struct device *device,
433			      struct device_attribute *attr,
434			      const char *buf, size_t count)
435{
436	struct fb_info *fb_info = dev_get_drvdata(device);
437	u8 tmp_curve[FB_BACKLIGHT_LEVELS];
438	unsigned int i;
439
440	/* Some drivers don't use framebuffer_alloc(), but those also
441	 * don't have backlights.
442	 */
443	if (!fb_info || !fb_info->bl_dev)
444		return -ENODEV;
445
446	if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
447		return -EINVAL;
448
449	for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
450		if (sscanf(&buf[i * 24],
451			"%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
452			&tmp_curve[i * 8 + 0],
453			&tmp_curve[i * 8 + 1],
454			&tmp_curve[i * 8 + 2],
455			&tmp_curve[i * 8 + 3],
456			&tmp_curve[i * 8 + 4],
457			&tmp_curve[i * 8 + 5],
458			&tmp_curve[i * 8 + 6],
459			&tmp_curve[i * 8 + 7]) != 8)
460			return -EINVAL;
461
462	/* If there has been an error in the input data, we won't
463	 * reach this loop.
464	 */
465	mutex_lock(&fb_info->bl_curve_mutex);
466	for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
467		fb_info->bl_curve[i] = tmp_curve[i];
468	mutex_unlock(&fb_info->bl_curve_mutex);
469
470	return count;
471}
472
473static ssize_t show_bl_curve(struct device *device,
474			     struct device_attribute *attr, char *buf)
475{
476	struct fb_info *fb_info = dev_get_drvdata(device);
477	ssize_t len = 0;
478	unsigned int i;
479
480	/* Some drivers don't use framebuffer_alloc(), but those also
481	 * don't have backlights.
482	 */
483	if (!fb_info || !fb_info->bl_dev)
484		return -ENODEV;
485
486	mutex_lock(&fb_info->bl_curve_mutex);
487	for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
488		len += snprintf(&buf[len], PAGE_SIZE, "%8ph\n",
489				fb_info->bl_curve + i);
490	mutex_unlock(&fb_info->bl_curve_mutex);
491
492	return len;
493}
494#endif
495
496/* When cmap is added back in it should be a binary attribute
497 * not a text one. Consideration should also be given to converting
498 * fbdev to use configfs instead of sysfs */
499static struct device_attribute device_attrs[] = {
500	__ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
501	__ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
502	__ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
503	__ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
504	__ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
505	__ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
506	__ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
507	__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
508	__ATTR(name, S_IRUGO, show_name, NULL),
509	__ATTR(stride, S_IRUGO, show_stride, NULL),
510	__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
511	__ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
512#ifdef CONFIG_FB_BACKLIGHT
513	__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
514#endif
515};
516
517int fb_init_device(struct fb_info *fb_info)
518{
519	int i, error = 0;
520
521	dev_set_drvdata(fb_info->dev, fb_info);
522
523	fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
524
525	for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
526		error = device_create_file(fb_info->dev, &device_attrs[i]);
527
528		if (error)
529			break;
530	}
531
532	if (error) {
533		while (--i >= 0)
534			device_remove_file(fb_info->dev, &device_attrs[i]);
535		fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
536	}
537
538	return 0;
539}
540
541void fb_cleanup_device(struct fb_info *fb_info)
542{
543	unsigned int i;
544
545	if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
546		for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
547			device_remove_file(fb_info->dev, &device_attrs[i]);
548
549		fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
550	}
551}
552
553#ifdef CONFIG_FB_BACKLIGHT
554/* This function generates a linear backlight curve
555 *
556 *     0: off
557 *   1-7: min
558 * 8-127: linear from min to max
559 */
560void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
561{
562	unsigned int i, flat, count, range = (max - min);
563
564	mutex_lock(&fb_info->bl_curve_mutex);
565
566	fb_info->bl_curve[0] = off;
567
568	for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
569		fb_info->bl_curve[flat] = min;
570
571	count = FB_BACKLIGHT_LEVELS * 15 / 16;
572	for (i = 0; i < count; ++i)
573		fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count);
574
575	mutex_unlock(&fb_info->bl_curve_mutex);
576}
577EXPORT_SYMBOL_GPL(fb_bl_default_curve);
578#endif
579