[PATCH 4/4] video: add driver for PXA3xx 2D graphics accelerator
Daniel Mack
daniel at caiaq.de
Thu Feb 25 11:05:58 EST 2010
(now used the new AKML address)
Hi,
Was anyone able to give that driver a try?
Haojian, you wanted to have a look IIRC?
I would appreciate seeing this in .35 if possible.
Daniel
On Thu, Jul 09, 2009 at 07:04:51PM +0200, Daniel Mack wrote:
> This adds a driver for the the 2D graphics accelerator found on PXA3xx
> processors. Only resource mapping, interrupt handling and a simple ioctl
> handler is done by the kernel part, the rest of the logic is implemented
> in DirectFB userspace.
>
> Graphic applications benefit for line drawing, blend, and rectangle and
> triangle filling operations.
>
> Measurements on a PXA303 using the df_dok benchmarking tool follow,
> where the value in square brackets show the CPU usage during that test.
>
> Without accelerator:
>
> Fill Rectangle 3.007 secs ( 175.922 MPixel/sec) [100.3%]
> Fill Rectangles [10] 3.178 secs ( 182.696 MPixel/sec) [100.0%]
> Fill Triangles 3.023 secs ( 99.232 MPixel/sec) [100.3%]
> Blit 3.082 secs ( 39.770 MPixel/sec) [100.0%]
> Blit 180 3.115 secs ( 28.994 MPixel/sec) [100.3%]
> Blit with format conversion 3.078 secs ( 18.863 MPixel/sec) [100.0%]
>
> With accelerator:
>
> Fill Rectangle 4.377 secs (* 88.433 MPixel/sec) [ 5.9%]
> Fill Rectangles [10] 5.176 secs (* 87.245 MPixel/sec) [ 0.5%]
> Fill Triangles 3.025 secs ( 87.437 MPixel/sec) [ 46.6%]
> Blit 12.177 secs (* 22.250 MPixel/sec) [ 1.3%]
> Blit 180 8.421 secs (* 32.175 MPixel/sec) [ 4.9%]
> Blit with format conversion 8.216 secs (* 40.045 MPixel/sec) [ 2.5%]
>
> Signed-off-by: Daniel Mack <daniel at caiaq.de>
> Cc: Denis Oliver Kropp <dok at directfb.org>
> ---
> drivers/video/Kconfig | 10 +
> drivers/video/Makefile | 1 +
> drivers/video/pxa3xx-gcu.c | 591 ++++++++++++++++++++++++++++++++++++++++++++
> drivers/video/pxa3xx-gcu.h | 49 ++++
> 4 files changed, 651 insertions(+), 0 deletions(-)
> create mode 100644 drivers/video/pxa3xx-gcu.c
> create mode 100644 drivers/video/pxa3xx-gcu.h
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 8afcf08..6ad0a1c 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -1817,6 +1817,16 @@ config FB_PXA_PARAMETERS
>
> <file:Documentation/fb/pxafb.txt> describes the available parameters.
>
> +config PXA3XX_GCU
> + tristate "PXA3xx 2D graphics accelerator driver"
> + depends on FB_PXA
> + help
> + Kernelspace driver for the 2D graphics controller unit (GCU)
> + found on PXA3xx processors. There is a counterpart driver in the
> + DirectFB suite, see http://www.directfb.org/
> +
> + If you compile this as a module, it will be called pxa3xx_gcu.
> +
> config FB_MBX
> tristate "2700G LCD framebuffer support"
> depends on FB && ARCH_PXA
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 01a819f..ffe382c 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -98,6 +98,7 @@ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
> obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o
> obj-$(CONFIG_FB_PXA) += pxafb.o
> obj-$(CONFIG_FB_PXA168) += pxa168fb.o
> +obj-$(CONFIG_PXA3XX_GCU) += pxa3xx-gcu.o
> obj-$(CONFIG_FB_W100) += w100fb.o
> obj-$(CONFIG_FB_TMIO) += tmiofb.o
> obj-$(CONFIG_FB_AU1100) += au1100fb.o
> diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c
> new file mode 100644
> index 0000000..224faf7
> --- /dev/null
> +++ b/drivers/video/pxa3xx-gcu.c
> @@ -0,0 +1,591 @@
> +/*
> + * pxa3xx-gc.c - Linux kernel module for PXA3xx graphics controllers
> + *
> + * This driver needs a DirectFB counterpart in user space, communication
> + * is handled via mmap()ed memory areas and an ioctl.
> + *
> + * Copyright (c) 2009 Daniel Mack <daniel at caiaq.de>
> + * Copyright (c) 2009 Janine Kropp <nin at directfb.org>
> + * Copyright (c) 2009 Denis Oliver Kropp <dok at directfb.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +/*
> + * WARNING: This controller is attached to System Bus 2 of the PXA which
> + * needs its arbiter to be enabled explictly (CKENB & 1<<9).
> + * There is currently no way to do this from Linux, so you need to teach
> + * your bootloader for now.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/version.h>
> +
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/miscdevice.h>
> +#include <linux/interrupt.h>
> +#include <linux/spinlock.h>
> +#include <linux/uaccess.h>
> +#include <linux/ioctl.h>
> +#include <linux/delay.h>
> +#include <linux/sched.h>
> +#include <linux/clk.h>
> +#include <linux/fs.h>
> +#include <linux/io.h>
> +
> +#include "pxa3xx-gcu.h"
> +
> +#define DRV_NAME "pxa3xx-gcu"
> +#define MISCDEV_MINOR 197
> +
> +#define REG_GCCR 0x00
> +#define GCCR_SYNC_CLR (1 << 9)
> +#define GCCR_BP_RST (1 << 8)
> +#define GCCR_ABORT (1 << 6)
> +#define GCCR_STOP (1 << 4)
> +
> +#define REG_GCISCR 0x04
> +#define REG_GCIECR 0x08
> +#define REG_GCRBBR 0x20
> +#define REG_GCRBLR 0x24
> +#define REG_GCRBHR 0x28
> +#define REG_GCRBTR 0x2C
> +#define REG_GCRBEXHR 0x30
> +
> +#define IE_EOB (1 << 0)
> +#define IE_EEOB (1 << 5)
> +#define IE_ALL 0xff
> +
> +#define SHARED_SIZE PAGE_ALIGN(sizeof(struct pxa3xx_gcu_shared))
> +
> +/* #define PXA3XX_GCU_DEBUG */
> +/* #define PXA3XX_GCU_DEBUG_TIMER */
> +
> +#ifdef PXA3XX_GCU_DEBUG
> +#define QDUMP(msg) \
> + do { \
> + QPRINT(priv, KERN_DEBUG, msg); \
> + } while (0)
> +#else
> +#define QDUMP(msg) do {} while (0)
> +#endif
> +
> +#define QERROR(msg) \
> + do { \
> + QPRINT(priv, KERN_ERR, msg); \
> + } while (0)
> +
> +struct pxa3xx_gcu_priv {
> + void __iomem *mmio_base;
> + struct clk *clk;
> + struct pxa3xx_gcu_shared *shared;
> + dma_addr_t shared_phys;
> + struct resource *resource_mem;
> + struct miscdevice misc_dev;
> + struct file_operations misc_fops;
> + wait_queue_head_t wait_idle;
> + wait_queue_head_t wait_next;
> + spinlock_t spinlock;
> + struct timeval base_time;
> +};
> +
> +static inline unsigned long
> +gc_readl(struct pxa3xx_gcu_priv *priv, unsigned int off)
> +{
> + return __raw_readl(priv->mmio_base + off);
> +}
> +
> +static inline void
> +gc_writel(struct pxa3xx_gcu_priv *priv, unsigned int off, unsigned long val)
> +{
> + __raw_writel(val, priv->mmio_base + off);
> +}
> +
> +#define QPRINT(priv, level, msg) \
> + do { \
> + struct timeval tv; \
> + struct pxa3xx_gcu_shared *shared = priv->shared; \
> + u32 base = gc_readl(priv, REG_GCRBBR); \
> + \
> + do_gettimeofday(&tv); \
> + \
> + printk(level "%ld.%03ld.%03ld - %-17s: %-21s (%s, " \
> + "hw %5d-%5d, next %5d-%5d, %svalid, STATUS " \
> + "0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, " \
> + "T %5ld)\n", \
> + tv.tv_sec - priv->base_time.tv_sec, \
> + tv.tv_usec / 1000, tv.tv_usec % 1000, \
> + __func__, msg, \
> + shared->hw_running ? "running" : " idle", \
> + shared->hw_start, \
> + shared->hw_end, \
> + shared->next_start, \
> + shared->next_end, \
> + shared->next_valid ? " " : "in", \
> + gc_readl(priv, REG_GCISCR), \
> + gc_readl(priv, REG_GCRBBR), \
> + gc_readl(priv, REG_GCRBLR), \
> + (gc_readl(priv, REG_GCRBEXHR) - base) / 4, \
> + (gc_readl(priv, REG_GCRBHR) - base) / 4, \
> + (gc_readl(priv, REG_GCRBTR) - base) / 4); \
> + } while (0)
> +
> +static void pxa3xx_gcu_reset(struct pxa3xx_gcu_priv *priv)
> +{
> + QDUMP("RESET");
> +
> + /* disable interrupts */
> + gc_writel(priv, REG_GCIECR, 0);
> +
> + /* reset hardware */
> + gc_writel(priv, REG_GCCR, GCCR_ABORT);
> + gc_writel(priv, REG_GCCR, 0);
> +
> + memset(priv->shared, 0, SHARED_SIZE);
> + priv->shared->buffer_phys = priv->shared_phys;
> + priv->shared->magic = PXA3XX_GCU_SHARED_MAGIC;
> +
> + do_gettimeofday(&priv->base_time);
> +
> + /* set up the ring buffer pointers */
> + gc_writel(priv, REG_GCRBLR, 0);
> + gc_writel(priv, REG_GCRBBR, priv->shared_phys);
> + gc_writel(priv, REG_GCRBTR, priv->shared_phys);
> +
> + /* enable all IRQs except EOB */
> + gc_writel(priv, REG_GCIECR, IE_ALL & ~IE_EOB);
> +}
> +
> +static void pxa3xx_gcu_start(struct pxa3xx_gcu_priv *priv)
> +{
> + struct pxa3xx_gcu_shared *sh = priv->shared;
> +
> + QDUMP("Start");
> +
> + gc_writel(priv, REG_GCRBLR, 0);
> +
> + /* ring base address */
> + gc_writel(priv, REG_GCRBBR, sh->buffer_phys + sh->hw_start * 4);
> +
> + /* ring tail address */
> + gc_writel(priv, REG_GCRBTR, sh->buffer_phys + sh->hw_end * 4);
> +
> + /* ring length */
> + gc_writel(priv, REG_GCRBLR,
> + ((sh->hw_end - sh->hw_start + 63) & ~63) * 4);
> +}
> +
> +static irqreturn_t pxa3xx_gcu_handle_irq(int irq, void *ctx)
> +{
> + struct pxa3xx_gcu_priv *priv = ctx;
> + struct pxa3xx_gcu_shared *shared = priv->shared;
> + u32 status = gc_readl(priv, REG_GCISCR) & IE_ALL;
> +
> + if (!status)
> + return IRQ_NONE;
> +
> + QDUMP("-Interrupt");
> +
> + spin_lock(&priv->spinlock);
> +
> + shared->num_interrupts++;
> +
> + if (status & IE_EEOB) {
> + QDUMP(" [EEOB]");
> +
> + /* next_valid means user space is not in the process of
> + * extending the buffer, so we can safely access the
> + * contents. */
> + if (shared->next_valid &&
> + shared->next_start != shared->next_end) {
> + shared->hw_start = shared->next_start;
> + shared->hw_end = shared->next_end;
> + shared->next_start = (shared->hw_end + 63) & ~63;
> + shared->next_end = shared->next_start;
> + shared->num_words += shared->hw_end - shared->hw_start;
> + shared->num_starts++;
> +
> + pxa3xx_gcu_start(priv);
> +
> + wake_up_all(&priv->wait_next);
> + } else {
> + /* There is no more data prepared by the userspace.
> + * Set hw_running = 0 and wait for the next userspace
> + * kick-off */
> + shared->num_idle++;
> + shared->hw_running = 0;
> +
> + QDUMP(" '-> Idle.");
> +
> + /* set ring buffer length to zero */
> + gc_writel(priv, REG_GCRBLR, 0);
> +
> + wake_up_all(&priv->wait_next);
> + wake_up_all(&priv->wait_idle);
> + }
> +
> + shared->num_done++;
> + } else
> + QERROR(" [???]");
> +
> + /* Clear the interrupt */
> + gc_writel(priv, REG_GCISCR, status);
> + spin_unlock(&priv->spinlock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int
> +pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv)
> +{
> + int ret = 0;
> +
> + QDUMP("Waiting for idle...");
> +
> + while (priv->shared->hw_running) {
> + int num = priv->shared->num_interrupts;
> + u32 rbexhr = gc_readl(priv, REG_GCRBEXHR);
> +
> + ret = wait_event_interruptible_timeout(priv->wait_idle,
> + !priv->shared->hw_running, HZ/4);
> +
> + if (ret < 0)
> + break;
> +
> + if (ret > 0)
> + continue;
> +
> + if (gc_readl(priv, REG_GCRBEXHR) == rbexhr &&
> + priv->shared->num_interrupts == num) {
> + QERROR("TIMEOUT");
> + ret = -ETIMEDOUT;
> + break;
> + }
> + }
> +
> + QDUMP("done");
> +
> + return ret;
> +}
> +
> +static int
> +pxa3xx_gcu_wait_next(struct pxa3xx_gcu_priv *priv)
> +{
> + int ret = 0;
> + int num = priv->shared->num_interrupts;
> +
> + QDUMP("Waiting for next...");
> +
> + while (priv->shared->hw_running &&
> + num == priv->shared->num_interrupts) {
> + u32 rbexhr = gc_readl(priv, REG_GCRBEXHR);
> +
> + ret = wait_event_interruptible_timeout(priv->wait_next,
> + (!priv->shared->hw_running ||
> + num != priv->shared->num_interrupts), HZ/4);
> +
> + if (ret < 0)
> + break;
> +
> + if (ret > 0)
> + continue;
> +
> + if (gc_readl(priv, REG_GCRBEXHR) == rbexhr) {
> + QERROR("TIMEOUT");
> + ret = -ETIMEDOUT;
> + break;
> + }
> + }
> +
> + QDUMP("done");
> +
> + return ret;
> +}
> +
> +/* Misc device layer */
> +
> +static int
> +pxa3xx_gcu_misc_ioctl(struct inode *inode, struct file *filp,
> + unsigned int cmd, unsigned long arg)
> +{
> + unsigned long flags;
> + struct pxa3xx_gcu_priv *priv =
> + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops);
> +
> + switch (cmd) {
> + case PXA3XX_GCU_IOCTL_RESET:
> + spin_lock_irqsave(&priv->spinlock, flags);
> + pxa3xx_gcu_reset(priv);
> + spin_unlock_irqrestore(&priv->spinlock, flags);
> + return 0;
> +
> + case PXA3XX_GCU_IOCTL_START:
> + spin_lock_irqsave(&priv->spinlock, flags);
> + pxa3xx_gcu_start(priv);
> + spin_unlock_irqrestore(&priv->spinlock, flags);
> + return 0;
> +
> + case PXA3XX_GCU_IOCTL_WAIT_IDLE:
> + /* Does not need to be atomic. There's a lock in user space,
> + * but anyhow, this is just for statistics. */
> + priv->shared->num_wait_idle++;
> + return pxa3xx_gcu_wait_idle(priv);
> +
> + case PXA3XX_GCU_IOCTL_WAIT_NEXT:
> + /* Does not need to be atomic. There's a lock in user space,
> + * but anyhow, this is just for statistics. */
> + priv->shared->num_wait_next++;
> + return pxa3xx_gcu_wait_next(priv);
> + }
> +
> + return -ENOSYS;
> +}
> +
> +static int
> +pxa3xx_gcu_misc_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> + unsigned int size = vma->vm_end - vma->vm_start;
> + struct pxa3xx_gcu_priv *priv =
> + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops);
> +
> + switch (vma->vm_pgoff) {
> + case 0:
> + /* hand out the shared data area */
> + if (size != SHARED_SIZE)
> + return -EINVAL;
> +
> + return dma_mmap_coherent(NULL, vma,
> + priv->shared, priv->shared_phys, size);
> +
> + case SHARED_SIZE >> PAGE_SHIFT:
> + /* hand out the MMIO base for direct register access
> + * from userspace */
> + if (size != resource_size(priv->resource_mem))
> + return -EINVAL;
> +
> + vma->vm_flags |= VM_IO;
> + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> +
> + return io_remap_pfn_range(vma, vma->vm_start,
> + priv->resource_mem->start >> PAGE_SHIFT,
> + size, vma->vm_page_prot);
> + }
> +
> + return -EINVAL;
> +}
> +
> +
> +#ifdef PXA3XX_GCU_DEBUG_TIMER
> +static struct timer_list pxa3xx_gcu_debug_timer;
> +
> +static void pxa3xx_gcu_debug_timedout(unsigned long ptr)
> +{
> + struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr;
> +
> + QERROR("Timer DUMP");
> +
> + /* init the timer structure */
> + init_timer(&pxa3xx_gcu_debug_timer);
> + pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout;
> + pxa3xx_gcu_debug_timer.data = ptr;
> + pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */
> +
> + add_timer(&pxa3xx_gcu_debug_timer);
> +}
> +
> +static void pxa3xx_gcu_init_debug_timer(void)
> +{
> + pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer);
> +}
> +#else
> +static inline void pxa3xx_gcu_init_debug_timer(void) {}
> +#endif
> +
> +static int __devinit pxa3xx_gcu_probe(struct platform_device *dev)
> +{
> + int ret, irq;
> + struct resource *r;
> + struct pxa3xx_gcu_priv *priv;
> +
> + priv = kzalloc(sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + init_waitqueue_head(&priv->wait_idle);
> + init_waitqueue_head(&priv->wait_next);
> + spin_lock_init(&priv->spinlock);
> +
> + /* we allocate the misc device structure as part of our own allocation,
> + * so we can get a pointer to our priv structure later on with
> + * container_of(). This isn't really necessary as we have a fixed minor
> + * number anyway, but this is to avoid statics. */
> +
> + priv->misc_fops.owner = THIS_MODULE;
> + priv->misc_fops.ioctl = pxa3xx_gcu_misc_ioctl;
> + priv->misc_fops.mmap = pxa3xx_gcu_misc_mmap;
> +
> + priv->misc_dev.minor = MISCDEV_MINOR,
> + priv->misc_dev.name = DRV_NAME,
> + priv->misc_dev.fops = &priv->misc_fops,
> +
> + /* register misc device */
> + ret = misc_register(&priv->misc_dev);
> + if (ret < 0) {
> + dev_err(&dev->dev, "misc_register() for minor %d failed\n",
> + MISCDEV_MINOR);
> + goto err_free_priv;
> + }
> +
> + /* handle IO resources */
> + r = platform_get_resource(dev, IORESOURCE_MEM, 0);
> + if (r == NULL) {
> + dev_err(&dev->dev, "no I/O memory resource defined\n");
> + ret = -ENODEV;
> + goto err_misc_deregister;
> + }
> +
> + if (!request_mem_region(r->start, resource_size(r), dev->name)) {
> + dev_err(&dev->dev, "failed to request I/O memory\n");
> + ret = -EBUSY;
> + goto err_misc_deregister;
> + }
> +
> + priv->mmio_base = ioremap_nocache(r->start, resource_size(r));
> + if (!priv->mmio_base) {
> + dev_err(&dev->dev, "failed to map I/O memory\n");
> + ret = -EBUSY;
> + goto err_free_mem_region;
> + }
> +
> + /* allocate dma memory */
> + priv->shared = dma_alloc_coherent(&dev->dev, SHARED_SIZE,
> + &priv->shared_phys, GFP_KERNEL);
> +
> + if (!priv->shared) {
> + dev_err(&dev->dev, "failed to allocate DMA memory\n");
> + ret = -ENOMEM;
> + goto err_free_io;
> + }
> +
> + /* enable the clock */
> + priv->clk = clk_get(&dev->dev, NULL);
> + if (IS_ERR(priv->clk)) {
> + dev_err(&dev->dev, "failed to get clock\n");
> + ret = -ENODEV;
> + goto err_free_dma;
> + }
> +
> + ret = clk_enable(priv->clk);
> + if (ret < 0) {
> + dev_err(&dev->dev, "failed to enable clock\n");
> + goto err_put_clk;
> + }
> +
> + /* request the IRQ */
> + irq = platform_get_irq(dev, 0);
> + if (irq < 0) {
> + dev_err(&dev->dev, "no IRQ defined\n");
> + ret = -ENODEV;
> + goto err_put_clk;
> + }
> +
> + ret = request_irq(irq, pxa3xx_gcu_handle_irq,
> + IRQF_DISABLED, DRV_NAME, priv);
> + if (ret) {
> + dev_err(&dev->dev, "request_irq failed\n");
> + ret = -EBUSY;
> + goto err_put_clk;
> + }
> +
> + platform_set_drvdata(dev, priv);
> + priv->resource_mem = r;
> + pxa3xx_gcu_reset(priv);
> + pxa3xx_gcu_init_debug_timer();
> +
> + dev_info(&dev->dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n",
> + (void *) r->start, (void *) priv->shared_phys,
> + SHARED_SIZE, irq);
> + return 0;
> +
> +err_put_clk:
> + clk_put(priv->clk);
> +
> +err_free_dma:
> + dma_free_coherent(&dev->dev, SHARED_SIZE,
> + priv->shared, priv->shared_phys);
> +
> +err_free_io:
> + iounmap(priv->mmio_base);
> + clk_disable(priv->clk);
> +
> +err_free_mem_region:
> + release_mem_region(r->start, resource_size(r));
> +
> +err_misc_deregister:
> + misc_deregister(&priv->misc_dev);
> +
> +err_free_priv:
> + platform_set_drvdata(dev, NULL);
> + kfree(priv);
> + return ret;
> +}
> +
> +static int __devexit pxa3xx_gcu_remove(struct platform_device *dev)
> +{
> + struct pxa3xx_gcu_priv *priv = platform_get_drvdata(dev);
> + struct resource *r = priv->resource_mem;
> +
> + misc_deregister(&priv->misc_dev);
> + dma_free_coherent(&dev->dev, SHARED_SIZE,
> + priv->shared, priv->shared_phys);
> + iounmap(priv->mmio_base);
> + release_mem_region(r->start, resource_size(r));
> + platform_set_drvdata(dev, NULL);
> + clk_disable(priv->clk);
> + kfree(priv);
> +
> + return 0;
> +}
> +
> +static struct platform_driver pxa3xx_gcu_driver = {
> + .probe = pxa3xx_gcu_probe,
> + .remove = __devexit_p(pxa3xx_gcu_remove),
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = DRV_NAME,
> + },
> +};
> +
> +static int __init pxa3xx_gcu_init(void)
> +{
> + return platform_driver_register(&pxa3xx_gcu_driver);
> +}
> +
> +static void __exit pxa3xx_gcu_exit(void)
> +{
> + platform_driver_unregister(&pxa3xx_gcu_driver);
> +}
> +
> +module_init(pxa3xx_gcu_init);
> +module_exit(pxa3xx_gcu_exit);
> +
> +MODULE_DESCRIPTION("PXA3xx graphics controller unit driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS_MISCDEV(MISCDEV_MINOR);
> +MODULE_AUTHOR("Janine Kropp <nin at directfb.org>, "
> + "Denis Oliver Kropp <dok at directfb.org>, "
> + "Daniel Mack <daniel at caiaq.de>");
> +
> diff --git a/drivers/video/pxa3xx-gcu.h b/drivers/video/pxa3xx-gcu.h
> new file mode 100644
> index 0000000..18d8e43
> --- /dev/null
> +++ b/drivers/video/pxa3xx-gcu.h
> @@ -0,0 +1,49 @@
> +#ifndef __PXA3XX_GCU_H__
> +#define __PXA3XX_GCU_H__
> +
> +#include <linux/types.h>
> +
> +/* Number of 32bit words in display list (ring buffer). */
> +#define PXA3XX_GCU_BUFFER_WORDS ((256 * 1024 - 256) / 4)
> +
> +/* To be increased when breaking the ABI */
> +#define PXA3XX_GCU_SHARED_MAGIC 0x30000001
> +
> +struct pxa3xx_gcu_shared {
> + u32 buffer[PXA3XX_GCU_BUFFER_WORDS];
> +
> + bool hw_running;
> + int hw_start;
> + int hw_end;
> +
> + int next_start;
> + int next_end;
> + int next_valid;
> +
> + unsigned long buffer_phys;
> +
> + unsigned int num_words;
> + unsigned int num_starts;
> + unsigned int num_done;
> + unsigned int num_interrupts;
> + unsigned int num_wait_idle;
> + unsigned int num_wait_next;
> + unsigned int num_idle;
> +
> + u32 magic;
> +};
> +
> +struct pxa3xx_gcu_register {
> + u32 address; /* in */
> + u32 value; /* in/out */
> +};
> +
> +/* Initialization and synchronization.
> + * Hardware is started from user space via MMIO or via ioctl. */
> +#define PXA3XX_GCU_IOCTL_RESET _IO('G', 0)
> +#define PXA3XX_GCU_IOCTL_START _IO('G', 1)
> +#define PXA3XX_GCU_IOCTL_WAIT_IDLE _IO('G', 2)
> +#define PXA3XX_GCU_IOCTL_WAIT_NEXT _IO('G', 3)
> +
> +#endif /* __PXA3XX_GCU_H__ */
> +
> --
> 1.6.3.1
>
More information about the linux-arm-kernel
mailing list