Blame view

drivers/video/efifb.c 9.62 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>
2995e5062   David Herrmann   x86: sysfb: move ...
18
  #include <asm/sysfb.h>
7c83172b9   Huang, Ying   x86_64 EFI boot s...
19

da0241f12   Andy Lutomirski   efifb: Fix mismat...
20
  static bool request_mem_succeeded = false;
b4aa01630   Matthew Garrett   efifb: Implement ...
21
  static struct pci_dev *default_vga;
48c68c4f1   Greg Kroah-Hartman   Drivers: video: r...
22
  static struct fb_var_screeninfo efifb_defined = {
7c83172b9   Huang, Ying   x86_64 EFI boot s...
23
24
25
26
27
28
29
30
31
  	.activate		= FB_ACTIVATE_NOW,
  	.height			= -1,
  	.width			= -1,
  	.right_margin		= 32,
  	.upper_margin		= 16,
  	.lower_margin		= 4,
  	.vsync_len		= 4,
  	.vmode			= FB_VMODE_NONINTERLACED,
  };
48c68c4f1   Greg Kroah-Hartman   Drivers: video: r...
32
  static struct fb_fix_screeninfo efifb_fix = {
7c83172b9   Huang, Ying   x86_64 EFI boot s...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  	.id			= "EFI VGA",
  	.type			= FB_TYPE_PACKED_PIXELS,
  	.accel			= FB_ACCEL_NONE,
  	.visual			= FB_VISUAL_TRUECOLOR,
  };
  
  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...
64
65
66
67
  static void efifb_destroy(struct fb_info *info)
  {
  	if (info->screen_base)
  		iounmap(info->screen_base);
da0241f12   Andy Lutomirski   efifb: Fix mismat...
68
69
70
  	if (request_mem_succeeded)
  		release_mem_region(info->apertures->ranges[0].base,
  				   info->apertures->ranges[0].size);
907355a97   Peter Jones   Release efifb's c...
71
  	fb_dealloc_cmap(&info->cmap);
89f3f2199   Marcin Slusarz   efifb: fix frameb...
72
73
  	framebuffer_release(info);
  }
7c83172b9   Huang, Ying   x86_64 EFI boot s...
74
75
  static struct fb_ops efifb_ops = {
  	.owner		= THIS_MODULE,
89f3f2199   Marcin Slusarz   efifb: fix frameb...
76
  	.fb_destroy	= efifb_destroy,
7c83172b9   Huang, Ying   x86_64 EFI boot s...
77
78
79
80
81
  	.fb_setcolreg	= efifb_setcolreg,
  	.fb_fillrect	= cfb_fillrect,
  	.fb_copyarea	= cfb_copyarea,
  	.fb_imageblit	= cfb_imageblit,
  };
b4aa01630   Matthew Garrett   efifb: Implement ...
82
83
84
85
  struct pci_dev *vga_default_device(void)
  {
  	return default_vga;
  }
1b23170a8   Matthew Garrett   vga: fix build wh...
86
  EXPORT_SYMBOL_GPL(vga_default_device);
b4aa01630   Matthew Garrett   efifb: Implement ...
87
88
89
90
  void vga_set_default_device(struct pci_dev *pdev)
  {
  	default_vga = pdev;
  }
e6816a8d8   David Herrmann   fbdev: efifb: bin...
91
  static int efifb_setup(char *options)
7c08c9ae0   Peter Jones   efifb/imacfb cons...
92
93
94
  {
  	char *this_opt;
  	int i;
b4aa01630   Matthew Garrett   efifb: Implement ...
95
  	struct pci_dev *dev = NULL;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
96

b4aa01630   Matthew Garrett   efifb: Implement ...
97
98
99
  	if (options && *options) {
  		while ((this_opt = strsep(&options, ",")) != NULL) {
  			if (!*this_opt) continue;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
100

b4aa01630   Matthew Garrett   efifb: Implement ...
101
  			for (i = 0; i < M_UNKNOWN; i++) {
55aa42f2e   James Bates   efifb: prevent nu...
102
103
  				if (efifb_dmi_list[i].base != 0 &&
  				    !strcmp(this_opt, efifb_dmi_list[i].optname)) {
2995e5062   David Herrmann   x86: sysfb: move ...
104
105
106
107
  					screen_info.lfb_base = efifb_dmi_list[i].base;
  					screen_info.lfb_linelength = efifb_dmi_list[i].stride;
  					screen_info.lfb_width = efifb_dmi_list[i].width;
  					screen_info.lfb_height = efifb_dmi_list[i].height;
b4aa01630   Matthew Garrett   efifb: Implement ...
108
  				}
7c08c9ae0   Peter Jones   efifb/imacfb cons...
109
  			}
b4aa01630   Matthew Garrett   efifb: Implement ...
110
111
112
113
114
115
116
117
  			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);
7c08c9ae0   Peter Jones   efifb/imacfb cons...
118
  		}
7c08c9ae0   Peter Jones   efifb/imacfb cons...
119
  	}
b4aa01630   Matthew Garrett   efifb: Implement ...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  
  	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;
  
  			if (!(pci_resource_flags(dev, i) & IORESOURCE_MEM))
  				continue;
  
  			start = pci_resource_start(dev, i);
  			end  = pci_resource_end(dev, i);
  
  			if (!start || !end)
  				continue;
  
  			if (screen_info.lfb_base >= start &&
  			    (screen_info.lfb_base + screen_info.lfb_size) < end)
  				default_vga = dev;
  		}
  	}
7c08c9ae0   Peter Jones   efifb/imacfb cons...
144
145
  	return 0;
  }
e6816a8d8   David Herrmann   fbdev: efifb: bin...
146
  static int efifb_probe(struct platform_device *dev)
7c83172b9   Huang, Ying   x86_64 EFI boot s...
147
148
149
150
151
152
  {
  	struct fb_info *info;
  	int err;
  	unsigned int size_vmode;
  	unsigned int size_remap;
  	unsigned int size_total;
e6816a8d8   David Herrmann   fbdev: efifb: bin...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  	char *option = NULL;
  
  	if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
  		return -ENODEV;
  
  	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;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
168

7c08c9ae0   Peter Jones   efifb/imacfb cons...
169
170
171
172
  	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...
173
174
175
176
177
178
179
  	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...
180
181
182
183
184
185
186
187
188
189
190
191
  
  	/* 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...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  
  	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...
216
217
  	if (size_remap > size_total)
  		size_remap = size_total;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
218
219
  	if (size_remap % PAGE_SIZE)
  		size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
220
  	efifb_fix.smem_len = size_remap;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
221
  	if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
da0241f12   Andy Lutomirski   efifb: Fix mismat...
222
  		request_mem_succeeded = true;
7c08c9ae0   Peter Jones   efifb/imacfb cons...
223
  	} else {
7c83172b9   Huang, Ying   x86_64 EFI boot s...
224
225
226
227
228
229
  		/* 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...
230
  	}
7c83172b9   Huang, Ying   x86_64 EFI boot s...
231
232
233
  
  	info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
  	if (!info) {
7c08c9ae0   Peter Jones   efifb/imacfb cons...
234
235
  		printk(KERN_ERR "efifb: cannot allocate framebuffer
  ");
7c83172b9   Huang, Ying   x86_64 EFI boot s...
236
237
238
239
240
  		err = -ENOMEM;
  		goto err_release_mem;
  	}
  	info->pseudo_palette = info->par;
  	info->par = NULL;
1471ca9aa   Marcin Slusarz   fbdev: allow pass...
241
242
243
244
245
246
247
  	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...
248

3c004b4f7   Andy Lutomirski   efifb: Enable wri...
249
  	info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
250
251
252
253
254
255
  	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...
256
  		goto err_release_fb;
7c83172b9   Huang, Ying   x86_64 EFI boot s...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  	}
  
  	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...
311
  	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;
7c83172b9   Huang, Ying   x86_64 EFI boot s...
312

7c08c9ae0   Peter Jones   efifb/imacfb cons...
313
314
315
  	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...
316
317
  		goto err_unmap;
  	}
7c08c9ae0   Peter Jones   efifb/imacfb cons...
318
319
320
  	if ((err = register_framebuffer(info)) < 0) {
  		printk(KERN_ERR "efifb: cannot register framebuffer
  ");
7c83172b9   Huang, Ying   x86_64 EFI boot s...
321
322
  		goto err_fb_dealoc;
  	}
31b6780c1   Joe Perches   framebuffer: Use ...
323
324
  	fb_info(info, "%s frame buffer device
  ", info->fix.id);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
325
326
327
328
329
330
  	return 0;
  
  err_fb_dealoc:
  	fb_dealloc_cmap(&info->cmap);
  err_unmap:
  	iounmap(info->screen_base);
7c08c9ae0   Peter Jones   efifb/imacfb cons...
331
  err_release_fb:
7c83172b9   Huang, Ying   x86_64 EFI boot s...
332
333
  	framebuffer_release(info);
  err_release_mem:
da0241f12   Andy Lutomirski   efifb: Fix mismat...
334
  	if (request_mem_succeeded)
7c08c9ae0   Peter Jones   efifb/imacfb cons...
335
  		release_mem_region(efifb_fix.smem_start, size_total);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
336
337
338
339
  	return err;
  }
  
  static struct platform_driver efifb_driver = {
e6816a8d8   David Herrmann   fbdev: efifb: bin...
340
341
342
  	.driver = {
  		.name = "efi-framebuffer",
  		.owner = THIS_MODULE,
7c83172b9   Huang, Ying   x86_64 EFI boot s...
343
  	},
e6816a8d8   David Herrmann   fbdev: efifb: bin...
344
  	.probe = efifb_probe,
7c83172b9   Huang, Ying   x86_64 EFI boot s...
345
  };
e6816a8d8   David Herrmann   fbdev: efifb: bin...
346
  module_platform_driver(efifb_driver);
7c83172b9   Huang, Ying   x86_64 EFI boot s...
347
  MODULE_LICENSE("GPL");