b549f77222c83ceda69352bcabf884c37904d5a6
[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 tegra_dc_ext_pin_window(struct tegra_dc_ext_user *user,
241                                    struct tegra_dc_ext_flip_win *flip_win)
242 {
243         struct tegra_dc_ext *ext = user->ext;
244         struct nvmap_handle_ref *win_dup;
245         struct nvmap_handle *win_handle;
246         u32 id = flip_win->attr.buff_id;
247
248         if (!id) {
249                 flip_win->handle = NULL;
250                 flip_win->phys_addr = -1;
251
252                 return 0;
253         }
254
255         /*
256          * Take a reference to the buffer using the user's nvmap context, to
257          * make sure they have permissions to access it.
258          */
259         win_handle = nvmap_get_handle_id(user->nvmap, id);
260         if (!win_handle)
261                 return -EACCES;
262
263         /*
264          * Duplicate the buffer's handle into the dc_ext driver's nvmap
265          * context, to ensure that the handle won't be freed as long as it is
266          * in use by display.
267          */
268         win_dup = nvmap_duplicate_handle_id(ext->nvmap, id);
269
270         /* Release the reference we took in the user's context above */
271         nvmap_handle_put(win_handle);
272
273         if (IS_ERR(win_dup))
274                 return PTR_ERR(win_dup);
275
276         flip_win->handle = win_dup;
277
278         flip_win->phys_addr = nvmap_pin(ext->nvmap, win_dup);
279         /* XXX this isn't correct for non-pointers... */
280         if (IS_ERR((void *)flip_win->phys_addr)) {
281                 nvmap_free(ext->nvmap, win_dup);
282                 return PTR_ERR((void *)flip_win->phys_addr);
283         }
284
285         return 0;
286 }
287
288 static int lock_windows_for_flip(struct tegra_dc_ext_user *user,
289                                  struct tegra_dc_ext_flip *args)
290 {
291         struct tegra_dc_ext *ext = user->ext;
292         int i;
293
294         for (i = 0; i < DC_N_WINDOWS; i++) {
295                 int index = args->win[i].index;
296                 struct tegra_dc_ext_win *win;
297
298                 if (index < 0)
299                         continue;
300
301                 win = &ext->win[index];
302
303                 mutex_lock(&win->lock);
304
305                 if (win->user != user)
306                         goto fail_unlock;
307         }
308
309         return 0;
310
311 fail_unlock:
312         do {
313                 int index = args->win[i].index;
314
315                 if (index < 0)
316                         continue;
317
318                 mutex_unlock(&ext->win[index].lock);
319         } while (i--);
320
321         return -EACCES;
322 }
323
324 static void unlock_windows_for_flip(struct tegra_dc_ext_user *user,
325                                     struct tegra_dc_ext_flip *args)
326 {
327         struct tegra_dc_ext *ext = user->ext;
328         int i;
329
330         for (i = 0; i < DC_N_WINDOWS; i++) {
331                 int index = args->win[i].index;
332
333                 if (index < 0)
334                         continue;
335
336                 mutex_unlock(&ext->win[index].lock);
337         }
338 }
339
340 static int sanitize_flip_args(struct tegra_dc_ext_user *user,
341                               struct tegra_dc_ext_flip *args)
342 {
343         int i, used_windows = 0;
344
345         for (i = 0; i < DC_N_WINDOWS; i++) {
346                 int index = args->win[i].index;
347
348                 if (index < 0)
349                         continue;
350
351                 if (index >= DC_N_WINDOWS)
352                         return -EINVAL;
353
354                 if (used_windows & BIT(index))
355                         return -EINVAL;
356
357                 used_windows |= BIT(index);
358         }
359
360         if (!used_windows)
361                 return -EINVAL;
362
363         return 0;
364 }
365
366 static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
367                              struct tegra_dc_ext_flip *args)
368 {
369         struct tegra_dc_ext *ext = user->ext;
370         struct tegra_dc_ext_flip_data *data;
371         int work_index;
372         int i, ret = 0;
373
374         if (!user->nvmap)
375                 return -EFAULT;
376
377         ret = sanitize_flip_args(user, args);
378         if (ret)
379                 return ret;
380
381         data = kzalloc(sizeof(*data), GFP_KERNEL);
382         if (!data)
383                 return -ENOMEM;
384
385         INIT_WORK(&data->work, tegra_dc_ext_flip_worker);
386         data->ext = ext;
387
388         for (i = 0; i < DC_N_WINDOWS; i++) {
389                 struct tegra_dc_ext_flip_win *flip_win = &data->win[i];
390                 int index = args->win[i].index;
391
392                 memcpy(&flip_win->attr, &args->win[i], sizeof(flip_win->attr));
393
394                 if (index < 0)
395                         continue;
396
397                 ret = tegra_dc_ext_pin_window(user, flip_win);
398                 if (ret)
399                         goto fail_pin;
400         }
401
402         ret = lock_windows_for_flip(user, args);
403         if (ret)
404                 goto fail_pin;
405
406         for (i = 0; i < DC_N_WINDOWS; i++) {
407                 u32 syncpt_max;
408                 int index = args->win[i].index;
409
410                 if (index < 0)
411                         continue;
412
413                 syncpt_max = tegra_dc_incr_syncpt_max(ext->dc, index);
414
415                 data->win[i].syncpt_max = syncpt_max;
416
417                 /*
418                  * Any of these windows' syncpoints should be equivalent for
419                  * the client, so we just send back an arbitrary one of them
420                  */
421                 args->post_syncpt_val = syncpt_max;
422                 args->post_syncpt_id = tegra_dc_get_syncpt_id(ext->dc, index);
423                 work_index = index;
424         }
425         queue_work(ext->win[work_index].flip_wq, &data->work);
426
427         unlock_windows_for_flip(user, args);
428
429         return 0;
430
431 fail_pin:
432         while (i--) {
433                 if (!data->win[i].handle)
434                         continue;
435
436                 nvmap_unpin(ext->nvmap, data->win[i].handle);
437                 nvmap_free(ext->nvmap, data->win[i].handle);
438         }
439         kfree(data);
440
441         return ret;
442 }
443
444 static long tegra_dc_ioctl(struct file *filp, unsigned int cmd,
445                            unsigned long arg)
446 {
447         void __user *user_arg = (void __user *)arg;
448         struct tegra_dc_ext_user *user = filp->private_data;
449
450         switch (cmd) {
451         case TEGRA_DC_EXT_SET_NVMAP_FD:
452                 return tegra_dc_ext_set_nvmap_fd(user, arg);
453
454         case TEGRA_DC_EXT_GET_WINDOW:
455                 return tegra_dc_ext_get_window(user, arg);
456         case TEGRA_DC_EXT_PUT_WINDOW:
457                 return tegra_dc_ext_put_window(user, arg);
458
459         case TEGRA_DC_EXT_FLIP:
460         {
461                 struct tegra_dc_ext_flip args;
462                 int ret;
463
464                 if (copy_from_user(&args, user_arg, sizeof(args)))
465                         return -EFAULT;
466
467                 ret = tegra_dc_ext_flip(user, &args);
468
469                 if (copy_to_user(user_arg, &args, sizeof(args)))
470                         return -EFAULT;
471
472                 return ret;
473         }
474
475         default:
476                 return -EINVAL;
477         }
478 }
479
480 static int tegra_dc_open(struct inode *inode, struct file *filp)
481 {
482         struct tegra_dc_ext_user *user;
483         struct tegra_dc_ext *ext;
484
485         user = kzalloc(sizeof(*user), GFP_KERNEL);
486         if (!user)
487                 return -ENOMEM;
488
489         ext = container_of(inode->i_cdev, struct tegra_dc_ext, cdev);
490         user->ext = ext;
491
492         filp->private_data = user;
493
494         return 0;
495 }
496
497 static int tegra_dc_release(struct inode *inode, struct file *filp)
498 {
499         struct tegra_dc_ext_user *user = filp->private_data;
500         struct tegra_dc_ext *ext = user->ext;
501         unsigned int i;
502
503         for (i = 0; i < DC_N_WINDOWS; i++) {
504                 if (ext->win[i].user == user)
505                         tegra_dc_ext_put_window(user, i);
506         }
507
508         if (user->nvmap)
509                 nvmap_client_put(user->nvmap);
510
511         kfree(user);
512
513         return 0;
514 }
515
516 static int tegra_dc_ext_setup_windows(struct tegra_dc_ext *ext)
517 {
518         int i, ret;
519
520         for (i = 0; i < ext->dc->n_windows; i++) {
521                 struct tegra_dc_ext_win *win = &ext->win[i];
522                 char name[32];
523
524                 win->ext = ext;
525                 win->idx = i;
526
527                 snprintf(name, sizeof(name), "tegradc.%d/%c",
528                          ext->dc->ndev->id, 'a' + i);
529                 win->flip_wq = create_singlethread_workqueue(name);
530                 if (!win->flip_wq) {
531                         ret = -ENOMEM;
532                         goto cleanup;
533                 }
534
535                 mutex_init(&win->lock);
536         }
537
538         return 0;
539
540 cleanup:
541         while (i--) {
542                 struct tegra_dc_ext_win *win = &ext->win[i];
543                 destroy_workqueue(win->flip_wq);
544         }
545
546         return ret;
547 }
548
549 static const struct file_operations tegra_dc_devops = {
550         .owner =                THIS_MODULE,
551         .open =                 tegra_dc_open,
552         .release =              tegra_dc_release,
553         .unlocked_ioctl =       tegra_dc_ioctl,
554 };
555
556 struct tegra_dc_ext *tegra_dc_ext_register(struct nvhost_device *ndev,
557                                            struct tegra_dc *dc)
558 {
559         int ret;
560         struct tegra_dc_ext *ext;
561
562         ext = kzalloc(sizeof(*ext), GFP_KERNEL);
563         if (!ext)
564                 return ERR_PTR(-ENOMEM);
565
566         BUG_ON(!tegra_dc_ext_devno);
567         cdev_init(&ext->cdev, &tegra_dc_devops);
568         ext->cdev.owner = THIS_MODULE;
569         ret = cdev_add(&ext->cdev, tegra_dc_ext_devno, 1);
570         if (ret) {
571                 dev_err(&ndev->dev, "Failed to create character device\n");
572                 goto cleanup_alloc;
573         }
574
575         ext->dev = device_create(tegra_dc_ext_class,
576                                  &ndev->dev,
577                                  tegra_dc_ext_devno,
578                                  NULL,
579                                  "tegra_dc_%d",
580                                  ndev->id);
581
582         if (IS_ERR(ext->dev)) {
583                 ret = PTR_ERR(ext->dev);
584                 goto cleanup_cdev;
585         }
586
587         ext->dc = dc;
588
589         ext->nvmap = nvmap_create_client(nvmap_dev, "tegra_dc_ext");
590         if (!ext->nvmap) {
591                 ret = -ENOMEM;
592                 goto cleanup_device;
593         }
594
595         ret = tegra_dc_ext_setup_windows(ext);
596         if (ret)
597                 goto cleanup_nvmap;
598
599         tegra_dc_ext_devno++;
600
601         return ext;
602
603 cleanup_nvmap:
604         nvmap_client_put(ext->nvmap);
605
606 cleanup_device:
607         device_del(ext->dev);
608
609 cleanup_cdev:
610         cdev_del(&ext->cdev);
611
612 cleanup_alloc:
613         kfree(ext);
614
615         return ERR_PTR(ret);
616 }
617
618 void tegra_dc_ext_unregister(struct tegra_dc_ext *ext)
619 {
620         int i;
621
622         for (i = 0; i < ext->dc->n_windows; i++) {
623                 struct tegra_dc_ext_win *win = &ext->win[i];
624
625                 flush_workqueue(win->flip_wq);
626                 destroy_workqueue(win->flip_wq);
627         }
628
629         nvmap_client_put(ext->nvmap);
630         device_del(ext->dev);
631         cdev_del(&ext->cdev);
632
633         kfree(ext);
634 }
635
636 int __init tegra_dc_ext_module_init(void)
637 {
638         int ret;
639
640         tegra_dc_ext_class = class_create(THIS_MODULE, "tegra_dc_ext");
641         if (!tegra_dc_ext_class) {
642                 printk(KERN_ERR "tegra_dc_ext: failed to create class\n");
643                 return -ENOMEM;
644         }
645
646         ret = alloc_chrdev_region(&tegra_dc_ext_devno,
647                                   0, TEGRA_MAX_DC,
648                                   "tegra_dc_ext");
649         if (ret)
650                 class_destroy(tegra_dc_ext_class);
651
652         return ret;
653 }
654
655 void __exit tegra_dc_ext_module_exit(void)
656 {
657         unregister_chrdev_region(tegra_dc_ext_devno, TEGRA_MAX_DC);
658         class_destroy(tegra_dc_ext_class);
659 }