projects/linux·blob master · refs

519 lines · 12.4 KiB

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 <[email protected]>
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
15static 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
22static 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
35static 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
60static 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
102static 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
122static 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
158static 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
179static 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
194static 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
201static 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
220static 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
228static 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
249static 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
257static 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
264static 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
281static 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
288static 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
296static 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
303static 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
311static 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
318static 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
343static 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
351static 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
359static 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
380static 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)
388static 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
429static 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 */
455static DEVICE_ATTR(bits_per_pixel, 0644, show_bpp, store_bpp);
456static DEVICE_ATTR(blank, 0644, show_blank, store_blank);
457static DEVICE_ATTR(console, 0644, show_console, store_console);
458static DEVICE_ATTR(cursor, 0644, show_cursor, store_cursor);
459static DEVICE_ATTR(mode, 0644, show_mode, store_mode);
460static DEVICE_ATTR(modes, 0644, show_modes, store_modes);
461static DEVICE_ATTR(pan, 0644, show_pan, store_pan);
462static DEVICE_ATTR(virtual_size, 0644, show_virtual, store_virtual);
463static DEVICE_ATTR(name, 0444, show_name, NULL);
464static DEVICE_ATTR(stride, 0444, show_stride, NULL);
465static DEVICE_ATTR(rotate, 0644, show_rotate, store_rotate);
466static DEVICE_ATTR(state, 0644, show_fbstate, store_fbstate);
467#if IS_ENABLED(CONFIG_FB_BACKLIGHT)
468static DEVICE_ATTR(bl_curve, 0644, show_bl_curve, store_bl_curve);
469#endif
470
471static 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
490ATTRIBUTE_GROUPS(fb_device);
491
492int 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
510void 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}