diff options
author | Antonino A. Daplas <adaplas@gmail.com> | 2007-07-17 07:05:28 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-17 13:23:11 -0400 |
commit | 623e71b035cb5271028500720b3622ba76db42bb (patch) | |
tree | 651cd606c57b1e1322f92e0406d744a2b341da7e | |
parent | 317b3c2167f5326a7de30a1abe50c9897da7a0e3 (diff) |
fbcon: allow fbcon to use the primary display driver
Allow fbcon to select the primary display adapter using the
fb_is_primary_device() arch-specific helper. If a a primary adapter is
detected, fbcon will unbind the old adapter from the VT layer, then rebind
using the new adapter. This requires that bind_/unbind_con_driver() be made
public.
Because this feature may produce unexpected behavior (from the user's POV),
this must be explicitly enabled in Kconfig.
[akpm@linux-foundation.org: export unbind_con_driver]
Signed-off-by: Antonino Daplas <adaplas@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | arch/i386/video/fbdev.c | 4 | ||||
-rw-r--r-- | drivers/char/vt.c | 8 | ||||
-rw-r--r-- | drivers/video/console/Kconfig | 20 | ||||
-rw-r--r-- | drivers/video/console/fbcon.c | 80 | ||||
-rw-r--r-- | include/linux/vt_kern.h | 4 |
5 files changed, 102 insertions, 14 deletions
diff --git a/arch/i386/video/fbdev.c b/arch/i386/video/fbdev.c index 7fc712c46a64..48fb38d7d2c0 100644 --- a/arch/i386/video/fbdev.c +++ b/arch/i386/video/fbdev.c | |||
@@ -13,13 +13,11 @@ | |||
13 | 13 | ||
14 | int fb_is_primary_device(struct fb_info *info) | 14 | int fb_is_primary_device(struct fb_info *info) |
15 | { | 15 | { |
16 | struct device *device; | 16 | struct device *device = info->device; |
17 | struct pci_dev *pci_dev = NULL; | 17 | struct pci_dev *pci_dev = NULL; |
18 | struct resource *res = NULL; | 18 | struct resource *res = NULL; |
19 | int retval = 0; | 19 | int retval = 0; |
20 | 20 | ||
21 | device = info->device; | ||
22 | |||
23 | if (device) | 21 | if (device) |
24 | pci_dev = to_pci_dev(device); | 22 | pci_dev = to_pci_dev(device); |
25 | 23 | ||
diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 349673d432f1..8a389b314624 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c | |||
@@ -2869,8 +2869,7 @@ int __init vty_init(void) | |||
2869 | 2869 | ||
2870 | static struct class *vtconsole_class; | 2870 | static struct class *vtconsole_class; |
2871 | 2871 | ||
2872 | static int bind_con_driver(const struct consw *csw, int first, int last, | 2872 | int bind_con_driver(const struct consw *csw, int first, int last, int deflt) |
2873 | int deflt) | ||
2874 | { | 2873 | { |
2875 | struct module *owner = csw->owner; | 2874 | struct module *owner = csw->owner; |
2876 | const char *desc = NULL; | 2875 | const char *desc = NULL; |
@@ -2969,6 +2968,7 @@ err: | |||
2969 | module_put(owner); | 2968 | module_put(owner); |
2970 | return retval; | 2969 | return retval; |
2971 | }; | 2970 | }; |
2971 | EXPORT_SYMBOL(bind_con_driver); | ||
2972 | 2972 | ||
2973 | #ifdef CONFIG_VT_HW_CONSOLE_BINDING | 2973 | #ifdef CONFIG_VT_HW_CONSOLE_BINDING |
2974 | static int con_is_graphics(const struct consw *csw, int first, int last) | 2974 | static int con_is_graphics(const struct consw *csw, int first, int last) |
@@ -2987,8 +2987,7 @@ static int con_is_graphics(const struct consw *csw, int first, int last) | |||
2987 | return retval; | 2987 | return retval; |
2988 | } | 2988 | } |
2989 | 2989 | ||
2990 | static int unbind_con_driver(const struct consw *csw, int first, int last, | 2990 | int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) |
2991 | int deflt) | ||
2992 | { | 2991 | { |
2993 | struct module *owner = csw->owner; | 2992 | struct module *owner = csw->owner; |
2994 | const struct consw *defcsw = NULL; | 2993 | const struct consw *defcsw = NULL; |
@@ -3073,6 +3072,7 @@ err: | |||
3073 | return retval; | 3072 | return retval; |
3074 | 3073 | ||
3075 | } | 3074 | } |
3075 | EXPORT_SYMBOL(unbind_con_driver); | ||
3076 | 3076 | ||
3077 | static int vt_bind(struct con_driver *con) | 3077 | static int vt_bind(struct con_driver *con) |
3078 | { | 3078 | { |
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index d3b8a6be2916..160f55f7ac96 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig | |||
@@ -118,6 +118,26 @@ config FRAMEBUFFER_CONSOLE | |||
118 | help | 118 | help |
119 | Low-level framebuffer-based console driver. | 119 | Low-level framebuffer-based console driver. |
120 | 120 | ||
121 | config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY | ||
122 | bool "Map the console to the primary display device" | ||
123 | depends on FRAMEBUFFER_CONSOLE && VT_HW_CONSOLE_BINDING | ||
124 | default n | ||
125 | ---help--- | ||
126 | If this option is selected, the framebuffer console will | ||
127 | automatically select the primary display device (if the architecture | ||
128 | supports this feature). Otherwise, the framebuffer console will | ||
129 | always select the first framebuffer driver that is loaded. The latter | ||
130 | is the default behavior. | ||
131 | |||
132 | You can always override the automatic selection of the primary device | ||
133 | by using the fbcon=map: boot option. | ||
134 | |||
135 | To select this feature, "Support for binding and unbinding console | ||
136 | drivers", under "Device Drivers"->"Character Devices" must be set to | ||
137 | y. | ||
138 | |||
139 | If unsure, select n. | ||
140 | |||
121 | config FRAMEBUFFER_CONSOLE_ROTATION | 141 | config FRAMEBUFFER_CONSOLE_ROTATION |
122 | bool "Framebuffer Console Rotation" | 142 | bool "Framebuffer Console Rotation" |
123 | depends on FRAMEBUFFER_CONSOLE | 143 | depends on FRAMEBUFFER_CONSOLE |
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 7f4ed792307c..7ba21eb1676c 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c | |||
@@ -75,6 +75,7 @@ | |||
75 | #include <linux/init.h> | 75 | #include <linux/init.h> |
76 | #include <linux/interrupt.h> | 76 | #include <linux/interrupt.h> |
77 | #include <linux/crc32.h> /* For counting font checksums */ | 77 | #include <linux/crc32.h> /* For counting font checksums */ |
78 | #include <asm/fb.h> | ||
78 | #include <asm/irq.h> | 79 | #include <asm/irq.h> |
79 | #include <asm/system.h> | 80 | #include <asm/system.h> |
80 | #include <asm/uaccess.h> | 81 | #include <asm/uaccess.h> |
@@ -125,6 +126,8 @@ static int first_fb_vc; | |||
125 | static int last_fb_vc = MAX_NR_CONSOLES - 1; | 126 | static int last_fb_vc = MAX_NR_CONSOLES - 1; |
126 | static int fbcon_is_default = 1; | 127 | static int fbcon_is_default = 1; |
127 | static int fbcon_has_exited; | 128 | static int fbcon_has_exited; |
129 | static int primary_device = -1; | ||
130 | static int map_override; | ||
128 | 131 | ||
129 | /* font data */ | 132 | /* font data */ |
130 | static char fontname[40]; | 133 | static char fontname[40]; |
@@ -497,13 +500,17 @@ static int __init fb_console_setup(char *this_opt) | |||
497 | 500 | ||
498 | if (!strncmp(options, "map:", 4)) { | 501 | if (!strncmp(options, "map:", 4)) { |
499 | options += 4; | 502 | options += 4; |
500 | if (*options) | 503 | if (*options) { |
501 | for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) { | 504 | for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) { |
502 | if (!options[j]) | 505 | if (!options[j]) |
503 | j = 0; | 506 | j = 0; |
504 | con2fb_map_boot[i] = | 507 | con2fb_map_boot[i] = |
505 | (options[j++]-'0') % FB_MAX; | 508 | (options[j++]-'0') % FB_MAX; |
506 | } | 509 | } |
510 | |||
511 | map_override = 1; | ||
512 | } | ||
513 | |||
507 | return 1; | 514 | return 1; |
508 | } | 515 | } |
509 | 516 | ||
@@ -3004,9 +3011,9 @@ static int fbcon_mode_deleted(struct fb_info *info, | |||
3004 | return found; | 3011 | return found; |
3005 | } | 3012 | } |
3006 | 3013 | ||
3007 | static int fbcon_fb_unregistered(int idx) | 3014 | static int fbcon_fb_unregistered(struct fb_info *info) |
3008 | { | 3015 | { |
3009 | int i; | 3016 | int i, idx = info->node; |
3010 | 3017 | ||
3011 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3018 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
3012 | if (con2fb_map[i] == idx) | 3019 | if (con2fb_map[i] == idx) |
@@ -3034,12 +3041,70 @@ static int fbcon_fb_unregistered(int idx) | |||
3034 | if (!num_registered_fb) | 3041 | if (!num_registered_fb) |
3035 | unregister_con_driver(&fb_con); | 3042 | unregister_con_driver(&fb_con); |
3036 | 3043 | ||
3044 | |||
3045 | if (primary_device == idx) | ||
3046 | primary_device = -1; | ||
3047 | |||
3048 | return 0; | ||
3049 | } | ||
3050 | |||
3051 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY | ||
3052 | static int fbcon_select_primary(struct fb_info *info) | ||
3053 | { | ||
3054 | int ret = 0; | ||
3055 | |||
3056 | if (!map_override && primary_device == -1 && | ||
3057 | fb_is_primary_device(info)) { | ||
3058 | int i, err; | ||
3059 | |||
3060 | printk(KERN_INFO "fbcon: %s is primary device\n", | ||
3061 | info->fix.id); | ||
3062 | primary_device = info->node; | ||
3063 | |||
3064 | if (!con_is_bound(&fb_con)) | ||
3065 | goto done; | ||
3066 | |||
3067 | printk(KERN_INFO "fbcon: Unbinding old driver\n"); | ||
3068 | unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, | ||
3069 | fbcon_is_default); | ||
3070 | info_idx = primary_device; | ||
3071 | |||
3072 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | ||
3073 | con2fb_map_boot[i] = primary_device; | ||
3074 | con2fb_map[i] = primary_device; | ||
3075 | } | ||
3076 | |||
3077 | printk(KERN_INFO "fbcon: Selecting new driver\n"); | ||
3078 | err = bind_con_driver(&fb_con, first_fb_vc, last_fb_vc, | ||
3079 | fbcon_is_default); | ||
3080 | |||
3081 | if (err) { | ||
3082 | for (i = first_fb_vc; i <= last_fb_vc; i++) | ||
3083 | con2fb_map[i] = -1; | ||
3084 | |||
3085 | info_idx = -1; | ||
3086 | } | ||
3087 | |||
3088 | ret = 1; | ||
3089 | } | ||
3090 | |||
3091 | done: | ||
3092 | return ret; | ||
3093 | } | ||
3094 | #else | ||
3095 | static inline int fbcon_select_primary(struct fb_info *info) | ||
3096 | { | ||
3037 | return 0; | 3097 | return 0; |
3038 | } | 3098 | } |
3099 | #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ | ||
3039 | 3100 | ||
3040 | static int fbcon_fb_registered(int idx) | 3101 | static int fbcon_fb_registered(struct fb_info *info) |
3041 | { | 3102 | { |
3042 | int ret = 0, i; | 3103 | int ret = 0, i, idx = info->node; |
3104 | |||
3105 | if (fbcon_select_primary(info)) | ||
3106 | goto done; | ||
3107 | |||
3043 | 3108 | ||
3044 | if (info_idx == -1) { | 3109 | if (info_idx == -1) { |
3045 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3110 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
@@ -3059,6 +3124,7 @@ static int fbcon_fb_registered(int idx) | |||
3059 | } | 3124 | } |
3060 | } | 3125 | } |
3061 | 3126 | ||
3127 | done: | ||
3062 | return ret; | 3128 | return ret; |
3063 | } | 3129 | } |
3064 | 3130 | ||
@@ -3182,10 +3248,10 @@ static int fbcon_event_notify(struct notifier_block *self, | |||
3182 | ret = fbcon_mode_deleted(info, mode); | 3248 | ret = fbcon_mode_deleted(info, mode); |
3183 | break; | 3249 | break; |
3184 | case FB_EVENT_FB_REGISTERED: | 3250 | case FB_EVENT_FB_REGISTERED: |
3185 | ret = fbcon_fb_registered(info->node); | 3251 | ret = fbcon_fb_registered(info); |
3186 | break; | 3252 | break; |
3187 | case FB_EVENT_FB_UNREGISTERED: | 3253 | case FB_EVENT_FB_UNREGISTERED: |
3188 | ret = fbcon_fb_unregistered(info->node); | 3254 | ret = fbcon_fb_unregistered(info); |
3189 | break; | 3255 | break; |
3190 | case FB_EVENT_SET_CONSOLE_MAP: | 3256 | case FB_EVENT_SET_CONSOLE_MAP: |
3191 | con2fb = event->data; | 3257 | con2fb = event->data; |
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index d961635d0e61..0d034d89ee8a 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h | |||
@@ -75,6 +75,10 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc); | |||
75 | int vt_waitactive(int vt); | 75 | int vt_waitactive(int vt); |
76 | void change_console(struct vc_data *new_vc); | 76 | void change_console(struct vc_data *new_vc); |
77 | void reset_vc(struct vc_data *vc); | 77 | void reset_vc(struct vc_data *vc); |
78 | extern int bind_con_driver(const struct consw *csw, int first, int last, | ||
79 | int deflt); | ||
80 | extern int unbind_con_driver(const struct consw *csw, int first, int last, | ||
81 | int deflt); | ||
78 | 82 | ||
79 | /* | 83 | /* |
80 | * vc_screen.c shares this temporary buffer with the console write code so that | 84 | * vc_screen.c shares this temporary buffer with the console write code so that |