blob: bab30f104064efb27fcfa83689fb1c8d7366afe0 [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
Jordan Crouse2c22e912013-02-28 13:40:56 -070019#include "kgsl_trace.h"
20
Jordan Crouseef3456c2013-01-04 16:46:51 -070021static void _add_event_to_list(struct list_head *head, struct kgsl_event *event)
22{
23 struct list_head *n;
24
25 for (n = head->next; n != head; n = n->next) {
26 struct kgsl_event *e =
27 list_entry(n, struct kgsl_event, list);
28
29 if (timestamp_cmp(e->timestamp, event->timestamp) > 0) {
30 list_add(&event->list, n->prev);
31 break;
32 }
33 }
34
35 if (n == head)
36 list_add_tail(&event->list, head);
37}
38
39/**
40 * kgsl_add_event - Add a new timstamp event for the KGSL device
41 * @device - KGSL device for the new event
42 * @id - the context ID that the event should be added to
43 * @ts - the timestamp to trigger the event on
44 * @cb - callback function to call when the timestamp expires
45 * @priv - private data for the specific event type
46 * @owner - driver instance that owns this event
47 *
48 * @returns - 0 on success or error code on failure
49 */
50int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
51 void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
52 void *owner)
53{
54 struct kgsl_event *event;
55 unsigned int cur_ts;
56 struct kgsl_context *context = NULL;
57
58 if (cb == NULL)
59 return -EINVAL;
60
61 if (id != KGSL_MEMSTORE_GLOBAL) {
62 context = idr_find(&device->context_idr, id);
63 if (context == NULL)
64 return -EINVAL;
65 }
66 cur_ts = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
67
Jordan Crousec77df992013-01-28 11:28:29 -070068 /*
69 * Check to see if the requested timestamp has already fired. If it
70 * did do the callback right away. Make sure to send the timestamp that
71 * the event expected instead of the current timestamp because sometimes
72 * the event handlers can get confused.
73 */
Jordan Crouseef3456c2013-01-04 16:46:51 -070074
75 if (timestamp_cmp(cur_ts, ts) >= 0) {
Jordan Crouse2c22e912013-02-28 13:40:56 -070076 trace_kgsl_fire_event(id, ts, 0);
Jordan Crousec77df992013-01-28 11:28:29 -070077 cb(device, priv, id, ts);
Jordan Crouseef3456c2013-01-04 16:46:51 -070078 return 0;
79 }
80
81 event = kzalloc(sizeof(*event), GFP_KERNEL);
82 if (event == NULL)
83 return -ENOMEM;
84
85 event->context = context;
86 event->timestamp = ts;
87 event->priv = priv;
88 event->func = cb;
89 event->owner = owner;
Jordan Crouse2c22e912013-02-28 13:40:56 -070090 event->created = jiffies;
91
92 trace_kgsl_register_event(id, ts);
Jordan Crouseef3456c2013-01-04 16:46:51 -070093
Carter Cooperecbb99a2013-01-08 11:41:02 -070094 /* inc refcount to avoid race conditions in cleanup */
95 if (context)
96 kgsl_context_get(context);
97
Jordan Crouseef3456c2013-01-04 16:46:51 -070098 /* Add the event to either the owning context or the global list */
99
100 if (context) {
101 _add_event_to_list(&context->events, event);
102
103 /*
104 * Add it to the master list of contexts with pending events if
105 * it isn't already there
106 */
107
108 if (list_empty(&context->events_list))
109 list_add_tail(&context->events_list,
110 &device->events_pending_list);
111
112 } else
113 _add_event_to_list(&device->events, event);
114
Jordan Crouseab601992013-03-05 11:18:20 -0700115 /*
116 * Increase the active count on the device to avoid going into power
117 * saving modes while events are pending
118 */
119
120 device->active_cnt++;
121
Jordan Crouseef3456c2013-01-04 16:46:51 -0700122 queue_work(device->work_queue, &device->ts_expired_ws);
123 return 0;
124}
125EXPORT_SYMBOL(kgsl_add_event);
126
127/**
128 * kgsl_cancel_events_ctxt - Cancel all events for a context
129 * @device - KGSL device for the events to cancel
130 * @context - context whose events we want to cancel
131 *
132 */
133void kgsl_cancel_events_ctxt(struct kgsl_device *device,
134 struct kgsl_context *context)
135{
136 struct kgsl_event *event, *event_tmp;
137 unsigned int id, cur;
138
139 cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
140 id = context->id;
141
Lynus Vaz139a5292013-05-07 14:02:33 +0530142 /*
143 * Increment the refcount to avoid freeing the context while
144 * cancelling its events
145 */
146 kgsl_context_get(context);
147
148 /* Remove ourselves from the master pending list */
149 list_del_init(&context->events_list);
150
Jordan Crouseef3456c2013-01-04 16:46:51 -0700151 list_for_each_entry_safe(event, event_tmp, &context->events, list) {
152 /*
153 * "cancel" the events by calling their callback.
154 * Currently, events are used for lock and memory
155 * management, so if the process is dying the right
156 * thing to do is release or free.
Jordan Crousec77df992013-01-28 11:28:29 -0700157 *
158 * Send the current timestamp so the event knows how far the
159 * system got before the event was canceled
Jordan Crouseef3456c2013-01-04 16:46:51 -0700160 */
Lynus Vazf5e67a22013-03-04 18:38:33 +0530161 list_del(&event->list);
Jordan Crousec77df992013-01-28 11:28:29 -0700162
Jordan Crouse2c22e912013-02-28 13:40:56 -0700163 trace_kgsl_fire_event(id, cur, jiffies - event->created);
164
Jordan Crouseef3456c2013-01-04 16:46:51 -0700165 if (event->func)
166 event->func(device, event->priv, id, cur);
167
Carter Cooperecbb99a2013-01-08 11:41:02 -0700168 kgsl_context_put(context);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700169 kfree(event);
Jordan Crouseab601992013-03-05 11:18:20 -0700170
171 kgsl_active_count_put(device);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700172 }
Lynus Vaz139a5292013-05-07 14:02:33 +0530173 kgsl_context_put(context);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700174}
175
176/**
177 * kgsl_cancel_events - Cancel all generic events for a process
178 * @device - KGSL device for the events to cancel
179 * @owner - driver instance that owns the events to cancel
180 *
181 */
182void kgsl_cancel_events(struct kgsl_device *device,
183 void *owner)
184{
185 struct kgsl_event *event, *event_tmp;
186 unsigned int cur;
187
188 cur = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
189
190 list_for_each_entry_safe(event, event_tmp, &device->events, list) {
191 if (event->owner != owner)
192 continue;
193
194 /*
195 * "cancel" the events by calling their callback.
196 * Currently, events are used for lock and memory
197 * management, so if the process is dying the right
Jordan Crousec77df992013-01-28 11:28:29 -0700198 * thing to do is release or free. Send the current timestamp so
199 * the callback knows how far the GPU made it before things went
200 * explosion
Jordan Crouseef3456c2013-01-04 16:46:51 -0700201 */
Lynus Vazf5e67a22013-03-04 18:38:33 +0530202 list_del(&event->list);
Jordan Crouse2c22e912013-02-28 13:40:56 -0700203
204 trace_kgsl_fire_event(KGSL_MEMSTORE_GLOBAL, cur,
205 jiffies - event->created);
206
Jordan Crouseef3456c2013-01-04 16:46:51 -0700207 if (event->func)
208 event->func(device, event->priv, KGSL_MEMSTORE_GLOBAL,
209 cur);
210
Carter Cooperecbb99a2013-01-08 11:41:02 -0700211 if (event->context)
212 kgsl_context_put(event->context);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700213 kfree(event);
Jordan Crouseab601992013-03-05 11:18:20 -0700214
215 kgsl_active_count_put(device);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700216 }
217}
218EXPORT_SYMBOL(kgsl_cancel_events);
219
220static void _process_event_list(struct kgsl_device *device,
221 struct list_head *head, unsigned int timestamp)
222{
223 struct kgsl_event *event, *tmp;
224 unsigned int id;
225
226 list_for_each_entry_safe(event, tmp, head, list) {
227 if (timestamp_cmp(timestamp, event->timestamp) < 0)
228 break;
229
230 id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
231
Jordan Crousec77df992013-01-28 11:28:29 -0700232 /*
233 * Send the timestamp of the expired event, not the current
234 * timestamp. This prevents the event handlers from getting
235 * confused if they don't bother comparing the current timetamp
236 * to the timestamp they wanted
237 */
Lynus Vazf5e67a22013-03-04 18:38:33 +0530238 list_del(&event->list);
Jordan Crousec77df992013-01-28 11:28:29 -0700239
Jordan Crouse2c22e912013-02-28 13:40:56 -0700240 trace_kgsl_fire_event(id, event->timestamp,
241 jiffies - event->created);
242
Jordan Crouseef3456c2013-01-04 16:46:51 -0700243 if (event->func)
Jordan Crousec77df992013-01-28 11:28:29 -0700244 event->func(device, event->priv, id, event->timestamp);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700245
Carter Cooperecbb99a2013-01-08 11:41:02 -0700246 if (event->context)
247 kgsl_context_put(event->context);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700248 kfree(event);
Jordan Crouseab601992013-03-05 11:18:20 -0700249
250 kgsl_active_count_put(device);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700251 }
252}
253
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700254static inline int _mark_next_event(struct kgsl_device *device,
Jordan Crouseef3456c2013-01-04 16:46:51 -0700255 struct list_head *head)
256{
257 struct kgsl_event *event;
258
259 if (!list_empty(head)) {
260 event = list_first_entry(head, struct kgsl_event, list);
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700261
262 /*
263 * Next event will return 0 if the event was marked or 1 if the
264 * timestamp on the event has passed - return that up a layer
265 */
266
267 return device->ftbl->next_event(device, event);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700268 }
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700269
270 return 0;
Jordan Crouseef3456c2013-01-04 16:46:51 -0700271}
272
273static int kgsl_process_context_events(struct kgsl_device *device,
274 struct kgsl_context *context)
275{
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700276 while (1) {
277 unsigned int timestamp = kgsl_readtimestamp(device, context,
278 KGSL_TIMESTAMP_RETIRED);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700279
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700280 _process_event_list(device, &context->events, timestamp);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700281
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700282 /*
283 * _mark_next event will return 1 as long as the next event
284 * timestamp has expired - this is to cope with an unavoidable
285 * race condition with the GPU that is still processing events.
286 */
287
288 if (!_mark_next_event(device, &context->events))
289 break;
290 }
Jordan Crouseef3456c2013-01-04 16:46:51 -0700291
292 /*
293 * Return 0 if the list is empty so the calling function can remove the
294 * context from the pending list
295 */
296
297 return list_empty(&context->events) ? 0 : 1;
298}
299
300void kgsl_process_events(struct work_struct *work)
301{
302 struct kgsl_device *device = container_of(work, struct kgsl_device,
303 ts_expired_ws);
304 struct kgsl_context *context, *tmp;
305 uint32_t timestamp;
306
307 mutex_lock(&device->mutex);
308
309 /* Process expired global events */
310 timestamp = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
311 _process_event_list(device, &device->events, timestamp);
312 _mark_next_event(device, &device->events);
313
314 /* Now process all of the pending contexts */
315 list_for_each_entry_safe(context, tmp, &device->events_pending_list,
316 events_list) {
317
318 /*
Lynus Vaz139a5292013-05-07 14:02:33 +0530319 * Increment the refcount to make sure that the list_del_init
320 * is called with a valid context's list
321 */
322 kgsl_context_get(context);
323 /*
Jordan Crouseef3456c2013-01-04 16:46:51 -0700324 * If kgsl_timestamp_expired_context returns 0 then it no longer
325 * has any pending events and can be removed from the list
326 */
327
328 if (kgsl_process_context_events(device, context) == 0)
329 list_del_init(&context->events_list);
Lynus Vaz139a5292013-05-07 14:02:33 +0530330 kgsl_context_put(context);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700331 }
332
333 mutex_unlock(&device->mutex);
334}
335EXPORT_SYMBOL(kgsl_process_events);