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 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 85 static 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 96 static 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 125 static 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 141 static 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 156 static 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 163 static 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 182 static 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 190 static 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 211 static 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 219 static 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 226 static 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 245 static 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 252 static 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 260 static 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 267 static 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 275 static 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 282 static 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 307 static 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 315 static 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 323 static 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 344 static 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) 352 static 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 393 static 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 */ 419 static 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 437 static 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 461 static 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 473 int 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 492 void 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