9dbd17be51f7c3d0318a228d3222785f14862ff9
[releases.git] / msm_fence.c
1 /*
2  * Copyright (C) 2013-2016 Red Hat
3  * Author: Rob Clark <robdclark@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published by
7  * the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <linux/fence.h>
19
20 #include "msm_drv.h"
21 #include "msm_fence.h"
22
23
24 struct msm_fence_context *
25 msm_fence_context_alloc(struct drm_device *dev, const char *name)
26 {
27         struct msm_fence_context *fctx;
28
29         fctx = kzalloc(sizeof(*fctx), GFP_KERNEL);
30         if (!fctx)
31                 return ERR_PTR(-ENOMEM);
32
33         fctx->dev = dev;
34         fctx->name = name;
35         fctx->context = fence_context_alloc(1);
36         init_waitqueue_head(&fctx->event);
37         spin_lock_init(&fctx->spinlock);
38
39         return fctx;
40 }
41
42 void msm_fence_context_free(struct msm_fence_context *fctx)
43 {
44         kfree(fctx);
45 }
46
47 static inline bool fence_completed(struct msm_fence_context *fctx, uint32_t fence)
48 {
49         return (int32_t)(fctx->completed_fence - fence) >= 0;
50 }
51
52 /* legacy path for WAIT_FENCE ioctl: */
53 int msm_wait_fence(struct msm_fence_context *fctx, uint32_t fence,
54                 ktime_t *timeout, bool interruptible)
55 {
56         int ret;
57
58         if (fence > fctx->last_fence) {
59                 DRM_ERROR_RATELIMITED("%s: waiting on invalid fence: %u (of %u)\n",
60                                 fctx->name, fence, fctx->last_fence);
61                 return -EINVAL;
62         }
63
64         if (!timeout) {
65                 /* no-wait: */
66                 ret = fence_completed(fctx, fence) ? 0 : -EBUSY;
67         } else {
68                 unsigned long remaining_jiffies = timeout_to_jiffies(timeout);
69
70                 if (interruptible)
71                         ret = wait_event_interruptible_timeout(fctx->event,
72                                 fence_completed(fctx, fence),
73                                 remaining_jiffies);
74                 else
75                         ret = wait_event_timeout(fctx->event,
76                                 fence_completed(fctx, fence),
77                                 remaining_jiffies);
78
79                 if (ret == 0) {
80                         DBG("timeout waiting for fence: %u (completed: %u)",
81                                         fence, fctx->completed_fence);
82                         ret = -ETIMEDOUT;
83                 } else if (ret != -ERESTARTSYS) {
84                         ret = 0;
85                 }
86         }
87
88         return ret;
89 }
90
91 /* called from workqueue */
92 void msm_update_fence(struct msm_fence_context *fctx, uint32_t fence)
93 {
94         spin_lock(&fctx->spinlock);
95         fctx->completed_fence = max(fence, fctx->completed_fence);
96         spin_unlock(&fctx->spinlock);
97
98         wake_up_all(&fctx->event);
99 }
100
101 struct msm_fence {
102         struct msm_fence_context *fctx;
103         struct fence base;
104 };
105
106 static inline struct msm_fence *to_msm_fence(struct fence *fence)
107 {
108         return container_of(fence, struct msm_fence, base);
109 }
110
111 static const char *msm_fence_get_driver_name(struct fence *fence)
112 {
113         return "msm";
114 }
115
116 static const char *msm_fence_get_timeline_name(struct fence *fence)
117 {
118         struct msm_fence *f = to_msm_fence(fence);
119         return f->fctx->name;
120 }
121
122 static bool msm_fence_enable_signaling(struct fence *fence)
123 {
124         return true;
125 }
126
127 static bool msm_fence_signaled(struct fence *fence)
128 {
129         struct msm_fence *f = to_msm_fence(fence);
130         return fence_completed(f->fctx, f->base.seqno);
131 }
132
133 static void msm_fence_release(struct fence *fence)
134 {
135         struct msm_fence *f = to_msm_fence(fence);
136         kfree_rcu(f, base.rcu);
137 }
138
139 static const struct fence_ops msm_fence_ops = {
140         .get_driver_name = msm_fence_get_driver_name,
141         .get_timeline_name = msm_fence_get_timeline_name,
142         .enable_signaling = msm_fence_enable_signaling,
143         .signaled = msm_fence_signaled,
144         .wait = fence_default_wait,
145         .release = msm_fence_release,
146 };
147
148 struct fence *
149 msm_fence_alloc(struct msm_fence_context *fctx)
150 {
151         struct msm_fence *f;
152
153         f = kzalloc(sizeof(*f), GFP_KERNEL);
154         if (!f)
155                 return ERR_PTR(-ENOMEM);
156
157         f->fctx = fctx;
158
159         fence_init(&f->base, &msm_fence_ops, &fctx->spinlock,
160                         fctx->context, ++fctx->last_fence);
161
162         return &f->base;
163 }