]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mxc/ipu3/ipu_device.c
ENGR00142551-1 IPUv3:Support triple buffer
[karo-tx-linux.git] / drivers / mxc / ipu3 / ipu_device.c
1 /*
2  * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved.
3  */
4
5 /*
6  * The code contained herein is licensed under the GNU General Public
7  * License. You may obtain a copy of the GNU General Public License
8  * Version 2 or later at the following locations:
9  *
10  * http://www.opensource.org/licenses/gpl-license.html
11  * http://www.gnu.org/copyleft/gpl.html
12  */
13
14 /*!
15  * @file ipu_device.c
16  *
17  * @brief This file contains the IPUv3 driver device interface and fops functions.
18  *
19  * @ingroup IPU
20  */
21
22 #include <linux/types.h>
23 #include <linux/init.h>
24 #include <linux/platform_device.h>
25 #include <linux/err.h>
26 #include <linux/spinlock.h>
27 #include <linux/delay.h>
28 #include <linux/clk.h>
29 #include <linux/poll.h>
30 #include <linux/sched.h>
31 #include <linux/time.h>
32 #include <linux/wait.h>
33 #include <linux/dma-mapping.h>
34 #include <linux/io.h>
35 #include <linux/ipu.h>
36 #include <asm/cacheflush.h>
37
38 #include "ipu_prv.h"
39 #include "ipu_regs.h"
40 #include "ipu_param_mem.h"
41
42 /* Strucutures and variables for exporting MXC IPU as device*/
43
44 static int mxc_ipu_major;
45 static struct class *mxc_ipu_class;
46
47 DEFINE_SPINLOCK(event_lock);
48
49 struct ipu_dev_irq_info {
50         wait_queue_head_t waitq;
51         int irq_pending;
52 } irq_info[480];
53
54 int register_ipu_device(void);
55
56 /* Static functions */
57
58 int get_events(ipu_event_info *p)
59 {
60         unsigned long flags;
61         int ret = 0;
62
63         spin_lock_irqsave(&event_lock, flags);
64         if (irq_info[p->irq].irq_pending > 0)
65                 irq_info[p->irq].irq_pending--;
66         else
67                 ret = -1;
68         spin_unlock_irqrestore(&event_lock, flags);
69
70         return ret;
71 }
72
73 static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
74 {
75         irq_info[irq].irq_pending++;
76
77         /* Wakeup any blocking user context */
78         wake_up_interruptible(&(irq_info[irq].waitq));
79         return IRQ_HANDLED;
80 }
81
82 static int mxc_ipu_open(struct inode *inode, struct file *file)
83 {
84         int ret = 0;
85         return ret;
86 }
87
88 static long mxc_ipu_ioctl(struct file *file,
89                 unsigned int cmd, unsigned long arg)
90 {
91         int ret = 0;
92
93         switch (cmd) {
94         case IPU_INIT_CHANNEL:
95                 {
96                         ipu_channel_parm parm;
97
98                         if (copy_from_user
99                                         (&parm, (ipu_channel_parm *) arg,
100                                          sizeof(ipu_channel_parm)))
101                                 return -EFAULT;
102
103                         if (!parm.flag) {
104                                 ret =
105                                         ipu_init_channel(parm.channel,
106                                                         &parm.params);
107                         } else {
108                                 ret = ipu_init_channel(parm.channel, NULL);
109                         }
110                 }
111                 break;
112         case IPU_UNINIT_CHANNEL:
113                 {
114                 ipu_channel_t ch;
115                 int __user *argp = (void __user *)arg;
116                 if (get_user(ch, argp))
117                                 return -EFAULT;
118                         ipu_uninit_channel(ch);
119                 }
120                 break;
121         case IPU_INIT_CHANNEL_BUFFER:
122                 {
123                         ipu_channel_buf_parm parm;
124                         if (copy_from_user
125                                 (&parm, (ipu_channel_buf_parm *) arg,
126                                 sizeof(ipu_channel_buf_parm)))
127                                 return -EFAULT;
128
129                         ret =
130                                 ipu_init_channel_buffer(
131                                                 parm.channel, parm.type,
132                                                 parm.pixel_fmt,
133                                                 parm.width, parm.height,
134                                                 parm.stride,
135                                                 parm.rot_mode,
136                                                 parm.phyaddr_0,
137                                                 parm.phyaddr_1,
138                                                 parm.phyaddr_2,
139                                                 parm.u_offset,
140                                                 parm.v_offset);
141
142                 }
143                 break;
144         case IPU_UPDATE_CHANNEL_BUFFER:
145                 {
146                         ipu_channel_buf_parm parm;
147                         if (copy_from_user
148                                 (&parm, (ipu_channel_buf_parm *) arg,
149                                 sizeof(ipu_channel_buf_parm)))
150                                 return -EFAULT;
151
152                         if ((parm.phyaddr_0 != (dma_addr_t) NULL)
153                                 && (parm.phyaddr_1 == (dma_addr_t) NULL)) {
154                                 ret =
155                                         ipu_update_channel_buffer(
156                                                         parm.channel,
157                                                         parm.type,
158                                                         parm.bufNum,
159                                                         parm.phyaddr_0);
160                         } else if ((parm.phyaddr_0 == (dma_addr_t) NULL)
161                                 && (parm.phyaddr_1 != (dma_addr_t) NULL)) {
162                                 ret =
163                                         ipu_update_channel_buffer(
164                                                         parm.channel,
165                                                         parm.type,
166                                                         parm.bufNum,
167                                                         parm.phyaddr_1);
168                         } else {
169                                 ret = -1;
170                         }
171
172                 }
173                 break;
174         case IPU_SELECT_CHANNEL_BUFFER:
175                 {
176                         ipu_channel_buf_parm parm;
177                         if (copy_from_user
178                                 (&parm, (ipu_channel_buf_parm *) arg,
179                                 sizeof(ipu_channel_buf_parm)))
180                                 return -EFAULT;
181
182                         ret =
183                                 ipu_select_buffer(parm.channel,
184                                         parm.type, parm.bufNum);
185
186                 }
187                 break;
188         case IPU_SELECT_MULTI_VDI_BUFFER:
189                 {
190                         uint32_t parm;
191                         if (copy_from_user
192                                 (&parm, (uint32_t *) arg,
193                                 sizeof(uint32_t)))
194                                 return -EFAULT;
195
196                         ret = ipu_select_multi_vdi_buffer(parm);
197                 }
198                 break;
199         case IPU_LINK_CHANNELS:
200                 {
201                         ipu_channel_link link;
202                         if (copy_from_user
203                                 (&link, (ipu_channel_link *) arg,
204                                 sizeof(ipu_channel_link)))
205                                 return -EFAULT;
206
207                         ret = ipu_link_channels(link.src_ch,
208                                 link.dest_ch);
209
210                 }
211                 break;
212         case IPU_UNLINK_CHANNELS:
213                 {
214                         ipu_channel_link link;
215                         if (copy_from_user
216                                 (&link, (ipu_channel_link *) arg,
217                                 sizeof(ipu_channel_link)))
218                                 return -EFAULT;
219
220                         ret = ipu_unlink_channels(link.src_ch,
221                                 link.dest_ch);
222
223                 }
224                 break;
225         case IPU_ENABLE_CHANNEL:
226                 {
227                         ipu_channel_t ch;
228                         int __user *argp = (void __user *)arg;
229                         if (get_user(ch, argp))
230                                 return -EFAULT;
231                         ipu_enable_channel(ch);
232                 }
233                 break;
234         case IPU_DISABLE_CHANNEL:
235                 {
236                         ipu_channel_info info;
237                         if (copy_from_user
238                                 (&info, (ipu_channel_info *) arg,
239                                  sizeof(ipu_channel_info)))
240                                 return -EFAULT;
241
242                         ret = ipu_disable_channel(info.channel,
243                                 info.stop);
244                 }
245                 break;
246         case IPU_ENABLE_IRQ:
247                 {
248                         uint32_t irq;
249                         int __user *argp = (void __user *)arg;
250                         if (get_user(irq, argp))
251                                 return -EFAULT;
252                         ipu_enable_irq(irq);
253                 }
254                 break;
255         case IPU_DISABLE_IRQ:
256                 {
257                         uint32_t irq;
258                         int __user *argp = (void __user *)arg;
259                         if (get_user(irq, argp))
260                                 return -EFAULT;
261                         ipu_disable_irq(irq);
262                 }
263                 break;
264         case IPU_CLEAR_IRQ:
265                 {
266                         uint32_t irq;
267                         int __user *argp = (void __user *)arg;
268                         if (get_user(irq, argp))
269                                 return -EFAULT;
270                         ipu_clear_irq(irq);
271                 }
272                 break;
273         case IPU_FREE_IRQ:
274                 {
275                         ipu_irq_info info;
276
277                         if (copy_from_user
278                                         (&info, (ipu_irq_info *) arg,
279                                          sizeof(ipu_irq_info)))
280                                 return -EFAULT;
281
282                         ipu_free_irq(info.irq, info.dev_id);
283                         irq_info[info.irq].irq_pending = 0;
284                 }
285                 break;
286         case IPU_REQUEST_IRQ_STATUS:
287                 {
288                         uint32_t irq;
289                         int __user *argp = (void __user *)arg;
290                         if (get_user(irq, argp))
291                                 return -EFAULT;
292                         ret = ipu_get_irq_status(irq);
293                 }
294                 break;
295         case IPU_REGISTER_GENERIC_ISR:
296                 {
297                         ipu_event_info info;
298                         if (copy_from_user
299                                         (&info, (ipu_event_info *) arg,
300                                          sizeof(ipu_event_info)))
301                                 return -EFAULT;
302
303                         ret =
304                                 ipu_request_irq(info.irq,
305                                         mxc_ipu_generic_handler,
306                                         0, "video_sink", info.dev);
307                         if (ret == 0)
308                                 init_waitqueue_head(&(irq_info[info.irq].waitq));
309                 }
310                 break;
311         case IPU_GET_EVENT:
312                 /* User will have to allocate event_type
313                 structure and pass the pointer in arg */
314                 {
315                         ipu_event_info info;
316                         int r = -1;
317
318                         if (copy_from_user
319                                         (&info, (ipu_event_info *) arg,
320                                          sizeof(ipu_event_info)))
321                                 return -EFAULT;
322
323                         r = get_events(&info);
324                         if (r == -1) {
325                                 if ((file->f_flags & O_NONBLOCK) &&
326                                         (irq_info[info.irq].irq_pending == 0))
327                                         return -EAGAIN;
328                                 wait_event_interruptible_timeout(irq_info[info.irq].waitq,
329                                                 (irq_info[info.irq].irq_pending != 0), 2 * HZ);
330                                 r = get_events(&info);
331                         }
332                         ret = -1;
333                         if (r == 0) {
334                                 if (!copy_to_user((ipu_event_info *) arg,
335                                         &info, sizeof(ipu_event_info)))
336                                         ret = 0;
337                         }
338                 }
339                 break;
340         case IPU_ALOC_MEM:
341                 {
342                         ipu_mem_info info;
343                         if (copy_from_user
344                                         (&info, (ipu_mem_info *) arg,
345                                          sizeof(ipu_mem_info)))
346                                 return -EFAULT;
347
348                         info.vaddr = dma_alloc_coherent(0,
349                                         PAGE_ALIGN(info.size),
350                                         &info.paddr,
351                                         GFP_DMA | GFP_KERNEL);
352                         if (info.vaddr == 0) {
353                                 printk(KERN_ERR "dma alloc failed!\n");
354                                 return -ENOBUFS;
355                         }
356                         if (copy_to_user((ipu_mem_info *) arg, &info,
357                                         sizeof(ipu_mem_info)) > 0)
358                                 return -EFAULT;
359                 }
360                 break;
361         case IPU_FREE_MEM:
362                 {
363                         ipu_mem_info info;
364                         if (copy_from_user
365                                         (&info, (ipu_mem_info *) arg,
366                                          sizeof(ipu_mem_info)))
367                                 return -EFAULT;
368
369                         if (info.vaddr)
370                                 dma_free_coherent(0, PAGE_ALIGN(info.size),
371                                         info.vaddr, info.paddr);
372                         else
373                                 return -EFAULT;
374                 }
375                 break;
376         case IPU_IS_CHAN_BUSY:
377                 {
378                         ipu_channel_t chan;
379                         if (copy_from_user
380                                         (&chan, (ipu_channel_t *)arg,
381                                          sizeof(ipu_channel_t)))
382                                 return -EFAULT;
383
384                         if (ipu_is_channel_busy(chan))
385                                 ret = 1;
386                         else
387                                 ret = 0;
388                 }
389                 break;
390         case IPU_CALC_STRIPES_SIZE:
391                 {
392                         ipu_stripe_parm stripe_parm;
393
394                         if (copy_from_user (&stripe_parm, (ipu_stripe_parm *)arg,
395                                          sizeof(ipu_stripe_parm)))
396                                 return -EFAULT;
397                         ipu_calc_stripes_sizes(stripe_parm.input_width,
398                                                 stripe_parm.output_width,
399                                                 stripe_parm.maximal_stripe_width,
400                                                 stripe_parm.cirr,
401                                                 stripe_parm.equal_stripes,
402                                                 stripe_parm.input_pixelformat,
403                                                 stripe_parm.output_pixelformat,
404                                                 &stripe_parm.left,
405                                                 &stripe_parm.right);
406                         if (copy_to_user((ipu_stripe_parm *) arg, &stripe_parm,
407                                         sizeof(ipu_stripe_parm)) > 0)
408                                 return -EFAULT;
409                 }
410                 break;
411         case IPU_UPDATE_BUF_OFFSET:
412                 {
413                         ipu_buf_offset_parm offset_parm;
414
415                         if (copy_from_user (&offset_parm, (ipu_buf_offset_parm *)arg,
416                                          sizeof(ipu_buf_offset_parm)))
417                                 return -EFAULT;
418                         ret = ipu_update_channel_offset(offset_parm.channel,
419                                                         offset_parm.type,
420                                                         offset_parm.pixel_fmt,
421                                                         offset_parm.width,
422                                                         offset_parm.height,
423                                                         offset_parm.stride,
424                                                         offset_parm.u_offset,
425                                                         offset_parm.v_offset,
426                                                         offset_parm.vertical_offset,
427                                                         offset_parm.horizontal_offset);
428                 }
429                 break;
430         case IPU_CSC_UPDATE:
431                 {
432                         int param[5][3];
433                         ipu_csc_update csc;
434                         if (copy_from_user(&csc, (void *) arg,
435                                            sizeof(ipu_csc_update)))
436                                 return -EFAULT;
437                         if (copy_from_user(&param[0][0], (void *) csc.param,
438                                            sizeof(param)))
439                                 return -EFAULT;
440                         ipu_set_csc_coefficients(csc.channel, param);
441                 }
442                 break;
443         default:
444                 break;
445         }
446         return ret;
447 }
448
449 static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma)
450 {
451         vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
452
453         if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
454                                 vma->vm_end - vma->vm_start,
455                                 vma->vm_page_prot)) {
456                 printk(KERN_ERR
457                                 "mmap failed!\n");
458                 return -ENOBUFS;
459         }
460         return 0;
461 }
462
463 static int mxc_ipu_release(struct inode *inode, struct file *file)
464 {
465         return 0;
466 }
467
468 static struct file_operations mxc_ipu_fops = {
469         .owner = THIS_MODULE,
470         .open = mxc_ipu_open,
471         .mmap = mxc_ipu_mmap,
472         .release = mxc_ipu_release,
473         .unlocked_ioctl = mxc_ipu_ioctl,
474 };
475
476 int register_ipu_device()
477 {
478         int ret = 0;
479         struct device *temp;
480         mxc_ipu_major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops);
481         if (mxc_ipu_major < 0) {
482                 printk(KERN_ERR
483                         "Unable to register Mxc Ipu as a char device\n");
484                 return mxc_ipu_major;
485         }
486
487         mxc_ipu_class = class_create(THIS_MODULE, "mxc_ipu");
488         if (IS_ERR(mxc_ipu_class)) {
489                 printk(KERN_ERR "Unable to create class for Mxc Ipu\n");
490                 ret = PTR_ERR(mxc_ipu_class);
491                 goto err1;
492         }
493
494         temp = device_create(mxc_ipu_class, NULL, MKDEV(mxc_ipu_major, 0),
495                         NULL, "mxc_ipu");
496
497         if (IS_ERR(temp)) {
498                 printk(KERN_ERR "Unable to create class device for Mxc Ipu\n");
499                 ret = PTR_ERR(temp);
500                 goto err2;
501         }
502         spin_lock_init(&event_lock);
503
504         return ret;
505
506 err2:
507         class_destroy(mxc_ipu_class);
508 err1:
509         unregister_chrdev(mxc_ipu_major, "mxc_ipu");
510         return ret;
511
512 }