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