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 + * Copyright (c) 2005 Jon Smirl * * 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 #include +#include /** * 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);