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 DEVICE_ATTR(bits_per_pixel, 0644, show_bpp, store_bpp); 420 static DEVICE_ATTR(blank, 0644, show_blank, store_blank); 421 static DEVICE_ATTR(console, 0644, show_console, store_console); 422 static DEVICE_ATTR(cursor, 0644, show_cursor, store_cursor); 423 static DEVICE_ATTR(mode, 0644, show_mode, store_mode); 424 static DEVICE_ATTR(modes, 0644, show_modes, store_modes); 425 static DEVICE_ATTR(pan, 0644, show_pan, store_pan); 426 static DEVICE_ATTR(virtual_size, 0644, show_virtual, store_virtual); 427 static DEVICE_ATTR(name, 0444, show_name, NULL); 428 static DEVICE_ATTR(stride, 0444, show_stride, NULL); 429 static DEVICE_ATTR(rotate, 0644, show_rotate, store_rotate); 430 static DEVICE_ATTR(state, 0644, show_fbstate, store_fbstate); 431 #if IS_ENABLED(CONFIG_FB_BACKLIGHT) 432 static DEVICE_ATTR(bl_curve, 0644, show_bl_curve, store_bl_curve); 433 #endif 434 435 static struct attribute *fb_device_attrs[] = { 436 &dev_attr_bits_per_pixel.attr, 437 &dev_attr_blank.attr, 438 &dev_attr_console.attr, 439 &dev_attr_cursor.attr, 440 &dev_attr_mode.attr, 441 &dev_attr_modes.attr, 442 &dev_attr_pan.attr, 443 &dev_attr_virtual_size.attr, 444 &dev_attr_name.attr, 445 &dev_attr_stride.attr, 446 &dev_attr_rotate.attr, 447 &dev_attr_state.attr, 448 #if IS_ENABLED(CONFIG_FB_BACKLIGHT) 449 &dev_attr_bl_curve.attr, 450 #endif 451 NULL, 452 }; 453 454 static const struct attribute_group fb_device_attr_group = { 455 .attrs = fb_device_attrs, 456 }; 457 458 static int fb_init_device(struct fb_info *fb_info) 459 { 460 int ret; 461 462 dev_set_drvdata(fb_info->dev, fb_info); 463 464 fb_info->class_flag |= FB_SYSFS_FLAG_ATTR; 465 466 ret = device_add_group(fb_info->dev, &fb_device_attr_group); 467 if (ret) 468 fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR; 469 470 return 0; 471 } 472 473 static void fb_cleanup_device(struct fb_info *fb_info) 474 { 475 if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) { 476 device_remove_group(fb_info->dev, &fb_device_attr_group); 477 478 fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR; 479 } 480 } 481 482 int fb_device_create(struct fb_info *fb_info) 483 { 484 int node = fb_info->node; 485 dev_t devt = MKDEV(FB_MAJOR, node); 486 int ret; 487 488 fb_info->dev = device_create(fb_class, fb_info->device, devt, NULL, "fb%d", node); 489 if (IS_ERR(fb_info->dev)) { 490 /* Not fatal */ 491 ret = PTR_ERR(fb_info->dev); 492 pr_warn("Unable to create device for framebuffer %d; error %d\n", node, ret); 493 fb_info->dev = NULL; 494 } else { 495 fb_init_device(fb_info); 496 } 497 498 return 0; 499 } 500 501 void fb_device_destroy(struct fb_info *fb_info) 502 { 503 dev_t devt = MKDEV(FB_MAJOR, fb_info->node); 504 505 if (!fb_info->dev) 506 return; 507 508 fb_cleanup_device(fb_info); 509 device_destroy(fb_class, devt); 510 fb_info->dev = NULL; 511 } 512