diff options
Diffstat (limited to 'target/linux/goldfish/patches-2.6.30/0128--ARM-goldfish-fb-Add-fb-driver-for-goldfish.patch')
-rw-r--r-- | target/linux/goldfish/patches-2.6.30/0128--ARM-goldfish-fb-Add-fb-driver-for-goldfish.patch | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/target/linux/goldfish/patches-2.6.30/0128--ARM-goldfish-fb-Add-fb-driver-for-goldfish.patch b/target/linux/goldfish/patches-2.6.30/0128--ARM-goldfish-fb-Add-fb-driver-for-goldfish.patch new file mode 100644 index 0000000000..48e69daae0 --- /dev/null +++ b/target/linux/goldfish/patches-2.6.30/0128--ARM-goldfish-fb-Add-fb-driver-for-goldfish.patch @@ -0,0 +1,384 @@ +From f8101c780fdc050eda9f93c94f8841de9f384b4f Mon Sep 17 00:00:00 2001 +From: =?utf-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= <arve@google.com> +Date: Fri, 29 Jun 2007 22:14:27 -0700 +Subject: [PATCH 128/134] [ARM] goldfish: fb: Add fb driver for goldfish. +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Transfer-Encoding: 8bit + +Supports panning for double buffering. + +Signed-off-by: Mike A. Chan <mikechan@google.com> +Signed-off-by: Arve Hjønnevåg <arve@android.com> +--- + drivers/video/Kconfig | 9 ++ + drivers/video/Makefile | 1 + + drivers/video/goldfishfb.c | 334 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 344 insertions(+), 0 deletions(-) + create mode 100644 drivers/video/goldfishfb.c + +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -2005,6 +2005,15 @@ config FB_XILINX + framebuffer. ML300 carries a 640*480 LCD display on the board, + ML403 uses a standard DB15 VGA connector. + ++config FB_GOLDFISH ++ tristate "Goldfish Framebuffer" ++ depends on FB ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ ---help--- ++ Framebuffer driver for Goldfish Virtual Platform ++ + config FB_COBALT + tristate "Cobalt server LCD frame buffer support" + depends on FB && MIPS_COBALT +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -92,6 +92,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb + obj-$(CONFIG_FB_PVR2) += pvr2fb.o + obj-$(CONFIG_FB_VOODOO1) += sstfb.o + obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o ++obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o + obj-$(CONFIG_FB_68328) += 68328fb.o + obj-$(CONFIG_FB_GBE) += gbefb.o + obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o +--- /dev/null ++++ b/drivers/video/goldfishfb.c +@@ -0,0 +1,334 @@ ++/* drivers/video/goldfishfb.c ++** ++** Copyright (C) 2007 Google, Inc. ++** ++** This software is licensed under the terms of the GNU General Public ++** License version 2, as published by the Free Software Foundation, and ++** may be copied, distributed, and modified under those terms. ++** ++** This program is distributed in the hope that it will be useful, ++** but WITHOUT ANY WARRANTY; without even the implied warranty of ++** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++** GNU General Public License for more details. ++** ++*/ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/dma-mapping.h> ++#include <linux/errno.h> ++#include <linux/string.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/mm.h> ++#include <linux/fb.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/ioport.h> ++#include <linux/platform_device.h> ++#ifdef CONFIG_ANDROID_POWER ++#include <linux/android_power.h> ++#endif ++ ++#include <mach/hardware.h> ++ ++enum { ++ FB_GET_WIDTH = 0x00, ++ FB_GET_HEIGHT = 0x04, ++ FB_INT_STATUS = 0x08, ++ FB_INT_ENABLE = 0x0c, ++ FB_SET_BASE = 0x10, ++ FB_SET_ROTATION = 0x14, ++ FB_SET_BLANK = 0x18, ++ FB_GET_PHYS_WIDTH = 0x1c, ++ FB_GET_PHYS_HEIGHT = 0x20, ++ ++ FB_INT_VSYNC = 1U << 0, ++ FB_INT_BASE_UPDATE_DONE = 1U << 1 ++}; ++ ++struct goldfish_fb { ++ uint32_t reg_base; ++ int irq; ++ spinlock_t lock; ++ wait_queue_head_t wait; ++ int base_update_count; ++ int rotation; ++ struct fb_info fb; ++ u32 cmap[16]; ++#ifdef CONFIG_ANDROID_POWER ++ android_early_suspend_t early_suspend; ++#endif ++}; ++ ++static irqreturn_t ++goldfish_fb_interrupt(int irq, void *dev_id) ++{ ++ unsigned long irq_flags; ++ struct goldfish_fb *fb = dev_id; ++ uint32_t status; ++ ++ spin_lock_irqsave(&fb->lock, irq_flags); ++ status = readl(fb->reg_base + FB_INT_STATUS); ++ if(status & FB_INT_BASE_UPDATE_DONE) { ++ fb->base_update_count++; ++ wake_up(&fb->wait); ++ } ++ spin_unlock_irqrestore(&fb->lock, irq_flags); ++ return status ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) ++{ ++ unsigned int mask = (1 << bf->length) - 1; ++ ++ return (val >> (16 - bf->length) & mask) << bf->offset; ++} ++ ++static int ++goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, ++ unsigned int blue, unsigned int transp, struct fb_info *info) ++{ ++ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); ++ ++ if (regno < 16) { ++ fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | ++ convert_bitfield(blue, &fb->fb.var.blue) | ++ convert_bitfield(green, &fb->fb.var.green) | ++ convert_bitfield(red, &fb->fb.var.red); ++ return 0; ++ } ++ else { ++ return 1; ++ } ++} ++ ++static int goldfish_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ if((var->rotate & 1) != (info->var.rotate & 1)) { ++ if((var->xres != info->var.yres) || ++ (var->yres != info->var.xres) || ++ (var->xres_virtual != info->var.yres) || ++ (var->yres_virtual > info->var.xres * 2) || ++ (var->yres_virtual < info->var.xres )) { ++ return -EINVAL; ++ } ++ } ++ else { ++ if((var->xres != info->var.xres) || ++ (var->yres != info->var.yres) || ++ (var->xres_virtual != info->var.xres) || ++ (var->yres_virtual > info->var.yres * 2) || ++ (var->yres_virtual < info->var.yres )) { ++ return -EINVAL; ++ } ++ } ++ if((var->xoffset != info->var.xoffset) || ++ (var->bits_per_pixel != info->var.bits_per_pixel) || ++ (var->grayscale != info->var.grayscale)) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int goldfish_fb_set_par(struct fb_info *info) ++{ ++ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); ++ if(fb->rotation != fb->fb.var.rotate) { ++ info->fix.line_length = info->var.xres * 2; ++ fb->rotation = fb->fb.var.rotate; ++ writel(fb->rotation, fb->reg_base + FB_SET_ROTATION); ++ } ++ return 0; ++} ++ ++ ++static int goldfish_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ unsigned long irq_flags; ++ int base_update_count; ++ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); ++ ++ spin_lock_irqsave(&fb->lock, irq_flags); ++ base_update_count = fb->base_update_count; ++ writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset, fb->reg_base + FB_SET_BASE); ++ spin_unlock_irqrestore(&fb->lock, irq_flags); ++ wait_event_timeout(fb->wait, fb->base_update_count != base_update_count, HZ / 15); ++ if(fb->base_update_count == base_update_count) ++ printk("goldfish_fb_pan_display: timeout wating for base update\n"); ++ return 0; ++} ++ ++#ifdef CONFIG_ANDROID_POWER ++static void goldfish_fb_early_suspend(android_early_suspend_t *h) ++{ ++ struct goldfish_fb *fb = container_of(h, struct goldfish_fb, early_suspend); ++ writel(1, fb->reg_base + FB_SET_BLANK); ++} ++ ++static void goldfish_fb_late_resume(android_early_suspend_t *h) ++{ ++ struct goldfish_fb *fb = container_of(h, struct goldfish_fb, early_suspend); ++ writel(0, fb->reg_base + FB_SET_BLANK); ++} ++#endif ++ ++static struct fb_ops goldfish_fb_ops = { ++ .owner = THIS_MODULE, ++ .fb_check_var = goldfish_fb_check_var, ++ .fb_set_par = goldfish_fb_set_par, ++ .fb_setcolreg = goldfish_fb_setcolreg, ++ .fb_pan_display = goldfish_fb_pan_display, ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++}; ++ ++ ++static int goldfish_fb_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct resource *r; ++ struct goldfish_fb *fb; ++ size_t framesize; ++ uint32_t width, height; ++ dma_addr_t fbpaddr; ++ ++ fb = kzalloc(sizeof(*fb), GFP_KERNEL); ++ if(fb == NULL) { ++ ret = -ENOMEM; ++ goto err_fb_alloc_failed; ++ } ++ spin_lock_init(&fb->lock); ++ init_waitqueue_head(&fb->wait); ++ platform_set_drvdata(pdev, fb); ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if(r == NULL) { ++ ret = -ENODEV; ++ goto err_no_io_base; ++ } ++ fb->reg_base = IO_ADDRESS(r->start - IO_START); ++ ++ fb->irq = platform_get_irq(pdev, 0); ++ if(fb->irq < 0) { ++ ret = -ENODEV; ++ goto err_no_irq; ++ } ++ ++ width = readl(fb->reg_base + FB_GET_WIDTH); ++ height = readl(fb->reg_base + FB_GET_HEIGHT); ++ ++ fb->fb.fbops = &goldfish_fb_ops; ++ fb->fb.flags = FBINFO_FLAG_DEFAULT; ++ fb->fb.pseudo_palette = fb->cmap; ++ //strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id)); ++ fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; ++ fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; ++ fb->fb.fix.line_length = width * 2; ++ fb->fb.fix.accel = FB_ACCEL_NONE; ++ fb->fb.fix.ypanstep = 1; ++ ++ fb->fb.var.xres = width; ++ fb->fb.var.yres = height; ++ fb->fb.var.xres_virtual = width; ++ fb->fb.var.yres_virtual = height * 2; ++ fb->fb.var.bits_per_pixel = 16; ++ fb->fb.var.activate = FB_ACTIVATE_NOW; ++ fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT); ++ fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH); ++ ++ fb->fb.var.red.offset = 11; ++ fb->fb.var.red.length = 5; ++ fb->fb.var.green.offset = 5; ++ fb->fb.var.green.length = 6; ++ fb->fb.var.blue.offset = 0; ++ fb->fb.var.blue.length = 5; ++ ++ framesize = width * height * 2 * 2; ++ fb->fb.screen_base = dma_alloc_writecombine(&pdev->dev, framesize, ++ &fbpaddr, GFP_KERNEL); ++ printk("allocating frame buffer %d * %d, got %p\n", width, height, fb->fb.screen_base); ++ if(fb->fb.screen_base == 0) { ++ ret = -ENOMEM; ++ goto err_alloc_screen_base_failed; ++ } ++ fb->fb.fix.smem_start = fbpaddr; ++ fb->fb.fix.smem_len = framesize; ++ ++ ret = fb_set_var(&fb->fb, &fb->fb.var); ++ if(ret) ++ goto err_fb_set_var_failed; ++ ++ ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED, pdev->name, fb); ++ if(ret) ++ goto err_request_irq_failed; ++ ++ writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE); ++ goldfish_fb_pan_display(&fb->fb.var, &fb->fb); // updates base ++ ++ ret = register_framebuffer(&fb->fb); ++ if(ret) ++ goto err_register_framebuffer_failed; ++ ++#ifdef CONFIG_ANDROID_POWER ++ fb->early_suspend.suspend = goldfish_fb_early_suspend; ++ fb->early_suspend.resume = goldfish_fb_late_resume; ++ android_register_early_suspend(&fb->early_suspend); ++#endif ++ ++ return 0; ++ ++ ++err_register_framebuffer_failed: ++ free_irq(fb->irq, fb); ++err_request_irq_failed: ++err_fb_set_var_failed: ++ dma_free_writecombine(&pdev->dev, framesize, fb->fb.screen_base, fb->fb.fix.smem_start); ++err_alloc_screen_base_failed: ++err_no_irq: ++err_no_io_base: ++ kfree(fb); ++err_fb_alloc_failed: ++ return ret; ++} ++ ++static int goldfish_fb_remove(struct platform_device *pdev) ++{ ++ size_t framesize; ++ struct goldfish_fb *fb = platform_get_drvdata(pdev); ++ ++ framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2; ++ ++#ifdef CONFIG_ANDROID_POWER ++ android_unregister_early_suspend(&fb->early_suspend); ++#endif ++ unregister_framebuffer(&fb->fb); ++ free_irq(fb->irq, fb); ++ dma_free_writecombine(&pdev->dev, framesize, fb->fb.screen_base, fb->fb.fix.smem_start); ++ kfree(fb); ++ return 0; ++} ++ ++ ++static struct platform_driver goldfish_fb_driver = { ++ .probe = goldfish_fb_probe, ++ .remove = goldfish_fb_remove, ++ .driver = { ++ .name = "goldfish_fb" ++ } ++}; ++ ++static int __init goldfish_fb_init(void) ++{ ++ return platform_driver_register(&goldfish_fb_driver); ++} ++ ++static void __exit goldfish_fb_exit(void) ++{ ++ platform_driver_unregister(&goldfish_fb_driver); ++} ++ ++module_init(goldfish_fb_init); ++module_exit(goldfish_fb_exit); ++ |