diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2013-08-02 08:05:22 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2013-08-02 19:17:46 -0400 |
commit | e3263ab389a7bc9398c3d366819d6f39b9cfd677 (patch) | |
tree | aa42182804337d3a491f16a5d581e1102a110858 | |
parent | df0960ab2d95543a7c162b04b2064991666adbad (diff) |
x86: provide platform-devices for boot-framebuffers
The current situation regarding boot-framebuffers (VGA, VESA/VBE, EFI) on
x86 causes troubles when loading multiple fbdev drivers. The global
"struct screen_info" does not provide any state-tracking about which
drivers use the FBs. request_mem_region() theoretically works, but
unfortunately vesafb/efifb ignore it due to quirks for broken boards.
Avoid this by creating a platform framebuffer devices with a pointer
to the "struct screen_info" as platform-data. Drivers can now create
platform-drivers and the driver-core will refuse multiple drivers being
active simultaneously.
We keep the screen_info available for backwards-compatibility. Drivers
can be converted in follow-up patches.
Different devices are created for VGA/VESA/EFI FBs to allow multiple
drivers to be loaded on distro kernels. We create:
- "vesa-framebuffer" for VBE/VESA graphics FBs
- "efi-framebuffer" for EFI FBs
- "platform-framebuffer" for everything else
This allows to load vesafb, efifb and others simultaneously and each
picks up only the supported FB types.
Apart from platform-framebuffer devices, this also introduces a
compatibility option for "simple-framebuffer" drivers which recently got
introduced for OF based systems. If CONFIG_X86_SYSFB is selected, we
try to match the screen_info against a simple-framebuffer supported
format. If we succeed, we create a "simple-framebuffer" device instead
of a platform-framebuffer.
This allows to reuse the simplefb.c driver across architectures and also
to introduce a SimpleDRM driver. There is no need to have vesafb.c,
efifb.c, simplefb.c and more just to have architecture specific quirks
in their setup-routines.
Instead, we now move the architecture specific quirks into x86-setup and
provide a generic simple-framebuffer. For backwards-compatibility (if
strange formats are used), we still allow vesafb/efifb to be loaded
simultaneously and pick up all remaining devices.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Link: http://lkml.kernel.org/r/1375445127-15480-4-git-send-email-dh.herrmann@gmail.com
Tested-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | arch/x86/Kconfig | 26 | ||||
-rw-r--r-- | arch/x86/include/asm/sysfb.h | 41 | ||||
-rw-r--r-- | arch/x86/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/kernel/sysfb.c | 71 | ||||
-rw-r--r-- | arch/x86/kernel/sysfb_simplefb.c | 95 |
5 files changed, 235 insertions, 0 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b32ebf92b0ce..5c56559e0c50 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -2270,6 +2270,32 @@ config RAPIDIO | |||
2270 | 2270 | ||
2271 | source "drivers/rapidio/Kconfig" | 2271 | source "drivers/rapidio/Kconfig" |
2272 | 2272 | ||
2273 | config X86_SYSFB | ||
2274 | bool "Mark VGA/VBE/EFI FB as generic system framebuffer" | ||
2275 | help | ||
2276 | Firmwares often provide initial graphics framebuffers so the BIOS, | ||
2277 | bootloader or kernel can show basic video-output during boot for | ||
2278 | user-guidance and debugging. Historically, x86 used the VESA BIOS | ||
2279 | Extensions and EFI-framebuffers for this, which are mostly limited | ||
2280 | to x86. | ||
2281 | This option, if enabled, marks VGA/VBE/EFI framebuffers as generic | ||
2282 | framebuffers so the new generic system-framebuffer drivers can be | ||
2283 | used on x86. If the framebuffer is not compatible with the generic | ||
2284 | modes, it is adverticed as fallback platform framebuffer so legacy | ||
2285 | drivers like efifb, vesafb and uvesafb can pick it up. | ||
2286 | If this option is not selected, all system framebuffers are always | ||
2287 | marked as fallback platform framebuffers as usual. | ||
2288 | |||
2289 | Note: Legacy fbdev drivers, including vesafb, efifb, uvesafb, will | ||
2290 | not be able to pick up generic system framebuffers if this option | ||
2291 | is selected. You are highly encouraged to enable simplefb as | ||
2292 | replacement if you select this option. simplefb can correctly deal | ||
2293 | with generic system framebuffers. But you should still keep vesafb | ||
2294 | and others enabled as fallback if a system framebuffer is | ||
2295 | incompatible with simplefb. | ||
2296 | |||
2297 | If unsure, say Y. | ||
2298 | |||
2273 | endmenu | 2299 | endmenu |
2274 | 2300 | ||
2275 | 2301 | ||
diff --git a/arch/x86/include/asm/sysfb.h b/arch/x86/include/asm/sysfb.h new file mode 100644 index 000000000000..2395fe03f56b --- /dev/null +++ b/arch/x86/include/asm/sysfb.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef _ARCH_X86_KERNEL_SYSFB_H | ||
2 | #define _ARCH_X86_KERNEL_SYSFB_H | ||
3 | |||
4 | /* | ||
5 | * Generic System Framebuffers on x86 | ||
6 | * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the Free | ||
10 | * Software Foundation; either version 2 of the License, or (at your option) | ||
11 | * any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/platform_data/simplefb.h> | ||
16 | #include <linux/screen_info.h> | ||
17 | |||
18 | #ifdef CONFIG_X86_SYSFB | ||
19 | |||
20 | bool parse_mode(const struct screen_info *si, | ||
21 | struct simplefb_platform_data *mode); | ||
22 | int create_simplefb(const struct screen_info *si, | ||
23 | const struct simplefb_platform_data *mode); | ||
24 | |||
25 | #else /* CONFIG_X86_SYSFB */ | ||
26 | |||
27 | static inline bool parse_mode(const struct screen_info *si, | ||
28 | struct simplefb_platform_data *mode) | ||
29 | { | ||
30 | return false; | ||
31 | } | ||
32 | |||
33 | static inline int create_simplefb(const struct screen_info *si, | ||
34 | const struct simplefb_platform_data *mode) | ||
35 | { | ||
36 | return -EINVAL; | ||
37 | } | ||
38 | |||
39 | #endif /* CONFIG_X86_SYSFB */ | ||
40 | |||
41 | #endif /* _ARCH_X86_KERNEL_SYSFB_H */ | ||
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 88d99ea77723..90ecdc5d471a 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile | |||
@@ -103,6 +103,8 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o | |||
103 | obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o | 103 | obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o |
104 | obj-$(CONFIG_OF) += devicetree.o | 104 | obj-$(CONFIG_OF) += devicetree.o |
105 | obj-$(CONFIG_UPROBES) += uprobes.o | 105 | obj-$(CONFIG_UPROBES) += uprobes.o |
106 | obj-y += sysfb.o | ||
107 | obj-$(CONFIG_X86_SYSFB) += sysfb_simplefb.o | ||
106 | 108 | ||
107 | obj-$(CONFIG_PERF_EVENTS) += perf_regs.o | 109 | obj-$(CONFIG_PERF_EVENTS) += perf_regs.o |
108 | obj-$(CONFIG_TRACING) += tracepoint.o | 110 | obj-$(CONFIG_TRACING) += tracepoint.o |
diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c new file mode 100644 index 000000000000..7f30e194eb52 --- /dev/null +++ b/arch/x86/kernel/sysfb.c | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * Generic System Framebuffers on x86 | ||
3 | * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the Free | ||
7 | * Software Foundation; either version 2 of the License, or (at your option) | ||
8 | * any later version. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * Simple-Framebuffer support for x86 systems | ||
13 | * Create a platform-device for any available boot framebuffer. The | ||
14 | * simple-framebuffer platform device is already available on DT systems, so | ||
15 | * this module parses the global "screen_info" object and creates a suitable | ||
16 | * platform device compatible with the "simple-framebuffer" DT object. If | ||
17 | * the framebuffer is incompatible, we instead create a legacy | ||
18 | * "vesa-framebuffer", "efi-framebuffer" or "platform-framebuffer" device and | ||
19 | * pass the screen_info as platform_data. This allows legacy drivers | ||
20 | * to pick these devices up without messing with simple-framebuffer drivers. | ||
21 | * The global "screen_info" is still valid at all times. | ||
22 | * | ||
23 | * If CONFIG_X86_SYSFB is not selected, we never register "simple-framebuffer" | ||
24 | * platform devices, but only use legacy framebuffer devices for | ||
25 | * backwards compatibility. | ||
26 | * | ||
27 | * TODO: We set the dev_id field of all platform-devices to 0. This allows | ||
28 | * other x86 OF/DT parsers to create such devices, too. However, they must | ||
29 | * start at offset 1 for this to work. | ||
30 | */ | ||
31 | |||
32 | #include <linux/err.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/mm.h> | ||
36 | #include <linux/platform_data/simplefb.h> | ||
37 | #include <linux/platform_device.h> | ||
38 | #include <linux/screen_info.h> | ||
39 | #include <asm/sysfb.h> | ||
40 | |||
41 | static __init int sysfb_init(void) | ||
42 | { | ||
43 | struct screen_info *si = &screen_info; | ||
44 | struct simplefb_platform_data mode; | ||
45 | struct platform_device *pd; | ||
46 | const char *name; | ||
47 | bool compatible; | ||
48 | int ret; | ||
49 | |||
50 | /* try to create a simple-framebuffer device */ | ||
51 | compatible = parse_mode(si, &mode); | ||
52 | if (compatible) { | ||
53 | ret = create_simplefb(si, &mode); | ||
54 | if (!ret) | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | /* if the FB is incompatible, create a legacy framebuffer device */ | ||
59 | if (si->orig_video_isVGA == VIDEO_TYPE_EFI) | ||
60 | name = "efi-framebuffer"; | ||
61 | else if (si->orig_video_isVGA == VIDEO_TYPE_VLFB) | ||
62 | name = "vesa-framebuffer"; | ||
63 | else | ||
64 | name = "platform-framebuffer"; | ||
65 | |||
66 | pd = platform_device_register_resndata(NULL, name, 0, | ||
67 | NULL, 0, si, sizeof(*si)); | ||
68 | return IS_ERR(pd) ? PTR_ERR(pd) : 0; | ||
69 | } | ||
70 | |||
71 | device_initcall(sysfb_init); | ||
diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c new file mode 100644 index 000000000000..22513e96b012 --- /dev/null +++ b/arch/x86/kernel/sysfb_simplefb.c | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * Generic System Framebuffers on x86 | ||
3 | * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the Free | ||
7 | * Software Foundation; either version 2 of the License, or (at your option) | ||
8 | * any later version. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * simple-framebuffer probing | ||
13 | * Try to convert "screen_info" into a "simple-framebuffer" compatible mode. | ||
14 | * If the mode is incompatible, we return "false" and let the caller create | ||
15 | * legacy nodes instead. | ||
16 | */ | ||
17 | |||
18 | #include <linux/err.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/platform_data/simplefb.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/screen_info.h> | ||
25 | #include <asm/sysfb.h> | ||
26 | |||
27 | static const char simplefb_resname[] = "BOOTFB"; | ||
28 | static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; | ||
29 | |||
30 | /* try parsing x86 screen_info into a simple-framebuffer mode struct */ | ||
31 | __init bool parse_mode(const struct screen_info *si, | ||
32 | struct simplefb_platform_data *mode) | ||
33 | { | ||
34 | const struct simplefb_format *f; | ||
35 | __u8 type; | ||
36 | unsigned int i; | ||
37 | |||
38 | type = si->orig_video_isVGA; | ||
39 | if (type != VIDEO_TYPE_VLFB && type != VIDEO_TYPE_EFI) | ||
40 | return false; | ||
41 | |||
42 | for (i = 0; i < ARRAY_SIZE(formats); ++i) { | ||
43 | f = &formats[i]; | ||
44 | if (si->lfb_depth == f->bits_per_pixel && | ||
45 | si->red_size == f->red.length && | ||
46 | si->red_pos == f->red.offset && | ||
47 | si->green_size == f->green.length && | ||
48 | si->green_pos == f->green.offset && | ||
49 | si->blue_size == f->blue.length && | ||
50 | si->blue_pos == f->blue.offset && | ||
51 | si->rsvd_size == f->transp.length && | ||
52 | si->rsvd_pos == f->transp.offset) { | ||
53 | mode->format = f->name; | ||
54 | mode->width = si->lfb_width; | ||
55 | mode->height = si->lfb_height; | ||
56 | mode->stride = si->lfb_linelength; | ||
57 | return true; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | return false; | ||
62 | } | ||
63 | |||
64 | __init int create_simplefb(const struct screen_info *si, | ||
65 | const struct simplefb_platform_data *mode) | ||
66 | { | ||
67 | struct platform_device *pd; | ||
68 | struct resource res; | ||
69 | unsigned long len; | ||
70 | |||
71 | /* don't use lfb_size as it may contain the whole VMEM instead of only | ||
72 | * the part that is occupied by the framebuffer */ | ||
73 | len = mode->height * mode->stride; | ||
74 | len = PAGE_ALIGN(len); | ||
75 | if (len > si->lfb_size << 16) { | ||
76 | printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n"); | ||
77 | return -EINVAL; | ||
78 | } | ||
79 | |||
80 | /* setup IORESOURCE_MEM as framebuffer memory */ | ||
81 | memset(&res, 0, sizeof(res)); | ||
82 | res.flags = IORESOURCE_MEM; | ||
83 | res.name = simplefb_resname; | ||
84 | res.start = si->lfb_base; | ||
85 | res.end = si->lfb_base + len - 1; | ||
86 | if (res.end <= res.start) | ||
87 | return -EINVAL; | ||
88 | |||
89 | pd = platform_device_register_resndata(NULL, "simple-framebuffer", 0, | ||
90 | &res, 1, mode, sizeof(*mode)); | ||
91 | if (IS_ERR(pd)) | ||
92 | return PTR_ERR(pd); | ||
93 | |||
94 | return 0; | ||
95 | } | ||