xref: /linux/drivers/video/fbdev/efifb.c (revision db6d8d5fdf9537641c76ba7f32e02b4bcc600972)
1 /*
2  * Framebuffer driver for EFI/UEFI based system
3  *
4  * (c) 2006 Edgar Hucek <gimli@dark-green.com>
5  * Original efi driver written by Gerd Knorr <kraxel@goldbach.in-berlin.de>
6  *
7  */
8 
9 #include <linux/kernel.h>
10 #include <linux/efi.h>
11 #include <linux/errno.h>
12 #include <linux/fb.h>
13 #include <linux/platform_device.h>
14 #include <linux/screen_info.h>
15 #include <video/vga.h>
16 #include <asm/efi.h>
17 
18 static bool request_mem_succeeded = false;
19 
20 static struct fb_var_screeninfo efifb_defined = {
21 	.activate		= FB_ACTIVATE_NOW,
22 	.height			= -1,
23 	.width			= -1,
24 	.right_margin		= 32,
25 	.upper_margin		= 16,
26 	.lower_margin		= 4,
27 	.vsync_len		= 4,
28 	.vmode			= FB_VMODE_NONINTERLACED,
29 };
30 
31 static struct fb_fix_screeninfo efifb_fix = {
32 	.id			= "EFI VGA",
33 	.type			= FB_TYPE_PACKED_PIXELS,
34 	.accel			= FB_ACCEL_NONE,
35 	.visual			= FB_VISUAL_TRUECOLOR,
36 };
37 
38 static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
39 			   unsigned blue, unsigned transp,
40 			   struct fb_info *info)
41 {
42 	/*
43 	 *  Set a single color register. The values supplied are
44 	 *  already rounded down to the hardware's capabilities
45 	 *  (according to the entries in the `var' structure). Return
46 	 *  != 0 for invalid regno.
47 	 */
48 
49 	if (regno >= info->cmap.len)
50 		return 1;
51 
52 	if (regno < 16) {
53 		red   >>= 16 - info->var.red.length;
54 		green >>= 16 - info->var.green.length;
55 		blue  >>= 16 - info->var.blue.length;
56 		((u32 *)(info->pseudo_palette))[regno] =
57 			(red   << info->var.red.offset)   |
58 			(green << info->var.green.offset) |
59 			(blue  << info->var.blue.offset);
60 	}
61 	return 0;
62 }
63 
64 static void efifb_destroy(struct fb_info *info)
65 {
66 	if (info->screen_base)
67 		iounmap(info->screen_base);
68 	if (request_mem_succeeded)
69 		release_mem_region(info->apertures->ranges[0].base,
70 				   info->apertures->ranges[0].size);
71 	fb_dealloc_cmap(&info->cmap);
72 }
73 
74 static struct fb_ops efifb_ops = {
75 	.owner		= THIS_MODULE,
76 	.fb_destroy	= efifb_destroy,
77 	.fb_setcolreg	= efifb_setcolreg,
78 	.fb_fillrect	= cfb_fillrect,
79 	.fb_copyarea	= cfb_copyarea,
80 	.fb_imageblit	= cfb_imageblit,
81 };
82 
83 static int efifb_setup(char *options)
84 {
85 	char *this_opt;
86 
87 	if (options && *options) {
88 		while ((this_opt = strsep(&options, ",")) != NULL) {
89 			if (!*this_opt) continue;
90 
91 			efifb_setup_from_dmi(&screen_info, this_opt);
92 
93 			if (!strncmp(this_opt, "base:", 5))
94 				screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0);
95 			else if (!strncmp(this_opt, "stride:", 7))
96 				screen_info.lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4;
97 			else if (!strncmp(this_opt, "height:", 7))
98 				screen_info.lfb_height = simple_strtoul(this_opt+7, NULL, 0);
99 			else if (!strncmp(this_opt, "width:", 6))
100 				screen_info.lfb_width = simple_strtoul(this_opt+6, NULL, 0);
101 		}
102 	}
103 
104 	return 0;
105 }
106 
107 static inline bool fb_base_is_valid(void)
108 {
109 	if (screen_info.lfb_base)
110 		return true;
111 
112 	if (!(screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE))
113 		return false;
114 
115 	if (screen_info.ext_lfb_base)
116 		return true;
117 
118 	return false;
119 }
120 
121 static int efifb_probe(struct platform_device *dev)
122 {
123 	struct fb_info *info;
124 	int err;
125 	unsigned int size_vmode;
126 	unsigned int size_remap;
127 	unsigned int size_total;
128 	char *option = NULL;
129 
130 	if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
131 		return -ENODEV;
132 
133 	if (fb_get_options("efifb", &option))
134 		return -ENODEV;
135 	efifb_setup(option);
136 
137 	/* We don't get linelength from UGA Draw Protocol, only from
138 	 * EFI Graphics Protocol.  So if it's not in DMI, and it's not
139 	 * passed in from the user, we really can't use the framebuffer.
140 	 */
141 	if (!screen_info.lfb_linelength)
142 		return -ENODEV;
143 
144 	if (!screen_info.lfb_depth)
145 		screen_info.lfb_depth = 32;
146 	if (!screen_info.pages)
147 		screen_info.pages = 1;
148 	if (!fb_base_is_valid()) {
149 		printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
150 		return -ENODEV;
151 	}
152 	printk(KERN_INFO "efifb: probing for efifb\n");
153 
154 	/* just assume they're all unset if any are */
155 	if (!screen_info.blue_size) {
156 		screen_info.blue_size = 8;
157 		screen_info.blue_pos = 0;
158 		screen_info.green_size = 8;
159 		screen_info.green_pos = 8;
160 		screen_info.red_size = 8;
161 		screen_info.red_pos = 16;
162 		screen_info.rsvd_size = 8;
163 		screen_info.rsvd_pos = 24;
164 	}
165 
166 	efifb_fix.smem_start = screen_info.lfb_base;
167 
168 	if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
169 		u64 ext_lfb_base;
170 
171 		ext_lfb_base = (u64)(unsigned long)screen_info.ext_lfb_base << 32;
172 		efifb_fix.smem_start |= ext_lfb_base;
173 	}
174 
175 	efifb_defined.bits_per_pixel = screen_info.lfb_depth;
176 	efifb_defined.xres = screen_info.lfb_width;
177 	efifb_defined.yres = screen_info.lfb_height;
178 	efifb_fix.line_length = screen_info.lfb_linelength;
179 
180 	/*   size_vmode -- that is the amount of memory needed for the
181 	 *                 used video mode, i.e. the minimum amount of
182 	 *                 memory we need. */
183 	size_vmode = efifb_defined.yres * efifb_fix.line_length;
184 
185 	/*   size_total -- all video memory we have. Used for
186 	 *                 entries, ressource allocation and bounds
187 	 *                 checking. */
188 	size_total = screen_info.lfb_size;
189 	if (size_total < size_vmode)
190 		size_total = size_vmode;
191 
192 	/*   size_remap -- the amount of video memory we are going to
193 	 *                 use for efifb.  With modern cards it is no
194 	 *                 option to simply use size_total as that
195 	 *                 wastes plenty of kernel address space. */
196 	size_remap  = size_vmode * 2;
197 	if (size_remap > size_total)
198 		size_remap = size_total;
199 	if (size_remap % PAGE_SIZE)
200 		size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
201 	efifb_fix.smem_len = size_remap;
202 
203 	if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
204 		request_mem_succeeded = true;
205 	} else {
206 		/* We cannot make this fatal. Sometimes this comes from magic
207 		   spaces our resource handlers simply don't know about */
208 		printk(KERN_WARNING
209 		       "efifb: cannot reserve video memory at 0x%lx\n",
210 			efifb_fix.smem_start);
211 	}
212 
213 	info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
214 	if (!info) {
215 		printk(KERN_ERR "efifb: cannot allocate framebuffer\n");
216 		err = -ENOMEM;
217 		goto err_release_mem;
218 	}
219 	platform_set_drvdata(dev, info);
220 	info->pseudo_palette = info->par;
221 	info->par = NULL;
222 
223 	info->apertures = alloc_apertures(1);
224 	if (!info->apertures) {
225 		err = -ENOMEM;
226 		goto err_release_fb;
227 	}
228 	info->apertures->ranges[0].base = efifb_fix.smem_start;
229 	info->apertures->ranges[0].size = size_remap;
230 
231 	info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len);
232 	if (!info->screen_base) {
233 		printk(KERN_ERR "efifb: abort, cannot ioremap video memory "
234 				"0x%x @ 0x%lx\n",
235 			efifb_fix.smem_len, efifb_fix.smem_start);
236 		err = -EIO;
237 		goto err_release_fb;
238 	}
239 
240 	printk(KERN_INFO "efifb: framebuffer at 0x%lx, using %dk, total %dk\n",
241 	       efifb_fix.smem_start, size_remap/1024, size_total/1024);
242 	printk(KERN_INFO "efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
243 	       efifb_defined.xres, efifb_defined.yres,
244 	       efifb_defined.bits_per_pixel, efifb_fix.line_length,
245 	       screen_info.pages);
246 
247 	efifb_defined.xres_virtual = efifb_defined.xres;
248 	efifb_defined.yres_virtual = efifb_fix.smem_len /
249 					efifb_fix.line_length;
250 	printk(KERN_INFO "efifb: scrolling: redraw\n");
251 	efifb_defined.yres_virtual = efifb_defined.yres;
252 
253 	/* some dummy values for timing to make fbset happy */
254 	efifb_defined.pixclock     = 10000000 / efifb_defined.xres *
255 					1000 / efifb_defined.yres;
256 	efifb_defined.left_margin  = (efifb_defined.xres / 8) & 0xf8;
257 	efifb_defined.hsync_len    = (efifb_defined.xres / 8) & 0xf8;
258 
259 	efifb_defined.red.offset    = screen_info.red_pos;
260 	efifb_defined.red.length    = screen_info.red_size;
261 	efifb_defined.green.offset  = screen_info.green_pos;
262 	efifb_defined.green.length  = screen_info.green_size;
263 	efifb_defined.blue.offset   = screen_info.blue_pos;
264 	efifb_defined.blue.length   = screen_info.blue_size;
265 	efifb_defined.transp.offset = screen_info.rsvd_pos;
266 	efifb_defined.transp.length = screen_info.rsvd_size;
267 
268 	printk(KERN_INFO "efifb: %s: "
269 	       "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
270 	       "Truecolor",
271 	       screen_info.rsvd_size,
272 	       screen_info.red_size,
273 	       screen_info.green_size,
274 	       screen_info.blue_size,
275 	       screen_info.rsvd_pos,
276 	       screen_info.red_pos,
277 	       screen_info.green_pos,
278 	       screen_info.blue_pos);
279 
280 	efifb_fix.ypanstep  = 0;
281 	efifb_fix.ywrapstep = 0;
282 
283 	info->fbops = &efifb_ops;
284 	info->var = efifb_defined;
285 	info->fix = efifb_fix;
286 	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;
287 
288 	if ((err = fb_alloc_cmap(&info->cmap, 256, 0)) < 0) {
289 		printk(KERN_ERR "efifb: cannot allocate colormap\n");
290 		goto err_unmap;
291 	}
292 	if ((err = register_framebuffer(info)) < 0) {
293 		printk(KERN_ERR "efifb: cannot register framebuffer\n");
294 		goto err_fb_dealoc;
295 	}
296 	fb_info(info, "%s frame buffer device\n", info->fix.id);
297 	return 0;
298 
299 err_fb_dealoc:
300 	fb_dealloc_cmap(&info->cmap);
301 err_unmap:
302 	iounmap(info->screen_base);
303 err_release_fb:
304 	framebuffer_release(info);
305 err_release_mem:
306 	if (request_mem_succeeded)
307 		release_mem_region(efifb_fix.smem_start, size_total);
308 	return err;
309 }
310 
311 static int efifb_remove(struct platform_device *pdev)
312 {
313 	struct fb_info *info = platform_get_drvdata(pdev);
314 
315 	unregister_framebuffer(info);
316 	framebuffer_release(info);
317 
318 	return 0;
319 }
320 
321 static struct platform_driver efifb_driver = {
322 	.driver = {
323 		.name = "efi-framebuffer",
324 	},
325 	.probe = efifb_probe,
326 	.remove = efifb_remove,
327 };
328 
329 builtin_platform_driver(efifb_driver);
330