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