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
activate_locked(struct fb_info * fb_info,struct fb_var_screeninfo * var)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
activate(struct fb_info * fb_info,struct fb_var_screeninfo * var)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
mode_string(char * buf,size_t size,unsigned int offset,const struct fb_videomode * mode)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
store_mode(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_mode(struct device * device,struct device_attribute * attr,char * buf)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
store_modes(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_modes(struct device * device,struct device_attribute * attr,char * buf)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
store_bpp(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_bpp(struct device * device,struct device_attribute * attr,char * buf)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
store_rotate(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_rotate(struct device * device,struct device_attribute * attr,char * buf)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
store_virtual(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_virtual(struct device * device,struct device_attribute * attr,char * buf)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
show_stride(struct device * device,struct device_attribute * attr,char * buf)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
store_blank(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_blank(struct device * device,struct device_attribute * attr,char * buf)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
store_console(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_console(struct device * device,struct device_attribute * attr,char * buf)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
store_cursor(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_cursor(struct device * device,struct device_attribute * attr,char * buf)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
store_pan(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_pan(struct device * device,struct device_attribute * attr,char * buf)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
show_name(struct device * device,struct device_attribute * attr,char * buf)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
store_fbstate(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_fbstate(struct device * device,struct device_attribute * attr,char * buf)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)
store_bl_curve(struct device * device,struct device_attribute * attr,const char * buf,size_t count)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
show_bl_curve(struct device * device,struct device_attribute * attr,char * buf)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
fb_device_create(struct fb_info * fb_info)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
fb_device_destroy(struct fb_info * fb_info)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