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