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