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