xref: /openbmc/linux/drivers/video/fbdev/pxa3xx-gcu.c (revision f7018c21350204c4cf628462f229d44d03545254)
1*f7018c21STomi Valkeinen /*
2*f7018c21STomi Valkeinen  *  pxa3xx-gcu.c - Linux kernel module for PXA3xx graphics controllers
3*f7018c21STomi Valkeinen  *
4*f7018c21STomi Valkeinen  *  This driver needs a DirectFB counterpart in user space, communication
5*f7018c21STomi Valkeinen  *  is handled via mmap()ed memory areas and an ioctl.
6*f7018c21STomi Valkeinen  *
7*f7018c21STomi Valkeinen  *  Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
8*f7018c21STomi Valkeinen  *  Copyright (c) 2009 Janine Kropp <nin@directfb.org>
9*f7018c21STomi Valkeinen  *  Copyright (c) 2009 Denis Oliver Kropp <dok@directfb.org>
10*f7018c21STomi Valkeinen  *
11*f7018c21STomi Valkeinen  *  This program is free software; you can redistribute it and/or modify
12*f7018c21STomi Valkeinen  *  it under the terms of the GNU General Public License as published by
13*f7018c21STomi Valkeinen  *  the Free Software Foundation; either version 2 of the License, or
14*f7018c21STomi Valkeinen  *  (at your option) any later version.
15*f7018c21STomi Valkeinen  *
16*f7018c21STomi Valkeinen  *  This program is distributed in the hope that it will be useful,
17*f7018c21STomi Valkeinen  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18*f7018c21STomi Valkeinen  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19*f7018c21STomi Valkeinen  *  GNU General Public License for more details.
20*f7018c21STomi Valkeinen  *
21*f7018c21STomi Valkeinen  *  You should have received a copy of the GNU General Public License
22*f7018c21STomi Valkeinen  *  along with this program; if not, write to the Free Software
23*f7018c21STomi Valkeinen  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24*f7018c21STomi Valkeinen  */
25*f7018c21STomi Valkeinen 
26*f7018c21STomi Valkeinen /*
27*f7018c21STomi Valkeinen  * WARNING: This controller is attached to System Bus 2 of the PXA which
28*f7018c21STomi Valkeinen  * needs its arbiter to be enabled explicitly (CKENB & 1<<9).
29*f7018c21STomi Valkeinen  * There is currently no way to do this from Linux, so you need to teach
30*f7018c21STomi Valkeinen  * your bootloader for now.
31*f7018c21STomi Valkeinen  */
32*f7018c21STomi Valkeinen 
33*f7018c21STomi Valkeinen #include <linux/module.h>
34*f7018c21STomi Valkeinen #include <linux/platform_device.h>
35*f7018c21STomi Valkeinen #include <linux/dma-mapping.h>
36*f7018c21STomi Valkeinen #include <linux/miscdevice.h>
37*f7018c21STomi Valkeinen #include <linux/interrupt.h>
38*f7018c21STomi Valkeinen #include <linux/spinlock.h>
39*f7018c21STomi Valkeinen #include <linux/uaccess.h>
40*f7018c21STomi Valkeinen #include <linux/ioctl.h>
41*f7018c21STomi Valkeinen #include <linux/delay.h>
42*f7018c21STomi Valkeinen #include <linux/sched.h>
43*f7018c21STomi Valkeinen #include <linux/slab.h>
44*f7018c21STomi Valkeinen #include <linux/clk.h>
45*f7018c21STomi Valkeinen #include <linux/fs.h>
46*f7018c21STomi Valkeinen #include <linux/io.h>
47*f7018c21STomi Valkeinen 
48*f7018c21STomi Valkeinen #include "pxa3xx-gcu.h"
49*f7018c21STomi Valkeinen 
50*f7018c21STomi Valkeinen #define DRV_NAME	"pxa3xx-gcu"
51*f7018c21STomi Valkeinen #define MISCDEV_MINOR	197
52*f7018c21STomi Valkeinen 
53*f7018c21STomi Valkeinen #define REG_GCCR	0x00
54*f7018c21STomi Valkeinen #define GCCR_SYNC_CLR	(1 << 9)
55*f7018c21STomi Valkeinen #define GCCR_BP_RST	(1 << 8)
56*f7018c21STomi Valkeinen #define GCCR_ABORT	(1 << 6)
57*f7018c21STomi Valkeinen #define GCCR_STOP	(1 << 4)
58*f7018c21STomi Valkeinen 
59*f7018c21STomi Valkeinen #define REG_GCISCR	0x04
60*f7018c21STomi Valkeinen #define REG_GCIECR	0x08
61*f7018c21STomi Valkeinen #define REG_GCRBBR	0x20
62*f7018c21STomi Valkeinen #define REG_GCRBLR	0x24
63*f7018c21STomi Valkeinen #define REG_GCRBHR	0x28
64*f7018c21STomi Valkeinen #define REG_GCRBTR	0x2C
65*f7018c21STomi Valkeinen #define REG_GCRBEXHR	0x30
66*f7018c21STomi Valkeinen 
67*f7018c21STomi Valkeinen #define IE_EOB		(1 << 0)
68*f7018c21STomi Valkeinen #define IE_EEOB		(1 << 5)
69*f7018c21STomi Valkeinen #define IE_ALL		0xff
70*f7018c21STomi Valkeinen 
71*f7018c21STomi Valkeinen #define SHARED_SIZE	PAGE_ALIGN(sizeof(struct pxa3xx_gcu_shared))
72*f7018c21STomi Valkeinen 
73*f7018c21STomi Valkeinen /* #define PXA3XX_GCU_DEBUG */
74*f7018c21STomi Valkeinen /* #define PXA3XX_GCU_DEBUG_TIMER */
75*f7018c21STomi Valkeinen 
76*f7018c21STomi Valkeinen #ifdef PXA3XX_GCU_DEBUG
77*f7018c21STomi Valkeinen #define QDUMP(msg)					\
78*f7018c21STomi Valkeinen 	do {						\
79*f7018c21STomi Valkeinen 		QPRINT(priv, KERN_DEBUG, msg);		\
80*f7018c21STomi Valkeinen 	} while (0)
81*f7018c21STomi Valkeinen #else
82*f7018c21STomi Valkeinen #define QDUMP(msg)	do {} while (0)
83*f7018c21STomi Valkeinen #endif
84*f7018c21STomi Valkeinen 
85*f7018c21STomi Valkeinen #define QERROR(msg)					\
86*f7018c21STomi Valkeinen 	do {						\
87*f7018c21STomi Valkeinen 		QPRINT(priv, KERN_ERR, msg);		\
88*f7018c21STomi Valkeinen 	} while (0)
89*f7018c21STomi Valkeinen 
90*f7018c21STomi Valkeinen struct pxa3xx_gcu_batch {
91*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch *next;
92*f7018c21STomi Valkeinen 	u32			*ptr;
93*f7018c21STomi Valkeinen 	dma_addr_t		 phys;
94*f7018c21STomi Valkeinen 	unsigned long		 length;
95*f7018c21STomi Valkeinen };
96*f7018c21STomi Valkeinen 
97*f7018c21STomi Valkeinen struct pxa3xx_gcu_priv {
98*f7018c21STomi Valkeinen 	void __iomem		 *mmio_base;
99*f7018c21STomi Valkeinen 	struct clk		 *clk;
100*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_shared *shared;
101*f7018c21STomi Valkeinen 	dma_addr_t		  shared_phys;
102*f7018c21STomi Valkeinen 	struct resource		 *resource_mem;
103*f7018c21STomi Valkeinen 	struct miscdevice	  misc_dev;
104*f7018c21STomi Valkeinen 	wait_queue_head_t	  wait_idle;
105*f7018c21STomi Valkeinen 	wait_queue_head_t	  wait_free;
106*f7018c21STomi Valkeinen 	spinlock_t		  spinlock;
107*f7018c21STomi Valkeinen 	struct timeval 		  base_time;
108*f7018c21STomi Valkeinen 
109*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch *free;
110*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch *ready;
111*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch *ready_last;
112*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch *running;
113*f7018c21STomi Valkeinen };
114*f7018c21STomi Valkeinen 
115*f7018c21STomi Valkeinen static inline unsigned long
116*f7018c21STomi Valkeinen gc_readl(struct pxa3xx_gcu_priv *priv, unsigned int off)
117*f7018c21STomi Valkeinen {
118*f7018c21STomi Valkeinen 	return __raw_readl(priv->mmio_base + off);
119*f7018c21STomi Valkeinen }
120*f7018c21STomi Valkeinen 
121*f7018c21STomi Valkeinen static inline void
122*f7018c21STomi Valkeinen gc_writel(struct pxa3xx_gcu_priv *priv, unsigned int off, unsigned long val)
123*f7018c21STomi Valkeinen {
124*f7018c21STomi Valkeinen 	__raw_writel(val, priv->mmio_base + off);
125*f7018c21STomi Valkeinen }
126*f7018c21STomi Valkeinen 
127*f7018c21STomi Valkeinen #define QPRINT(priv, level, msg)					\
128*f7018c21STomi Valkeinen 	do {								\
129*f7018c21STomi Valkeinen 		struct timeval tv;					\
130*f7018c21STomi Valkeinen 		struct pxa3xx_gcu_shared *shared = priv->shared;	\
131*f7018c21STomi Valkeinen 		u32 base = gc_readl(priv, REG_GCRBBR);			\
132*f7018c21STomi Valkeinen 									\
133*f7018c21STomi Valkeinen 		do_gettimeofday(&tv);					\
134*f7018c21STomi Valkeinen 									\
135*f7018c21STomi Valkeinen 		printk(level "%ld.%03ld.%03ld - %-17s: %-21s (%s, "	\
136*f7018c21STomi Valkeinen 			"STATUS "					\
137*f7018c21STomi Valkeinen 			"0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, "	\
138*f7018c21STomi Valkeinen 			"T %5ld)\n",					\
139*f7018c21STomi Valkeinen 			tv.tv_sec - priv->base_time.tv_sec,		\
140*f7018c21STomi Valkeinen 			tv.tv_usec / 1000, tv.tv_usec % 1000,		\
141*f7018c21STomi Valkeinen 			__func__, msg,					\
142*f7018c21STomi Valkeinen 			shared->hw_running ? "running" : "   idle",	\
143*f7018c21STomi Valkeinen 			gc_readl(priv, REG_GCISCR),			\
144*f7018c21STomi Valkeinen 			gc_readl(priv, REG_GCRBBR),			\
145*f7018c21STomi Valkeinen 			gc_readl(priv, REG_GCRBLR),			\
146*f7018c21STomi Valkeinen 			(gc_readl(priv, REG_GCRBEXHR) - base) / 4,	\
147*f7018c21STomi Valkeinen 			(gc_readl(priv, REG_GCRBHR) - base) / 4,	\
148*f7018c21STomi Valkeinen 			(gc_readl(priv, REG_GCRBTR) - base) / 4);	\
149*f7018c21STomi Valkeinen 	} while (0)
150*f7018c21STomi Valkeinen 
151*f7018c21STomi Valkeinen static void
152*f7018c21STomi Valkeinen pxa3xx_gcu_reset(struct pxa3xx_gcu_priv *priv)
153*f7018c21STomi Valkeinen {
154*f7018c21STomi Valkeinen 	QDUMP("RESET");
155*f7018c21STomi Valkeinen 
156*f7018c21STomi Valkeinen 	/* disable interrupts */
157*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCIECR, 0);
158*f7018c21STomi Valkeinen 
159*f7018c21STomi Valkeinen 	/* reset hardware */
160*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCCR, GCCR_ABORT);
161*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCCR, 0);
162*f7018c21STomi Valkeinen 
163*f7018c21STomi Valkeinen 	memset(priv->shared, 0, SHARED_SIZE);
164*f7018c21STomi Valkeinen 	priv->shared->buffer_phys = priv->shared_phys;
165*f7018c21STomi Valkeinen 	priv->shared->magic = PXA3XX_GCU_SHARED_MAGIC;
166*f7018c21STomi Valkeinen 
167*f7018c21STomi Valkeinen 	do_gettimeofday(&priv->base_time);
168*f7018c21STomi Valkeinen 
169*f7018c21STomi Valkeinen 	/* set up the ring buffer pointers */
170*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCRBLR, 0);
171*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCRBBR, priv->shared_phys);
172*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCRBTR, priv->shared_phys);
173*f7018c21STomi Valkeinen 
174*f7018c21STomi Valkeinen 	/* enable all IRQs except EOB */
175*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCIECR, IE_ALL & ~IE_EOB);
176*f7018c21STomi Valkeinen }
177*f7018c21STomi Valkeinen 
178*f7018c21STomi Valkeinen static void
179*f7018c21STomi Valkeinen dump_whole_state(struct pxa3xx_gcu_priv *priv)
180*f7018c21STomi Valkeinen {
181*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_shared *sh = priv->shared;
182*f7018c21STomi Valkeinen 	u32 base = gc_readl(priv, REG_GCRBBR);
183*f7018c21STomi Valkeinen 
184*f7018c21STomi Valkeinen 	QDUMP("DUMP");
185*f7018c21STomi Valkeinen 
186*f7018c21STomi Valkeinen 	printk(KERN_DEBUG "== PXA3XX-GCU DUMP ==\n"
187*f7018c21STomi Valkeinen 		"%s, STATUS 0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, T %5ld\n",
188*f7018c21STomi Valkeinen 		sh->hw_running ? "running" : "idle   ",
189*f7018c21STomi Valkeinen 		gc_readl(priv, REG_GCISCR),
190*f7018c21STomi Valkeinen 		gc_readl(priv, REG_GCRBBR),
191*f7018c21STomi Valkeinen 		gc_readl(priv, REG_GCRBLR),
192*f7018c21STomi Valkeinen 		(gc_readl(priv, REG_GCRBEXHR) - base) / 4,
193*f7018c21STomi Valkeinen 		(gc_readl(priv, REG_GCRBHR) - base) / 4,
194*f7018c21STomi Valkeinen 		(gc_readl(priv, REG_GCRBTR) - base) / 4);
195*f7018c21STomi Valkeinen }
196*f7018c21STomi Valkeinen 
197*f7018c21STomi Valkeinen static void
198*f7018c21STomi Valkeinen flush_running(struct pxa3xx_gcu_priv *priv)
199*f7018c21STomi Valkeinen {
200*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch *running = priv->running;
201*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch *next;
202*f7018c21STomi Valkeinen 
203*f7018c21STomi Valkeinen 	while (running) {
204*f7018c21STomi Valkeinen 		next = running->next;
205*f7018c21STomi Valkeinen 		running->next = priv->free;
206*f7018c21STomi Valkeinen 		priv->free = running;
207*f7018c21STomi Valkeinen 		running = next;
208*f7018c21STomi Valkeinen 	}
209*f7018c21STomi Valkeinen 
210*f7018c21STomi Valkeinen 	priv->running = NULL;
211*f7018c21STomi Valkeinen }
212*f7018c21STomi Valkeinen 
213*f7018c21STomi Valkeinen static void
214*f7018c21STomi Valkeinen run_ready(struct pxa3xx_gcu_priv *priv)
215*f7018c21STomi Valkeinen {
216*f7018c21STomi Valkeinen 	unsigned int num = 0;
217*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_shared *shared = priv->shared;
218*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch	*ready = priv->ready;
219*f7018c21STomi Valkeinen 
220*f7018c21STomi Valkeinen 	QDUMP("Start");
221*f7018c21STomi Valkeinen 
222*f7018c21STomi Valkeinen 	BUG_ON(!ready);
223*f7018c21STomi Valkeinen 
224*f7018c21STomi Valkeinen 	shared->buffer[num++] = 0x05000000;
225*f7018c21STomi Valkeinen 
226*f7018c21STomi Valkeinen 	while (ready) {
227*f7018c21STomi Valkeinen 		shared->buffer[num++] = 0x00000001;
228*f7018c21STomi Valkeinen 		shared->buffer[num++] = ready->phys;
229*f7018c21STomi Valkeinen 		ready = ready->next;
230*f7018c21STomi Valkeinen 	}
231*f7018c21STomi Valkeinen 
232*f7018c21STomi Valkeinen 	shared->buffer[num++] = 0x05000000;
233*f7018c21STomi Valkeinen 	priv->running = priv->ready;
234*f7018c21STomi Valkeinen 	priv->ready = priv->ready_last = NULL;
235*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCRBLR, 0);
236*f7018c21STomi Valkeinen 	shared->hw_running = 1;
237*f7018c21STomi Valkeinen 
238*f7018c21STomi Valkeinen 	/* ring base address */
239*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCRBBR, shared->buffer_phys);
240*f7018c21STomi Valkeinen 
241*f7018c21STomi Valkeinen 	/* ring tail address */
242*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCRBTR, shared->buffer_phys + num * 4);
243*f7018c21STomi Valkeinen 
244*f7018c21STomi Valkeinen 	/* ring length */
245*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCRBLR, ((num + 63) & ~63) * 4);
246*f7018c21STomi Valkeinen }
247*f7018c21STomi Valkeinen 
248*f7018c21STomi Valkeinen static irqreturn_t
249*f7018c21STomi Valkeinen pxa3xx_gcu_handle_irq(int irq, void *ctx)
250*f7018c21STomi Valkeinen {
251*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_priv *priv = ctx;
252*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_shared *shared = priv->shared;
253*f7018c21STomi Valkeinen 	u32 status = gc_readl(priv, REG_GCISCR) & IE_ALL;
254*f7018c21STomi Valkeinen 
255*f7018c21STomi Valkeinen 	QDUMP("-Interrupt");
256*f7018c21STomi Valkeinen 
257*f7018c21STomi Valkeinen 	if (!status)
258*f7018c21STomi Valkeinen 		return IRQ_NONE;
259*f7018c21STomi Valkeinen 
260*f7018c21STomi Valkeinen 	spin_lock(&priv->spinlock);
261*f7018c21STomi Valkeinen 	shared->num_interrupts++;
262*f7018c21STomi Valkeinen 
263*f7018c21STomi Valkeinen 	if (status & IE_EEOB) {
264*f7018c21STomi Valkeinen 		QDUMP(" [EEOB]");
265*f7018c21STomi Valkeinen 
266*f7018c21STomi Valkeinen 		flush_running(priv);
267*f7018c21STomi Valkeinen 		wake_up_all(&priv->wait_free);
268*f7018c21STomi Valkeinen 
269*f7018c21STomi Valkeinen 		if (priv->ready) {
270*f7018c21STomi Valkeinen 			run_ready(priv);
271*f7018c21STomi Valkeinen 		} else {
272*f7018c21STomi Valkeinen 			/* There is no more data prepared by the userspace.
273*f7018c21STomi Valkeinen 			 * Set hw_running = 0 and wait for the next userspace
274*f7018c21STomi Valkeinen 			 * kick-off */
275*f7018c21STomi Valkeinen 			shared->num_idle++;
276*f7018c21STomi Valkeinen 			shared->hw_running = 0;
277*f7018c21STomi Valkeinen 
278*f7018c21STomi Valkeinen 			QDUMP(" '-> Idle.");
279*f7018c21STomi Valkeinen 
280*f7018c21STomi Valkeinen 			/* set ring buffer length to zero */
281*f7018c21STomi Valkeinen 			gc_writel(priv, REG_GCRBLR, 0);
282*f7018c21STomi Valkeinen 
283*f7018c21STomi Valkeinen 			wake_up_all(&priv->wait_idle);
284*f7018c21STomi Valkeinen 		}
285*f7018c21STomi Valkeinen 
286*f7018c21STomi Valkeinen 		shared->num_done++;
287*f7018c21STomi Valkeinen 	} else {
288*f7018c21STomi Valkeinen 		QERROR(" [???]");
289*f7018c21STomi Valkeinen 		dump_whole_state(priv);
290*f7018c21STomi Valkeinen 	}
291*f7018c21STomi Valkeinen 
292*f7018c21STomi Valkeinen 	/* Clear the interrupt */
293*f7018c21STomi Valkeinen 	gc_writel(priv, REG_GCISCR, status);
294*f7018c21STomi Valkeinen 	spin_unlock(&priv->spinlock);
295*f7018c21STomi Valkeinen 
296*f7018c21STomi Valkeinen 	return IRQ_HANDLED;
297*f7018c21STomi Valkeinen }
298*f7018c21STomi Valkeinen 
299*f7018c21STomi Valkeinen static int
300*f7018c21STomi Valkeinen pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv)
301*f7018c21STomi Valkeinen {
302*f7018c21STomi Valkeinen 	int ret = 0;
303*f7018c21STomi Valkeinen 
304*f7018c21STomi Valkeinen 	QDUMP("Waiting for idle...");
305*f7018c21STomi Valkeinen 
306*f7018c21STomi Valkeinen 	/* Does not need to be atomic. There's a lock in user space,
307*f7018c21STomi Valkeinen 	 * but anyhow, this is just for statistics. */
308*f7018c21STomi Valkeinen 	priv->shared->num_wait_idle++;
309*f7018c21STomi Valkeinen 
310*f7018c21STomi Valkeinen 	while (priv->shared->hw_running) {
311*f7018c21STomi Valkeinen 		int num = priv->shared->num_interrupts;
312*f7018c21STomi Valkeinen 		u32 rbexhr = gc_readl(priv, REG_GCRBEXHR);
313*f7018c21STomi Valkeinen 
314*f7018c21STomi Valkeinen 		ret = wait_event_interruptible_timeout(priv->wait_idle,
315*f7018c21STomi Valkeinen 					!priv->shared->hw_running, HZ*4);
316*f7018c21STomi Valkeinen 
317*f7018c21STomi Valkeinen 		if (ret != 0)
318*f7018c21STomi Valkeinen 			break;
319*f7018c21STomi Valkeinen 
320*f7018c21STomi Valkeinen 		if (gc_readl(priv, REG_GCRBEXHR) == rbexhr &&
321*f7018c21STomi Valkeinen 		    priv->shared->num_interrupts == num) {
322*f7018c21STomi Valkeinen 			QERROR("TIMEOUT");
323*f7018c21STomi Valkeinen 			ret = -ETIMEDOUT;
324*f7018c21STomi Valkeinen 			break;
325*f7018c21STomi Valkeinen 		}
326*f7018c21STomi Valkeinen 	}
327*f7018c21STomi Valkeinen 
328*f7018c21STomi Valkeinen 	QDUMP("done");
329*f7018c21STomi Valkeinen 
330*f7018c21STomi Valkeinen 	return ret;
331*f7018c21STomi Valkeinen }
332*f7018c21STomi Valkeinen 
333*f7018c21STomi Valkeinen static int
334*f7018c21STomi Valkeinen pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv *priv)
335*f7018c21STomi Valkeinen {
336*f7018c21STomi Valkeinen 	int ret = 0;
337*f7018c21STomi Valkeinen 
338*f7018c21STomi Valkeinen 	QDUMP("Waiting for free...");
339*f7018c21STomi Valkeinen 
340*f7018c21STomi Valkeinen 	/* Does not need to be atomic. There's a lock in user space,
341*f7018c21STomi Valkeinen 	 * but anyhow, this is just for statistics. */
342*f7018c21STomi Valkeinen 	priv->shared->num_wait_free++;
343*f7018c21STomi Valkeinen 
344*f7018c21STomi Valkeinen 	while (!priv->free) {
345*f7018c21STomi Valkeinen 		u32 rbexhr = gc_readl(priv, REG_GCRBEXHR);
346*f7018c21STomi Valkeinen 
347*f7018c21STomi Valkeinen 		ret = wait_event_interruptible_timeout(priv->wait_free,
348*f7018c21STomi Valkeinen 						       priv->free, HZ*4);
349*f7018c21STomi Valkeinen 
350*f7018c21STomi Valkeinen 		if (ret < 0)
351*f7018c21STomi Valkeinen 			break;
352*f7018c21STomi Valkeinen 
353*f7018c21STomi Valkeinen 		if (ret > 0)
354*f7018c21STomi Valkeinen 			continue;
355*f7018c21STomi Valkeinen 
356*f7018c21STomi Valkeinen 		if (gc_readl(priv, REG_GCRBEXHR) == rbexhr) {
357*f7018c21STomi Valkeinen 			QERROR("TIMEOUT");
358*f7018c21STomi Valkeinen 			ret = -ETIMEDOUT;
359*f7018c21STomi Valkeinen 			break;
360*f7018c21STomi Valkeinen 		}
361*f7018c21STomi Valkeinen 	}
362*f7018c21STomi Valkeinen 
363*f7018c21STomi Valkeinen 	QDUMP("done");
364*f7018c21STomi Valkeinen 
365*f7018c21STomi Valkeinen 	return ret;
366*f7018c21STomi Valkeinen }
367*f7018c21STomi Valkeinen 
368*f7018c21STomi Valkeinen /* Misc device layer */
369*f7018c21STomi Valkeinen 
370*f7018c21STomi Valkeinen static inline struct pxa3xx_gcu_priv *to_pxa3xx_gcu_priv(struct file *file)
371*f7018c21STomi Valkeinen {
372*f7018c21STomi Valkeinen 	struct miscdevice *dev = file->private_data;
373*f7018c21STomi Valkeinen 	return container_of(dev, struct pxa3xx_gcu_priv, misc_dev);
374*f7018c21STomi Valkeinen }
375*f7018c21STomi Valkeinen 
376*f7018c21STomi Valkeinen /*
377*f7018c21STomi Valkeinen  * provide an empty .open callback, so the core sets file->private_data
378*f7018c21STomi Valkeinen  * for us.
379*f7018c21STomi Valkeinen  */
380*f7018c21STomi Valkeinen static int pxa3xx_gcu_open(struct inode *inode, struct file *file)
381*f7018c21STomi Valkeinen {
382*f7018c21STomi Valkeinen 	return 0;
383*f7018c21STomi Valkeinen }
384*f7018c21STomi Valkeinen 
385*f7018c21STomi Valkeinen static ssize_t
386*f7018c21STomi Valkeinen pxa3xx_gcu_write(struct file *file, const char *buff,
387*f7018c21STomi Valkeinen 		 size_t count, loff_t *offp)
388*f7018c21STomi Valkeinen {
389*f7018c21STomi Valkeinen 	int ret;
390*f7018c21STomi Valkeinen 	unsigned long flags;
391*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch	*buffer;
392*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
393*f7018c21STomi Valkeinen 
394*f7018c21STomi Valkeinen 	int words = count / 4;
395*f7018c21STomi Valkeinen 
396*f7018c21STomi Valkeinen 	/* Does not need to be atomic. There's a lock in user space,
397*f7018c21STomi Valkeinen 	 * but anyhow, this is just for statistics. */
398*f7018c21STomi Valkeinen 	priv->shared->num_writes++;
399*f7018c21STomi Valkeinen 	priv->shared->num_words += words;
400*f7018c21STomi Valkeinen 
401*f7018c21STomi Valkeinen 	/* Last word reserved for batch buffer end command */
402*f7018c21STomi Valkeinen 	if (words >= PXA3XX_GCU_BATCH_WORDS)
403*f7018c21STomi Valkeinen 		return -E2BIG;
404*f7018c21STomi Valkeinen 
405*f7018c21STomi Valkeinen 	/* Wait for a free buffer */
406*f7018c21STomi Valkeinen 	if (!priv->free) {
407*f7018c21STomi Valkeinen 		ret = pxa3xx_gcu_wait_free(priv);
408*f7018c21STomi Valkeinen 		if (ret < 0)
409*f7018c21STomi Valkeinen 			return ret;
410*f7018c21STomi Valkeinen 	}
411*f7018c21STomi Valkeinen 
412*f7018c21STomi Valkeinen 	/*
413*f7018c21STomi Valkeinen 	 * Get buffer from free list
414*f7018c21STomi Valkeinen 	 */
415*f7018c21STomi Valkeinen 	spin_lock_irqsave(&priv->spinlock, flags);
416*f7018c21STomi Valkeinen 	buffer = priv->free;
417*f7018c21STomi Valkeinen 	priv->free = buffer->next;
418*f7018c21STomi Valkeinen 	spin_unlock_irqrestore(&priv->spinlock, flags);
419*f7018c21STomi Valkeinen 
420*f7018c21STomi Valkeinen 
421*f7018c21STomi Valkeinen 	/* Copy data from user into buffer */
422*f7018c21STomi Valkeinen 	ret = copy_from_user(buffer->ptr, buff, words * 4);
423*f7018c21STomi Valkeinen 	if (ret) {
424*f7018c21STomi Valkeinen 		spin_lock_irqsave(&priv->spinlock, flags);
425*f7018c21STomi Valkeinen 		buffer->next = priv->free;
426*f7018c21STomi Valkeinen 		priv->free = buffer;
427*f7018c21STomi Valkeinen 		spin_unlock_irqrestore(&priv->spinlock, flags);
428*f7018c21STomi Valkeinen 		return -EFAULT;
429*f7018c21STomi Valkeinen 	}
430*f7018c21STomi Valkeinen 
431*f7018c21STomi Valkeinen 	buffer->length = words;
432*f7018c21STomi Valkeinen 
433*f7018c21STomi Valkeinen 	/* Append batch buffer end command */
434*f7018c21STomi Valkeinen 	buffer->ptr[words] = 0x01000000;
435*f7018c21STomi Valkeinen 
436*f7018c21STomi Valkeinen 	/*
437*f7018c21STomi Valkeinen 	 * Add buffer to ready list
438*f7018c21STomi Valkeinen 	 */
439*f7018c21STomi Valkeinen 	spin_lock_irqsave(&priv->spinlock, flags);
440*f7018c21STomi Valkeinen 
441*f7018c21STomi Valkeinen 	buffer->next = NULL;
442*f7018c21STomi Valkeinen 
443*f7018c21STomi Valkeinen 	if (priv->ready) {
444*f7018c21STomi Valkeinen 		BUG_ON(priv->ready_last == NULL);
445*f7018c21STomi Valkeinen 
446*f7018c21STomi Valkeinen 		priv->ready_last->next = buffer;
447*f7018c21STomi Valkeinen 	} else
448*f7018c21STomi Valkeinen 		priv->ready = buffer;
449*f7018c21STomi Valkeinen 
450*f7018c21STomi Valkeinen 	priv->ready_last = buffer;
451*f7018c21STomi Valkeinen 
452*f7018c21STomi Valkeinen 	if (!priv->shared->hw_running)
453*f7018c21STomi Valkeinen 		run_ready(priv);
454*f7018c21STomi Valkeinen 
455*f7018c21STomi Valkeinen 	spin_unlock_irqrestore(&priv->spinlock, flags);
456*f7018c21STomi Valkeinen 
457*f7018c21STomi Valkeinen 	return words * 4;
458*f7018c21STomi Valkeinen }
459*f7018c21STomi Valkeinen 
460*f7018c21STomi Valkeinen 
461*f7018c21STomi Valkeinen static long
462*f7018c21STomi Valkeinen pxa3xx_gcu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
463*f7018c21STomi Valkeinen {
464*f7018c21STomi Valkeinen 	unsigned long flags;
465*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
466*f7018c21STomi Valkeinen 
467*f7018c21STomi Valkeinen 	switch (cmd) {
468*f7018c21STomi Valkeinen 	case PXA3XX_GCU_IOCTL_RESET:
469*f7018c21STomi Valkeinen 		spin_lock_irqsave(&priv->spinlock, flags);
470*f7018c21STomi Valkeinen 		pxa3xx_gcu_reset(priv);
471*f7018c21STomi Valkeinen 		spin_unlock_irqrestore(&priv->spinlock, flags);
472*f7018c21STomi Valkeinen 		return 0;
473*f7018c21STomi Valkeinen 
474*f7018c21STomi Valkeinen 	case PXA3XX_GCU_IOCTL_WAIT_IDLE:
475*f7018c21STomi Valkeinen 		return pxa3xx_gcu_wait_idle(priv);
476*f7018c21STomi Valkeinen 	}
477*f7018c21STomi Valkeinen 
478*f7018c21STomi Valkeinen 	return -ENOSYS;
479*f7018c21STomi Valkeinen }
480*f7018c21STomi Valkeinen 
481*f7018c21STomi Valkeinen static int
482*f7018c21STomi Valkeinen pxa3xx_gcu_mmap(struct file *file, struct vm_area_struct *vma)
483*f7018c21STomi Valkeinen {
484*f7018c21STomi Valkeinen 	unsigned int size = vma->vm_end - vma->vm_start;
485*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
486*f7018c21STomi Valkeinen 
487*f7018c21STomi Valkeinen 	switch (vma->vm_pgoff) {
488*f7018c21STomi Valkeinen 	case 0:
489*f7018c21STomi Valkeinen 		/* hand out the shared data area */
490*f7018c21STomi Valkeinen 		if (size != SHARED_SIZE)
491*f7018c21STomi Valkeinen 			return -EINVAL;
492*f7018c21STomi Valkeinen 
493*f7018c21STomi Valkeinen 		return dma_mmap_coherent(NULL, vma,
494*f7018c21STomi Valkeinen 			priv->shared, priv->shared_phys, size);
495*f7018c21STomi Valkeinen 
496*f7018c21STomi Valkeinen 	case SHARED_SIZE >> PAGE_SHIFT:
497*f7018c21STomi Valkeinen 		/* hand out the MMIO base for direct register access
498*f7018c21STomi Valkeinen 		 * from userspace */
499*f7018c21STomi Valkeinen 		if (size != resource_size(priv->resource_mem))
500*f7018c21STomi Valkeinen 			return -EINVAL;
501*f7018c21STomi Valkeinen 
502*f7018c21STomi Valkeinen 		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
503*f7018c21STomi Valkeinen 
504*f7018c21STomi Valkeinen 		return io_remap_pfn_range(vma, vma->vm_start,
505*f7018c21STomi Valkeinen 				priv->resource_mem->start >> PAGE_SHIFT,
506*f7018c21STomi Valkeinen 				size, vma->vm_page_prot);
507*f7018c21STomi Valkeinen 	}
508*f7018c21STomi Valkeinen 
509*f7018c21STomi Valkeinen 	return -EINVAL;
510*f7018c21STomi Valkeinen }
511*f7018c21STomi Valkeinen 
512*f7018c21STomi Valkeinen 
513*f7018c21STomi Valkeinen #ifdef PXA3XX_GCU_DEBUG_TIMER
514*f7018c21STomi Valkeinen static struct timer_list pxa3xx_gcu_debug_timer;
515*f7018c21STomi Valkeinen 
516*f7018c21STomi Valkeinen static void pxa3xx_gcu_debug_timedout(unsigned long ptr)
517*f7018c21STomi Valkeinen {
518*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr;
519*f7018c21STomi Valkeinen 
520*f7018c21STomi Valkeinen 	QERROR("Timer DUMP");
521*f7018c21STomi Valkeinen 
522*f7018c21STomi Valkeinen 	/* init the timer structure */
523*f7018c21STomi Valkeinen 	init_timer(&pxa3xx_gcu_debug_timer);
524*f7018c21STomi Valkeinen 	pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout;
525*f7018c21STomi Valkeinen 	pxa3xx_gcu_debug_timer.data = ptr;
526*f7018c21STomi Valkeinen 	pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */
527*f7018c21STomi Valkeinen 
528*f7018c21STomi Valkeinen 	add_timer(&pxa3xx_gcu_debug_timer);
529*f7018c21STomi Valkeinen }
530*f7018c21STomi Valkeinen 
531*f7018c21STomi Valkeinen static void pxa3xx_gcu_init_debug_timer(void)
532*f7018c21STomi Valkeinen {
533*f7018c21STomi Valkeinen 	pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer);
534*f7018c21STomi Valkeinen }
535*f7018c21STomi Valkeinen #else
536*f7018c21STomi Valkeinen static inline void pxa3xx_gcu_init_debug_timer(void) {}
537*f7018c21STomi Valkeinen #endif
538*f7018c21STomi Valkeinen 
539*f7018c21STomi Valkeinen static int
540*f7018c21STomi Valkeinen pxa3xx_gcu_add_buffer(struct device *dev,
541*f7018c21STomi Valkeinen 		      struct pxa3xx_gcu_priv *priv)
542*f7018c21STomi Valkeinen {
543*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch *buffer;
544*f7018c21STomi Valkeinen 
545*f7018c21STomi Valkeinen 	buffer = kzalloc(sizeof(struct pxa3xx_gcu_batch), GFP_KERNEL);
546*f7018c21STomi Valkeinen 	if (!buffer)
547*f7018c21STomi Valkeinen 		return -ENOMEM;
548*f7018c21STomi Valkeinen 
549*f7018c21STomi Valkeinen 	buffer->ptr = dma_alloc_coherent(dev, PXA3XX_GCU_BATCH_WORDS * 4,
550*f7018c21STomi Valkeinen 					 &buffer->phys, GFP_KERNEL);
551*f7018c21STomi Valkeinen 	if (!buffer->ptr) {
552*f7018c21STomi Valkeinen 		kfree(buffer);
553*f7018c21STomi Valkeinen 		return -ENOMEM;
554*f7018c21STomi Valkeinen 	}
555*f7018c21STomi Valkeinen 
556*f7018c21STomi Valkeinen 	buffer->next = priv->free;
557*f7018c21STomi Valkeinen 	priv->free = buffer;
558*f7018c21STomi Valkeinen 
559*f7018c21STomi Valkeinen 	return 0;
560*f7018c21STomi Valkeinen }
561*f7018c21STomi Valkeinen 
562*f7018c21STomi Valkeinen static void
563*f7018c21STomi Valkeinen pxa3xx_gcu_free_buffers(struct device *dev,
564*f7018c21STomi Valkeinen 			struct pxa3xx_gcu_priv *priv)
565*f7018c21STomi Valkeinen {
566*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_batch *next, *buffer = priv->free;
567*f7018c21STomi Valkeinen 
568*f7018c21STomi Valkeinen 	while (buffer) {
569*f7018c21STomi Valkeinen 		next = buffer->next;
570*f7018c21STomi Valkeinen 
571*f7018c21STomi Valkeinen 		dma_free_coherent(dev, PXA3XX_GCU_BATCH_WORDS * 4,
572*f7018c21STomi Valkeinen 				  buffer->ptr, buffer->phys);
573*f7018c21STomi Valkeinen 
574*f7018c21STomi Valkeinen 		kfree(buffer);
575*f7018c21STomi Valkeinen 		buffer = next;
576*f7018c21STomi Valkeinen 	}
577*f7018c21STomi Valkeinen 
578*f7018c21STomi Valkeinen 	priv->free = NULL;
579*f7018c21STomi Valkeinen }
580*f7018c21STomi Valkeinen 
581*f7018c21STomi Valkeinen static const struct file_operations pxa3xx_gcu_miscdev_fops = {
582*f7018c21STomi Valkeinen 	.owner =		THIS_MODULE,
583*f7018c21STomi Valkeinen 	.open =			pxa3xx_gcu_open,
584*f7018c21STomi Valkeinen 	.write =		pxa3xx_gcu_write,
585*f7018c21STomi Valkeinen 	.unlocked_ioctl =	pxa3xx_gcu_ioctl,
586*f7018c21STomi Valkeinen 	.mmap =			pxa3xx_gcu_mmap,
587*f7018c21STomi Valkeinen };
588*f7018c21STomi Valkeinen 
589*f7018c21STomi Valkeinen static int pxa3xx_gcu_probe(struct platform_device *pdev)
590*f7018c21STomi Valkeinen {
591*f7018c21STomi Valkeinen 	int i, ret, irq;
592*f7018c21STomi Valkeinen 	struct resource *r;
593*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_priv *priv;
594*f7018c21STomi Valkeinen 	struct device *dev = &pdev->dev;
595*f7018c21STomi Valkeinen 
596*f7018c21STomi Valkeinen 	priv = devm_kzalloc(dev, sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL);
597*f7018c21STomi Valkeinen 	if (!priv)
598*f7018c21STomi Valkeinen 		return -ENOMEM;
599*f7018c21STomi Valkeinen 
600*f7018c21STomi Valkeinen 	init_waitqueue_head(&priv->wait_idle);
601*f7018c21STomi Valkeinen 	init_waitqueue_head(&priv->wait_free);
602*f7018c21STomi Valkeinen 	spin_lock_init(&priv->spinlock);
603*f7018c21STomi Valkeinen 
604*f7018c21STomi Valkeinen 	/* we allocate the misc device structure as part of our own allocation,
605*f7018c21STomi Valkeinen 	 * so we can get a pointer to our priv structure later on with
606*f7018c21STomi Valkeinen 	 * container_of(). This isn't really necessary as we have a fixed minor
607*f7018c21STomi Valkeinen 	 * number anyway, but this is to avoid statics. */
608*f7018c21STomi Valkeinen 
609*f7018c21STomi Valkeinen 	priv->misc_dev.minor	= MISCDEV_MINOR,
610*f7018c21STomi Valkeinen 	priv->misc_dev.name	= DRV_NAME,
611*f7018c21STomi Valkeinen 	priv->misc_dev.fops	= &pxa3xx_gcu_miscdev_fops;
612*f7018c21STomi Valkeinen 
613*f7018c21STomi Valkeinen 	/* handle IO resources */
614*f7018c21STomi Valkeinen 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
615*f7018c21STomi Valkeinen 	priv->mmio_base = devm_request_and_ioremap(dev, r);
616*f7018c21STomi Valkeinen 	if (IS_ERR(priv->mmio_base)) {
617*f7018c21STomi Valkeinen 		dev_err(dev, "failed to map I/O memory\n");
618*f7018c21STomi Valkeinen 		return PTR_ERR(priv->mmio_base);
619*f7018c21STomi Valkeinen 	}
620*f7018c21STomi Valkeinen 
621*f7018c21STomi Valkeinen 	/* enable the clock */
622*f7018c21STomi Valkeinen 	priv->clk = devm_clk_get(dev, NULL);
623*f7018c21STomi Valkeinen 	if (IS_ERR(priv->clk)) {
624*f7018c21STomi Valkeinen 		dev_err(dev, "failed to get clock\n");
625*f7018c21STomi Valkeinen 		return PTR_ERR(priv->clk);
626*f7018c21STomi Valkeinen 	}
627*f7018c21STomi Valkeinen 
628*f7018c21STomi Valkeinen 	/* request the IRQ */
629*f7018c21STomi Valkeinen 	irq = platform_get_irq(pdev, 0);
630*f7018c21STomi Valkeinen 	if (irq < 0) {
631*f7018c21STomi Valkeinen 		dev_err(dev, "no IRQ defined\n");
632*f7018c21STomi Valkeinen 		return -ENODEV;
633*f7018c21STomi Valkeinen 	}
634*f7018c21STomi Valkeinen 
635*f7018c21STomi Valkeinen 	ret = devm_request_irq(dev, irq, pxa3xx_gcu_handle_irq,
636*f7018c21STomi Valkeinen 			       0, DRV_NAME, priv);
637*f7018c21STomi Valkeinen 	if (ret < 0) {
638*f7018c21STomi Valkeinen 		dev_err(dev, "request_irq failed\n");
639*f7018c21STomi Valkeinen 		return ret;
640*f7018c21STomi Valkeinen 	}
641*f7018c21STomi Valkeinen 
642*f7018c21STomi Valkeinen 	/* allocate dma memory */
643*f7018c21STomi Valkeinen 	priv->shared = dma_alloc_coherent(dev, SHARED_SIZE,
644*f7018c21STomi Valkeinen 					  &priv->shared_phys, GFP_KERNEL);
645*f7018c21STomi Valkeinen 	if (!priv->shared) {
646*f7018c21STomi Valkeinen 		dev_err(dev, "failed to allocate DMA memory\n");
647*f7018c21STomi Valkeinen 		return -ENOMEM;
648*f7018c21STomi Valkeinen 	}
649*f7018c21STomi Valkeinen 
650*f7018c21STomi Valkeinen 	/* register misc device */
651*f7018c21STomi Valkeinen 	ret = misc_register(&priv->misc_dev);
652*f7018c21STomi Valkeinen 	if (ret < 0) {
653*f7018c21STomi Valkeinen 		dev_err(dev, "misc_register() for minor %d failed\n",
654*f7018c21STomi Valkeinen 			MISCDEV_MINOR);
655*f7018c21STomi Valkeinen 		goto err_free_dma;
656*f7018c21STomi Valkeinen 	}
657*f7018c21STomi Valkeinen 
658*f7018c21STomi Valkeinen 	ret = clk_enable(priv->clk);
659*f7018c21STomi Valkeinen 	if (ret < 0) {
660*f7018c21STomi Valkeinen 		dev_err(dev, "failed to enable clock\n");
661*f7018c21STomi Valkeinen 		goto err_misc_deregister;
662*f7018c21STomi Valkeinen 	}
663*f7018c21STomi Valkeinen 
664*f7018c21STomi Valkeinen 	for (i = 0; i < 8; i++) {
665*f7018c21STomi Valkeinen 		ret = pxa3xx_gcu_add_buffer(dev, priv);
666*f7018c21STomi Valkeinen 		if (ret) {
667*f7018c21STomi Valkeinen 			dev_err(dev, "failed to allocate DMA memory\n");
668*f7018c21STomi Valkeinen 			goto err_disable_clk;
669*f7018c21STomi Valkeinen 		}
670*f7018c21STomi Valkeinen 	}
671*f7018c21STomi Valkeinen 
672*f7018c21STomi Valkeinen 	platform_set_drvdata(pdev, priv);
673*f7018c21STomi Valkeinen 	priv->resource_mem = r;
674*f7018c21STomi Valkeinen 	pxa3xx_gcu_reset(priv);
675*f7018c21STomi Valkeinen 	pxa3xx_gcu_init_debug_timer();
676*f7018c21STomi Valkeinen 
677*f7018c21STomi Valkeinen 	dev_info(dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n",
678*f7018c21STomi Valkeinen 			(void *) r->start, (void *) priv->shared_phys,
679*f7018c21STomi Valkeinen 			SHARED_SIZE, irq);
680*f7018c21STomi Valkeinen 	return 0;
681*f7018c21STomi Valkeinen 
682*f7018c21STomi Valkeinen err_free_dma:
683*f7018c21STomi Valkeinen 	dma_free_coherent(dev, SHARED_SIZE,
684*f7018c21STomi Valkeinen 			priv->shared, priv->shared_phys);
685*f7018c21STomi Valkeinen 
686*f7018c21STomi Valkeinen err_misc_deregister:
687*f7018c21STomi Valkeinen 	misc_deregister(&priv->misc_dev);
688*f7018c21STomi Valkeinen 
689*f7018c21STomi Valkeinen err_disable_clk:
690*f7018c21STomi Valkeinen 	clk_disable(priv->clk);
691*f7018c21STomi Valkeinen 
692*f7018c21STomi Valkeinen 	return ret;
693*f7018c21STomi Valkeinen }
694*f7018c21STomi Valkeinen 
695*f7018c21STomi Valkeinen static int pxa3xx_gcu_remove(struct platform_device *pdev)
696*f7018c21STomi Valkeinen {
697*f7018c21STomi Valkeinen 	struct pxa3xx_gcu_priv *priv = platform_get_drvdata(pdev);
698*f7018c21STomi Valkeinen 	struct device *dev = &pdev->dev;
699*f7018c21STomi Valkeinen 
700*f7018c21STomi Valkeinen 	pxa3xx_gcu_wait_idle(priv);
701*f7018c21STomi Valkeinen 	misc_deregister(&priv->misc_dev);
702*f7018c21STomi Valkeinen 	dma_free_coherent(dev, SHARED_SIZE, priv->shared, priv->shared_phys);
703*f7018c21STomi Valkeinen 	pxa3xx_gcu_free_buffers(dev, priv);
704*f7018c21STomi Valkeinen 
705*f7018c21STomi Valkeinen 	return 0;
706*f7018c21STomi Valkeinen }
707*f7018c21STomi Valkeinen 
708*f7018c21STomi Valkeinen static struct platform_driver pxa3xx_gcu_driver = {
709*f7018c21STomi Valkeinen 	.probe	  = pxa3xx_gcu_probe,
710*f7018c21STomi Valkeinen 	.remove	 = pxa3xx_gcu_remove,
711*f7018c21STomi Valkeinen 	.driver	 = {
712*f7018c21STomi Valkeinen 		.owner  = THIS_MODULE,
713*f7018c21STomi Valkeinen 		.name   = DRV_NAME,
714*f7018c21STomi Valkeinen 	},
715*f7018c21STomi Valkeinen };
716*f7018c21STomi Valkeinen 
717*f7018c21STomi Valkeinen module_platform_driver(pxa3xx_gcu_driver);
718*f7018c21STomi Valkeinen 
719*f7018c21STomi Valkeinen MODULE_DESCRIPTION("PXA3xx graphics controller unit driver");
720*f7018c21STomi Valkeinen MODULE_LICENSE("GPL");
721*f7018c21STomi Valkeinen MODULE_ALIAS_MISCDEV(MISCDEV_MINOR);
722*f7018c21STomi Valkeinen MODULE_AUTHOR("Janine Kropp <nin@directfb.org>, "
723*f7018c21STomi Valkeinen 		"Denis Oliver Kropp <dok@directfb.org>, "
724*f7018c21STomi Valkeinen 		"Daniel Mack <daniel@caiaq.de>");
725