[01/02] pxa: add 2d graphics driver
Daniel Mack
daniel at caiaq.de
Wed Oct 28 10:56:46 EDT 2009
On Wed, Oct 28, 2009 at 10:47:04AM -0400, Haojian Zhuang wrote:
> From 907ebc5b5dacf1c7ecb4c04decf409469cbe9c74 Mon Sep 17 00:00:00 2001
> From: Haojian Zhuang <haojian.zhuang at marvell.com>
> Date: Sun, 18 Oct 2009 13:43:42 -0400
> Subject: [PATCH] pxa: add 2d graphics driver
>
> PXA3xx series provides 2D graphics function. It supports line draw, chroma
> key, scale, alpha blending, and so on.
>
> Signed-off-by: Haojian Zhuang <haojian.zhuang at marvell.com>
> ---
> arch/arm/mach-pxa/include/mach/pxa3xx-gcu.h | 69 ++
> drivers/char/Kconfig | 6 +
> drivers/char/Makefile | 1 +
> drivers/char/pxa3xx-gcu.c | 1118 +++++++++++++++++++++++++++
> 4 files changed, 1194 insertions(+), 0 deletions(-)
> create mode 100644 arch/arm/mach-pxa/include/mach/pxa3xx-gcu.h
> create mode 100644 drivers/char/pxa3xx-gcu.c
Uh. What's that!? Another one? How is that driver supposed to be used?
Is there any userspace reference? And did you see the DirectFB supported
version of such a driver I submitted several times already?
Sigh. We sould really avoid to have two drivers for the same purpose.
Any idea how to solve this?
Daniel
> diff --git a/arch/arm/mach-pxa/include/mach/pxa3xx-gcu.h
> b/arch/arm/mach-pxa/include/mach/pxa3xx-gcu.h
> new file mode 100644
> index 0000000..14e6b0a
> --- /dev/null
> +++ b/arch/arm/mach-pxa/include/mach/pxa3xx-gcu.h
> @@ -0,0 +1,69 @@
> +/*
> + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#ifndef _PXA3xx_GCU_H
> +#define _PXA3xx_GCU_H
> +
> +#define MAX_DEVICE_GMEM_SIZE (16*1024*1024)
> +#define MAX_CONTEXT_GMEM_SIZE (16*1024*1024)
> +
> +#define GCU_RINGBUF_SIZE (16384)
> +#define GCU_SCRATCHREG_NR (8)
> +
> +#include <asm/ioctl.h>
> +
> +#define PXA3xx_GCU_IO_SUBMIT _IOW('2', 1, struct iovec *)
> +#define PXA3xx_GCU_IO_SYNC _IOW('2', 2, int)
> +
> +#define PXA3xx_GCU_IO_REQUEST_MEM _IOW('2', 10, struct
> pxa3xx_gcu_mem_request *)
> +#define PXA3xx_GCU_IO_RELEASE_MEM _IOW('2', 11, unsigned long)
> +#define PXA3xx_GCU_IO_FLUSH_MEM _IOW('2', 12, unsigned long)
> +
> +#define PXA3xx_GCU_IO_GET_BUS_ADDR _IOW('2', 20, unsigned long)
> +
> +/* #define PXA3xx_GCU_IO_QUERY_GCU _IOR('2', 20, struct m2d_gcu_stat *) */
> +
> +#define PXA3xx_GCU_GRAPHICS_MEM 0
> +#define PXA3xx_GCU_FRAME_BUFFER 1
> +#define PXA3xx_GCU_REGISTERS 2
> +#define PXA3xx_GCU_RING_BUFFER 3
> +
> +#define PXA3xx_GCU_ATTR_COHERENT 0x00
> +#define PXA3xx_GCU_ATTR_WRITECOMBINE 0x10
> +#define PXA3xx_GCU_ATTR_CACHEABLE 0x20
> +
> +#define PXA3xx_GCU_MEM_REQ_TYPE(f) (f & 0x0f)
> +#define PXA3xx_GCU_MEM_REQ_ATTR(f) (f & 0xf0)
> +
> +struct pxa3xx_gcu_mem_req {
> + unsigned int req_type;
> + unsigned int req_size;
> + unsigned long phys_addr;
> + unsigned long mmap_addr;
> + unsigned long mmap_size;
> +};
> +
> +#define PXA3xx_GCU_SUBMIT_MODE_NDELAY (1 << 0)
> +#define PXA3xx_GCU_SUBMIT_MODE_SYNC (1 << 1)
> +
> +struct pxa3xx_gcu_submit_req {
> + unsigned int mode;
> + void *base;
> + size_t len;
> +};
> +
> +#endif
> +
> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> index 08a6f50..a7c468c 100644
> --- a/drivers/char/Kconfig
> +++ b/drivers/char/Kconfig
> @@ -431,6 +431,12 @@ config SGI_MBCS
> If you have an SGI Altix with an attached SABrick
> say Y or M here, otherwise say N.
>
> +config PXA3xx_GCU
> + bool "PXA3xx Processor 2D Graphics Controller Driver"
> + depends on PXA3xx
> + help
> + Enable graphics support on PXA3xx Processor series.
> +
> source "drivers/serial/Kconfig"
>
> config UNIX98_PTYS
> diff --git a/drivers/char/Makefile b/drivers/char/Makefile
> index 19a79dd..b0beced 100644
> --- a/drivers/char/Makefile
> +++ b/drivers/char/Makefile
> @@ -108,6 +108,7 @@ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
> obj-$(CONFIG_TCG_TPM) += tpm/
>
> obj-$(CONFIG_PS3_FLASH) += ps3flash.o
> +obj-$(CONFIG_PXA3xx_GCU) += pxa3xx-gcu.o
>
> obj-$(CONFIG_JS_RTC) += js-rtc.o
> js-rtc-y = rtc.o
> diff --git a/drivers/char/pxa3xx-gcu.c b/drivers/char/pxa3xx-gcu.c
> new file mode 100644
> index 0000000..81c7f11
> --- /dev/null
> +++ b/drivers/char/pxa3xx-gcu.c
> @@ -0,0 +1,1118 @@
> +/*
> + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> +
> + *(C) Copyright 2006 Marvell International Ltd.
> + * All Rights Reserved
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/fs.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/completion.h>
> +#include <linux/miscdevice.h>
> +#include <linux/dma-mapping.h>
> +
> +#include <asm/uaccess.h>
> +#include <asm/dma-mapping.h>
> +#include <asm/cacheflush.h>
> +
> +#include <mach/pxa3xx-gcu.h>
> +
> +enum {
> + /* Miscellaneous Control and Interrupt Information */
> + GCCR = 0x000, /* Configuration Register */
> + GCISCR = 0x004, /* Interrupt Status Control Register */
> + GCIECR = 0x008, /* Interrupt Enable Control Register */
> + GCNOPID = 0x00C, /* NOP ID From Instruction Stream Register */
> + GCALPHASET = 0x010, /* Default Alpha value Control Register */
> + GCTSET = 0x014, /* Default Transparecy Value Control Register */
> + GCFLAGS = 0x018, /* ALU Operations Flags Status Control Register */
> +
> +
> + /* Ring Buffer Information */
> + GCRBBR = 0x020, /* Ring Buffer Base Address Register */
> + GCRBLR = 0x024, /* Ring Buffer Length Register */
> + GCRBHR = 0x028, /* Ring Buffer Head Register */
> + GCRBTR = 0x02C, /* Ring Buffer Tail Register */
> + GCRBEXHR = 0x030, /* Ring Buffer Execution Head Register */
> +
> + /* Batch Buffer Information */
> + GCBBBR = 0x040, /* Batch Buffer Base Address Register */
> + GCBBHR = 0x044, /* Batch Buffer Head Register */
> + GCBBEXHR = 0x048, /* Batch Buffer Execution Head Register */
> +
> + /* Destination 0 Information */
> + GCD0BR = 0x060, /* Destination 0 Base Address Register */
> + GCD0STP = 0x064, /* Destination 0 Step Size Register */
> + GCD0STR = 0x068, /* Destination 0 Stride Size Register */
> + GCD0PF = 0x06C, /* Destination 0 Pixel Type Register */
> +
> + /* Destination 1 Information */
> + GCD1BR = 0x070, /* Destination 1 Base Address Register */
> + GCD1STP = 0x074, /* Destination 1 Step Size Register */
> + GCD1STR = 0x078, /* Destination 1 Stride Size Register */
> + GCD1PF = 0x07C, /* Destination 1 Pixel Type Register */
> +
> + /* Destination 2 Information */
> + GCD2BR = 0x080, /* Destination 2 Base Address Register */
> + GCD2STP = 0x084, /* Destination 2 Step Size Register */
> + GCD2STR = 0x088, /* Destination 2 Stride Size Register */
> + GCD2PF = 0x08C, /* Destination 2 Pixel Type Register */
> +
> + /* Source 0 Information */
> + GCS0BR = 0x0E0, /* Source 0 Base Address Register */
> + GCS0STP = 0x0E4, /* Source 0 Step Size Register */
> + GCS0STR = 0x0E8, /* Source 0 Stride Size Register */
> + GCS0PF = 0x0EC, /* Source 0 Pixel Type Register */
> +
> + /* Source 1 Information */
> + GCS1BR = 0x0F0, /* Source 1 Base Address Register */
> + GCS1STP = 0x0F4, /* Source 1 Step Size Register */
> + GCS1STR = 0x0F8, /* Source 1 Stride Size Register */
> + GCS1PF = 0x0FC, /* Source 1 Pixel Type Register */
> +
> + /* Pixel ALU Scratch Registers */
> + GCSC0WD0 = 0x160, /* Pixel ALU Scratch Register 0 Word 0 */
> + GCSC0WD1 = 0x164, /* Pixel ALU Scratch Register 0 Word 1 */
> + GCSC1WD0 = 0x168, /* Pixel ALU Scratch Register 1 Word 0 */
> + GCSC1WD1 = 0x16C, /* Pixel ALU Scratch Register 1 Word 1 */
> + GCSC2WD0 = 0x170, /* Pixel ALU Scratch Register 2 Word 0 */
> + GCSC2WD1 = 0x174, /* Pixel ALU Scratch Register 2 Word 1 */
> + GCSC3WD0 = 0x178, /* Pixel ALU Scratch Register 3 Word 0 */
> + GCSC3WD1 = 0x17C, /* Pixel ALU Scratch Register 3 Word 1 */
> + GCSC4WD0 = 0x180, /* Pixel ALU Scratch Register 4 Word 0 */
> + GCSC4WD1 = 0x184, /* Pixel ALU Scratch Register 5 Word 1 */
> + GCSC5WD0 = 0x188, /* Pixel ALU Scratch Register 5 Word 0 */
> + GCSC5WD1 = 0x18C, /* Pixel ALU Scratch Register 5 Word 1 */
> + GCSC6WD0 = 0x190, /* Pixel ALU Scratch Register 6 Word 0 */
> + GCSC6WD1 = 0x194, /* Pixel ALU Scratch Register 6 Word 1 */
> + GCSC7WD0 = 0x198, /* Pixel ALU Scratch Register 7 Word 0 */
> + GCSC7WD1 = 0x19C, /* Pixel ALU Scratch Register 7 Word 1 */
> +
> + /* Abort Bad Address Storage Registers */
> + GCCABADDR = 0x1E0, /* Illegal Access Bad Address Register */
> + GCTABADDR = 0x1E4, /* Target Abort Access Register */
> + GCMABADDR = 0x1E8, /* Master Abort Access Register */
> +
> + GCU_MAX_REG = 0x200,
> +};
> +
> +#define GCCR_STOP (1 << 4)
> +#define GCCR_ABORT (1 << 6)
> +#define GCCR_BP_RST (1 << 8)
> +#define GCCR_SYNC_CLR (1 << 9)
> +#define GCCR_MASK (0x000007FF)
> +
> +#define GCIECR_EOB_INTEN (1 << 0)
> +#define GCIECR_IN_INTEN (1 << 1)
> +#define GCIECR_BF_INTEN (1 << 2)
> +#define GCIECR_IOP_INTEN (1 << 3)
> +#define GCIECR_IIN_INTEN (1 << 4)
> +#define GCIECR_EEOB_INTEN (1 << 5)
> +#define GCIECR_PF_INTEN (1 << 6)
> +#define GCIECR_STOP_INTEN (1 << 7)
> +#define GCIECR_FLAG_INTEN (1 << 8)
> +#define GCIECR_MASK (0x000001FF)
> +
> +#define GCISCR_EOB_INTST (1 << 0)
> +#define GCISCR_IN_INTST (1 << 1)
> +#define GCISCR_BF_INTST (1 << 2)
> +#define GCISCR_IOP_INTST (1 << 3)
> +#define GCISCR_IIN_INTST (1 << 4)
> +#define GCISCR_EEOB_INTST (1 << 5)
> +#define GCISCR_PF_INTST (1 << 6)
> +#define GCISCR_STOP_INTST (1 << 7)
> +#define GCISCR_FLAG_INTST (1 << 8)
> +#define GCISCR_MASK (0x000001FF)
> +
> +#define GCU_TIMEOUT (10) /* jiffies unit */
> +
> +struct pxa3xx_gcu_context;
> +
> +struct pxa3xx_gcu_info {
> + struct device *dev;
> + struct platform_device *pdev;
> + unsigned int irq;
> + void __iomem *mmio_base;
> + struct completion done;
> + struct mutex lock;
> +
> + struct list_head list; /* context list */
> + unsigned long count; /* counter of context */
> + struct pxa3xx_gcu_context *last;
> +
> + struct semaphore submit_sem;
> +
> + unsigned long *ring_addr;
> + unsigned long ring_size;
> + unsigned long ring_addr_dma;
> + unsigned long ring_tail_dma;
> +
> + unsigned long total_gmem_size;
> +};
> +
> +struct pxa3xx_gcu_gmem {
> + struct pxa3xx_gcu_info *info;
> + struct list_head list;
> + atomic_t gmem_count;
> + struct page *gmem_pages;
> + size_t gmem_size;
> + unsigned long gmem_virt_addr;
> + unsigned long gmem_phys_addr;
> + unsigned long gmem_uaddr;
> + unsigned long gmem_type;
> +};
> +
> +struct pxa3xx_gcu_buf {
> + unsigned long base;
> + unsigned long stride;
> + unsigned long step;
> + unsigned long format;
> +};
> +
> +struct pxa3xx_gcu_context {
> + struct pxa3xx_gcu_info *info;
> + struct task_struct *task;
> + struct list_head list; /* to struct pxa3xx_gcu_context */
> + //struct pxa3xx_gcu_gmem *gmem;
> + struct list_head mlist; /* to struct pxa3xx_gcu_gmem */
> +
> + unsigned long total_gmem_size;
> + unsigned int err_iin;
> + unsigned int err_iop;
> + unsigned int err_ipf;
> +
> + /*
> + * Graphics memory that is allocated for application usage will be mapped
> + * into user space. Application and GCU can allocate buffers from
> + * this area.
> + */
> + struct pxa3xx_gcu_buf srcbuf0;
> + struct pxa3xx_gcu_buf srcbuf1;
> + struct pxa3xx_gcu_buf dstbuf0;
> + struct pxa3xx_gcu_buf dstbuf1;
> + struct pxa3xx_gcu_buf dstbuf2;
> + unsigned long alpha_set;
> + unsigned long trans_set;
> + unsigned long buffer_mode;
> +};
> +
> +static int pxa3xx_gcu_reset(struct pxa3xx_gcu_info *info);
> +
> +static struct pxa3xx_gcu_info *priv_info = NULL;
> +
> +static irqreturn_t pxa3xx_gcu_irq(int irq, void *devid)
> +{
> + struct pxa3xx_gcu_info *info = (struct pxa3xx_gcu_info *)devid;
> + struct pxa3xx_gcu_context *context = info->last;
> + unsigned long status, gciscr, gciecr, gcrbexhr, gcrbtr;
> +
> + gciscr = __raw_readl(info->mmio_base + GCISCR);
> + gciecr = __raw_readl(info->mmio_base + GCIECR);
> + status = gciscr & gciecr;
> +
> + gciscr &= GCISCR_MASK;
> + __raw_writel(gciscr, info->mmio_base + GCISCR);
> +
> + if (status & (GCISCR_PF_INTST | GCISCR_IIN_INTST | GCISCR_IOP_INTST)) {
> + if (context != NULL) {
> + /* illegal pixel format */
> + if (gciscr & GCISCR_PF_INTST)
> + context->err_ipf++;
> + /* illegal instruction */
> + if (gciscr & GCISCR_IIN_INTST) {
> + context->err_iin++;
> + goto fail;
> + }
> + /* illegal operation */
> + if (gciscr & GCISCR_IOP_INTST) {
> + context->err_iop++;
> + goto fail;
> + }
> + dev_dbg(info->dev, "COUNT ipf:%d, iin:%d, iop:%d\n",
> + context->err_ipf, context->err_iin,
> + context->err_iop);
> + } else
> + dev_dbg(info->dev, "ipf:%d, iin:%d, iop:%d\n",
> + (gciscr & GCISCR_PF_INTST) ? 1 : 0,
> + (gciscr & GCISCR_IIN_INTST) ? 1 : 0,
> + (gciscr & GCISCR_IOP_INTST) ? 1 : 0);
> + }
> + if (status & GCISCR_EEOB_INTST) {
> + gciecr &= ~GCIECR_EEOB_INTEN & GCIECR_MASK;
> + __raw_writel(gciecr, info->mmio_base + GCIECR);
> +
> + gcrbexhr = __raw_readl(info->mmio_base + GCRBEXHR);
> + gcrbtr = __raw_readl(info->mmio_base + GCRBTR);
> + if (gcrbexhr != gcrbtr)
> + dev_err(info->dev, "EEOB: exhead:0x%lx, tail:0x%lx\n",
> + gcrbexhr, gcrbtr);
> + complete(&info->done);
> + }
> + return IRQ_HANDLED;
> +fail:
> + send_sig(SIGILL, context->task, 0);
> + pxa3xx_gcu_reset(info);
> + return IRQ_HANDLED;
> +}
> +
> +static void __save_context(struct pxa3xx_gcu_context *context)
> +{
> + struct pxa3xx_gcu_info *info = context->info;
> +
> + context->srcbuf0.base = __raw_readl(info->mmio_base + GCS0BR);
> + context->srcbuf0.step = __raw_readl(info->mmio_base + GCS0STP);
> + context->srcbuf0.stride = __raw_readl(info->mmio_base + GCS0STR);
> + context->srcbuf0.format = __raw_readl(info->mmio_base + GCS0PF);
> +
> + context->srcbuf1.base = __raw_readl(info->mmio_base + GCS1BR);
> + context->srcbuf1.step = __raw_readl(info->mmio_base + GCS1STP);
> + context->srcbuf1.stride = __raw_readl(info->mmio_base + GCS1STR);
> + context->srcbuf1.format = __raw_readl(info->mmio_base + GCS1PF);
> +
> + context->dstbuf0.base = __raw_readl(info->mmio_base + GCD0BR);
> + context->dstbuf0.step = __raw_readl(info->mmio_base + GCD0STP);
> + context->dstbuf0.stride = __raw_readl(info->mmio_base + GCD0STR);
> + context->dstbuf0.format = __raw_readl(info->mmio_base + GCD0PF);
> +
> + context->dstbuf1.base = __raw_readl(info->mmio_base + GCD1BR);
> + context->dstbuf1.step = __raw_readl(info->mmio_base + GCD1STP);
> + context->dstbuf1.stride = __raw_readl(info->mmio_base + GCD1STR);
> + context->dstbuf1.format = __raw_readl(info->mmio_base + GCD1PF);
> +
> + context->dstbuf2.base = __raw_readl(info->mmio_base + GCD2BR);
> + context->dstbuf2.step = __raw_readl(info->mmio_base + GCD2STP);
> + context->dstbuf2.stride = __raw_readl(info->mmio_base + GCD2STR);
> + context->dstbuf2.format = __raw_readl(info->mmio_base + GCD2PF);
> +
> + context->buffer_mode = __raw_readl(info->mmio_base + GCCR) & 0x0f;
> + context->alpha_set = __raw_readl(info->mmio_base + GCALPHASET);
> + context->trans_set = __raw_readl(info->mmio_base + GCTSET);
> +}
> +
> +static void __restore_context(struct pxa3xx_gcu_context *context)
> +{
> + struct pxa3xx_gcu_info *info = context->info;
> + unsigned long gccr;
> +
> + __raw_writel(context->srcbuf0.base, info->mmio_base + GCS0BR);
> + __raw_writel(context->srcbuf0.step, info->mmio_base + GCS0STP);
> + __raw_writel(context->srcbuf0.stride, info->mmio_base + GCS0STR);
> + __raw_writel(context->srcbuf0.format, info->mmio_base + GCS0PF);
> +
> + __raw_writel(context->srcbuf1.base, info->mmio_base + GCS1BR);
> + __raw_writel(context->srcbuf1.step, info->mmio_base + GCS1STP);
> + __raw_writel(context->srcbuf1.stride, info->mmio_base + GCS1STR);
> + __raw_writel(context->srcbuf1.format, info->mmio_base + GCS1PF);
> +
> + __raw_writel(context->dstbuf0.base, info->mmio_base + GCD0BR);
> + __raw_writel(context->dstbuf0.step, info->mmio_base + GCD0STP);
> + __raw_writel(context->dstbuf0.stride, info->mmio_base + GCD0STR);
> + __raw_writel(context->dstbuf0.format, info->mmio_base + GCD0PF);
> +
> + __raw_writel(context->dstbuf1.base, info->mmio_base + GCD1BR);
> + __raw_writel(context->dstbuf1.step, info->mmio_base + GCD1STP);
> + __raw_writel(context->dstbuf1.stride, info->mmio_base + GCD1STR);
> + __raw_writel(context->dstbuf1.format, info->mmio_base + GCD1PF);
> +
> + __raw_writel(context->dstbuf2.base, info->mmio_base + GCD2BR);
> + __raw_writel(context->dstbuf2.step, info->mmio_base + GCD2STP);
> + __raw_writel(context->dstbuf2.stride, info->mmio_base + GCD2STR);
> + __raw_writel(context->dstbuf2.format, info->mmio_base + GCD2PF);
> +
> + gccr = __raw_readl(info->mmio_base + GCCR);
> + gccr = (gccr & ~0xf) | context->buffer_mode;
> + __raw_writel(gccr, info->mmio_base + GCCR);
> + __raw_writel(context->alpha_set, info->mmio_base + GCALPHASET);
> + __raw_writel(context->trans_set, info->mmio_base + GCTSET);
> +}
> +
> +static int pxa3xx_gcu_append(struct pxa3xx_gcu_info *info, void *usrbuf,
> + size_t len)
> +{
> + unsigned int tail_room, head_room;
> + unsigned long exhead;
> + unsigned long tail = info->ring_tail_dma;
> + unsigned long base = info->ring_addr_dma;
> + unsigned long size = info->ring_size;
> + unsigned char *ring_addr = (unsigned char *)info->ring_addr;
> +
> + exhead = __raw_readl(info->mmio_base + GCRBEXHR);
> + if (tail >= exhead) {
> + tail_room = size - (tail - base);
> + head_room = exhead - base;
> + } else {
> + tail_room = exhead - tail;
> + head_room = 0;
> + }
> + dev_dbg(info->dev, "base:%lx, exhead:%lx, tail:%lx, head_room:%x,"
> + " tail_room:%x\n", base, exhead, tail, head_room, tail_room);
> + dev_dbg(info->dev, "base:%x, head:%x, exhead:%x, tail:%x, length:%x\n",
> + __raw_readl(info->mmio_base + GCRBBR),
> + __raw_readl(info->mmio_base + GCRBHR),
> + __raw_readl(info->mmio_base + GCRBEXHR),
> + __raw_readl(info->mmio_base + GCRBTR),
> + __raw_readl(info->mmio_base + GCRBLR));
> +
> + if (tail_room >= len) {
> + if (copy_from_user(ring_addr + (tail - base), usrbuf, len))
> + return -EFAULT;
> + tail += len;
> + } else if (head_room + tail_room >= len) {
> + if (copy_from_user(ring_addr + (tail - base), usrbuf,
> + tail_room))
> + return -EFAULT;
> + usrbuf += tail_room;
> + len -= tail_room;
> +
> + if (copy_from_user(ring_addr, usrbuf, len))
> + return -EFAULT;
> + tail = info->ring_addr_dma + len;
> + } else
> + return -ENOSPC;
> +
> + if (tail - base == size)
> + tail = base;
> + info->ring_tail_dma = tail;
> + __raw_writel(tail, info->mmio_base + GCRBTR);
> + dev_dbg(info->dev, "tail:%lx, size:%ld\n", tail, size);
> + return 0;
> +}
> +
> +static void pxa3xx_gcu_wait_for_eeob(struct pxa3xx_gcu_info *info)
> +{
> + unsigned long gciscr, gciecr, gcrbexhr, gcrbtr;
> +
> + gciscr = __raw_readl(info->mmio_base + GCISCR);
> + __raw_writel(gciscr | GCISCR_EEOB_INTST, info->mmio_base + GCISCR);
> + gciecr = __raw_readl(info->mmio_base + GCIECR);
> + __raw_writel(gciecr | GCIECR_EEOB_INTEN, info->mmio_base + GCIECR);
> +
> + for (;;) {
> + gcrbexhr = __raw_readl(info->mmio_base + GCRBEXHR);
> + gcrbtr = __raw_readl(info->mmio_base + GCRBTR);
> + if (gcrbexhr == gcrbtr)
> + return;
> + wait_for_completion_timeout(&info->done, GCU_TIMEOUT);
> + }
> +}
> +
> +static int pxa3xx_gcu_kick(struct pxa3xx_gcu_info *info, int sync)
> +{
> + unsigned long gccr;
> +
> + gccr = __raw_readl(info->mmio_base + GCCR);
> + gccr &= ~GCCR_STOP & GCCR_MASK;
> + __raw_writel(gccr, info->mmio_base + GCCR);
> +
> + if (sync)
> + pxa3xx_gcu_wait_for_eeob(info);
> + return 0;
> +}
> +
> +/*
> + * pxa3xx_gcu_sync is used to ensure that submitted instructions are
> + * completely finished or an error occured.
> + */
> +static int pxa3xx_gcu_sync(struct pxa3xx_gcu_context *context)
> +{
> + struct pxa3xx_gcu_info *info = context->info;
> +
> + if (info->last != context)
> + return -EINVAL;
> + pxa3xx_gcu_wait_for_eeob(info);
> + return 0;
> +}
> +
> +/*
> + * pxa3xx_gcu_switch_context should be called only when no operation on current
> + * context.
> + */
> +static void pxa3xx_gcu_switch_context(struct pxa3xx_gcu_context *context)
> +{
> + struct pxa3xx_gcu_info *info = context->info;
> +
> + mutex_lock(&info->lock);
> + if (info->last)
> + __save_context(info->last);
> + if (context)
> + __restore_context(context);
> + info->last = context;
> + mutex_unlock(&info->lock);
> +}
> +
> +/*
> + * pxa3xx_gcu_submit will try to copy the instructions from a user supplied
> + * buffer to the GCU Ring Buffer for execution. This function will return
> + * immediately once the copy is done and the GCU is started. Synchronization
> + * is done by calling pxa3xx_gcu_sync() to ensure that instructions submitted
> + * are completely executed or until error occurs.
> + *
> + * This function will block if the device is current serving another context,
> + * unless non-delay mode is set.
> + */
> +static int pxa3xx_gcu_submit(struct pxa3xx_gcu_context *context,
> + struct pxa3xx_gcu_submit_req *req)
> +{
> + void __user *usrbuf = (void *)req->base;
> + struct pxa3xx_gcu_info *info = NULL;
> + int ndelay, sync, ret;
> + size_t size = req->len;
> +
> + if (context == NULL || context->info == NULL)
> + return -ENODEV;
> + info = context->info;
> +
> + if ((size >= GCU_RINGBUF_SIZE) || (size % 4))
> + return -EINVAL;
> + if (!access_ok(VERIFY_READ, usrbuf, size))
> + return -EFAULT;
> +
> + if (context != info->last) {
> + pxa3xx_gcu_wait_for_eeob(info);
> + pxa3xx_gcu_switch_context(context);
> + }
> +
> + ndelay = req->mode & PXA3xx_GCU_SUBMIT_MODE_NDELAY;
> + sync = req->mode & PXA3xx_GCU_SUBMIT_MODE_SYNC;
> +
> +submit:
> + if (ndelay) {
> + if (down_trylock(&info->submit_sem))
> + return -EAGAIN;
> + } else {
> + if (down_interruptible(&info->submit_sem))
> + return -ERESTARTSYS;
> + }
> +
> + ret = pxa3xx_gcu_append(info, usrbuf, size);
> + if (ret == -ENOSPC) {
> + if (ndelay) {
> + up(&info->submit_sem);
> + pxa3xx_gcu_wait_for_eeob(info);
> + goto submit;
> + }
> + }
> +
> + if (ret == 0)
> + ret = pxa3xx_gcu_kick(info, sync);
> + up(&info->submit_sem);
> + return ret;
> +}
> +
> +static int pxa3xx_gcu_reset(struct pxa3xx_gcu_info *info)
> +{
> + unsigned int gccr, gciscr, gciecr;
> +
> + info->ring_tail_dma = info->ring_addr_dma;
> + gccr = __raw_readl(info->mmio_base + GCCR) & 0x0F;
> + __raw_writel(gccr | GCCR_ABORT, info->mmio_base + GCCR);
> + udelay(10);
> + gccr = __raw_readl(info->mmio_base + GCCR) & 0x0F;
> + __raw_writel(gccr | GCCR_STOP, info->mmio_base + GCCR);
> +
> + __raw_writel(0, info->mmio_base + GCRBLR);
> + __raw_writel(info->ring_addr_dma, info->mmio_base + GCRBBR);
> + __raw_writel(info->ring_tail_dma, info->mmio_base + GCRBTR);
> + __raw_writel(info->ring_size, info->mmio_base + GCRBLR);
> + dev_dbg(info->dev, "ring head:%lx, ring tail:%lx, ring size:%lx\n",
> + info->ring_addr_dma, info->ring_tail_dma, info->ring_size);
> + dev_dbg(info->dev, "base:%x, head:%x, exhead:%x, tail:%x, length:%x\n",
> + __raw_readl(info->mmio_base + GCRBBR),
> + __raw_readl(info->mmio_base + GCRBHR),
> + __raw_readl(info->mmio_base + GCRBEXHR),
> + __raw_readl(info->mmio_base + GCRBTR),
> + __raw_readl(info->mmio_base + GCRBLR));
> +
> + gciscr = __raw_readl(info->mmio_base + GCISCR);
> + __raw_writel(gciscr | GCISCR_MASK, info->mmio_base + GCISCR);
> + gciecr = __raw_readl(info->mmio_base + GCIECR);
> + gciecr &= ~GCIECR_MASK;
> + gciecr |= GCIECR_PF_INTEN | GCIECR_IIN_INTEN | GCIECR_IOP_INTEN;
> + __raw_writel(gciecr, info->mmio_base + GCIECR);
> +
> + __raw_writel(0, info->mmio_base + GCNOPID);
> + __raw_writel(0, info->mmio_base + GCTABADDR);
> + __raw_writel(0, info->mmio_base + GCMABADDR);
> + return 0;
> +}
> +
> +static int dump_gmem(struct pxa3xx_gcu_context *context)
> +{
> + struct pxa3xx_gcu_info *info = context->info;
> + struct pxa3xx_gcu_gmem *gmem = NULL, *n = NULL;
> +
> + list_for_each_entry_safe(gmem, n, &context->mlist, list) {
> + dev_dbg(info->dev, "virt addr:0x%lx, phys addr:0x%lx,"
> + " size:0x%x, usr addr:0x%lx\n", gmem->gmem_virt_addr,
> + gmem->gmem_phys_addr, gmem->gmem_size,
> + gmem->gmem_uaddr);
> + }
> + return 0;
> +}
> +
> +static struct pxa3xx_gcu_gmem *__alloc_gmem(struct pxa3xx_gcu_context *context,
> + size_t size)
> +{
> + struct pxa3xx_gcu_info *info = context->info;
> + struct page *page, *end;
> + struct pxa3xx_gcu_gmem *gmem;
> + unsigned long order, addr;
> +
> + if (info == NULL)
> + goto out;
> + if (info->total_gmem_size + size > MAX_DEVICE_GMEM_SIZE)
> + goto out;
> + if (context->total_gmem_size + size > MAX_CONTEXT_GMEM_SIZE)
> + goto out;
> +
> + gmem = kzalloc(sizeof(struct pxa3xx_gcu_gmem), GFP_KERNEL);
> + if (gmem == NULL)
> + goto out;
> +
> + size = PAGE_ALIGN(size);
> + order = get_order(size);
> + page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
> + if (page == NULL)
> + goto out_mem;
> + addr = (unsigned long)page_address(page);
> + end = page + (1 << order);
> + dma_cache_maint((void *)addr, size, DMA_FROM_DEVICE);
> +
> + gmem->gmem_size = size;
> + gmem->gmem_pages = page;
> + gmem->gmem_virt_addr = addr;
> + gmem->gmem_phys_addr = virt_to_phys((void *)addr);
> +
> + context->total_gmem_size += size;
> + info->total_gmem_size += size;
> +
> + do {
> + SetPageReserved(page);
> + page++;
> + } while (size -= PAGE_SIZE);
> +
> + /* free the unused pages */
> + while (page < end)
> + __free_page(page++);
> +
> + atomic_set(&gmem->gmem_count, 1);
> + return gmem;
> +out_mem:
> + kfree(gmem);
> +out:
> + return NULL;
> +}
> +
> +static void __free_gmem(struct pxa3xx_gcu_context *context,
> + struct pxa3xx_gcu_gmem *gmem)
> +{
> + struct pxa3xx_gcu_info *info = context->info;
> + struct page *page = gmem->gmem_pages;
> + size_t size = gmem->gmem_size;
> + unsigned long addr;
> +
> + if (!atomic_dec_and_test(&gmem->gmem_count))
> + return;
> + context->total_gmem_size -= size;
> + info->total_gmem_size -= size;
> +
> + addr = (unsigned long)page_address(page);
> + dma_cache_maint((void *)addr, size, DMA_FROM_DEVICE);
> + for (; size > 0; size -= PAGE_SIZE, page++) {
> + ClearPageReserved(page);
> + __free_page(page);
> + }
> +
> + mutex_lock(&info->lock);
> + list_del(&gmem->list);
> + mutex_unlock(&info->lock);
> + kfree(gmem);
> +}
> +
> +static int __request_mem(struct pxa3xx_gcu_context *context,
> + struct pxa3xx_gcu_mem_req *req)
> +{
> + struct pxa3xx_gcu_info *info = context->info;
> + struct pxa3xx_gcu_gmem *gmem = NULL;
> + int ret;
> +
> + switch (PXA3xx_GCU_MEM_REQ_TYPE(req->req_type)) {
> + case PXA3xx_GCU_GRAPHICS_MEM:
> + if (req->req_size == 0) {
> + ret = req->req_type;
> + memset(req, 0, sizeof(struct pxa3xx_gcu_mem_req));
> + req->req_type = ret;
> + return 0;
> + }
> +
> + gmem = __alloc_gmem(context, req->req_size);
> + if (gmem == NULL)
> + return -ENOMEM;
> +
> + gmem->gmem_type = req->req_type;
> +
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + mutex_lock(&info->lock);
> + list_add(&gmem->list, &context->mlist);
> + mutex_unlock(&info->lock);
> +
> + req->mmap_addr = (unsigned long)gmem->gmem_virt_addr;
> + req->mmap_size = gmem->gmem_size;
> + req->phys_addr = gmem->gmem_phys_addr;
> + dump_gmem(context);
> + return 0;
> +}
> +
> +static int __release_mem(struct pxa3xx_gcu_context *context,
> + unsigned long addr)
> +{
> + struct pxa3xx_gcu_gmem *gmem = NULL, *n = NULL;
> +
> + if (addr == 0) {
> + /* release all allocated memory for this context */
> + list_for_each_entry_safe(gmem, n, &context->mlist, list)
> + __free_gmem(context, gmem);
> + } else {
> + list_for_each_entry_safe(gmem, n, &context->mlist, list) {
> + if (gmem->gmem_virt_addr == addr)
> + __free_gmem(context, gmem);
> + }
> + }
> + gmem = NULL;
> + n = NULL;
> + return 0;
> +}
> +
> +static struct pxa3xx_gcu_context *__create_context(struct
> pxa3xx_gcu_info *info)
> +{
> + struct pxa3xx_gcu_context *context;
> +
> + if (info == NULL)
> + return NULL;
> +
> + context = kzalloc(sizeof(struct pxa3xx_gcu_context), GFP_KERNEL);
> + if (context == NULL)
> + return NULL;
> +
> + context->info = info;
> + context->task = current;
> + context->alpha_set = 0;
> + context->trans_set = 0;
> + context->buffer_mode = 0x08;
> +
> + INIT_LIST_HEAD(&context->mlist);
> +
> + mutex_lock(&info->lock);
> + list_add_tail(&context->list, &info->list);
> + info->count++;
> + mutex_unlock(&info->lock);
> +
> + return context;
> +}
> +
> +static void __free_context(struct pxa3xx_gcu_context *context)
> +{
> + struct pxa3xx_gcu_info *info;
> +
> + if (context == NULL || context->info == NULL)
> + return;
> +
> + info = context->info;
> + __release_mem(context, 0);
> + mutex_lock(&info->lock);
> + list_del(&context->list);
> + info->count--;
> + if (info->last == context)
> + info->last = NULL;
> + mutex_unlock(&info->lock);
> + kfree(context);
> +}
> +
> +#ifdef CONFIG_PM
> +static int pxa3xx_gcu_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> + struct pxa3xx_gcu_info *info = platform_get_drvdata(pdev);
> +
> + pxa3xx_gcu_wait_for_eeob(info);
> + if (info->last) {
> + mutex_lock(&info->lock);
> + __save_context(info->last);
> + mutex_unlock(&info->lock);
> + }
> + return 0;
> +}
> +
> +static int pxa3xx_gcu_resume(struct platform_device *pdev)
> +{
> + struct pxa3xx_gcu_info *info = platform_get_drvdata(pdev);
> +
> + pxa3xx_gcu_reset(info);
> + if (info->last) {
> + mutex_lock(&info->lock);
> + __restore_context(info->last);
> + mutex_unlock(&info->lock);
> + }
> + return 0;
> +}
> +#endif
> +
> +static int pxa3xx_gcu_open(struct inode *inode, struct file *file)
> +{
> + struct pxa3xx_gcu_context *context = file->private_data;
> +
> + if (context == NULL) {
> + if (priv_info == NULL)
> + return -EFAULT;
> + context = __create_context(priv_info);
> + if (context == NULL)
> + return -ENOMEM;
> + file->private_data = context;
> + }
> + return 0;
> +}
> +
> +static int pxa3xx_gcu_release(struct inode *inode, struct file *file)
> +{
> + struct pxa3xx_gcu_context *context = file->private_data;
> +
> + pxa3xx_gcu_wait_for_eeob(context->info);
> + __free_context(context);
> + return 0;
> +}
> +
> +static int pxa3xx_gcu_ioctl(struct inode *inode, struct file *file,
> + unsigned int cmd, unsigned long arg)
> +{
> + struct pxa3xx_gcu_context *context = file->private_data;
> + struct pxa3xx_gcu_info *info = (struct pxa3xx_gcu_info *)context->info;
> + struct pxa3xx_gcu_gmem *gmem = NULL;
> + struct pxa3xx_gcu_submit_req submit_req;
> + struct pxa3xx_gcu_mem_req mem_req;
> + struct vm_area_struct *vma = NULL;
> + void __user *uarg = (void *)arg;
> + int ret = 0;
> +
> + if (info == NULL)
> + return -ENODEV;
> +
> + switch (cmd) {
> + case PXA3xx_GCU_IO_REQUEST_MEM:
> + if (copy_from_user(&mem_req, uarg,
> + sizeof(struct pxa3xx_gcu_mem_req))) {
> + ret = -EFAULT;
> + goto out;
> + }
> + ret = __request_mem(context, &mem_req);
> + if (ret < 0)
> + goto out;
> + if (copy_to_user(uarg, &mem_req,
> + sizeof(struct pxa3xx_gcu_mem_req))) {
> + __release_mem(context, mem_req.mmap_addr);
> + ret = -EFAULT;
> + goto out;
> + }
> + break;
> + case PXA3xx_GCU_IO_RELEASE_MEM:
> + __release_mem(context, arg);
> + break;
> + case PXA3xx_GCU_IO_FLUSH_MEM:
> + list_for_each_entry(gmem, &context->mlist, list) {
> + if (gmem->gmem_uaddr == arg) {
> + dmac_flush_range((void *)arg, (void *)arg
> + + gmem->gmem_size);
> + outer_flush_range(gmem->gmem_phys_addr,
> + gmem->gmem_phys_addr
> + + gmem->gmem_size);
> + }
> + }
> + break;
> + case PXA3xx_GCU_IO_SUBMIT:
> + if (copy_from_user(&submit_req, uarg,
> + sizeof(struct pxa3xx_gcu_submit_req))) {
> + ret = -EFAULT;
> + goto out;
> + }
> + ret = pxa3xx_gcu_submit(context, &submit_req);
> + break;
> + case PXA3xx_GCU_IO_SYNC:
> + ret = pxa3xx_gcu_sync(context);
> + break;
> + case PXA3xx_GCU_IO_GET_BUS_ADDR:
> + /* assume user provided virtual memory are physical continous */
> + memset(&mem_req, 0, sizeof(struct pxa3xx_gcu_mem_req));
> + if (copy_from_user(&mem_req.mmap_addr, uarg,
> + sizeof(unsigned long))) {
> + ret = -EFAULT;
> + goto out;
> + }
> + vma = find_vma(current->mm, mem_req.mmap_addr);
> + if (vma == NULL) {
> + ret = -EINVAL;
> + goto out;
> + }
> + mem_req.phys_addr = (vma->vm_pgoff << PAGE_SHIFT)
> + + (mem_req.mmap_addr % PAGE_SIZE);
> + if (copy_to_user(uarg, &mem_req.phys_addr,
> + sizeof(unsigned long))) {
> + ret = -EFAULT;
> + goto out;
> + }
> + break;
> + default:
> + break;
> + }
> + return 0;
> +out:
> + dev_dbg(info->dev, "%s, return value: %d\n", __func__, ret);
> + return ret;
> +}
> +
> +static int pxa3xx_gcu_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> + struct pxa3xx_gcu_context *context = file->private_data;
> + struct pxa3xx_gcu_gmem *gmem = NULL, *n = NULL;
> + unsigned long pg_off;
> + pgprot_t pgprot;
> +
> + if (context == NULL)
> + return -ENODEV;
> +
> + /* mmap() specifies the physical address as offset parameter */
> + list_for_each_entry_safe(gmem, n, &context->mlist, list) {
> + pg_off = gmem->gmem_phys_addr >> PAGE_SHIFT;
> + if (pg_off == vma->vm_pgoff)
> + break;
> + }
> + if (gmem == NULL)
> + return -ENOMEM;
> +
> + switch (PXA3xx_GCU_MEM_REQ_ATTR(gmem->gmem_type)) {
> + case PXA3xx_GCU_ATTR_COHERENT:
> + pgprot = pgprot_noncached(vma->vm_page_prot);
> + break;
> + case PXA3xx_GCU_ATTR_WRITECOMBINE:
> + pgprot = pgprot_writecombine(vma->vm_page_prot);
> + break;
> + case PXA3xx_GCU_ATTR_CACHEABLE:
> + default:
> + pgprot = vma->vm_page_prot;
> + break;
> + }
> + vma->vm_page_prot = pgprot | PAGE_SHARED;
> + vma->vm_flags |= (VM_IO | VM_RESERVED);
> + if (remap_pfn_range(vma, vma->vm_start,
> + gmem->gmem_phys_addr >> PAGE_SHIFT,
> + vma->vm_end - vma->vm_start,
> + vma->vm_page_prot))
> + return -EAGAIN;
> + gmem->gmem_uaddr = vma->vm_start;
> + dump_gmem(context);
> + dma_cache_maint((void *)gmem->gmem_virt_addr,
> + vma->vm_end - vma->vm_start,
> + DMA_BIDIRECTIONAL);
> + return 0;
> +}
> +
> +static int pxa3xx_gcu_fsync(struct file *file, struct dentry *dentry,
> + int datasync)
> +{
> + struct pxa3xx_gcu_context *context = file->private_data;
> +
> + return pxa3xx_gcu_sync(context);
> +}
> +
> +static struct file_operations pxa3xx_gcu_fops = {
> + .owner = THIS_MODULE,
> + .open = pxa3xx_gcu_open,
> + .release = pxa3xx_gcu_release,
> + .ioctl = pxa3xx_gcu_ioctl,
> + .mmap = pxa3xx_gcu_mmap,
> + .fsync = pxa3xx_gcu_fsync,
> +};
> +
> +static struct miscdevice pxa3xx_gcu_miscdev = {
> + .minor = MISC_DYNAMIC_MINOR,
> + .name = "m2d",
> + .fops = &pxa3xx_gcu_fops,
> +};
> +
> +static int pxa3xx_gcu_probe(struct platform_device *pdev)
> +{
> + struct pxa3xx_gcu_info *info;
> + struct resource *res;
> + int ret, size;
> +
> + ret = misc_register(&pxa3xx_gcu_miscdev);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "unable to register device /dev/m2d\n");
> + return ret;
> + }
> +
> + info = kzalloc(sizeof(struct pxa3xx_gcu_info), GFP_KERNEL);
> + if (info == NULL) {
> + dev_err(&pdev->dev, "failed to allocate memory\n");
> + ret = -ENOMEM;
> + goto err;
> + }
> + info->pdev = pdev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (res == NULL) {
> + dev_err(&pdev->dev, "no resource definied for MEM\n");
> + ret = -ENXIO;
> + goto err_res;
> + }
> +
> + res = request_mem_region(res->start, GCU_MAX_REG, pdev->name);
> + if (res == NULL) {
> + dev_err(&pdev->dev, "failed to request memory resource\n");
> + ret = -EBUSY;
> + goto err_res;
> + }
> +
> + info->mmio_base = ioremap(res->start, GCU_MAX_REG);
> + if (info->mmio_base == NULL) {
> + dev_err(&pdev->dev, "failed to ioremap register\n");
> + ret = -ENOMEM;
> + goto err_map;
> + }
> +
> + info->irq = platform_get_irq(pdev, 0);
> + if (info->irq < 0) {
> + dev_err(&pdev->dev, "failed to get IRQ\n");
> + ret = -ENXIO;
> + goto err_irq;
> + }
> + ret = request_irq(info->irq, pxa3xx_gcu_irq, IRQF_DISABLED,
> + pdev->name, info);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to request GCU IRQ\n");
> + ret = -EBUSY;
> + goto err_irq;
> + }
> +
> + info->ring_addr = dma_alloc_coherent(info->dev,
> + GCU_RINGBUF_SIZE + 256,
> + (dma_addr_t *)&info->ring_addr_dma,
> + GFP_KERNEL);
> + if (info->ring_addr == NULL) {
> + dev_err(&pdev->dev, "failed to allocate ring buffer\n");
> + ret = -ENOMEM;
> + goto err_mem;
> + }
> + size = ALIGN(info->ring_addr_dma, 256) - info->ring_addr_dma;
> + if (size > 0) {
> + info->ring_addr_dma += size;
> + info->ring_addr += size;
> + }
> + info->ring_tail_dma = info->ring_addr_dma;
> + info->ring_size = ALIGN(info->ring_addr_dma + GCU_RINGBUF_SIZE, 256)
> + - info->ring_addr_dma;
> + info->dev = &pdev->dev;
> +
> + pxa3xx_gcu_reset(info);
> +
> + mutex_init(&info->lock);
> + init_MUTEX(&info->submit_sem);
> + init_completion(&info->done);
> + INIT_LIST_HEAD(&info->list);
> +
> + priv_info = info;
> +
> + platform_set_drvdata(pdev, info);
> +
> + dev_info(&pdev->dev, "2D Graphics Driver for PXA3xx\n");
> +
> + return 0;
> +err_mem:
> + free_irq(info->irq, info);
> +err_irq:
> + iounmap(info->mmio_base);
> +err_map:
> + release_mem_region(res->start, GCU_MAX_REG);
> +err_res:
> + kfree(info);
> +err:
> + misc_deregister(&pxa3xx_gcu_miscdev);
> + return ret;
> +}
> +
> +static int pxa3xx_gcu_remove(struct platform_device *pdev)
> +{
> + struct pxa3xx_gcu_info *info = platform_get_drvdata(pdev);
> + unsigned int gccr;
> +
> + /* Stop the graphics controller */
> + gccr = __raw_readl(info->mmio_base + GCCR);
> + if ((gccr & GCCR_STOP) == 0) {
> + gccr &= GCCR_MASK;
> + gccr |= GCCR_STOP;
> + }
> + __raw_writel(gccr, info->mmio_base + GCCR);
> +
> + __raw_writel(0, info->mmio_base + GCRBLR);
> + __raw_writel(0, info->mmio_base + GCRBBR);
> + __raw_writel(0, info->mmio_base + GCRBTR);
> + dma_free_coherent(info->dev, info->ring_size, info->ring_addr,
> + (dma_addr_t)info->ring_addr_dma);
> + free_irq(info->irq, info);
> +
> + misc_deregister(&pxa3xx_gcu_miscdev);
> +
> + priv_info = NULL;
> +
> + return 0;
> +}
> +
> +static struct platform_driver pxa3xx_gcu_driver = {
> + .driver = {
> + .name = "pxa3xx-gcu",
> + .owner = THIS_MODULE,
> + },
> + .probe = pxa3xx_gcu_probe,
> + .remove = pxa3xx_gcu_remove,
> +#ifdef CONFIG_PM
> + .suspend = pxa3xx_gcu_suspend,
> + .resume = pxa3xx_gcu_resume,
> +#endif
> +};
> +
> +static int __devinit pxa3xx_gcu_init(void)
> +{
> + platform_driver_register(&pxa3xx_gcu_driver);
> +
> + return 0;
> +}
> +
> +static void __exit pxa3xx_gcu_exit(void)
> +{
> + platform_driver_unregister(&pxa3xx_gcu_driver);
> +}
> +
> +module_init(pxa3xx_gcu_init);
> +module_exit(pxa3xx_gcu_exit);
> +MODULE_LICENSE("GPL");
> +
> --
> 1.5.6.5
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
More information about the linux-arm-kernel
mailing list