Loading drivers/video/Kconfig +14 −0 Original line number Diff line number Diff line Loading @@ -310,6 +310,20 @@ config FB_PM2_FIFO_DISCONNECT help Support the Permedia2 FIFO disconnect feature. config FB_BCM2708 tristate "BCM2708 framebuffer support" depends on FB && ARM select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT help This framebuffer device driver is for the BCM2708 framebuffer. If you want to compile this as a module (=code which can be inserted into and removed from the running kernel), say M here and read <file:Documentation/kbuild/modules.txt>. The module will be called bcm2708_fb. config FB_ARMCLCD tristate "ARM PrimeCell PL110 support" depends on ARM || ARM64 || COMPILE_TEST Loading drivers/video/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ 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_BCM2708) += bcm2708_fb.o obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_GBE) += gbefb.o obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o Loading drivers/video/bcm2708_fb.c 0 → 100644 +499 −0 Original line number Diff line number Diff line /* * linux/drivers/video/bcm2708_fb.c * * Copyright (C) 2010 Broadcom * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. * * Broadcom simple framebuffer driver * * This file is derived from cirrusfb.c * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com> * */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/list.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/printk.h> #include <linux/console.h> #include <mach/platform.h> #include <mach/vcio.h> #include <asm/sizes.h> #include <linux/io.h> #include <linux/dma-mapping.h> #ifdef BCM2708_FB_DEBUG #define print_debug(fmt,...) pr_debug("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) #else #define print_debug(fmt,...) #endif /* This is limited to 16 characters when displayed by X startup */ static const char *bcm2708_name = "BCM2708 FB"; #define DRIVER_NAME "bcm2708_fb" static int fbwidth = 800; /* module parameter */ static int fbheight = 480; /* module parameter */ static int fbdepth = 16; /* module parameter */ static int fbswap = 0; /* module parameter */ /* this data structure describes each frame buffer device we find */ struct fbinfo_s { u32 xres, yres, xres_virtual, yres_virtual; u32 pitch, bpp; u32 xoffset, yoffset; u32 base; u32 screen_size; u16 cmap[256]; }; struct bcm2708_fb { struct fb_info fb; struct platform_device *dev; struct fbinfo_s *info; dma_addr_t dma; u32 cmap[16]; }; #define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb) static int bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var) { int ret = 0; memset(&var->transp, 0, sizeof(var->transp)); var->red.msb_right = 0; var->green.msb_right = 0; var->blue.msb_right = 0; switch (var->bits_per_pixel) { case 1: case 2: case 4: case 8: var->red.length = var->bits_per_pixel; var->red.offset = 0; var->green.length = var->bits_per_pixel; var->green.offset = 0; var->blue.length = var->bits_per_pixel; var->blue.offset = 0; break; case 16: var->red.length = 5; var->blue.length = 5; /* * Green length can be 5 or 6 depending whether * we're operating in RGB555 or RGB565 mode. */ if (var->green.length != 5 && var->green.length != 6) var->green.length = 6; break; case 24: var->red.length = 8; var->blue.length = 8; var->green.length = 8; break; case 32: var->red.length = 8; var->green.length = 8; var->blue.length = 8; var->transp.length = 8; break; default: ret = -EINVAL; break; } /* * >= 16bpp displays have separate colour component bitfields * encoded in the pixel data. Calculate their position from * the bitfield length defined above. */ if (ret == 0 && var->bits_per_pixel >= 24 && fbswap) { var->blue.offset = 0; var->green.offset = var->blue.offset + var->blue.length; var->red.offset = var->green.offset + var->green.length; var->transp.offset = var->red.offset + var->red.length; } else if (ret == 0 && var->bits_per_pixel >= 24) { var->red.offset = 0; var->green.offset = var->red.offset + var->red.length; var->blue.offset = var->green.offset + var->green.length; var->transp.offset = var->blue.offset + var->blue.length; } else if (ret == 0 && var->bits_per_pixel >= 16) { var->blue.offset = 0; var->green.offset = var->blue.offset + var->blue.length; var->red.offset = var->green.offset + var->green.length; var->transp.offset = var->red.offset + var->red.length; } return ret; } static int bcm2708_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { /* info input, var output */ int yres; /* info input, var output */ print_debug("bcm2708_fb_check_var info(%p) %dx%d (%dx%d), %d, %d\n", info, info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual, (int)info->screen_size, info->var.bits_per_pixel); print_debug("bcm2708_fb_check_var var(%p) %dx%d (%dx%d), %d\n", var, var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->bits_per_pixel); if (!var->bits_per_pixel) var->bits_per_pixel = 16; if (bcm2708_fb_set_bitfields(var) != 0) { pr_err("bcm2708_fb_check_var: invalid bits_per_pixel %d\n", var->bits_per_pixel); return -EINVAL; } if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; /* use highest possible virtual resolution */ if (var->yres_virtual == -1) { var->yres_virtual = 480; pr_err ("bcm2708_fb_check_var: virtual resolution set to maximum of %dx%d\n", var->xres_virtual, var->yres_virtual); } if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; if (var->xoffset < 0) var->xoffset = 0; if (var->yoffset < 0) var->yoffset = 0; /* truncate xoffset and yoffset to maximum if too high */ if (var->xoffset > var->xres_virtual - var->xres) var->xoffset = var->xres_virtual - var->xres - 1; if (var->yoffset > var->yres_virtual - var->yres) var->yoffset = var->yres_virtual - var->yres - 1; yres = var->yres; if (var->vmode & FB_VMODE_DOUBLE) yres *= 2; else if (var->vmode & FB_VMODE_INTERLACED) yres = (yres + 1) / 2; if (var->xres * yres > 1920 * 1200) { pr_err("bcm2708_fb_check_var: ERROR: Pixel size >= 1920x1200; " "special treatment required! (TODO)\n"); return -EINVAL; } return 0; } static int bcm2708_fb_set_par(struct fb_info *info) { uint32_t val = 0; struct bcm2708_fb *fb = to_bcm2708(info); volatile struct fbinfo_s *fbinfo = fb->info; fbinfo->xres = info->var.xres; fbinfo->yres = info->var.yres; fbinfo->xres_virtual = info->var.xres_virtual; fbinfo->yres_virtual = info->var.yres_virtual; fbinfo->bpp = info->var.bits_per_pixel; fbinfo->xoffset = info->var.xoffset; fbinfo->yoffset = info->var.yoffset; fbinfo->base = 0; /* filled in by VC */ fbinfo->pitch = 0; /* filled in by VC */ print_debug("bcm2708_fb_set_par info(%p) %dx%d (%dx%d), %d, %d\n", info, info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual, (int)info->screen_size, info->var.bits_per_pixel); /* ensure last write to fbinfo is visible to GPU */ wmb(); /* inform vc about new framebuffer */ bcm_mailbox_write(MBOX_CHAN_FB, fb->dma); /* TODO: replace fb driver with vchiq version */ /* wait for response */ bcm_mailbox_read(MBOX_CHAN_FB, &val); /* ensure GPU writes are visible to us */ rmb(); if (val == 0) { fb->fb.fix.line_length = fbinfo->pitch; if (info->var.bits_per_pixel <= 8) fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; else fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; fb->fb.fix.smem_start = fbinfo->base; fb->fb.fix.smem_len = fbinfo->pitch * fbinfo->yres_virtual; fb->fb.screen_size = fbinfo->screen_size; if (fb->fb.screen_base) iounmap(fb->fb.screen_base); fb->fb.screen_base = (void *)ioremap_wc(fb->fb.fix.smem_start, fb->fb.screen_size); if (!fb->fb.screen_base) { /* the console may currently be locked */ console_trylock(); console_unlock(); BUG(); /* what can we do here */ } } print_debug ("BCM2708FB: start = %p,%p width=%d, height=%d, bpp=%d, pitch=%d size=%d success=%d\n", (void *)fb->fb.screen_base, (void *)fb->fb.fix.smem_start, fbinfo->xres, fbinfo->yres, fbinfo->bpp, fbinfo->pitch, (int)fb->fb.screen_size, val); return val; } 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 bcm2708_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info) { struct bcm2708_fb *fb = to_bcm2708(info); /*print_debug("BCM2708FB: setcolreg %d:(%02x,%02x,%02x,%02x) %x\n", regno, red, green, blue, transp, fb->fb.fix.visual);*/ if (fb->fb.var.bits_per_pixel <= 8) { if (regno < 256) { /* blue [0:4], green [5:10], red [11:15] */ fb->info->cmap[regno] = ((red >> (16-5)) & 0x1f) << 11 | ((green >> (16-6)) & 0x3f) << 5 | ((blue >> (16-5)) & 0x1f) << 0; } /* Hack: we need to tell GPU the palette has changed, but currently bcm2708_fb_set_par takes noticable time when called for every (256) colour */ /* So just call it for what looks like the last colour in a list for now. */ if (regno == 15 || regno == 255) bcm2708_fb_set_par(info); } else 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 regno > 255; } static int bcm2708_fb_blank(int blank_mode, struct fb_info *info) { /*print_debug("bcm2708_fb_blank\n"); */ return -1; } static void bcm2708_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { /* (is called) print_debug("bcm2708_fb_fillrect\n"); */ cfb_fillrect(info, rect); } static void bcm2708_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) { /*print_debug("bcm2708_fb_copyarea\n"); */ cfb_copyarea(info, region); } static void bcm2708_fb_imageblit(struct fb_info *info, const struct fb_image *image) { /* (is called) print_debug("bcm2708_fb_imageblit\n"); */ cfb_imageblit(info, image); } static struct fb_ops bcm2708_fb_ops = { .owner = THIS_MODULE, .fb_check_var = bcm2708_fb_check_var, .fb_set_par = bcm2708_fb_set_par, .fb_setcolreg = bcm2708_fb_setcolreg, .fb_blank = bcm2708_fb_blank, .fb_fillrect = bcm2708_fb_fillrect, .fb_copyarea = bcm2708_fb_copyarea, .fb_imageblit = bcm2708_fb_imageblit, }; static int bcm2708_fb_register(struct bcm2708_fb *fb) { int ret; dma_addr_t dma; void *mem; mem = dma_alloc_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), &dma, GFP_KERNEL); if (NULL == mem) { pr_err(": unable to allocate fbinfo buffer\n"); ret = -ENOMEM; } else { fb->info = (struct fbinfo_s *)mem; fb->dma = dma; } fb->fb.fbops = &bcm2708_fb_ops; fb->fb.flags = FBINFO_FLAG_DEFAULT; fb->fb.pseudo_palette = fb->cmap; strncpy(fb->fb.fix.id, bcm2708_name, sizeof(fb->fb.fix.id)); fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; fb->fb.fix.type_aux = 0; fb->fb.fix.xpanstep = 0; fb->fb.fix.ypanstep = 0; fb->fb.fix.ywrapstep = 0; fb->fb.fix.accel = FB_ACCEL_NONE; fb->fb.var.xres = fbwidth; fb->fb.var.yres = fbheight; fb->fb.var.xres_virtual = fbwidth; fb->fb.var.yres_virtual = fbheight; fb->fb.var.bits_per_pixel = fbdepth; fb->fb.var.vmode = FB_VMODE_NONINTERLACED; fb->fb.var.activate = FB_ACTIVATE_NOW; fb->fb.var.nonstd = 0; fb->fb.var.height = -1; /* height of picture in mm */ fb->fb.var.width = -1; /* width of picture in mm */ fb->fb.var.accel_flags = 0; fb->fb.monspecs.hfmin = 0; fb->fb.monspecs.hfmax = 100000; fb->fb.monspecs.vfmin = 0; fb->fb.monspecs.vfmax = 400; fb->fb.monspecs.dclkmin = 1000000; fb->fb.monspecs.dclkmax = 100000000; bcm2708_fb_set_bitfields(&fb->fb.var); /* * Allocate colourmap. */ fb_set_var(&fb->fb, &fb->fb.var); print_debug("BCM2708FB: registering framebuffer (%dx%d@%d) (%d)\n", fbwidth fbheight, fbdepth, fbswap); ret = register_framebuffer(&fb->fb); print_debug("BCM2708FB: register framebuffer (%d)\n", ret); if (ret == 0) goto out; print_debug("BCM2708FB: cannot register framebuffer (%d)\n", ret); out: return ret; } static int bcm2708_fb_probe(struct platform_device *dev) { struct bcm2708_fb *fb; int ret; fb = kmalloc(sizeof(struct bcm2708_fb), GFP_KERNEL); if (!fb) { dev_err(&dev->dev, "could not allocate new bcm2708_fb struct\n"); ret = -ENOMEM; goto free_region; } memset(fb, 0, sizeof(struct bcm2708_fb)); fb->dev = dev; ret = bcm2708_fb_register(fb); if (ret == 0) { platform_set_drvdata(dev, fb); goto out; } kfree(fb); free_region: dev_err(&dev->dev, "probe failed, err %d\n", ret); out: return ret; } static int bcm2708_fb_remove(struct platform_device *dev) { struct bcm2708_fb *fb = platform_get_drvdata(dev); platform_set_drvdata(dev, NULL); if (fb->fb.screen_base) iounmap(fb->fb.screen_base); unregister_framebuffer(&fb->fb); dma_free_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), (void *)fb->info, fb->dma); kfree(fb); return 0; } static struct platform_driver bcm2708_fb_driver = { .probe = bcm2708_fb_probe, .remove = bcm2708_fb_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, }; static int __init bcm2708_fb_init(void) { return platform_driver_register(&bcm2708_fb_driver); } module_init(bcm2708_fb_init); static void __exit bcm2708_fb_exit(void) { platform_driver_unregister(&bcm2708_fb_driver); } module_exit(bcm2708_fb_exit); module_param(fbwidth, int, 0644); module_param(fbheight, int, 0644); module_param(fbdepth, int, 0644); module_param(fbswap, int, 0644); MODULE_DESCRIPTION("BCM2708 framebuffer driver"); MODULE_LICENSE("GPL"); MODULE_PARM_DESC(fbwidth, "Width of ARM Framebuffer"); MODULE_PARM_DESC(fbheight, "Height of ARM Framebuffer"); MODULE_PARM_DESC(fbdepth, "Bit depth of ARM Framebuffer"); MODULE_PARM_DESC(fbswap, "Swap order of red and blue in 24 and 32 bit modes"); Loading
drivers/video/Kconfig +14 −0 Original line number Diff line number Diff line Loading @@ -310,6 +310,20 @@ config FB_PM2_FIFO_DISCONNECT help Support the Permedia2 FIFO disconnect feature. config FB_BCM2708 tristate "BCM2708 framebuffer support" depends on FB && ARM select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT help This framebuffer device driver is for the BCM2708 framebuffer. If you want to compile this as a module (=code which can be inserted into and removed from the running kernel), say M here and read <file:Documentation/kbuild/modules.txt>. The module will be called bcm2708_fb. config FB_ARMCLCD tristate "ARM PrimeCell PL110 support" depends on ARM || ARM64 || COMPILE_TEST Loading
drivers/video/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ 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_BCM2708) += bcm2708_fb.o obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_GBE) += gbefb.o obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o Loading
drivers/video/bcm2708_fb.c 0 → 100644 +499 −0 Original line number Diff line number Diff line /* * linux/drivers/video/bcm2708_fb.c * * Copyright (C) 2010 Broadcom * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. * * Broadcom simple framebuffer driver * * This file is derived from cirrusfb.c * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com> * */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/list.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/printk.h> #include <linux/console.h> #include <mach/platform.h> #include <mach/vcio.h> #include <asm/sizes.h> #include <linux/io.h> #include <linux/dma-mapping.h> #ifdef BCM2708_FB_DEBUG #define print_debug(fmt,...) pr_debug("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) #else #define print_debug(fmt,...) #endif /* This is limited to 16 characters when displayed by X startup */ static const char *bcm2708_name = "BCM2708 FB"; #define DRIVER_NAME "bcm2708_fb" static int fbwidth = 800; /* module parameter */ static int fbheight = 480; /* module parameter */ static int fbdepth = 16; /* module parameter */ static int fbswap = 0; /* module parameter */ /* this data structure describes each frame buffer device we find */ struct fbinfo_s { u32 xres, yres, xres_virtual, yres_virtual; u32 pitch, bpp; u32 xoffset, yoffset; u32 base; u32 screen_size; u16 cmap[256]; }; struct bcm2708_fb { struct fb_info fb; struct platform_device *dev; struct fbinfo_s *info; dma_addr_t dma; u32 cmap[16]; }; #define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb) static int bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var) { int ret = 0; memset(&var->transp, 0, sizeof(var->transp)); var->red.msb_right = 0; var->green.msb_right = 0; var->blue.msb_right = 0; switch (var->bits_per_pixel) { case 1: case 2: case 4: case 8: var->red.length = var->bits_per_pixel; var->red.offset = 0; var->green.length = var->bits_per_pixel; var->green.offset = 0; var->blue.length = var->bits_per_pixel; var->blue.offset = 0; break; case 16: var->red.length = 5; var->blue.length = 5; /* * Green length can be 5 or 6 depending whether * we're operating in RGB555 or RGB565 mode. */ if (var->green.length != 5 && var->green.length != 6) var->green.length = 6; break; case 24: var->red.length = 8; var->blue.length = 8; var->green.length = 8; break; case 32: var->red.length = 8; var->green.length = 8; var->blue.length = 8; var->transp.length = 8; break; default: ret = -EINVAL; break; } /* * >= 16bpp displays have separate colour component bitfields * encoded in the pixel data. Calculate their position from * the bitfield length defined above. */ if (ret == 0 && var->bits_per_pixel >= 24 && fbswap) { var->blue.offset = 0; var->green.offset = var->blue.offset + var->blue.length; var->red.offset = var->green.offset + var->green.length; var->transp.offset = var->red.offset + var->red.length; } else if (ret == 0 && var->bits_per_pixel >= 24) { var->red.offset = 0; var->green.offset = var->red.offset + var->red.length; var->blue.offset = var->green.offset + var->green.length; var->transp.offset = var->blue.offset + var->blue.length; } else if (ret == 0 && var->bits_per_pixel >= 16) { var->blue.offset = 0; var->green.offset = var->blue.offset + var->blue.length; var->red.offset = var->green.offset + var->green.length; var->transp.offset = var->red.offset + var->red.length; } return ret; } static int bcm2708_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { /* info input, var output */ int yres; /* info input, var output */ print_debug("bcm2708_fb_check_var info(%p) %dx%d (%dx%d), %d, %d\n", info, info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual, (int)info->screen_size, info->var.bits_per_pixel); print_debug("bcm2708_fb_check_var var(%p) %dx%d (%dx%d), %d\n", var, var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->bits_per_pixel); if (!var->bits_per_pixel) var->bits_per_pixel = 16; if (bcm2708_fb_set_bitfields(var) != 0) { pr_err("bcm2708_fb_check_var: invalid bits_per_pixel %d\n", var->bits_per_pixel); return -EINVAL; } if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; /* use highest possible virtual resolution */ if (var->yres_virtual == -1) { var->yres_virtual = 480; pr_err ("bcm2708_fb_check_var: virtual resolution set to maximum of %dx%d\n", var->xres_virtual, var->yres_virtual); } if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; if (var->xoffset < 0) var->xoffset = 0; if (var->yoffset < 0) var->yoffset = 0; /* truncate xoffset and yoffset to maximum if too high */ if (var->xoffset > var->xres_virtual - var->xres) var->xoffset = var->xres_virtual - var->xres - 1; if (var->yoffset > var->yres_virtual - var->yres) var->yoffset = var->yres_virtual - var->yres - 1; yres = var->yres; if (var->vmode & FB_VMODE_DOUBLE) yres *= 2; else if (var->vmode & FB_VMODE_INTERLACED) yres = (yres + 1) / 2; if (var->xres * yres > 1920 * 1200) { pr_err("bcm2708_fb_check_var: ERROR: Pixel size >= 1920x1200; " "special treatment required! (TODO)\n"); return -EINVAL; } return 0; } static int bcm2708_fb_set_par(struct fb_info *info) { uint32_t val = 0; struct bcm2708_fb *fb = to_bcm2708(info); volatile struct fbinfo_s *fbinfo = fb->info; fbinfo->xres = info->var.xres; fbinfo->yres = info->var.yres; fbinfo->xres_virtual = info->var.xres_virtual; fbinfo->yres_virtual = info->var.yres_virtual; fbinfo->bpp = info->var.bits_per_pixel; fbinfo->xoffset = info->var.xoffset; fbinfo->yoffset = info->var.yoffset; fbinfo->base = 0; /* filled in by VC */ fbinfo->pitch = 0; /* filled in by VC */ print_debug("bcm2708_fb_set_par info(%p) %dx%d (%dx%d), %d, %d\n", info, info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual, (int)info->screen_size, info->var.bits_per_pixel); /* ensure last write to fbinfo is visible to GPU */ wmb(); /* inform vc about new framebuffer */ bcm_mailbox_write(MBOX_CHAN_FB, fb->dma); /* TODO: replace fb driver with vchiq version */ /* wait for response */ bcm_mailbox_read(MBOX_CHAN_FB, &val); /* ensure GPU writes are visible to us */ rmb(); if (val == 0) { fb->fb.fix.line_length = fbinfo->pitch; if (info->var.bits_per_pixel <= 8) fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; else fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; fb->fb.fix.smem_start = fbinfo->base; fb->fb.fix.smem_len = fbinfo->pitch * fbinfo->yres_virtual; fb->fb.screen_size = fbinfo->screen_size; if (fb->fb.screen_base) iounmap(fb->fb.screen_base); fb->fb.screen_base = (void *)ioremap_wc(fb->fb.fix.smem_start, fb->fb.screen_size); if (!fb->fb.screen_base) { /* the console may currently be locked */ console_trylock(); console_unlock(); BUG(); /* what can we do here */ } } print_debug ("BCM2708FB: start = %p,%p width=%d, height=%d, bpp=%d, pitch=%d size=%d success=%d\n", (void *)fb->fb.screen_base, (void *)fb->fb.fix.smem_start, fbinfo->xres, fbinfo->yres, fbinfo->bpp, fbinfo->pitch, (int)fb->fb.screen_size, val); return val; } 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 bcm2708_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info) { struct bcm2708_fb *fb = to_bcm2708(info); /*print_debug("BCM2708FB: setcolreg %d:(%02x,%02x,%02x,%02x) %x\n", regno, red, green, blue, transp, fb->fb.fix.visual);*/ if (fb->fb.var.bits_per_pixel <= 8) { if (regno < 256) { /* blue [0:4], green [5:10], red [11:15] */ fb->info->cmap[regno] = ((red >> (16-5)) & 0x1f) << 11 | ((green >> (16-6)) & 0x3f) << 5 | ((blue >> (16-5)) & 0x1f) << 0; } /* Hack: we need to tell GPU the palette has changed, but currently bcm2708_fb_set_par takes noticable time when called for every (256) colour */ /* So just call it for what looks like the last colour in a list for now. */ if (regno == 15 || regno == 255) bcm2708_fb_set_par(info); } else 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 regno > 255; } static int bcm2708_fb_blank(int blank_mode, struct fb_info *info) { /*print_debug("bcm2708_fb_blank\n"); */ return -1; } static void bcm2708_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { /* (is called) print_debug("bcm2708_fb_fillrect\n"); */ cfb_fillrect(info, rect); } static void bcm2708_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) { /*print_debug("bcm2708_fb_copyarea\n"); */ cfb_copyarea(info, region); } static void bcm2708_fb_imageblit(struct fb_info *info, const struct fb_image *image) { /* (is called) print_debug("bcm2708_fb_imageblit\n"); */ cfb_imageblit(info, image); } static struct fb_ops bcm2708_fb_ops = { .owner = THIS_MODULE, .fb_check_var = bcm2708_fb_check_var, .fb_set_par = bcm2708_fb_set_par, .fb_setcolreg = bcm2708_fb_setcolreg, .fb_blank = bcm2708_fb_blank, .fb_fillrect = bcm2708_fb_fillrect, .fb_copyarea = bcm2708_fb_copyarea, .fb_imageblit = bcm2708_fb_imageblit, }; static int bcm2708_fb_register(struct bcm2708_fb *fb) { int ret; dma_addr_t dma; void *mem; mem = dma_alloc_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), &dma, GFP_KERNEL); if (NULL == mem) { pr_err(": unable to allocate fbinfo buffer\n"); ret = -ENOMEM; } else { fb->info = (struct fbinfo_s *)mem; fb->dma = dma; } fb->fb.fbops = &bcm2708_fb_ops; fb->fb.flags = FBINFO_FLAG_DEFAULT; fb->fb.pseudo_palette = fb->cmap; strncpy(fb->fb.fix.id, bcm2708_name, sizeof(fb->fb.fix.id)); fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; fb->fb.fix.type_aux = 0; fb->fb.fix.xpanstep = 0; fb->fb.fix.ypanstep = 0; fb->fb.fix.ywrapstep = 0; fb->fb.fix.accel = FB_ACCEL_NONE; fb->fb.var.xres = fbwidth; fb->fb.var.yres = fbheight; fb->fb.var.xres_virtual = fbwidth; fb->fb.var.yres_virtual = fbheight; fb->fb.var.bits_per_pixel = fbdepth; fb->fb.var.vmode = FB_VMODE_NONINTERLACED; fb->fb.var.activate = FB_ACTIVATE_NOW; fb->fb.var.nonstd = 0; fb->fb.var.height = -1; /* height of picture in mm */ fb->fb.var.width = -1; /* width of picture in mm */ fb->fb.var.accel_flags = 0; fb->fb.monspecs.hfmin = 0; fb->fb.monspecs.hfmax = 100000; fb->fb.monspecs.vfmin = 0; fb->fb.monspecs.vfmax = 400; fb->fb.monspecs.dclkmin = 1000000; fb->fb.monspecs.dclkmax = 100000000; bcm2708_fb_set_bitfields(&fb->fb.var); /* * Allocate colourmap. */ fb_set_var(&fb->fb, &fb->fb.var); print_debug("BCM2708FB: registering framebuffer (%dx%d@%d) (%d)\n", fbwidth fbheight, fbdepth, fbswap); ret = register_framebuffer(&fb->fb); print_debug("BCM2708FB: register framebuffer (%d)\n", ret); if (ret == 0) goto out; print_debug("BCM2708FB: cannot register framebuffer (%d)\n", ret); out: return ret; } static int bcm2708_fb_probe(struct platform_device *dev) { struct bcm2708_fb *fb; int ret; fb = kmalloc(sizeof(struct bcm2708_fb), GFP_KERNEL); if (!fb) { dev_err(&dev->dev, "could not allocate new bcm2708_fb struct\n"); ret = -ENOMEM; goto free_region; } memset(fb, 0, sizeof(struct bcm2708_fb)); fb->dev = dev; ret = bcm2708_fb_register(fb); if (ret == 0) { platform_set_drvdata(dev, fb); goto out; } kfree(fb); free_region: dev_err(&dev->dev, "probe failed, err %d\n", ret); out: return ret; } static int bcm2708_fb_remove(struct platform_device *dev) { struct bcm2708_fb *fb = platform_get_drvdata(dev); platform_set_drvdata(dev, NULL); if (fb->fb.screen_base) iounmap(fb->fb.screen_base); unregister_framebuffer(&fb->fb); dma_free_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), (void *)fb->info, fb->dma); kfree(fb); return 0; } static struct platform_driver bcm2708_fb_driver = { .probe = bcm2708_fb_probe, .remove = bcm2708_fb_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, }; static int __init bcm2708_fb_init(void) { return platform_driver_register(&bcm2708_fb_driver); } module_init(bcm2708_fb_init); static void __exit bcm2708_fb_exit(void) { platform_driver_unregister(&bcm2708_fb_driver); } module_exit(bcm2708_fb_exit); module_param(fbwidth, int, 0644); module_param(fbheight, int, 0644); module_param(fbdepth, int, 0644); module_param(fbswap, int, 0644); MODULE_DESCRIPTION("BCM2708 framebuffer driver"); MODULE_LICENSE("GPL"); MODULE_PARM_DESC(fbwidth, "Width of ARM Framebuffer"); MODULE_PARM_DESC(fbheight, "Height of ARM Framebuffer"); MODULE_PARM_DESC(fbdepth, "Bit depth of ARM Framebuffer"); MODULE_PARM_DESC(fbswap, "Swap order of red and blue in 24 and 32 bit modes");