blob: 6798eed52c87f1631a422a265d1032a75573c26b [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
142 list_for_each_entry_safe(event, event_tmp, &context->events, list) {
143 /*
144 * "cancel" the events by calling their callback.
145 * Currently, events are used for lock and memory
146 * management, so if the process is dying the right
147 * thing to do is release or free.
Jordan Crousec77df992013-01-28 11:28:29 -0700148 *
149 * Send the current timestamp so the event knows how far the
150 * system got before the event was canceled
Jordan Crouseef3456c2013-01-04 16:46:51 -0700151 */
Jordan Crousec77df992013-01-28 11:28:29 -0700152
Jordan Crouse2c22e912013-02-28 13:40:56 -0700153 trace_kgsl_fire_event(id, cur, jiffies - event->created);
154
Jordan Crouseef3456c2013-01-04 16:46:51 -0700155 if (event->func)
156 event->func(device, event->priv, id, cur);
157
Carter Cooperecbb99a2013-01-08 11:41:02 -0700158 kgsl_context_put(context);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700159 list_del(&event->list);
160 kfree(event);
Jordan Crouseab601992013-03-05 11:18:20 -0700161
162 kgsl_active_count_put(device);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700163 }
164
165 /* Remove ourselves from the master pending list */
166 list_del_init(&context->events_list);
167}
168
169/**
170 * kgsl_cancel_events - Cancel all generic events for a process
171 * @device - KGSL device for the events to cancel
172 * @owner - driver instance that owns the events to cancel
173 *
174 */
175void kgsl_cancel_events(struct kgsl_device *device,
176 void *owner)
177{
178 struct kgsl_event *event, *event_tmp;
179 unsigned int cur;
180
181 cur = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
182
183 list_for_each_entry_safe(event, event_tmp, &device->events, list) {
184 if (event->owner != owner)
185 continue;
186
187 /*
188 * "cancel" the events by calling their callback.
189 * Currently, events are used for lock and memory
190 * management, so if the process is dying the right
Jordan Crousec77df992013-01-28 11:28:29 -0700191 * thing to do is release or free. Send the current timestamp so
192 * the callback knows how far the GPU made it before things went
193 * explosion
Jordan Crouseef3456c2013-01-04 16:46:51 -0700194 */
Jordan Crouse2c22e912013-02-28 13:40:56 -0700195
196 trace_kgsl_fire_event(KGSL_MEMSTORE_GLOBAL, cur,
197 jiffies - event->created);
198
Jordan Crouseef3456c2013-01-04 16:46:51 -0700199 if (event->func)
200 event->func(device, event->priv, KGSL_MEMSTORE_GLOBAL,
201 cur);
202
Carter Cooperecbb99a2013-01-08 11:41:02 -0700203 if (event->context)
204 kgsl_context_put(event->context);
205
Jordan Crouseef3456c2013-01-04 16:46:51 -0700206 list_del(&event->list);
207 kfree(event);
Jordan Crouseab601992013-03-05 11:18:20 -0700208
209 kgsl_active_count_put(device);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700210 }
211}
212EXPORT_SYMBOL(kgsl_cancel_events);
213
214static void _process_event_list(struct kgsl_device *device,
215 struct list_head *head, unsigned int timestamp)
216{
217 struct kgsl_event *event, *tmp;
218 unsigned int id;
219
220 list_for_each_entry_safe(event, tmp, head, list) {
221 if (timestamp_cmp(timestamp, event->timestamp) < 0)
222 break;
223
224 id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
225
Jordan Crousec77df992013-01-28 11:28:29 -0700226 /*
227 * Send the timestamp of the expired event, not the current
228 * timestamp. This prevents the event handlers from getting
229 * confused if they don't bother comparing the current timetamp
230 * to the timestamp they wanted
231 */
232
Jordan Crouse2c22e912013-02-28 13:40:56 -0700233 trace_kgsl_fire_event(id, event->timestamp,
234 jiffies - event->created);
235
Jordan Crouseef3456c2013-01-04 16:46:51 -0700236 if (event->func)
Jordan Crousec77df992013-01-28 11:28:29 -0700237 event->func(device, event->priv, id, event->timestamp);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700238
Carter Cooperecbb99a2013-01-08 11:41:02 -0700239 if (event->context)
240 kgsl_context_put(event->context);
241
Jordan Crouseef3456c2013-01-04 16:46:51 -0700242 list_del(&event->list);
243 kfree(event);
Jordan Crouseab601992013-03-05 11:18:20 -0700244
245 kgsl_active_count_put(device);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700246 }
247}
248
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700249static inline int _mark_next_event(struct kgsl_device *device,
Jordan Crouseef3456c2013-01-04 16:46:51 -0700250 struct list_head *head)
251{
252 struct kgsl_event *event;
253
254 if (!list_empty(head)) {
255 event = list_first_entry(head, struct kgsl_event, list);
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700256
257 /*
258 * Next event will return 0 if the event was marked or 1 if the
259 * timestamp on the event has passed - return that up a layer
260 */
261
262 return device->ftbl->next_event(device, event);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700263 }
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700264
265 return 0;
Jordan Crouseef3456c2013-01-04 16:46:51 -0700266}
267
268static int kgsl_process_context_events(struct kgsl_device *device,
269 struct kgsl_context *context)
270{
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700271 while (1) {
272 unsigned int timestamp = kgsl_readtimestamp(device, context,
273 KGSL_TIMESTAMP_RETIRED);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700274
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700275 _process_event_list(device, &context->events, timestamp);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700276
Jordan Crouseed8c2dd2013-01-28 16:58:45 -0700277 /*
278 * _mark_next event will return 1 as long as the next event
279 * timestamp has expired - this is to cope with an unavoidable
280 * race condition with the GPU that is still processing events.
281 */
282
283 if (!_mark_next_event(device, &context->events))
284 break;
285 }
Jordan Crouseef3456c2013-01-04 16:46:51 -0700286
287 /*
288 * Return 0 if the list is empty so the calling function can remove the
289 * context from the pending list
290 */
291
292 return list_empty(&context->events) ? 0 : 1;
293}
294
295void kgsl_process_events(struct work_struct *work)
296{
297 struct kgsl_device *device = container_of(work, struct kgsl_device,
298 ts_expired_ws);
299 struct kgsl_context *context, *tmp;
300 uint32_t timestamp;
301
302 mutex_lock(&device->mutex);
303
304 /* Process expired global events */
305 timestamp = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
306 _process_event_list(device, &device->events, timestamp);
307 _mark_next_event(device, &device->events);
308
309 /* Now process all of the pending contexts */
310 list_for_each_entry_safe(context, tmp, &device->events_pending_list,
311 events_list) {
312
313 /*
314 * If kgsl_timestamp_expired_context returns 0 then it no longer
315 * has any pending events and can be removed from the list
316 */
317
318 if (kgsl_process_context_events(device, context) == 0)
319 list_del_init(&context->events_list);
320 }
321
322 mutex_unlock(&device->mutex);
323}
324EXPORT_SYMBOL(kgsl_process_events);