12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
219757fc8STomi Valkeinen /*
319757fc8STomi Valkeinen * fbsysfs.c - framebuffer device class and attributes
419757fc8STomi Valkeinen *
519757fc8STomi Valkeinen * Copyright (c) 2004 James Simmons <jsimmons@infradead.org>
619757fc8STomi Valkeinen */
719757fc8STomi Valkeinen
8f0f6839bSThomas Zimmermann #include <linux/console.h>
919757fc8STomi Valkeinen #include <linux/fb.h>
10de29ae5cSDaniel Vetter #include <linux/fbcon.h>
11e2af0032SThomas Zimmermann #include <linux/major.h>
12e2af0032SThomas Zimmermann
13e2af0032SThomas Zimmermann #include "fb_internal.h"
1419757fc8STomi Valkeinen
1519757fc8STomi Valkeinen #define FB_SYSFS_FLAG_ATTR 1
1619757fc8STomi Valkeinen
activate(struct fb_info * fb_info,struct fb_var_screeninfo * var)1719757fc8STomi Valkeinen static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
1819757fc8STomi Valkeinen {
1919757fc8STomi Valkeinen int err;
2019757fc8STomi Valkeinen
2119757fc8STomi Valkeinen var->activate |= FB_ACTIVATE_FORCE;
2219757fc8STomi Valkeinen console_lock();
239ad5cc9bSDaniel Vetter lock_fb_info(fb_info);
2419757fc8STomi Valkeinen err = fb_set_var(fb_info, var);
25d88ca7e1STetsuo Handa if (!err)
26d88ca7e1STetsuo Handa fbcon_update_vcs(fb_info, var->activate & FB_ACTIVATE_ALL);
279ad5cc9bSDaniel Vetter unlock_fb_info(fb_info);
2819757fc8STomi Valkeinen console_unlock();
2919757fc8STomi Valkeinen if (err)
3019757fc8STomi Valkeinen return err;
3119757fc8STomi Valkeinen return 0;
3219757fc8STomi Valkeinen }
3319757fc8STomi Valkeinen
mode_string(char * buf,unsigned int offset,const struct fb_videomode * mode)3419757fc8STomi Valkeinen static int mode_string(char *buf, unsigned int offset,
3519757fc8STomi Valkeinen const struct fb_videomode *mode)
3619757fc8STomi Valkeinen {
3719757fc8STomi Valkeinen char m = 'U';
3819757fc8STomi Valkeinen char v = 'p';
3919757fc8STomi Valkeinen
4019757fc8STomi Valkeinen if (mode->flag & FB_MODE_IS_DETAILED)
4119757fc8STomi Valkeinen m = 'D';
4219757fc8STomi Valkeinen if (mode->flag & FB_MODE_IS_VESA)
4319757fc8STomi Valkeinen m = 'V';
4419757fc8STomi Valkeinen if (mode->flag & FB_MODE_IS_STANDARD)
4519757fc8STomi Valkeinen m = 'S';
4619757fc8STomi Valkeinen
4719757fc8STomi Valkeinen if (mode->vmode & FB_VMODE_INTERLACED)
4819757fc8STomi Valkeinen v = 'i';
4919757fc8STomi Valkeinen if (mode->vmode & FB_VMODE_DOUBLE)
5019757fc8STomi Valkeinen v = 'd';
5119757fc8STomi Valkeinen
5219757fc8STomi Valkeinen return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
5319757fc8STomi Valkeinen m, mode->xres, mode->yres, v, mode->refresh);
5419757fc8STomi Valkeinen }
5519757fc8STomi Valkeinen
store_mode(struct device * device,struct device_attribute * attr,const char * buf,size_t count)5619757fc8STomi Valkeinen static ssize_t store_mode(struct device *device, struct device_attribute *attr,
5719757fc8STomi Valkeinen const char *buf, size_t count)
5819757fc8STomi Valkeinen {
5919757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
6019757fc8STomi Valkeinen char mstr[100];
6119757fc8STomi Valkeinen struct fb_var_screeninfo var;
6219757fc8STomi Valkeinen struct fb_modelist *modelist;
6319757fc8STomi Valkeinen struct fb_videomode *mode;
6419757fc8STomi Valkeinen size_t i;
6519757fc8STomi Valkeinen int err;
6619757fc8STomi Valkeinen
6719757fc8STomi Valkeinen memset(&var, 0, sizeof(var));
6819757fc8STomi Valkeinen
69*1520081aSJinjie Ruan list_for_each_entry(modelist, &fb_info->modelist, list) {
7019757fc8STomi Valkeinen mode = &modelist->mode;
7119757fc8STomi Valkeinen i = mode_string(mstr, 0, mode);
7219757fc8STomi Valkeinen if (strncmp(mstr, buf, max(count, i)) == 0) {
7319757fc8STomi Valkeinen
7419757fc8STomi Valkeinen var = fb_info->var;
7519757fc8STomi Valkeinen fb_videomode_to_var(&var, mode);
7619757fc8STomi Valkeinen if ((err = activate(fb_info, &var)))
7719757fc8STomi Valkeinen return err;
7819757fc8STomi Valkeinen fb_info->mode = mode;
7919757fc8STomi Valkeinen return count;
8019757fc8STomi Valkeinen }
8119757fc8STomi Valkeinen }
8219757fc8STomi Valkeinen return -EINVAL;
8319757fc8STomi Valkeinen }
8419757fc8STomi Valkeinen
show_mode(struct device * device,struct device_attribute * attr,char * buf)8519757fc8STomi Valkeinen static ssize_t show_mode(struct device *device, struct device_attribute *attr,
8619757fc8STomi Valkeinen char *buf)
8719757fc8STomi Valkeinen {
8819757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
8919757fc8STomi Valkeinen
9019757fc8STomi Valkeinen if (!fb_info->mode)
9119757fc8STomi Valkeinen return 0;
9219757fc8STomi Valkeinen
9319757fc8STomi Valkeinen return mode_string(buf, 0, fb_info->mode);
9419757fc8STomi Valkeinen }
9519757fc8STomi Valkeinen
store_modes(struct device * device,struct device_attribute * attr,const char * buf,size_t count)9619757fc8STomi Valkeinen static ssize_t store_modes(struct device *device,
9719757fc8STomi Valkeinen struct device_attribute *attr,
9819757fc8STomi Valkeinen const char *buf, size_t count)
9919757fc8STomi Valkeinen {
10019757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
10119757fc8STomi Valkeinen LIST_HEAD(old_list);
10219757fc8STomi Valkeinen int i = count / sizeof(struct fb_videomode);
10319757fc8STomi Valkeinen
10419757fc8STomi Valkeinen if (i * sizeof(struct fb_videomode) != count)
10519757fc8STomi Valkeinen return -EINVAL;
10619757fc8STomi Valkeinen
10719757fc8STomi Valkeinen console_lock();
10834f31f14SDaniel Vetter lock_fb_info(fb_info);
10919757fc8STomi Valkeinen
11019757fc8STomi Valkeinen list_splice(&fb_info->modelist, &old_list);
11119757fc8STomi Valkeinen fb_videomode_to_modelist((const struct fb_videomode *)buf, i,
11219757fc8STomi Valkeinen &fb_info->modelist);
11319757fc8STomi Valkeinen if (fb_new_modelist(fb_info)) {
11419757fc8STomi Valkeinen fb_destroy_modelist(&fb_info->modelist);
11519757fc8STomi Valkeinen list_splice(&old_list, &fb_info->modelist);
11619757fc8STomi Valkeinen } else
11719757fc8STomi Valkeinen fb_destroy_modelist(&old_list);
11819757fc8STomi Valkeinen
11919757fc8STomi Valkeinen unlock_fb_info(fb_info);
12019757fc8STomi Valkeinen console_unlock();
12119757fc8STomi Valkeinen
12219757fc8STomi Valkeinen return 0;
12319757fc8STomi Valkeinen }
12419757fc8STomi Valkeinen
show_modes(struct device * device,struct device_attribute * attr,char * buf)12519757fc8STomi Valkeinen static ssize_t show_modes(struct device *device, struct device_attribute *attr,
12619757fc8STomi Valkeinen char *buf)
12719757fc8STomi Valkeinen {
12819757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
12919757fc8STomi Valkeinen unsigned int i;
13019757fc8STomi Valkeinen struct fb_modelist *modelist;
13119757fc8STomi Valkeinen const struct fb_videomode *mode;
13219757fc8STomi Valkeinen
13319757fc8STomi Valkeinen i = 0;
134*1520081aSJinjie Ruan list_for_each_entry(modelist, &fb_info->modelist, list) {
13519757fc8STomi Valkeinen mode = &modelist->mode;
13619757fc8STomi Valkeinen i += mode_string(buf, i, mode);
13719757fc8STomi Valkeinen }
13819757fc8STomi Valkeinen return i;
13919757fc8STomi Valkeinen }
14019757fc8STomi Valkeinen
store_bpp(struct device * device,struct device_attribute * attr,const char * buf,size_t count)14119757fc8STomi Valkeinen static ssize_t store_bpp(struct device *device, struct device_attribute *attr,
14219757fc8STomi Valkeinen const char *buf, size_t count)
14319757fc8STomi Valkeinen {
14419757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
14519757fc8STomi Valkeinen struct fb_var_screeninfo var;
14619757fc8STomi Valkeinen char ** last = NULL;
14719757fc8STomi Valkeinen int err;
14819757fc8STomi Valkeinen
14919757fc8STomi Valkeinen var = fb_info->var;
15019757fc8STomi Valkeinen var.bits_per_pixel = simple_strtoul(buf, last, 0);
15119757fc8STomi Valkeinen if ((err = activate(fb_info, &var)))
15219757fc8STomi Valkeinen return err;
15319757fc8STomi Valkeinen return count;
15419757fc8STomi Valkeinen }
15519757fc8STomi Valkeinen
show_bpp(struct device * device,struct device_attribute * attr,char * buf)15619757fc8STomi Valkeinen static ssize_t show_bpp(struct device *device, struct device_attribute *attr,
15719757fc8STomi Valkeinen char *buf)
15819757fc8STomi Valkeinen {
15919757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
1604701a0ddSQing Wang return sysfs_emit(buf, "%d\n", fb_info->var.bits_per_pixel);
16119757fc8STomi Valkeinen }
16219757fc8STomi Valkeinen
store_rotate(struct device * device,struct device_attribute * attr,const char * buf,size_t count)16319757fc8STomi Valkeinen static ssize_t store_rotate(struct device *device,
16419757fc8STomi Valkeinen struct device_attribute *attr,
16519757fc8STomi Valkeinen const char *buf, size_t count)
16619757fc8STomi Valkeinen {
16719757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
16819757fc8STomi Valkeinen struct fb_var_screeninfo var;
16919757fc8STomi Valkeinen char **last = NULL;
17019757fc8STomi Valkeinen int err;
17119757fc8STomi Valkeinen
17219757fc8STomi Valkeinen var = fb_info->var;
17319757fc8STomi Valkeinen var.rotate = simple_strtoul(buf, last, 0);
17419757fc8STomi Valkeinen
17519757fc8STomi Valkeinen if ((err = activate(fb_info, &var)))
17619757fc8STomi Valkeinen return err;
17719757fc8STomi Valkeinen
17819757fc8STomi Valkeinen return count;
17919757fc8STomi Valkeinen }
18019757fc8STomi Valkeinen
18119757fc8STomi Valkeinen
show_rotate(struct device * device,struct device_attribute * attr,char * buf)18219757fc8STomi Valkeinen static ssize_t show_rotate(struct device *device,
18319757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
18419757fc8STomi Valkeinen {
18519757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
18619757fc8STomi Valkeinen
1874701a0ddSQing Wang return sysfs_emit(buf, "%d\n", fb_info->var.rotate);
18819757fc8STomi Valkeinen }
18919757fc8STomi Valkeinen
store_virtual(struct device * device,struct device_attribute * attr,const char * buf,size_t count)19019757fc8STomi Valkeinen static ssize_t store_virtual(struct device *device,
19119757fc8STomi Valkeinen struct device_attribute *attr,
19219757fc8STomi Valkeinen const char *buf, size_t count)
19319757fc8STomi Valkeinen {
19419757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
19519757fc8STomi Valkeinen struct fb_var_screeninfo var;
19619757fc8STomi Valkeinen char *last = NULL;
19719757fc8STomi Valkeinen int err;
19819757fc8STomi Valkeinen
19919757fc8STomi Valkeinen var = fb_info->var;
20019757fc8STomi Valkeinen var.xres_virtual = simple_strtoul(buf, &last, 0);
20119757fc8STomi Valkeinen last++;
20219757fc8STomi Valkeinen if (last - buf >= count)
20319757fc8STomi Valkeinen return -EINVAL;
20419757fc8STomi Valkeinen var.yres_virtual = simple_strtoul(last, &last, 0);
20519757fc8STomi Valkeinen
20619757fc8STomi Valkeinen if ((err = activate(fb_info, &var)))
20719757fc8STomi Valkeinen return err;
20819757fc8STomi Valkeinen return count;
20919757fc8STomi Valkeinen }
21019757fc8STomi Valkeinen
show_virtual(struct device * device,struct device_attribute * attr,char * buf)21119757fc8STomi Valkeinen static ssize_t show_virtual(struct device *device,
21219757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
21319757fc8STomi Valkeinen {
21419757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
2154701a0ddSQing Wang return sysfs_emit(buf, "%d,%d\n", fb_info->var.xres_virtual,
21619757fc8STomi Valkeinen fb_info->var.yres_virtual);
21719757fc8STomi Valkeinen }
21819757fc8STomi Valkeinen
show_stride(struct device * device,struct device_attribute * attr,char * buf)21919757fc8STomi Valkeinen static ssize_t show_stride(struct device *device,
22019757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
22119757fc8STomi Valkeinen {
22219757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
2234701a0ddSQing Wang return sysfs_emit(buf, "%d\n", fb_info->fix.line_length);
22419757fc8STomi Valkeinen }
22519757fc8STomi Valkeinen
store_blank(struct device * device,struct device_attribute * attr,const char * buf,size_t count)22619757fc8STomi Valkeinen static ssize_t store_blank(struct device *device,
22719757fc8STomi Valkeinen struct device_attribute *attr,
22819757fc8STomi Valkeinen const char *buf, size_t count)
22919757fc8STomi Valkeinen {
23019757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
23119757fc8STomi Valkeinen char *last = NULL;
232de29ae5cSDaniel Vetter int err, arg;
23319757fc8STomi Valkeinen
234de29ae5cSDaniel Vetter arg = simple_strtoul(buf, &last, 0);
23519757fc8STomi Valkeinen console_lock();
236de29ae5cSDaniel Vetter err = fb_blank(fb_info, arg);
237de29ae5cSDaniel Vetter /* might again call into fb_blank */
238de29ae5cSDaniel Vetter fbcon_fb_blanked(fb_info, arg);
23919757fc8STomi Valkeinen console_unlock();
24019757fc8STomi Valkeinen if (err < 0)
24119757fc8STomi Valkeinen return err;
24219757fc8STomi Valkeinen return count;
24319757fc8STomi Valkeinen }
24419757fc8STomi Valkeinen
show_blank(struct device * device,struct device_attribute * attr,char * buf)24519757fc8STomi Valkeinen static ssize_t show_blank(struct device *device,
24619757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
24719757fc8STomi Valkeinen {
24819757fc8STomi Valkeinen // struct fb_info *fb_info = dev_get_drvdata(device);
24919757fc8STomi Valkeinen return 0;
25019757fc8STomi Valkeinen }
25119757fc8STomi Valkeinen
store_console(struct device * device,struct device_attribute * attr,const char * buf,size_t count)25219757fc8STomi Valkeinen static ssize_t store_console(struct device *device,
25319757fc8STomi Valkeinen struct device_attribute *attr,
25419757fc8STomi Valkeinen const char *buf, size_t count)
25519757fc8STomi Valkeinen {
25619757fc8STomi Valkeinen // struct fb_info *fb_info = dev_get_drvdata(device);
25719757fc8STomi Valkeinen return 0;
25819757fc8STomi Valkeinen }
25919757fc8STomi Valkeinen
show_console(struct device * device,struct device_attribute * attr,char * buf)26019757fc8STomi Valkeinen static ssize_t show_console(struct device *device,
26119757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
26219757fc8STomi Valkeinen {
26319757fc8STomi Valkeinen // struct fb_info *fb_info = dev_get_drvdata(device);
26419757fc8STomi Valkeinen return 0;
26519757fc8STomi Valkeinen }
26619757fc8STomi Valkeinen
store_cursor(struct device * device,struct device_attribute * attr,const char * buf,size_t count)26719757fc8STomi Valkeinen static ssize_t store_cursor(struct device *device,
26819757fc8STomi Valkeinen struct device_attribute *attr,
26919757fc8STomi Valkeinen const char *buf, size_t count)
27019757fc8STomi Valkeinen {
27119757fc8STomi Valkeinen // struct fb_info *fb_info = dev_get_drvdata(device);
27219757fc8STomi Valkeinen return 0;
27319757fc8STomi Valkeinen }
27419757fc8STomi Valkeinen
show_cursor(struct device * device,struct device_attribute * attr,char * buf)27519757fc8STomi Valkeinen static ssize_t show_cursor(struct device *device,
27619757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
27719757fc8STomi Valkeinen {
27819757fc8STomi Valkeinen // struct fb_info *fb_info = dev_get_drvdata(device);
27919757fc8STomi Valkeinen return 0;
28019757fc8STomi Valkeinen }
28119757fc8STomi Valkeinen
store_pan(struct device * device,struct device_attribute * attr,const char * buf,size_t count)28219757fc8STomi Valkeinen static ssize_t store_pan(struct device *device,
28319757fc8STomi Valkeinen struct device_attribute *attr,
28419757fc8STomi Valkeinen const char *buf, size_t count)
28519757fc8STomi Valkeinen {
28619757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
28719757fc8STomi Valkeinen struct fb_var_screeninfo var;
28819757fc8STomi Valkeinen char *last = NULL;
28919757fc8STomi Valkeinen int err;
29019757fc8STomi Valkeinen
29119757fc8STomi Valkeinen var = fb_info->var;
29219757fc8STomi Valkeinen var.xoffset = simple_strtoul(buf, &last, 0);
29319757fc8STomi Valkeinen last++;
29419757fc8STomi Valkeinen if (last - buf >= count)
29519757fc8STomi Valkeinen return -EINVAL;
29619757fc8STomi Valkeinen var.yoffset = simple_strtoul(last, &last, 0);
29719757fc8STomi Valkeinen
29819757fc8STomi Valkeinen console_lock();
29919757fc8STomi Valkeinen err = fb_pan_display(fb_info, &var);
30019757fc8STomi Valkeinen console_unlock();
30119757fc8STomi Valkeinen
30219757fc8STomi Valkeinen if (err < 0)
30319757fc8STomi Valkeinen return err;
30419757fc8STomi Valkeinen return count;
30519757fc8STomi Valkeinen }
30619757fc8STomi Valkeinen
show_pan(struct device * device,struct device_attribute * attr,char * buf)30719757fc8STomi Valkeinen static ssize_t show_pan(struct device *device,
30819757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
30919757fc8STomi Valkeinen {
31019757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
3114701a0ddSQing Wang return sysfs_emit(buf, "%d,%d\n", fb_info->var.xoffset,
31219757fc8STomi Valkeinen fb_info->var.yoffset);
31319757fc8STomi Valkeinen }
31419757fc8STomi Valkeinen
show_name(struct device * device,struct device_attribute * attr,char * buf)31519757fc8STomi Valkeinen static ssize_t show_name(struct device *device,
31619757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
31719757fc8STomi Valkeinen {
31819757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
31919757fc8STomi Valkeinen
3204701a0ddSQing Wang return sysfs_emit(buf, "%s\n", fb_info->fix.id);
32119757fc8STomi Valkeinen }
32219757fc8STomi Valkeinen
store_fbstate(struct device * device,struct device_attribute * attr,const char * buf,size_t count)32319757fc8STomi Valkeinen static ssize_t store_fbstate(struct device *device,
32419757fc8STomi Valkeinen struct device_attribute *attr,
32519757fc8STomi Valkeinen const char *buf, size_t count)
32619757fc8STomi Valkeinen {
32719757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
32819757fc8STomi Valkeinen u32 state;
32919757fc8STomi Valkeinen char *last = NULL;
33019757fc8STomi Valkeinen
33119757fc8STomi Valkeinen state = simple_strtoul(buf, &last, 0);
33219757fc8STomi Valkeinen
33319757fc8STomi Valkeinen console_lock();
33434f31f14SDaniel Vetter lock_fb_info(fb_info);
33519757fc8STomi Valkeinen
33619757fc8STomi Valkeinen fb_set_suspend(fb_info, (int)state);
33719757fc8STomi Valkeinen
33819757fc8STomi Valkeinen unlock_fb_info(fb_info);
33919757fc8STomi Valkeinen console_unlock();
34019757fc8STomi Valkeinen
34119757fc8STomi Valkeinen return count;
34219757fc8STomi Valkeinen }
34319757fc8STomi Valkeinen
show_fbstate(struct device * device,struct device_attribute * attr,char * buf)34419757fc8STomi Valkeinen static ssize_t show_fbstate(struct device *device,
34519757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
34619757fc8STomi Valkeinen {
34719757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
3484701a0ddSQing Wang return sysfs_emit(buf, "%d\n", fb_info->state);
34919757fc8STomi Valkeinen }
35019757fc8STomi Valkeinen
351b4a1ed0cSRob Clark #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
store_bl_curve(struct device * device,struct device_attribute * attr,const char * buf,size_t count)35219757fc8STomi Valkeinen static ssize_t store_bl_curve(struct device *device,
35319757fc8STomi Valkeinen struct device_attribute *attr,
35419757fc8STomi Valkeinen const char *buf, size_t count)
35519757fc8STomi Valkeinen {
35619757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
35719757fc8STomi Valkeinen u8 tmp_curve[FB_BACKLIGHT_LEVELS];
35819757fc8STomi Valkeinen unsigned int i;
35919757fc8STomi Valkeinen
36019757fc8STomi Valkeinen /* Some drivers don't use framebuffer_alloc(), but those also
36119757fc8STomi Valkeinen * don't have backlights.
36219757fc8STomi Valkeinen */
36319757fc8STomi Valkeinen if (!fb_info || !fb_info->bl_dev)
36419757fc8STomi Valkeinen return -ENODEV;
36519757fc8STomi Valkeinen
36619757fc8STomi Valkeinen if (count != (FB_BACKLIGHT_LEVELS / 8 * 24))
36719757fc8STomi Valkeinen return -EINVAL;
36819757fc8STomi Valkeinen
36919757fc8STomi Valkeinen for (i = 0; i < (FB_BACKLIGHT_LEVELS / 8); ++i)
37019757fc8STomi Valkeinen if (sscanf(&buf[i * 24],
37119757fc8STomi Valkeinen "%2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx %2hhx\n",
37219757fc8STomi Valkeinen &tmp_curve[i * 8 + 0],
37319757fc8STomi Valkeinen &tmp_curve[i * 8 + 1],
37419757fc8STomi Valkeinen &tmp_curve[i * 8 + 2],
37519757fc8STomi Valkeinen &tmp_curve[i * 8 + 3],
37619757fc8STomi Valkeinen &tmp_curve[i * 8 + 4],
37719757fc8STomi Valkeinen &tmp_curve[i * 8 + 5],
37819757fc8STomi Valkeinen &tmp_curve[i * 8 + 6],
37919757fc8STomi Valkeinen &tmp_curve[i * 8 + 7]) != 8)
38019757fc8STomi Valkeinen return -EINVAL;
38119757fc8STomi Valkeinen
38219757fc8STomi Valkeinen /* If there has been an error in the input data, we won't
38319757fc8STomi Valkeinen * reach this loop.
38419757fc8STomi Valkeinen */
38519757fc8STomi Valkeinen mutex_lock(&fb_info->bl_curve_mutex);
38619757fc8STomi Valkeinen for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i)
38719757fc8STomi Valkeinen fb_info->bl_curve[i] = tmp_curve[i];
38819757fc8STomi Valkeinen mutex_unlock(&fb_info->bl_curve_mutex);
38919757fc8STomi Valkeinen
39019757fc8STomi Valkeinen return count;
39119757fc8STomi Valkeinen }
39219757fc8STomi Valkeinen
show_bl_curve(struct device * device,struct device_attribute * attr,char * buf)39319757fc8STomi Valkeinen static ssize_t show_bl_curve(struct device *device,
39419757fc8STomi Valkeinen struct device_attribute *attr, char *buf)
39519757fc8STomi Valkeinen {
39619757fc8STomi Valkeinen struct fb_info *fb_info = dev_get_drvdata(device);
39719757fc8STomi Valkeinen ssize_t len = 0;
39819757fc8STomi Valkeinen unsigned int i;
39919757fc8STomi Valkeinen
40019757fc8STomi Valkeinen /* Some drivers don't use framebuffer_alloc(), but those also
40119757fc8STomi Valkeinen * don't have backlights.
40219757fc8STomi Valkeinen */
40319757fc8STomi Valkeinen if (!fb_info || !fb_info->bl_dev)
40419757fc8STomi Valkeinen return -ENODEV;
40519757fc8STomi Valkeinen
40619757fc8STomi Valkeinen mutex_lock(&fb_info->bl_curve_mutex);
40719757fc8STomi Valkeinen for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8)
408b4df2047SDan Carpenter len += scnprintf(&buf[len], PAGE_SIZE - len, "%8ph\n",
40930296f61SAndy Shevchenko fb_info->bl_curve + i);
41019757fc8STomi Valkeinen mutex_unlock(&fb_info->bl_curve_mutex);
41119757fc8STomi Valkeinen
41219757fc8STomi Valkeinen return len;
41319757fc8STomi Valkeinen }
41419757fc8STomi Valkeinen #endif
41519757fc8STomi Valkeinen
41619757fc8STomi Valkeinen /* When cmap is added back in it should be a binary attribute
41719757fc8STomi Valkeinen * not a text one. Consideration should also be given to converting
41819757fc8STomi Valkeinen * fbdev to use configfs instead of sysfs */
41919757fc8STomi Valkeinen static struct device_attribute device_attrs[] = {
42019757fc8STomi Valkeinen __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
42119757fc8STomi Valkeinen __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
42219757fc8STomi Valkeinen __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
42319757fc8STomi Valkeinen __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
42419757fc8STomi Valkeinen __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
42519757fc8STomi Valkeinen __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
42619757fc8STomi Valkeinen __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
42719757fc8STomi Valkeinen __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
42819757fc8STomi Valkeinen __ATTR(name, S_IRUGO, show_name, NULL),
42919757fc8STomi Valkeinen __ATTR(stride, S_IRUGO, show_stride, NULL),
43019757fc8STomi Valkeinen __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
43119757fc8STomi Valkeinen __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
432b4a1ed0cSRob Clark #if IS_ENABLED(CONFIG_FB_BACKLIGHT)
43319757fc8STomi Valkeinen __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
43419757fc8STomi Valkeinen #endif
43519757fc8STomi Valkeinen };
43619757fc8STomi Valkeinen
fb_init_device(struct fb_info * fb_info)437e2af0032SThomas Zimmermann static int fb_init_device(struct fb_info *fb_info)
43819757fc8STomi Valkeinen {
43919757fc8STomi Valkeinen int i, error = 0;
44019757fc8STomi Valkeinen
44119757fc8STomi Valkeinen dev_set_drvdata(fb_info->dev, fb_info);
44219757fc8STomi Valkeinen
44319757fc8STomi Valkeinen fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
44419757fc8STomi Valkeinen
44519757fc8STomi Valkeinen for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
44619757fc8STomi Valkeinen error = device_create_file(fb_info->dev, &device_attrs[i]);
44719757fc8STomi Valkeinen
44819757fc8STomi Valkeinen if (error)
44919757fc8STomi Valkeinen break;
45019757fc8STomi Valkeinen }
45119757fc8STomi Valkeinen
45219757fc8STomi Valkeinen if (error) {
45319757fc8STomi Valkeinen while (--i >= 0)
45419757fc8STomi Valkeinen device_remove_file(fb_info->dev, &device_attrs[i]);
45519757fc8STomi Valkeinen fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
45619757fc8STomi Valkeinen }
45719757fc8STomi Valkeinen
45819757fc8STomi Valkeinen return 0;
45919757fc8STomi Valkeinen }
46019757fc8STomi Valkeinen
fb_cleanup_device(struct fb_info * fb_info)461e2af0032SThomas Zimmermann static void fb_cleanup_device(struct fb_info *fb_info)
46219757fc8STomi Valkeinen {
46319757fc8STomi Valkeinen unsigned int i;
46419757fc8STomi Valkeinen
46519757fc8STomi Valkeinen if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
46619757fc8STomi Valkeinen for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
46719757fc8STomi Valkeinen device_remove_file(fb_info->dev, &device_attrs[i]);
46819757fc8STomi Valkeinen
46919757fc8STomi Valkeinen fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
47019757fc8STomi Valkeinen }
47119757fc8STomi Valkeinen }
472e2af0032SThomas Zimmermann
fb_device_create(struct fb_info * fb_info)473e2af0032SThomas Zimmermann int fb_device_create(struct fb_info *fb_info)
474e2af0032SThomas Zimmermann {
475e2af0032SThomas Zimmermann int node = fb_info->node;
476e2af0032SThomas Zimmermann dev_t devt = MKDEV(FB_MAJOR, node);
477e2af0032SThomas Zimmermann int ret;
478e2af0032SThomas Zimmermann
479e2af0032SThomas Zimmermann fb_info->dev = device_create(fb_class, fb_info->device, devt, NULL, "fb%d", node);
480e2af0032SThomas Zimmermann if (IS_ERR(fb_info->dev)) {
481e2af0032SThomas Zimmermann /* Not fatal */
482e2af0032SThomas Zimmermann ret = PTR_ERR(fb_info->dev);
483e2af0032SThomas Zimmermann pr_warn("Unable to create device for framebuffer %d; error %d\n", node, ret);
484e2af0032SThomas Zimmermann fb_info->dev = NULL;
485e2af0032SThomas Zimmermann } else {
486e2af0032SThomas Zimmermann fb_init_device(fb_info);
487e2af0032SThomas Zimmermann }
488e2af0032SThomas Zimmermann
489e2af0032SThomas Zimmermann return 0;
490e2af0032SThomas Zimmermann }
491e2af0032SThomas Zimmermann
fb_device_destroy(struct fb_info * fb_info)492e2af0032SThomas Zimmermann void fb_device_destroy(struct fb_info *fb_info)
493e2af0032SThomas Zimmermann {
494e2af0032SThomas Zimmermann dev_t devt = MKDEV(FB_MAJOR, fb_info->node);
495e2af0032SThomas Zimmermann
496e2af0032SThomas Zimmermann if (!fb_info->dev)
497e2af0032SThomas Zimmermann return;
498e2af0032SThomas Zimmermann
499e2af0032SThomas Zimmermann fb_cleanup_device(fb_info);
500e2af0032SThomas Zimmermann device_destroy(fb_class, devt);
501e2af0032SThomas Zimmermann fb_info->dev = NULL;
502e2af0032SThomas Zimmermann }
503