blob: 3ea8f4bb09295c1de628a7349d577eb85413c311 [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
66 /* Check to see if the requested timestamp has already fired */
67
68 if (timestamp_cmp(cur_ts, ts) >= 0) {
69 cb(device, priv, id, cur_ts);
70 return 0;
71 }
72
73 event = kzalloc(sizeof(*event), GFP_KERNEL);
74 if (event == NULL)
75 return -ENOMEM;
76
77 event->context = context;
78 event->timestamp = ts;
79 event->priv = priv;
80 event->func = cb;
81 event->owner = owner;
82
Carter Cooperecbb99a2013-01-08 11:41:02 -070083 /* inc refcount to avoid race conditions in cleanup */
84 if (context)
85 kgsl_context_get(context);
86
Jordan Crouseef3456c2013-01-04 16:46:51 -070087 /* Add the event to either the owning context or the global list */
88
89 if (context) {
90 _add_event_to_list(&context->events, event);
91
92 /*
93 * Add it to the master list of contexts with pending events if
94 * it isn't already there
95 */
96
97 if (list_empty(&context->events_list))
98 list_add_tail(&context->events_list,
99 &device->events_pending_list);
100
101 } else
102 _add_event_to_list(&device->events, event);
103
104 queue_work(device->work_queue, &device->ts_expired_ws);
105 return 0;
106}
107EXPORT_SYMBOL(kgsl_add_event);
108
109/**
110 * kgsl_cancel_events_ctxt - Cancel all events for a context
111 * @device - KGSL device for the events to cancel
112 * @context - context whose events we want to cancel
113 *
114 */
115void kgsl_cancel_events_ctxt(struct kgsl_device *device,
116 struct kgsl_context *context)
117{
118 struct kgsl_event *event, *event_tmp;
119 unsigned int id, cur;
120
121 cur = kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED);
122 id = context->id;
123
124 list_for_each_entry_safe(event, event_tmp, &context->events, list) {
125 /*
126 * "cancel" the events by calling their callback.
127 * Currently, events are used for lock and memory
128 * management, so if the process is dying the right
129 * thing to do is release or free.
130 */
131 if (event->func)
132 event->func(device, event->priv, id, cur);
133
Carter Cooperecbb99a2013-01-08 11:41:02 -0700134 kgsl_context_put(context);
Jordan Crouseef3456c2013-01-04 16:46:51 -0700135 list_del(&event->list);
136 kfree(event);
137 }
138
139 /* Remove ourselves from the master pending list */
140 list_del_init(&context->events_list);
141}
142
143/**
144 * kgsl_cancel_events - Cancel all generic events for a process
145 * @device - KGSL device for the events to cancel
146 * @owner - driver instance that owns the events to cancel
147 *
148 */
149void kgsl_cancel_events(struct kgsl_device *device,
150 void *owner)
151{
152 struct kgsl_event *event, *event_tmp;
153 unsigned int cur;
154
155 cur = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
156
157 list_for_each_entry_safe(event, event_tmp, &device->events, list) {
158 if (event->owner != owner)
159 continue;
160
161 /*
162 * "cancel" the events by calling their callback.
163 * Currently, events are used for lock and memory
164 * management, so if the process is dying the right
165 * thing to do is release or free.
166 */
167 if (event->func)
168 event->func(device, event->priv, KGSL_MEMSTORE_GLOBAL,
169 cur);
170
Carter Cooperecbb99a2013-01-08 11:41:02 -0700171 if (event->context)
172 kgsl_context_put(event->context);
173
Jordan Crouseef3456c2013-01-04 16:46:51 -0700174 list_del(&event->list);
175 kfree(event);
176 }
177}
178EXPORT_SYMBOL(kgsl_cancel_events);
179
180static void _process_event_list(struct kgsl_device *device,
181 struct list_head *head, unsigned int timestamp)
182{
183 struct kgsl_event *event, *tmp;
184 unsigned int id;
185
186 list_for_each_entry_safe(event, tmp, head, list) {
187 if (timestamp_cmp(timestamp, event->timestamp) < 0)
188 break;
189
190 id = event->context ? event->context->id : KGSL_MEMSTORE_GLOBAL;
191
192 if (event->func)
193 event->func(device, event->priv, id, timestamp);
194
Carter Cooperecbb99a2013-01-08 11:41:02 -0700195 if (event->context)
196 kgsl_context_put(event->context);
197
Jordan Crouseef3456c2013-01-04 16:46:51 -0700198 list_del(&event->list);
199 kfree(event);
200 }
201}
202
203static inline void _mark_next_event(struct kgsl_device *device,
204 struct list_head *head)
205{
206 struct kgsl_event *event;
207
208 if (!list_empty(head)) {
209 event = list_first_entry(head, struct kgsl_event, list);
210 device->ftbl->next_event(device, event);
211 }
212}
213
214static int kgsl_process_context_events(struct kgsl_device *device,
215 struct kgsl_context *context)
216{
217 unsigned int timestamp = kgsl_readtimestamp(device, context,
218 KGSL_TIMESTAMP_RETIRED);
219
220 _process_event_list(device, &context->events, timestamp);
221
222 /* Mark the next pending event on the list to fire an interrupt */
223 _mark_next_event(device, &context->events);
224
225 /*
226 * Return 0 if the list is empty so the calling function can remove the
227 * context from the pending list
228 */
229
230 return list_empty(&context->events) ? 0 : 1;
231}
232
233void kgsl_process_events(struct work_struct *work)
234{
235 struct kgsl_device *device = container_of(work, struct kgsl_device,
236 ts_expired_ws);
237 struct kgsl_context *context, *tmp;
238 uint32_t timestamp;
239
240 mutex_lock(&device->mutex);
241
242 /* Process expired global events */
243 timestamp = kgsl_readtimestamp(device, NULL, KGSL_TIMESTAMP_RETIRED);
244 _process_event_list(device, &device->events, timestamp);
245 _mark_next_event(device, &device->events);
246
247 /* Now process all of the pending contexts */
248 list_for_each_entry_safe(context, tmp, &device->events_pending_list,
249 events_list) {
250
251 /*
252 * If kgsl_timestamp_expired_context returns 0 then it no longer
253 * has any pending events and can be removed from the list
254 */
255
256 if (kgsl_process_context_events(device, context) == 0)
257 list_del_init(&context->events_list);
258 }
259
260 mutex_unlock(&device->mutex);
261}
262EXPORT_SYMBOL(kgsl_process_events);