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