linux-fbdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] sysfs support for fbdev
@ 2005-03-01 10:00 Jon Smirl
  2005-03-01 21:11 ` James Simmons
  2005-03-01 23:49 ` Benjamin Herrenschmidt
  0 siblings, 2 replies; 17+ messages in thread
From: Jon Smirl @ 2005-03-01 10:00 UTC (permalink / raw)
  To: fbdev

[-- Attachment #1: Type: text/plain, Size: 812 bytes --]

Not finished yet but here's an update.

Merges Antonio's fbcon support
Creates monitor device inside of fbdev
Creates two fbdev devices for radeonfb

The changes in class.c are temporary GregKH promises a real fix soon.
Patch is 1000+ lines so I made it an attachment.

Making the monitor device inside of the fbdev allows hotplug to work
for monitors. Now all I need to do is hook up the radeon interrupt.
When finished, unplug the monitor, get interrupt, build monitor
device, get hotplug event, helper builds modelist, set modelist to
fbdev, this causes sysfs attribute change event, hal then dbus hears
this, X rebuilds the display. Still a few weeks of work to go.

If anyone wants to help implement some of the other attributes, send
me a patch and I will incorporate it.

-- 
Jon Smirl
jonsmirl@gmail.com

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: sysfs.patch --]
[-- Type: text/x-diff; name="sysfs.patch", Size: 32605 bytes --]

diff -Nru a/drivers/base/class.c b/drivers/base/class.c
--- a/drivers/base/class.c	2005-03-01 04:49:39 -05:00
+++ b/drivers/base/class.c	2005-03-01 04:49:39 -05:00
@@ -417,8 +417,9 @@
 
 	/* first, register with generic layer. */
 	kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
-	if (parent)
+	if (parent && !class_dev->kobj.parent)
 		class_dev->kobj.parent = &parent->subsys.kset.kobj;
+	class_dev->kobj.parent = kobject_get(class_dev->kobj.parent);
 
 	if ((error = kobject_add(&class_dev->kobj)))
 		goto register_done;
@@ -467,6 +468,7 @@
 	class_device_driver_unlink(class_dev);
 	class_device_remove_attrs(class_dev);
 
+	kobject_put(class_dev->kobj.parent);
 	kobject_del(&class_dev->kobj);
 
 	if (parent)
diff -Nru a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c
--- a/drivers/video/aty/radeon_base.c	2005-03-01 04:49:39 -05:00
+++ b/drivers/video/aty/radeon_base.c	2005-03-01 04:49:39 -05:00
@@ -1802,10 +1802,8 @@
 };
 
 
-static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo)
+static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo, struct fb_info *info)
 {
-	struct fb_info *info = rinfo->info;
-
 	info->par = rinfo;
 	info->pseudo_palette = rinfo->pseudo_palette;
 	info->flags = FBINFO_DEFAULT
@@ -2147,13 +2145,12 @@
 	return count;
 }
 
+#define to_class_device(x) container_of((x), struct class_device, kobj)
 
 static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, size_t count)
 {
-	struct device *dev = container_of(kobj, struct device, kobj);
-	struct pci_dev *pdev = to_pci_dev(dev);
-        struct fb_info *info = pci_get_drvdata(pdev);
-        struct radeonfb_info *rinfo = info->par;
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(to_class_device(kobj));
+        struct radeonfb_info *rinfo = fb_info->par;
 
 	return radeon_show_one_edid(buf, off, count, rinfo->mon1_EDID);
 }
@@ -2161,17 +2158,15 @@
 
 static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, size_t count)
 {
-	struct device *dev = container_of(kobj, struct device, kobj);
-	struct pci_dev *pdev = to_pci_dev(dev);
-        struct fb_info *info = pci_get_drvdata(pdev);
-        struct radeonfb_info *rinfo = info->par;
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(to_class_device(kobj));
+	struct radeonfb_info *rinfo = fb_info->par;
 
 	return radeon_show_one_edid(buf, off, count, rinfo->mon2_EDID);
 }
 
 static struct bin_attribute edid1_attr = {
 	.attr   = {
-		.name	= "edid1",
+		.name	= "edid",
 		.owner	= THIS_MODULE,
 		.mode	= 0444,
 	},
@@ -2181,7 +2176,7 @@
 
 static struct bin_attribute edid2_attr = {
 	.attr   = {
-		.name	= "edid2",
+		.name	= "edid",
 		.owner	= THIS_MODULE,
 		.mode	= 0444,
 	},
@@ -2189,7 +2184,6 @@
 	.read	= radeon_show_edid2,
 };
 
-
 static int radeonfb_pci_register (struct pci_dev *pdev,
 				  const struct pci_device_id *ent)
 {
@@ -2217,6 +2211,7 @@
 	rinfo = info->par;
 	rinfo->info = info;	
 	rinfo->pdev = pdev;
+	rinfo->info2.device = &pdev->dev;
 	
 	spin_lock_init(&rinfo->reg_lock);
 	init_timer(&rinfo->lvds_timer);
@@ -2339,7 +2334,8 @@
 #endif
 
 	/* set all the vital stuff */
-	radeon_set_fbinfo (rinfo);
+	radeon_set_fbinfo (rinfo, rinfo->info);
+	radeon_set_fbinfo (rinfo, &rinfo->info2);
 
 	/* Probe screen types */
 	radeon_probe_screens(rinfo, monitor_layout, ignore_edid);
@@ -2347,12 +2343,6 @@
 	/* Build mode list, check out panel native model */
 	radeon_check_modes(rinfo, mode_option);
 
-	/* Register some sysfs stuff (should be done better) */
-	if (rinfo->mon1_EDID)
-		sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
-	if (rinfo->mon2_EDID)
-		sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
-
 	/* save current mode regs before we switch into the new one
 	 * so we can restore this upon __exit
 	 */
@@ -2364,10 +2354,26 @@
 	/* Register with fbdev layer */
 	ret = register_framebuffer(info);
 	if (ret < 0) {
-		printk (KERN_ERR "radeonfb (%s): could not register framebuffer\n",
+		printk (KERN_ERR "radeonfb (%s): could not register framebuffer 1\n",
 			pci_name(rinfo->pdev));
 		goto err_unmap_fb;
 	}
+	ret = register_framebuffer(&rinfo->info2);
+	if (ret < 0) {
+		printk (KERN_ERR "radeonfb (%s): could not register framebuffer 2\n",
+			pci_name(rinfo->pdev));
+		goto err_unmap_fb;
+	}
+
+	/* Register some sysfs stuff (should be done better) */
+	if (rinfo->mon1_EDID) {
+		rinfo->monitor1 = fb_add_child(info->class_device, info, "monitor");
+		class_device_create_bin_file(rinfo->monitor1, &edid1_attr);
+	}
+	if (rinfo->mon2_EDID) {
+		rinfo->monitor2 = fb_add_child(rinfo->info2.class_device, &rinfo->info2, "monitor");
+		class_device_create_bin_file(rinfo->monitor2, &edid2_attr);
+	}
 
 	/* Setup Power Management capabilities */
 	if (default_dynclk < -1) {
@@ -2424,8 +2430,6 @@
 	return ret;
 }
 
-
-
 static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev)
 {
         struct fb_info *info = pci_get_drvdata(pdev);
@@ -2452,8 +2456,15 @@
 	if (rinfo->mtrr_hdl >= 0)
 		mtrr_del(rinfo->mtrr_hdl, 0, 0);
 #endif
-
-        unregister_framebuffer(info);
+	if (rinfo->mon1_EDID)
+		class_device_remove_bin_file(rinfo->monitor1, &edid1_attr);
+	if (rinfo->mon2_EDID)
+		class_device_remove_bin_file(rinfo->monitor1, &edid2_attr);
+	class_device_unregister(rinfo->monitor1);
+	class_device_unregister(rinfo->monitor2);
+	
+	unregister_framebuffer(info);
+        unregister_framebuffer(&rinfo->info2);
 
         iounmap(rinfo->mmio_base);
         iounmap(rinfo->fb_base);
diff -Nru a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h
--- a/drivers/video/aty/radeonfb.h	2005-03-01 04:49:39 -05:00
+++ b/drivers/video/aty/radeonfb.h	2005-03-01 04:49:39 -05:00
@@ -270,7 +270,11 @@
 };
 
 struct radeonfb_info {
+	struct fb_info		info2;
 	struct fb_info		*info;
+	
+	struct class_device 	*monitor1;
+	struct class_device 	*monitor2;
 
 	struct radeon_regs 	state;
 	struct radeon_regs	init_state;
diff -Nru a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
--- a/drivers/video/console/bitblit.c	2005-03-01 04:49:39 -05:00
+++ b/drivers/video/console/bitblit.c	2005-03-01 04:49:39 -05:00
@@ -39,7 +39,7 @@
 {
 	int attribute = 0;
 
-	if (fb_get_color_depth(info) == 1) {
+	if (fb_get_color_depth(&info->var) == 1) {
 		if (attr_underline(c))
 			attribute |= FBCON_ATTRIBUTE_UNDERLINE;
 		if (attr_reverse(c))
diff -Nru a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
--- a/drivers/video/console/fbcon.c	2005-03-01 04:49:39 -05:00
+++ b/drivers/video/console/fbcon.c	2005-03-01 04:49:39 -05:00
@@ -182,8 +182,10 @@
 static __inline__ void ypan_down(struct vc_data *vc, int count);
 static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx,
 			    int dy, int dx, int height, int width, u_int y_break);
-static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc);
-static void fbcon_preset_disp(struct fb_info *info, int unit);
+static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
+			   struct vc_data *vc);
+static void fbcon_preset_disp(struct fb_info *info, struct fb_var_screeninfo *var,
+			      int unit);
 static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
 			      int line, int count, int dy);
 
@@ -209,7 +211,7 @@
 static inline int get_color(struct vc_data *vc, struct fb_info *info,
 	      u16 c, int is_fg)
 {
-	int depth = fb_get_color_depth(info);
+	int depth = fb_get_color_depth(&info->var);
 	int color = 0;
 
 	if (console_blanked) {
@@ -418,7 +420,7 @@
 	 * remove underline attribute from erase character
 	 * if black and white framebuffer.
 	 */
-	if (fb_get_color_depth(info) == 1)
+	if (fb_get_color_depth(&info->var) == 1)
 		erase &= ~0x400;
 	logo_height = fb_prepare_logo(info);
 	logo_lines = (logo_height + vc->vc_font.height - 1) /
@@ -595,9 +597,9 @@
 		info->fbops->fb_set_par(info);
 
 	if (vc)
-		fbcon_set_disp(info, vc);
+		fbcon_set_disp(info, &info->var, vc);
 	else
-		fbcon_preset_disp(info, unit);
+		fbcon_preset_disp(info, &info->var, unit);
 
 	if (show_logo) {
 		struct vc_data *fg_vc = vc_cons[fg_console].d;
@@ -681,6 +683,8 @@
 			  struct fb_var_screeninfo *var,
 			  struct fb_info *info)
 {
+	struct fb_videomode *mode;
+
 	disp->xres_virtual = var->xres_virtual;
 	disp->yres_virtual = var->yres_virtual;
 	disp->bits_per_pixel = var->bits_per_pixel;
@@ -693,10 +697,11 @@
 	disp->green = var->green;
 	disp->blue = var->blue;
 	disp->transp = var->transp;
-	disp->mode = fb_match_mode(var, &info->modelist);
-	if (disp->mode == NULL)
+	mode = fb_match_mode(var, &info->modelist);
+	if (mode == NULL)
 		/* This should not happen */
 		return -EINVAL;
+	disp->mode = mode;
 	return 0;
 }
 
@@ -918,7 +923,7 @@
 	}
 	if (p->userfont)
 		charcnt = FNTCHARCNT(p->fontdata);
-	vc->vc_can_do_color = (fb_get_color_depth(info) != 1);
+	vc->vc_can_do_color = (fb_get_color_depth(&info->var) != 1);
 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
 	if (charcnt == 256) {
 		vc->vc_hi_font_mask = 0;
@@ -1124,13 +1129,14 @@
 /*
  * If no vc is existent yet, just set struct display
  */
-static void fbcon_preset_disp(struct fb_info *info, int unit)
+static void fbcon_preset_disp(struct fb_info *info, struct fb_var_screeninfo *var,
+			      int unit)
 {
 	struct display *p = &fb_display[unit];
 	struct display *t = &fb_display[fg_console];
 
-	info->var.xoffset = info->var.yoffset = p->yscroll = 0;
-	if (var_to_display(p, &info->var, info))
+	var->xoffset = var->yoffset = p->yscroll = 0;
+	if (var_to_display(p, var, info))
 		return;
 
 	p->fontdata = t->fontdata;
@@ -1139,7 +1145,8 @@
 		REFCOUNT(p->fontdata)++;
 }
 
-static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc)
+static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
+			   struct vc_data *vc)
 {
 	struct display *p = &fb_display[vc->vc_num], *t;
 	struct vc_data **default_mode = vc->vc_display_fg;
@@ -1147,8 +1154,8 @@
 	int display_fg = (*default_mode)->vc_num;
 	int rows, cols, charcnt = 256;
 
-	info->var.xoffset = info->var.yoffset = p->yscroll = 0;
-	if (var_to_display(p, &info->var, info))
+	var->xoffset = var->yoffset = p->yscroll = 0;
+	if (var_to_display(p, var, info))
 		return;
 	t = &fb_display[display_fg];
 	if (!vc->vc_font.data) {
@@ -1162,7 +1169,7 @@
 	if (p->userfont)
 		charcnt = FNTCHARCNT(p->fontdata);
 
-	vc->vc_can_do_color = (fb_get_color_depth(info) != 1);
+	vc->vc_can_do_color = (fb_get_color_depth(var) != 1);
 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
 	if (charcnt == 256) {
 		vc->vc_hi_font_mask = 0;
@@ -1177,8 +1184,8 @@
 	if (!*vc->vc_uni_pagedir_loc)
 		con_copy_unimap(vc->vc_num, display_fg);
 
-	cols = info->var.xres / vc->vc_font.width;
-	rows = info->var.yres / vc->vc_font.height;
+	cols = var->xres / vc->vc_font.width;
+	rows = var->yres / vc->vc_font.height;
 	vc_resize(vc->vc_num, cols, rows);
 	if (CON_IS_VISIBLE(vc)) {
 		update_screen(vc->vc_num);
@@ -1957,7 +1964,7 @@
 	set_blitting_type(vc, info, p);
 	((struct fbcon_ops *)info->fbcon_par)->cursor_reset = 1;
 
-	vc->vc_can_do_color = (fb_get_color_depth(info) != 1);
+	vc->vc_can_do_color = (fb_get_color_depth(&info->var) != 1);
 	vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
 	updatescrollmode(p, info, vc);
 
@@ -2328,7 +2335,7 @@
 	if (fbcon_is_inactive(vc, info))
 		return -EINVAL;
 
-	depth = fb_get_color_depth(info);
+	depth = fb_get_color_depth(&info->var);
 	if (depth > 3) {
 		for (i = j = 0; i < 16; i++) {
 			k = table[i];
@@ -2605,7 +2612,7 @@
 		if (fb_info != info)
 			continue;
 		p = &fb_display[i];
-		if (!p || !p->mode)
+		if (!p)
 			continue;
 		if (fb_mode_is_equal(p->mode, mode)) {
 			found = 1;
@@ -2663,6 +2670,31 @@
 	}
 }
 
+static void fbcon_new_modelist(struct fb_info *info)
+{
+	int i;
+	struct vc_data *vc;
+	struct fb_var_screeninfo var;
+	struct fb_videomode *mode;
+
+	for (i = 0; i < MAX_NR_CONSOLES; i++) {
+		if (registered_fb[con2fb_map[i]] != info)
+			continue;
+		if (!fb_display[i].mode)
+			continue;
+		vc = vc_cons[i].d;
+		display_to_var(&var, &fb_display[i]);
+		mode = fb_find_nearest_mode(&var, &info->modelist);
+		fb_videomode_to_var(&var, mode);
+
+		if (vc)
+			fbcon_set_disp(info, &var, vc);
+		else
+			fbcon_preset_disp(info, &var, i);
+
+	}
+}
+
 static int fbcon_event_notify(struct notifier_block *self, 
 			      unsigned long action, void *data)
 {
@@ -2700,6 +2732,9 @@
 		break;
 	case FB_EVENT_BLANK:
 		fbcon_fb_blanked(info, *(int *)event->data);
+		break;
+	case FB_EVENT_NEW_MODELIST:
+		fbcon_new_modelist(info);
 		break;
 	}
 
diff -Nru a/drivers/video/fbmem.c b/drivers/video/fbmem.c
--- a/drivers/video/fbmem.c	2005-03-01 04:49:39 -05:00
+++ b/drivers/video/fbmem.c	2005-03-01 04:49:39 -05:00
@@ -62,10 +62,8 @@
  * Helpers
  */
 
-int fb_get_color_depth(struct fb_info *info)
+int fb_get_color_depth(struct fb_var_screeninfo *var)
 {
-	struct fb_var_screeninfo *var = &info->var;
-
 	if (var->green.length == var->blue.length &&
 	    var->green.length == var->red.length &&
 	    !var->green.offset && !var->blue.offset &&
@@ -300,7 +298,7 @@
 	const u8 *src = logo->data;
 	u8 d, xor = (info->fix.visual == FB_VISUAL_MONO01) ? 0xff : 0;
 
-	if (fb_get_color_depth(info) == 3)
+	if (fb_get_color_depth(&info->var) == 3)
 		fg = 7;
 
 	switch (depth) {
@@ -365,7 +363,7 @@
 
 int fb_prepare_logo(struct fb_info *info)
 {
-	int depth = fb_get_color_depth(info);
+	int depth = fb_get_color_depth(&info->var);
 
 	memset(&fb_logo, 0, sizeof(struct logo_data));
 
@@ -1042,7 +1040,7 @@
 #endif
 };
 
-static struct class_simple *fb_class;
+struct class *fb_class;
 
 /**
  *	register_framebuffer - registers a frame buffer device
@@ -1058,7 +1056,6 @@
 register_framebuffer(struct fb_info *fb_info)
 {
 	int i;
-	struct class_device *c;
 	struct fb_event event;
 
 	if (num_registered_fb == FB_MAX)
@@ -1069,13 +1066,13 @@
 			break;
 	fb_info->node = i;
 
-	c = class_simple_device_add(fb_class, MKDEV(FB_MAJOR, i),
-				    fb_info->device, "fb%d", i);
-	if (IS_ERR(c)) {
+	fb_info->class_device = fb_device_add(fb_class, fb_info);
+	if (IS_ERR(fb_info->class_device)) {
 		/* Not fatal */
-		printk(KERN_WARNING "Unable to create class_device for framebuffer %d; errno = %ld\n", i, PTR_ERR(c));
+		printk(KERN_WARNING "Unable to create class_device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->class_device));
+		fb_info->class_device = NULL;
 	}
-	
+
 	if (fb_info->pixmap.addr == NULL) {
 		fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
 		if (fb_info->pixmap.addr) {
@@ -1134,7 +1131,7 @@
 	fb_destroy_modelist(&fb_info->modelist);
 	registered_fb[i]=NULL;
 	num_registered_fb--;
-	class_simple_device_remove(MKDEV(FB_MAJOR, i));
+	class_device_unregister(fb_info->class_device);
 	return 0;
 }
 
@@ -1197,7 +1194,7 @@
 	if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
 		printk("unable to get major %d for fb devs\n", FB_MAJOR);
 
-	fb_class = class_simple_create(THIS_MODULE, "graphics");
+	fb_class = fb_create_class(THIS_MODULE, "graphics");
 	if (IS_ERR(fb_class)) {
 		printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
 		fb_class = NULL;
@@ -1208,7 +1205,7 @@
 void __exit
 fbmem_exit(void)
 {
-	class_simple_destroy(fb_class);
+	class_unregister(fb_class);
 }
 
 #ifdef MODULE
@@ -1221,6 +1218,40 @@
 subsys_exitcall(fbmem_exit);
 #endif
 
+int fb_new_modelist(struct fb_info *info)
+{
+	struct fb_event event;
+	struct fb_var_screeninfo var = info->var;
+	struct list_head *pos, *n;
+	struct fb_modelist *modelist;
+	struct fb_videomode *m, mode;
+	int err = 1;
+
+	list_for_each_safe(pos, n, &info->modelist) {
+		modelist = list_entry(pos, struct fb_modelist, list);
+		m = &modelist->mode;
+		fb_videomode_to_var(&var, m);
+		var.activate = FB_ACTIVATE_TEST;
+		err = fb_set_var(info, &var);
+		fb_var_to_videomode(&mode, &var);
+		if (err || !fb_mode_is_equal(m, &mode)) {
+			list_del(pos);
+			kfree(pos);
+		}
+	}
+
+	err = 1;
+
+	if (!list_empty(&info->modelist)) {
+		event.info = info;
+		err = notifier_call_chain(&fb_notifier_list,
+					   FB_EVENT_NEW_MODELIST,
+					   &event);
+	}
+
+	return err;
+}
+
 static char *video_options[FB_MAX];
 static int ofonly;
 
@@ -1332,5 +1363,6 @@
 EXPORT_SYMBOL(fb_register_client);
 EXPORT_SYMBOL(fb_unregister_client);
 EXPORT_SYMBOL(fb_get_options);
+EXPORT_SYMBOL(fb_new_modelist);
 
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c
--- a/drivers/video/fbsysfs.c	2005-03-01 04:49:39 -05:00
+++ b/drivers/video/fbsysfs.c	2005-03-01 04:49:39 -05:00
@@ -2,6 +2,7 @@
  * fbsysfs.c - framebuffer device class and attributes
  *
  * Copyright (c) 2004 James Simmons <jsimmons@infradead.org>
+ * Copyright (c) 2005 Jon Smirl <jonsmirl@gmail.com>
  * 
  *	This program is free software you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -17,6 +18,7 @@
 
 #include <linux/kernel.h>
 #include <linux/fb.h>
+#include <linux/console.h>
 
 /**
  * framebuffer_alloc - creates a new frame buffer info structure
@@ -57,6 +59,7 @@
 #undef PADDING
 #undef BYTES_PER_LONG
 }
+EXPORT_SYMBOL(framebuffer_alloc);
 
 /**
  * framebuffer_release - marks the structure available for freeing
@@ -71,6 +74,371 @@
 {
 	kfree(info);
 }
-
 EXPORT_SYMBOL(framebuffer_release);
-EXPORT_SYMBOL(framebuffer_alloc);
+
+static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
+{
+	int err;
+	
+	var->activate |= FB_ACTIVATE_FORCE;
+	acquire_console_sem();
+	fb_info->flags |= FBINFO_MISC_USEREVENT;
+	err = fb_set_var(fb_info, var);
+	fb_info->flags &= ~FBINFO_MISC_USEREVENT;
+	release_console_sem();
+	if (err)
+		return err;
+	return 0;
+}
+
+static int mode_string(char *buf, unsigned int offset, const struct fb_videomode *mode)
+{
+	char m = 'U';
+	if (mode->flag & FB_MODE_IS_DETAILED)
+		m = 'D';
+	if (mode->flag & FB_MODE_IS_VESA)
+		m = 'V';
+	if (mode->flag & FB_MODE_IS_STANDARD)
+		m = 'S';
+	return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d-%d\n", m, mode->xres, mode->yres, mode->refresh);
+}
+
+static ssize_t store_mode(struct class_device *class_device, const char * buf, size_t count)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	char mstr[100];
+	struct fb_var_screeninfo var;
+	struct fb_modelist *modelist;
+	struct fb_videomode *mode;
+	struct list_head *pos;
+	size_t i;
+	int err;
+
+	memset(&var, 0, sizeof(var));
+
+	list_for_each(pos, &fb_info->modelist) {
+		modelist = list_entry(pos, struct fb_modelist, list);
+		mode = &modelist->mode;
+		i = mode_string(mstr, 0, mode);
+		if (strncmp(mstr, buf, max(count, i)) == 0) {
+
+			var = fb_info->var;
+			fb_videomode_to_var(&var, mode);
+			if ((err = activate(fb_info, &var)))
+				return err;
+			fb_info->mode = mode;
+			return count;
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t show_mode(struct class_device *class_device, char *buf)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+
+	if (!fb_info->mode)
+		return 0;
+
+	return mode_string(buf, 0, fb_info->mode);
+}
+
+static ssize_t store_modes(struct class_device *class_device, const char * buf, size_t count)
+{
+	struct fb_info *fb_info = 
+			(struct fb_info *)class_get_devdata(class_device);
+	LIST_HEAD(old_list);
+	
+	int i = count / sizeof(struct fb_videomode);
+
+	if (i * sizeof(struct fb_videomode) != count)
+		return -EINVAL;
+
+	acquire_console_sem();
+	list_splice(&fb_info->modelist, &old_list);
+	fb_videomode_to_modelist((struct fb_videomode *)buf, i,
+				  &fb_info->modelist);
+	if (fb_new_modelist(fb_info)) {
+		fb_destroy_modelist(&fb_info->modelist);
+		list_splice(&old_list, &fb_info->modelist);
+	} else 
+		fb_destroy_modelist(&old_list);
+
+	release_console_sem();
+
+	return 0;
+}
+
+
+static ssize_t show_modes(struct class_device *class_device, char *buf)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	unsigned int i;
+	struct list_head *pos;
+	struct fb_modelist *modelist;
+	const struct fb_videomode *mode;
+
+	i = 0;
+	list_for_each(pos, &fb_info->modelist) {
+		modelist = list_entry(pos, struct fb_modelist, list);
+		mode = &modelist->mode;
+		i += mode_string(buf, i, mode);
+	}
+	return i;
+}
+
+static ssize_t store_bpp(struct class_device *class_device, const char * buf, size_t count)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	struct fb_var_screeninfo var;
+	char ** last = NULL;
+	int err;
+
+	var = fb_info->var;
+	var.bits_per_pixel = simple_strtoul(buf, last, 0);
+	if ((err = activate(fb_info, &var)))
+		return err;
+	return count;
+}
+
+static ssize_t show_bpp(struct class_device *class_device, char *buf)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.bits_per_pixel);
+}
+
+static ssize_t store_virtual(struct class_device *class_device, const char * buf, size_t count)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	struct fb_var_screeninfo var;
+	char *last = NULL;
+	int err;
+
+	var = fb_info->var;
+	var.xres_virtual = simple_strtoul(buf, &last, 0);
+	last++;
+	if (last - buf >= count)
+		return -EINVAL;
+	var.yres_virtual = simple_strtoul(last, &last, 0);
+	printk(KERN_ERR "fb: xres %d yres %d\n", var.xres_virtual, var.yres_virtual);
+	
+	if ((err = activate(fb_info, &var)))
+		return err;
+	return count;
+}
+
+static ssize_t show_virtual(struct class_device *class_device, char *buf)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xres_virtual, fb_info->var.xres_virtual);
+}
+
+static ssize_t store_cmap(struct class_device *class_device, const char * buf, size_t count)
+{
+//	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return 0;
+}
+
+static ssize_t show_cmap(struct class_device *class_device, char *buf)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	unsigned int offset = 0, i;
+	
+	if (!fb_info->cmap.red || !fb_info->cmap.blue || fb_info->cmap.green || fb_info->cmap.transp)
+		return -EINVAL;
+	
+	for (i = 0; i < fb_info->cmap.len; i++) {
+		offset += snprintf(buf, PAGE_SIZE - offset, "%d,%d,%d,%d,%d\n", i + fb_info->cmap.start,
+				   fb_info->cmap.red[i], fb_info->cmap.blue[i], fb_info->cmap.green[i], 
+				   fb_info->cmap.transp[i]); 
+	}
+	return offset;
+}
+
+static ssize_t store_blank(struct class_device *class_device, const char * buf, size_t count)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	char *last = NULL;
+	int err;
+
+	acquire_console_sem();
+	fb_info->flags |= FBINFO_MISC_USEREVENT;
+	err = fb_blank(fb_info, simple_strtoul(buf, &last, 0));
+	fb_info->flags &= ~FBINFO_MISC_USEREVENT;
+	release_console_sem();
+	if (err < 0)
+		return err;
+	return count;
+}
+
+static ssize_t show_blank(struct class_device *class_device, char *buf)
+{
+//	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return 0;
+}
+
+static ssize_t store_console(struct class_device *class_device, const char * buf, size_t count)
+{
+//	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return 0;
+}
+
+static ssize_t show_console(struct class_device *class_device, char *buf)
+{
+//	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return 0;
+}
+
+static ssize_t store_cursor(struct class_device *class_device, const char * buf, size_t count)
+{
+//	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return 0;
+}
+
+static ssize_t show_cursor(struct class_device *class_device, char *buf)
+{
+//	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return 0;
+}
+
+static ssize_t store_pan(struct class_device *class_device, const char * buf, size_t count)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	struct fb_var_screeninfo var;
+	char *last = NULL;
+	int err;
+
+	var = fb_info->var;
+	var.xoffset = simple_strtoul(buf, &last, 0);
+	last++;
+	if (last - buf >= count)
+		return -EINVAL;
+	var.yoffset = simple_strtoul(last, &last, 0);
+	
+	acquire_console_sem();
+	err = fb_pan_display(fb_info, &var);
+	release_console_sem();
+	
+	if (err < 0)
+		return err;
+	return count;
+}
+
+static ssize_t show_pan(struct class_device *class_device, char *buf)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return snprintf(buf, PAGE_SIZE, "%d,%d\n", fb_info->var.xoffset, fb_info->var.xoffset);
+}
+
+static ssize_t show_dev(struct class_device *class_device, char *buf)
+{
+	struct fb_info *fb_info = (struct fb_info *)class_get_devdata(class_device);
+	return print_dev_t(buf, MKDEV(FB_MAJOR, fb_info->node));
+}
+
+struct class_device_attribute class_device_attrs[] = {
+	__ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp),
+	__ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank),
+	__ATTR(color_map, S_IRUGO|S_IWUSR, show_cmap, store_cmap),
+	__ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console),
+	__ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor),
+	__ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode),
+	__ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes),
+	__ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),
+	__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),
+	__ATTR(dev, S_IRUGO, show_dev, NULL),
+	__ATTR_NULL,
+};
+
+static void release_fb_dev(struct class_device *class_dev)
+{
+	kfree(class_dev);
+}
+
+static void class_fb_release(struct class *class)
+{
+	kfree(class);
+}
+
+struct class *fb_create_class(struct module *owner, char *name)
+{
+	struct class *cs;
+	int retval;
+
+	cs = kmalloc(sizeof(*cs), GFP_KERNEL);
+	if (!cs) {
+		retval = -ENOMEM;
+		goto error;
+	}
+	memset(cs, 0x00, sizeof(*cs));
+
+	cs->name = name;
+	cs->class_release = class_fb_release;
+	cs->release = release_fb_dev;
+	cs->class_dev_attrs = class_device_attrs;
+
+	retval = class_register(cs);
+	if (retval)
+		goto error;
+
+	return cs;
+
+error:
+	kfree(cs);
+	return ERR_PTR(retval);
+}
+
+struct class_device *fb_device_add(struct class *cs, struct fb_info *fb_info)
+{
+	struct class_device *s_dev = NULL;
+	int retval;
+
+	if ((cs == NULL) || (IS_ERR(cs))) {
+		retval = -ENODEV;
+		goto error;
+	}
+
+	s_dev = kmalloc(sizeof(*s_dev), GFP_KERNEL);
+	if (!s_dev) {
+		retval = -ENOMEM;
+		goto error;
+	}
+	memset(s_dev, 0x00, sizeof(*s_dev));
+
+	s_dev->dev = fb_info->device;
+	s_dev->class = cs;
+	class_set_devdata(s_dev, fb_info);
+
+	snprintf(s_dev->class_id, BUS_ID_SIZE, "fb%d", fb_info->node);
+
+	retval = class_device_register(s_dev);
+	if (retval)
+		goto error;
+
+	return s_dev;
+
+error:
+	kfree(s_dev);
+	return ERR_PTR(retval);
+}
+
+struct class_device *fb_add_child(struct class_device *class_device, struct fb_info *fb_info, const char *fmt, ...)
+{
+	va_list args;
+	struct class_device *fb_dev;
+	int ret;
+	
+	fb_dev = kmalloc(sizeof(*fb_dev), GFP_KERNEL);
+	memset(fb_dev, 0, sizeof(*fb_dev));
+	fb_dev->class = class_device->class;
+	fb_dev->kobj.parent = &class_device->kobj;
+	class_set_devdata(fb_dev, fb_info);
+	
+	va_start(args, fmt);
+	vsnprintf(fb_dev->class_id, sizeof(fb_dev->class_id), fmt, args);
+	va_end(args);
+	
+	ret = class_device_register(fb_dev);
+	return fb_dev;
+}
+EXPORT_SYMBOL(fb_add_child);
diff -Nru a/drivers/video/modedb.c b/drivers/video/modedb.c
--- a/drivers/video/modedb.c	2005-03-01 04:49:39 -05:00
+++ b/drivers/video/modedb.c	2005-03-01 04:49:39 -05:00
@@ -722,6 +722,47 @@
 }
 
 /**
+ * fb_find_nearest_mode - find mode closest video mode
+ *
+ * @var: pointer to struct fb_var_screeninfo
+ * @head: pointer to modelist
+ *
+ * Finds best matching videomode, smaller or greater in dimension.
+ * If more than 1 videomode is found, will return the videomode with
+ * the closest refresh rate
+ */
+struct fb_videomode *fb_find_nearest_mode(struct fb_var_screeninfo *var,
+					  struct list_head *head)
+{
+	struct list_head *pos;
+	struct fb_modelist *modelist;
+	struct fb_videomode *mode, *best = NULL;
+	u32 diff = -1, diff_refresh = -1;
+
+	list_for_each(pos, head) {
+		u32 d;
+
+		modelist = list_entry(pos, struct fb_modelist, list);
+		mode = &modelist->mode;
+
+		d = abs(mode->xres - var->xres) + 
+			abs(mode->yres - var->yres);
+		if (diff > d) {
+			diff = d;
+			best = mode;
+		} else if (diff == d) {
+			d = abs(mode->refresh - best->refresh);
+			if (diff_refresh > d) {
+				diff_refresh = d;
+				best = mode;
+			}
+		}
+	}
+	
+	return best;
+}    
+
+/**
  * fb_match_mode - find a videomode which exactly matches the timings in var
  * @var: pointer to struct fb_var_screeninfo
  * @head: pointer to struct list_head of modelist
@@ -846,5 +887,6 @@
 EXPORT_SYMBOL(fb_destroy_modelist);
 EXPORT_SYMBOL(fb_match_mode);
 EXPORT_SYMBOL(fb_find_best_mode);
+EXPORT_SYMBOL(fb_find_nearest_mode);
 EXPORT_SYMBOL(fb_videomode_to_modelist);
 EXPORT_SYMBOL(fb_find_mode);
diff -Nru a/include/linux/fb.h b/include/linux/fb.h
--- a/include/linux/fb.h	2005-03-01 04:49:39 -05:00
+++ b/include/linux/fb.h	2005-03-01 04:49:39 -05:00
@@ -490,6 +490,8 @@
 #define FB_EVENT_SET_CONSOLE_MAP        0x07
 /*      A display blank is requested       */
 #define FB_EVENT_BLANK                  0x08
+/*      Private modelist is to be replaced */
+#define FB_EVENT_NEW_MODELIST           0x09
 
 struct fb_event {
 	struct fb_info *info;
@@ -712,8 +714,10 @@
 	struct fb_pixmap sprite;	/* Cursor hardware mapper */
 	struct fb_cmap cmap;		/* Current cmap */
 	struct list_head modelist;      /* mode list */
+	struct fb_videomode *mode;	/* current mode */
 	struct fb_ops *fbops;
 	struct device *device;
+	struct class_device *class_device; /* sysfs per device attrs */
 #ifdef CONFIG_FB_TILEBLITTING
 	struct fb_tile_ops *tileops;    /* Tile Blitting */
 #endif
@@ -821,8 +825,9 @@
 				u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch,
 				u32 height);
 extern void fb_set_suspend(struct fb_info *info, int state);
-extern int fb_get_color_depth(struct fb_info *info);
+extern int fb_get_color_depth(struct fb_var_screeninfo *var);
 extern int fb_get_options(char *name, char **option);
+extern int fb_new_modelist(struct fb_info *info);
 
 extern struct fb_info *registered_fb[FB_MAX];
 extern int num_registered_fb;
@@ -830,6 +835,10 @@
 /* drivers/video/fbsysfs.c */
 extern struct fb_info *framebuffer_alloc(size_t size, struct device *dev);
 extern void framebuffer_release(struct fb_info *info);
+extern void fb_cleanup_class_device(struct fb_info *head);
+extern struct class *fb_create_class(struct module *owner, char *name);
+extern struct class_device *fb_device_add(struct class *cs, struct fb_info *fb_info);
+extern struct class_device *fb_add_child(struct class_device *class_device, struct fb_info *fb_info, const char *fmt, ...);
 
 /* drivers/video/fbmon.c */
 #define FB_MAXTIMINGS		0
@@ -875,6 +884,8 @@
 					  struct list_head *head);
 extern struct fb_videomode *fb_find_best_mode(struct fb_var_screeninfo *var,
 					      struct list_head *head);
+extern struct fb_videomode *fb_find_nearest_mode(struct fb_var_screeninfo *var,
+						 struct list_head *head);
 extern void fb_destroy_modelist(struct list_head *head);
 extern void fb_videomode_to_modelist(struct fb_videomode *modedb, int num,
 				     struct list_head *head);

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2005-03-07 18:10 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-03-01 10:00 [PATCH] sysfs support for fbdev Jon Smirl
2005-03-01 21:11 ` James Simmons
2005-03-02 18:13   ` Jon Smirl
2005-03-03  0:12     ` James Simmons
2005-03-03  0:57       ` Jon Smirl
2005-03-07 18:05         ` James Simmons
2005-03-07 18:10           ` Jon Smirl
2005-03-04  4:08       ` Jon Smirl
2005-03-04 18:26         ` James Simmons
2005-03-04 18:48           ` Jon Smirl
2005-03-04 18:50             ` Sylvain Meyer
2005-03-04 19:48               ` Buttchereit, Axel (XL)
2005-03-04 18:43         ` Sylvain Meyer
2005-03-04 18:50           ` Jon Smirl
2005-03-04 18:54             ` Sylvain Meyer
2005-03-01 23:49 ` Benjamin Herrenschmidt
2005-03-02  1:05   ` James Simmons

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).