aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/goldfishfb.c
diff options
context:
space:
mode:
authorArve Hjønnevåg <arve@android.com>2013-02-21 19:42:09 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 20:22:17 -0500
commitc289ba2d26cf872570ba23fceee8d80ae64be351 (patch)
tree110cabcd79c50eeb9abe2ec5cbfccd6483986c66 /drivers/video/goldfishfb.c
parent256fc0e57dc0fce1e698d1be55bedd26f6d6bb5a (diff)
goldfish: framebuffer driver
Framebuffer support for the Goldfish emulator. This takes the Google emulator and applies the x86 cleanups as well as moving the blank methods to the usual Linux place and dropping the Android early suspend logic (for now at least, that can be looked at as Android and upstream converge). Dropped various oddities like setting MTRRs on a virtual frame buffer emulation... With the drivers so far you can now boot a Linux initrd and have fun. [sheng@linux.intel.com: cleaned up to handle x86] [thomas.keel@intel.com: ported to 3.4] [alan@linux.intel.com: cleaned up for style and 3.7, moved blank methods] [akpm@linux-foundation.org: fix (silly) sparse warnings] Signed-off-by: Mike A. Chan <mikechan@google.com> Signed-off-by: Arve Hjønnevåg <arve@android.com> Signed-off-by: Sheng Yang <sheng@linux.intel.com> Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com> Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com> Signed-off-by: Jun Nakajima <jun.nakajima@intel.com> Signed-off-by: Bruce Beare <bruce.j.beare@intel.com> Signed-off-by: Tom Keel <thomas.keel@intel.com> Signed-off-by: Alan Cox <alan@linux.intel.com> Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de> Cc: Tomi Valkeinen <tomi.valkeinen@ti.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/goldfishfb.c')
-rw-r--r--drivers/video/goldfishfb.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/drivers/video/goldfishfb.c b/drivers/video/goldfishfb.c
new file mode 100644
index 000000000000..489abb32fc04
--- /dev/null
+++ b/drivers/video/goldfishfb.c
@@ -0,0 +1,318 @@
1/*
2 * Copyright (C) 2007 Google, Inc.
3 * Copyright (C) 2012 Intel, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/dma-mapping.h>
19#include <linux/errno.h>
20#include <linux/string.h>
21#include <linux/slab.h>
22#include <linux/delay.h>
23#include <linux/mm.h>
24#include <linux/fb.h>
25#include <linux/init.h>
26#include <linux/interrupt.h>
27#include <linux/ioport.h>
28#include <linux/platform_device.h>
29
30enum {
31 FB_GET_WIDTH = 0x00,
32 FB_GET_HEIGHT = 0x04,
33 FB_INT_STATUS = 0x08,
34 FB_INT_ENABLE = 0x0c,
35 FB_SET_BASE = 0x10,
36 FB_SET_ROTATION = 0x14,
37 FB_SET_BLANK = 0x18,
38 FB_GET_PHYS_WIDTH = 0x1c,
39 FB_GET_PHYS_HEIGHT = 0x20,
40
41 FB_INT_VSYNC = 1U << 0,
42 FB_INT_BASE_UPDATE_DONE = 1U << 1
43};
44
45struct goldfish_fb {
46 void __iomem *reg_base;
47 int irq;
48 spinlock_t lock;
49 wait_queue_head_t wait;
50 int base_update_count;
51 int rotation;
52 struct fb_info fb;
53 u32 cmap[16];
54};
55
56static irqreturn_t goldfish_fb_interrupt(int irq, void *dev_id)
57{
58 unsigned long irq_flags;
59 struct goldfish_fb *fb = dev_id;
60 u32 status;
61
62 spin_lock_irqsave(&fb->lock, irq_flags);
63 status = readl(fb->reg_base + FB_INT_STATUS);
64 if (status & FB_INT_BASE_UPDATE_DONE) {
65 fb->base_update_count++;
66 wake_up(&fb->wait);
67 }
68 spin_unlock_irqrestore(&fb->lock, irq_flags);
69 return status ? IRQ_HANDLED : IRQ_NONE;
70}
71
72static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
73{
74 unsigned int mask = (1 << bf->length) - 1;
75
76 return (val >> (16 - bf->length) & mask) << bf->offset;
77}
78
79static int
80goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
81 unsigned int blue, unsigned int transp, struct fb_info *info)
82{
83 struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
84
85 if (regno < 16) {
86 fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
87 convert_bitfield(blue, &fb->fb.var.blue) |
88 convert_bitfield(green, &fb->fb.var.green) |
89 convert_bitfield(red, &fb->fb.var.red);
90 return 0;
91 } else {
92 return 1;
93 }
94}
95
96static int goldfish_fb_check_var(struct fb_var_screeninfo *var,
97 struct fb_info *info)
98{
99 if ((var->rotate & 1) != (info->var.rotate & 1)) {
100 if ((var->xres != info->var.yres) ||
101 (var->yres != info->var.xres) ||
102 (var->xres_virtual != info->var.yres) ||
103 (var->yres_virtual > info->var.xres * 2) ||
104 (var->yres_virtual < info->var.xres)) {
105 return -EINVAL;
106 }
107 } else {
108 if ((var->xres != info->var.xres) ||
109 (var->yres != info->var.yres) ||
110 (var->xres_virtual != info->var.xres) ||
111 (var->yres_virtual > info->var.yres * 2) ||
112 (var->yres_virtual < info->var.yres)) {
113 return -EINVAL;
114 }
115 }
116 if ((var->xoffset != info->var.xoffset) ||
117 (var->bits_per_pixel != info->var.bits_per_pixel) ||
118 (var->grayscale != info->var.grayscale)) {
119 return -EINVAL;
120 }
121 return 0;
122}
123
124static int goldfish_fb_set_par(struct fb_info *info)
125{
126 struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
127 if (fb->rotation != fb->fb.var.rotate) {
128 info->fix.line_length = info->var.xres * 2;
129 fb->rotation = fb->fb.var.rotate;
130 writel(fb->rotation, fb->reg_base + FB_SET_ROTATION);
131 }
132 return 0;
133}
134
135
136static int goldfish_fb_pan_display(struct fb_var_screeninfo *var,
137 struct fb_info *info)
138{
139 unsigned long irq_flags;
140 int base_update_count;
141 struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
142
143 spin_lock_irqsave(&fb->lock, irq_flags);
144 base_update_count = fb->base_update_count;
145 writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset,
146 fb->reg_base + FB_SET_BASE);
147 spin_unlock_irqrestore(&fb->lock, irq_flags);
148 wait_event_timeout(fb->wait,
149 fb->base_update_count != base_update_count, HZ / 15);
150 if (fb->base_update_count == base_update_count)
151 pr_err("goldfish_fb_pan_display: timeout wating for base update\n");
152 return 0;
153}
154
155static int goldfish_fb_blank(int blank, struct fb_info *info)
156{
157 struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
158 switch (blank) {
159 case FB_BLANK_NORMAL:
160 writel(1, fb->reg_base + FB_SET_BLANK);
161 break;
162 case FB_BLANK_UNBLANK:
163 writel(0, fb->reg_base + FB_SET_BLANK);
164 break;
165 }
166 return 0;
167}
168
169static struct fb_ops goldfish_fb_ops = {
170 .owner = THIS_MODULE,
171 .fb_check_var = goldfish_fb_check_var,
172 .fb_set_par = goldfish_fb_set_par,
173 .fb_setcolreg = goldfish_fb_setcolreg,
174 .fb_pan_display = goldfish_fb_pan_display,
175 .fb_blank = goldfish_fb_blank,
176 .fb_fillrect = cfb_fillrect,
177 .fb_copyarea = cfb_copyarea,
178 .fb_imageblit = cfb_imageblit,
179};
180
181
182static int goldfish_fb_probe(struct platform_device *pdev)
183{
184 int ret;
185 struct resource *r;
186 struct goldfish_fb *fb;
187 size_t framesize;
188 u32 width, height;
189 dma_addr_t fbpaddr;
190
191 fb = kzalloc(sizeof(*fb), GFP_KERNEL);
192 if (fb == NULL) {
193 ret = -ENOMEM;
194 goto err_fb_alloc_failed;
195 }
196 spin_lock_init(&fb->lock);
197 init_waitqueue_head(&fb->wait);
198 platform_set_drvdata(pdev, fb);
199
200 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
201 if (r == NULL) {
202 ret = -ENODEV;
203 goto err_no_io_base;
204 }
205 fb->reg_base = ioremap(r->start, PAGE_SIZE);
206 if (fb->reg_base == NULL) {
207 ret = -ENOMEM;
208 goto err_no_io_base;
209 }
210
211 fb->irq = platform_get_irq(pdev, 0);
212 if (fb->irq <= 0) {
213 ret = -ENODEV;
214 goto err_no_irq;
215 }
216
217 width = readl(fb->reg_base + FB_GET_WIDTH);
218 height = readl(fb->reg_base + FB_GET_HEIGHT);
219
220 fb->fb.fbops = &goldfish_fb_ops;
221 fb->fb.flags = FBINFO_FLAG_DEFAULT;
222 fb->fb.pseudo_palette = fb->cmap;
223 fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
224 fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
225 fb->fb.fix.line_length = width * 2;
226 fb->fb.fix.accel = FB_ACCEL_NONE;
227 fb->fb.fix.ypanstep = 1;
228
229 fb->fb.var.xres = width;
230 fb->fb.var.yres = height;
231 fb->fb.var.xres_virtual = width;
232 fb->fb.var.yres_virtual = height * 2;
233 fb->fb.var.bits_per_pixel = 16;
234 fb->fb.var.activate = FB_ACTIVATE_NOW;
235 fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
236 fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
237 fb->fb.var.pixclock = 10000;
238
239 fb->fb.var.red.offset = 11;
240 fb->fb.var.red.length = 5;
241 fb->fb.var.green.offset = 5;
242 fb->fb.var.green.length = 6;
243 fb->fb.var.blue.offset = 0;
244 fb->fb.var.blue.length = 5;
245
246 framesize = width * height * 2 * 2;
247 fb->fb.screen_base = (char __force __iomem *)dma_alloc_coherent(
248 &pdev->dev, framesize,
249 &fbpaddr, GFP_KERNEL);
250 pr_debug("allocating frame buffer %d * %d, got %p\n",
251 width, height, fb->fb.screen_base);
252 if (fb->fb.screen_base == NULL) {
253 ret = -ENOMEM;
254 goto err_alloc_screen_base_failed;
255 }
256 fb->fb.fix.smem_start = fbpaddr;
257 fb->fb.fix.smem_len = framesize;
258
259 ret = fb_set_var(&fb->fb, &fb->fb.var);
260 if (ret)
261 goto err_fb_set_var_failed;
262
263 ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED,
264 pdev->name, fb);
265 if (ret)
266 goto err_request_irq_failed;
267
268 writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE);
269 goldfish_fb_pan_display(&fb->fb.var, &fb->fb); /* updates base */
270
271 ret = register_framebuffer(&fb->fb);
272 if (ret)
273 goto err_register_framebuffer_failed;
274 return 0;
275
276err_register_framebuffer_failed:
277 free_irq(fb->irq, fb);
278err_request_irq_failed:
279err_fb_set_var_failed:
280 dma_free_coherent(&pdev->dev, framesize,
281 (void *)fb->fb.screen_base,
282 fb->fb.fix.smem_start);
283err_alloc_screen_base_failed:
284err_no_irq:
285 iounmap(fb->reg_base);
286err_no_io_base:
287 kfree(fb);
288err_fb_alloc_failed:
289 return ret;
290}
291
292static int goldfish_fb_remove(struct platform_device *pdev)
293{
294 size_t framesize;
295 struct goldfish_fb *fb = platform_get_drvdata(pdev);
296
297 framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2;
298 unregister_framebuffer(&fb->fb);
299 free_irq(fb->irq, fb);
300
301 dma_free_coherent(&pdev->dev, framesize, (void *)fb->fb.screen_base,
302 fb->fb.fix.smem_start);
303 iounmap(fb->reg_base);
304 return 0;
305}
306
307
308static struct platform_driver goldfish_fb_driver = {
309 .probe = goldfish_fb_probe,
310 .remove = goldfish_fb_remove,
311 .driver = {
312 .name = "goldfish_fb"
313 }
314};
315
316module_platform_driver(goldfish_fb_driver);
317
318MODULE_LICENSE("GPL v2");