blob: 813305add94b2cfd76df85699c27e0caf001606d [file] [log] [blame]
Jordan Crousec77df992013-01-28 11:28:29 -07001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Jeff Boodyfe6c39c2012-08-09 13:54:50 -06002 *
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/file.h>
15#include <linux/slab.h>
16#include <linux/uaccess.h>
17
18#include "kgsl_sync.h"
19
20struct sync_pt *kgsl_sync_pt_create(struct sync_timeline *timeline,
21 unsigned int timestamp)
22{
23 struct sync_pt *pt;
24 pt = sync_pt_create(timeline, (int) sizeof(struct kgsl_sync_pt));
25 if (pt) {
26 struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) pt;
27 kpt->timestamp = timestamp;
28 }
29 return pt;
30}
31
32/*
33 * This should only be called on sync_pts which have been created but
34 * not added to a fence.
35 */
36void kgsl_sync_pt_destroy(struct sync_pt *pt)
37{
38 sync_pt_free(pt);
39}
40
Jeff Boody0fb359a2012-09-20 11:16:47 -060041static struct sync_pt *kgsl_sync_pt_dup(struct sync_pt *pt)
Jeff Boodyfe6c39c2012-08-09 13:54:50 -060042{
43 struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) pt;
44 return kgsl_sync_pt_create(pt->parent, kpt->timestamp);
45}
46
Jeff Boody0fb359a2012-09-20 11:16:47 -060047static int kgsl_sync_pt_has_signaled(struct sync_pt *pt)
Jeff Boodyfe6c39c2012-08-09 13:54:50 -060048{
49 struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) pt;
50 struct kgsl_sync_timeline *ktimeline =
51 (struct kgsl_sync_timeline *) pt->parent;
52 unsigned int ts = kpt->timestamp;
53 unsigned int last_ts = ktimeline->last_timestamp;
54 if (timestamp_cmp(last_ts, ts) >= 0) {
55 /* signaled */
56 return 1;
57 }
58 return 0;
59}
60
Jeff Boody0b120d02012-09-19 21:48:44 -060061static int kgsl_sync_pt_compare(struct sync_pt *a, struct sync_pt *b)
62{
63 struct kgsl_sync_pt *kpt_a = (struct kgsl_sync_pt *) a;
64 struct kgsl_sync_pt *kpt_b = (struct kgsl_sync_pt *) b;
65 unsigned int ts_a = kpt_a->timestamp;
66 unsigned int ts_b = kpt_b->timestamp;
67 return timestamp_cmp(ts_a, ts_b);
68}
69
Jeff Boodyfe6c39c2012-08-09 13:54:50 -060070struct kgsl_fence_event_priv {
71 struct kgsl_context *context;
Jordan Crouse92406e62013-02-26 10:04:31 -070072 unsigned int timestamp;
Jeff Boodyfe6c39c2012-08-09 13:54:50 -060073};
74
75/**
76 * kgsl_fence_event_cb - Event callback for a fence timestamp event
77 * @device - The KGSL device that expired the timestamp
78 * @priv - private data for the event
79 * @context_id - the context id that goes with the timestamp
80 * @timestamp - the timestamp that triggered the event
81 *
82 * Signal a fence following the expiration of a timestamp
83 */
84
85static inline void kgsl_fence_event_cb(struct kgsl_device *device,
86 void *priv, u32 context_id, u32 timestamp)
87{
88 struct kgsl_fence_event_priv *ev = priv;
Jordan Crouse92406e62013-02-26 10:04:31 -070089 kgsl_sync_timeline_signal(ev->context->timeline, ev->timestamp);
Jeff Boodyfe6c39c2012-08-09 13:54:50 -060090 kgsl_context_put(ev->context);
91 kfree(ev);
92}
93
94/**
95 * kgsl_add_fence_event - Create a new fence event
96 * @device - KGSL device to create the event on
97 * @timestamp - Timestamp to trigger the event
98 * @data - Return fence fd stored in struct kgsl_timestamp_event_fence
99 * @len - length of the fence event
100 * @owner - driver instance that owns this event
101 * @returns 0 on success or error code on error
102 *
103 * Create a fence and register an event to signal the fence when
104 * the timestamp expires
105 */
106
107int kgsl_add_fence_event(struct kgsl_device *device,
108 u32 context_id, u32 timestamp, void __user *data, int len,
109 struct kgsl_device_private *owner)
110{
111 struct kgsl_fence_event_priv *event;
112 struct kgsl_timestamp_event_fence priv;
113 struct kgsl_context *context;
114 struct sync_pt *pt;
115 struct sync_fence *fence = NULL;
116 int ret = -EINVAL;
117
118 if (len != sizeof(priv))
119 return -EINVAL;
120
121 context = kgsl_find_context(owner, context_id);
122 if (context == NULL)
123 return -EINVAL;
124
125 event = kzalloc(sizeof(*event), GFP_KERNEL);
126 if (event == NULL)
127 return -ENOMEM;
128 event->context = context;
Jordan Crouse92406e62013-02-26 10:04:31 -0700129 event->timestamp = timestamp;
Jeff Boodyfe6c39c2012-08-09 13:54:50 -0600130 kgsl_context_get(context);
131
132 pt = kgsl_sync_pt_create(context->timeline, timestamp);
133 if (pt == NULL) {
134 KGSL_DRV_ERR(device, "kgsl_sync_pt_create failed\n");
135 ret = -ENOMEM;
136 goto fail_pt;
137 }
138
139 fence = sync_fence_create("kgsl-fence", pt);
140 if (fence == NULL) {
141 /* only destroy pt when not added to fence */
142 kgsl_sync_pt_destroy(pt);
143 KGSL_DRV_ERR(device, "sync_fence_create failed\n");
144 ret = -ENOMEM;
145 goto fail_fence;
146 }
147
148 priv.fence_fd = get_unused_fd_flags(0);
149 if (priv.fence_fd < 0) {
150 KGSL_DRV_ERR(device, "invalid fence fd\n");
151 ret = -EINVAL;
152 goto fail_fd;
153 }
154 sync_fence_install(fence, priv.fence_fd);
155
156 if (copy_to_user(data, &priv, sizeof(priv))) {
157 ret = -EFAULT;
158 goto fail_copy_fd;
159 }
160
161 ret = kgsl_add_event(device, context_id, timestamp,
162 kgsl_fence_event_cb, event, owner);
163 if (ret)
164 goto fail_event;
165
166 return 0;
167
168fail_event:
169fail_copy_fd:
170 /* clean up sync_fence_install */
171 sync_fence_put(fence);
172 put_unused_fd(priv.fence_fd);
173fail_fd:
174 /* clean up sync_fence_create */
175 sync_fence_put(fence);
176fail_fence:
177fail_pt:
178 kgsl_context_put(context);
179 kfree(event);
180 return ret;
181}
182
183static const struct sync_timeline_ops kgsl_sync_timeline_ops = {
Iliyan Malchev745147e2012-12-07 14:57:47 -0800184 .driver_name = "kgsl-timeline",
Jeff Boodyfe6c39c2012-08-09 13:54:50 -0600185 .dup = kgsl_sync_pt_dup,
186 .has_signaled = kgsl_sync_pt_has_signaled,
Jeff Boody0b120d02012-09-19 21:48:44 -0600187 .compare = kgsl_sync_pt_compare,
Jeff Boodyfe6c39c2012-08-09 13:54:50 -0600188};
189
190int kgsl_sync_timeline_create(struct kgsl_context *context)
191{
192 struct kgsl_sync_timeline *ktimeline;
193
194 context->timeline = sync_timeline_create(&kgsl_sync_timeline_ops,
195 (int) sizeof(struct kgsl_sync_timeline), "kgsl-timeline");
196 if (context->timeline == NULL)
197 return -EINVAL;
198
199 ktimeline = (struct kgsl_sync_timeline *) context->timeline;
200 ktimeline->last_timestamp = 0;
201
202 return 0;
203}
204
205void kgsl_sync_timeline_signal(struct sync_timeline *timeline,
206 unsigned int timestamp)
207{
208 struct kgsl_sync_timeline *ktimeline =
209 (struct kgsl_sync_timeline *) timeline;
Jordan Crousec77df992013-01-28 11:28:29 -0700210
Jeff Boody99149a72013-03-29 13:23:31 -0600211 if (timestamp_cmp(timestamp, ktimeline->last_timestamp) > 0)
Jordan Crousec77df992013-01-28 11:28:29 -0700212 ktimeline->last_timestamp = timestamp;
Jeff Boodyfe6c39c2012-08-09 13:54:50 -0600213 sync_timeline_signal(timeline);
214}
215
216void kgsl_sync_timeline_destroy(struct kgsl_context *context)
217{
218 sync_timeline_destroy(context->timeline);
219}