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