linux/drivers/video/fbdev/core/fbsysfs.c
<<
>>
Prefs
   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/*
   9 * Note:  currently there's only stubs for framebuffer_alloc and
  10 * framebuffer_release here.  The reson for that is that until all drivers
  11 * are converted to use it a sysfsification will open OOPSable races.
  12 */
  13
  14#include <linux/kernel.h>
  15#include <linux/slab.h>
  16#include <linux/fb.h>
  17#include <linux/fbcon.h>
  18#include <linux/console.h>
  19#include <linux/module.h>
  20
  21#define FB_SYSFS_FLAG_ATTR 1
  22
  23/**
  24 * framebuffer_alloc - creates a new frame buffer info structure
  25 *
  26 * @size: size of driver private data, can be zero
  27 * @dev: pointer to the device for this fb, this can be NULL
  28 *
  29 * Creates a new frame buffer info structure. Also reserves @size bytes
  30 * for driver private data (info->par). info->par (if any) will be
  31 * aligned to sizeof(long).
  32 *
  33 * Returns the new structure, or NULL if an error occurred.
  34 *
  35 */
  36struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
  37{
  38#define BYTES_PER_LONG (BITS_PER_LONG/8)
  39#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG))
  40        int fb_info_size = sizeof(struct fb_info);
  41        struct fb_info *info;
  42        char *p;
  43
  44        if (size)
  45                fb_info_size += PADDING;
  46
  47        p = kzalloc(fb_info_size + size, GFP_KERNEL);
  48
  49        if (!p)
  50                return NULL;
  51
  52        info = (struct fb_info *) p;
  53
  54        if (size)
  55                info->par = p + fb_info_size;
  56
  57        info->device = dev;
  58        info->fbcon_rotate_hint = -1;
  59
  60#if IS_ENABLED(CONFIG_FB_BACKLIGHT)
  61        mutex_init(&info->bl_curve_mutex);
  62#endif
  63
  64        return info;
  65#undef PADDING
  66#undef BYTES_PER_LONG
  67}
  68EXPORT_SYMBOL(framebuffer_alloc);
  69
  70/**
  71 * framebuffer_release - marks the structure available for freeing
  72 *
  73 * @info: frame buffer info structure
  74 *
  75 * Drop the reference count of the device embedded in the
  76 * framebuffer info structure.
  77 *
  78 */
  79void framebuffer_release(struct fb_info *info)
  80{
  81        if (!info)
  82                return;
  83        kfree(info->apertures);
  84        kfree(info);
  85}
  86EXPORT_SYMBOL(framebuffer_release);
  87
  88static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
  89{
  90        int err;
  91
  92        var->activate |= FB_ACTIVATE_FORCE;
  93        console_lock();
  94        err = fb_set_var(fb_info, var);
  95        if (!err)
  96                fbcon_update_vcs(fb_info, var->activate & FB_ACTIVATE_ALL);
  97        console_unlock();
  98        if (err)
  99                return err;
 100        return 0;
 101}
 102
 103static int mode_string(char *buf, unsigned int offset,
 104                       const struct fb_videomode *mode)
 105{
 106        char m = 'U';
 107        char v = 'p';
 108
 109        if (mode->flag & FB_MODE_IS_DETAILED)
 110                m = 'D';
 111        if (mode->flag & FB_MODE_IS_VESA)
 112                m = 'V';
 113        if (mode->flag & FB_MODE_IS_STANDARD)
 114                m = 'S';
 115
 116        if (mode->vmode & FB_VMODE_INTERLACED)
 117                v = 'i';
 118        if (mode->vmode & FB_VMODE_DOUBLE)
 119                v = 'd';
 120
 121        return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
 122                        m, mode->xres, mode->yres, v, mode->refresh);
 123}
 124
 125static ssize_t store_mode(struct device *device, struct device_attribute *attr,
 126                          const char *buf, size_t count)
 127{
 128        struct fb_info *fb_info = dev_get_drvdata(device);
 129        char mstr[100];
 130        struct fb_var_screeninfo var;
 131        struct fb_modelist *modelist;
 132        struct fb_videomode *mode;
 133        struct list_head *pos;
 134        size_t i;
 135        int err;
 136
 137        memset(&var, 0, sizeof(var));
 138
 139        list_for_each(pos, &fb_info->modelist) {
 140                modelist = list_entry(pos, struct fb_modelist, list);
 141                mode = &modelist->mode;
 142                i = mode_string(mstr, 0, mode);
 143                if (strncmp(mstr, buf, max(count, i)) == 0) {
 144
 145                        var = fb_info->var;
 146                        fb_videomode_to_var(&var, mode);
 147                        if ((err = activate(fb_info, &var)))
 148                                return err;
 149                        fb_info->mode = mode;
 150                        return count;
 151                }
 152        }
 153        return -EINVAL;
 154}
 155
 156static ssize_t show_mode(struct device *device, struct device_attribute *attr,
 157                         char *buf)
 158{
 159        struct fb_info *fb_info = dev_get_drvdata(device);
 160
 161        if (!fb_info->mode)
 162                return 0;
 163
 164        return mode_string(buf, 0, fb_info->mode);
 165}
 166
 167static ssize_t store_modes(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        LIST_HEAD(old_list);
 173        int i = count / sizeof(struct fb_videomode);
 174
 175        if (i * sizeof(struct fb_videomode) != count)
 176                return -EINVAL;
 177
 178        console_lock();
 179        lock_fb_info(fb_info);
 180
 181        list_splice(&fb_info->modelist, &old_list);
 182        fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
 183                                 &fb_info->modelist);
 184        if (fb_new_modelist(fb_info)) {
 185                fb_destroy_modelist(&fb_info->modelist);
 186                list_splice(&old_list, &fb_info->modelist);
 187        } else
 188                fb_destroy_modelist(&old_list);
 189
 190        unlock_fb_info(fb_info);
 191        console_unlock();
 192
 193        return 0;
 194}
 195
 196static ssize_t show_modes(struct device *device, struct device_attribute *attr,
 197                          char *buf)
 198{
 199        struct fb_info *fb_info = dev_get_drvdata(device);
 200        unsigned int i;
 201        struct list_head *pos;
 202        struct fb_modelist *modelist;
 203        const struct fb_videomode *mode;
 204
 205        i = 0;
 206        list_for_each(pos, &fb_info->modelist) {
 207                modelist = list_entry(pos, struct fb_modelist, list);
 208                mode = &modelist->mode;
 209                i += mode_string(buf, i, mode);
 210        }
 211        return i;
 212}
 213
 214static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
 215                         const char *buf, size_t count)
 216{
 217        struct fb_info *fb_info = dev_get_drvdata(device);
 218        struct fb_var_screeninfo var;
 219        char ** last = NULL;
 220        int err;
 221
 222        var = fb_info->var;
 223        var.bits_per_pixel = simple_strtoul(buf, last, 0);
 224        if ((err = activate(fb_info, &var)))
 225                return err;
 226        return count;
 227}
 228
 229static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
 230                        char *buf)
 231{
 232        struct fb_info *fb_info = dev_get_drvdata(device);
 233        return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel);
 234}
 235
 236static ssize_t store_rotate(struct device *device,
 237                            struct device_attribute *attr,
 238                            const char *buf, size_t count)
 239{
 240        struct fb_info *fb_info = dev_get_drvdata(device);
 241        struct fb_var_screeninfo var;
 242        char **last = NULL;
 243        int err;
 244
 245        var = fb_info->var;
 246        var.rotate = simple_strtoul(buf, last, 0);
 247
 248        if ((err = activate(fb_info, &var)))
 249                return err;
 250
 251        return count;
 252}
 253
 254
 255static ssize_t show_rotate(struct device *device,
 256                           struct device_attribute *attr, char *buf)
 257{
 258        struct fb_info *fb_info = dev_get_drvdata(device);
 259
 260        return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.rotate);
 261}
 262
 263static ssize_t store_virtual(struct device *device,
 264                             struct device_attribute *attr,
 265                             const char *buf, size_t count)
 266{
 267        struct fb_info *fb_info = dev_get_drvdata(device);
 268        struct fb_var_screeninfo var;
 269        char *last = NULL;
 270        int err;
 271
 272        var = fb_info->var;
 273        var.xres_virtual = simple_strtoul(buf, &last, 0);
 274        last++;
 275        if (last - buf >= count)
 276                return -EINVAL;
 277        var.yres_virtual = simple_strtoul(last, &last, 0);
 278
 279        if ((err = activate(fb_info, &var)))
 280                return err;
 281        return count;
 282}
 283
 284static ssize_t show_virtual(struct device *device,
 285                            struct device_attribute *attr, char *buf)
 286{
 287        struct fb_info *fb_info = dev_get_drvdata(device);
 288        return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xres_virtual,
 289                        fb_info->var.yres_virtual);
 290}
 291
 292static ssize_t show_stride(struct device *device,
 293                           struct device_attribute *attr, char *buf)
 294{
 295        struct fb_info *fb_info = dev_get_drvdata(device);
 296        return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length);
 297}
 298
 299static ssize_t store_blank(struct device *device,
 300                           struct device_attribute *attr,
 301                           const char *buf, size_t count)
 302{
 303        struct fb_info *fb_info = dev_get_drvdata(device);
 304        char *last = NULL;
 305        int err, arg;
 306
 307        arg = simple_strtoul(buf, &last, 0);
 308        console_lock();
 309        err = fb_blank(fb_info, arg);
 310        /* might again call into fb_blank */
 311        fbcon_fb_blanked(fb_info, arg);
 312        console_unlock();
 313        if (err < 0)
 314                return err;
 315        return count;
 316}
 317
 318static ssize_t show_blank(struct device *device,
 319                          struct device_attribute *attr, char *buf)
 320{
 321//      struct fb_info *fb_info = dev_get_drvdata(device);
 322        return 0;
 323}
 324
 325static ssize_t store_console(struct device *device,
 326                             struct device_attribute *attr,
 327                             const char *buf, size_t count)
 328{
 329//      struct fb_info *fb_info = dev_get_drvdata(device);
 330        return 0;
 331}
 332
 333static ssize_t show_console(struct device *device,
 334                            struct device_attribute *attr, char *buf)
 335{
 336//      struct fb_info *fb_info = dev_get_drvdata(device);
 337        return 0;
 338}
 339
 340static ssize_t store_cursor(struct device *device,
 341                            struct device_attribute *attr,
 342                            const char *buf, size_t count)
 343{
 344//      struct fb_info *fb_info = dev_get_drvdata(device);
 345        return 0;
 346}
 347
 348static ssize_t show_cursor(struct device *device,
 349                           struct device_attribute *attr, char *buf)
 350{
 351//      struct fb_info *fb_info = dev_get_drvdata(device);
 352        return 0;
 353}
 354
 355static ssize_t store_pan(struct device *device,
 356                         struct device_attribute *attr,
 357                         const char *buf, size_t count)
 358{
 359        struct fb_info *fb_info = dev_get_drvdata(device);
 360        struct fb_var_screeninfo var;
 361        char *last = NULL;
 362        int err;
 363
 364        var = fb_info->var;
 365        var.xoffset = simple_strtoul(buf, &last, 0);
 366        last++;
 367        if (last - buf >= count)
 368                return -EINVAL;
 369        var.yoffset = simple_strtoul(last, &last, 0);
 370
 371        console_lock();
 372        err = fb_pan_display(fb_info, &var);
 373        console_unlock();
 374
 375        if (err < 0)
 376                return err;
 377        return count;
 378}
 379
 380static ssize_t show_pan(struct device *device,
 381                        struct device_attribute *attr, char *buf)
 382{
 383        struct fb_info *fb_info = dev_get_drvdata(device);
 384        return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xoffset,
 385                        fb_info->var.yoffset);
 386}
 387
 388static ssize_t show_name(struct device *device,
 389                         struct device_attribute *attr, char *buf)
 390{
 391        struct fb_info *fb_info = dev_get_drvdata(device);
 392
 393        return snprintf(buf, PAGE_SIZE, "%s\n", fb_info->fix.id);
 394}
 395
 396static ssize_t store_fbstate(struct device *device,
 397                             struct device_attribute *attr,
 398                             const char *buf, size_t count)
 399{
 400        struct fb_info *fb_info = dev_get_drvdata(device);
 401        u32 state;
 402        char *last = NULL;
 403
 404        state = simple_strtoul(buf, &last, 0);
 405
 406        console_lock();
 407        lock_fb_info(fb_info);
 408
 409        fb_set_suspend(fb_info, (int)state);
 410
 411        unlock_fb_info(fb_info);
 412        console_unlock();
 413
 414        return count;
 415}
 416
 417static ssize_t show_fbstate(struct device *device,
 418                            struct device_attribute *attr, char *buf)
 419{
 420        struct fb_info *fb_info = dev_get_drvdata(device);
 421        return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state);
 422}
 423
 424#if IS_ENABLED(CONFIG_FB_BACKLIGHT)
 425static ssize_t store_bl_curve(struct device *device,
 426                              struct device_attribute *attr,
 427                              const char *buf, size_t count)
 428{
 429        struct fb_info *fb_info = dev_get_drvdata(device);
 430        u8 tmp_curve[FB_BACKLIGHT_LEVELS];
 431        unsigned int i;
 432
 433        /* Some drivers don't use framebuffer_alloc(), but those also
 434         * don't have backlights.
 435         */
 436        if (!fb_info || !fb_info->bl_dev)
 437                return -ENODEV;
 438
 439        if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
 440                return -EINVAL;
 441
 442        for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
 443                if (sscanf(&buf[i * 24],
 444                        "%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
 445                        &tmp_curve[i * 8 + 0],
 446                        &tmp_curve[i * 8 + 1],
 447                        &tmp_curve[i * 8 + 2],
 448                        &tmp_curve[i * 8 + 3],
 449                        &tmp_curve[i * 8 + 4],
 450                        &tmp_curve[i * 8 + 5],
 451                        &tmp_curve[i * 8 + 6],
 452                        &tmp_curve[i * 8 + 7]) != 8)
 453                        return -EINVAL;
 454
 455        /* If there has been an error in the input data, we won't
 456         * reach this loop.
 457         */
 458        mutex_lock(&fb_info->bl_curve_mutex);
 459        for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
 460                fb_info->bl_curve[i] = tmp_curve[i];
 461        mutex_unlock(&fb_info->bl_curve_mutex);
 462
 463        return count;
 464}
 465
 466static ssize_t show_bl_curve(struct device *device,
 467                             struct device_attribute *attr, char *buf)
 468{
 469        struct fb_info *fb_info = dev_get_drvdata(device);
 470        ssize_t len = 0;
 471        unsigned int i;
 472
 473        /* Some drivers don't use framebuffer_alloc(), but those also
 474         * don't have backlights.
 475         */
 476        if (!fb_info || !fb_info->bl_dev)
 477                return -ENODEV;
 478
 479        mutex_lock(&fb_info->bl_curve_mutex);
 480        for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
 481                len += scnprintf(&buf[len], PAGE_SIZE - len, "%8ph\n",
 482                                fb_info->bl_curve + i);
 483        mutex_unlock(&fb_info->bl_curve_mutex);
 484
 485        return len;
 486}
 487#endif
 488
 489/* When cmap is added back in it should be a binary attribute
 490 * not a text one. Consideration should also be given to converting
 491 * fbdev to use configfs instead of sysfs */
 492static struct device_attribute device_attrs[] = {
 493        __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
 494        __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
 495        __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
 496        __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
 497        __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
 498        __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
 499        __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
 500        __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
 501        __ATTR(name, S_IRUGO, show_name, NULL),
 502        __ATTR(stride, S_IRUGO, show_stride, NULL),
 503        __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
 504        __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
 505#if IS_ENABLED(CONFIG_FB_BACKLIGHT)
 506        __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
 507#endif
 508};
 509
 510int fb_init_device(struct fb_info *fb_info)
 511{
 512        int i, error = 0;
 513
 514        dev_set_drvdata(fb_info->dev, fb_info);
 515
 516        fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
 517
 518        for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
 519                error = device_create_file(fb_info->dev, &device_attrs[i]);
 520
 521                if (error)
 522                        break;
 523        }
 524
 525        if (error) {
 526                while (--i >= 0)
 527                        device_remove_file(fb_info->dev, &device_attrs[i]);
 528                fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
 529        }
 530
 531        return 0;
 532}
 533
 534void fb_cleanup_device(struct fb_info *fb_info)
 535{
 536        unsigned int i;
 537
 538        if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
 539                for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
 540                        device_remove_file(fb_info->dev, &device_attrs[i]);
 541
 542                fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
 543        }
 544}
 545
 546#if IS_ENABLED(CONFIG_FB_BACKLIGHT)
 547/* This function generates a linear backlight curve
 548 *
 549 *     0: off
 550 *   1-7: min
 551 * 8-127: linear from min to max
 552 */
 553void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
 554{
 555        unsigned int i, flat, count, range = (max - min);
 556
 557        mutex_lock(&fb_info->bl_curve_mutex);
 558
 559        fb_info->bl_curve[0] = off;
 560
 561        for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat)
 562                fb_info->bl_curve[flat] = min;
 563
 564        count = FB_BACKLIGHT_LEVELS * 15 / 16;
 565        for (i = 0; i < count; ++i)
 566                fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count);
 567
 568        mutex_unlock(&fb_info->bl_curve_mutex);
 569}
 570EXPORT_SYMBOL_GPL(fb_bl_default_curve);
 571#endif
 572