xref: /openbmc/linux/drivers/video/fbdev/core/fbsysfs.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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