xref: /linux/drivers/video/fbdev/core/fbsysfs.c (revision f4cdf7ca9a1fdcca413157df19753f388a5a224e)
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 #include <linux/console.h>
9 #include <linux/fb.h>
10 #include <linux/major.h>
11 
12 #include "fb_internal.h"
13 #include "fbcon.h"
14 
15 static int activate_locked(struct fb_info *fb_info,
16 			    struct fb_var_screeninfo *var)
17 {
18 	var->activate |= FB_ACTIVATE_FORCE;
19 	return fb_set_var_from_user(fb_info, var);
20 }
21 
22 static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
23 {
24 	int err;
25 
26 	console_lock();
27 	lock_fb_info(fb_info);
28 	err = activate_locked(fb_info, var);
29 	unlock_fb_info(fb_info);
30 	console_unlock();
31 
32 	return err;
33 }
34 
35 static int mode_string(char *buf, size_t size, unsigned int offset,
36 		       const struct fb_videomode *mode)
37 {
38 	char m = 'U';
39 	char v = 'p';
40 
41 	if (offset >= size)
42 		return 0;
43 
44 	if (mode->flag & FB_MODE_IS_DETAILED)
45 		m = 'D';
46 	if (mode->flag & FB_MODE_IS_VESA)
47 		m = 'V';
48 	if (mode->flag & FB_MODE_IS_STANDARD)
49 		m = 'S';
50 
51 	if (mode->vmode & FB_VMODE_INTERLACED)
52 		v = 'i';
53 	if (mode->vmode & FB_VMODE_DOUBLE)
54 		v = 'd';
55 
56 	return scnprintf(&buf[offset], size - offset, "%c:%dx%d%c-%d\n",
57 	                m, mode->xres, mode->yres, v, mode->refresh);
58 }
59 
60 static ssize_t store_mode(struct device *device, struct device_attribute *attr,
61 			  const char *buf, size_t count)
62 {
63 	struct fb_info *fb_info = dev_get_drvdata(device);
64 	char mstr[100];
65 	struct fb_var_screeninfo var;
66 	struct fb_modelist *modelist;
67 	struct fb_videomode *mode;
68 	size_t i;
69 	int err;
70 
71 	memset(&var, 0, sizeof(var));
72 
73 	console_lock();
74 	lock_fb_info(fb_info);
75 
76 	list_for_each_entry(modelist, &fb_info->modelist, list) {
77 		mode = &modelist->mode;
78 		i = mode_string(mstr, sizeof(mstr), 0, mode);
79 		if (strncmp(mstr, buf, max(count, i)) == 0) {
80 
81 			var = fb_info->var;
82 			fb_videomode_to_var(&var, mode);
83 			err = activate_locked(fb_info, &var);
84 			if (err) {
85 				unlock_fb_info(fb_info);
86 				console_unlock();
87 				return err;
88 			}
89 			fb_info->mode = mode;
90 			unlock_fb_info(fb_info);
91 			console_unlock();
92 			return count;
93 		}
94 	}
95 
96 	unlock_fb_info(fb_info);
97 	console_unlock();
98 
99 	return -EINVAL;
100 }
101 
102 static ssize_t show_mode(struct device *device, struct device_attribute *attr,
103 			 char *buf)
104 {
105 	struct fb_info *fb_info = dev_get_drvdata(device);
106 	struct fb_videomode mode;
107 	bool have_mode = false;
108 
109 	lock_fb_info(fb_info);
110 	if (fb_info->mode) {
111 		mode = *fb_info->mode;
112 		have_mode = true;
113 	}
114 	unlock_fb_info(fb_info);
115 
116 	if (!have_mode)
117 		return 0;
118 
119 	return mode_string(buf, PAGE_SIZE, 0, &mode);
120 }
121 
122 static ssize_t store_modes(struct device *device,
123 			   struct device_attribute *attr,
124 			   const char *buf, size_t count)
125 {
126 	struct fb_info *fb_info = dev_get_drvdata(device);
127 	LIST_HEAD(old_list);
128 	int i = count / sizeof(struct fb_videomode);
129 
130 	if (i * sizeof(struct fb_videomode) != count)
131 		return -EINVAL;
132 
133 	console_lock();
134 	lock_fb_info(fb_info);
135 
136 	list_splice(&fb_info->modelist, &old_list);
137 	fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
138 				 &fb_info->modelist);
139 	if (fb_new_modelist(fb_info)) {
140 		fb_destroy_modelist(&fb_info->modelist);
141 		list_splice(&old_list, &fb_info->modelist);
142 	} else {
143 		/*
144 		 * fb_display[i].mode and fb_info->mode both point into the old
145 		 * list. Clear them before it is freed.
146 		 */
147 		fbcon_delete_modelist(&old_list);
148 		fb_info->mode = NULL;
149 		fb_destroy_modelist(&old_list);
150 	}
151 
152 	unlock_fb_info(fb_info);
153 	console_unlock();
154 
155 	return 0;
156 }
157 
158 static ssize_t show_modes(struct device *device, struct device_attribute *attr,
159 			  char *buf)
160 {
161 	struct fb_info *fb_info = dev_get_drvdata(device);
162 	unsigned int i;
163 	struct fb_modelist *modelist;
164 	const struct fb_videomode *mode;
165 
166 	i = 0;
167 	lock_fb_info(fb_info);
168 	list_for_each_entry(modelist, &fb_info->modelist, list) {
169 		mode = &modelist->mode;
170 		i += mode_string(buf, PAGE_SIZE, i, mode);
171 		if (i >= PAGE_SIZE - 1)
172 			break;
173 	}
174 	unlock_fb_info(fb_info);
175 
176 	return i;
177 }
178 
179 static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
180 			 const char *buf, size_t count)
181 {
182 	struct fb_info *fb_info = dev_get_drvdata(device);
183 	struct fb_var_screeninfo var;
184 	char ** last = NULL;
185 	int err;
186 
187 	var = fb_info->var;
188 	var.bits_per_pixel = simple_strtoul(buf, last, 0);
189 	if ((err = activate(fb_info, &var)))
190 		return err;
191 	return count;
192 }
193 
194 static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
195 			char *buf)
196 {
197 	struct fb_info *fb_info = dev_get_drvdata(device);
198 	return sysfs_emit(buf, "%d\n", fb_info->var.bits_per_pixel);
199 }
200 
201 static ssize_t store_rotate(struct device *device,
202 			    struct device_attribute *attr,
203 			    const char *buf, size_t count)
204 {
205 	struct fb_info *fb_info = dev_get_drvdata(device);
206 	struct fb_var_screeninfo var;
207 	char **last = NULL;
208 	int err;
209 
210 	var = fb_info->var;
211 	var.rotate = simple_strtoul(buf, last, 0);
212 
213 	if ((err = activate(fb_info, &var)))
214 		return err;
215 
216 	return count;
217 }
218 
219 
220 static ssize_t show_rotate(struct device *device,
221 			   struct device_attribute *attr, char *buf)
222 {
223 	struct fb_info *fb_info = dev_get_drvdata(device);
224 
225 	return sysfs_emit(buf, "%d\n", fb_info->var.rotate);
226 }
227 
228 static ssize_t store_virtual(struct device *device,
229 			     struct device_attribute *attr,
230 			     const char *buf, size_t count)
231 {
232 	struct fb_info *fb_info = dev_get_drvdata(device);
233 	struct fb_var_screeninfo var;
234 	char *last = NULL;
235 	int err;
236 
237 	var = fb_info->var;
238 	var.xres_virtual = simple_strtoul(buf, &last, 0);
239 	last++;
240 	if (last - buf >= count)
241 		return -EINVAL;
242 	var.yres_virtual = simple_strtoul(last, &last, 0);
243 
244 	if ((err = activate(fb_info, &var)))
245 		return err;
246 	return count;
247 }
248 
249 static ssize_t show_virtual(struct device *device,
250 			    struct device_attribute *attr, char *buf)
251 {
252 	struct fb_info *fb_info = dev_get_drvdata(device);
253 	return sysfs_emit(buf, "%d,%d\n", fb_info->var.xres_virtual,
254 			fb_info->var.yres_virtual);
255 }
256 
257 static ssize_t show_stride(struct device *device,
258 			   struct device_attribute *attr, char *buf)
259 {
260 	struct fb_info *fb_info = dev_get_drvdata(device);
261 	return sysfs_emit(buf, "%d\n", fb_info->fix.line_length);
262 }
263 
264 static ssize_t store_blank(struct device *device,
265 			   struct device_attribute *attr,
266 			   const char *buf, size_t count)
267 {
268 	struct fb_info *fb_info = dev_get_drvdata(device);
269 	char *last = NULL;
270 	int err, arg;
271 
272 	arg = simple_strtoul(buf, &last, 0);
273 	console_lock();
274 	err = fb_blank_from_user(fb_info, arg);
275 	console_unlock();
276 	if (err < 0)
277 		return err;
278 	return count;
279 }
280 
281 static ssize_t show_blank(struct device *device, struct device_attribute *attr, char *buf)
282 {
283 	struct fb_info *fb_info = dev_get_drvdata(device);
284 
285 	return sysfs_emit(buf, "%d\n", fb_info->blank);
286 }
287 
288 static ssize_t store_console(struct device *device,
289 			     struct device_attribute *attr,
290 			     const char *buf, size_t count)
291 {
292 //	struct fb_info *fb_info = dev_get_drvdata(device);
293 	return 0;
294 }
295 
296 static ssize_t show_console(struct device *device,
297 			    struct device_attribute *attr, char *buf)
298 {
299 //	struct fb_info *fb_info = dev_get_drvdata(device);
300 	return 0;
301 }
302 
303 static ssize_t store_cursor(struct device *device,
304 			    struct device_attribute *attr,
305 			    const char *buf, size_t count)
306 {
307 //	struct fb_info *fb_info = dev_get_drvdata(device);
308 	return 0;
309 }
310 
311 static ssize_t show_cursor(struct device *device,
312 			   struct device_attribute *attr, char *buf)
313 {
314 //	struct fb_info *fb_info = dev_get_drvdata(device);
315 	return 0;
316 }
317 
318 static ssize_t store_pan(struct device *device,
319 			 struct device_attribute *attr,
320 			 const char *buf, size_t count)
321 {
322 	struct fb_info *fb_info = dev_get_drvdata(device);
323 	struct fb_var_screeninfo var;
324 	char *last = NULL;
325 	int err;
326 
327 	var = fb_info->var;
328 	var.xoffset = simple_strtoul(buf, &last, 0);
329 	last++;
330 	if (last - buf >= count)
331 		return -EINVAL;
332 	var.yoffset = simple_strtoul(last, &last, 0);
333 
334 	console_lock();
335 	err = fb_pan_display(fb_info, &var);
336 	console_unlock();
337 
338 	if (err < 0)
339 		return err;
340 	return count;
341 }
342 
343 static ssize_t show_pan(struct device *device,
344 			struct device_attribute *attr, char *buf)
345 {
346 	struct fb_info *fb_info = dev_get_drvdata(device);
347 	return sysfs_emit(buf, "%d,%d\n", fb_info->var.xoffset,
348 			fb_info->var.yoffset);
349 }
350 
351 static ssize_t show_name(struct device *device,
352 			 struct device_attribute *attr, char *buf)
353 {
354 	struct fb_info *fb_info = dev_get_drvdata(device);
355 
356 	return sysfs_emit(buf, "%s\n", fb_info->fix.id);
357 }
358 
359 static ssize_t store_fbstate(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 	u32 state;
365 	char *last = NULL;
366 
367 	state = simple_strtoul(buf, &last, 0);
368 
369 	console_lock();
370 	lock_fb_info(fb_info);
371 
372 	fb_set_suspend(fb_info, (int)state);
373 
374 	unlock_fb_info(fb_info);
375 	console_unlock();
376 
377 	return count;
378 }
379 
380 static ssize_t show_fbstate(struct device *device,
381 			    struct device_attribute *attr, char *buf)
382 {
383 	struct fb_info *fb_info = dev_get_drvdata(device);
384 	return sysfs_emit(buf, "%d\n", fb_info->state);
385 }
386 
387 #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
388 static ssize_t store_bl_curve(struct device *device,
389 			      struct device_attribute *attr,
390 			      const char *buf, size_t count)
391 {
392 	struct fb_info *fb_info = dev_get_drvdata(device);
393 	u8 tmp_curve[FB_BACKLIGHT_LEVELS];
394 	unsigned int i;
395 
396 	/* Some drivers don't use framebuffer_alloc(), but those also
397 	 * don't have backlights.
398 	 */
399 	if (!fb_info || !fb_info->bl_dev)
400 		return -ENODEV;
401 
402 	if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
403 		return -EINVAL;
404 
405 	for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
406 		if (sscanf(&buf[i * 24],
407 			"%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
408 			&tmp_curve[i * 8 + 0],
409 			&tmp_curve[i * 8 + 1],
410 			&tmp_curve[i * 8 + 2],
411 			&tmp_curve[i * 8 + 3],
412 			&tmp_curve[i * 8 + 4],
413 			&tmp_curve[i * 8 + 5],
414 			&tmp_curve[i * 8 + 6],
415 			&tmp_curve[i * 8 + 7]) != 8)
416 			return -EINVAL;
417 
418 	/* If there has been an error in the input data, we won't
419 	 * reach this loop.
420 	 */
421 	mutex_lock(&fb_info->bl_curve_mutex);
422 	for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
423 		fb_info->bl_curve[i] = tmp_curve[i];
424 	mutex_unlock(&fb_info->bl_curve_mutex);
425 
426 	return count;
427 }
428 
429 static ssize_t show_bl_curve(struct device *device,
430 			     struct device_attribute *attr, char *buf)
431 {
432 	struct fb_info *fb_info = dev_get_drvdata(device);
433 	ssize_t len = 0;
434 	unsigned int i;
435 
436 	/* Some drivers don't use framebuffer_alloc(), but those also
437 	 * don't have backlights.
438 	 */
439 	if (!fb_info || !fb_info->bl_dev)
440 		return -ENODEV;
441 
442 	mutex_lock(&fb_info->bl_curve_mutex);
443 	for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
444 		len += scnprintf(&buf[len], PAGE_SIZE - len, "%8ph\n",
445 				fb_info->bl_curve + i);
446 	mutex_unlock(&fb_info->bl_curve_mutex);
447 
448 	return len;
449 }
450 #endif
451 
452 /* When cmap is added back in it should be a binary attribute
453  * not a text one. Consideration should also be given to converting
454  * fbdev to use configfs instead of sysfs */
455 static DEVICE_ATTR(bits_per_pixel, 0644, show_bpp, store_bpp);
456 static DEVICE_ATTR(blank, 0644, show_blank, store_blank);
457 static DEVICE_ATTR(console, 0644, show_console, store_console);
458 static DEVICE_ATTR(cursor, 0644, show_cursor, store_cursor);
459 static DEVICE_ATTR(mode, 0644, show_mode, store_mode);
460 static DEVICE_ATTR(modes, 0644, show_modes, store_modes);
461 static DEVICE_ATTR(pan, 0644, show_pan, store_pan);
462 static DEVICE_ATTR(virtual_size, 0644, show_virtual, store_virtual);
463 static DEVICE_ATTR(name, 0444, show_name, NULL);
464 static DEVICE_ATTR(stride, 0444, show_stride, NULL);
465 static DEVICE_ATTR(rotate, 0644, show_rotate, store_rotate);
466 static DEVICE_ATTR(state, 0644, show_fbstate, store_fbstate);
467 #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
468 static DEVICE_ATTR(bl_curve, 0644, show_bl_curve, store_bl_curve);
469 #endif
470 
471 static struct attribute *fb_device_attrs[] = {
472 	&dev_attr_bits_per_pixel.attr,
473 	&dev_attr_blank.attr,
474 	&dev_attr_console.attr,
475 	&dev_attr_cursor.attr,
476 	&dev_attr_mode.attr,
477 	&dev_attr_modes.attr,
478 	&dev_attr_pan.attr,
479 	&dev_attr_virtual_size.attr,
480 	&dev_attr_name.attr,
481 	&dev_attr_stride.attr,
482 	&dev_attr_rotate.attr,
483 	&dev_attr_state.attr,
484 #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
485 	&dev_attr_bl_curve.attr,
486 #endif
487 	NULL,
488 };
489 
490 ATTRIBUTE_GROUPS(fb_device);
491 
492 int fb_device_create(struct fb_info *fb_info)
493 {
494 	int node = fb_info->node;
495 	dev_t devt = MKDEV(FB_MAJOR, node);
496 	int ret;
497 
498 	fb_info->dev = device_create_with_groups(fb_class, fb_info->device, devt, fb_info,
499 						 fb_device_groups, "fb%d", node);
500 	if (IS_ERR(fb_info->dev)) {
501 		/* Not fatal */
502 		ret = PTR_ERR(fb_info->dev);
503 		pr_warn("Unable to create device for framebuffer %d; error %d\n", node, ret);
504 		fb_info->dev = NULL;
505 	}
506 
507 	return 0;
508 }
509 
510 void fb_device_destroy(struct fb_info *fb_info)
511 {
512 	dev_t devt = MKDEV(FB_MAJOR, fb_info->node);
513 
514 	if (!fb_info->dev)
515 		return;
516 
517 	device_destroy(fb_class, devt);
518 	fb_info->dev = NULL;
519 }
520