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