ARM: Add support for the display controllers in VT8500 and WM8505
authorAlexey Charkov <alchark@gmail.com>
Mon, 8 Nov 2010 23:42:39 +0000 (02:42 +0300)
committerPaul Mundt <lethal@linux-sh.org>
Tue, 9 Nov 2010 09:52:07 +0000 (18:52 +0900)
This adds drivers for the LCD controller found in VIA VT8500 SoC,
GOVR display controller found in WonderMedia WM8505 SoC and for the
Graphics Engine present in both of them that provides hardware
accelerated raster operations (used for copyarea and fillrect).

Signed-off-by: Alexey Charkov <alchark@gmail.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/vt8500lcdfb.c [new file with mode: 0644]
drivers/video/vt8500lcdfb.h [new file with mode: 0644]
drivers/video/wm8505fb.c [new file with mode: 0644]
drivers/video/wm8505fb_regs.h [new file with mode: 0644]
drivers/video/wmt_ge_rops.c [new file with mode: 0644]
drivers/video/wmt_ge_rops.h [new file with mode: 0644]

index 27c1fb4b1e0d71f91dcdd7d528dafa3b3457cb32..954f6e9d8d5ab841c2ea70d9937ab3d1ee730128 100644 (file)
@@ -186,6 +186,14 @@ config FB_SYS_FOPS
        depends on FB
        default n
 
+config FB_WMT_GE_ROPS
+       tristate
+       depends on FB
+       default n
+       ---help---
+         Include functions for accelerated rectangle filling and area
+         copying using WonderMedia Graphics Engine operations.
+
 config FB_DEFERRED_IO
        bool
        depends on FB
@@ -1722,6 +1730,24 @@ config FB_AU1200
          various panels and CRTs by passing in kernel cmd line option
          au1200fb:panel=<name>.
 
+config FB_VT8500
+       bool "VT8500 LCD Driver"
+       depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500
+       select FB_WMT_GE_ROPS
+       select FB_SYS_IMAGEBLIT
+       help
+         This is the framebuffer driver for VIA VT8500 integrated LCD
+         controller.
+
+config FB_WM8505
+       bool "WM8505 frame buffer support"
+       depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505
+       select FB_WMT_GE_ROPS
+       select FB_SYS_IMAGEBLIT
+       help
+         This is the framebuffer driver for WonderMedia WM8505
+         integrated LCD controller.
+
 source "drivers/video/geode/Kconfig"
 
 config FB_HIT
index 485e8ed1318c4bd0a055553f784b6c642ca6cf81..8d916dcb379fda4229b151354b887409ef037164 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB)       += svgalib.o
 obj-$(CONFIG_FB_MACMODES)      += macmodes.o
 obj-$(CONFIG_FB_DDC)           += fb_ddc.o
 obj-$(CONFIG_FB_DEFERRED_IO)   += fb_defio.o
+obj-$(CONFIG_FB_WMT_GE_ROPS)   += wmt_ge_rops.o
 
 # Hardware specific drivers go first
 obj-$(CONFIG_FB_AMIGA)            += amifb.o c2p_planar.o
@@ -104,6 +105,8 @@ obj-$(CONFIG_FB_W100)                 += w100fb.o
 obj-$(CONFIG_FB_TMIO)            += tmiofb.o
 obj-$(CONFIG_FB_AU1100)                  += au1100fb.o
 obj-$(CONFIG_FB_AU1200)                  += au1200fb.o
+obj-$(CONFIG_FB_VT8500)                  += vt8500lcdfb.o
+obj-$(CONFIG_FB_WM8505)                  += wm8505fb.o
 obj-$(CONFIG_FB_PMAG_AA)         += pmag-aa-fb.o
 obj-$(CONFIG_FB_PMAG_BA)         += pmag-ba-fb.o
 obj-$(CONFIG_FB_PMAGB_B)         += pmagb-b-fb.o
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
new file mode 100644 (file)
index 0000000..7617f12
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on skeletonfb.c and pxafb.c
+ *
+ * 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/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <mach/vt8500fb.h>
+
+#include "vt8500lcdfb.h"
+#include "wmt_ge_rops.h"
+
+#define to_vt8500lcd_info(__info) container_of(__info, \
+                                               struct vt8500lcd_info, fb)
+
+static int vt8500lcd_set_par(struct fb_info *info)
+{
+       struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+       int reg_bpp = 5; /* 16bpp */
+       int i;
+       unsigned long control0;
+
+       if (!fbi)
+               return -EINVAL;
+
+       if (info->var.bits_per_pixel <= 8) {
+               /* palettized */
+               info->var.red.offset    = 0;
+               info->var.red.length    = info->var.bits_per_pixel;
+               info->var.red.msb_right = 0;
+
+               info->var.green.offset  = 0;
+               info->var.green.length  = info->var.bits_per_pixel;
+               info->var.green.msb_right = 0;
+
+               info->var.blue.offset   = 0;
+               info->var.blue.length   = info->var.bits_per_pixel;
+               info->var.blue.msb_right = 0;
+
+               info->var.transp.offset = 0;
+               info->var.transp.length = 0;
+               info->var.transp.msb_right = 0;
+
+               info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+               info->fix.line_length = info->var.xres_virtual /
+                                               (8/info->var.bits_per_pixel);
+       } else {
+               /* non-palettized */
+               info->var.transp.offset = 0;
+               info->var.transp.length = 0;
+               info->var.transp.msb_right = 0;
+
+               if (info->var.bits_per_pixel == 16) {
+                       /* RGB565 */
+                       info->var.red.offset = 11;
+                       info->var.red.length = 5;
+                       info->var.red.msb_right = 0;
+                       info->var.green.offset = 5;
+                       info->var.green.length = 6;
+                       info->var.green.msb_right = 0;
+                       info->var.blue.offset = 0;
+                       info->var.blue.length = 5;
+                       info->var.blue.msb_right = 0;
+               } else {
+                       /* Equal depths per channel */
+                       info->var.red.offset = info->var.bits_per_pixel
+                                                       * 2 / 3;
+                       info->var.red.length = info->var.bits_per_pixel / 3;
+                       info->var.red.msb_right = 0;
+                       info->var.green.offset = info->var.bits_per_pixel / 3;
+                       info->var.green.length = info->var.bits_per_pixel / 3;
+                       info->var.green.msb_right = 0;
+                       info->var.blue.offset = 0;
+                       info->var.blue.length = info->var.bits_per_pixel / 3;
+                       info->var.blue.msb_right = 0;
+               }
+
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+               info->fix.line_length = info->var.bits_per_pixel > 16 ?
+                                       info->var.xres_virtual << 2 :
+                                       info->var.xres_virtual << 1;
+       }
+
+       for (i = 0; i < 8; i++) {
+               if (bpp_values[i] == info->var.bits_per_pixel) {
+                       reg_bpp = i;
+                       continue;
+               }
+       }
+
+       control0 = readl(fbi->regbase) & ~0xf;
+       writel(0, fbi->regbase);
+       while (readl(fbi->regbase + 0x38) & 0x10)
+               /* wait */;
+       writel((((info->var.hsync_len - 1) & 0x3f) << 26)
+               | ((info->var.left_margin & 0xff) << 18)
+               | (((info->var.xres - 1) & 0x3ff) << 8)
+               | (info->var.right_margin & 0xff), fbi->regbase + 0x4);
+       writel((((info->var.vsync_len - 1) & 0x3f) << 26)
+               | ((info->var.upper_margin & 0xff) << 18)
+               | (((info->var.yres - 1) & 0x3ff) << 8)
+               | (info->var.lower_margin & 0xff), fbi->regbase + 0x8);
+       writel((((info->var.yres - 1) & 0x400) << 2)
+               | ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10);
+       writel(0x80000000, fbi->regbase + 0x20);
+       writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase);
+
+       return 0;
+}
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+       chan &= 0xffff;
+       chan >>= 16 - bf->length;
+       return chan << bf->offset;
+}
+
+static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green,
+                          unsigned blue, unsigned transp,
+                          struct fb_info *info) {
+       struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+       int ret = 1;
+       unsigned int val;
+       if (regno >= 256)
+               return -EINVAL;
+
+       if (info->var.grayscale)
+               red = green = blue =
+                       (19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+       switch (fbi->fb.fix.visual) {
+       case FB_VISUAL_TRUECOLOR:
+               if (regno < 16) {
+                       u32 *pal = fbi->fb.pseudo_palette;
+
+                       val  = chan_to_field(red, &fbi->fb.var.red);
+                       val |= chan_to_field(green, &fbi->fb.var.green);
+                       val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+                       pal[regno] = val;
+                       ret = 0;
+               }
+               break;
+
+       case FB_VISUAL_STATIC_PSEUDOCOLOR:
+       case FB_VISUAL_PSEUDOCOLOR:
+               writew((red & 0xf800)
+                     | ((green >> 5) & 0x7e0)
+                     | ((blue >> 11) & 0x1f),
+                      fbi->palette_cpu + sizeof(u16) * regno);
+               break;
+       }
+
+       return ret;
+}
+
+static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd,
+                        unsigned long arg)
+{
+       int ret = 0;
+       struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+
+       if (cmd == FBIO_WAITFORVSYNC) {
+               /* Unmask End of Frame interrupt */
+               writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c);
+               ret = wait_event_interruptible_timeout(fbi->wait,
+                       readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10);
+               /* Mask back to reduce unwanted interrupt traffic */
+               writel(0xffffffff, fbi->regbase + 0x3c);
+               if (ret < 0)
+                       return ret;
+               if (ret == 0)
+                       return -ETIMEDOUT;
+       }
+
+       return ret;
+}
+
+static int vt8500lcd_pan_display(struct fb_var_screeninfo *var,
+                               struct fb_info *info)
+{
+       unsigned pixlen = info->fix.line_length / info->var.xres_virtual;
+       unsigned off = pixlen * var->xoffset
+                     + info->fix.line_length * var->yoffset;
+       struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+
+       writel((1 << 31)
+               | (((var->xres_virtual - var->xres) * pixlen / 4) << 20)
+               | (off >> 2), fbi->regbase + 0x20);
+       return 0;
+}
+
+static struct fb_ops vt8500lcd_ops = {
+       .owner          = THIS_MODULE,
+       .fb_set_par     = vt8500lcd_set_par,
+       .fb_setcolreg   = vt8500lcd_setcolreg,
+       .fb_fillrect    = wmt_ge_fillrect,
+       .fb_copyarea    = wmt_ge_copyarea,
+       .fb_imageblit   = sys_imageblit,
+       .fb_sync        = wmt_ge_sync,
+       .fb_ioctl       = vt8500lcd_ioctl,
+       .fb_pan_display = vt8500lcd_pan_display,
+};
+
+static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id)
+{
+       struct vt8500lcd_info *fbi = dev_id;
+
+       if (readl(fbi->regbase + 0x38) & (1 << 3))
+               wake_up_interruptible(&fbi->wait);
+
+       writel(0xffffffff, fbi->regbase + 0x38);
+       return IRQ_HANDLED;
+}
+
+static int __devinit vt8500lcd_probe(struct platform_device *pdev)
+{
+       struct vt8500lcd_info *fbi;
+       struct resource *res;
+       struct vt8500fb_platform_data *pdata = pdev->dev.platform_data;
+       void *addr;
+       int irq, ret;
+
+       ret = -ENOMEM;
+       fbi = NULL;
+
+       fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16,
+                                                       GFP_KERNEL);
+       if (!fbi) {
+               dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+               ret = -ENOMEM;
+               goto failed;
+       }
+
+       strcpy(fbi->fb.fix.id, "VT8500 LCD");
+
+       fbi->fb.fix.type        = FB_TYPE_PACKED_PIXELS;
+       fbi->fb.fix.xpanstep    = 0;
+       fbi->fb.fix.ypanstep    = 1;
+       fbi->fb.fix.ywrapstep   = 0;
+       fbi->fb.fix.accel       = FB_ACCEL_NONE;
+
+       fbi->fb.var.nonstd      = 0;
+       fbi->fb.var.activate    = FB_ACTIVATE_NOW;
+       fbi->fb.var.height      = -1;
+       fbi->fb.var.width       = -1;
+       fbi->fb.var.vmode       = FB_VMODE_NONINTERLACED;
+
+       fbi->fb.fbops           = &vt8500lcd_ops;
+       fbi->fb.flags           = FBINFO_DEFAULT
+                               | FBINFO_HWACCEL_COPYAREA
+                               | FBINFO_HWACCEL_FILLRECT
+                               | FBINFO_HWACCEL_YPAN
+                               | FBINFO_VIRTFB
+                               | FBINFO_PARTIAL_PAN_OK;
+       fbi->fb.node            = -1;
+
+       addr = fbi;
+       addr = addr + sizeof(struct vt8500lcd_info);
+       fbi->fb.pseudo_palette  = addr;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "no I/O memory resource defined\n");
+               ret = -ENODEV;
+               goto failed_fbi;
+       }
+
+       res = request_mem_region(res->start, resource_size(res), "vt8500lcd");
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to request I/O memory\n");
+               ret = -EBUSY;
+               goto failed_fbi;
+       }
+
+       fbi->regbase = ioremap(res->start, resource_size(res));
+       if (fbi->regbase == NULL) {
+               dev_err(&pdev->dev, "failed to map I/O memory\n");
+               ret = -EBUSY;
+               goto failed_free_res;
+       }
+
+       fbi->fb.fix.smem_start  = pdata->video_mem_phys;
+       fbi->fb.fix.smem_len    = pdata->video_mem_len;
+       fbi->fb.screen_base     = pdata->video_mem_virt;
+
+       fbi->palette_size       = PAGE_ALIGN(512);
+       fbi->palette_cpu        = dma_alloc_coherent(&pdev->dev,
+                                                    fbi->palette_size,
+                                                    &fbi->palette_phys,
+                                                    GFP_KERNEL);
+       if (fbi->palette_cpu == NULL) {
+               dev_err(&pdev->dev, "Failed to allocate palette buffer\n");
+               ret = -ENOMEM;
+               goto failed_free_io;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "no IRQ defined\n");
+               ret = -ENODEV;
+               goto failed_free_palette;
+       }
+
+       ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi);
+       if (ret) {
+               dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
+               ret = -EBUSY;
+               goto failed_free_palette;
+       }
+
+       init_waitqueue_head(&fbi->wait);
+
+       if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+               dev_err(&pdev->dev, "Failed to allocate color map\n");
+               ret = -ENOMEM;
+               goto failed_free_irq;
+       }
+
+       fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+       fbi->fb.var.bits_per_pixel      = pdata->bpp;
+       fbi->fb.var.xres_virtual        = pdata->xres_virtual;
+       fbi->fb.var.yres_virtual        = pdata->yres_virtual;
+
+       ret = vt8500lcd_set_par(&fbi->fb);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to set parameters\n");
+               goto failed_free_cmap;
+       }
+
+       writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c);
+       writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18);
+
+       platform_set_drvdata(pdev, fbi);
+
+       ret = register_framebuffer(&fbi->fb);
+       if (ret < 0) {
+               dev_err(&pdev->dev,
+                       "Failed to register framebuffer device: %d\n", ret);
+               goto failed_free_cmap;
+       }
+
+       /*
+        * Ok, now enable the LCD controller
+        */
+       writel(readl(fbi->regbase) | 1, fbi->regbase);
+
+       return 0;
+
+failed_free_cmap:
+       if (fbi->fb.cmap.len)
+               fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_irq:
+       free_irq(irq, fbi);
+failed_free_palette:
+       dma_free_coherent(&pdev->dev, fbi->palette_size,
+                         fbi->palette_cpu, fbi->palette_phys);
+failed_free_io:
+       iounmap(fbi->regbase);
+failed_free_res:
+       release_mem_region(res->start, resource_size(res));
+failed_fbi:
+       platform_set_drvdata(pdev, NULL);
+       kfree(fbi);
+failed:
+       return ret;
+}
+
+static int __devexit vt8500lcd_remove(struct platform_device *pdev)
+{
+       struct vt8500lcd_info *fbi = platform_get_drvdata(pdev);
+       struct resource *res;
+       int irq;
+
+       unregister_framebuffer(&fbi->fb);
+
+       writel(0, fbi->regbase);
+
+       if (fbi->fb.cmap.len)
+               fb_dealloc_cmap(&fbi->fb.cmap);
+
+       irq = platform_get_irq(pdev, 0);
+       free_irq(irq, fbi);
+
+       dma_free_coherent(&pdev->dev, fbi->palette_size,
+                         fbi->palette_cpu, fbi->palette_phys);
+
+       iounmap(fbi->regbase);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       kfree(fbi);
+
+       return 0;
+}
+
+static struct platform_driver vt8500lcd_driver = {
+       .probe          = vt8500lcd_probe,
+       .remove         = __devexit_p(vt8500lcd_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "vt8500-lcd",
+       },
+};
+
+static int __init vt8500lcd_init(void)
+{
+       return platform_driver_register(&vt8500lcd_driver);
+}
+
+static void __exit vt8500lcd_exit(void)
+{
+       platform_driver_unregister(&vt8500lcd_driver);
+}
+
+module_init(vt8500lcd_init);
+module_exit(vt8500lcd_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("LCD controller driver for VIA VT8500");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/vt8500lcdfb.h b/drivers/video/vt8500lcdfb.h
new file mode 100644 (file)
index 0000000..36ca3ca
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * 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.
+ */
+
+struct vt8500lcd_info {
+       struct fb_info          fb;
+       void __iomem            *regbase;
+       void __iomem            *palette_cpu;
+       dma_addr_t              palette_phys;
+       size_t                  palette_size;
+       wait_queue_head_t       wait;
+};
+
+static int bpp_values[] = {
+       1,
+       2,
+       4,
+       8,
+       12,
+       16,
+       18,
+       24,
+};
diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c
new file mode 100644 (file)
index 0000000..e37251b
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ *  WonderMedia WM8505 Frame Buffer device driver
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *    Based on vt8500lcdfb.c
+ *
+ * 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/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <mach/vt8500fb.h>
+
+#include "wm8505fb_regs.h"
+#include "wmt_ge_rops.h"
+
+#define DRIVER_NAME "wm8505-fb"
+
+#define to_wm8505fb_info(__info) container_of(__info, \
+                                               struct wm8505fb_info, fb)
+struct wm8505fb_info {
+       struct fb_info          fb;
+       void __iomem            *regbase;
+       unsigned int            contrast;
+};
+
+
+static int wm8505fb_init_hw(struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       int i;
+
+       /* I know the purpose only of few registers, so clear unknown */
+       for (i = 0; i < 0x200; i += 4)
+               writel(0, fbi->regbase + i);
+
+       /* Set frame buffer address */
+       writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
+       writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
+
+       /* Set in-memory picture format to RGB 32bpp */
+       writel(0x1c,                   fbi->regbase + WMT_GOVR_COLORSPACE);
+       writel(1,                      fbi->regbase + WMT_GOVR_COLORSPACE1);
+
+       /* Virtual buffer size */
+       writel(info->var.xres,         fbi->regbase + WMT_GOVR_XRES);
+       writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL);
+
+       /* black magic ;) */
+       writel(0xf,                    fbi->regbase + WMT_GOVR_FHI);
+       writel(4,                      fbi->regbase + WMT_GOVR_DVO_SET);
+       writel(1,                      fbi->regbase + WMT_GOVR_MIF_ENABLE);
+       writel(1,                      fbi->regbase + WMT_GOVR_REG_UPDATE);
+
+       return 0;
+}
+
+static int wm8505fb_set_timing(struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       int h_start = info->var.left_margin;
+       int h_end = h_start + info->var.xres;
+       int h_all = h_end + info->var.right_margin;
+       int h_sync = info->var.hsync_len;
+
+       int v_start = info->var.upper_margin;
+       int v_end = v_start + info->var.yres;
+       int v_all = v_end + info->var.lower_margin;
+       int v_sync = info->var.vsync_len + 1;
+
+       writel(0, fbi->regbase + WMT_GOVR_TG);
+
+       writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START);
+       writel(h_end,   fbi->regbase + WMT_GOVR_TIMING_H_END);
+       writel(h_all,   fbi->regbase + WMT_GOVR_TIMING_H_ALL);
+       writel(h_sync,  fbi->regbase + WMT_GOVR_TIMING_H_SYNC);
+
+       writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START);
+       writel(v_end,   fbi->regbase + WMT_GOVR_TIMING_V_END);
+       writel(v_all,   fbi->regbase + WMT_GOVR_TIMING_V_ALL);
+       writel(v_sync,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+
+       writel(1, fbi->regbase + WMT_GOVR_TG);
+
+       return 0;
+}
+
+
+static int wm8505fb_set_par(struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       if (!fbi)
+               return -EINVAL;
+
+       if (info->var.bits_per_pixel == 32) {
+               info->var.red.offset = 16;
+               info->var.red.length = 8;
+               info->var.red.msb_right = 0;
+               info->var.green.offset = 8;
+               info->var.green.length = 8;
+               info->var.green.msb_right = 0;
+               info->var.blue.offset = 0;
+               info->var.blue.length = 8;
+               info->var.blue.msb_right = 0;
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+               info->fix.line_length = info->var.xres_virtual << 2;
+       }
+
+       wm8505fb_set_timing(info);
+
+       writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast,
+               fbi->regbase + WMT_GOVR_CONTRAST);
+
+       return 0;
+}
+
+static ssize_t contrast_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       return sprintf(buf, "%d\n", fbi->contrast);
+}
+
+static ssize_t contrast_store(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+       unsigned long tmp;
+
+       if (strict_strtoul(buf, 10, &tmp) || (tmp > 0xff))
+               return -EINVAL;
+       fbi->contrast = tmp;
+
+       wm8505fb_set_par(info);
+
+       return count;
+}
+
+static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store);
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+       chan &= 0xffff;
+       chan >>= 16 - bf->length;
+       return chan << bf->offset;
+}
+
+static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                          unsigned blue, unsigned transp,
+                          struct fb_info *info) {
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+       int ret = 1;
+       unsigned int val;
+       if (regno >= 256)
+               return -EINVAL;
+
+       if (info->var.grayscale)
+               red = green = blue =
+                       (19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+       switch (fbi->fb.fix.visual) {
+       case FB_VISUAL_TRUECOLOR:
+               if (regno < 16) {
+                       u32 *pal = info->pseudo_palette;
+
+                       val  = chan_to_field(red, &fbi->fb.var.red);
+                       val |= chan_to_field(green, &fbi->fb.var.green);
+                       val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+                       pal[regno] = val;
+                       ret = 0;
+               }
+               break;
+       }
+
+       return ret;
+}
+
+static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
+                               struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN);
+       writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN);
+       return 0;
+}
+
+static int wm8505fb_blank(int blank, struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       switch (blank) {
+       case FB_BLANK_UNBLANK:
+               wm8505fb_set_timing(info);
+               break;
+       default:
+               writel(0,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+               break;
+       }
+
+       return 0;
+}
+
+static struct fb_ops wm8505fb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_set_par     = wm8505fb_set_par,
+       .fb_setcolreg   = wm8505fb_setcolreg,
+       .fb_fillrect    = wmt_ge_fillrect,
+       .fb_copyarea    = wmt_ge_copyarea,
+       .fb_imageblit   = sys_imageblit,
+       .fb_sync        = wmt_ge_sync,
+       .fb_pan_display = wm8505fb_pan_display,
+       .fb_blank       = wm8505fb_blank,
+};
+
+static int __devinit wm8505fb_probe(struct platform_device *pdev)
+{
+       struct wm8505fb_info    *fbi;
+       struct resource         *res;
+       void                    *addr;
+       struct vt8500fb_platform_data *pdata;
+       int ret;
+
+       pdata = pdev->dev.platform_data;
+
+       ret = -ENOMEM;
+       fbi = NULL;
+
+       fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16,
+                                                       GFP_KERNEL);
+       if (!fbi) {
+               dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+               ret = -ENOMEM;
+               goto failed;
+       }
+
+       strcpy(fbi->fb.fix.id, DRIVER_NAME);
+
+       fbi->fb.fix.type        = FB_TYPE_PACKED_PIXELS;
+       fbi->fb.fix.xpanstep    = 1;
+       fbi->fb.fix.ypanstep    = 1;
+       fbi->fb.fix.ywrapstep   = 0;
+       fbi->fb.fix.accel       = FB_ACCEL_NONE;
+
+       fbi->fb.fbops           = &wm8505fb_ops;
+       fbi->fb.flags           = FBINFO_DEFAULT
+                               | FBINFO_HWACCEL_COPYAREA
+                               | FBINFO_HWACCEL_FILLRECT
+                               | FBINFO_HWACCEL_XPAN
+                               | FBINFO_HWACCEL_YPAN
+                               | FBINFO_VIRTFB
+                               | FBINFO_PARTIAL_PAN_OK;
+       fbi->fb.node            = -1;
+
+       addr = fbi;
+       addr = addr + sizeof(struct wm8505fb_info);
+       fbi->fb.pseudo_palette  = addr;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "no I/O memory resource defined\n");
+               ret = -ENODEV;
+               goto failed_fbi;
+       }
+
+       res = request_mem_region(res->start, resource_size(res), "wm8505fb");
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to request I/O memory\n");
+               ret = -EBUSY;
+               goto failed_fbi;
+       }
+
+       fbi->regbase = ioremap(res->start, resource_size(res));
+       if (fbi->regbase == NULL) {
+               dev_err(&pdev->dev, "failed to map I/O memory\n");
+               ret = -EBUSY;
+               goto failed_free_res;
+       }
+
+       fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+
+       fbi->fb.var.nonstd              = 0;
+       fbi->fb.var.activate            = FB_ACTIVATE_NOW;
+
+       fbi->fb.var.height              = -1;
+       fbi->fb.var.width               = -1;
+       fbi->fb.var.xres_virtual        = pdata->xres_virtual;
+       fbi->fb.var.yres_virtual        = pdata->yres_virtual;
+       fbi->fb.var.bits_per_pixel      = pdata->bpp;
+
+       fbi->fb.fix.smem_start  = pdata->video_mem_phys;
+       fbi->fb.fix.smem_len    = pdata->video_mem_len;
+       fbi->fb.screen_base     = pdata->video_mem_virt;
+       fbi->fb.screen_size     = pdata->video_mem_len;
+
+       if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+               dev_err(&pdev->dev, "Failed to allocate color map\n");
+               ret = -ENOMEM;
+               goto failed_free_io;
+       }
+
+       wm8505fb_init_hw(&fbi->fb);
+
+       fbi->contrast = 0x80;
+       ret = wm8505fb_set_par(&fbi->fb);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to set parameters\n");
+               goto failed_free_cmap;
+       }
+
+       platform_set_drvdata(pdev, fbi);
+
+       ret = register_framebuffer(&fbi->fb);
+       if (ret < 0) {
+               dev_err(&pdev->dev,
+                       "Failed to register framebuffer device: %d\n", ret);
+               goto failed_free_cmap;
+       }
+
+       ret = device_create_file(&pdev->dev, &dev_attr_contrast);
+       if (ret < 0) {
+               printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
+                       fbi->fb.node, ret);
+       }
+
+       printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n",
+              fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start,
+              fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1);
+
+       return 0;
+
+failed_free_cmap:
+       if (fbi->fb.cmap.len)
+               fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_io:
+       iounmap(fbi->regbase);
+failed_free_res:
+       release_mem_region(res->start, resource_size(res));
+failed_fbi:
+       platform_set_drvdata(pdev, NULL);
+       kfree(fbi);
+failed:
+       return ret;
+}
+
+static int __devexit wm8505fb_remove(struct platform_device *pdev)
+{
+       struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
+       struct resource *res;
+
+       device_remove_file(&pdev->dev, &dev_attr_contrast);
+
+       unregister_framebuffer(&fbi->fb);
+
+       writel(0, fbi->regbase);
+
+       if (fbi->fb.cmap.len)
+               fb_dealloc_cmap(&fbi->fb.cmap);
+
+       iounmap(fbi->regbase);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       kfree(fbi);
+
+       return 0;
+}
+
+static struct platform_driver wm8505fb_driver = {
+       .probe          = wm8505fb_probe,
+       .remove         = __devexit_p(wm8505fb_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = DRIVER_NAME,
+       },
+};
+
+static int __init wm8505fb_init(void)
+{
+       return platform_driver_register(&wm8505fb_driver);
+}
+
+static void __exit wm8505fb_exit(void)
+{
+       platform_driver_unregister(&wm8505fb_driver);
+}
+
+module_init(wm8505fb_init);
+module_exit(wm8505fb_exit);
+
+MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
+MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wm8505fb_regs.h b/drivers/video/wm8505fb_regs.h
new file mode 100644 (file)
index 0000000..4dd4166
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  GOVR registers list for WM8505 chips
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *   Based on VIA/WonderMedia wm8510-govrh-reg.h
+ *   http://github.com/projectgus/kernel_wm8505/blob/wm8505_2.6.29/
+ *         drivers/video/wmt/register/wm8510/wm8510-govrh-reg.h
+ *
+ * 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.
+ */
+
+#ifndef _WM8505FB_REGS_H
+#define _WM8505FB_REGS_H
+
+/*
+ * Color space select register, default value 0x1c
+ *   BIT0 GOVRH_DVO_YUV2RGB_ENABLE
+ *   BIT1 GOVRH_VGA_YUV2RGB_ENABLE
+ *   BIT2 GOVRH_RGB_MODE
+ *   BIT3 GOVRH_DAC_CLKINV
+ *   BIT4 GOVRH_BLANK_ZERO
+ */
+#define WMT_GOVR_COLORSPACE    0x1e4
+/*
+ * Another colorspace select register, default value 1
+ *   BIT0 GOVRH_DVO_RGB
+ *   BIT1 GOVRH_DVO_YUV422
+ */
+#define WMT_GOVR_COLORSPACE1    0x30
+
+#define WMT_GOVR_CONTRAST      0x1b8
+#define WMT_GOVR_BRGHTNESS     0x1bc /* incompatible with RGB? */
+
+/* Framubeffer address */
+#define WMT_GOVR_FBADDR                 0x90
+#define WMT_GOVR_FBADDR1        0x94 /* UV offset in YUV mode */
+
+/* Offset of visible window */
+#define WMT_GOVR_XPAN           0xa4
+#define WMT_GOVR_YPAN           0xa0
+
+#define WMT_GOVR_XRES           0x98
+#define WMT_GOVR_XRES_VIRTUAL   0x9c
+
+#define WMT_GOVR_MIF_ENABLE     0x80
+#define WMT_GOVR_FHI            0xa8
+#define WMT_GOVR_REG_UPDATE     0xe4
+
+/*
+ *   BIT0 GOVRH_DVO_OUTWIDTH
+ *   BIT1 GOVRH_DVO_SYNC_POLAR
+ *   BIT2 GOVRH_DVO_ENABLE
+ */
+#define WMT_GOVR_DVO_SET       0x148
+
+/* Timing generator? */
+#define WMT_GOVR_TG            0x100
+
+/* Timings */
+#define WMT_GOVR_TIMING_H_ALL  0x108
+#define WMT_GOVR_TIMING_V_ALL  0x10c
+#define WMT_GOVR_TIMING_V_START        0x110
+#define WMT_GOVR_TIMING_V_END  0x114
+#define WMT_GOVR_TIMING_H_START        0x118
+#define WMT_GOVR_TIMING_H_END  0x11c
+#define WMT_GOVR_TIMING_V_SYNC 0x128
+#define WMT_GOVR_TIMING_H_SYNC 0x12c
+
+#endif /* _WM8505FB_REGS_H */
diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
new file mode 100644 (file)
index 0000000..f31883f
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ *  linux/drivers/video/wmt_ge_rops.c
+ *
+ *  Accelerators for raster operations using WonderMedia Graphics Engine
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * 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/fb.h>
+#include <linux/platform_device.h>
+#include "fb_draw.h"
+
+#define GE_COMMAND_OFF         0x00
+#define GE_DEPTH_OFF           0x04
+#define GE_HIGHCOLOR_OFF       0x08
+#define GE_ROPCODE_OFF         0x14
+#define GE_FIRE_OFF            0x18
+#define GE_SRCBASE_OFF         0x20
+#define GE_SRCDISPW_OFF                0x24
+#define GE_SRCDISPH_OFF                0x28
+#define GE_SRCAREAX_OFF                0x2c
+#define GE_SRCAREAY_OFF                0x30
+#define GE_SRCAREAW_OFF                0x34
+#define GE_SRCAREAH_OFF                0x38
+#define GE_DESTBASE_OFF                0x3c
+#define GE_DESTDISPW_OFF       0x40
+#define GE_DESTDISPH_OFF       0x44
+#define GE_DESTAREAX_OFF       0x48
+#define GE_DESTAREAY_OFF       0x4c
+#define GE_DESTAREAW_OFF       0x50
+#define GE_DESTAREAH_OFF       0x54
+#define GE_PAT0C_OFF           0x88    /* Pattern 0 color */
+#define GE_ENABLE_OFF          0xec
+#define GE_INTEN_OFF           0xf0
+#define GE_STATUS_OFF          0xf8
+
+static void __iomem *regbase;
+
+void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+       unsigned long fg, pat;
+
+       if (p->state != FBINFO_STATE_RUNNING)
+               return;
+
+       if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+           p->fix.visual == FB_VISUAL_DIRECTCOLOR)
+               fg = ((u32 *) (p->pseudo_palette))[rect->color];
+       else
+               fg = rect->color;
+
+       pat = pixel_to_pat(p->var.bits_per_pixel, fg);
+
+       if (p->fbops->fb_sync)
+               p->fbops->fb_sync(p);
+
+       writel(p->var.bits_per_pixel == 32 ? 3 :
+             (p->var.bits_per_pixel == 8 ? 0 : 1), regbase + GE_DEPTH_OFF);
+       writel(p->var.bits_per_pixel == 15 ? 1 : 0, regbase + GE_HIGHCOLOR_OFF);
+       writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+       writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+       writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+       writel(rect->dx, regbase + GE_DESTAREAX_OFF);
+       writel(rect->dy, regbase + GE_DESTAREAY_OFF);
+       writel(rect->width - 1, regbase + GE_DESTAREAW_OFF);
+       writel(rect->height - 1, regbase + GE_DESTAREAH_OFF);
+
+       writel(pat, regbase + GE_PAT0C_OFF);
+       writel(1, regbase + GE_COMMAND_OFF);
+       writel(rect->rop == ROP_XOR ? 0x5a : 0xf0, regbase + GE_ROPCODE_OFF);
+       writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL_GPL(wmt_ge_fillrect);
+
+void wmt_ge_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+       if (p->state != FBINFO_STATE_RUNNING)
+               return;
+
+       if (p->fbops->fb_sync)
+               p->fbops->fb_sync(p);
+
+       writel(p->var.bits_per_pixel > 16 ? 3 :
+             (p->var.bits_per_pixel > 8 ? 1 : 0), regbase + GE_DEPTH_OFF);
+
+       writel(p->fix.smem_start, regbase + GE_SRCBASE_OFF);
+       writel(p->var.xres_virtual - 1, regbase + GE_SRCDISPW_OFF);
+       writel(p->var.yres_virtual - 1, regbase + GE_SRCDISPH_OFF);
+       writel(area->sx, regbase + GE_SRCAREAX_OFF);
+       writel(area->sy, regbase + GE_SRCAREAY_OFF);
+       writel(area->width - 1, regbase + GE_SRCAREAW_OFF);
+       writel(area->height - 1, regbase + GE_SRCAREAH_OFF);
+
+       writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+       writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+       writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+       writel(area->dx, regbase + GE_DESTAREAX_OFF);
+       writel(area->dy, regbase + GE_DESTAREAY_OFF);
+       writel(area->width - 1, regbase + GE_DESTAREAW_OFF);
+       writel(area->height - 1, regbase + GE_DESTAREAH_OFF);
+
+       writel(0xcc, regbase + GE_ROPCODE_OFF);
+       writel(1, regbase + GE_COMMAND_OFF);
+       writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL_GPL(wmt_ge_copyarea);
+
+int wmt_ge_sync(struct fb_info *p)
+{
+       int loops = 5000000;
+       while ((readl(regbase + GE_STATUS_OFF) & 4) && --loops)
+               cpu_relax();
+       return loops > 0 ? 0 : -EBUSY;
+}
+EXPORT_SYMBOL_GPL(wmt_ge_sync);
+
+static int __devinit wmt_ge_rops_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "no I/O memory resource defined\n");
+               ret = -ENODEV;
+               goto error;
+       }
+
+       /* Only one ROP engine is presently supported. */
+       if (unlikely(regbase)) {
+               WARN_ON(1);
+               return -EBUSY;
+       }
+
+       regbase = ioremap(res->start, resource_size(res));
+       if (regbase == NULL) {
+               dev_err(&pdev->dev, "failed to map I/O memory\n");
+               ret = -EBUSY;
+               goto error;
+       }
+
+       writel(1, regbase + GE_ENABLE_OFF);
+       printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n");
+
+       return 0;
+
+error:
+       return ret;
+}
+
+static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
+{
+       iounmap(regbase);
+       return 0;
+}
+
+static struct platform_driver wmt_ge_rops_driver = {
+       .probe          = wmt_ge_rops_probe,
+       .remove         = __devexit_p(wmt_ge_rops_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "wmt_ge_rops",
+       },
+};
+
+static int __init wmt_ge_rops_init(void)
+{
+       return platform_driver_register(&wmt_ge_rops_driver);
+}
+
+static void __exit wmt_ge_rops_exit(void)
+{
+       platform_driver_unregister(&wmt_ge_rops_driver);
+}
+
+module_init(wmt_ge_rops_init);
+module_exit(wmt_ge_rops_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com");
+MODULE_DESCRIPTION("Accelerators for raster operations using "
+                  "WonderMedia Graphics Engine");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h
new file mode 100644 (file)
index 0000000..8738075
--- /dev/null
@@ -0,0 +1,5 @@
+extern void wmt_ge_fillrect(struct fb_info *info,
+                           const struct fb_fillrect *rect);
+extern void wmt_ge_copyarea(struct fb_info *info,
+                           const struct fb_copyarea *area);
+extern int wmt_ge_sync(struct fb_info *info);