Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * 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/delay.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 <mach/dma.h>
- #include <mach/platform.h>
- #include <mach/vcio.h>
- #include <asm/sizes.h>
- #include <linux/io.h>
- #include <linux/dma-mapping.h>
- /* This is limited to 16 characters when displayed by X startup */
- static const char *bcm2708_name = "BCM2708 FB";
- #define DRIVER_NAME "bcm2708_fb"
- /* 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;
- };
- struct bcm2708_fb {
- struct fb_info fb;
- struct platform_device *dev;
- struct fbinfo_s *info;
- dma_addr_t dma;
- u32 cmap[16];
- int dma_chan;
- int dma_irq;
- void __iomem *dma_chan_base;
- void *cb_base; /* DMA control blocks */
- dma_addr_t cb_handle;
- };
- #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 >= 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;
- /* memory size in pixels */
- unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
- /* info input, var output */
- pr_info("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);
- pr_info("bcm2708_fb_check_var var(%p) %dx%d (%dx%d), %d, %d\n", var,
- var->xres, var->yres, var->xres_virtual, var->yres_virtual,
- var->bits_per_pixel, pixels);
- 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 (yres > 1200) {
- pr_err("bcm2708_fb_check_var: ERROR: VerticalTotal >= 1200; "
- "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 */
- pr_info("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);
- msleep(10);
- /* 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)
- BUG(); /* what can we do here */
- }
- pr_info
- ("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);
- 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)
- {
- /*pr_info("bcm2708_fb_blank\n"); */
- return -1;
- }
- static void bcm2708_fb_fillrect(struct fb_info *info,
- const struct fb_fillrect *rect)
- {
- struct bcm2708_fb *fb = to_bcm2708(info);
- struct bcm2708_dma_cb *cb = (struct bcm2708_dma_cb *)fb->cb_base;
- unsigned int *color = (unsigned int *)fb->cb_base + 8;
- unsigned int c = rect->color << 16 | rect->color;
- if (rect->rop != ROP_COPY) {
- cfb_fillrect(info, rect);
- return;
- }
- cb->info = BCM2708_DMA_BURST(0) |
- BCM2708_DMA_S_WIDTH |
- BCM2708_DMA_D_WIDTH |
- BCM2708_DMA_D_INC | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_TDMODE;
- cb->dst =
- fb->fb.fix.smem_start + 2 * rect->dx +
- rect->dy * fb->fb.fix.line_length;
- cb->src = fb->cb_handle + 32;
- cb->length = BCM2708_DMA_TDMODE_LEN(2 * rect->width, rect->height);
- cb->stride = (fb->fb.fix.line_length - 2 * rect->width) << 16;
- cb->next = 0;
- cb->pad[0] = 0;
- cb->pad[1] = 0;
- color[0] = c;
- color[1] = c;
- color[2] = c;
- color[3] = c;
- color[4] = c;
- color[5] = c;
- color[6] = c;
- color[7] = c;
- bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
- bcm_dma_wait_idle(fb->dma_chan_base);
- }
- static void bcm2708_fb_copyarea(struct fb_info *info,
- const struct fb_copyarea *region)
- {
- struct bcm2708_fb *fb = to_bcm2708(info);
- struct bcm2708_dma_cb *cb = fb->cb_base;
- int bytes_per_pixel = (info->var.bits_per_pixel + 7)>>3;
- cb->info = BCM2708_DMA_BURST(0) |
- BCM2708_DMA_S_WIDTH |
- BCM2708_DMA_S_INC |
- BCM2708_DMA_D_WIDTH |
- BCM2708_DMA_D_INC | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_TDMODE;
- cb->dst =
- fb->fb.fix.smem_start + bytes_per_pixel * region->dx +
- region->dy * fb->fb.fix.line_length;
- cb->src =
- fb->fb.fix.smem_start + bytes_per_pixel * region->sx +
- region->sy * fb->fb.fix.line_length;
- cb->length = BCM2708_DMA_TDMODE_LEN(bytes_per_pixel * region->width, region->height);
- cb->stride =
- ((fb->fb.fix.line_length -
- bytes_per_pixel * region->width) << 16) | (fb->fb.fix.line_length -
- bytes_per_pixel * region->width);
- cb->next = 0;
- cb->pad[0] = 0;
- cb->pad[1] = 0;
- bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
- bcm_dma_wait_idle(fb->dma_chan_base);
- }
- static void bcm2708_fb_imageblit(struct fb_info *info,
- const struct fb_image *image)
- {
- struct bcm2708_fb *fb = to_bcm2708(info);
- struct bcm2708_dma_cb *cb = fb->cb_base;
- if (image->depth != 16) {
- cfb_imageblit(info, image);
- return;
- }
- cb->info = BCM2708_DMA_BURST(0) |
- BCM2708_DMA_S_WIDTH |
- BCM2708_DMA_S_INC |
- BCM2708_DMA_D_WIDTH |
- BCM2708_DMA_D_INC | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_TDMODE;
- cb->dst =
- fb->fb.fix.smem_start + 2 * image->dx +
- image->dy * fb->fb.fix.line_length;
- cb->src = (unsigned long)image->data;
- cb->length = BCM2708_DMA_TDMODE_LEN(2 * image->width, image->height);
- cb->stride =
- ((fb->fb.fix.line_length -
- 2 * image->width) << 16) | (image->width * 2);
- cb->next = 0;
- cb->pad[0] = 0;
- cb->pad[1] = 0;
- // flush_kern_dcache_area(image->data, width * height);
- bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
- bcm_dma_wait_idle(fb->dma_chan_base);
- }
- static void bcm2708_fb_destroy(struct fb_info *info)
- {
- pr_debug("bcm2708_fb_destroy\n");
- }
- 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,
- .fb_destroy = bcm2708_fb_destroy,
- };
- static int fbwidth = 800; /* module parameter */
- static int fbheight = 480; /* module parameter */
- 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 | FBINFO_HWACCEL_COPYAREA |
- FBINFO_HWACCEL_FILLRECT /* | FBINFO_HWACCEL_IMAGEBLIT | FBINFO_READS_FAST*/;
- 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 = 16;
- fb->fb.var.vmode = FB_VMODE_NONINTERLACED;
- fb->fb.var.activate = FB_ACTIVATE_NOW;
- fb->fb.var.nonstd = 0;
- fb->fb.var.height = fbwidth;
- fb->fb.var.width = fbheight;
- 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);
- pr_info("BCM2708FB: registering framebuffer (%d, %d)\n", fbwidth,
- fbheight);
- ret = register_framebuffer(&fb->fb);
- pr_info("BCM2708FB: register framebuffer (%d)\n", ret);
- if (ret == 0)
- goto out;
- pr_info("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->cb_base = dma_alloc_writecombine(&dev->dev, SZ_4K,
- &fb->cb_handle, GFP_KERNEL);
- if (!fb->cb_base) {
- dev_err(&dev->dev, "cannot allocate DMA CBs\n");
- ret = -ENOMEM;
- goto free_fb;
- }
- pr_info("BCM2708FB: allocated DMA memory %08x\n",
- fb->cb_handle);
- ret = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST,
- &fb->dma_chan_base, &fb->dma_irq);
- if (ret < 0) {
- dev_err(&dev->dev, "couldn't allocate a DMA channel\n");
- goto free_cb;
- }
- fb->dma_chan = ret;
- pr_info("BCM2708FB: allocated DMA channel %d @ %p\n",
- fb->dma_chan, fb->dma_chan_base);
- fb->dev = dev;
- ret = bcm2708_fb_register(fb);
- if (ret == 0) {
- platform_set_drvdata(dev, fb);
- goto out;
- }
- free_cb:
- dma_free_writecombine(&dev->dev, SZ_4K, fb->cb_base, fb->cb_handle);
- free_fb:
- 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_writecombine(&dev->dev, SZ_4K, fb->cb_base, fb->cb_handle);
- bcm_dma_chan_free(fb->dma_chan);
- 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_DESCRIPTION("BCM2708 framebuffer driver");
- MODULE_LICENSE("GPL");
- MODULE_PARM_DESC(fbwidth, "Width of ARM Framebuffer");
- MODULE_PARM_DESC(fbheight, "Height of ARM Framebuffer");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement