blob: 6c0a6fa8c5f8c60c28d382ca2aee563b31ce0b9c [file] [log] [blame]
Jordan Crouseef3456c2013-01-04 16:46:51 -07001/* Copyright (c) 2011-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/slab.h>
15#include <linux/list.h>
16#include <linux/module.h>
17#include <kgsl_device.h>
18
19static void _add_event_to_list(struct list_head *head, struct kgsl_event *event)
20{
21 struct list_head *n;
22
23 for (n = head->next; n != head; n = n->next) {
24 struct kgsl_event *e =
25 list_entry(n, struct kgsl_event, list);
26
27 if (timestamp_cmp(e->timestamp, event->timestamp) > 0) {
28 list_add(&event->list, n->prev);
29 break;
30 }
31 }
32
33 if (n == head)
34 list_add_tail(&event->list, head);
35}
36
37/**
38 * kgsl_add_event - Add a new timstamp event for the KGSL device
39 * @device - KGSL device for the new event
40 * @id - the context ID that the event should be added to
41 * @ts - the timestamp to trigger the event on
42 * @cb - callback function to call when the timestamp expires
43 * @priv - private data for the specific event type
44 * @owner - driver instance that owns this event
45 *
46 * @returns - 0 on success or error code on failure
47 */
48int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
49 void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
50 void *owner)
51{
52 struct kgsl_event *event;
53 unsigned int cur_ts;
54 struct kgsl_context *context = NULL;
55
56 if (cb == NULL)
57 return -EINVAL;
58
59 if (id != KGSL_MEMSTORE_GLOBAL) {
60 context = idr_find(&device->context_idr, id);
61 if (context == NULL)
62 return -EINVAL;
63 }
64 cur_ts = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
65
Jordan Crousec77df992013-01-28 11:28:29 -070066 /*
67 * Check to see if the requested timestamp has already fired. If it
68 * did do the callback right away. Make sure to send the timestamp that
69 * the event expected instead of the current timestamp because sometimes
70 * the event handlers can get confused.
71 */
Jordan Crouseef3456c2013-01-04 16:46:51 -070072
73 if (timestamp_cmp(cur_ts, ts) >= 0) {
Jordan Crousec77df992013-01-28 11:28:29 -070074 cb(device, priv, id, ts);
Jordan Crouseef3456c2013-01-04 16:46:51 -070075 return 0;
76 }
77
78 event = kzalloc(sizeof(*event), GFP_KERNEL);
79 if (event == NULL)
80 return -ENOMEM;
81
82 event->context = context;
83 event->timestamp = ts;
84 event->priv = priv;
85 event->func = cb;
86 event->owner = owner;
87
Carter Cooperecbb99a2013-01-08 11:41:02 -070088 /* inc refcount to avoid race conditions in cleanup */
89 if (context)
90 kgsl_context_get(context);
91
Jordan Crouseef3456c2013-01-04 16:46:51 -070092 /* Add the event to either the owning context or the global list */
93
94 if (context) {
95 _add_event_to_list(&context->events, event);
96
97 /*
98 * Add it to the master list of contexts with pending events if
99 * it isn't already there
100 */
101
102 if (list_empty(&context->events_list))
103 list_add_tail(&context->events_list,
104 &device->events_pending_list);
105
106 } else
107 _add_event_to_list(&device->events, event);
108
Jordan Crouseab601992013-03-05 11:18:20 -0700109 /*
110 * Increase the active count on the device to avoid going into power
111 * saving modes while events are pending
112 */
113
114 device->active_cnt++;
115
Jordan Crouseef3456c2013-01-04 16:46:51 -0700116 queue_work(device->work_queue, &device->ts_expired_ws);
117 return 0;
118}
119EXPORT_SYMBOL(kgsl_add_event);
120
121/**
122 * kgsl_cancel_events_ctxt - Cancel all events for a context
123 * @device - KGSL device for the events to cancel
124 * @context - context whose events we want to cancel
125 *
126 */
127void kgsl_cancel_events_ctxt(struct kgsl_device *device,
128 struct kgsl_context *context)
129{
130 struct kgsl_event *event, *event_tmp;
131 unsigned int id, cur;
132
133 cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
134 id = context->id;
135
136 list_for_each_entry_safe(event, event_tmp, &context->events, list) {
137 /*
138 * "cancel" the events by calling their callback.
139 * Currently, events are used for lock and memory
140 * management, so if the process is dying the right
141 * thing to do is release or free.
Jordan Crousec77df992013-01-28 11:28:29 -0700142 *
143 * Send the current timestamp so the event knows how far the
144 * system got before the event was canceled
Jordan Crouseef3456c2013-01-04 16:46:51 -0700145 */
Jordan Crousec77df992013-01-28 11:28:29 -0700146
Jordan Crouseef3456c2013-01-04 16:46:51 -0700147 if (event->func)
148 event->func(device, event->priv, id, cur);
149
Carter Cooperecbb99a2013-01-08 11:41:02 -0700150 kgsl_context_put(context);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700151 list_del(&event->list);
152 kfree(event);
Jordan Crouseab601992013-03-05 11:18:20 -0700153
154 kgsl_active_count_put(device);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700155 }
156
157 /* Remove ourselves from the master pending list */
158 list_del_init(&context->events_list);
159}
160
161/**
162 * kgsl_cancel_events - Cancel all generic events for a process
163 * @device - KGSL device for the events to cancel
164 * @owner - driver instance that owns the events to cancel
165 *
166 */
167void kgsl_cancel_events(struct kgsl_device *device,
168 void *owner)
169{
170 struct kgsl_event *event, *event_tmp;
171 unsigned int cur;
172
173 cur = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
174
175 list_for_each_entry_safe(event, event_tmp, &device->events, list) {
176 if (event->owner != owner)
177 continue;
178
179 /*
180 * "cancel" the events by calling their callback.
181 * Currently, events are used for lock and memory
182 * management, so if the process is dying the right
Jordan Crousec77df992013-01-28 11:28:29 -0700183 * thing to do is release or free. Send the current timestamp so
184 * the callback knows how far the GPU made it before things went
185 * explosion
Jordan Crouseef3456c2013-01-04 16:46:51 -0700186 */
187 if (event->func)
188 event->func(device, event->priv, KGSL_MEMSTORE_GLOBAL,
189 cur);
190
Carter Cooperecbb99a2013-01-08 11:41:02 -0700191 if (event->context)
192 kgsl_context_put(event->context);
193
Jordan Crouseef3456c2013-01-04 16:46:51 -0700194 list_del(&event->list);
195 kfree(event);
Jordan Crouseab601992013-03-05 11:18:20 -0700196
197 kgsl_active_count_put(device);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700198 }
199}
200EXPORT_SYMBOL(kgsl_cancel_events);
201
202static void _process_event_list(struct kgsl_device *device,
203 struct list_head *head, unsigned int timestamp)
204{
205 struct kgsl_event *event, *tmp;
206 unsigned int id;
207
208 list_for_each_entry_safe(event, tmp, head, list) {
209 if (timestamp_cmp(timestamp, event->timestamp) < 0)
210 break;
211
212 id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
213
Jordan Crousec77df992013-01-28 11:28:29 -0700214 /*
215 * Send the timestamp of the expired event, not the current
216 * timestamp. This prevents the event handlers from getting
217 * confused if they don't bother comparing the current timetamp
218 * to the timestamp they wanted
219 */
220
Jordan Crouseef3456c2013-01-04 16:46:51 -0700221 if (event->func)
Jordan Crousec77df992013-01-28 11:28:29 -0700222 event->func(device, event->priv, id, event->timestamp);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700223
Carter Cooperecbb99a2013-01-08 11:41:02 -0700224 if (event->context)
225 kgsl_context_put(event->context);
226
Jordan Crouseef3456c2013-01-04 16:46:51 -0700227 list_del(&event->list);
228 kfree(event);
Jordan Crouseab601992013-03-05 11:18:20 -0700229
230 kgsl_active_count_put(device);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700231 }
232}
233
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700234static inline int _mark_next_event(struct kgsl_device *device,
Jordan Crouseef3456c2013-01-04 16:46:51 -0700235 struct list_head *head)
236{
237 struct kgsl_event *event;
238
239 if (!list_empty(head)) {
240 event = list_first_entry(head, struct kgsl_event, list);
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700241
242 /*
243 * Next event will return 0 if the event was marked or 1 if the
244 * timestamp on the event has passed - return that up a layer
245 */
246
247 return device->ftbl->next_event(device, event);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700248 }
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700249
250 return 0;
Jordan Crouseef3456c2013-01-04 16:46:51 -0700251}
252
253static int kgsl_process_context_events(struct kgsl_device *device,
254 struct kgsl_context *context)
255{
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700256 while (1) {
257 unsigned int timestamp = kgsl_readtimestamp(device, context,
258 KGSL_TIMESTAMP_RETIRED);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700259
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700260 _process_event_list(device, &context->events, timestamp);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700261
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700262 /*
263 * _mark_next event will return 1 as long as the next event
264 * timestamp has expired - this is to cope with an unavoidable
265 * race condition with the GPU that is still processing events.
266 */
267
268 if (!_mark_next_event(device, &context->events))
269 break;
270 }
Jordan Crouseef3456c2013-01-04 16:46:51 -0700271
272 /*
273 * Return 0 if the list is empty so the calling function can remove the
274 * context from the pending list
275 */
276
277 return list_empty(&context->events) ? 0 : 1;
278}
279
280void kgsl_process_events(struct work_struct *work)
281{
282 struct kgsl_device *device = container_of(work, struct kgsl_device,
283 ts_expired_ws);
284 struct kgsl_context *context, *tmp;
285 uint32_t timestamp;
286
287 mutex_lock(&device->mutex);
288
289 /* Process expired global events */
290 timestamp = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
291 _process_event_list(device, &device->events, timestamp);
292 _mark_next_event(device, &device->events);
293
294 /* Now process all of the pending contexts */
295 list_for_each_entry_safe(context, tmp, &device->events_pending_list,
296 events_list) {
297
298 /*
299 * If kgsl_timestamp_expired_context returns 0 then it no longer
300 * has any pending events and can be removed from the list
301 */
302
303 if (kgsl_process_context_events(device, context) == 0)
304 list_del_init(&context->events_list);
305 }
306
307 mutex_unlock(&device->mutex);
308}
309EXPORT_SYMBOL(kgsl_process_events);