Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Arve Hjønnevåg and committed by
Linus Torvalds
c289ba2d 256fc0e5

+328
+9
drivers/video/Kconfig
··· 2183 2183 framebuffer. ML300 carries a 640*480 LCD display on the board, 2184 2184 ML403 uses a standard DB15 VGA connector. 2185 2185 2186 + config FB_GOLDFISH 2187 + tristate "Goldfish Framebuffer" 2188 + depends on FB 2189 + select FB_CFB_FILLRECT 2190 + select FB_CFB_COPYAREA 2191 + select FB_CFB_IMAGEBLIT 2192 + ---help--- 2193 + Framebuffer driver for Goldfish Virtual Platform 2194 + 2186 2195 config FB_COBALT 2187 2196 tristate "Cobalt server LCD frame buffer support" 2188 2197 depends on FB && (MIPS_COBALT || MIPS_SEAD3)
+1
drivers/video/Makefile
··· 98 98 obj-$(CONFIG_FB_PVR2) += pvr2fb.o 99 99 obj-$(CONFIG_FB_VOODOO1) += sstfb.o 100 100 obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o 101 + obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o 101 102 obj-$(CONFIG_FB_68328) += 68328fb.o 102 103 obj-$(CONFIG_FB_GBE) += gbefb.o 103 104 obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
+318
drivers/video/goldfishfb.c
··· 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 + 30 + enum { 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 + 45 + struct 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 + 56 + static 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 + 72 + static 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 + 79 + static int 80 + goldfish_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 + 96 + static 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 + 124 + static 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 + 136 + static 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 + 155 + static 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 + 169 + static 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 + 182 + static 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 + 276 + err_register_framebuffer_failed: 277 + free_irq(fb->irq, fb); 278 + err_request_irq_failed: 279 + err_fb_set_var_failed: 280 + dma_free_coherent(&pdev->dev, framesize, 281 + (void *)fb->fb.screen_base, 282 + fb->fb.fix.smem_start); 283 + err_alloc_screen_base_failed: 284 + err_no_irq: 285 + iounmap(fb->reg_base); 286 + err_no_io_base: 287 + kfree(fb); 288 + err_fb_alloc_failed: 289 + return ret; 290 + } 291 + 292 + static 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 + 308 + static 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 + 316 + module_platform_driver(goldfish_fb_driver); 317 + 318 + MODULE_LICENSE("GPL v2");