xref: /linux/drivers/video/fbdev/core/fbsysfs.c (revision b85d45947951d23cb22d90caecf4c1eb81342c96)
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  */
39 struct 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 }
70 EXPORT_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  */
81 void framebuffer_release(struct fb_info *info)
82 {
83 	if (!info)
84 		return;
85 	kfree(info->apertures);
86 	kfree(info);
87 }
88 EXPORT_SYMBOL(framebuffer_release);
89 
90 static 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 
105 static 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 
127 static 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 
158 static 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 
169 static 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 
201 static 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 
219 static 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 
234 static 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 
241 static 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 
260 static 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 
268 static 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 
289 static 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 
297 static 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 
304 static 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 
322 static 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 
329 static 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 
337 static 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 
344 static 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 
352 static 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 
359 static 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 
384 static 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 
392 static 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 
400 static 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 
424 static 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
432 static 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 
473 static 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 += scnprintf(&buf[len], PAGE_SIZE - len, "%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 */
499 static 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 
517 int 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 
541 void 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  */
560 void 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 }
577 EXPORT_SYMBOL_GPL(fb_bl_default_curve);
578 #endif
579