]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/video/fbdev/efifb.c
Merge remote-tracking branch 'asoc/fix/dapm' into asoc-linus
[karo-tx-linux.git] / drivers / video / fbdev / efifb.c
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 #define efifb_attr_decl(name, fmt)                                      \
122 static ssize_t name##_show(struct device *dev,                          \
123                            struct device_attribute *attr,               \
124                            char *buf)                                   \
125 {                                                                       \
126         return sprintf(buf, fmt "\n", (screen_info.lfb_##name));        \
127 }                                                                       \
128 static DEVICE_ATTR_RO(name)
129
130 efifb_attr_decl(base, "0x%x");
131 efifb_attr_decl(linelength, "%u");
132 efifb_attr_decl(height, "%u");
133 efifb_attr_decl(width, "%u");
134 efifb_attr_decl(depth, "%u");
135
136 static struct attribute *efifb_attrs[] = {
137         &dev_attr_base.attr,
138         &dev_attr_linelength.attr,
139         &dev_attr_width.attr,
140         &dev_attr_height.attr,
141         &dev_attr_depth.attr,
142         NULL
143 };
144 ATTRIBUTE_GROUPS(efifb);
145
146 static int efifb_probe(struct platform_device *dev)
147 {
148         struct fb_info *info;
149         int err;
150         unsigned int size_vmode;
151         unsigned int size_remap;
152         unsigned int size_total;
153         char *option = NULL;
154
155         if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
156                 return -ENODEV;
157
158         if (fb_get_options("efifb", &option))
159                 return -ENODEV;
160         efifb_setup(option);
161
162         /* We don't get linelength from UGA Draw Protocol, only from
163          * EFI Graphics Protocol.  So if it's not in DMI, and it's not
164          * passed in from the user, we really can't use the framebuffer.
165          */
166         if (!screen_info.lfb_linelength)
167                 return -ENODEV;
168
169         if (!screen_info.lfb_depth)
170                 screen_info.lfb_depth = 32;
171         if (!screen_info.pages)
172                 screen_info.pages = 1;
173         if (!fb_base_is_valid()) {
174                 printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
175                 return -ENODEV;
176         }
177         printk(KERN_INFO "efifb: probing for efifb\n");
178
179         /* just assume they're all unset if any are */
180         if (!screen_info.blue_size) {
181                 screen_info.blue_size = 8;
182                 screen_info.blue_pos = 0;
183                 screen_info.green_size = 8;
184                 screen_info.green_pos = 8;
185                 screen_info.red_size = 8;
186                 screen_info.red_pos = 16;
187                 screen_info.rsvd_size = 8;
188                 screen_info.rsvd_pos = 24;
189         }
190
191         efifb_fix.smem_start = screen_info.lfb_base;
192
193         if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
194                 u64 ext_lfb_base;
195
196                 ext_lfb_base = (u64)(unsigned long)screen_info.ext_lfb_base << 32;
197                 efifb_fix.smem_start |= ext_lfb_base;
198         }
199
200         efifb_defined.bits_per_pixel = screen_info.lfb_depth;
201         efifb_defined.xres = screen_info.lfb_width;
202         efifb_defined.yres = screen_info.lfb_height;
203         efifb_fix.line_length = screen_info.lfb_linelength;
204
205         /*   size_vmode -- that is the amount of memory needed for the
206          *                 used video mode, i.e. the minimum amount of
207          *                 memory we need. */
208         size_vmode = efifb_defined.yres * efifb_fix.line_length;
209
210         /*   size_total -- all video memory we have. Used for
211          *                 entries, ressource allocation and bounds
212          *                 checking. */
213         size_total = screen_info.lfb_size;
214         if (size_total < size_vmode)
215                 size_total = size_vmode;
216
217         /*   size_remap -- the amount of video memory we are going to
218          *                 use for efifb.  With modern cards it is no
219          *                 option to simply use size_total as that
220          *                 wastes plenty of kernel address space. */
221         size_remap  = size_vmode * 2;
222         if (size_remap > size_total)
223                 size_remap = size_total;
224         if (size_remap % PAGE_SIZE)
225                 size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
226         efifb_fix.smem_len = size_remap;
227
228         if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
229                 request_mem_succeeded = true;
230         } else {
231                 /* We cannot make this fatal. Sometimes this comes from magic
232                    spaces our resource handlers simply don't know about */
233                 pr_warn("efifb: cannot reserve video memory at 0x%lx\n",
234                         efifb_fix.smem_start);
235         }
236
237         info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
238         if (!info) {
239                 pr_err("efifb: cannot allocate framebuffer\n");
240                 err = -ENOMEM;
241                 goto err_release_mem;
242         }
243         platform_set_drvdata(dev, info);
244         info->pseudo_palette = info->par;
245         info->par = NULL;
246
247         info->apertures = alloc_apertures(1);
248         if (!info->apertures) {
249                 err = -ENOMEM;
250                 goto err_release_fb;
251         }
252         info->apertures->ranges[0].base = efifb_fix.smem_start;
253         info->apertures->ranges[0].size = size_remap;
254
255         info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len);
256         if (!info->screen_base) {
257                 pr_err("efifb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n",
258                         efifb_fix.smem_len, efifb_fix.smem_start);
259                 err = -EIO;
260                 goto err_release_fb;
261         }
262
263         pr_info("efifb: framebuffer at 0x%lx, using %dk, total %dk\n",
264                efifb_fix.smem_start, size_remap/1024, size_total/1024);
265         pr_info("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
266                efifb_defined.xres, efifb_defined.yres,
267                efifb_defined.bits_per_pixel, efifb_fix.line_length,
268                screen_info.pages);
269
270         efifb_defined.xres_virtual = efifb_defined.xres;
271         efifb_defined.yres_virtual = efifb_fix.smem_len /
272                                         efifb_fix.line_length;
273         pr_info("efifb: scrolling: redraw\n");
274         efifb_defined.yres_virtual = efifb_defined.yres;
275
276         /* some dummy values for timing to make fbset happy */
277         efifb_defined.pixclock     = 10000000 / efifb_defined.xres *
278                                         1000 / efifb_defined.yres;
279         efifb_defined.left_margin  = (efifb_defined.xres / 8) & 0xf8;
280         efifb_defined.hsync_len    = (efifb_defined.xres / 8) & 0xf8;
281
282         efifb_defined.red.offset    = screen_info.red_pos;
283         efifb_defined.red.length    = screen_info.red_size;
284         efifb_defined.green.offset  = screen_info.green_pos;
285         efifb_defined.green.length  = screen_info.green_size;
286         efifb_defined.blue.offset   = screen_info.blue_pos;
287         efifb_defined.blue.length   = screen_info.blue_size;
288         efifb_defined.transp.offset = screen_info.rsvd_pos;
289         efifb_defined.transp.length = screen_info.rsvd_size;
290
291         pr_info("efifb: %s: "
292                "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
293                "Truecolor",
294                screen_info.rsvd_size,
295                screen_info.red_size,
296                screen_info.green_size,
297                screen_info.blue_size,
298                screen_info.rsvd_pos,
299                screen_info.red_pos,
300                screen_info.green_pos,
301                screen_info.blue_pos);
302
303         efifb_fix.ypanstep  = 0;
304         efifb_fix.ywrapstep = 0;
305
306         info->fbops = &efifb_ops;
307         info->var = efifb_defined;
308         info->fix = efifb_fix;
309         info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;
310
311         err = sysfs_create_groups(&dev->dev.kobj, efifb_groups);
312         if (err) {
313                 pr_err("efifb: cannot add sysfs attrs\n");
314                 goto err_unmap;
315         }
316         err = fb_alloc_cmap(&info->cmap, 256, 0);
317         if (err < 0) {
318                 pr_err("efifb: cannot allocate colormap\n");
319                 goto err_groups;
320         }
321         err = register_framebuffer(info);
322         if (err < 0) {
323                 pr_err("efifb: cannot register framebuffer\n");
324                 goto err_fb_dealoc;
325         }
326         fb_info(info, "%s frame buffer device\n", info->fix.id);
327         return 0;
328
329 err_fb_dealoc:
330         fb_dealloc_cmap(&info->cmap);
331 err_groups:
332         sysfs_remove_groups(&dev->dev.kobj, efifb_groups);
333 err_unmap:
334         iounmap(info->screen_base);
335 err_release_fb:
336         framebuffer_release(info);
337 err_release_mem:
338         if (request_mem_succeeded)
339                 release_mem_region(efifb_fix.smem_start, size_total);
340         return err;
341 }
342
343 static int efifb_remove(struct platform_device *pdev)
344 {
345         struct fb_info *info = platform_get_drvdata(pdev);
346
347         unregister_framebuffer(info);
348         sysfs_remove_groups(&pdev->dev.kobj, efifb_groups);
349         framebuffer_release(info);
350
351         return 0;
352 }
353
354 static struct platform_driver efifb_driver = {
355         .driver = {
356                 .name = "efi-framebuffer",
357         },
358         .probe = efifb_probe,
359         .remove = efifb_remove,
360 };
361
362 builtin_platform_driver(efifb_driver);