diff options
author | Peter Jones <pjones@redhat.com> | 2008-10-16 01:03:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-16 14:21:44 -0400 |
commit | 7c08c9ae0c145807c0dae4a55f240fa3d4fd5262 (patch) | |
tree | 63e2edd3731e0293b4190a82391e2d3f27e61983 /drivers/video | |
parent | f928ac0a9810d46c8ba3bde7c969984340da9f5d (diff) |
efifb/imacfb consolidation + hardware support
Remove imacfb entirely, merging its DMI table into the (otherwise very
similar) efifb driver. This also adds hardware support for many of the
newer Intel Apple hardware. This has been fairly well tested; we've been
shipping it in Fedora for some time.
Signed-off-by: Peter Jones <pjones@redhat.com>
Cc: Krzysztof Helt <krzysztof.h1@poczta.fm>
Cc: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
Cc: Jaya Kumar <jayakumar.lkml@gmail.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Maciej W. Rozycki <macro@linux-mips.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/Kconfig | 15 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/efifb.c | 191 | ||||
-rw-r--r-- | drivers/video/imacfb.c | 376 |
4 files changed, 184 insertions, 399 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e63e5648e0a6..3b296e410f91 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -689,23 +689,14 @@ config FB_VESA | |||
689 | 689 | ||
690 | config FB_EFI | 690 | config FB_EFI |
691 | bool "EFI-based Framebuffer Support" | 691 | bool "EFI-based Framebuffer Support" |
692 | depends on (FB = y) && X86 | ||
693 | select FB_CFB_FILLRECT | ||
694 | select FB_CFB_COPYAREA | ||
695 | select FB_CFB_IMAGEBLIT | ||
696 | help | ||
697 | This is the EFI frame buffer device driver. If the firmware on | ||
698 | your platform is UEFI2.0, select Y to add support for | ||
699 | Graphics Output Protocol for early console messages to appear. | ||
700 | |||
701 | config FB_IMAC | ||
702 | bool "Intel-based Macintosh Framebuffer Support" | ||
703 | depends on (FB = y) && X86 && EFI | 692 | depends on (FB = y) && X86 && EFI |
704 | select FB_CFB_FILLRECT | 693 | select FB_CFB_FILLRECT |
705 | select FB_CFB_COPYAREA | 694 | select FB_CFB_COPYAREA |
706 | select FB_CFB_IMAGEBLIT | 695 | select FB_CFB_IMAGEBLIT |
707 | help | 696 | help |
708 | This is the frame buffer device driver for the Intel-based Macintosh | 697 | This is the EFI frame buffer device driver. If the firmware on |
698 | your platform is EFI 1.10 or UEFI 2.0, select Y to add support for | ||
699 | using the EFI framebuffer as your console. | ||
709 | 700 | ||
710 | config FB_N411 | 701 | config FB_N411 |
711 | tristate "N411 Apollo/Hecuba devkit support" | 702 | tristate "N411 Apollo/Hecuba devkit support" |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index dcae8d402fb2..2bc94d8eb3c8 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -125,7 +125,6 @@ obj-$(CONFIG_FB_CARMINE) += carminefb.o | |||
125 | # Platform or fallback drivers go here | 125 | # Platform or fallback drivers go here |
126 | obj-$(CONFIG_FB_UVESA) += uvesafb.o | 126 | obj-$(CONFIG_FB_UVESA) += uvesafb.o |
127 | obj-$(CONFIG_FB_VESA) += vesafb.o | 127 | obj-$(CONFIG_FB_VESA) += vesafb.o |
128 | obj-$(CONFIG_FB_IMAC) += imacfb.o | ||
129 | obj-$(CONFIG_FB_EFI) += efifb.o | 128 | obj-$(CONFIG_FB_EFI) += efifb.o |
130 | obj-$(CONFIG_FB_VGA16) += vga16fb.o | 129 | obj-$(CONFIG_FB_VGA16) += vga16fb.o |
131 | obj-$(CONFIG_FB_OF) += offb.o | 130 | obj-$(CONFIG_FB_OF) += offb.o |
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c index bd779ae44b1e..daf9b81878a4 100644 --- a/drivers/video/efifb.c +++ b/drivers/video/efifb.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/fb.h> | 12 | #include <linux/fb.h> |
13 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
14 | #include <linux/screen_info.h> | 14 | #include <linux/screen_info.h> |
15 | #include <linux/dmi.h> | ||
15 | 16 | ||
16 | #include <video/vga.h> | 17 | #include <video/vga.h> |
17 | 18 | ||
@@ -33,6 +34,105 @@ static struct fb_fix_screeninfo efifb_fix __initdata = { | |||
33 | .visual = FB_VISUAL_TRUECOLOR, | 34 | .visual = FB_VISUAL_TRUECOLOR, |
34 | }; | 35 | }; |
35 | 36 | ||
37 | enum { | ||
38 | M_I17, /* 17-Inch iMac */ | ||
39 | M_I20, /* 20-Inch iMac */ | ||
40 | M_I20_SR, /* 20-Inch iMac (Santa Rosa) */ | ||
41 | M_I24, /* 24-Inch iMac */ | ||
42 | M_MINI, /* Mac Mini */ | ||
43 | M_MB, /* MacBook */ | ||
44 | M_MB_2, /* MacBook, 2nd rev. */ | ||
45 | M_MB_3, /* MacBook, 3rd rev. */ | ||
46 | M_MB_SR, /* MacBook, 2nd gen, (Santa Rosa) */ | ||
47 | M_MBA, /* MacBook Air */ | ||
48 | M_MBP, /* MacBook Pro */ | ||
49 | M_MBP_2, /* MacBook Pro 2nd gen */ | ||
50 | M_MBP_SR, /* MacBook Pro (Santa Rosa) */ | ||
51 | M_MBP_4, /* MacBook Pro, 4th gen */ | ||
52 | M_UNKNOWN /* placeholder */ | ||
53 | }; | ||
54 | |||
55 | static struct efifb_dmi_info { | ||
56 | char *optname; | ||
57 | unsigned long base; | ||
58 | int stride; | ||
59 | int width; | ||
60 | int height; | ||
61 | } dmi_list[] = { | ||
62 | [M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900 }, | ||
63 | [M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050 }, /* guess */ | ||
64 | [M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050 }, | ||
65 | [M_I24] = { "i24", 0x80010000, 2048 * 4, 1920, 1200 }, /* guess */ | ||
66 | [M_MINI]= { "mini", 0x80000000, 2048 * 4, 1024, 768 }, | ||
67 | [M_MB] = { "macbook", 0x80000000, 2048 * 4, 1280, 800 }, | ||
68 | [M_MBA] = { "mba", 0x80000000, 2048 * 4, 1280, 800 }, | ||
69 | [M_MBP] = { "mbp", 0x80010000, 1472 * 4, 1440, 900 }, | ||
70 | [M_MBP_2] = { "mbp2", 0, 0, 0, 0 }, /* placeholder */ | ||
71 | [M_MBP_SR] = { "mbp3", 0x80030000, 2048 * 4, 1440, 900 }, | ||
72 | [M_MBP_4] = { "mbp4", 0xc0060000, 2048 * 4, 1920, 1200 }, | ||
73 | [M_UNKNOWN] = { NULL, 0, 0, 0, 0 } | ||
74 | }; | ||
75 | |||
76 | static int set_system(const struct dmi_system_id *id); | ||
77 | |||
78 | #define EFIFB_DMI_SYSTEM_ID(vendor, name, enumid) \ | ||
79 | { set_system, name, { \ | ||
80 | DMI_MATCH(DMI_BIOS_VENDOR, vendor), \ | ||
81 | DMI_MATCH(DMI_PRODUCT_NAME, name) }, \ | ||
82 | &dmi_list[enumid] } | ||
83 | |||
84 | static struct dmi_system_id __initdata dmi_system_table[] = { | ||
85 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17), | ||
86 | /* At least one of these two will be right; maybe both? */ | ||
87 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20), | ||
88 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac5,1", M_I20), | ||
89 | /* At least one of these two will be right; maybe both? */ | ||
90 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac6,1", M_I24), | ||
91 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac6,1", M_I24), | ||
92 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac7,1", M_I20_SR), | ||
93 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "Macmini1,1", M_MINI), | ||
94 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook1,1", M_MB), | ||
95 | /* At least one of these two will be right; maybe both? */ | ||
96 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook2,1", M_MB), | ||
97 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook2,1", M_MB), | ||
98 | /* At least one of these two will be right; maybe both? */ | ||
99 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook3,1", M_MB), | ||
100 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook3,1", M_MB), | ||
101 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook4,1", M_MB), | ||
102 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookAir1,1", M_MBA), | ||
103 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro1,1", M_MBP), | ||
104 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro2,1", M_MBP_2), | ||
105 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro2,1", M_MBP_2), | ||
106 | EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro3,1", M_MBP_SR), | ||
107 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro3,1", M_MBP_SR), | ||
108 | EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro4,1", M_MBP_4), | ||
109 | {}, | ||
110 | }; | ||
111 | |||
112 | static int set_system(const struct dmi_system_id *id) | ||
113 | { | ||
114 | struct efifb_dmi_info *info = id->driver_data; | ||
115 | if (info->base == 0) | ||
116 | return -ENODEV; | ||
117 | |||
118 | printk(KERN_INFO "efifb: dmi detected %s - framebuffer at %p " | ||
119 | "(%dx%d, stride %d)\n", id->ident, | ||
120 | (void *)info->base, info->width, info->height, | ||
121 | info->stride); | ||
122 | |||
123 | /* Trust the bootloader over the DMI tables */ | ||
124 | if (screen_info.lfb_base == 0) | ||
125 | screen_info.lfb_base = info->base; | ||
126 | if (screen_info.lfb_linelength == 0) | ||
127 | screen_info.lfb_linelength = info->stride; | ||
128 | if (screen_info.lfb_width == 0) | ||
129 | screen_info.lfb_width = info->width; | ||
130 | if (screen_info.lfb_height == 0) | ||
131 | screen_info.lfb_height = info->height; | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
36 | static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green, | 136 | static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green, |
37 | unsigned blue, unsigned transp, | 137 | unsigned blue, unsigned transp, |
38 | struct fb_info *info) | 138 | struct fb_info *info) |
@@ -67,6 +167,38 @@ static struct fb_ops efifb_ops = { | |||
67 | .fb_imageblit = cfb_imageblit, | 167 | .fb_imageblit = cfb_imageblit, |
68 | }; | 168 | }; |
69 | 169 | ||
170 | static int __init efifb_setup(char *options) | ||
171 | { | ||
172 | char *this_opt; | ||
173 | int i; | ||
174 | |||
175 | if (!options || !*options) | ||
176 | return 0; | ||
177 | |||
178 | while ((this_opt = strsep(&options, ",")) != NULL) { | ||
179 | if (!*this_opt) continue; | ||
180 | |||
181 | for (i = 0; i < M_UNKNOWN; i++) { | ||
182 | if (!strcmp(this_opt, dmi_list[i].optname) && | ||
183 | dmi_list[i].base != 0) { | ||
184 | screen_info.lfb_base = dmi_list[i].base; | ||
185 | screen_info.lfb_linelength = dmi_list[i].stride; | ||
186 | screen_info.lfb_width = dmi_list[i].width; | ||
187 | screen_info.lfb_height = dmi_list[i].height; | ||
188 | } | ||
189 | } | ||
190 | if (!strncmp(this_opt, "base:", 5)) | ||
191 | screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0); | ||
192 | else if (!strncmp(this_opt, "stride:", 7)) | ||
193 | screen_info.lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4; | ||
194 | else if (!strncmp(this_opt, "height:", 7)) | ||
195 | screen_info.lfb_height = simple_strtoul(this_opt+7, NULL, 0); | ||
196 | else if (!strncmp(this_opt, "width:", 6)) | ||
197 | screen_info.lfb_width = simple_strtoul(this_opt+6, NULL, 0); | ||
198 | } | ||
199 | return 0; | ||
200 | } | ||
201 | |||
70 | static int __init efifb_probe(struct platform_device *dev) | 202 | static int __init efifb_probe(struct platform_device *dev) |
71 | { | 203 | { |
72 | struct fb_info *info; | 204 | struct fb_info *info; |
@@ -74,6 +206,26 @@ static int __init efifb_probe(struct platform_device *dev) | |||
74 | unsigned int size_vmode; | 206 | unsigned int size_vmode; |
75 | unsigned int size_remap; | 207 | unsigned int size_remap; |
76 | unsigned int size_total; | 208 | unsigned int size_total; |
209 | int request_succeeded = 0; | ||
210 | |||
211 | printk(KERN_INFO "efifb: probing for efifb\n"); | ||
212 | |||
213 | if (!screen_info.lfb_depth) | ||
214 | screen_info.lfb_depth = 32; | ||
215 | if (!screen_info.pages) | ||
216 | screen_info.pages = 1; | ||
217 | |||
218 | /* just assume they're all unset if any are */ | ||
219 | if (!screen_info.blue_size) { | ||
220 | screen_info.blue_size = 8; | ||
221 | screen_info.blue_pos = 0; | ||
222 | screen_info.green_size = 8; | ||
223 | screen_info.green_pos = 8; | ||
224 | screen_info.red_size = 8; | ||
225 | screen_info.red_pos = 16; | ||
226 | screen_info.rsvd_size = 8; | ||
227 | screen_info.rsvd_pos = 24; | ||
228 | } | ||
77 | 229 | ||
78 | efifb_fix.smem_start = screen_info.lfb_base; | 230 | efifb_fix.smem_start = screen_info.lfb_base; |
79 | efifb_defined.bits_per_pixel = screen_info.lfb_depth; | 231 | efifb_defined.bits_per_pixel = screen_info.lfb_depth; |
@@ -98,21 +250,25 @@ static int __init efifb_probe(struct platform_device *dev) | |||
98 | * option to simply use size_total as that | 250 | * option to simply use size_total as that |
99 | * wastes plenty of kernel address space. */ | 251 | * wastes plenty of kernel address space. */ |
100 | size_remap = size_vmode * 2; | 252 | size_remap = size_vmode * 2; |
101 | if (size_remap < size_vmode) | ||
102 | size_remap = size_vmode; | ||
103 | if (size_remap > size_total) | 253 | if (size_remap > size_total) |
104 | size_remap = size_total; | 254 | size_remap = size_total; |
255 | if (size_remap % PAGE_SIZE) | ||
256 | size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE); | ||
105 | efifb_fix.smem_len = size_remap; | 257 | efifb_fix.smem_len = size_remap; |
106 | 258 | ||
107 | if (!request_mem_region(efifb_fix.smem_start, size_total, "efifb")) | 259 | if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) { |
260 | request_succeeded = 1; | ||
261 | } else { | ||
108 | /* We cannot make this fatal. Sometimes this comes from magic | 262 | /* We cannot make this fatal. Sometimes this comes from magic |
109 | spaces our resource handlers simply don't know about */ | 263 | spaces our resource handlers simply don't know about */ |
110 | printk(KERN_WARNING | 264 | printk(KERN_WARNING |
111 | "efifb: cannot reserve video memory at 0x%lx\n", | 265 | "efifb: cannot reserve video memory at 0x%lx\n", |
112 | efifb_fix.smem_start); | 266 | efifb_fix.smem_start); |
267 | } | ||
113 | 268 | ||
114 | info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); | 269 | info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); |
115 | if (!info) { | 270 | if (!info) { |
271 | printk(KERN_ERR "efifb: cannot allocate framebuffer\n"); | ||
116 | err = -ENOMEM; | 272 | err = -ENOMEM; |
117 | goto err_release_mem; | 273 | goto err_release_mem; |
118 | } | 274 | } |
@@ -125,7 +281,7 @@ static int __init efifb_probe(struct platform_device *dev) | |||
125 | "0x%x @ 0x%lx\n", | 281 | "0x%x @ 0x%lx\n", |
126 | efifb_fix.smem_len, efifb_fix.smem_start); | 282 | efifb_fix.smem_len, efifb_fix.smem_start); |
127 | err = -EIO; | 283 | err = -EIO; |
128 | goto err_unmap; | 284 | goto err_release_fb; |
129 | } | 285 | } |
130 | 286 | ||
131 | printk(KERN_INFO "efifb: framebuffer at 0x%lx, mapped to 0x%p, " | 287 | printk(KERN_INFO "efifb: framebuffer at 0x%lx, mapped to 0x%p, " |
@@ -178,25 +334,27 @@ static int __init efifb_probe(struct platform_device *dev) | |||
178 | info->fix = efifb_fix; | 334 | info->fix = efifb_fix; |
179 | info->flags = FBINFO_FLAG_DEFAULT; | 335 | info->flags = FBINFO_FLAG_DEFAULT; |
180 | 336 | ||
181 | if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { | 337 | if ((err = fb_alloc_cmap(&info->cmap, 256, 0)) < 0) { |
182 | err = -ENOMEM; | 338 | printk(KERN_ERR "efifb: cannot allocate colormap\n"); |
183 | goto err_unmap; | 339 | goto err_unmap; |
184 | } | 340 | } |
185 | if (register_framebuffer(info) < 0) { | 341 | if ((err = register_framebuffer(info)) < 0) { |
186 | err = -EINVAL; | 342 | printk(KERN_ERR "efifb: cannot register framebuffer\n"); |
187 | goto err_fb_dealoc; | 343 | goto err_fb_dealoc; |
188 | } | 344 | } |
189 | printk(KERN_INFO "fb%d: %s frame buffer device\n", | 345 | printk(KERN_INFO "fb%d: %s frame buffer device\n", |
190 | info->node, info->fix.id); | 346 | info->node, info->fix.id); |
191 | return 0; | 347 | return 0; |
192 | 348 | ||
193 | err_fb_dealoc: | 349 | err_fb_dealoc: |
194 | fb_dealloc_cmap(&info->cmap); | 350 | fb_dealloc_cmap(&info->cmap); |
195 | err_unmap: | 351 | err_unmap: |
196 | iounmap(info->screen_base); | 352 | iounmap(info->screen_base); |
353 | err_release_fb: | ||
197 | framebuffer_release(info); | 354 | framebuffer_release(info); |
198 | err_release_mem: | 355 | err_release_mem: |
199 | release_mem_region(efifb_fix.smem_start, size_total); | 356 | if (request_succeeded) |
357 | release_mem_region(efifb_fix.smem_start, size_total); | ||
200 | return err; | 358 | return err; |
201 | } | 359 | } |
202 | 360 | ||
@@ -214,9 +372,22 @@ static struct platform_device efifb_device = { | |||
214 | static int __init efifb_init(void) | 372 | static int __init efifb_init(void) |
215 | { | 373 | { |
216 | int ret; | 374 | int ret; |
375 | char *option = NULL; | ||
217 | 376 | ||
218 | if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) | 377 | if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) |
219 | return -ENODEV; | 378 | return -ENODEV; |
379 | dmi_check_system(dmi_system_table); | ||
380 | |||
381 | if (fb_get_options("efifb", &option)) | ||
382 | return -ENODEV; | ||
383 | efifb_setup(option); | ||
384 | |||
385 | /* We don't get linelength from UGA Draw Protocol, only from | ||
386 | * EFI Graphics Protocol. So if it's not in DMI, and it's not | ||
387 | * passed in from the user, we really can't use the framebuffer. | ||
388 | */ | ||
389 | if (!screen_info.lfb_linelength) | ||
390 | return -ENODEV; | ||
220 | 391 | ||
221 | ret = platform_driver_register(&efifb_driver); | 392 | ret = platform_driver_register(&efifb_driver); |
222 | 393 | ||
diff --git a/drivers/video/imacfb.c b/drivers/video/imacfb.c index 9366ef2bb5f7..e69de29bb2d1 100644 --- a/drivers/video/imacfb.c +++ b/drivers/video/imacfb.c | |||
@@ -1,376 +0,0 @@ | |||
1 | /* | ||
2 | * framebuffer driver for Intel Based Mac's | ||
3 | * | ||
4 | * (c) 2006 Edgar Hucek <gimli@dark-green.com> | ||
5 | * Original imac driver written by Gerd Knorr <kraxel@goldbach.in-berlin.de> | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #include <linux/delay.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/fb.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/ioport.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/screen_info.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/dmi.h> | ||
22 | #include <linux/efi.h> | ||
23 | |||
24 | #include <asm/io.h> | ||
25 | |||
26 | #include <video/vga.h> | ||
27 | |||
28 | typedef enum _MAC_TYPE { | ||
29 | M_I17, | ||
30 | M_I20, | ||
31 | M_MINI, | ||
32 | M_MACBOOK, | ||
33 | M_UNKNOWN | ||
34 | } MAC_TYPE; | ||
35 | |||
36 | /* --------------------------------------------------------------------- */ | ||
37 | |||
38 | static struct fb_var_screeninfo imacfb_defined __initdata = { | ||
39 | .activate = FB_ACTIVATE_NOW, | ||
40 | .height = -1, | ||
41 | .width = -1, | ||
42 | .right_margin = 32, | ||
43 | .upper_margin = 16, | ||
44 | .lower_margin = 4, | ||
45 | .vsync_len = 4, | ||
46 | .vmode = FB_VMODE_NONINTERLACED, | ||
47 | }; | ||
48 | |||
49 | static struct fb_fix_screeninfo imacfb_fix __initdata = { | ||
50 | .id = "IMAC VGA", | ||
51 | .type = FB_TYPE_PACKED_PIXELS, | ||
52 | .accel = FB_ACCEL_NONE, | ||
53 | .visual = FB_VISUAL_TRUECOLOR, | ||
54 | }; | ||
55 | |||
56 | static int inverse; | ||
57 | static int model = M_UNKNOWN; | ||
58 | static int manual_height; | ||
59 | static int manual_width; | ||
60 | |||
61 | static int set_system(const struct dmi_system_id *id) | ||
62 | { | ||
63 | printk(KERN_INFO "imacfb: %s detected - set system to %ld\n", | ||
64 | id->ident, (long)id->driver_data); | ||
65 | |||
66 | model = (long)id->driver_data; | ||
67 | |||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static struct dmi_system_id __initdata dmi_system_table[] = { | ||
72 | { set_system, "iMac4,1", { | ||
73 | DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."), | ||
74 | DMI_MATCH(DMI_PRODUCT_NAME,"iMac4,1") }, (void*)M_I17}, | ||
75 | { set_system, "MacBookPro1,1", { | ||
76 | DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."), | ||
77 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro1,1") }, (void*)M_I17}, | ||
78 | { set_system, "MacBook1,1", { | ||
79 | DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."), | ||
80 | DMI_MATCH(DMI_PRODUCT_NAME,"MacBook1,1")}, (void *)M_MACBOOK}, | ||
81 | { set_system, "Macmini1,1", { | ||
82 | DMI_MATCH(DMI_BIOS_VENDOR,"Apple Computer, Inc."), | ||
83 | DMI_MATCH(DMI_PRODUCT_NAME,"Macmini1,1")}, (void *)M_MINI}, | ||
84 | {}, | ||
85 | }; | ||
86 | |||
87 | #define DEFAULT_FB_MEM 1024*1024*16 | ||
88 | |||
89 | /* --------------------------------------------------------------------- */ | ||
90 | |||
91 | static int imacfb_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
92 | unsigned blue, unsigned transp, | ||
93 | struct fb_info *info) | ||
94 | { | ||
95 | /* | ||
96 | * Set a single color register. The values supplied are | ||
97 | * already rounded down to the hardware's capabilities | ||
98 | * (according to the entries in the `var' structure). Return | ||
99 | * != 0 for invalid regno. | ||
100 | */ | ||
101 | |||
102 | if (regno >= info->cmap.len) | ||
103 | return 1; | ||
104 | |||
105 | if (regno < 16) { | ||
106 | red >>= 8; | ||
107 | green >>= 8; | ||
108 | blue >>= 8; | ||
109 | ((u32 *)(info->pseudo_palette))[regno] = | ||
110 | (red << info->var.red.offset) | | ||
111 | (green << info->var.green.offset) | | ||
112 | (blue << info->var.blue.offset); | ||
113 | } | ||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static struct fb_ops imacfb_ops = { | ||
118 | .owner = THIS_MODULE, | ||
119 | .fb_setcolreg = imacfb_setcolreg, | ||
120 | .fb_fillrect = cfb_fillrect, | ||
121 | .fb_copyarea = cfb_copyarea, | ||
122 | .fb_imageblit = cfb_imageblit, | ||
123 | }; | ||
124 | |||
125 | static int __init imacfb_setup(char *options) | ||
126 | { | ||
127 | char *this_opt; | ||
128 | |||
129 | if (!options || !*options) | ||
130 | return 0; | ||
131 | |||
132 | while ((this_opt = strsep(&options, ",")) != NULL) { | ||
133 | if (!*this_opt) continue; | ||
134 | |||
135 | if (!strcmp(this_opt, "inverse")) | ||
136 | inverse = 1; | ||
137 | else if (!strcmp(this_opt, "i17")) | ||
138 | model = M_I17; | ||
139 | else if (!strcmp(this_opt, "i20")) | ||
140 | model = M_I20; | ||
141 | else if (!strcmp(this_opt, "mini")) | ||
142 | model = M_MINI; | ||
143 | else if (!strcmp(this_opt, "macbook")) | ||
144 | model = M_MACBOOK; | ||
145 | else if (!strncmp(this_opt, "height:", 7)) | ||
146 | manual_height = simple_strtoul(this_opt+7, NULL, 0); | ||
147 | else if (!strncmp(this_opt, "width:", 6)) | ||
148 | manual_width = simple_strtoul(this_opt+6, NULL, 0); | ||
149 | } | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int __init imacfb_probe(struct platform_device *dev) | ||
154 | { | ||
155 | struct fb_info *info; | ||
156 | int err; | ||
157 | unsigned int size_vmode; | ||
158 | unsigned int size_remap; | ||
159 | unsigned int size_total; | ||
160 | |||
161 | screen_info.lfb_depth = 32; | ||
162 | screen_info.lfb_size = DEFAULT_FB_MEM / 0x10000; | ||
163 | screen_info.pages=1; | ||
164 | screen_info.blue_size = 8; | ||
165 | screen_info.blue_pos = 0; | ||
166 | screen_info.green_size = 8; | ||
167 | screen_info.green_pos = 8; | ||
168 | screen_info.red_size = 8; | ||
169 | screen_info.red_pos = 16; | ||
170 | screen_info.rsvd_size = 8; | ||
171 | screen_info.rsvd_pos = 24; | ||
172 | |||
173 | switch (model) { | ||
174 | case M_I17: | ||
175 | screen_info.lfb_width = 1440; | ||
176 | screen_info.lfb_height = 900; | ||
177 | screen_info.lfb_linelength = 1472 * 4; | ||
178 | screen_info.lfb_base = 0x80010000; | ||
179 | break; | ||
180 | case M_I20: | ||
181 | screen_info.lfb_width = 1680; | ||
182 | screen_info.lfb_height = 1050; | ||
183 | screen_info.lfb_linelength = 1728 * 4; | ||
184 | screen_info.lfb_base = 0x80010000; | ||
185 | break; | ||
186 | case M_MINI: | ||
187 | screen_info.lfb_width = 1024; | ||
188 | screen_info.lfb_height = 768; | ||
189 | screen_info.lfb_linelength = 2048 * 4; | ||
190 | screen_info.lfb_base = 0x80000000; | ||
191 | break; | ||
192 | case M_MACBOOK: | ||
193 | screen_info.lfb_width = 1280; | ||
194 | screen_info.lfb_height = 800; | ||
195 | screen_info.lfb_linelength = 2048 * 4; | ||
196 | screen_info.lfb_base = 0x80000000; | ||
197 | break; | ||
198 | } | ||
199 | |||
200 | /* if the user wants to manually specify height/width, | ||
201 | we will override the defaults */ | ||
202 | /* TODO: eventually get auto-detection working */ | ||
203 | if (manual_height > 0) | ||
204 | screen_info.lfb_height = manual_height; | ||
205 | if (manual_width > 0) | ||
206 | screen_info.lfb_width = manual_width; | ||
207 | |||
208 | imacfb_fix.smem_start = screen_info.lfb_base; | ||
209 | imacfb_defined.bits_per_pixel = screen_info.lfb_depth; | ||
210 | imacfb_defined.xres = screen_info.lfb_width; | ||
211 | imacfb_defined.yres = screen_info.lfb_height; | ||
212 | imacfb_fix.line_length = screen_info.lfb_linelength; | ||
213 | |||
214 | /* size_vmode -- that is the amount of memory needed for the | ||
215 | * used video mode, i.e. the minimum amount of | ||
216 | * memory we need. */ | ||
217 | size_vmode = imacfb_defined.yres * imacfb_fix.line_length; | ||
218 | |||
219 | /* size_total -- all video memory we have. Used for | ||
220 | * entries, ressource allocation and bounds | ||
221 | * checking. */ | ||
222 | size_total = screen_info.lfb_size * 65536; | ||
223 | if (size_total < size_vmode) | ||
224 | size_total = size_vmode; | ||
225 | |||
226 | /* size_remap -- the amount of video memory we are going to | ||
227 | * use for imacfb. With modern cards it is no | ||
228 | * option to simply use size_total as that | ||
229 | * wastes plenty of kernel address space. */ | ||
230 | size_remap = size_vmode * 2; | ||
231 | if (size_remap < size_vmode) | ||
232 | size_remap = size_vmode; | ||
233 | if (size_remap > size_total) | ||
234 | size_remap = size_total; | ||
235 | imacfb_fix.smem_len = size_remap; | ||
236 | |||
237 | if (!request_mem_region(imacfb_fix.smem_start, size_total, "imacfb")) { | ||
238 | printk(KERN_WARNING | ||
239 | "imacfb: cannot reserve video memory at 0x%lx\n", | ||
240 | imacfb_fix.smem_start); | ||
241 | /* We cannot make this fatal. Sometimes this comes from magic | ||
242 | spaces our resource handlers simply don't know about */ | ||
243 | } | ||
244 | |||
245 | info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev); | ||
246 | if (!info) { | ||
247 | err = -ENOMEM; | ||
248 | goto err_release_mem; | ||
249 | } | ||
250 | info->pseudo_palette = info->par; | ||
251 | info->par = NULL; | ||
252 | |||
253 | info->screen_base = ioremap(imacfb_fix.smem_start, imacfb_fix.smem_len); | ||
254 | if (!info->screen_base) { | ||
255 | printk(KERN_ERR "imacfb: abort, cannot ioremap video memory " | ||
256 | "0x%x @ 0x%lx\n", | ||
257 | imacfb_fix.smem_len, imacfb_fix.smem_start); | ||
258 | err = -EIO; | ||
259 | goto err_unmap; | ||
260 | } | ||
261 | |||
262 | printk(KERN_INFO "imacfb: framebuffer at 0x%lx, mapped to 0x%p, " | ||
263 | "using %dk, total %dk\n", | ||
264 | imacfb_fix.smem_start, info->screen_base, | ||
265 | size_remap/1024, size_total/1024); | ||
266 | printk(KERN_INFO "imacfb: mode is %dx%dx%d, linelength=%d, pages=%d\n", | ||
267 | imacfb_defined.xres, imacfb_defined.yres, | ||
268 | imacfb_defined.bits_per_pixel, imacfb_fix.line_length, | ||
269 | screen_info.pages); | ||
270 | |||
271 | imacfb_defined.xres_virtual = imacfb_defined.xres; | ||
272 | imacfb_defined.yres_virtual = imacfb_fix.smem_len / | ||
273 | imacfb_fix.line_length; | ||
274 | printk(KERN_INFO "imacfb: scrolling: redraw\n"); | ||
275 | imacfb_defined.yres_virtual = imacfb_defined.yres; | ||
276 | |||
277 | /* some dummy values for timing to make fbset happy */ | ||
278 | imacfb_defined.pixclock = 10000000 / imacfb_defined.xres * | ||
279 | 1000 / imacfb_defined.yres; | ||
280 | imacfb_defined.left_margin = (imacfb_defined.xres / 8) & 0xf8; | ||
281 | imacfb_defined.hsync_len = (imacfb_defined.xres / 8) & 0xf8; | ||
282 | |||
283 | imacfb_defined.red.offset = screen_info.red_pos; | ||
284 | imacfb_defined.red.length = screen_info.red_size; | ||
285 | imacfb_defined.green.offset = screen_info.green_pos; | ||
286 | imacfb_defined.green.length = screen_info.green_size; | ||
287 | imacfb_defined.blue.offset = screen_info.blue_pos; | ||
288 | imacfb_defined.blue.length = screen_info.blue_size; | ||
289 | imacfb_defined.transp.offset = screen_info.rsvd_pos; | ||
290 | imacfb_defined.transp.length = screen_info.rsvd_size; | ||
291 | |||
292 | printk(KERN_INFO "imacfb: %s: " | ||
293 | "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n", | ||
294 | "Truecolor", | ||
295 | screen_info.rsvd_size, | ||
296 | screen_info.red_size, | ||
297 | screen_info.green_size, | ||
298 | screen_info.blue_size, | ||
299 | screen_info.rsvd_pos, | ||
300 | screen_info.red_pos, | ||
301 | screen_info.green_pos, | ||
302 | screen_info.blue_pos); | ||
303 | |||
304 | imacfb_fix.ypanstep = 0; | ||
305 | imacfb_fix.ywrapstep = 0; | ||
306 | |||
307 | /* request failure does not faze us, as vgacon probably has this | ||
308 | * region already (FIXME) */ | ||
309 | request_region(0x3c0, 32, "imacfb"); | ||
310 | |||
311 | info->fbops = &imacfb_ops; | ||
312 | info->var = imacfb_defined; | ||
313 | info->fix = imacfb_fix; | ||
314 | info->flags = FBINFO_FLAG_DEFAULT; | ||
315 | |||
316 | if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { | ||
317 | err = -ENOMEM; | ||
318 | goto err_unmap; | ||
319 | } | ||
320 | if (register_framebuffer(info)<0) { | ||
321 | err = -EINVAL; | ||
322 | goto err_fb_dealoc; | ||
323 | } | ||
324 | printk(KERN_INFO "fb%d: %s frame buffer device\n", | ||
325 | info->node, info->fix.id); | ||
326 | return 0; | ||
327 | |||
328 | err_fb_dealoc: | ||
329 | fb_dealloc_cmap(&info->cmap); | ||
330 | err_unmap: | ||
331 | iounmap(info->screen_base); | ||
332 | framebuffer_release(info); | ||
333 | err_release_mem: | ||
334 | release_mem_region(imacfb_fix.smem_start, size_total); | ||
335 | return err; | ||
336 | } | ||
337 | |||
338 | static struct platform_driver imacfb_driver = { | ||
339 | .probe = imacfb_probe, | ||
340 | .driver = { | ||
341 | .name = "imacfb", | ||
342 | }, | ||
343 | }; | ||
344 | |||
345 | static struct platform_device imacfb_device = { | ||
346 | .name = "imacfb", | ||
347 | }; | ||
348 | |||
349 | static int __init imacfb_init(void) | ||
350 | { | ||
351 | int ret; | ||
352 | char *option = NULL; | ||
353 | |||
354 | if (!efi_enabled) | ||
355 | return -ENODEV; | ||
356 | if (!dmi_check_system(dmi_system_table)) | ||
357 | return -ENODEV; | ||
358 | if (model == M_UNKNOWN) | ||
359 | return -ENODEV; | ||
360 | |||
361 | if (fb_get_options("imacfb", &option)) | ||
362 | return -ENODEV; | ||
363 | |||
364 | imacfb_setup(option); | ||
365 | ret = platform_driver_register(&imacfb_driver); | ||
366 | |||
367 | if (!ret) { | ||
368 | ret = platform_device_register(&imacfb_device); | ||
369 | if (ret) | ||
370 | platform_driver_unregister(&imacfb_driver); | ||
371 | } | ||
372 | return ret; | ||
373 | } | ||
374 | module_init(imacfb_init); | ||
375 | |||
376 | MODULE_LICENSE("GPL"); | ||