diff options
author | Kristoffer <kristoffer@Buttox.(none)> | 2009-05-15 20:14:08 +0200 |
---|---|---|
committer | Kristoffer Ericson <kristoffer.ericson@gmail.com> | 2011-12-10 10:34:48 +0100 |
commit | 0990cf467190247a01cfa325ec6344efdf96ecaa (patch) | |
tree | 1be00511f8f3a9f6fcba5723ac2bf1c2e75f249f | |
parent | cd8fdc50b39b78ce10f4cba14eecbdb85613915e (diff) | |
download | linux-hpc-0990cf467190247a01cfa325ec6344efdf96ecaa.tar.gz |
s1d13xxxfb: Backport 2D acceleration * Since the s1d13xxxfb driver is pretty much alone, its quite easy to do a backport. So we add 2D accel (fill_rect & copy_area).
-rw-r--r-- | drivers/video/s1d13xxxfb.c | 331 | ||||
-rw-r--r-- | include/video/s1d13xxxfb.h | 28 |
2 files changed, 329 insertions, 30 deletions
diff --git a/drivers/video/s1d13xxxfb.c b/drivers/video/s1d13xxxfb.c index cee73c61d6f984..b496e9ac3834a9 100644 --- a/drivers/video/s1d13xxxfb.c +++ b/drivers/video/s1d13xxxfb.c @@ -2,6 +2,7 @@ * * (c) 2004 Simtec Electronics * (c) 2005 Thibaut VARENE <varenet@parisc-linux.org> + * (c) 2009 Kristoffer Ericson <kristoffer.ericson@gmail.com * * Driver for Epson S1D13xxx series framebuffer chips * @@ -10,17 +11,8 @@ * linux/drivers/video/epson1355fb.c * linux/drivers/video/epson/s1d13xxxfb.c (2.4 driver by Epson) * - * Note, currently only tested on S1D13806 with 16bit CRT. - * As such, this driver might still contain some hardcoded bits relating to - * S1D13806. - * Making it work on other S1D13XXX chips should merely be a matter of adding - * a few switch()s, some missing glue here and there maybe, and split header - * files. - * * TODO: - handle dual screen display (CRT and LCD at the same time). * - check_var(), mode change, etc. - * - PM untested. - * - Accelerated interfaces. * - Probably not SMP safe :) * * This file is subject to the terms and conditions of the GNU General Public @@ -37,11 +29,16 @@ #include <linux/mm.h> #include <linux/mman.h> #include <linux/fb.h> +#include <linux/spinlock_types.h> +#include <linux/spinlock.h> #include <asm/io.h> #include <video/s1d13xxxfb.h> +//#include "fb_draw.h" + + #define PFX "s1d13xxxfb: " #if 0 @@ -50,6 +47,35 @@ #define dbg(fmt, args...) do { } while (0) #endif +/* to limit the output we use this for + bitblit bugtracking */ + +#if 0 +#define dbg_bitblt(fmt, args...) do {printk(KERN_ERR fmt, ## args); } while(0) +#else +#define dbg_bitblt(fmt, args...) do { } while (0) +#endif + +static DEFINE_SPINLOCK(s1d13xxxfb_bitblt_lock); + +/* + * List of card production ids + */ +static const int s1d13xxxfb_prod_ids[] = { + S1D13505_PROD_ID, + S1D13506_PROD_ID, + S1D13806_PROD_ID, +}; + +/* + * List of card strings + */ +static const char *s1d13xxxfb_prod_names[] = { + "S1D13505", + "S1D13506", + "S1D13806", +}; + /* * Here we define the default struct fb_fix_screeninfo */ @@ -372,9 +398,240 @@ s1d13xxxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) return 0; } +/* + * + * waits until register changes INTO bit + * + */ +u8 bltbit_wait_bitset(struct fb_info *info, u8 bit, int timeout) +{ + while (!(s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0) & bit)) { + udelay(10); + if (!--timeout) { + printk(KERN_ERR "s1d13xxxfb_bitblt : wait_bitset timeout\n"); + break; + } + } -/* framebuffer information structures */ + return timeout; +} + +/** + * bltbit_wait_bitclear - waits for change in register value + * @info : frambuffer structure + * @bit : value currently in register + * @timeout : ... + * + * waits until value changes FROM bit + * + */ +u8 bltbit_wait_bitclear(struct fb_info *info, u8 bit, int timeout) +{ + while (s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0) & bit) { + udelay(10); + if (!--timeout) { + printk(KERN_ERR "s1d13xxxfb_bitblt : wait_bitclear timeout\n"); + break; + } + } + + return timeout; +} + +/** + * bltbit_fifo_status - checks the current status of the fifo + * @info : framebuffer structure + * + * returns number of free words in buffer + */ +u8 bltbit_fifo_status(struct fb_info *info) +{ + u8 status; + + status = s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0); + + /* its empty so room for 16 words */ + if (status & BBLT_FIFO_EMPTY) + return 16; + + /* its full so we dont want to add */ + if (status & BBLT_FIFO_FULL) + return 0; + /* its atleast half full but we can add one atleast */ + if (status & BBLT_FIFO_NOT_FULL) + return 1; + + return 0; +} + +void s1d13xxxfb_bitblt_copyarea(struct fb_info *info, const struct fb_copyarea *area) +{ + u32 dst, src; + u32 stride; + u16 reverse = 0; + u16 sx = area->sx, sy = area->sy; + u16 dx = area->dx, dy = area->dy; + u16 width = area->width, height = area->height; + u16 bpp; + + spin_lock(&s1d13xxxfb_bitblt_lock); + + /* bytes per xres line */ + bpp = (info->var.bits_per_pixel >> 3); + stride = bpp * info->var.xres; + + /* reverse, calculate the last pixel in rectangle */ + if ((dy > sy) || ((dy == sy) && (dx >= sx))) { + dst = (((dy + height - 1) * stride) + (bpp * (dx + width - 1))); + src = (((sy + height - 1) * stride) + (bpp * (sx + width - 1))); + reverse = 1; + /* not reverse, calculate the first pixel in rectangle */ + } else { /* (y * xres) + (bpp * x) */ + dst = (dy * stride) + (bpp * dx); + src = (sy * stride) + (bpp * sx); + } + + /* set source adress */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_SRC_START0, (src & 0xff)); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_SRC_START1, (src >> 8) & 0x00ff); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_SRC_START2, (src >> 16) & 0x00ff); + + /* set destination adress */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START0, (dst & 0xff)); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START1, (dst >> 8) & 0x00ff); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START2, (dst >> 16) & 0x00ff); + + /* program height and width */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH0, (width & 0xff) - 1); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH1, (width >> 8)); + + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT0, (height & 0xff) - 1); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT1, (height >> 8)); + + /* negative direction ROP */ + if (reverse == 1) { + printk(KERN_ERR "s1d13xxxfb_bitblt_copyarea : negative ROP\n"); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_OP, 0x03); + } else /* positive direction ROP */ { + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_OP, 0x02); + printk(KERN_ERR "s1d13xxxfb_bitblt_copyarea : positive ROP\n"); + } + + /* set for rectangel mode and not linear */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x0); + + /* setup the bpp 1 = 16bpp, 0 = 8bpp*/ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL1, (bpp >> 1)); + + /* set words per xres */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF0, (stride >> 1) & 0xff); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF1, (stride >> 9)); + + dbg_bitblt("s1d13xxxfb_bitblt_copyarea : dx=%d, dy=%d\n", dx, dy); + dbg_bitblt("s1d13xxxfb_bitblt_copyarea : sx=%d, sy=%d\n", sx, sy); + dbg_bitblt("s1d13xxxfb_bitblt_copyarea : width=%d, height=%d\n", width - 1, height - 1); + dbg_bitblt("s1d13xxxfb_bitblt_copyarea : stride=%d\n", stride); + dbg_bitblt("s1d13xxxfb_bitblt_copyarea : bpp=%d=0x0%d, mem_offset1=%d, mem_offset2=%d\n", bpp, (bpp >> 1), (stride >> 1) & 0xff, stride >> 9); + + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CC_EXP, 0x0c); + + /* initialize the engine */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x80); + + /* wait to complete */ + bltbit_wait_bitclear(info, 0x80, 8000); + + spin_unlock(&s1d13xxxfb_bitblt_lock); +} + +/** + * @info pointer to fb_info structure + * @rect pointer to fb_fillrect structure + * + * Supported for : 13506, 13806 (and probably more) + * Not supported : 13505, + * + * Accelerated function for fb_fillrect through + * bitblt soldfill. + * + **/ +void s1d13xxxfb_bitblt_solidfill(struct fb_info *info, const struct fb_fillrect *rect) +{ + u32 screen_stride, dest; + u32 fg; + u16 bpp = (info->var.bits_per_pixel >> 3); + + /* grab spinlock */ + spin_lock(&s1d13xxxfb_bitblt_lock); + + /* bytes per x width */ + screen_stride = (bpp * info->var.xres); + + /* bytes to starting point */ + dest = ((rect->dy * screen_stride) + (bpp * rect->dx)); + + dbg_bitblt("s1d13xxxfb_bitblt_solidfill : dx=%d, dy=%d, stride=%d, dest=%d\n" + "s1d13xxxfb_bitblt_solidfill : rect_width=%d, rect_height=%d\n", + rect->dx, rect->dy, screen_stride, dest, + rect->width - 1, rect->height - 1); + + dbg_bitblt("s1d13xxxfb_bitblt_solidfill : xres=%d, yres=%d, bpp=%d\n", + info->var.xres, info->var.yres, + info->var.bits_per_pixel); + dbg_bitblt("s1d13xxxfb_bitblt_solidfill : ROP=%d\n", rect->rop); + + /* We split the destination into the three registers */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START0, (dest & 0x00ff)); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START1, ((dest >> 8) & 0x00ff)); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_DST_START2, ((dest >> 16) & 0x00ff)); + + /* give information regarding rectangel width */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH0, ((rect->width) & 0x00ff) - 1); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_WIDTH1, (rect->width >> 8)); + + /* give information regarding rectangel height */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT0, ((rect->height) & 0x00ff) - 1); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_HEIGHT1, (rect->height >> 8)); + + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) { + fg = ((u32 *)info->pseudo_palette)[rect->color]; + dbg_bitblt("s1d13xxxfb_bitblt_solidfill : TRUECOLOR/DIRECTCOLOR\n"); + dbg_bitblt("s1d13xxxfb_bitblt_solidfill : pseudo_palette[%d] = %d\n", rect->color, fg); + } else { + fg = rect->color; + dbg_bitblt("s1d13xxxfb_bitblt_solidfill : color = %d\n", rect->color); + } + + /* set foreground color */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_FGC0, (fg & 0xff)); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_FGC1, (fg >> 8) & 0xff); + + /* set rectangual region of memory (rectangle and not linear) */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x0); + + /* set operation mode SOLID_FILL */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_OP, BBLT_SOLID_FILL); + + /* set bits per pixel (1 = 16bpp, 0 = 8bpp) */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL1, (info->var.bits_per_pixel >> 4)); + + /* set the memory offset for the bblt in word sizes */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF0, (screen_stride >> 1) & 0x00ff); + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_MEM_OFF1, (screen_stride >> 9)); + + /* and away we go.... */ + s1d13xxxfb_writereg(info->par, S1DREG_BBLT_CTL0, 0x80); + + /* wait until its done */ + bltbit_wait_bitclear(info, 0x80, 8000); + + /* let others play */ + spin_unlock(&s1d13xxxfb_bitblt_lock); +} + +/* framebuffer information structures */ static struct fb_ops s1d13xxxfb_fbops = { .owner = THIS_MODULE, .fb_set_par = s1d13xxxfb_set_par, @@ -383,7 +640,7 @@ static struct fb_ops s1d13xxxfb_fbops = { .fb_pan_display = s1d13xxxfb_pan_display, - /* to be replaced by any acceleration we can */ + /* gets replaced at chip detection time */ .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, @@ -538,7 +795,8 @@ s1d13xxxfb_probe(struct platform_device *pdev) struct fb_info *info; struct s1d13xxxfb_pdata *pdata = NULL; int ret = 0; - u8 revision; + int i; + u8 revision, prod_id; dbg("probe called: device is %p\n", pdev); @@ -551,7 +809,6 @@ s1d13xxxfb_probe(struct platform_device *pdev) if (pdata && pdata->platform_init_video) pdata->platform_init_video(); - if (pdev->num_resources != 2) { dev_err(&pdev->dev, "invalid num_resources: %i\n", pdev->num_resources); @@ -607,25 +864,56 @@ s1d13xxxfb_probe(struct platform_device *pdev) goto bail; } - revision = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE); - if ((revision >> 2) != S1D_CHIP_REV) { - printk(KERN_INFO PFX "chip not found: %i\n", (revision >> 2)); - ret = -ENODEV; + /* production id is top 6 bits */ + prod_id = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) >> 2; + /* revision id is lower 2 bits */ + revision = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE) & 0x3; + ret = -ENODEV; + + for (i = 0; i < ARRAY_SIZE(s1d13xxxfb_prod_ids); i++) { + if (prod_id == s1d13xxxfb_prod_ids[i]) { + /* looks like we got it in our list */ + default_par->prod_id = prod_id; + default_par->revision = revision; + ret = 0; + break; + } + } + + if (!ret) { + printk(KERN_INFO PFX "chip production id %i = %s\n", + prod_id, s1d13xxxfb_prod_names[i]); + printk(KERN_INFO PFX "chip revision %i\n", revision); + } else { + printk(KERN_INFO PFX + "unknown chip production id %i, revision %i\n", + prod_id, revision); + printk(KERN_INFO PFX "please contant maintainer\n"); goto bail; } info->fix = s1d13xxxfb_fix; info->fix.mmio_start = pdev->resource[1].start; - info->fix.mmio_len = pdev->resource[1].end - pdev->resource[1].start +1; + info->fix.mmio_len = pdev->resource[1].end - pdev->resource[1].start + 1; info->fix.smem_start = pdev->resource[0].start; - info->fix.smem_len = pdev->resource[0].end - pdev->resource[0].start +1; + info->fix.smem_len = pdev->resource[0].end - pdev->resource[0].start + 1; printk(KERN_INFO PFX "regs mapped at 0x%p, fb %d KiB mapped at 0x%p\n", default_par->regs, info->fix.smem_len / 1024, info->screen_base); info->par = default_par; - info->fbops = &s1d13xxxfb_fbops; info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; + info->fbops = &s1d13xxxfb_fbops; + + switch(prod_id) { + case S1D13506_PROD_ID : /* activate acceleration */ + info->fbops->fb_fillrect = s1d13xxxfb_bitblt_solidfill; + info->fbops->fb_copyarea = s1d13xxxfb_bitblt_copyarea; + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA; + break; + default : + break; + } /* perform "manual" chip initialization, if needed */ if (pdata && pdata->initregs) @@ -753,8 +1041,11 @@ static struct platform_driver s1d13xxxfb_driver = { static int __init s1d13xxxfb_init(void) { + +#ifndef MODULE if (fb_get_options("s1d13xxxfb", NULL)) return -ENODEV; +#endif return platform_driver_register(&s1d13xxxfb_driver); } diff --git a/include/video/s1d13xxxfb.h b/include/video/s1d13xxxfb.h index 2af4c6759ff250..f0736cff2ca3f1 100644 --- a/include/video/s1d13xxxfb.h +++ b/include/video/s1d13xxxfb.h @@ -14,18 +14,16 @@ #define S1D13XXXFB_H #define S1D_PALETTE_SIZE 256 +#define S1D_FBID "S1D13xxx" +#define S1D_DEVICENAME "s1d13xxxfb" -#if defined(CONFIG_SA1100_JORNADA720) -#define S1D_CHIP_REV 4 /* expected chip revision number for s1d13506 */ -#else -#define S1D_CHIP_REV 7 /* expected chip revision number for s1d13806 */ -#endif - -#define S1D_FBID "S1D13806" -#define S1D_DEVICENAME "s1d13806fb" +/* S1DREG_REV_CODE register = prod_id (6 bits) + revision (2 bits) */ +#define S1D13505_PROD_ID 0x3 /* 000011 */ +#define S1D13506_PROD_ID 0x4 /* 000100 */ +#define S1D13806_PROD_ID 0x7 /* 000111 */ /* register definitions (tested on s1d13896) */ -#define S1DREG_REV_CODE 0x0000 /* Revision Code Register */ +#define S1DREG_REV_CODE 0x0000 /* Prod + Rev Code Register */ #define S1DREG_MISC 0x0001 /* Miscellaneous Register */ #define S1DREG_GPIO_CNF0 0x0004 /* General IO Pins Configuration Register 0 */ #define S1DREG_GPIO_CNF1 0x0005 /* General IO Pins Configuration Register 1 */ @@ -138,6 +136,15 @@ #define S1DREG_DELAYOFF 0xFFFE #define S1DREG_DELAYON 0xFFFF +#define BBLT_FIFO_EMPTY 0x00 +#define BBLT_FIFO_NOT_EMPTY 0x40 +#define BBLT_FIFO_NOT_FULL 0x30 +#define BBLT_FIFO_HALF_FULL 0x20 +#define BBLT_FIFO_FULL 0x10 + +#define BBLT_SOLID_FILL 0x0c + + /* Note: all above defines should go in separate header files when implementing other S1D13xxx chip support. */ @@ -146,10 +153,11 @@ struct s1d13xxxfb_regval { u8 value; }; - struct s1d13xxxfb_par { void __iomem *regs; unsigned char display; + unsigned char prod_id; + unsigned char revision; unsigned int pseudo_palette[16]; #ifdef CONFIG_PM |