video: tegra: Move pin function to new util file
[linux-2.6.git] / drivers / video / tegra / dc / ext / dev.c
1 /*
2  * drivers/video/tegra/dc/dev.c
3  *
4  * Copyright (C) 2011, NVIDIA Corporation
5  *
6  * Author: Robert Morell <rmorell@nvidia.com>
7  * Some code based on fbdev extensions written by:
8  *      Erik Gilling <konkers@android.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
18  * more details.
19  */
20
21 #include <linux/file.h>
22 #include <linux/fs.h>
23 #include <linux/uaccess.h>
24 #include <linux/slab.h>
25 #include <linux/workqueue.h>
26
27 #include <video/tegra_dc_ext.h>
28
29 #include <mach/dc.h>
30 #include <mach/nvmap.h>
31 #include <mach/tegra_dc_ext.h>
32
33 /* XXX ew */
34 #include "../dc_priv.h"
35 /* XXX ew 2 */
36 #include "../../host/dev.h"
37 /* XXX ew 3 */
38 #include "../../nvmap/nvmap.h"
39 #include "tegra_dc_ext_priv.h"
40
41 static int tegra_dc_ext_devno;
42 static struct class *tegra_dc_ext_class;
43
44 struct tegra_dc_ext_flip_win {
45         struct tegra_dc_ext_flip_windowattr     attr;
46         struct nvmap_handle_ref                 *handle;
47         /* ugh. is this really necessary */
48         dma_addr_t                              phys_addr;
49         u32                                     syncpt_max;
50 };
51
52 struct tegra_dc_ext_flip_data {
53         struct tegra_dc_ext             *ext;
54         struct work_struct              work;
55         struct tegra_dc_ext_flip_win    win[DC_N_WINDOWS];
56 };
57
58 static int tegra_dc_ext_set_nvmap_fd(struct tegra_dc_ext_user *user,
59                                      int fd)
60 {
61         struct nvmap_client *nvmap = NULL;
62
63         if (fd >= 0) {
64                 nvmap = nvmap_client_get_file(fd);
65                 if (IS_ERR(nvmap))
66                         return PTR_ERR(nvmap);
67         }
68
69         if (user->nvmap)
70                 nvmap_client_put(user->nvmap);
71
72         user->nvmap = nvmap;
73
74         return 0;
75 }
76
77 static int tegra_dc_ext_get_window(struct tegra_dc_ext_user *user,
78                                    unsigned int n)
79 {
80         struct tegra_dc_ext *ext = user->ext;
81         struct tegra_dc_ext_win *win;
82         int ret = 0;
83
84         if (n >= DC_N_WINDOWS)
85                 return -EINVAL;
86
87         win = &ext->win[n];
88
89         mutex_lock(&win->lock);
90
91         if (!win->user)
92                 win->user = user;
93         else if (win->user != user)
94                 ret = -EBUSY;
95
96         mutex_unlock(&win->lock);
97
98         return ret;
99 }
100
101 static int tegra_dc_ext_put_window(struct tegra_dc_ext_user *user,
102                                    unsigned int n)
103 {
104         struct tegra_dc_ext *ext = user->ext;
105         struct tegra_dc_ext_win *win;
106         int ret = 0;
107
108         if (n >= DC_N_WINDOWS)
109                 return -EINVAL;
110
111         win = &ext->win[n];
112
113         mutex_lock(&win->lock);
114
115         if (win->user == user) {
116                 flush_workqueue(win->flip_wq);
117                 win->user = 0;
118         } else {
119                 ret = -EACCES;
120         }
121
122         mutex_unlock(&win->lock);
123
124         return ret;
125 }
126
127 void tegra_dc_ext_suspend(struct tegra_dc_ext *ext)
128 {
129         int i;
130
131         for (i = 0; i < ext->dc->n_windows; i++) {
132                 struct tegra_dc_ext_win *win = &ext->win[i];
133
134                 flush_workqueue(win->flip_wq);
135         }
136 }
137
138 static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
139                                struct tegra_dc_win *win,
140                                const struct tegra_dc_ext_flip_win *flip_win)
141 {
142         struct tegra_dc_ext_win *ext_win = &ext->win[win->idx];
143
144         if (flip_win->handle == NULL) {
145                 win->flags = 0;
146                 ext_win->cur_handle = NULL;
147                 return 0;
148         }
149
150         win->flags = TEGRA_WIN_FLAG_ENABLED;
151         if (flip_win->attr.blend == TEGRA_DC_EXT_BLEND_PREMULT)
152                 win->flags |= TEGRA_WIN_FLAG_BLEND_PREMULT;
153         else if (flip_win->attr.blend == TEGRA_DC_EXT_BLEND_COVERAGE)
154                 win->flags |= TEGRA_WIN_FLAG_BLEND_COVERAGE;
155         win->fmt = flip_win->attr.pixformat;
156         win->x = flip_win->attr.x;
157         win->y = flip_win->attr.y;
158         win->w = flip_win->attr.w;
159         win->h = flip_win->attr.h;
160         /* XXX verify that this doesn't go outside display's active region */
161         win->out_x = flip_win->attr.out_x;
162         win->out_y = flip_win->attr.out_y;
163         win->out_w = flip_win->attr.out_w;
164         win->out_h = flip_win->attr.out_h;
165         win->z = flip_win->attr.z;
166         ext_win->cur_handle = flip_win->handle;
167
168         /* XXX verify that this won't read outside of the surface */
169         win->phys_addr = flip_win->phys_addr + flip_win->attr.offset;
170         win->offset_u = flip_win->attr.offset_u + flip_win->attr.offset;
171         win->offset_v = flip_win->attr.offset_v + flip_win->attr.offset;
172         win->stride = flip_win->attr.stride;
173         win->stride_uv = flip_win->attr.stride_uv;
174
175         if ((s32)flip_win->attr.pre_syncpt_id >= 0) {
176                 nvhost_syncpt_wait_timeout(&ext->dc->ndev->host->syncpt,
177                                            flip_win->attr.pre_syncpt_id,
178                                            flip_win->attr.pre_syncpt_val,
179                                            msecs_to_jiffies(500), NULL);
180         }
181
182
183         return 0;
184 }
185
186 static void tegra_dc_ext_flip_worker(struct work_struct *work)
187 {
188         struct tegra_dc_ext_flip_data *data =
189                 container_of(work, struct tegra_dc_ext_flip_data, work);
190         struct tegra_dc_ext *ext = data->ext;
191         struct tegra_dc_win *wins[DC_N_WINDOWS];
192         struct nvmap_handle_ref *unpin_handles[DC_N_WINDOWS];
193         int i, nr_unpin = 0, nr_win = 0;
194
195         for (i = 0; i < DC_N_WINDOWS; i++) {
196                 struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
197                 int index = flip_win->attr.index;
198                 struct tegra_dc_win *win;
199                 struct tegra_dc_ext_win *ext_win;
200
201                 if (index < 0)
202                         continue;
203
204                 win = tegra_dc_get_window(ext->dc, index);
205                 ext_win = &ext->win[index];
206
207                 if ((win->flags & TEGRA_WIN_FLAG_ENABLED) &&
208                     ext_win->cur_handle)
209                         unpin_handles[nr_unpin++] = ext_win->cur_handle;
210
211                 tegra_dc_ext_set_windowattr(ext, win, &data->win[i]);
212
213                 wins[nr_win++] = win;
214         }
215
216         tegra_dc_update_windows(wins, nr_win);
217         /* TODO: implement swapinterval here */
218         tegra_dc_sync_windows(wins, nr_win);
219
220         for (i = 0; i < DC_N_WINDOWS; i++) {
221                 struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
222                 int index = flip_win->attr.index;
223
224                 if (index < 0)
225                         continue;
226
227                 tegra_dc_incr_syncpt_min(ext->dc, index,
228                         flip_win->syncpt_max);
229         }
230
231         /* unpin and deref previous front buffers */
232         for (i = 0; i < nr_unpin; i++) {
233                 nvmap_unpin(ext->nvmap, unpin_handles[i]);
234                 nvmap_free(ext->nvmap, unpin_handles[i]);
235         }
236
237         kfree(data);
238 }
239
240 static int lock_windows_for_flip(struct tegra_dc_ext_user *user,
241                                  struct tegra_dc_ext_flip *args)
242 {
243         struct tegra_dc_ext *ext = user->ext;
244         int i;
245
246         for (i = 0; i < DC_N_WINDOWS; i++) {
247                 int index = args->win[i].index;
248                 struct tegra_dc_ext_win *win;
249
250                 if (index < 0)
251                         continue;
252
253                 win = &ext->win[index];
254
255                 mutex_lock(&win->lock);
256
257                 if (win->user != user)
258                         goto fail_unlock;
259         }
260
261         return 0;
262
263 fail_unlock:
264         do {
265                 int index = args->win[i].index;
266
267                 if (index < 0)
268                         continue;
269
270                 mutex_unlock(&ext->win[index].lock);
271         } while (i--);
272
273         return -EACCES;
274 }
275
276 static void unlock_windows_for_flip(struct tegra_dc_ext_user *user,
277                                     struct tegra_dc_ext_flip *args)
278 {
279         struct tegra_dc_ext *ext = user->ext;
280         int i;
281
282         for (i = 0; i < DC_N_WINDOWS; i++) {
283                 int index = args->win[i].index;
284
285                 if (index < 0)
286                         continue;
287
288                 mutex_unlock(&ext->win[index].lock);
289         }
290 }
291
292 static int sanitize_flip_args(struct tegra_dc_ext_user *user,
293                               struct tegra_dc_ext_flip *args)
294 {
295         int i, used_windows = 0;
296
297         for (i = 0; i < DC_N_WINDOWS; i++) {
298                 int index = args->win[i].index;
299
300                 if (index < 0)
301                         continue;
302
303                 if (index >= DC_N_WINDOWS)
304                         return -EINVAL;
305
306                 if (used_windows & BIT(index))
307                         return -EINVAL;
308
309                 used_windows |= BIT(index);
310         }
311
312         if (!used_windows)
313                 return -EINVAL;
314
315         return 0;
316 }
317
318 static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
319                              struct tegra_dc_ext_flip *args)
320 {
321         struct tegra_dc_ext *ext = user->ext;
322         struct tegra_dc_ext_flip_data *data;
323         int work_index;
324         int i, ret = 0;
325
326         if (!user->nvmap)
327                 return -EFAULT;
328
329         ret = sanitize_flip_args(user, args);
330         if (ret)
331                 return ret;
332
333         data = kzalloc(sizeof(*data), GFP_KERNEL);
334         if (!data)
335                 return -ENOMEM;
336
337         INIT_WORK(&data->work, tegra_dc_ext_flip_worker);
338         data->ext = ext;
339
340         for (i = 0; i < DC_N_WINDOWS; i++) {
341                 struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
342                 int index = args->win[i].index;
343
344                 memcpy(&flip_win->attr, &args->win[i], sizeof(flip_win->attr));
345
346                 if (index < 0)
347                         continue;
348
349                 ret = tegra_dc_ext_pin_window(user, flip_win->attr.buff_id,
350                                               &flip_win->handle,
351                                               &flip_win->phys_addr);
352                 if (ret)
353                         goto fail_pin;
354         }
355
356         ret = lock_windows_for_flip(user, args);
357         if (ret)
358                 goto fail_pin;
359
360         for (i = 0; i < DC_N_WINDOWS; i++) {
361                 u32 syncpt_max;
362                 int index = args->win[i].index;
363
364                 if (index < 0)
365                         continue;
366
367                 syncpt_max = tegra_dc_incr_syncpt_max(ext->dc, index);
368
369                 data->win[i].syncpt_max = syncpt_max;
370
371                 /*
372                  * Any of these windows' syncpoints should be equivalent for
373                  * the client, so we just send back an arbitrary one of them
374                  */
375                 args->post_syncpt_val = syncpt_max;
376                 args->post_syncpt_id = tegra_dc_get_syncpt_id(ext->dc, index);
377                 work_index = index;
378         }
379         queue_work(ext->win[work_index].flip_wq, &data->work);
380
381         unlock_windows_for_flip(user, args);
382
383         return 0;
384
385 fail_pin:
386         while (i--) {
387                 if (!data->win[i].handle)
388                         continue;
389
390                 nvmap_unpin(ext->nvmap, data->win[i].handle);
391                 nvmap_free(ext->nvmap, data->win[i].handle);
392         }
393         kfree(data);
394
395         return ret;
396 }
397
398 static long tegra_dc_ioctl(struct file *filp, unsigned int cmd,
399                            unsigned long arg)
400 {
401         void __user *user_arg = (void __user *)arg;
402         struct tegra_dc_ext_user *user = filp->private_data;
403
404         switch (cmd) {
405         case TEGRA_DC_EXT_SET_NVMAP_FD:
406                 return tegra_dc_ext_set_nvmap_fd(user, arg);
407
408         case TEGRA_DC_EXT_GET_WINDOW:
409                 return tegra_dc_ext_get_window(user, arg);
410         case TEGRA_DC_EXT_PUT_WINDOW:
411                 return tegra_dc_ext_put_window(user, arg);
412
413         case TEGRA_DC_EXT_FLIP:
414         {
415                 struct tegra_dc_ext_flip args;
416                 int ret;
417
418                 if (copy_from_user(&args, user_arg, sizeof(args)))
419                         return -EFAULT;
420
421                 ret = tegra_dc_ext_flip(user, &args);
422
423                 if (copy_to_user(user_arg, &args, sizeof(args)))
424                         return -EFAULT;
425
426                 return ret;
427         }
428
429         default:
430                 return -EINVAL;
431         }
432 }
433
434 static int tegra_dc_open(struct inode *inode, struct file *filp)
435 {
436         struct tegra_dc_ext_user *user;
437         struct tegra_dc_ext *ext;
438
439         user = kzalloc(sizeof(*user), GFP_KERNEL);
440         if (!user)
441                 return -ENOMEM;
442
443         ext = container_of(inode->i_cdev, struct tegra_dc_ext, cdev);
444         user->ext = ext;
445
446         filp->private_data = user;
447
448         return 0;
449 }
450
451 static int tegra_dc_release(struct inode *inode, struct file *filp)
452 {
453         struct tegra_dc_ext_user *user = filp->private_data;
454         struct tegra_dc_ext *ext = user->ext;
455         unsigned int i;
456
457         for (i = 0; i < DC_N_WINDOWS; i++) {
458                 if (ext->win[i].user == user)
459                         tegra_dc_ext_put_window(user, i);
460         }
461
462         if (user->nvmap)
463                 nvmap_client_put(user->nvmap);
464
465         kfree(user);
466
467         return 0;
468 }
469
470 static int tegra_dc_ext_setup_windows(struct tegra_dc_ext *ext)
471 {
472         int i, ret;
473
474         for (i = 0; i < ext->dc->n_windows; i++) {
475                 struct tegra_dc_ext_win *win = &ext->win[i];
476                 char name[32];
477
478                 win->ext = ext;
479                 win->idx = i;
480
481                 snprintf(name, sizeof(name), "tegradc.%d/%c",
482                          ext->dc->ndev->id, 'a' + i);
483                 win->flip_wq = create_singlethread_workqueue(name);
484                 if (!win->flip_wq) {
485                         ret = -ENOMEM;
486                         goto cleanup;
487                 }
488
489                 mutex_init(&win->lock);
490         }
491
492         return 0;
493
494 cleanup:
495         while (i--) {
496                 struct tegra_dc_ext_win *win = &ext->win[i];
497                 destroy_workqueue(win->flip_wq);
498         }
499
500         return ret;
501 }
502
503 static const struct file_operations tegra_dc_devops = {
504         .owner =                THIS_MODULE,
505         .open =                 tegra_dc_open,
506         .release =              tegra_dc_release,
507         .unlocked_ioctl =       tegra_dc_ioctl,
508 };
509
510 struct tegra_dc_ext *tegra_dc_ext_register(struct nvhost_device *ndev,
511                                            struct tegra_dc *dc)
512 {
513         int ret;
514         struct tegra_dc_ext *ext;
515
516         ext = kzalloc(sizeof(*ext), GFP_KERNEL);
517         if (!ext)
518                 return ERR_PTR(-ENOMEM);
519
520         BUG_ON(!tegra_dc_ext_devno);
521         cdev_init(&ext->cdev, &tegra_dc_devops);
522         ext->cdev.owner = THIS_MODULE;
523         ret = cdev_add(&ext->cdev, tegra_dc_ext_devno, 1);
524         if (ret) {
525                 dev_err(&ndev->dev, "Failed to create character device\n");
526                 goto cleanup_alloc;
527         }
528
529         ext->dev = device_create(tegra_dc_ext_class,
530                                  &ndev->dev,
531                                  tegra_dc_ext_devno,
532                                  NULL,
533                                  "tegra_dc_%d",
534                                  ndev->id);
535
536         if (IS_ERR(ext->dev)) {
537                 ret = PTR_ERR(ext->dev);
538                 goto cleanup_cdev;
539         }
540
541         ext->dc = dc;
542
543         ext->nvmap = nvmap_create_client(nvmap_dev, "tegra_dc_ext");
544         if (!ext->nvmap) {
545                 ret = -ENOMEM;
546                 goto cleanup_device;
547         }
548
549         ret = tegra_dc_ext_setup_windows(ext);
550         if (ret)
551                 goto cleanup_nvmap;
552
553         tegra_dc_ext_devno++;
554
555         return ext;
556
557 cleanup_nvmap:
558         nvmap_client_put(ext->nvmap);
559
560 cleanup_device:
561         device_del(ext->dev);
562
563 cleanup_cdev:
564         cdev_del(&ext->cdev);
565
566 cleanup_alloc:
567         kfree(ext);
568
569         return ERR_PTR(ret);
570 }
571
572 void tegra_dc_ext_unregister(struct tegra_dc_ext *ext)
573 {
574         int i;
575
576         for (i = 0; i < ext->dc->n_windows; i++) {
577                 struct tegra_dc_ext_win *win = &ext->win[i];
578
579                 flush_workqueue(win->flip_wq);
580                 destroy_workqueue(win->flip_wq);
581         }
582
583         nvmap_client_put(ext->nvmap);
584         device_del(ext->dev);
585         cdev_del(&ext->cdev);
586
587         kfree(ext);
588 }
589
590 int __init tegra_dc_ext_module_init(void)
591 {
592         int ret;
593
594         tegra_dc_ext_class = class_create(THIS_MODULE, "tegra_dc_ext");
595         if (!tegra_dc_ext_class) {
596                 printk(KERN_ERR "tegra_dc_ext: failed to create class\n");
597                 return -ENOMEM;
598         }
599
600         ret = alloc_chrdev_region(&tegra_dc_ext_devno,
601                                   0, TEGRA_MAX_DC,
602                                   "tegra_dc_ext");
603         if (ret)
604                 class_destroy(tegra_dc_ext_class);
605
606         return ret;
607 }
608
609 void __exit tegra_dc_ext_module_exit(void)
610 {
611         unregister_chrdev_region(tegra_dc_ext_devno, TEGRA_MAX_DC);
612         class_destroy(tegra_dc_ext_class);
613 }