blob: ce98fa45b31207ec5da2143231a3d3d55506a7e4 [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
109 queue_work(device->work_queue, &device->ts_expired_ws);
110 return 0;
111}
112EXPORT_SYMBOL(kgsl_add_event);
113
114/**
115 * kgsl_cancel_events_ctxt - Cancel all events for a context
116 * @device - KGSL device for the events to cancel
117 * @context - context whose events we want to cancel
118 *
119 */
120void kgsl_cancel_events_ctxt(struct kgsl_device *device,
121 struct kgsl_context *context)
122{
123 struct kgsl_event *event, *event_tmp;
124 unsigned int id, cur;
125
126 cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
127 id = context->id;
128
129 list_for_each_entry_safe(event, event_tmp, &context->events, list) {
130 /*
131 * "cancel" the events by calling their callback.
132 * Currently, events are used for lock and memory
133 * management, so if the process is dying the right
134 * thing to do is release or free.
Jordan Crousec77df992013-01-28 11:28:29 -0700135 *
136 * Send the current timestamp so the event knows how far the
137 * system got before the event was canceled
Jordan Crouseef3456c2013-01-04 16:46:51 -0700138 */
Jordan Crousec77df992013-01-28 11:28:29 -0700139
Jordan Crouseef3456c2013-01-04 16:46:51 -0700140 if (event->func)
141 event->func(device, event->priv, id, cur);
142
Carter Cooperecbb99a2013-01-08 11:41:02 -0700143 kgsl_context_put(context);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700144 list_del(&event->list);
145 kfree(event);
146 }
147
148 /* Remove ourselves from the master pending list */
149 list_del_init(&context->events_list);
150}
151
152/**
153 * kgsl_cancel_events - Cancel all generic events for a process
154 * @device - KGSL device for the events to cancel
155 * @owner - driver instance that owns the events to cancel
156 *
157 */
158void kgsl_cancel_events(struct kgsl_device *device,
159 void *owner)
160{
161 struct kgsl_event *event, *event_tmp;
162 unsigned int cur;
163
164 cur = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
165
166 list_for_each_entry_safe(event, event_tmp, &device->events, list) {
167 if (event->owner != owner)
168 continue;
169
170 /*
171 * "cancel" the events by calling their callback.
172 * Currently, events are used for lock and memory
173 * management, so if the process is dying the right
Jordan Crousec77df992013-01-28 11:28:29 -0700174 * thing to do is release or free. Send the current timestamp so
175 * the callback knows how far the GPU made it before things went
176 * explosion
Jordan Crouseef3456c2013-01-04 16:46:51 -0700177 */
178 if (event->func)
179 event->func(device, event->priv, KGSL_MEMSTORE_GLOBAL,
180 cur);
181
Carter Cooperecbb99a2013-01-08 11:41:02 -0700182 if (event->context)
183 kgsl_context_put(event->context);
184
Jordan Crouseef3456c2013-01-04 16:46:51 -0700185 list_del(&event->list);
186 kfree(event);
187 }
188}
189EXPORT_SYMBOL(kgsl_cancel_events);
190
191static void _process_event_list(struct kgsl_device *device,
192 struct list_head *head, unsigned int timestamp)
193{
194 struct kgsl_event *event, *tmp;
195 unsigned int id;
196
197 list_for_each_entry_safe(event, tmp, head, list) {
198 if (timestamp_cmp(timestamp, event->timestamp) < 0)
199 break;
200
201 id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
202
Jordan Crousec77df992013-01-28 11:28:29 -0700203 /*
204 * Send the timestamp of the expired event, not the current
205 * timestamp. This prevents the event handlers from getting
206 * confused if they don't bother comparing the current timetamp
207 * to the timestamp they wanted
208 */
209
Jordan Crouseef3456c2013-01-04 16:46:51 -0700210 if (event->func)
Jordan Crousec77df992013-01-28 11:28:29 -0700211 event->func(device, event->priv, id, event->timestamp);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700212
Carter Cooperecbb99a2013-01-08 11:41:02 -0700213 if (event->context)
214 kgsl_context_put(event->context);
215
Jordan Crouseef3456c2013-01-04 16:46:51 -0700216 list_del(&event->list);
217 kfree(event);
218 }
219}
220
221static inline void _mark_next_event(struct kgsl_device *device,
222 struct list_head *head)
223{
224 struct kgsl_event *event;
225
226 if (!list_empty(head)) {
227 event = list_first_entry(head, struct kgsl_event, list);
228 device->ftbl->next_event(device, event);
229 }
230}
231
232static int kgsl_process_context_events(struct kgsl_device *device,
233 struct kgsl_context *context)
234{
235 unsigned int timestamp = kgsl_readtimestamp(device, context,
236 KGSL_TIMESTAMP_RETIRED);
237
238 _process_event_list(device, &context->events, timestamp);
239
240 /* Mark the next pending event on the list to fire an interrupt */
241 _mark_next_event(device, &context->events);
242
243 /*
244 * Return 0 if the list is empty so the calling function can remove the
245 * context from the pending list
246 */
247
248 return list_empty(&context->events) ? 0 : 1;
249}
250
251void kgsl_process_events(struct work_struct *work)
252{
253 struct kgsl_device *device = container_of(work, struct kgsl_device,
254 ts_expired_ws);
255 struct kgsl_context *context, *tmp;
256 uint32_t timestamp;
257
258 mutex_lock(&device->mutex);
259
260 /* Process expired global events */
261 timestamp = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
262 _process_event_list(device, &device->events, timestamp);
263 _mark_next_event(device, &device->events);
264
265 /* Now process all of the pending contexts */
266 list_for_each_entry_safe(context, tmp, &device->events_pending_list,
267 events_list) {
268
269 /*
270 * If kgsl_timestamp_expired_context returns 0 then it no longer
271 * has any pending events and can be removed from the list
272 */
273
274 if (kgsl_process_context_events(device, context) == 0)
275 list_del_init(&context->events_list);
276 }
277
278 mutex_unlock(&device->mutex);
279}
280EXPORT_SYMBOL(kgsl_process_events);