Blame view

drivers/video/efifb.c 17.8 KB
7c83172b9   Huang, Ying   x86_64 EFI boot s...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * Framebuffer driver for EFI/UEFI based system
   *
   * (c) 2006 Edgar Hucek <gimli@dark-green.com>
   * Original efi driver written by Gerd Knorr <kraxel@goldbach.in-berlin.de>
   *
   */
  
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/fb.h>
  #include <linux/platform_device.h>
  #include <linux/screen_info.h>
7c08c9ae0   Peter Jones   efifb/imacfb cons...
15
  #include <linux/dmi.h>
85a00d9bb   Peter Jones   efifb: check that...
16
  #include <linux/pci.h>
7c83172b9   Huang, Ying   x86_64 EFI boot s...
17
  #include <video/vga.h>
da0241f12   Andy Lutomirski   efifb: Fix mismat...
18
  static bool request_mem_succeeded = false;
ec81c9cc2   Henrik Kretzschmar   fbdev: efifb: sec...
19
  static struct fb_var_screeninfo efifb_defined __devinitdata = {
7c83172b9   Huang, Ying   x86_64 EFI boot s...
20
21
22
23
24
25
26
27
28
  	.activate		= FB_ACTIVATE_NOW,
  	.height			= -1,
  	.width			= -1,
  	.right_margin		= 32,
  	.upper_margin		= 16,
  	.lower_margin		= 4,
  	.vsync_len		= 4,
  	.vmode			= FB_VMODE_NONINTERLACED,
  };
ec81c9cc2   Henrik Kretzschmar   fbdev: efifb: sec...
29
  static struct fb_fix_screeninfo efifb_fix __devinitdata = {
7c83172b9   Huang, Ying   x86_64 EFI boot s...
30
31
32
33
34
  	.id			= "EFI VGA",
  	.type			= FB_TYPE_PACKED_PIXELS,
  	.accel			= FB_ACCEL_NONE,
  	.visual			= FB_VISUAL_TRUECOLOR,
  };
7c08c9ae0   Peter Jones   efifb/imacfb cons...
35
36
37
38
39
  enum {
  	M_I17,		/* 17-Inch iMac */
  	M_I20,		/* 20-Inch iMac */
  	M_I20_SR,	/* 20-Inch iMac (Santa Rosa) */
  	M_I24,		/* 24-Inch iMac */
a5757c2a4   Luke Macken   efifb: support th...
40
41
42
  	M_I24_8_1,	/* 24-Inch iMac, 8,1th gen */
  	M_I24_10_1,	/* 24-Inch iMac, 10,1th gen */
  	M_I27_11_1,	/* 27-Inch iMac, 11,1th gen */
7c08c9ae0   Peter Jones   efifb/imacfb cons...
43
  	M_MINI,		/* Mac Mini */
a5757c2a4   Luke Macken   efifb: support th...
44
45
  	M_MINI_3_1,	/* Mac Mini, 3,1th gen */
  	M_MINI_4_1,	/* Mac Mini, 4,1th gen */
7c08c9ae0   Peter Jones   efifb/imacfb cons...
46
47
48
  	M_MB,		/* MacBook */
  	M_MB_2,		/* MacBook, 2nd rev. */
  	M_MB_3,		/* MacBook, 3rd rev. */
a5757c2a4   Luke Macken   efifb: support th...
49
50
51
  	M_MB_5_1,	/* MacBook, 5th rev. */
  	M_MB_6_1,	/* MacBook, 6th rev. */
  	M_MB_7_1,	/* MacBook, 7th rev. */
7c08c9ae0   Peter Jones   efifb/imacfb cons...
52
53
  	M_MB_SR,	/* MacBook, 2nd gen, (Santa Rosa) */
  	M_MBA,		/* MacBook Air */
97fb85076   Matthew Garrett   efifb: Add overri...
54
  	M_MBA_3,	/* Macbook Air, 3rd rev */
7c08c9ae0   Peter Jones   efifb/imacfb cons...
55
56
  	M_MBP,		/* MacBook Pro */
  	M_MBP_2,	/* MacBook Pro 2nd gen */
a5757c2a4   Luke Macken   efifb: support th...
57
  	M_MBP_2_2,	/* MacBook Pro 2,2nd gen */
7c08c9ae0   Peter Jones   efifb/imacfb cons...
58
59
  	M_MBP_SR,	/* MacBook Pro (Santa Rosa) */
  	M_MBP_4,	/* MacBook Pro, 4th gen */
8a3bdfe6c   Thomas Gerlach   drivers/video/efi...
60
  	M_MBP_5_1,    /* MacBook Pro, 5,1th gen */
a5757c2a4   Luke Macken   efifb: support th...
61
62
63
64
65
  	M_MBP_5_2,	/* MacBook Pro, 5,2th gen */
  	M_MBP_5_3,	/* MacBook Pro, 5,3rd gen */
  	M_MBP_6_1,	/* MacBook Pro, 6,1th gen */
  	M_MBP_6_2,	/* MacBook Pro, 6,2th gen */
  	M_MBP_7_1,	/* MacBook Pro, 7,1th gen */
e9c5db0b8   Davidlohr Bueso   efifb: support AM...
66
  	M_MBP_8_2,	/* MacBook Pro, 8,2nd gen */
7c08c9ae0   Peter Jones   efifb/imacfb cons...
67
68
  	M_UNKNOWN	/* placeholder */
  };
47dfe51f8   Peter Jones   efifb: Support ov...
69
70
71
72
73
  #define OVERRIDE_NONE	0x0
  #define OVERRIDE_BASE	0x1
  #define OVERRIDE_STRIDE	0x2
  #define OVERRIDE_HEIGHT	0x4
  #define OVERRIDE_WIDTH	0x8
7c08c9ae0   Peter Jones   efifb/imacfb cons...
74
75
76
77
78
79
  static struct efifb_dmi_info {
  	char *optname;
  	unsigned long base;
  	int stride;
  	int width;
  	int height;
47dfe51f8   Peter Jones   efifb: Support ov...
80
  	int flags;
ec81c9cc2   Henrik Kretzschmar   fbdev: efifb: sec...
81
  } dmi_list[] __initdata = {
47dfe51f8   Peter Jones   efifb: Support ov...
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  	[M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
  	[M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050, OVERRIDE_NONE }, /* guess */
  	[M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050, OVERRIDE_NONE },
  	[M_I24] = { "i24", 0x80010000, 2048 * 4, 1920, 1200, OVERRIDE_NONE }, /* guess */
  	[M_I24_8_1] = { "imac8", 0xc0060000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
  	[M_I24_10_1] = { "imac10", 0xc0010000, 2048 * 4, 1920, 1080, OVERRIDE_NONE },
  	[M_I27_11_1] = { "imac11", 0xc0010000, 2560 * 4, 2560, 1440, OVERRIDE_NONE },
  	[M_MINI]= { "mini", 0x80000000, 2048 * 4, 1024, 768, OVERRIDE_NONE },
  	[M_MINI_3_1] = { "mini31", 0x40010000, 1024 * 4, 1024, 768, OVERRIDE_NONE },
  	[M_MINI_4_1] = { "mini41", 0xc0010000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
  	[M_MB] = { "macbook", 0x80000000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
  	[M_MB_5_1] = { "macbook51", 0x80010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
  	[M_MB_6_1] = { "macbook61", 0x80010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
  	[M_MB_7_1] = { "macbook71", 0x80010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
  	[M_MBA] = { "mba", 0x80000000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
97fb85076   Matthew Garrett   efifb: Add overri...
97
98
  	/* 11" Macbook Air 3,1 passes the wrong stride */
  	[M_MBA_3] = { "mba3", 0, 2048 * 4, 0, 0, OVERRIDE_STRIDE },
47dfe51f8   Peter Jones   efifb: Support ov...
99
100
101
102
103
104
105
106
107
108
109
110
111
  	[M_MBP] = { "mbp", 0x80010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
  	[M_MBP_2] = { "mbp2", 0, 0, 0, 0, OVERRIDE_NONE }, /* placeholder */
  	[M_MBP_2_2] = { "mbp22", 0x80010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
  	[M_MBP_SR] = { "mbp3", 0x80030000, 2048 * 4, 1440, 900, OVERRIDE_NONE },
  	[M_MBP_4] = { "mbp4", 0xc0060000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
  	[M_MBP_5_1] = { "mbp51", 0xc0010000, 2048 * 4, 1440, 900, OVERRIDE_NONE },
  	[M_MBP_5_2] = { "mbp52", 0xc0010000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
  	[M_MBP_5_3] = { "mbp53", 0xd0010000, 2048 * 4, 1440, 900, OVERRIDE_NONE },
  	[M_MBP_6_1] = { "mbp61", 0x90030000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
  	[M_MBP_6_2] = { "mbp62", 0x90030000, 2048 * 4, 1680, 1050, OVERRIDE_NONE },
  	[M_MBP_7_1] = { "mbp71", 0xc0010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
  	[M_MBP_8_2] = { "mbp82", 0x90010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
  	[M_UNKNOWN] = { NULL, 0, 0, 0, 0, OVERRIDE_NONE }
7c08c9ae0   Peter Jones   efifb/imacfb cons...
112
113
114
115
116
117
118
119
120
  };
  
  static int set_system(const struct dmi_system_id *id);
  
  #define EFIFB_DMI_SYSTEM_ID(vendor, name, enumid)		\
  	{ set_system, name, {					\
  		DMI_MATCH(DMI_BIOS_VENDOR, vendor),		\
  		DMI_MATCH(DMI_PRODUCT_NAME, name) },		\
  	  &dmi_list[enumid] }
ec81c9cc2   Henrik Kretzschmar   fbdev: efifb: sec...
121
  static const struct dmi_system_id dmi_system_table[] __initconst = {
7c08c9ae0   Peter Jones   efifb/imacfb cons...
122
123
124
125
126
127
128
129
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17),
  	/* At least one of these two will be right; maybe both? */
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac5,1", M_I20),
  	/* At least one of these two will be right; maybe both? */
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac6,1", M_I24),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac6,1", M_I24),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac7,1", M_I20_SR),
a5757c2a4   Luke Macken   efifb: support th...
130
131
132
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac8,1", M_I24_8_1),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac10,1", M_I24_10_1),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac11,1", M_I27_11_1),
7c08c9ae0   Peter Jones   efifb/imacfb cons...
133
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "Macmini1,1", M_MINI),
a5757c2a4   Luke Macken   efifb: support th...
134
135
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "Macmini3,1", M_MINI_3_1),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "Macmini4,1", M_MINI_4_1),
7c08c9ae0   Peter Jones   efifb/imacfb cons...
136
137
138
139
140
141
142
143
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook1,1", M_MB),
  	/* At least one of these two will be right; maybe both? */
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook2,1", M_MB),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook2,1", M_MB),
  	/* At least one of these two will be right; maybe both? */
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook3,1", M_MB),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook3,1", M_MB),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook4,1", M_MB),
a5757c2a4   Luke Macken   efifb: support th...
144
145
146
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook5,1", M_MB_5_1),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook6,1", M_MB_6_1),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook7,1", M_MB_7_1),
7c08c9ae0   Peter Jones   efifb/imacfb cons...
147
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookAir1,1", M_MBA),
97fb85076   Matthew Garrett   efifb: Add overri...
148
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookAir3,1", M_MBA_3),
7c08c9ae0   Peter Jones   efifb/imacfb cons...
149
150
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro1,1", M_MBP),
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro2,1", M_MBP_2),
a5757c2a4   Luke Macken   efifb: support th...
151
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro2,2", M_MBP_2_2),
7c08c9ae0   Peter Jones   efifb/imacfb cons...
152
153
154
155
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro2,1", M_MBP_2),
  	EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro3,1", M_MBP_SR),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro3,1", M_MBP_SR),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro4,1", M_MBP_4),
8a3bdfe6c   Thomas Gerlach   drivers/video/efi...
156
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,1", M_MBP_5_1),
a5757c2a4   Luke Macken   efifb: support th...
157
158
159
160
161
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,2", M_MBP_5_2),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,3", M_MBP_5_3),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro6,1", M_MBP_6_1),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro6,2", M_MBP_6_2),
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro7,1", M_MBP_7_1),
e9c5db0b8   Davidlohr Bueso   efifb: support AM...
162
  	EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro8,2", M_MBP_8_2),
7c08c9ae0   Peter Jones   efifb/imacfb cons...
163
164
  	{},
  };
47dfe51f8   Peter Jones   efifb: Support ov...
165
166
167
168
169
170
171
172
  #define choose_value(dmivalue, fwvalue, field, flags) ({	\
  		typeof(fwvalue) _ret_ = fwvalue;		\
  		if ((flags) & (field))				\
  			_ret_ = dmivalue;			\
  		else if ((fwvalue) == 0)			\
  			_ret_ = dmivalue;			\
  		_ret_;						\
  	})
7c08c9ae0   Peter Jones   efifb/imacfb cons...
173
174
175
  static int set_system(const struct dmi_system_id *id)
  {
  	struct efifb_dmi_info *info = id->driver_data;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
176

47dfe51f8   Peter Jones   efifb: Support ov...
177
178
179
  	if (info->base == 0 && info->height == 0 && info->width == 0
  			&& info->stride == 0)
  		return 0;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
180
181
  
  	/* Trust the bootloader over the DMI tables */
85a00d9bb   Peter Jones   efifb: check that...
182
183
184
185
186
  	if (screen_info.lfb_base == 0) {
  #if defined(CONFIG_PCI)
  		struct pci_dev *dev = NULL;
  		int found_bar = 0;
  #endif
47dfe51f8   Peter Jones   efifb: Support ov...
187
188
189
190
  		if (info->base) {
  			screen_info.lfb_base = choose_value(info->base,
  				screen_info.lfb_base, OVERRIDE_BASE,
  				info->flags);
7c08c9ae0   Peter Jones   efifb/imacfb cons...
191

85a00d9bb   Peter Jones   efifb: check that...
192
  #if defined(CONFIG_PCI)
47dfe51f8   Peter Jones   efifb: Support ov...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
  			/* make sure that the address in the table is actually
  			 * on a VGA device's PCI BAR */
  
  			for_each_pci_dev(dev) {
  				int i;
  				if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
  					continue;
  				for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
  					resource_size_t start, end;
  
  					start = pci_resource_start(dev, i);
  					if (start == 0)
  						break;
  					end = pci_resource_end(dev, i);
  					if (screen_info.lfb_base >= start &&
  					    screen_info.lfb_base < end) {
  						found_bar = 1;
  					}
85a00d9bb   Peter Jones   efifb: check that...
211
212
  				}
  			}
47dfe51f8   Peter Jones   efifb: Support ov...
213
214
  			if (!found_bar)
  				screen_info.lfb_base = 0;
85a00d9bb   Peter Jones   efifb: check that...
215
  #endif
47dfe51f8   Peter Jones   efifb: Support ov...
216
  		}
85a00d9bb   Peter Jones   efifb: check that...
217
218
  	}
  	if (screen_info.lfb_base) {
47dfe51f8   Peter Jones   efifb: Support ov...
219
220
221
222
223
224
225
226
227
  		screen_info.lfb_linelength = choose_value(info->stride,
  			screen_info.lfb_linelength, OVERRIDE_STRIDE,
  			info->flags);
  		screen_info.lfb_width = choose_value(info->width,
  			screen_info.lfb_width, OVERRIDE_WIDTH,
  			info->flags);
  		screen_info.lfb_height = choose_value(info->height,
  			screen_info.lfb_height, OVERRIDE_HEIGHT,
  			info->flags);
85a00d9bb   Peter Jones   efifb: check that...
228
229
230
231
232
233
234
235
236
  		if (screen_info.orig_video_isVGA == 0)
  			screen_info.orig_video_isVGA = VIDEO_TYPE_EFI;
  	} else {
  		screen_info.lfb_linelength = 0;
  		screen_info.lfb_width = 0;
  		screen_info.lfb_height = 0;
  		screen_info.orig_video_isVGA = 0;
  		return 0;
  	}
47dfe51f8   Peter Jones   efifb: Support ov...
237

dc3e5b6a6   Konstantin Khlebnikov   efifb: fix int to...
238
  	printk(KERN_INFO "efifb: dmi detected %s - framebuffer at 0x%08x "
47dfe51f8   Peter Jones   efifb: Support ov...
239
240
  			 "(%dx%d, stride %d)
  ", id->ident,
dc3e5b6a6   Konstantin Khlebnikov   efifb: fix int to...
241
  			 screen_info.lfb_base, screen_info.lfb_width,
47dfe51f8   Peter Jones   efifb: Support ov...
242
  			 screen_info.lfb_height, screen_info.lfb_linelength);
85a00d9bb   Peter Jones   efifb: check that...
243
  	return 1;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
244
  }
7c83172b9   Huang, Ying   x86_64 EFI boot s...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
  			   unsigned blue, unsigned transp,
  			   struct fb_info *info)
  {
  	/*
  	 *  Set a single color register. The values supplied are
  	 *  already rounded down to the hardware's capabilities
  	 *  (according to the entries in the `var' structure). Return
  	 *  != 0 for invalid regno.
  	 */
  
  	if (regno >= info->cmap.len)
  		return 1;
  
  	if (regno < 16) {
  		red   >>= 8;
  		green >>= 8;
  		blue  >>= 8;
  		((u32 *)(info->pseudo_palette))[regno] =
  			(red   << info->var.red.offset)   |
  			(green << info->var.green.offset) |
  			(blue  << info->var.blue.offset);
  	}
  	return 0;
  }
89f3f2199   Marcin Slusarz   efifb: fix frameb...
270
271
272
273
  static void efifb_destroy(struct fb_info *info)
  {
  	if (info->screen_base)
  		iounmap(info->screen_base);
da0241f12   Andy Lutomirski   efifb: Fix mismat...
274
275
276
  	if (request_mem_succeeded)
  		release_mem_region(info->apertures->ranges[0].base,
  				   info->apertures->ranges[0].size);
89f3f2199   Marcin Slusarz   efifb: fix frameb...
277
278
  	framebuffer_release(info);
  }
7c83172b9   Huang, Ying   x86_64 EFI boot s...
279
280
  static struct fb_ops efifb_ops = {
  	.owner		= THIS_MODULE,
89f3f2199   Marcin Slusarz   efifb: fix frameb...
281
  	.fb_destroy	= efifb_destroy,
7c83172b9   Huang, Ying   x86_64 EFI boot s...
282
283
284
285
286
  	.fb_setcolreg	= efifb_setcolreg,
  	.fb_fillrect	= cfb_fillrect,
  	.fb_copyarea	= cfb_copyarea,
  	.fb_imageblit	= cfb_imageblit,
  };
7c08c9ae0   Peter Jones   efifb/imacfb cons...
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  static int __init efifb_setup(char *options)
  {
  	char *this_opt;
  	int i;
  
  	if (!options || !*options)
  		return 0;
  
  	while ((this_opt = strsep(&options, ",")) != NULL) {
  		if (!*this_opt) continue;
  
  		for (i = 0; i < M_UNKNOWN; i++) {
  			if (!strcmp(this_opt, dmi_list[i].optname) &&
  					dmi_list[i].base != 0) {
  				screen_info.lfb_base = dmi_list[i].base;
  				screen_info.lfb_linelength = dmi_list[i].stride;
  				screen_info.lfb_width = dmi_list[i].width;
  				screen_info.lfb_height = dmi_list[i].height;
  			}
  		}
  		if (!strncmp(this_opt, "base:", 5))
  			screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0);
  		else if (!strncmp(this_opt, "stride:", 7))
  			screen_info.lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4;
  		else if (!strncmp(this_opt, "height:", 7))
  			screen_info.lfb_height = simple_strtoul(this_opt+7, NULL, 0);
  		else if (!strncmp(this_opt, "width:", 6))
  			screen_info.lfb_width = simple_strtoul(this_opt+6, NULL, 0);
  	}
  	return 0;
  }
bb8b26627   Andy Lutomirski   efifb: Disallow m...
318
  static int __init efifb_probe(struct platform_device *dev)
7c83172b9   Huang, Ying   x86_64 EFI boot s...
319
320
321
322
323
324
  {
  	struct fb_info *info;
  	int err;
  	unsigned int size_vmode;
  	unsigned int size_remap;
  	unsigned int size_total;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
325

7c08c9ae0   Peter Jones   efifb/imacfb cons...
326
327
328
329
  	if (!screen_info.lfb_depth)
  		screen_info.lfb_depth = 32;
  	if (!screen_info.pages)
  		screen_info.pages = 1;
133bb070e   Matthew Garrett   efifb: exit if fr...
330
331
332
333
334
335
336
  	if (!screen_info.lfb_base) {
  		printk(KERN_DEBUG "efifb: invalid framebuffer address
  ");
  		return -ENODEV;
  	}
  	printk(KERN_INFO "efifb: probing for efifb
  ");
7c08c9ae0   Peter Jones   efifb/imacfb cons...
337
338
339
340
341
342
343
344
345
346
347
348
  
  	/* just assume they're all unset if any are */
  	if (!screen_info.blue_size) {
  		screen_info.blue_size = 8;
  		screen_info.blue_pos = 0;
  		screen_info.green_size = 8;
  		screen_info.green_pos = 8;
  		screen_info.red_size = 8;
  		screen_info.red_pos = 16;
  		screen_info.rsvd_size = 8;
  		screen_info.rsvd_pos = 24;
  	}
7c83172b9   Huang, Ying   x86_64 EFI boot s...
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
  
  	efifb_fix.smem_start = screen_info.lfb_base;
  	efifb_defined.bits_per_pixel = screen_info.lfb_depth;
  	efifb_defined.xres = screen_info.lfb_width;
  	efifb_defined.yres = screen_info.lfb_height;
  	efifb_fix.line_length = screen_info.lfb_linelength;
  
  	/*   size_vmode -- that is the amount of memory needed for the
  	 *                 used video mode, i.e. the minimum amount of
  	 *                 memory we need. */
  	size_vmode = efifb_defined.yres * efifb_fix.line_length;
  
  	/*   size_total -- all video memory we have. Used for
  	 *                 entries, ressource allocation and bounds
  	 *                 checking. */
  	size_total = screen_info.lfb_size;
  	if (size_total < size_vmode)
  		size_total = size_vmode;
  
  	/*   size_remap -- the amount of video memory we are going to
  	 *                 use for efifb.  With modern cards it is no
  	 *                 option to simply use size_total as that
  	 *                 wastes plenty of kernel address space. */
  	size_remap  = size_vmode * 2;
7c83172b9   Huang, Ying   x86_64 EFI boot s...
373
374
  	if (size_remap > size_total)
  		size_remap = size_total;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
375
376
  	if (size_remap % PAGE_SIZE)
  		size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
377
  	efifb_fix.smem_len = size_remap;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
378
  	if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
da0241f12   Andy Lutomirski   efifb: Fix mismat...
379
  		request_mem_succeeded = true;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
380
  	} else {
7c83172b9   Huang, Ying   x86_64 EFI boot s...
381
382
383
384
385
386
  		/* We cannot make this fatal. Sometimes this comes from magic
  		   spaces our resource handlers simply don't know about */
  		printk(KERN_WARNING
  		       "efifb: cannot reserve video memory at 0x%lx
  ",
  			efifb_fix.smem_start);
7c08c9ae0   Peter Jones   efifb/imacfb cons...
387
  	}
7c83172b9   Huang, Ying   x86_64 EFI boot s...
388
389
390
  
  	info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
  	if (!info) {
7c08c9ae0   Peter Jones   efifb/imacfb cons...
391
392
  		printk(KERN_ERR "efifb: cannot allocate framebuffer
  ");
7c83172b9   Huang, Ying   x86_64 EFI boot s...
393
394
395
396
397
  		err = -ENOMEM;
  		goto err_release_mem;
  	}
  	info->pseudo_palette = info->par;
  	info->par = NULL;
1471ca9aa   Marcin Slusarz   fbdev: allow pass...
398
399
400
401
402
403
404
  	info->apertures = alloc_apertures(1);
  	if (!info->apertures) {
  		err = -ENOMEM;
  		goto err_release_fb;
  	}
  	info->apertures->ranges[0].base = efifb_fix.smem_start;
  	info->apertures->ranges[0].size = size_remap;
4410f3910   Dave Airlie   fbdev: add suppor...
405

3c004b4f7   Andy Lutomirski   efifb: Enable wri...
406
  	info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
407
408
409
410
411
412
  	if (!info->screen_base) {
  		printk(KERN_ERR "efifb: abort, cannot ioremap video memory "
  				"0x%x @ 0x%lx
  ",
  			efifb_fix.smem_len, efifb_fix.smem_start);
  		err = -EIO;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
413
  		goto err_release_fb;
7c83172b9   Huang, Ying   x86_64 EFI boot s...
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
  	}
  
  	printk(KERN_INFO "efifb: framebuffer at 0x%lx, mapped to 0x%p, "
  	       "using %dk, total %dk
  ",
  	       efifb_fix.smem_start, info->screen_base,
  	       size_remap/1024, size_total/1024);
  	printk(KERN_INFO "efifb: mode is %dx%dx%d, linelength=%d, pages=%d
  ",
  	       efifb_defined.xres, efifb_defined.yres,
  	       efifb_defined.bits_per_pixel, efifb_fix.line_length,
  	       screen_info.pages);
  
  	efifb_defined.xres_virtual = efifb_defined.xres;
  	efifb_defined.yres_virtual = efifb_fix.smem_len /
  					efifb_fix.line_length;
  	printk(KERN_INFO "efifb: scrolling: redraw
  ");
  	efifb_defined.yres_virtual = efifb_defined.yres;
  
  	/* some dummy values for timing to make fbset happy */
  	efifb_defined.pixclock     = 10000000 / efifb_defined.xres *
  					1000 / efifb_defined.yres;
  	efifb_defined.left_margin  = (efifb_defined.xres / 8) & 0xf8;
  	efifb_defined.hsync_len    = (efifb_defined.xres / 8) & 0xf8;
  
  	efifb_defined.red.offset    = screen_info.red_pos;
  	efifb_defined.red.length    = screen_info.red_size;
  	efifb_defined.green.offset  = screen_info.green_pos;
  	efifb_defined.green.length  = screen_info.green_size;
  	efifb_defined.blue.offset   = screen_info.blue_pos;
  	efifb_defined.blue.length   = screen_info.blue_size;
  	efifb_defined.transp.offset = screen_info.rsvd_pos;
  	efifb_defined.transp.length = screen_info.rsvd_size;
  
  	printk(KERN_INFO "efifb: %s: "
  	       "size=%d:%d:%d:%d, shift=%d:%d:%d:%d
  ",
  	       "Truecolor",
  	       screen_info.rsvd_size,
  	       screen_info.red_size,
  	       screen_info.green_size,
  	       screen_info.blue_size,
  	       screen_info.rsvd_pos,
  	       screen_info.red_pos,
  	       screen_info.green_pos,
  	       screen_info.blue_pos);
  
  	efifb_fix.ypanstep  = 0;
  	efifb_fix.ywrapstep = 0;
  
  	info->fbops = &efifb_ops;
  	info->var = efifb_defined;
  	info->fix = efifb_fix;
4410f3910   Dave Airlie   fbdev: add suppor...
468
  	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;
7c83172b9   Huang, Ying   x86_64 EFI boot s...
469

7c08c9ae0   Peter Jones   efifb/imacfb cons...
470
471
472
  	if ((err = fb_alloc_cmap(&info->cmap, 256, 0)) < 0) {
  		printk(KERN_ERR "efifb: cannot allocate colormap
  ");
7c83172b9   Huang, Ying   x86_64 EFI boot s...
473
474
  		goto err_unmap;
  	}
7c08c9ae0   Peter Jones   efifb/imacfb cons...
475
476
477
  	if ((err = register_framebuffer(info)) < 0) {
  		printk(KERN_ERR "efifb: cannot register framebuffer
  ");
7c83172b9   Huang, Ying   x86_64 EFI boot s...
478
479
480
481
  		goto err_fb_dealoc;
  	}
  	printk(KERN_INFO "fb%d: %s frame buffer device
  ",
7c08c9ae0   Peter Jones   efifb/imacfb cons...
482
  		info->node, info->fix.id);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
483
484
485
486
487
488
  	return 0;
  
  err_fb_dealoc:
  	fb_dealloc_cmap(&info->cmap);
  err_unmap:
  	iounmap(info->screen_base);
7c08c9ae0   Peter Jones   efifb/imacfb cons...
489
  err_release_fb:
7c83172b9   Huang, Ying   x86_64 EFI boot s...
490
491
  	framebuffer_release(info);
  err_release_mem:
da0241f12   Andy Lutomirski   efifb: Fix mismat...
492
  	if (request_mem_succeeded)
7c08c9ae0   Peter Jones   efifb/imacfb cons...
493
  		release_mem_region(efifb_fix.smem_start, size_total);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
494
495
496
497
  	return err;
  }
  
  static struct platform_driver efifb_driver = {
7c83172b9   Huang, Ying   x86_64 EFI boot s...
498
499
500
501
502
503
504
505
506
507
508
509
  	.driver	= {
  		.name	= "efifb",
  	},
  };
  
  static struct platform_device efifb_device = {
  	.name	= "efifb",
  };
  
  static int __init efifb_init(void)
  {
  	int ret;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
510
  	char *option = NULL;
7c83172b9   Huang, Ying   x86_64 EFI boot s...
511

e14a685df   Brian Maly   efifb: dmi set vi...
512
  	dmi_check_system(dmi_system_table);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
513
514
  	if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
  		return -ENODEV;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
515
516
517
518
519
520
521
522
523
524
525
  
  	if (fb_get_options("efifb", &option))
  		return -ENODEV;
  	efifb_setup(option);
  
  	/* We don't get linelength from UGA Draw Protocol, only from
  	 * EFI Graphics Protocol.  So if it's not in DMI, and it's not
  	 * passed in from the user, we really can't use the framebuffer.
  	 */
  	if (!screen_info.lfb_linelength)
  		return -ENODEV;
7c83172b9   Huang, Ying   x86_64 EFI boot s...
526

bb8b26627   Andy Lutomirski   efifb: Disallow m...
527
528
529
  	ret = platform_device_register(&efifb_device);
  	if (ret)
  		return ret;
7c83172b9   Huang, Ying   x86_64 EFI boot s...
530

bb8b26627   Andy Lutomirski   efifb: Disallow m...
531
532
533
534
535
536
537
  	/*
  	 * This is not just an optimization.  We will interfere
  	 * with a real driver if we get reprobed, so don't allow
  	 * it.
  	 */
  	ret = platform_driver_probe(&efifb_driver, efifb_probe);
  	if (ret) {
e6b8480cd   Wanlong Gao   efifb: Fix call t...
538
  		platform_device_unregister(&efifb_device);
bb8b26627   Andy Lutomirski   efifb: Disallow m...
539
  		return ret;
7c83172b9   Huang, Ying   x86_64 EFI boot s...
540
  	}
bb8b26627   Andy Lutomirski   efifb: Disallow m...
541

7c83172b9   Huang, Ying   x86_64 EFI boot s...
542
543
544
545
546
  	return ret;
  }
  module_init(efifb_init);
  
  MODULE_LICENSE("GPL");