aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKristoffer <kristoffer@Buttox.(none)>2009-05-15 20:14:08 +0200
committerKristoffer Ericson <kristoffer.ericson@gmail.com>2011-12-10 10:34:48 +0100
commit0990cf467190247a01cfa325ec6344efdf96ecaa (patch)
tree1be00511f8f3a9f6fcba5723ac2bf1c2e75f249f
parentcd8fdc50b39b78ce10f4cba14eecbdb85613915e (diff)
downloadlinux-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.c331
-rw-r--r--include/video/s1d13xxxfb.h28
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