blob: dd32e14794364b67707019d02b8d3d0ed02fdfb2 [file] [log] [blame]
Steve Kondikf7652b32013-11-26 15:20:51 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/err.h>
15#include <linux/file.h>
Steve Kondikf7652b32013-11-26 15:20:51 -080016#include <linux/slab.h>
17#include <linux/uaccess.h>
18
Steve Kondikf7652b32013-11-26 15:20:51 -080019#include "kgsl_sync.h"
20
21struct sync_pt *kgsl_sync_pt_create(struct sync_timeline *timeline,
22 unsigned int timestamp)
23{
24 struct sync_pt *pt;
25 pt = sync_pt_create(timeline, (int) sizeof(struct kgsl_sync_pt));
26 if (pt) {
27 struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) pt;
28 kpt->timestamp = timestamp;
29 }
30 return pt;
31}
32
33/*
34 * This should only be called on sync_pts which have been created but
35 * not added to a fence.
36 */
37void kgsl_sync_pt_destroy(struct sync_pt *pt)
38{
39 sync_pt_free(pt);
40}
41
42static struct sync_pt *kgsl_sync_pt_dup(struct sync_pt *pt)
43{
44 struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) pt;
45 return kgsl_sync_pt_create(pt->parent, kpt->timestamp);
46}
47
48static int kgsl_sync_pt_has_signaled(struct sync_pt *pt)
49{
50 struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) pt;
51 struct kgsl_sync_timeline *ktimeline =
52 (struct kgsl_sync_timeline *) pt->parent;
53 unsigned int ts = kpt->timestamp;
54 unsigned int last_ts = ktimeline->last_timestamp;
55 if (timestamp_cmp(last_ts, ts) >= 0) {
56 /* signaled */
57 return 1;
58 }
59 return 0;
60}
61
62static int kgsl_sync_pt_compare(struct sync_pt *a, struct sync_pt *b)
63{
64 struct kgsl_sync_pt *kpt_a = (struct kgsl_sync_pt *) a;
65 struct kgsl_sync_pt *kpt_b = (struct kgsl_sync_pt *) b;
66 unsigned int ts_a = kpt_a->timestamp;
67 unsigned int ts_b = kpt_b->timestamp;
68 return timestamp_cmp(ts_a, ts_b);
69}
70
71struct kgsl_fence_event_priv {
72 struct kgsl_context *context;
73 unsigned int timestamp;
74};
75
76/**
77 * kgsl_fence_event_cb - Event callback for a fence timestamp event
78 * @device - The KGSL device that expired the timestamp
79 * @priv - private data for the event
80 * @context_id - the context id that goes with the timestamp
81 * @timestamp - the timestamp that triggered the event
82 *
83 * Signal a fence following the expiration of a timestamp
84 */
85
86static inline void kgsl_fence_event_cb(struct kgsl_device *device,
87 void *priv, u32 context_id, u32 timestamp, u32 type)
88{
89 struct kgsl_fence_event_priv *ev = priv;
Ethan Chen7b185902014-11-16 16:48:31 -080090 kgsl_sync_timeline_signal(ev->context->timeline, ev->timestamp);
Steve Kondikf7652b32013-11-26 15:20:51 -080091 kgsl_context_put(ev->context);
92 kfree(ev);
93}
94
95/**
96 * kgsl_add_fence_event - Create a new fence event
97 * @device - KGSL device to create the event on
98 * @timestamp - Timestamp to trigger the event
99 * @data - Return fence fd stored in struct kgsl_timestamp_event_fence
100 * @len - length of the fence event
101 * @owner - driver instance that owns this event
102 * @returns 0 on success or error code on error
103 *
104 * Create a fence and register an event to signal the fence when
105 * the timestamp expires
106 */
107
108int kgsl_add_fence_event(struct kgsl_device *device,
109 u32 context_id, u32 timestamp, void __user *data, int len,
110 struct kgsl_device_private *owner)
111{
112 struct kgsl_fence_event_priv *event;
113 struct kgsl_timestamp_event_fence priv;
114 struct kgsl_context *context;
115 struct sync_pt *pt;
116 struct sync_fence *fence = NULL;
117 int ret = -EINVAL;
118
119 if (len != sizeof(priv))
120 return -EINVAL;
121
122 event = kzalloc(sizeof(*event), GFP_KERNEL);
123 if (event == NULL)
124 return -ENOMEM;
125
126 context = kgsl_context_get_owner(owner, context_id);
127
Ethan Chen7b185902014-11-16 16:48:31 -0800128 if (context == NULL)
129 goto fail_pt;
Steve Kondikf7652b32013-11-26 15:20:51 -0800130
131 event->context = context;
132 event->timestamp = timestamp;
133
134 pt = kgsl_sync_pt_create(context->timeline, timestamp);
135 if (pt == NULL) {
136 KGSL_DRV_ERR(device, "kgsl_sync_pt_create failed\n");
137 ret = -ENOMEM;
138 goto fail_pt;
139 }
140
141 fence = sync_fence_create("kgsl-fence", pt);
142 if (fence == NULL) {
143 /* only destroy pt when not added to fence */
144 kgsl_sync_pt_destroy(pt);
145 KGSL_DRV_ERR(device, "sync_fence_create failed\n");
146 ret = -ENOMEM;
147 goto fail_fence;
148 }
149
150 priv.fence_fd = get_unused_fd_flags(0);
151 if (priv.fence_fd < 0) {
152 KGSL_DRV_ERR(device, "invalid fence fd\n");
153 ret = -EINVAL;
154 goto fail_fd;
155 }
156 sync_fence_install(fence, priv.fence_fd);
157
158 if (copy_to_user(data, &priv, sizeof(priv))) {
159 ret = -EFAULT;
160 goto fail_copy_fd;
161 }
162
163 /*
164 * Hold the context ref-count for the event - it will get released in
165 * the callback
166 */
167 ret = kgsl_add_event(device, context_id, timestamp,
168 kgsl_fence_event_cb, event, owner);
169 if (ret)
170 goto fail_event;
171
172 return 0;
173
174fail_event:
175fail_copy_fd:
176 /* clean up sync_fence_install */
177 put_unused_fd(priv.fence_fd);
178fail_fd:
179 /* clean up sync_fence_create */
180 sync_fence_put(fence);
181fail_fence:
182fail_pt:
183 kgsl_context_put(context);
184 kfree(event);
185 return ret;
186}
187
Steve Kondikf7652b32013-11-26 15:20:51 -0800188static void kgsl_sync_timeline_release_obj(struct sync_timeline *sync_timeline)
189{
190 /*
191 * Make sure to free the timeline only after destroy flag is set.
192 * This is to avoid further accessing to the timeline from KGSL and
193 * also to catch any unbalanced kref of timeline.
194 */
195 BUG_ON(sync_timeline && (sync_timeline->destroyed != true));
196}
197static const struct sync_timeline_ops kgsl_sync_timeline_ops = {
198 .driver_name = "kgsl-timeline",
199 .dup = kgsl_sync_pt_dup,
200 .has_signaled = kgsl_sync_pt_has_signaled,
201 .compare = kgsl_sync_pt_compare,
Steve Kondikf7652b32013-11-26 15:20:51 -0800202 .release_obj = kgsl_sync_timeline_release_obj,
203};
204
205int kgsl_sync_timeline_create(struct kgsl_context *context)
206{
207 struct kgsl_sync_timeline *ktimeline;
208
Steve Kondikf7652b32013-11-26 15:20:51 -0800209 context->timeline = sync_timeline_create(&kgsl_sync_timeline_ops,
Ethan Chen7b185902014-11-16 16:48:31 -0800210 (int) sizeof(struct kgsl_sync_timeline), "kgsl-timeline");
Steve Kondikf7652b32013-11-26 15:20:51 -0800211 if (context->timeline == NULL)
212 return -EINVAL;
213
214 ktimeline = (struct kgsl_sync_timeline *) context->timeline;
215 ktimeline->last_timestamp = 0;
Steve Kondikf7652b32013-11-26 15:20:51 -0800216
217 return 0;
218}
219
220void kgsl_sync_timeline_signal(struct sync_timeline *timeline,
221 unsigned int timestamp)
222{
223 struct kgsl_sync_timeline *ktimeline =
224 (struct kgsl_sync_timeline *) timeline;
225
226 if (timestamp_cmp(timestamp, ktimeline->last_timestamp) > 0)
227 ktimeline->last_timestamp = timestamp;
228 sync_timeline_signal(timeline);
229}
230
231void kgsl_sync_timeline_destroy(struct kgsl_context *context)
232{
233 sync_timeline_destroy(context->timeline);
234}
235
236static void kgsl_sync_callback(struct sync_fence *fence,
237 struct sync_fence_waiter *waiter)
238{
239 struct kgsl_sync_fence_waiter *kwaiter =
240 (struct kgsl_sync_fence_waiter *) waiter;
241 kwaiter->func(kwaiter->priv);
242 sync_fence_put(kwaiter->fence);
243 kfree(kwaiter);
244}
245
246struct kgsl_sync_fence_waiter *kgsl_sync_fence_async_wait(int fd,
247 void (*func)(void *priv), void *priv)
248{
249 struct kgsl_sync_fence_waiter *kwaiter;
250 struct sync_fence *fence;
251 int status;
252
253 fence = sync_fence_fdget(fd);
254 if (fence == NULL)
255 return ERR_PTR(-EINVAL);
256
257 /* create the waiter */
Ethan Chen7b185902014-11-16 16:48:31 -0800258 kwaiter = kzalloc(sizeof(*kwaiter), GFP_ATOMIC);
Steve Kondikf7652b32013-11-26 15:20:51 -0800259 if (kwaiter == NULL) {
260 sync_fence_put(fence);
261 return ERR_PTR(-ENOMEM);
262 }
263 kwaiter->fence = fence;
264 kwaiter->priv = priv;
265 kwaiter->func = func;
266 sync_fence_waiter_init((struct sync_fence_waiter *) kwaiter,
267 kgsl_sync_callback);
268
269 /* if status then error or signaled */
270 status = sync_fence_wait_async(fence,
271 (struct sync_fence_waiter *) kwaiter);
272 if (status) {
273 kfree(kwaiter);
274 sync_fence_put(fence);
275 if (status < 0)
276 kwaiter = ERR_PTR(status);
277 else
278 kwaiter = NULL;
279 }
280
281 return kwaiter;
282}
283
284int kgsl_sync_fence_async_cancel(struct kgsl_sync_fence_waiter *kwaiter)
285{
286 if (kwaiter == NULL)
287 return 0;
288
289 if(sync_fence_cancel_async(kwaiter->fence,
290 (struct sync_fence_waiter *) kwaiter) == 0) {
291 sync_fence_put(kwaiter->fence);
292 kfree(kwaiter);
293 return 1;
294 }
295 return 0;
296}