pxa3xx-gcu.c 16.7 KB
Newer Older
1
/*
2
 *  pxa3xx-gcu.c - Linux kernel module for PXA3xx graphics controllers
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 *  This driver needs a DirectFB counterpart in user space, communication
 *  is handled via mmap()ed memory areas and an ioctl.
 *
 *  Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
 *  Copyright (c) 2009 Janine Kropp <nin@directfb.org>
 *  Copyright (c) 2009 Denis Oliver Kropp <dok@directfb.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * WARNING: This controller is attached to System Bus 2 of the PXA which
L
Lucas De Marchi 已提交
28
 * needs its arbiter to be enabled explicitly (CKENB & 1<<9).
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
 * There is currently no way to do this from Linux, so you need to teach
 * your bootloader for now.
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <linux/io.h>

#include "pxa3xx-gcu.h"

#define DRV_NAME	"pxa3xx-gcu"
#define MISCDEV_MINOR	197

#define REG_GCCR	0x00
#define GCCR_SYNC_CLR	(1 << 9)
#define GCCR_BP_RST	(1 << 8)
#define GCCR_ABORT	(1 << 6)
#define GCCR_STOP	(1 << 4)

#define REG_GCISCR	0x04
#define REG_GCIECR	0x08
#define REG_GCRBBR	0x20
#define REG_GCRBLR	0x24
#define REG_GCRBHR	0x28
#define REG_GCRBTR	0x2C
#define REG_GCRBEXHR	0x30

#define IE_EOB		(1 << 0)
#define IE_EEOB		(1 << 5)
#define IE_ALL		0xff

#define SHARED_SIZE	PAGE_ALIGN(sizeof(struct pxa3xx_gcu_shared))

/* #define PXA3XX_GCU_DEBUG */
/* #define PXA3XX_GCU_DEBUG_TIMER */

#ifdef PXA3XX_GCU_DEBUG
#define QDUMP(msg)					\
	do {						\
		QPRINT(priv, KERN_DEBUG, msg);		\
	} while (0)
#else
#define QDUMP(msg)	do {} while (0)
#endif

#define QERROR(msg)					\
	do {						\
		QPRINT(priv, KERN_ERR, msg);		\
	} while (0)

struct pxa3xx_gcu_batch {
	struct pxa3xx_gcu_batch *next;
	u32			*ptr;
	dma_addr_t		 phys;
	unsigned long		 length;
};

struct pxa3xx_gcu_priv {
	void __iomem		 *mmio_base;
	struct clk		 *clk;
	struct pxa3xx_gcu_shared *shared;
	dma_addr_t		  shared_phys;
	struct resource		 *resource_mem;
	struct miscdevice	  misc_dev;
	wait_queue_head_t	  wait_idle;
	wait_queue_head_t	  wait_free;
	spinlock_t		  spinlock;
	struct timeval 		  base_time;

	struct pxa3xx_gcu_batch *free;
	struct pxa3xx_gcu_batch *ready;
	struct pxa3xx_gcu_batch *ready_last;
	struct pxa3xx_gcu_batch *running;
};

static inline unsigned long
gc_readl(struct pxa3xx_gcu_priv *priv, unsigned int off)
{
	return __raw_readl(priv->mmio_base + off);
}

static inline void
gc_writel(struct pxa3xx_gcu_priv *priv, unsigned int off, unsigned long val)
{
	__raw_writel(val, priv->mmio_base + off);
}

#define QPRINT(priv, level, msg)					\
	do {								\
		struct timeval tv;					\
		struct pxa3xx_gcu_shared *shared = priv->shared;	\
		u32 base = gc_readl(priv, REG_GCRBBR);			\
									\
		do_gettimeofday(&tv);					\
									\
		printk(level "%ld.%03ld.%03ld - %-17s: %-21s (%s, "	\
			"STATUS "					\
			"0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, "	\
			"T %5ld)\n",					\
			tv.tv_sec - priv->base_time.tv_sec,		\
			tv.tv_usec / 1000, tv.tv_usec % 1000,		\
			__func__, msg,					\
			shared->hw_running ? "running" : "   idle",	\
			gc_readl(priv, REG_GCISCR),			\
			gc_readl(priv, REG_GCRBBR),			\
			gc_readl(priv, REG_GCRBLR),			\
			(gc_readl(priv, REG_GCRBEXHR) - base) / 4,	\
			(gc_readl(priv, REG_GCRBHR) - base) / 4,	\
			(gc_readl(priv, REG_GCRBTR) - base) / 4);	\
	} while (0)

static void
pxa3xx_gcu_reset(struct pxa3xx_gcu_priv *priv)
{
	QDUMP("RESET");

	/* disable interrupts */
	gc_writel(priv, REG_GCIECR, 0);

	/* reset hardware */
	gc_writel(priv, REG_GCCR, GCCR_ABORT);
	gc_writel(priv, REG_GCCR, 0);

	memset(priv->shared, 0, SHARED_SIZE);
	priv->shared->buffer_phys = priv->shared_phys;
	priv->shared->magic = PXA3XX_GCU_SHARED_MAGIC;

	do_gettimeofday(&priv->base_time);

	/* set up the ring buffer pointers */
	gc_writel(priv, REG_GCRBLR, 0);
	gc_writel(priv, REG_GCRBBR, priv->shared_phys);
	gc_writel(priv, REG_GCRBTR, priv->shared_phys);

	/* enable all IRQs except EOB */
	gc_writel(priv, REG_GCIECR, IE_ALL & ~IE_EOB);
}

static void
dump_whole_state(struct pxa3xx_gcu_priv *priv)
{
	struct pxa3xx_gcu_shared *sh = priv->shared;
	u32 base = gc_readl(priv, REG_GCRBBR);

	QDUMP("DUMP");

	printk(KERN_DEBUG "== PXA3XX-GCU DUMP ==\n"
		"%s, STATUS 0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, T %5ld\n",
		sh->hw_running ? "running" : "idle   ",
		gc_readl(priv, REG_GCISCR),
		gc_readl(priv, REG_GCRBBR),
		gc_readl(priv, REG_GCRBLR),
		(gc_readl(priv, REG_GCRBEXHR) - base) / 4,
		(gc_readl(priv, REG_GCRBHR) - base) / 4,
		(gc_readl(priv, REG_GCRBTR) - base) / 4);
}

static void
flush_running(struct pxa3xx_gcu_priv *priv)
{
	struct pxa3xx_gcu_batch *running = priv->running;
	struct pxa3xx_gcu_batch *next;

	while (running) {
		next = running->next;
		running->next = priv->free;
		priv->free = running;
		running = next;
	}

	priv->running = NULL;
}

static void
run_ready(struct pxa3xx_gcu_priv *priv)
{
	unsigned int num = 0;
	struct pxa3xx_gcu_shared *shared = priv->shared;
	struct pxa3xx_gcu_batch	*ready = priv->ready;

	QDUMP("Start");

	BUG_ON(!ready);

	shared->buffer[num++] = 0x05000000;

	while (ready) {
		shared->buffer[num++] = 0x00000001;
		shared->buffer[num++] = ready->phys;
		ready = ready->next;
	}

	shared->buffer[num++] = 0x05000000;
	priv->running = priv->ready;
	priv->ready = priv->ready_last = NULL;
	gc_writel(priv, REG_GCRBLR, 0);
	shared->hw_running = 1;

	/* ring base address */
	gc_writel(priv, REG_GCRBBR, shared->buffer_phys);

	/* ring tail address */
	gc_writel(priv, REG_GCRBTR, shared->buffer_phys + num * 4);

	/* ring length */
	gc_writel(priv, REG_GCRBLR, ((num + 63) & ~63) * 4);
}

static irqreturn_t
pxa3xx_gcu_handle_irq(int irq, void *ctx)
{
	struct pxa3xx_gcu_priv *priv = ctx;
	struct pxa3xx_gcu_shared *shared = priv->shared;
	u32 status = gc_readl(priv, REG_GCISCR) & IE_ALL;

	QDUMP("-Interrupt");

	if (!status)
		return IRQ_NONE;

	spin_lock(&priv->spinlock);
	shared->num_interrupts++;

	if (status & IE_EEOB) {
		QDUMP(" [EEOB]");

		flush_running(priv);
		wake_up_all(&priv->wait_free);

		if (priv->ready) {
			run_ready(priv);
		} else {
			/* There is no more data prepared by the userspace.
			 * Set hw_running = 0 and wait for the next userspace
			 * kick-off */
			shared->num_idle++;
			shared->hw_running = 0;

			QDUMP(" '-> Idle.");

			/* set ring buffer length to zero */
			gc_writel(priv, REG_GCRBLR, 0);

			wake_up_all(&priv->wait_idle);
		}

		shared->num_done++;
	} else {
		QERROR(" [???]");
		dump_whole_state(priv);
	}

	/* Clear the interrupt */
	gc_writel(priv, REG_GCISCR, status);
	spin_unlock(&priv->spinlock);

	return IRQ_HANDLED;
}

static int
pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv)
{
	int ret = 0;

	QDUMP("Waiting for idle...");

	/* Does not need to be atomic. There's a lock in user space,
	 * but anyhow, this is just for statistics. */
	priv->shared->num_wait_idle++;

	while (priv->shared->hw_running) {
		int num = priv->shared->num_interrupts;
		u32 rbexhr = gc_readl(priv, REG_GCRBEXHR);

		ret = wait_event_interruptible_timeout(priv->wait_idle,
					!priv->shared->hw_running, HZ*4);

317
		if (ret != 0)
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
			break;

		if (gc_readl(priv, REG_GCRBEXHR) == rbexhr &&
		    priv->shared->num_interrupts == num) {
			QERROR("TIMEOUT");
			ret = -ETIMEDOUT;
			break;
		}
	}

	QDUMP("done");

	return ret;
}

static int
pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv *priv)
{
	int ret = 0;

	QDUMP("Waiting for free...");

	/* Does not need to be atomic. There's a lock in user space,
	 * but anyhow, this is just for statistics. */
	priv->shared->num_wait_free++;

	while (!priv->free) {
		u32 rbexhr = gc_readl(priv, REG_GCRBEXHR);

		ret = wait_event_interruptible_timeout(priv->wait_free,
						       priv->free, HZ*4);

		if (ret < 0)
			break;

		if (ret > 0)
			continue;

		if (gc_readl(priv, REG_GCRBEXHR) == rbexhr) {
			QERROR("TIMEOUT");
			ret = -ETIMEDOUT;
			break;
		}
	}

	QDUMP("done");

	return ret;
}

/* Misc device layer */

370
static inline struct pxa3xx_gcu_priv *to_pxa3xx_gcu_priv(struct file *file)
371 372 373 374 375
{
	struct miscdevice *dev = file->private_data;
	return container_of(dev, struct pxa3xx_gcu_priv, misc_dev);
}

376 377 378 379 380 381 382 383 384
/*
 * provide an empty .open callback, so the core sets file->private_data
 * for us.
 */
static int pxa3xx_gcu_open(struct inode *inode, struct file *file)
{
	return 0;
}

385
static ssize_t
386 387
pxa3xx_gcu_write(struct file *file, const char *buff,
		 size_t count, loff_t *offp)
388 389 390 391
{
	int ret;
	unsigned long flags;
	struct pxa3xx_gcu_batch	*buffer;
392
	struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427

	int words = count / 4;

	/* Does not need to be atomic. There's a lock in user space,
	 * but anyhow, this is just for statistics. */
	priv->shared->num_writes++;
	priv->shared->num_words += words;

	/* Last word reserved for batch buffer end command */
	if (words >= PXA3XX_GCU_BATCH_WORDS)
		return -E2BIG;

	/* Wait for a free buffer */
	if (!priv->free) {
		ret = pxa3xx_gcu_wait_free(priv);
		if (ret < 0)
			return ret;
	}

	/*
	 * Get buffer from free list
	 */
	spin_lock_irqsave(&priv->spinlock, flags);
	buffer = priv->free;
	priv->free = buffer->next;
	spin_unlock_irqrestore(&priv->spinlock, flags);


	/* Copy data from user into buffer */
	ret = copy_from_user(buffer->ptr, buff, words * 4);
	if (ret) {
		spin_lock_irqsave(&priv->spinlock, flags);
		buffer->next = priv->free;
		priv->free = buffer;
		spin_unlock_irqrestore(&priv->spinlock, flags);
428
		return -EFAULT;
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
	}

	buffer->length = words;

	/* Append batch buffer end command */
	buffer->ptr[words] = 0x01000000;

	/*
	 * Add buffer to ready list
	 */
	spin_lock_irqsave(&priv->spinlock, flags);

	buffer->next = NULL;

	if (priv->ready) {
		BUG_ON(priv->ready_last == NULL);

		priv->ready_last->next = buffer;
	} else
		priv->ready = buffer;

	priv->ready_last = buffer;

	if (!priv->shared->hw_running)
		run_ready(priv);

	spin_unlock_irqrestore(&priv->spinlock, flags);

	return words * 4;
}


static long
462
pxa3xx_gcu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
463 464
{
	unsigned long flags;
465
	struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481

	switch (cmd) {
	case PXA3XX_GCU_IOCTL_RESET:
		spin_lock_irqsave(&priv->spinlock, flags);
		pxa3xx_gcu_reset(priv);
		spin_unlock_irqrestore(&priv->spinlock, flags);
		return 0;

	case PXA3XX_GCU_IOCTL_WAIT_IDLE:
		return pxa3xx_gcu_wait_idle(priv);
	}

	return -ENOSYS;
}

static int
482
pxa3xx_gcu_mmap(struct file *file, struct vm_area_struct *vma)
483 484
{
	unsigned int size = vma->vm_end - vma->vm_start;
485
	struct pxa3xx_gcu_priv *priv = to_pxa3xx_gcu_priv(file);
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539

	switch (vma->vm_pgoff) {
	case 0:
		/* hand out the shared data area */
		if (size != SHARED_SIZE)
			return -EINVAL;

		return dma_mmap_coherent(NULL, vma,
			priv->shared, priv->shared_phys, size);

	case SHARED_SIZE >> PAGE_SHIFT:
		/* hand out the MMIO base for direct register access
		 * from userspace */
		if (size != resource_size(priv->resource_mem))
			return -EINVAL;

		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

		return io_remap_pfn_range(vma, vma->vm_start,
				priv->resource_mem->start >> PAGE_SHIFT,
				size, vma->vm_page_prot);
	}

	return -EINVAL;
}


#ifdef PXA3XX_GCU_DEBUG_TIMER
static struct timer_list pxa3xx_gcu_debug_timer;

static void pxa3xx_gcu_debug_timedout(unsigned long ptr)
{
	struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr;

	QERROR("Timer DUMP");

	/* init the timer structure */
	init_timer(&pxa3xx_gcu_debug_timer);
	pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout;
	pxa3xx_gcu_debug_timer.data = ptr;
	pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */

	add_timer(&pxa3xx_gcu_debug_timer);
}

static void pxa3xx_gcu_init_debug_timer(void)
{
	pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer);
}
#else
static inline void pxa3xx_gcu_init_debug_timer(void) {}
#endif

static int
540
pxa3xx_gcu_add_buffer(struct device *dev,
541
		      struct pxa3xx_gcu_priv *priv)
542 543 544 545 546 547 548
{
	struct pxa3xx_gcu_batch *buffer;

	buffer = kzalloc(sizeof(struct pxa3xx_gcu_batch), GFP_KERNEL);
	if (!buffer)
		return -ENOMEM;

549
	buffer->ptr = dma_alloc_coherent(dev, PXA3XX_GCU_BATCH_WORDS * 4,
550 551 552 553 554 555 556 557 558 559 560 561 562
					 &buffer->phys, GFP_KERNEL);
	if (!buffer->ptr) {
		kfree(buffer);
		return -ENOMEM;
	}

	buffer->next = priv->free;
	priv->free = buffer;

	return 0;
}

static void
563
pxa3xx_gcu_free_buffers(struct device *dev,
564
			struct pxa3xx_gcu_priv *priv)
565 566 567 568 569 570
{
	struct pxa3xx_gcu_batch *next, *buffer = priv->free;

	while (buffer) {
		next = buffer->next;

571
		dma_free_coherent(dev, PXA3XX_GCU_BATCH_WORDS * 4,
572 573 574 575 576 577 578 579 580
				  buffer->ptr, buffer->phys);

		kfree(buffer);
		buffer = next;
	}

	priv->free = NULL;
}

581 582
static const struct file_operations pxa3xx_gcu_miscdev_fops = {
	.owner =		THIS_MODULE,
583
	.open =			pxa3xx_gcu_open,
584 585 586
	.write =		pxa3xx_gcu_write,
	.unlocked_ioctl =	pxa3xx_gcu_ioctl,
	.mmap =			pxa3xx_gcu_mmap,
587 588
};

589
static int pxa3xx_gcu_probe(struct platform_device *pdev)
590 591 592 593
{
	int i, ret, irq;
	struct resource *r;
	struct pxa3xx_gcu_priv *priv;
594
	struct device *dev = &pdev->dev;
595

596
	priv = devm_kzalloc(dev, sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL);
597 598 599 600 601 602 603 604 605 606 607 608 609 610
	if (!priv)
		return -ENOMEM;

	init_waitqueue_head(&priv->wait_idle);
	init_waitqueue_head(&priv->wait_free);
	spin_lock_init(&priv->spinlock);

	/* we allocate the misc device structure as part of our own allocation,
	 * so we can get a pointer to our priv structure later on with
	 * container_of(). This isn't really necessary as we have a fixed minor
	 * number anyway, but this is to avoid statics. */

	priv->misc_dev.minor	= MISCDEV_MINOR,
	priv->misc_dev.name	= DRV_NAME,
611
	priv->misc_dev.fops	= &pxa3xx_gcu_miscdev_fops;
612 613

	/* handle IO resources */
614
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
615 616 617 618
	priv->mmio_base = devm_request_and_ioremap(dev, r);
	if (IS_ERR(priv->mmio_base)) {
		dev_err(dev, "failed to map I/O memory\n");
		return PTR_ERR(priv->mmio_base);
619 620
	}

621 622 623 624 625
	/* enable the clock */
	priv->clk = devm_clk_get(dev, NULL);
	if (IS_ERR(priv->clk)) {
		dev_err(dev, "failed to get clock\n");
		return PTR_ERR(priv->clk);
626 627
	}

628 629 630 631 632 633 634 635 636 637 638 639
	/* request the IRQ */
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(dev, "no IRQ defined\n");
		return -ENODEV;
	}

	ret = devm_request_irq(dev, irq, pxa3xx_gcu_handle_irq,
			       0, DRV_NAME, priv);
	if (ret < 0) {
		dev_err(dev, "request_irq failed\n");
		return ret;
640 641 642
	}

	/* allocate dma memory */
643
	priv->shared = dma_alloc_coherent(dev, SHARED_SIZE,
644 645
					  &priv->shared_phys, GFP_KERNEL);
	if (!priv->shared) {
646
		dev_err(dev, "failed to allocate DMA memory\n");
647
		return -ENOMEM;
648 649
	}

650 651 652 653 654
	/* register misc device */
	ret = misc_register(&priv->misc_dev);
	if (ret < 0) {
		dev_err(dev, "misc_register() for minor %d failed\n",
			MISCDEV_MINOR);
655 656 657 658 659
		goto err_free_dma;
	}

	ret = clk_enable(priv->clk);
	if (ret < 0) {
660
		dev_err(dev, "failed to enable clock\n");
661
		goto err_misc_deregister;
662 663
	}

664 665 666 667 668 669
	for (i = 0; i < 8; i++) {
		ret = pxa3xx_gcu_add_buffer(dev, priv);
		if (ret) {
			dev_err(dev, "failed to allocate DMA memory\n");
			goto err_disable_clk;
		}
670 671
	}

672
	platform_set_drvdata(pdev, priv);
673 674 675 676
	priv->resource_mem = r;
	pxa3xx_gcu_reset(priv);
	pxa3xx_gcu_init_debug_timer();

677
	dev_info(dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n",
678 679 680 681 682
			(void *) r->start, (void *) priv->shared_phys,
			SHARED_SIZE, irq);
	return 0;

err_free_dma:
683
	dma_free_coherent(dev, SHARED_SIZE,
684 685 686 687 688
			priv->shared, priv->shared_phys);

err_misc_deregister:
	misc_deregister(&priv->misc_dev);

689 690 691
err_disable_clk:
	clk_disable(priv->clk);

692 693 694
	return ret;
}

695
static int pxa3xx_gcu_remove(struct platform_device *pdev)
696
{
697 698
	struct pxa3xx_gcu_priv *priv = platform_get_drvdata(pdev);
	struct device *dev = &pdev->dev;
699 700 701

	pxa3xx_gcu_wait_idle(priv);
	misc_deregister(&priv->misc_dev);
702
	dma_free_coherent(dev, SHARED_SIZE, priv->shared, priv->shared_phys);
703
	pxa3xx_gcu_free_buffers(dev, priv);
704 705 706 707 708 709

	return 0;
}

static struct platform_driver pxa3xx_gcu_driver = {
	.probe	  = pxa3xx_gcu_probe,
710
	.remove	 = pxa3xx_gcu_remove,
711 712 713 714 715 716
	.driver	 = {
		.owner  = THIS_MODULE,
		.name   = DRV_NAME,
	},
};

717
module_platform_driver(pxa3xx_gcu_driver);
718 719 720 721 722 723 724

MODULE_DESCRIPTION("PXA3xx graphics controller unit driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(MISCDEV_MINOR);
MODULE_AUTHOR("Janine Kropp <nin@directfb.org>, "
		"Denis Oliver Kropp <dok@directfb.org>, "
		"Daniel Mack <daniel@caiaq.de>");