PageRenderTime 42ms CodeModel.GetById 18ms app.highlight 21ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/video/pxa3xx-gcu.c

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