aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntonino A. Daplas <adaplas@gmail.com>2006-06-26 03:27:09 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-26 12:58:33 -0400
commite614b18dcedb247ce6f848e623cdf2336df2b476 (patch)
treea867f1fa177eb2ed6c577e45d297374744adca3b
parent3e795de7631b2366d7301182c8d91f6d2911467b (diff)
[PATCH] VT binding: Update fbcon to support binding
The control for binding/unbinding is moved from fbcon to the console layer. Thus the fbcon sysfs attributes, attach and detach, are also gone. 1. Add a notifier event that tells fbcon if a framebuffer driver has been unregistered. If no registered driver remains, fbcon will unregister itself from the console layer. 2. Replaced calls to give_up_console() with unregister_con_driver(). 3. Still use take_over_console() instead of register_con_driver() to maintain compatibility 4. Respect the parameter first_fb_vc and last_fb_vc instead of using 0 and MAX_NR_CONSOLES - 1. These parameters are settable by the user. 5. When fbcon is completely unbound from the console layer, fbcon will also release (iow, decrement module reference counts to zero) all fbdev drivers. In other words, a bind or unbind request from the console layer will propagate down to the framebuffer drivers. 6. If fbcon is not bound to the console, it will ignore all notifier events (except driver registration and unregistration) and all sysfs requests. Signed-off-by: Antonino Daplas <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--drivers/video/console/fbcon.c204
-rw-r--r--drivers/video/fbmem.c7
-rw-r--r--include/linux/fb.h12
3 files changed, 157 insertions, 66 deletions
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 4b7be685c160..26effab5a13e 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -125,6 +125,8 @@ static int softback_lines;
125static int first_fb_vc; 125static int first_fb_vc;
126static int last_fb_vc = MAX_NR_CONSOLES - 1; 126static int last_fb_vc = MAX_NR_CONSOLES - 1;
127static int fbcon_is_default = 1; 127static int fbcon_is_default = 1;
128static int fbcon_has_exited;
129
128/* font data */ 130/* font data */
129static char fontname[40]; 131static char fontname[40];
130 132
@@ -140,7 +142,6 @@ static const struct consw fb_con;
140 142
141#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) 143#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
142 144
143static void fbcon_free_font(struct display *);
144static int fbcon_set_origin(struct vc_data *); 145static int fbcon_set_origin(struct vc_data *);
145 146
146#define CURSOR_DRAW_DELAY (1) 147#define CURSOR_DRAW_DELAY (1)
@@ -255,7 +256,7 @@ static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
255 if (!ops || ops->currcon < 0 || rotate > 3) 256 if (!ops || ops->currcon < 0 || rotate > 3)
256 return; 257 return;
257 258
258 for (i = 0; i < MAX_NR_CONSOLES; i++) { 259 for (i = first_fb_vc; i <= last_fb_vc; i++) {
259 vc = vc_cons[i].d; 260 vc = vc_cons[i].d;
260 if (!vc || vc->vc_mode != KD_TEXT || 261 if (!vc || vc->vc_mode != KD_TEXT ||
261 registered_fb[con2fb_map[i]] != info) 262 registered_fb[con2fb_map[i]] != info)
@@ -534,7 +535,7 @@ static int search_fb_in_map(int idx)
534{ 535{
535 int i, retval = 0; 536 int i, retval = 0;
536 537
537 for (i = 0; i < MAX_NR_CONSOLES; i++) { 538 for (i = first_fb_vc; i <= last_fb_vc; i++) {
538 if (con2fb_map[i] == idx) 539 if (con2fb_map[i] == idx)
539 retval = 1; 540 retval = 1;
540 } 541 }
@@ -545,7 +546,7 @@ static int search_for_mapped_con(void)
545{ 546{
546 int i, retval = 0; 547 int i, retval = 0;
547 548
548 for (i = 0; i < MAX_NR_CONSOLES; i++) { 549 for (i = first_fb_vc; i <= last_fb_vc; i++) {
549 if (con2fb_map[i] != -1) 550 if (con2fb_map[i] != -1)
550 retval = 1; 551 retval = 1;
551 } 552 }
@@ -567,6 +568,7 @@ static int fbcon_takeover(int show_logo)
567 568
568 err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, 569 err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
569 fbcon_is_default); 570 fbcon_is_default);
571
570 if (err) { 572 if (err) {
571 for (i = first_fb_vc; i <= last_fb_vc; i++) { 573 for (i = first_fb_vc; i <= last_fb_vc; i++) {
572 con2fb_map[i] = -1; 574 con2fb_map[i] = -1;
@@ -801,8 +803,8 @@ static int set_con2fb_map(int unit, int newidx, int user)
801 if (oldidx == newidx) 803 if (oldidx == newidx)
802 return 0; 804 return 0;
803 805
804 if (!info) 806 if (!info || fbcon_has_exited)
805 err = -EINVAL; 807 return -EINVAL;
806 808
807 if (!err && !search_for_mapped_con()) { 809 if (!err && !search_for_mapped_con()) {
808 info_idx = newidx; 810 info_idx = newidx;
@@ -838,6 +840,9 @@ static int set_con2fb_map(int unit, int newidx, int user)
838 con2fb_init_display(vc, info, unit, show_logo); 840 con2fb_init_display(vc, info, unit, show_logo);
839 } 841 }
840 842
843 if (!search_fb_in_map(info_idx))
844 info_idx = newidx;
845
841 release_console_sem(); 846 release_console_sem();
842 return err; 847 return err;
843} 848}
@@ -1040,6 +1045,7 @@ static const char *fbcon_startup(void)
1040#endif /* CONFIG_MAC */ 1045#endif /* CONFIG_MAC */
1041 1046
1042 fbcon_add_cursor_timer(info); 1047 fbcon_add_cursor_timer(info);
1048 fbcon_has_exited = 0;
1043 return display_desc; 1049 return display_desc;
1044} 1050}
1045 1051
@@ -1067,17 +1073,36 @@ static void fbcon_init(struct vc_data *vc, int init)
1067 1073
1068 /* If we are not the first console on this 1074 /* If we are not the first console on this
1069 fb, copy the font from that console */ 1075 fb, copy the font from that console */
1070 t = &fb_display[svc->vc_num]; 1076 t = &fb_display[fg_console];
1071 if (!vc->vc_font.data) { 1077 if (!p->fontdata) {
1072 vc->vc_font.data = (void *)(p->fontdata = t->fontdata); 1078 if (t->fontdata) {
1073 vc->vc_font.width = (*default_mode)->vc_font.width; 1079 struct vc_data *fvc = vc_cons[fg_console].d;
1074 vc->vc_font.height = (*default_mode)->vc_font.height; 1080
1075 p->userfont = t->userfont; 1081 vc->vc_font.data = (void *)(p->fontdata =
1076 if (p->userfont) 1082 fvc->vc_font.data);
1077 REFCOUNT(p->fontdata)++; 1083 vc->vc_font.width = fvc->vc_font.width;
1084 vc->vc_font.height = fvc->vc_font.height;
1085 p->userfont = t->userfont;
1086
1087 if (p->userfont)
1088 REFCOUNT(p->fontdata)++;
1089 } else {
1090 const struct font_desc *font = NULL;
1091
1092 if (!fontname[0] || !(font = find_font(fontname)))
1093 font = get_default_font(info->var.xres,
1094 info->var.yres);
1095 vc->vc_font.width = font->width;
1096 vc->vc_font.height = font->height;
1097 vc->vc_font.data = (void *)(p->fontdata = font->data);
1098 vc->vc_font.charcount = 256; /* FIXME Need to
1099 support more fonts */
1100 }
1078 } 1101 }
1102
1079 if (p->userfont) 1103 if (p->userfont)
1080 charcnt = FNTCHARCNT(p->fontdata); 1104 charcnt = FNTCHARCNT(p->fontdata);
1105
1081 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); 1106 vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
1082 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; 1107 vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
1083 if (charcnt == 256) { 1108 if (charcnt == 256) {
@@ -1151,13 +1176,47 @@ static void fbcon_init(struct vc_data *vc, int init)
1151 ops->p = &fb_display[fg_console]; 1176 ops->p = &fb_display[fg_console];
1152} 1177}
1153 1178
1179static void fbcon_free_font(struct display *p)
1180{
1181 if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
1182 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
1183 p->fontdata = NULL;
1184 p->userfont = 0;
1185}
1186
1154static void fbcon_deinit(struct vc_data *vc) 1187static void fbcon_deinit(struct vc_data *vc)
1155{ 1188{
1156 struct display *p = &fb_display[vc->vc_num]; 1189 struct display *p = &fb_display[vc->vc_num];
1190 struct fb_info *info;
1191 struct fbcon_ops *ops;
1192 int idx;
1157 1193
1158 if (info_idx != -1)
1159 return;
1160 fbcon_free_font(p); 1194 fbcon_free_font(p);
1195 idx = con2fb_map[vc->vc_num];
1196
1197 if (idx == -1)
1198 goto finished;
1199
1200 info = registered_fb[idx];
1201
1202 if (!info)
1203 goto finished;
1204
1205 ops = info->fbcon_par;
1206
1207 if (!ops)
1208 goto finished;
1209
1210 if (CON_IS_VISIBLE(vc))
1211 fbcon_del_cursor_timer(info);
1212
1213 ops->flags &= ~FBCON_FLAGS_INIT;
1214finished:
1215
1216 if (!con_is_bound(&fb_con))
1217 fbcon_exit();
1218
1219 return;
1161} 1220}
1162 1221
1163/* ====================================================================== */ 1222/* ====================================================================== */
@@ -2227,14 +2286,6 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
2227 return 0; 2286 return 0;
2228} 2287}
2229 2288
2230static void fbcon_free_font(struct display *p)
2231{
2232 if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
2233 kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
2234 p->fontdata = NULL;
2235 p->userfont = 0;
2236}
2237
2238static int fbcon_get_font(struct vc_data *vc, struct console_font *font) 2289static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
2239{ 2290{
2240 u8 *fontdata = vc->vc_font.data; 2291 u8 *fontdata = vc->vc_font.data;
@@ -2448,7 +2499,7 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigne
2448 2499
2449 FNTSUM(new_data) = csum; 2500 FNTSUM(new_data) = csum;
2450 /* Check if the same font is on some other console already */ 2501 /* Check if the same font is on some other console already */
2451 for (i = 0; i < MAX_NR_CONSOLES; i++) { 2502 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2452 struct vc_data *tmp = vc_cons[i].d; 2503 struct vc_data *tmp = vc_cons[i].d;
2453 2504
2454 if (fb_display[i].userfont && 2505 if (fb_display[i].userfont &&
@@ -2773,7 +2824,7 @@ static void fbcon_set_all_vcs(struct fb_info *info)
2773 if (!ops || ops->currcon < 0) 2824 if (!ops || ops->currcon < 0)
2774 return; 2825 return;
2775 2826
2776 for (i = 0; i < MAX_NR_CONSOLES; i++) { 2827 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2777 vc = vc_cons[i].d; 2828 vc = vc_cons[i].d;
2778 if (!vc || vc->vc_mode != KD_TEXT || 2829 if (!vc || vc->vc_mode != KD_TEXT ||
2779 registered_fb[con2fb_map[i]] != info) 2830 registered_fb[con2fb_map[i]] != info)
@@ -2835,21 +2886,55 @@ static int fbcon_mode_deleted(struct fb_info *info,
2835 return found; 2886 return found;
2836} 2887}
2837 2888
2889static int fbcon_fb_unregistered(int idx)
2890{
2891 int i;
2892
2893 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2894 if (con2fb_map[i] == idx)
2895 con2fb_map[i] = -1;
2896 }
2897
2898 if (idx == info_idx) {
2899 info_idx = -1;
2900
2901 for (i = 0; i < FB_MAX; i++) {
2902 if (registered_fb[i] != NULL) {
2903 info_idx = i;
2904 break;
2905 }
2906 }
2907 }
2908
2909 if (info_idx != -1) {
2910 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2911 if (con2fb_map[i] == -1)
2912 con2fb_map[i] = info_idx;
2913 }
2914 }
2915
2916 if (!num_registered_fb)
2917 unregister_con_driver(&fb_con);
2918
2919 return 0;
2920}
2921
2838static int fbcon_fb_registered(int idx) 2922static int fbcon_fb_registered(int idx)
2839{ 2923{
2840 int ret = 0, i; 2924 int ret = 0, i;
2841 2925
2842 if (info_idx == -1) { 2926 if (info_idx == -1) {
2843 for (i = 0; i < MAX_NR_CONSOLES; i++) { 2927 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2844 if (con2fb_map_boot[i] == idx) { 2928 if (con2fb_map_boot[i] == idx) {
2845 info_idx = idx; 2929 info_idx = idx;
2846 break; 2930 break;
2847 } 2931 }
2848 } 2932 }
2933
2849 if (info_idx != -1) 2934 if (info_idx != -1)
2850 ret = fbcon_takeover(1); 2935 ret = fbcon_takeover(1);
2851 } else { 2936 } else {
2852 for (i = 0; i < MAX_NR_CONSOLES; i++) { 2937 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2853 if (con2fb_map_boot[i] == idx && 2938 if (con2fb_map_boot[i] == idx &&
2854 con2fb_map[i] == -1) 2939 con2fb_map[i] == -1)
2855 set_con2fb_map(i, idx, 0); 2940 set_con2fb_map(i, idx, 0);
@@ -2888,7 +2973,7 @@ static void fbcon_new_modelist(struct fb_info *info)
2888 struct fb_var_screeninfo var; 2973 struct fb_var_screeninfo var;
2889 struct fb_videomode *mode; 2974 struct fb_videomode *mode;
2890 2975
2891 for (i = 0; i < MAX_NR_CONSOLES; i++) { 2976 for (i = first_fb_vc; i <= last_fb_vc; i++) {
2892 if (registered_fb[con2fb_map[i]] != info) 2977 if (registered_fb[con2fb_map[i]] != info)
2893 continue; 2978 continue;
2894 if (!fb_display[i].mode) 2979 if (!fb_display[i].mode)
@@ -2916,6 +3001,14 @@ static int fbcon_event_notify(struct notifier_block *self,
2916 struct fb_con2fbmap *con2fb; 3001 struct fb_con2fbmap *con2fb;
2917 int ret = 0; 3002 int ret = 0;
2918 3003
3004 /*
3005 * ignore all events except driver registration and deregistration
3006 * if fbcon is not active
3007 */
3008 if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
3009 action == FB_EVENT_FB_UNREGISTERED))
3010 goto done;
3011
2919 switch(action) { 3012 switch(action) {
2920 case FB_EVENT_SUSPEND: 3013 case FB_EVENT_SUSPEND:
2921 fbcon_suspended(info); 3014 fbcon_suspended(info);
@@ -2936,6 +3029,9 @@ static int fbcon_event_notify(struct notifier_block *self,
2936 case FB_EVENT_FB_REGISTERED: 3029 case FB_EVENT_FB_REGISTERED:
2937 ret = fbcon_fb_registered(info->node); 3030 ret = fbcon_fb_registered(info->node);
2938 break; 3031 break;
3032 case FB_EVENT_FB_UNREGISTERED:
3033 ret = fbcon_fb_unregistered(info->node);
3034 break;
2939 case FB_EVENT_SET_CONSOLE_MAP: 3035 case FB_EVENT_SET_CONSOLE_MAP:
2940 con2fb = event->data; 3036 con2fb = event->data;
2941 ret = set_con2fb_map(con2fb->console - 1, 3037 ret = set_con2fb_map(con2fb->console - 1,
@@ -2953,6 +3049,7 @@ static int fbcon_event_notify(struct notifier_block *self,
2953 break; 3049 break;
2954 } 3050 }
2955 3051
3052done:
2956 return ret; 3053 return ret;
2957} 3054}
2958 3055
@@ -2997,6 +3094,9 @@ static ssize_t store_rotate(struct class_device *class_device,
2997 int rotate, idx; 3094 int rotate, idx;
2998 char **last = NULL; 3095 char **last = NULL;
2999 3096
3097 if (fbcon_has_exited)
3098 return count;
3099
3000 acquire_console_sem(); 3100 acquire_console_sem();
3001 idx = con2fb_map[fg_console]; 3101 idx = con2fb_map[fg_console];
3002 3102
@@ -3018,6 +3118,9 @@ static ssize_t store_rotate_all(struct class_device *class_device,
3018 int rotate, idx; 3118 int rotate, idx;
3019 char **last = NULL; 3119 char **last = NULL;
3020 3120
3121 if (fbcon_has_exited)
3122 return count;
3123
3021 acquire_console_sem(); 3124 acquire_console_sem();
3022 idx = con2fb_map[fg_console]; 3125 idx = con2fb_map[fg_console];
3023 3126
@@ -3037,6 +3140,9 @@ static ssize_t show_rotate(struct class_device *class_device, char *buf)
3037 struct fb_info *info; 3140 struct fb_info *info;
3038 int rotate = 0, idx; 3141 int rotate = 0, idx;
3039 3142
3143 if (fbcon_has_exited)
3144 return 0;
3145
3040 acquire_console_sem(); 3146 acquire_console_sem();
3041 idx = con2fb_map[fg_console]; 3147 idx = con2fb_map[fg_console];
3042 3148
@@ -3050,32 +3156,9 @@ err:
3050 return snprintf(buf, PAGE_SIZE, "%d\n", rotate); 3156 return snprintf(buf, PAGE_SIZE, "%d\n", rotate);
3051} 3157}
3052 3158
3053static ssize_t store_attach(struct class_device *class_device,
3054 const char *buf, size_t count)
3055{
3056 if (info_idx == -1)
3057 fbcon_start();
3058
3059 return count;
3060}
3061
3062static ssize_t store_detach(struct class_device *class_device,
3063 const char *buf, size_t count)
3064{
3065 if (info_idx != -1) {
3066 fbcon_exit();
3067 give_up_console(&fb_con);
3068 }
3069
3070 info_idx = -1;
3071 return count;
3072}
3073
3074static struct class_device_attribute class_device_attrs[] = { 3159static struct class_device_attribute class_device_attrs[] = {
3075 __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), 3160 __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
3076 __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all), 3161 __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
3077 __ATTR(attach, S_IWUSR, NULL, store_attach),
3078 __ATTR(detach, S_IWUSR, NULL, store_detach),
3079}; 3162};
3080 3163
3081static int fbcon_init_class_device(void) 3164static int fbcon_init_class_device(void)
@@ -3112,7 +3195,9 @@ static void fbcon_exit(void)
3112 struct fb_info *info; 3195 struct fb_info *info;
3113 int i, j, mapped; 3196 int i, j, mapped;
3114 3197
3115 acquire_console_sem(); 3198 if (fbcon_has_exited)
3199 return;
3200
3116#ifdef CONFIG_ATARI 3201#ifdef CONFIG_ATARI
3117 free_irq(IRQ_AUTO_4, fbcon_vbl_handler); 3202 free_irq(IRQ_AUTO_4, fbcon_vbl_handler);
3118#endif 3203#endif
@@ -3131,11 +3216,9 @@ static void fbcon_exit(void)
3131 if (info == NULL) 3216 if (info == NULL)
3132 continue; 3217 continue;
3133 3218
3134 for (j = 0; j < MAX_NR_CONSOLES; j++) { 3219 for (j = first_fb_vc; j <= last_fb_vc; j++) {
3135 if (con2fb_map[j] == i) { 3220 if (con2fb_map[j] == i)
3136 con2fb_map[j] = -1;
3137 mapped = 1; 3221 mapped = 1;
3138 }
3139 } 3222 }
3140 3223
3141 if (mapped) { 3224 if (mapped) {
@@ -3151,11 +3234,10 @@ static void fbcon_exit(void)
3151 3234
3152 if (info->queue.func == fb_flashcursor) 3235 if (info->queue.func == fb_flashcursor)
3153 info->queue.func = NULL; 3236 info->queue.func = NULL;
3154
3155 } 3237 }
3156 } 3238 }
3157 3239
3158 release_console_sem(); 3240 fbcon_has_exited = 1;
3159} 3241}
3160 3242
3161static int __init fb_console_init(void) 3243static int __init fb_console_init(void)
@@ -3189,7 +3271,7 @@ module_init(fb_console_init);
3189 3271
3190#ifdef MODULE 3272#ifdef MODULE
3191 3273
3192static void fbcon_deinit_class_device(void) 3274static void __exit fbcon_deinit_class_device(void)
3193{ 3275{
3194 int i; 3276 int i;
3195 3277
@@ -3204,7 +3286,9 @@ static void __exit fb_console_exit(void)
3204 fb_unregister_client(&fbcon_event_notifier); 3286 fb_unregister_client(&fbcon_event_notifier);
3205 fbcon_deinit_class_device(); 3287 fbcon_deinit_class_device();
3206 class_device_destroy(fb_class, MKDEV(FB_MAJOR, FB_MAX)); 3288 class_device_destroy(fb_class, MKDEV(FB_MAJOR, FB_MAX));
3289 fbcon_exit();
3207 release_console_sem(); 3290 release_console_sem();
3291 unregister_con_driver(&fb_con);
3208} 3292}
3209 3293
3210module_exit(fb_console_exit); 3294module_exit(fb_console_exit);
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index a2102a543ee7..31143afe7c95 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1353,6 +1353,7 @@ register_framebuffer(struct fb_info *fb_info)
1353int 1353int
1354unregister_framebuffer(struct fb_info *fb_info) 1354unregister_framebuffer(struct fb_info *fb_info)
1355{ 1355{
1356 struct fb_event event;
1356 int i; 1357 int i;
1357 1358
1358 i = fb_info->node; 1359 i = fb_info->node;
@@ -1360,13 +1361,17 @@ unregister_framebuffer(struct fb_info *fb_info)
1360 return -EINVAL; 1361 return -EINVAL;
1361 devfs_remove("fb/%d", i); 1362 devfs_remove("fb/%d", i);
1362 1363
1363 if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) 1364 if (fb_info->pixmap.addr &&
1365 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
1364 kfree(fb_info->pixmap.addr); 1366 kfree(fb_info->pixmap.addr);
1365 fb_destroy_modelist(&fb_info->modelist); 1367 fb_destroy_modelist(&fb_info->modelist);
1366 registered_fb[i]=NULL; 1368 registered_fb[i]=NULL;
1367 num_registered_fb--; 1369 num_registered_fb--;
1368 fb_cleanup_class_device(fb_info); 1370 fb_cleanup_class_device(fb_info);
1369 class_device_destroy(fb_class, MKDEV(FB_MAJOR, i)); 1371 class_device_destroy(fb_class, MKDEV(FB_MAJOR, i));
1372 event.info = fb_info;
1373 blocking_notifier_call_chain(&fb_notifier_list,
1374 FB_EVENT_FB_UNREGISTERED, &event);
1370 return 0; 1375 return 0;
1371} 1376}
1372 1377
diff --git a/include/linux/fb.h b/include/linux/fb.h
index c64f25255286..07a08e92bc73 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -504,17 +504,19 @@ struct fb_cursor_user {
504#define FB_EVENT_MODE_DELETE 0x04 504#define FB_EVENT_MODE_DELETE 0x04
505/* A driver registered itself */ 505/* A driver registered itself */
506#define FB_EVENT_FB_REGISTERED 0x05 506#define FB_EVENT_FB_REGISTERED 0x05
507/* A driver unregistered itself */
508#define FB_EVENT_FB_UNREGISTERED 0x06
507/* CONSOLE-SPECIFIC: get console to framebuffer mapping */ 509/* CONSOLE-SPECIFIC: get console to framebuffer mapping */
508#define FB_EVENT_GET_CONSOLE_MAP 0x06 510#define FB_EVENT_GET_CONSOLE_MAP 0x07
509/* CONSOLE-SPECIFIC: set console to framebuffer mapping */ 511/* CONSOLE-SPECIFIC: set console to framebuffer mapping */
510#define FB_EVENT_SET_CONSOLE_MAP 0x07 512#define FB_EVENT_SET_CONSOLE_MAP 0x08
511/* A display blank is requested */ 513/* A display blank is requested */
512#define FB_EVENT_BLANK 0x08 514#define FB_EVENT_BLANK 0x09
513/* Private modelist is to be replaced */ 515/* Private modelist is to be replaced */
514#define FB_EVENT_NEW_MODELIST 0x09 516#define FB_EVENT_NEW_MODELIST 0x0A
515/* The resolution of the passed in fb_info about to change and 517/* The resolution of the passed in fb_info about to change and
516 all vc's should be changed */ 518 all vc's should be changed */
517#define FB_EVENT_MODE_CHANGE_ALL 0x0A 519#define FB_EVENT_MODE_CHANGE_ALL 0x0B
518 520
519struct fb_event { 521struct fb_event {
520 struct fb_info *info; 522 struct fb_info *info;