xref: /linux/drivers/video/fbdev/pxa3xx-gcu.c (revision c771600c6af14749609b49565ffb4cac2959710d)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2364dbdf3SDaniel Mack /*
30b7f1cc7Saxel lin  *  pxa3xx-gcu.c - Linux kernel module for PXA3xx graphics controllers
4364dbdf3SDaniel Mack  *
5364dbdf3SDaniel Mack  *  This driver needs a DirectFB counterpart in user space, communication
6364dbdf3SDaniel Mack  *  is handled via mmap()ed memory areas and an ioctl.
7364dbdf3SDaniel Mack  *
8364dbdf3SDaniel Mack  *  Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
9364dbdf3SDaniel Mack  *  Copyright (c) 2009 Janine Kropp <nin@directfb.org>
10364dbdf3SDaniel Mack  *  Copyright (c) 2009 Denis Oliver Kropp <dok@directfb.org>
11364dbdf3SDaniel Mack  */
12364dbdf3SDaniel Mack 
13364dbdf3SDaniel Mack /*
14364dbdf3SDaniel Mack  * WARNING: This controller is attached to System Bus 2 of the PXA which
1525985edcSLucas De Marchi  * needs its arbiter to be enabled explicitly (CKENB & 1<<9).
16364dbdf3SDaniel Mack  * There is currently no way to do this from Linux, so you need to teach
17364dbdf3SDaniel Mack  * your bootloader for now.
18364dbdf3SDaniel Mack  */
19364dbdf3SDaniel Mack 
20364dbdf3SDaniel Mack #include <linux/module.h>
21364dbdf3SDaniel Mack #include <linux/platform_device.h>
22364dbdf3SDaniel Mack #include <linux/dma-mapping.h>
23364dbdf3SDaniel Mack #include <linux/miscdevice.h>
24364dbdf3SDaniel Mack #include <linux/interrupt.h>
25364dbdf3SDaniel Mack #include <linux/spinlock.h>
26364dbdf3SDaniel Mack #include <linux/uaccess.h>
27364dbdf3SDaniel Mack #include <linux/ioctl.h>
28364dbdf3SDaniel Mack #include <linux/delay.h>
29364dbdf3SDaniel Mack #include <linux/sched.h>
30364dbdf3SDaniel Mack #include <linux/slab.h>
31364dbdf3SDaniel Mack #include <linux/clk.h>
32364dbdf3SDaniel Mack #include <linux/fs.h>
33364dbdf3SDaniel Mack #include <linux/io.h>
34aa45ee8eSDaniel Mack #include <linux/of.h>
35364dbdf3SDaniel Mack 
36364dbdf3SDaniel Mack #include "pxa3xx-gcu.h"
37364dbdf3SDaniel Mack 
38364dbdf3SDaniel Mack #define DRV_NAME	"pxa3xx-gcu"
39364dbdf3SDaniel Mack 
40364dbdf3SDaniel Mack #define REG_GCCR	0x00
41364dbdf3SDaniel Mack #define GCCR_SYNC_CLR	(1 << 9)
42364dbdf3SDaniel Mack #define GCCR_BP_RST	(1 << 8)
43364dbdf3SDaniel Mack #define GCCR_ABORT	(1 << 6)
44364dbdf3SDaniel Mack #define GCCR_STOP	(1 << 4)
45364dbdf3SDaniel Mack 
46364dbdf3SDaniel Mack #define REG_GCISCR	0x04
47364dbdf3SDaniel Mack #define REG_GCIECR	0x08
48364dbdf3SDaniel Mack #define REG_GCRBBR	0x20
49364dbdf3SDaniel Mack #define REG_GCRBLR	0x24
50364dbdf3SDaniel Mack #define REG_GCRBHR	0x28
51364dbdf3SDaniel Mack #define REG_GCRBTR	0x2C
52364dbdf3SDaniel Mack #define REG_GCRBEXHR	0x30
53364dbdf3SDaniel Mack 
54364dbdf3SDaniel Mack #define IE_EOB		(1 << 0)
55364dbdf3SDaniel Mack #define IE_EEOB		(1 << 5)
56364dbdf3SDaniel Mack #define IE_ALL		0xff
57364dbdf3SDaniel Mack 
58364dbdf3SDaniel Mack #define SHARED_SIZE	PAGE_ALIGN(sizeof(struct pxa3xx_gcu_shared))
59364dbdf3SDaniel Mack 
60364dbdf3SDaniel Mack /* #define PXA3XX_GCU_DEBUG */
61364dbdf3SDaniel Mack /* #define PXA3XX_GCU_DEBUG_TIMER */
62364dbdf3SDaniel Mack 
63364dbdf3SDaniel Mack #ifdef PXA3XX_GCU_DEBUG
64364dbdf3SDaniel Mack #define QDUMP(msg)					\
65364dbdf3SDaniel Mack 	do {						\
66364dbdf3SDaniel Mack 		QPRINT(priv, KERN_DEBUG, msg);		\
67364dbdf3SDaniel Mack 	} while (0)
68364dbdf3SDaniel Mack #else
69364dbdf3SDaniel Mack #define QDUMP(msg)	do {} while (0)
70364dbdf3SDaniel Mack #endif
71364dbdf3SDaniel Mack 
72364dbdf3SDaniel Mack #define QERROR(msg)					\
73364dbdf3SDaniel Mack 	do {						\
74364dbdf3SDaniel Mack 		QPRINT(priv, KERN_ERR, msg);		\
75364dbdf3SDaniel Mack 	} while (0)
76364dbdf3SDaniel Mack 
77364dbdf3SDaniel Mack struct pxa3xx_gcu_batch {
78364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch *next;
79364dbdf3SDaniel Mack 	u32			*ptr;
80364dbdf3SDaniel Mack 	dma_addr_t		 phys;
81364dbdf3SDaniel Mack 	unsigned long		 length;
82364dbdf3SDaniel Mack };
83364dbdf3SDaniel Mack 
84364dbdf3SDaniel Mack struct pxa3xx_gcu_priv {
8502c486f4SChristoph Hellwig 	struct device		 *dev;
86364dbdf3SDaniel Mack 	void __iomem		 *mmio_base;
87364dbdf3SDaniel Mack 	struct clk		 *clk;
88364dbdf3SDaniel Mack 	struct pxa3xx_gcu_shared *shared;
89364dbdf3SDaniel Mack 	dma_addr_t		  shared_phys;
90364dbdf3SDaniel Mack 	struct resource		 *resource_mem;
91364dbdf3SDaniel Mack 	struct miscdevice	  misc_dev;
92364dbdf3SDaniel Mack 	wait_queue_head_t	  wait_idle;
93364dbdf3SDaniel Mack 	wait_queue_head_t	  wait_free;
94364dbdf3SDaniel Mack 	spinlock_t		  spinlock;
95f7a75354SArnd Bergmann 	struct timespec64	  base_time;
96364dbdf3SDaniel Mack 
97364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch *free;
98364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch *ready;
99364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch *ready_last;
100364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch *running;
101364dbdf3SDaniel Mack };
102364dbdf3SDaniel Mack 
103364dbdf3SDaniel Mack static inline unsigned long
gc_readl(struct pxa3xx_gcu_priv * priv,unsigned int off)104364dbdf3SDaniel Mack gc_readl(struct pxa3xx_gcu_priv *priv, unsigned int off)
105364dbdf3SDaniel Mack {
106364dbdf3SDaniel Mack 	return __raw_readl(priv->mmio_base + off);
107364dbdf3SDaniel Mack }
108364dbdf3SDaniel Mack 
109364dbdf3SDaniel Mack static inline void
gc_writel(struct pxa3xx_gcu_priv * priv,unsigned int off,unsigned long val)110364dbdf3SDaniel Mack gc_writel(struct pxa3xx_gcu_priv *priv, unsigned int off, unsigned long val)
111364dbdf3SDaniel Mack {
112364dbdf3SDaniel Mack 	__raw_writel(val, priv->mmio_base + off);
113364dbdf3SDaniel Mack }
114364dbdf3SDaniel Mack 
115364dbdf3SDaniel Mack #define QPRINT(priv, level, msg)					\
116364dbdf3SDaniel Mack 	do {								\
117f7a75354SArnd Bergmann 		struct timespec64 ts;					\
118364dbdf3SDaniel Mack 		struct pxa3xx_gcu_shared *shared = priv->shared;	\
119364dbdf3SDaniel Mack 		u32 base = gc_readl(priv, REG_GCRBBR);			\
120364dbdf3SDaniel Mack 									\
121f7a75354SArnd Bergmann 		ktime_get_ts64(&ts);					\
122f7a75354SArnd Bergmann 		ts = timespec64_sub(ts, priv->base_time);		\
123364dbdf3SDaniel Mack 									\
124f7a75354SArnd Bergmann 		printk(level "%lld.%03ld.%03ld - %-17s: %-21s (%s, "	\
125364dbdf3SDaniel Mack 			"STATUS "					\
126364dbdf3SDaniel Mack 			"0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, "	\
127364dbdf3SDaniel Mack 			"T %5ld)\n",					\
128f7a75354SArnd Bergmann 			(s64)(ts.tv_sec),				\
129f7a75354SArnd Bergmann 			ts.tv_nsec / NSEC_PER_MSEC,			\
130f7a75354SArnd Bergmann 			(ts.tv_nsec % NSEC_PER_MSEC) / USEC_PER_MSEC,	\
131364dbdf3SDaniel Mack 			__func__, msg,					\
132364dbdf3SDaniel Mack 			shared->hw_running ? "running" : "   idle",	\
133364dbdf3SDaniel Mack 			gc_readl(priv, REG_GCISCR),			\
134364dbdf3SDaniel Mack 			gc_readl(priv, REG_GCRBBR),			\
135364dbdf3SDaniel Mack 			gc_readl(priv, REG_GCRBLR),			\
136364dbdf3SDaniel Mack 			(gc_readl(priv, REG_GCRBEXHR) - base) / 4,	\
137364dbdf3SDaniel Mack 			(gc_readl(priv, REG_GCRBHR) - base) / 4,	\
138364dbdf3SDaniel Mack 			(gc_readl(priv, REG_GCRBTR) - base) / 4);	\
139364dbdf3SDaniel Mack 	} while (0)
140364dbdf3SDaniel Mack 
141364dbdf3SDaniel Mack static void
pxa3xx_gcu_reset(struct pxa3xx_gcu_priv * priv)142364dbdf3SDaniel Mack pxa3xx_gcu_reset(struct pxa3xx_gcu_priv *priv)
143364dbdf3SDaniel Mack {
144364dbdf3SDaniel Mack 	QDUMP("RESET");
145364dbdf3SDaniel Mack 
146364dbdf3SDaniel Mack 	/* disable interrupts */
147364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCIECR, 0);
148364dbdf3SDaniel Mack 
149364dbdf3SDaniel Mack 	/* reset hardware */
150364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCCR, GCCR_ABORT);
151364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCCR, 0);
152364dbdf3SDaniel Mack 
153364dbdf3SDaniel Mack 	memset(priv->shared, 0, SHARED_SIZE);
154364dbdf3SDaniel Mack 	priv->shared->buffer_phys = priv->shared_phys;
155364dbdf3SDaniel Mack 	priv->shared->magic = PXA3XX_GCU_SHARED_MAGIC;
156364dbdf3SDaniel Mack 
157f7a75354SArnd Bergmann 	ktime_get_ts64(&priv->base_time);
158364dbdf3SDaniel Mack 
159364dbdf3SDaniel Mack 	/* set up the ring buffer pointers */
160364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCRBLR, 0);
161364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCRBBR, priv->shared_phys);
162364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCRBTR, priv->shared_phys);
163364dbdf3SDaniel Mack 
164364dbdf3SDaniel Mack 	/* enable all IRQs except EOB */
165364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCIECR, IE_ALL & ~IE_EOB);
166364dbdf3SDaniel Mack }
167364dbdf3SDaniel Mack 
168364dbdf3SDaniel Mack static void
dump_whole_state(struct pxa3xx_gcu_priv * priv)169364dbdf3SDaniel Mack dump_whole_state(struct pxa3xx_gcu_priv *priv)
170364dbdf3SDaniel Mack {
171364dbdf3SDaniel Mack 	struct pxa3xx_gcu_shared *sh = priv->shared;
172364dbdf3SDaniel Mack 	u32 base = gc_readl(priv, REG_GCRBBR);
173364dbdf3SDaniel Mack 
174364dbdf3SDaniel Mack 	QDUMP("DUMP");
175364dbdf3SDaniel Mack 
176364dbdf3SDaniel Mack 	printk(KERN_DEBUG "== PXA3XX-GCU DUMP ==\n"
177364dbdf3SDaniel Mack 		"%s, STATUS 0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, T %5ld\n",
178364dbdf3SDaniel Mack 		sh->hw_running ? "running" : "idle   ",
179364dbdf3SDaniel Mack 		gc_readl(priv, REG_GCISCR),
180364dbdf3SDaniel Mack 		gc_readl(priv, REG_GCRBBR),
181364dbdf3SDaniel Mack 		gc_readl(priv, REG_GCRBLR),
182364dbdf3SDaniel Mack 		(gc_readl(priv, REG_GCRBEXHR) - base) / 4,
183364dbdf3SDaniel Mack 		(gc_readl(priv, REG_GCRBHR) - base) / 4,
184364dbdf3SDaniel Mack 		(gc_readl(priv, REG_GCRBTR) - base) / 4);
185364dbdf3SDaniel Mack }
186364dbdf3SDaniel Mack 
187364dbdf3SDaniel Mack static void
flush_running(struct pxa3xx_gcu_priv * priv)188364dbdf3SDaniel Mack flush_running(struct pxa3xx_gcu_priv *priv)
189364dbdf3SDaniel Mack {
190364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch *running = priv->running;
191364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch *next;
192364dbdf3SDaniel Mack 
193364dbdf3SDaniel Mack 	while (running) {
194364dbdf3SDaniel Mack 		next = running->next;
195364dbdf3SDaniel Mack 		running->next = priv->free;
196364dbdf3SDaniel Mack 		priv->free = running;
197364dbdf3SDaniel Mack 		running = next;
198364dbdf3SDaniel Mack 	}
199364dbdf3SDaniel Mack 
200364dbdf3SDaniel Mack 	priv->running = NULL;
201364dbdf3SDaniel Mack }
202364dbdf3SDaniel Mack 
203364dbdf3SDaniel Mack static void
run_ready(struct pxa3xx_gcu_priv * priv)204364dbdf3SDaniel Mack run_ready(struct pxa3xx_gcu_priv *priv)
205364dbdf3SDaniel Mack {
206364dbdf3SDaniel Mack 	unsigned int num = 0;
207364dbdf3SDaniel Mack 	struct pxa3xx_gcu_shared *shared = priv->shared;
208364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch	*ready = priv->ready;
209364dbdf3SDaniel Mack 
210364dbdf3SDaniel Mack 	QDUMP("Start");
211364dbdf3SDaniel Mack 
212364dbdf3SDaniel Mack 	BUG_ON(!ready);
213364dbdf3SDaniel Mack 
214364dbdf3SDaniel Mack 	shared->buffer[num++] = 0x05000000;
215364dbdf3SDaniel Mack 
216364dbdf3SDaniel Mack 	while (ready) {
217364dbdf3SDaniel Mack 		shared->buffer[num++] = 0x00000001;
218364dbdf3SDaniel Mack 		shared->buffer[num++] = ready->phys;
219364dbdf3SDaniel Mack 		ready = ready->next;
220364dbdf3SDaniel Mack 	}
221364dbdf3SDaniel Mack 
222364dbdf3SDaniel Mack 	shared->buffer[num++] = 0x05000000;
223364dbdf3SDaniel Mack 	priv->running = priv->ready;
224364dbdf3SDaniel Mack 	priv->ready = priv->ready_last = NULL;
225364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCRBLR, 0);
226364dbdf3SDaniel Mack 	shared->hw_running = 1;
227364dbdf3SDaniel Mack 
228364dbdf3SDaniel Mack 	/* ring base address */
229364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCRBBR, shared->buffer_phys);
230364dbdf3SDaniel Mack 
231364dbdf3SDaniel Mack 	/* ring tail address */
232364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCRBTR, shared->buffer_phys + num * 4);
233364dbdf3SDaniel Mack 
234364dbdf3SDaniel Mack 	/* ring length */
235364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCRBLR, ((num + 63) & ~63) * 4);
236364dbdf3SDaniel Mack }
237364dbdf3SDaniel Mack 
238364dbdf3SDaniel Mack static irqreturn_t
pxa3xx_gcu_handle_irq(int irq,void * ctx)239364dbdf3SDaniel Mack pxa3xx_gcu_handle_irq(int irq, void *ctx)
240364dbdf3SDaniel Mack {
241364dbdf3SDaniel Mack 	struct pxa3xx_gcu_priv *priv = ctx;
242364dbdf3SDaniel Mack 	struct pxa3xx_gcu_shared *shared = priv->shared;
243364dbdf3SDaniel Mack 	u32 status = gc_readl(priv, REG_GCISCR) & IE_ALL;
244364dbdf3SDaniel Mack 
245364dbdf3SDaniel Mack 	QDUMP("-Interrupt");
246364dbdf3SDaniel Mack 
247364dbdf3SDaniel Mack 	if (!status)
248364dbdf3SDaniel Mack 		return IRQ_NONE;
249364dbdf3SDaniel Mack 
250364dbdf3SDaniel Mack 	spin_lock(&priv->spinlock);
251364dbdf3SDaniel Mack 	shared->num_interrupts++;
252364dbdf3SDaniel Mack 
253364dbdf3SDaniel Mack 	if (status & IE_EEOB) {
254364dbdf3SDaniel Mack 		QDUMP(" [EEOB]");
255364dbdf3SDaniel Mack 
256364dbdf3SDaniel Mack 		flush_running(priv);
257364dbdf3SDaniel Mack 		wake_up_all(&priv->wait_free);
258364dbdf3SDaniel Mack 
259364dbdf3SDaniel Mack 		if (priv->ready) {
260364dbdf3SDaniel Mack 			run_ready(priv);
261364dbdf3SDaniel Mack 		} else {
262364dbdf3SDaniel Mack 			/* There is no more data prepared by the userspace.
263364dbdf3SDaniel Mack 			 * Set hw_running = 0 and wait for the next userspace
264364dbdf3SDaniel Mack 			 * kick-off */
265364dbdf3SDaniel Mack 			shared->num_idle++;
266364dbdf3SDaniel Mack 			shared->hw_running = 0;
267364dbdf3SDaniel Mack 
268364dbdf3SDaniel Mack 			QDUMP(" '-> Idle.");
269364dbdf3SDaniel Mack 
270364dbdf3SDaniel Mack 			/* set ring buffer length to zero */
271364dbdf3SDaniel Mack 			gc_writel(priv, REG_GCRBLR, 0);
272364dbdf3SDaniel Mack 
273364dbdf3SDaniel Mack 			wake_up_all(&priv->wait_idle);
274364dbdf3SDaniel Mack 		}
275364dbdf3SDaniel Mack 
276364dbdf3SDaniel Mack 		shared->num_done++;
277364dbdf3SDaniel Mack 	} else {
278364dbdf3SDaniel Mack 		QERROR(" [???]");
279364dbdf3SDaniel Mack 		dump_whole_state(priv);
280364dbdf3SDaniel Mack 	}
281364dbdf3SDaniel Mack 
282364dbdf3SDaniel Mack 	/* Clear the interrupt */
283364dbdf3SDaniel Mack 	gc_writel(priv, REG_GCISCR, status);
284364dbdf3SDaniel Mack 	spin_unlock(&priv->spinlock);
285364dbdf3SDaniel Mack 
286364dbdf3SDaniel Mack 	return IRQ_HANDLED;
287364dbdf3SDaniel Mack }
288364dbdf3SDaniel Mack 
289364dbdf3SDaniel Mack static int
pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv * priv)290364dbdf3SDaniel Mack pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv)
291364dbdf3SDaniel Mack {
292364dbdf3SDaniel Mack 	int ret = 0;
293364dbdf3SDaniel Mack 
294364dbdf3SDaniel Mack 	QDUMP("Waiting for idle...");
295364dbdf3SDaniel Mack 
296364dbdf3SDaniel Mack 	/* Does not need to be atomic. There's a lock in user space,
297364dbdf3SDaniel Mack 	 * but anyhow, this is just for statistics. */
298364dbdf3SDaniel Mack 	priv->shared->num_wait_idle++;
299364dbdf3SDaniel Mack 
300364dbdf3SDaniel Mack 	while (priv->shared->hw_running) {
301364dbdf3SDaniel Mack 		int num = priv->shared->num_interrupts;
302364dbdf3SDaniel Mack 		u32 rbexhr = gc_readl(priv, REG_GCRBEXHR);
303364dbdf3SDaniel Mack 
304364dbdf3SDaniel Mack 		ret = wait_event_interruptible_timeout(priv->wait_idle,
305364dbdf3SDaniel Mack 					!priv->shared->hw_running, HZ*4);
306364dbdf3SDaniel Mack 
307688ec344SAxel Lin 		if (ret != 0)
308364dbdf3SDaniel Mack 			break;
309364dbdf3SDaniel Mack 
310364dbdf3SDaniel Mack 		if (gc_readl(priv, REG_GCRBEXHR) == rbexhr &&
311364dbdf3SDaniel Mack 		    priv->shared->num_interrupts == num) {
312364dbdf3SDaniel Mack 			QERROR("TIMEOUT");
313364dbdf3SDaniel Mack 			ret = -ETIMEDOUT;
314364dbdf3SDaniel Mack 			break;
315364dbdf3SDaniel Mack 		}
316364dbdf3SDaniel Mack 	}
317364dbdf3SDaniel Mack 
318364dbdf3SDaniel Mack 	QDUMP("done");
319364dbdf3SDaniel Mack 
320364dbdf3SDaniel Mack 	return ret;
321364dbdf3SDaniel Mack }
322364dbdf3SDaniel Mack 
323364dbdf3SDaniel Mack static int
pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv * priv)324364dbdf3SDaniel Mack pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv *priv)
325364dbdf3SDaniel Mack {
326364dbdf3SDaniel Mack 	int ret = 0;
327364dbdf3SDaniel Mack 
328364dbdf3SDaniel Mack 	QDUMP("Waiting for free...");
329364dbdf3SDaniel Mack 
330364dbdf3SDaniel Mack 	/* Does not need to be atomic. There's a lock in user space,
331364dbdf3SDaniel Mack 	 * but anyhow, this is just for statistics. */
332364dbdf3SDaniel Mack 	priv->shared->num_wait_free++;
333364dbdf3SDaniel Mack 
334364dbdf3SDaniel Mack 	while (!priv->free) {
335364dbdf3SDaniel Mack 		u32 rbexhr = gc_readl(priv, REG_GCRBEXHR);
336364dbdf3SDaniel Mack 
337364dbdf3SDaniel Mack 		ret = wait_event_interruptible_timeout(priv->wait_free,
338364dbdf3SDaniel Mack 						       priv->free, HZ*4);
339364dbdf3SDaniel Mack 
340364dbdf3SDaniel Mack 		if (ret < 0)
341364dbdf3SDaniel Mack 			break;
342364dbdf3SDaniel Mack 
343364dbdf3SDaniel Mack 		if (ret > 0)
344364dbdf3SDaniel Mack 			continue;
345364dbdf3SDaniel Mack 
346364dbdf3SDaniel Mack 		if (gc_readl(priv, REG_GCRBEXHR) == rbexhr) {
347364dbdf3SDaniel Mack 			QERROR("TIMEOUT");
348364dbdf3SDaniel Mack 			ret = -ETIMEDOUT;
349364dbdf3SDaniel Mack 			break;
350364dbdf3SDaniel Mack 		}
351364dbdf3SDaniel Mack 	}
352364dbdf3SDaniel Mack 
353364dbdf3SDaniel Mack 	QDUMP("done");
354364dbdf3SDaniel Mack 
355364dbdf3SDaniel Mack 	return ret;
356364dbdf3SDaniel Mack }
357364dbdf3SDaniel Mack 
358364dbdf3SDaniel Mack /* Misc device layer */
359364dbdf3SDaniel Mack 
to_pxa3xx_gcu_priv(struct file * file)360109393afSDaniel Mack static inline struct pxa3xx_gcu_priv *to_pxa3xx_gcu_priv(struct file *file)
361996142e6SAl Viro {
362996142e6SAl Viro 	struct miscdevice *dev = file->private_data;
363996142e6SAl Viro 	return container_of(dev, struct pxa3xx_gcu_priv, misc_dev);
364996142e6SAl Viro }
365996142e6SAl Viro 
3663437b2b8SDaniel Mack /*
3673437b2b8SDaniel Mack  * provide an empty .open callback, so the core sets file->private_data
3683437b2b8SDaniel Mack  * for us.
3693437b2b8SDaniel Mack  */
pxa3xx_gcu_open(struct inode * inode,struct file * file)3703437b2b8SDaniel Mack static int pxa3xx_gcu_open(struct inode *inode, struct file *file)
3713437b2b8SDaniel Mack {
3723437b2b8SDaniel Mack 	return 0;
3733437b2b8SDaniel Mack }
3743437b2b8SDaniel Mack 
375364dbdf3SDaniel Mack static ssize_t
pxa3xx_gcu_write(struct file * file,const char * buff,size_t count,loff_t * offp)376109393afSDaniel Mack pxa3xx_gcu_write(struct file *file, const char *buff,
377364dbdf3SDaniel Mack 		 size_t count, loff_t *offp)
378364dbdf3SDaniel Mack {
379364dbdf3SDaniel Mack 	int ret;
380364dbdf3SDaniel Mack 	unsigned long flags;
381364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch	*buffer;
382109393afSDaniel Mack 	struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
383364dbdf3SDaniel Mack 
384a09d2d00SHyunwoo Kim 	size_t words = count / 4;
385364dbdf3SDaniel Mack 
386364dbdf3SDaniel Mack 	/* Does not need to be atomic. There's a lock in user space,
387364dbdf3SDaniel Mack 	 * but anyhow, this is just for statistics. */
388364dbdf3SDaniel Mack 	priv->shared->num_writes++;
389364dbdf3SDaniel Mack 	priv->shared->num_words += words;
390364dbdf3SDaniel Mack 
391364dbdf3SDaniel Mack 	/* Last word reserved for batch buffer end command */
392364dbdf3SDaniel Mack 	if (words >= PXA3XX_GCU_BATCH_WORDS)
393364dbdf3SDaniel Mack 		return -E2BIG;
394364dbdf3SDaniel Mack 
395364dbdf3SDaniel Mack 	/* Wait for a free buffer */
396364dbdf3SDaniel Mack 	if (!priv->free) {
397364dbdf3SDaniel Mack 		ret = pxa3xx_gcu_wait_free(priv);
398364dbdf3SDaniel Mack 		if (ret < 0)
399364dbdf3SDaniel Mack 			return ret;
400364dbdf3SDaniel Mack 	}
401364dbdf3SDaniel Mack 
402364dbdf3SDaniel Mack 	/*
403364dbdf3SDaniel Mack 	 * Get buffer from free list
404364dbdf3SDaniel Mack 	 */
405364dbdf3SDaniel Mack 	spin_lock_irqsave(&priv->spinlock, flags);
406364dbdf3SDaniel Mack 	buffer = priv->free;
407364dbdf3SDaniel Mack 	priv->free = buffer->next;
408364dbdf3SDaniel Mack 	spin_unlock_irqrestore(&priv->spinlock, flags);
409364dbdf3SDaniel Mack 
410364dbdf3SDaniel Mack 
411364dbdf3SDaniel Mack 	/* Copy data from user into buffer */
412364dbdf3SDaniel Mack 	ret = copy_from_user(buffer->ptr, buff, words * 4);
413364dbdf3SDaniel Mack 	if (ret) {
414364dbdf3SDaniel Mack 		spin_lock_irqsave(&priv->spinlock, flags);
415364dbdf3SDaniel Mack 		buffer->next = priv->free;
416364dbdf3SDaniel Mack 		priv->free = buffer;
417364dbdf3SDaniel Mack 		spin_unlock_irqrestore(&priv->spinlock, flags);
4180b7f1cc7Saxel lin 		return -EFAULT;
419364dbdf3SDaniel Mack 	}
420364dbdf3SDaniel Mack 
421364dbdf3SDaniel Mack 	buffer->length = words;
422364dbdf3SDaniel Mack 
423364dbdf3SDaniel Mack 	/* Append batch buffer end command */
424364dbdf3SDaniel Mack 	buffer->ptr[words] = 0x01000000;
425364dbdf3SDaniel Mack 
426364dbdf3SDaniel Mack 	/*
427364dbdf3SDaniel Mack 	 * Add buffer to ready list
428364dbdf3SDaniel Mack 	 */
429364dbdf3SDaniel Mack 	spin_lock_irqsave(&priv->spinlock, flags);
430364dbdf3SDaniel Mack 
431364dbdf3SDaniel Mack 	buffer->next = NULL;
432364dbdf3SDaniel Mack 
433364dbdf3SDaniel Mack 	if (priv->ready) {
434364dbdf3SDaniel Mack 		BUG_ON(priv->ready_last == NULL);
435364dbdf3SDaniel Mack 
436364dbdf3SDaniel Mack 		priv->ready_last->next = buffer;
437364dbdf3SDaniel Mack 	} else
438364dbdf3SDaniel Mack 		priv->ready = buffer;
439364dbdf3SDaniel Mack 
440364dbdf3SDaniel Mack 	priv->ready_last = buffer;
441364dbdf3SDaniel Mack 
442364dbdf3SDaniel Mack 	if (!priv->shared->hw_running)
443364dbdf3SDaniel Mack 		run_ready(priv);
444364dbdf3SDaniel Mack 
445364dbdf3SDaniel Mack 	spin_unlock_irqrestore(&priv->spinlock, flags);
446364dbdf3SDaniel Mack 
447364dbdf3SDaniel Mack 	return words * 4;
448364dbdf3SDaniel Mack }
449364dbdf3SDaniel Mack 
450364dbdf3SDaniel Mack 
451364dbdf3SDaniel Mack static long
pxa3xx_gcu_ioctl(struct file * file,unsigned int cmd,unsigned long arg)452109393afSDaniel Mack pxa3xx_gcu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
453364dbdf3SDaniel Mack {
454364dbdf3SDaniel Mack 	unsigned long flags;
455109393afSDaniel Mack 	struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
456364dbdf3SDaniel Mack 
457364dbdf3SDaniel Mack 	switch (cmd) {
458364dbdf3SDaniel Mack 	case PXA3XX_GCU_IOCTL_RESET:
459364dbdf3SDaniel Mack 		spin_lock_irqsave(&priv->spinlock, flags);
460364dbdf3SDaniel Mack 		pxa3xx_gcu_reset(priv);
461364dbdf3SDaniel Mack 		spin_unlock_irqrestore(&priv->spinlock, flags);
462364dbdf3SDaniel Mack 		return 0;
463364dbdf3SDaniel Mack 
464364dbdf3SDaniel Mack 	case PXA3XX_GCU_IOCTL_WAIT_IDLE:
465364dbdf3SDaniel Mack 		return pxa3xx_gcu_wait_idle(priv);
466364dbdf3SDaniel Mack 	}
467364dbdf3SDaniel Mack 
468364dbdf3SDaniel Mack 	return -ENOSYS;
469364dbdf3SDaniel Mack }
470364dbdf3SDaniel Mack 
471364dbdf3SDaniel Mack static int
pxa3xx_gcu_mmap(struct file * file,struct vm_area_struct * vma)472109393afSDaniel Mack pxa3xx_gcu_mmap(struct file *file, struct vm_area_struct *vma)
473364dbdf3SDaniel Mack {
474364dbdf3SDaniel Mack 	unsigned int size = vma->vm_end - vma->vm_start;
475109393afSDaniel Mack 	struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
476364dbdf3SDaniel Mack 
477364dbdf3SDaniel Mack 	switch (vma->vm_pgoff) {
478364dbdf3SDaniel Mack 	case 0:
479364dbdf3SDaniel Mack 		/* hand out the shared data area */
480364dbdf3SDaniel Mack 		if (size != SHARED_SIZE)
481364dbdf3SDaniel Mack 			return -EINVAL;
482364dbdf3SDaniel Mack 
48302c486f4SChristoph Hellwig 		return dma_mmap_coherent(priv->dev, vma,
484364dbdf3SDaniel Mack 			priv->shared, priv->shared_phys, size);
485364dbdf3SDaniel Mack 
486364dbdf3SDaniel Mack 	case SHARED_SIZE >> PAGE_SHIFT:
487364dbdf3SDaniel Mack 		/* hand out the MMIO base for direct register access
488364dbdf3SDaniel Mack 		 * from userspace */
489364dbdf3SDaniel Mack 		if (size != resource_size(priv->resource_mem))
490364dbdf3SDaniel Mack 			return -EINVAL;
491364dbdf3SDaniel Mack 
492364dbdf3SDaniel Mack 		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
493364dbdf3SDaniel Mack 
494364dbdf3SDaniel Mack 		return io_remap_pfn_range(vma, vma->vm_start,
495364dbdf3SDaniel Mack 				priv->resource_mem->start >> PAGE_SHIFT,
496364dbdf3SDaniel Mack 				size, vma->vm_page_prot);
497364dbdf3SDaniel Mack 	}
498364dbdf3SDaniel Mack 
499364dbdf3SDaniel Mack 	return -EINVAL;
500364dbdf3SDaniel Mack }
501364dbdf3SDaniel Mack 
502364dbdf3SDaniel Mack 
503364dbdf3SDaniel Mack #ifdef PXA3XX_GCU_DEBUG_TIMER
504364dbdf3SDaniel Mack static struct timer_list pxa3xx_gcu_debug_timer;
505e4a67df7SKees Cook static struct pxa3xx_gcu_priv *debug_timer_priv;
506364dbdf3SDaniel Mack 
pxa3xx_gcu_debug_timedout(struct timer_list * unused)507e4a67df7SKees Cook static void pxa3xx_gcu_debug_timedout(struct timer_list *unused)
508364dbdf3SDaniel Mack {
509e4a67df7SKees Cook 	struct pxa3xx_gcu_priv *priv = debug_timer_priv;
510364dbdf3SDaniel Mack 
511364dbdf3SDaniel Mack 	QERROR("Timer DUMP");
512364dbdf3SDaniel Mack 
5135eabff1cSHimanshu Jha 	mod_timer(&pxa3xx_gcu_debug_timer, jiffies + 5 * HZ);
514364dbdf3SDaniel Mack }
515364dbdf3SDaniel Mack 
pxa3xx_gcu_init_debug_timer(struct pxa3xx_gcu_priv * priv)516e4a67df7SKees Cook static void pxa3xx_gcu_init_debug_timer(struct pxa3xx_gcu_priv *priv)
517364dbdf3SDaniel Mack {
518e4a67df7SKees Cook 	/* init the timer structure */
519e4a67df7SKees Cook 	debug_timer_priv = priv;
520e4a67df7SKees Cook 	timer_setup(&pxa3xx_gcu_debug_timer, pxa3xx_gcu_debug_timedout, 0);
521e4a67df7SKees Cook 	pxa3xx_gcu_debug_timedout(NULL);
522364dbdf3SDaniel Mack }
523364dbdf3SDaniel Mack #else
pxa3xx_gcu_init_debug_timer(struct pxa3xx_gcu_priv * priv)524e4a67df7SKees Cook static inline void pxa3xx_gcu_init_debug_timer(struct pxa3xx_gcu_priv *priv) {}
525364dbdf3SDaniel Mack #endif
526364dbdf3SDaniel Mack 
527364dbdf3SDaniel Mack static int
pxa3xx_gcu_add_buffer(struct device * dev,struct pxa3xx_gcu_priv * priv)5289e4f9675SDaniel Mack pxa3xx_gcu_add_buffer(struct device *dev,
529364dbdf3SDaniel Mack 		      struct pxa3xx_gcu_priv *priv)
530364dbdf3SDaniel Mack {
531364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch *buffer;
532364dbdf3SDaniel Mack 
533364dbdf3SDaniel Mack 	buffer = kzalloc(sizeof(struct pxa3xx_gcu_batch), GFP_KERNEL);
534364dbdf3SDaniel Mack 	if (!buffer)
535364dbdf3SDaniel Mack 		return -ENOMEM;
536364dbdf3SDaniel Mack 
5379e4f9675SDaniel Mack 	buffer->ptr = dma_alloc_coherent(dev, PXA3XX_GCU_BATCH_WORDS * 4,
538364dbdf3SDaniel Mack 					 &buffer->phys, GFP_KERNEL);
539364dbdf3SDaniel Mack 	if (!buffer->ptr) {
540364dbdf3SDaniel Mack 		kfree(buffer);
541364dbdf3SDaniel Mack 		return -ENOMEM;
542364dbdf3SDaniel Mack 	}
543364dbdf3SDaniel Mack 
544364dbdf3SDaniel Mack 	buffer->next = priv->free;
545364dbdf3SDaniel Mack 	priv->free = buffer;
546364dbdf3SDaniel Mack 
547364dbdf3SDaniel Mack 	return 0;
548364dbdf3SDaniel Mack }
549364dbdf3SDaniel Mack 
550364dbdf3SDaniel Mack static void
pxa3xx_gcu_free_buffers(struct device * dev,struct pxa3xx_gcu_priv * priv)5519e4f9675SDaniel Mack pxa3xx_gcu_free_buffers(struct device *dev,
552364dbdf3SDaniel Mack 			struct pxa3xx_gcu_priv *priv)
553364dbdf3SDaniel Mack {
554364dbdf3SDaniel Mack 	struct pxa3xx_gcu_batch *next, *buffer = priv->free;
555364dbdf3SDaniel Mack 
556364dbdf3SDaniel Mack 	while (buffer) {
557364dbdf3SDaniel Mack 		next = buffer->next;
558364dbdf3SDaniel Mack 
5599e4f9675SDaniel Mack 		dma_free_coherent(dev, PXA3XX_GCU_BATCH_WORDS * 4,
560364dbdf3SDaniel Mack 				  buffer->ptr, buffer->phys);
561364dbdf3SDaniel Mack 
562364dbdf3SDaniel Mack 		kfree(buffer);
563364dbdf3SDaniel Mack 		buffer = next;
564364dbdf3SDaniel Mack 	}
565364dbdf3SDaniel Mack 
566364dbdf3SDaniel Mack 	priv->free = NULL;
567364dbdf3SDaniel Mack }
568364dbdf3SDaniel Mack 
569109393afSDaniel Mack static const struct file_operations pxa3xx_gcu_miscdev_fops = {
570264bd660SAl Viro 	.owner =		THIS_MODULE,
5713437b2b8SDaniel Mack 	.open =			pxa3xx_gcu_open,
572109393afSDaniel Mack 	.write =		pxa3xx_gcu_write,
573109393afSDaniel Mack 	.unlocked_ioctl =	pxa3xx_gcu_ioctl,
574109393afSDaniel Mack 	.mmap =			pxa3xx_gcu_mmap,
575264bd660SAl Viro };
576264bd660SAl Viro 
pxa3xx_gcu_probe(struct platform_device * pdev)5779e4f9675SDaniel Mack static int pxa3xx_gcu_probe(struct platform_device *pdev)
578364dbdf3SDaniel Mack {
579364dbdf3SDaniel Mack 	int i, ret, irq;
580364dbdf3SDaniel Mack 	struct resource *r;
581364dbdf3SDaniel Mack 	struct pxa3xx_gcu_priv *priv;
5829e4f9675SDaniel Mack 	struct device *dev = &pdev->dev;
583364dbdf3SDaniel Mack 
584a9b47c7fSDaniel Mack 	priv = devm_kzalloc(dev, sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL);
585364dbdf3SDaniel Mack 	if (!priv)
586364dbdf3SDaniel Mack 		return -ENOMEM;
587364dbdf3SDaniel Mack 
588364dbdf3SDaniel Mack 	init_waitqueue_head(&priv->wait_idle);
589364dbdf3SDaniel Mack 	init_waitqueue_head(&priv->wait_free);
590364dbdf3SDaniel Mack 	spin_lock_init(&priv->spinlock);
591364dbdf3SDaniel Mack 
592364dbdf3SDaniel Mack 	/* we allocate the misc device structure as part of our own allocation,
593364dbdf3SDaniel Mack 	 * so we can get a pointer to our priv structure later on with
594364dbdf3SDaniel Mack 	 * container_of(). This isn't really necessary as we have a fixed minor
595364dbdf3SDaniel Mack 	 * number anyway, but this is to avoid statics. */
596364dbdf3SDaniel Mack 
5972ff86df2SChen Ni 	priv->misc_dev.minor	= PXA3XX_GCU_MINOR;
5982ff86df2SChen Ni 	priv->misc_dev.name	= DRV_NAME;
599109393afSDaniel Mack 	priv->misc_dev.fops	= &pxa3xx_gcu_miscdev_fops;
600364dbdf3SDaniel Mack 
601364dbdf3SDaniel Mack 	/* handle IO resources */
6020db0a1ebSYang Li 	priv->mmio_base = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
6039b22b8c5SJingoo Han 	if (IS_ERR(priv->mmio_base))
604a9b47c7fSDaniel Mack 		return PTR_ERR(priv->mmio_base);
605364dbdf3SDaniel Mack 
606364dbdf3SDaniel Mack 	/* enable the clock */
607a9b47c7fSDaniel Mack 	priv->clk = devm_clk_get(dev, NULL);
608626e021dSCai Huoqing 	if (IS_ERR(priv->clk))
609626e021dSCai Huoqing 		return dev_err_probe(dev, PTR_ERR(priv->clk), "failed to get clock\n");
610364dbdf3SDaniel Mack 
611364dbdf3SDaniel Mack 	/* request the IRQ */
6129e4f9675SDaniel Mack 	irq = platform_get_irq(pdev, 0);
613e2bc5533SYang Li 	if (irq < 0)
6147588f1ecSGustavo A. R. Silva 		return irq;
615364dbdf3SDaniel Mack 
616a9b47c7fSDaniel Mack 	ret = devm_request_irq(dev, irq, pxa3xx_gcu_handle_irq,
617f8798ccbSYong Zhang 			       0, DRV_NAME, priv);
618a9b47c7fSDaniel Mack 	if (ret < 0) {
6199e4f9675SDaniel Mack 		dev_err(dev, "request_irq failed\n");
620a9b47c7fSDaniel Mack 		return ret;
621a9b47c7fSDaniel Mack 	}
622a9b47c7fSDaniel Mack 
623a9b47c7fSDaniel Mack 	/* allocate dma memory */
624a9b47c7fSDaniel Mack 	priv->shared = dma_alloc_coherent(dev, SHARED_SIZE,
625a9b47c7fSDaniel Mack 					  &priv->shared_phys, GFP_KERNEL);
626a9b47c7fSDaniel Mack 	if (!priv->shared) {
627a9b47c7fSDaniel Mack 		dev_err(dev, "failed to allocate DMA memory\n");
628a9b47c7fSDaniel Mack 		return -ENOMEM;
629a9b47c7fSDaniel Mack 	}
630a9b47c7fSDaniel Mack 
631a9b47c7fSDaniel Mack 	/* register misc device */
632a9b47c7fSDaniel Mack 	ret = misc_register(&priv->misc_dev);
633a9b47c7fSDaniel Mack 	if (ret < 0) {
634a9b47c7fSDaniel Mack 		dev_err(dev, "misc_register() for minor %d failed\n",
6356ce6ae7cSZhenzhong Duan 			PXA3XX_GCU_MINOR);
636a9b47c7fSDaniel Mack 		goto err_free_dma;
637a9b47c7fSDaniel Mack 	}
638a9b47c7fSDaniel Mack 
6399e6e35edSRobert Jarzmik 	ret = clk_prepare_enable(priv->clk);
640a9b47c7fSDaniel Mack 	if (ret < 0) {
641a9b47c7fSDaniel Mack 		dev_err(dev, "failed to enable clock\n");
642a9b47c7fSDaniel Mack 		goto err_misc_deregister;
643a9b47c7fSDaniel Mack 	}
644a9b47c7fSDaniel Mack 
645a9b47c7fSDaniel Mack 	for (i = 0; i < 8; i++) {
646a9b47c7fSDaniel Mack 		ret = pxa3xx_gcu_add_buffer(dev, priv);
647a9b47c7fSDaniel Mack 		if (ret) {
648d87ad457SYang Yingliang 			pxa3xx_gcu_free_buffers(dev, priv);
649a9b47c7fSDaniel Mack 			dev_err(dev, "failed to allocate DMA memory\n");
650a9b47c7fSDaniel Mack 			goto err_disable_clk;
651a9b47c7fSDaniel Mack 		}
652364dbdf3SDaniel Mack 	}
653364dbdf3SDaniel Mack 
6549e4f9675SDaniel Mack 	platform_set_drvdata(pdev, priv);
655364dbdf3SDaniel Mack 	priv->resource_mem = r;
65602c486f4SChristoph Hellwig 	priv->dev = dev;
657364dbdf3SDaniel Mack 	pxa3xx_gcu_reset(priv);
658e4a67df7SKees Cook 	pxa3xx_gcu_init_debug_timer(priv);
659364dbdf3SDaniel Mack 
6609e4f9675SDaniel Mack 	dev_info(dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n",
661364dbdf3SDaniel Mack 			(void *) r->start, (void *) priv->shared_phys,
662364dbdf3SDaniel Mack 			SHARED_SIZE, irq);
663364dbdf3SDaniel Mack 	return 0;
664364dbdf3SDaniel Mack 
665d87ad457SYang Yingliang err_disable_clk:
666d87ad457SYang Yingliang 	clk_disable_unprepare(priv->clk);
667364dbdf3SDaniel Mack 
668364dbdf3SDaniel Mack err_misc_deregister:
669364dbdf3SDaniel Mack 	misc_deregister(&priv->misc_dev);
670364dbdf3SDaniel Mack 
671d87ad457SYang Yingliang err_free_dma:
672d87ad457SYang Yingliang 	dma_free_coherent(dev, SHARED_SIZE,
673d87ad457SYang Yingliang 			  priv->shared, priv->shared_phys);
674a9b47c7fSDaniel Mack 
675364dbdf3SDaniel Mack 	return ret;
676364dbdf3SDaniel Mack }
677364dbdf3SDaniel Mack 
pxa3xx_gcu_remove(struct platform_device * pdev)6782872c291SUwe Kleine-König static void pxa3xx_gcu_remove(struct platform_device *pdev)
679364dbdf3SDaniel Mack {
6809e4f9675SDaniel Mack 	struct pxa3xx_gcu_priv *priv = platform_get_drvdata(pdev);
6819e4f9675SDaniel Mack 	struct device *dev = &pdev->dev;
682364dbdf3SDaniel Mack 
683364dbdf3SDaniel Mack 	pxa3xx_gcu_wait_idle(priv);
684364dbdf3SDaniel Mack 	misc_deregister(&priv->misc_dev);
685a9b47c7fSDaniel Mack 	dma_free_coherent(dev, SHARED_SIZE, priv->shared, priv->shared_phys);
686d87ad457SYang Yingliang 	clk_disable_unprepare(priv->clk);
687109393afSDaniel Mack 	pxa3xx_gcu_free_buffers(dev, priv);
688364dbdf3SDaniel Mack }
689364dbdf3SDaniel Mack 
690aa45ee8eSDaniel Mack #ifdef CONFIG_OF
691aa45ee8eSDaniel Mack static const struct of_device_id pxa3xx_gcu_of_match[] = {
692aa45ee8eSDaniel Mack 	{ .compatible = "marvell,pxa300-gcu", },
693aa45ee8eSDaniel Mack 	{ }
694aa45ee8eSDaniel Mack };
695aa45ee8eSDaniel Mack MODULE_DEVICE_TABLE(of, pxa3xx_gcu_of_match);
696aa45ee8eSDaniel Mack #endif
697aa45ee8eSDaniel Mack 
698364dbdf3SDaniel Mack static struct platform_driver pxa3xx_gcu_driver = {
699364dbdf3SDaniel Mack 	.probe = pxa3xx_gcu_probe,
70001ecc142SUwe Kleine-König 	.remove = pxa3xx_gcu_remove,
701364dbdf3SDaniel Mack 	.driver = {
702364dbdf3SDaniel Mack 		.name = DRV_NAME,
703aa45ee8eSDaniel Mack 		.of_match_table = of_match_ptr(pxa3xx_gcu_of_match),
704364dbdf3SDaniel Mack 	},
705364dbdf3SDaniel Mack };
706364dbdf3SDaniel Mack 
7074277f2c4SAxel Lin module_platform_driver(pxa3xx_gcu_driver);
708364dbdf3SDaniel Mack 
709364dbdf3SDaniel Mack MODULE_DESCRIPTION("PXA3xx graphics controller unit driver");
710364dbdf3SDaniel Mack MODULE_LICENSE("GPL");
7116ce6ae7cSZhenzhong Duan MODULE_ALIAS_MISCDEV(PXA3XX_GCU_MINOR);
712364dbdf3SDaniel Mack MODULE_AUTHOR("Janine Kropp <nin@directfb.org>, "
713364dbdf3SDaniel Mack 		"Denis Oliver Kropp <dok@directfb.org>, "
714364dbdf3SDaniel Mack 		"Daniel Mack <daniel@caiaq.de>");
715