Commit | Line | Data |
---|---|---|
10905d70 HV |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Media device request objects | |
4 | * | |
5 | * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | |
6 | * Copyright (C) 2018 Intel Corporation | |
7 | * | |
8 | * Author: Hans Verkuil <hans.verkuil@cisco.com> | |
9 | * Author: Sakari Ailus <sakari.ailus@linux.intel.com> | |
10 | */ | |
11 | ||
12 | #ifndef MEDIA_REQUEST_H | |
13 | #define MEDIA_REQUEST_H | |
14 | ||
15 | #include <linux/list.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/spinlock.h> | |
18 | #include <linux/refcount.h> | |
19 | ||
20 | #include <media/media-device.h> | |
21 | ||
22 | /** | |
23 | * enum media_request_state - media request state | |
24 | * | |
25 | * @MEDIA_REQUEST_STATE_IDLE: Idle | |
26 | * @MEDIA_REQUEST_STATE_VALIDATING: Validating the request, no state changes | |
27 | * allowed | |
28 | * @MEDIA_REQUEST_STATE_QUEUED: Queued | |
29 | * @MEDIA_REQUEST_STATE_COMPLETE: Completed, the request is done | |
30 | * @MEDIA_REQUEST_STATE_CLEANING: Cleaning, the request is being re-inited | |
31 | * @MEDIA_REQUEST_STATE_UPDATING: The request is being updated, i.e. | |
32 | * request objects are being added, | |
33 | * modified or removed | |
34 | * @NR_OF_MEDIA_REQUEST_STATE: The number of media request states, used | |
35 | * internally for sanity check purposes | |
36 | */ | |
37 | enum media_request_state { | |
38 | MEDIA_REQUEST_STATE_IDLE, | |
39 | MEDIA_REQUEST_STATE_VALIDATING, | |
40 | MEDIA_REQUEST_STATE_QUEUED, | |
41 | MEDIA_REQUEST_STATE_COMPLETE, | |
42 | MEDIA_REQUEST_STATE_CLEANING, | |
43 | MEDIA_REQUEST_STATE_UPDATING, | |
44 | NR_OF_MEDIA_REQUEST_STATE, | |
45 | }; | |
46 | ||
47 | struct media_request_object; | |
48 | ||
49 | /** | |
50 | * struct media_request - Media device request | |
51 | * @mdev: Media device this request belongs to | |
52 | * @kref: Reference count | |
53 | * @debug_str: Prefix for debug messages (process name:fd) | |
54 | * @state: The state of the request | |
55 | * @updating_count: count the number of request updates that are in progress | |
6736f4e9 | 56 | * @access_count: count the number of request accesses that are in progress |
10905d70 HV |
57 | * @objects: List of @struct media_request_object request objects |
58 | * @num_incomplete_objects: The number of incomplete objects in the request | |
59 | * @poll_wait: Wait queue for poll | |
60 | * @lock: Serializes access to this struct | |
61 | */ | |
62 | struct media_request { | |
63 | struct media_device *mdev; | |
64 | struct kref kref; | |
65 | char debug_str[TASK_COMM_LEN + 11]; | |
66 | enum media_request_state state; | |
67 | unsigned int updating_count; | |
6736f4e9 | 68 | unsigned int access_count; |
10905d70 HV |
69 | struct list_head objects; |
70 | unsigned int num_incomplete_objects; | |
42123687 | 71 | wait_queue_head_t poll_wait; |
10905d70 HV |
72 | spinlock_t lock; |
73 | }; | |
74 | ||
75 | #ifdef CONFIG_MEDIA_CONTROLLER | |
76 | ||
6736f4e9 HV |
77 | /** |
78 | * media_request_lock_for_access - Lock the request to access its objects | |
79 | * | |
80 | * @req: The media request | |
81 | * | |
82 | * Use before accessing a completed request. A reference to the request must | |
83 | * be held during the access. This usually takes place automatically through | |
84 | * a file handle. Use @media_request_unlock_for_access when done. | |
85 | */ | |
86 | static inline int __must_check | |
87 | media_request_lock_for_access(struct media_request *req) | |
88 | { | |
89 | unsigned long flags; | |
90 | int ret = -EBUSY; | |
91 | ||
92 | spin_lock_irqsave(&req->lock, flags); | |
93 | if (req->state == MEDIA_REQUEST_STATE_COMPLETE) { | |
94 | req->access_count++; | |
95 | ret = 0; | |
96 | } | |
97 | spin_unlock_irqrestore(&req->lock, flags); | |
98 | ||
99 | return ret; | |
100 | } | |
101 | ||
102 | /** | |
103 | * media_request_unlock_for_access - Unlock a request previously locked for | |
104 | * access | |
105 | * | |
106 | * @req: The media request | |
107 | * | |
108 | * Unlock a request that has previously been locked using | |
109 | * @media_request_lock_for_access. | |
110 | */ | |
111 | static inline void media_request_unlock_for_access(struct media_request *req) | |
112 | { | |
113 | unsigned long flags; | |
114 | ||
115 | spin_lock_irqsave(&req->lock, flags); | |
116 | if (!WARN_ON(!req->access_count)) | |
117 | req->access_count--; | |
118 | spin_unlock_irqrestore(&req->lock, flags); | |
119 | } | |
120 | ||
10905d70 HV |
121 | /** |
122 | * media_request_lock_for_update - Lock the request for updating its objects | |
123 | * | |
124 | * @req: The media request | |
125 | * | |
126 | * Use before updating a request, i.e. adding, modifying or removing a request | |
127 | * object in it. A reference to the request must be held during the update. This | |
128 | * usually takes place automatically through a file handle. Use | |
129 | * @media_request_unlock_for_update when done. | |
130 | */ | |
131 | static inline int __must_check | |
132 | media_request_lock_for_update(struct media_request *req) | |
133 | { | |
134 | unsigned long flags; | |
135 | int ret = 0; | |
136 | ||
137 | spin_lock_irqsave(&req->lock, flags); | |
138 | if (req->state == MEDIA_REQUEST_STATE_IDLE || | |
139 | req->state == MEDIA_REQUEST_STATE_UPDATING) { | |
140 | req->state = MEDIA_REQUEST_STATE_UPDATING; | |
141 | req->updating_count++; | |
142 | } else { | |
143 | ret = -EBUSY; | |
144 | } | |
145 | spin_unlock_irqrestore(&req->lock, flags); | |
146 | ||
147 | return ret; | |
148 | } | |
149 | ||
150 | /** | |
151 | * media_request_unlock_for_update - Unlock a request previously locked for | |
152 | * update | |
153 | * | |
154 | * @req: The media request | |
155 | * | |
156 | * Unlock a request that has previously been locked using | |
157 | * @media_request_lock_for_update. | |
158 | */ | |
159 | static inline void media_request_unlock_for_update(struct media_request *req) | |
160 | { | |
161 | unsigned long flags; | |
162 | ||
163 | spin_lock_irqsave(&req->lock, flags); | |
164 | WARN_ON(req->updating_count <= 0); | |
165 | if (!--req->updating_count) | |
166 | req->state = MEDIA_REQUEST_STATE_IDLE; | |
167 | spin_unlock_irqrestore(&req->lock, flags); | |
168 | } | |
169 | ||
170 | /** | |
171 | * media_request_get - Get the media request | |
172 | * | |
173 | * @req: The media request | |
174 | * | |
175 | * Get the media request. | |
176 | */ | |
177 | static inline void media_request_get(struct media_request *req) | |
178 | { | |
179 | kref_get(&req->kref); | |
180 | } | |
181 | ||
182 | /** | |
183 | * media_request_put - Put the media request | |
184 | * | |
185 | * @req: The media request | |
186 | * | |
187 | * Put the media request. The media request will be released | |
188 | * when the refcount reaches 0. | |
189 | */ | |
190 | void media_request_put(struct media_request *req); | |
191 | ||
be9d6d4b HV |
192 | /** |
193 | * media_request_get_by_fd - Get a media request by fd | |
194 | * | |
195 | * @mdev: Media device this request belongs to | |
196 | * @request_fd: The file descriptor of the request | |
197 | * | |
198 | * Get the request represented by @request_fd that is owned | |
199 | * by the media device. | |
200 | * | |
e79c7159 | 201 | * Return a -EBADR error pointer if requests are not supported |
34b41472 | 202 | * by this driver. Return -EINVAL if the request was not found. |
be9d6d4b HV |
203 | * Return the pointer to the request if found: the caller will |
204 | * have to call @media_request_put when it finished using the | |
205 | * request. | |
206 | */ | |
207 | struct media_request * | |
208 | media_request_get_by_fd(struct media_device *mdev, int request_fd); | |
209 | ||
10905d70 HV |
210 | /** |
211 | * media_request_alloc - Allocate the media request | |
212 | * | |
213 | * @mdev: Media device this request belongs to | |
214 | * @alloc_fd: Store the request's file descriptor in this int | |
215 | * | |
216 | * Allocated the media request and put the fd in @alloc_fd. | |
217 | */ | |
218 | int media_request_alloc(struct media_device *mdev, | |
219 | int *alloc_fd); | |
220 | ||
221 | #else | |
222 | ||
223 | static inline void media_request_get(struct media_request *req) | |
224 | { | |
225 | } | |
226 | ||
227 | static inline void media_request_put(struct media_request *req) | |
228 | { | |
229 | } | |
230 | ||
be9d6d4b HV |
231 | static inline struct media_request * |
232 | media_request_get_by_fd(struct media_device *mdev, int request_fd) | |
233 | { | |
e79c7159 | 234 | return ERR_PTR(-EBADR); |
be9d6d4b HV |
235 | } |
236 | ||
10905d70 HV |
237 | #endif |
238 | ||
239 | /** | |
240 | * struct media_request_object_ops - Media request object operations | |
241 | * @prepare: Validate and prepare the request object, optional. | |
242 | * @unprepare: Unprepare the request object, optional. | |
243 | * @queue: Queue the request object, optional. | |
244 | * @unbind: Unbind the request object, optional. | |
245 | * @release: Release the request object, required. | |
246 | */ | |
247 | struct media_request_object_ops { | |
248 | int (*prepare)(struct media_request_object *object); | |
249 | void (*unprepare)(struct media_request_object *object); | |
250 | void (*queue)(struct media_request_object *object); | |
251 | void (*unbind)(struct media_request_object *object); | |
252 | void (*release)(struct media_request_object *object); | |
253 | }; | |
254 | ||
255 | /** | |
256 | * struct media_request_object - An opaque object that belongs to a media | |
257 | * request | |
258 | * | |
259 | * @ops: object's operations | |
260 | * @priv: object's priv pointer | |
261 | * @req: the request this object belongs to (can be NULL) | |
262 | * @list: List entry of the object for @struct media_request | |
263 | * @kref: Reference count of the object, acquire before releasing req->lock | |
264 | * @completed: If true, then this object was completed. | |
265 | * | |
266 | * An object related to the request. This struct is always embedded in | |
267 | * another struct that contains the actual data for this request object. | |
268 | */ | |
269 | struct media_request_object { | |
270 | const struct media_request_object_ops *ops; | |
271 | void *priv; | |
272 | struct media_request *req; | |
273 | struct list_head list; | |
274 | struct kref kref; | |
275 | bool completed; | |
276 | }; | |
277 | ||
278 | #ifdef CONFIG_MEDIA_CONTROLLER | |
279 | ||
280 | /** | |
281 | * media_request_object_get - Get a media request object | |
282 | * | |
283 | * @obj: The object | |
284 | * | |
285 | * Get a media request object. | |
286 | */ | |
287 | static inline void media_request_object_get(struct media_request_object *obj) | |
288 | { | |
289 | kref_get(&obj->kref); | |
290 | } | |
291 | ||
292 | /** | |
293 | * media_request_object_put - Put a media request object | |
294 | * | |
295 | * @obj: The object | |
296 | * | |
297 | * Put a media request object. Once all references are gone, the | |
298 | * object's memory is released. | |
299 | */ | |
300 | void media_request_object_put(struct media_request_object *obj); | |
301 | ||
0ca0e844 HV |
302 | /** |
303 | * media_request_object_find - Find an object in a request | |
304 | * | |
305 | * @req: The media request | |
306 | * @ops: Find an object with this ops value | |
307 | * @priv: Find an object with this priv value | |
308 | * | |
309 | * Both @ops and @priv must be non-NULL. | |
310 | * | |
311 | * Returns the object pointer or NULL if not found. The caller must | |
312 | * call media_request_object_put() once it finished using the object. | |
313 | * | |
314 | * Since this function needs to walk the list of objects it takes | |
315 | * the @req->lock spin lock to make this safe. | |
316 | */ | |
317 | struct media_request_object * | |
318 | media_request_object_find(struct media_request *req, | |
319 | const struct media_request_object_ops *ops, | |
320 | void *priv); | |
321 | ||
10905d70 HV |
322 | /** |
323 | * media_request_object_init - Initialise a media request object | |
324 | * | |
325 | * @obj: The object | |
326 | * | |
327 | * Initialise a media request object. The object will be released using the | |
328 | * release callback of the ops once it has no references (this function | |
329 | * initialises references to one). | |
330 | */ | |
331 | void media_request_object_init(struct media_request_object *obj); | |
332 | ||
333 | /** | |
334 | * media_request_object_bind - Bind a media request object to a request | |
335 | * | |
336 | * @req: The media request | |
337 | * @ops: The object ops for this object | |
338 | * @priv: A driver-specific priv pointer associated with this object | |
339 | * @is_buffer: Set to true if the object a buffer object. | |
340 | * @obj: The object | |
341 | * | |
342 | * Bind this object to the request and set the ops and priv values of | |
343 | * the object so it can be found later with media_request_object_find(). | |
344 | * | |
345 | * Every bound object must be unbound or completed by the kernel at some | |
346 | * point in time, otherwise the request will never complete. When the | |
347 | * request is released all completed objects will be unbound by the | |
348 | * request core code. | |
349 | * | |
350 | * Buffer objects will be added to the end of the request's object | |
351 | * list, non-buffer objects will be added to the front of the list. | |
352 | * This ensures that all buffer objects are at the end of the list | |
353 | * and that all non-buffer objects that they depend on are processed | |
354 | * first. | |
355 | */ | |
356 | int media_request_object_bind(struct media_request *req, | |
357 | const struct media_request_object_ops *ops, | |
358 | void *priv, bool is_buffer, | |
359 | struct media_request_object *obj); | |
360 | ||
361 | /** | |
362 | * media_request_object_unbind - Unbind a media request object | |
363 | * | |
364 | * @obj: The object | |
365 | * | |
366 | * Unbind the media request object from the request. | |
367 | */ | |
368 | void media_request_object_unbind(struct media_request_object *obj); | |
369 | ||
370 | /** | |
371 | * media_request_object_complete - Mark the media request object as complete | |
372 | * | |
373 | * @obj: The object | |
374 | * | |
375 | * Mark the media request object as complete. Only bound objects can | |
376 | * be completed. | |
377 | */ | |
378 | void media_request_object_complete(struct media_request_object *obj); | |
379 | ||
380 | #else | |
381 | ||
6736f4e9 HV |
382 | static inline int __must_check |
383 | media_request_lock_for_access(struct media_request *req) | |
384 | { | |
385 | return -EINVAL; | |
386 | } | |
387 | ||
388 | static inline void media_request_unlock_for_access(struct media_request *req) | |
389 | { | |
390 | } | |
391 | ||
10905d70 HV |
392 | static inline int __must_check |
393 | media_request_lock_for_update(struct media_request *req) | |
394 | { | |
395 | return -EINVAL; | |
396 | } | |
397 | ||
398 | static inline void media_request_unlock_for_update(struct media_request *req) | |
399 | { | |
400 | } | |
401 | ||
402 | static inline void media_request_object_get(struct media_request_object *obj) | |
403 | { | |
404 | } | |
405 | ||
406 | static inline void media_request_object_put(struct media_request_object *obj) | |
407 | { | |
408 | } | |
409 | ||
0ca0e844 HV |
410 | static inline struct media_request_object * |
411 | media_request_object_find(struct media_request *req, | |
412 | const struct media_request_object_ops *ops, | |
413 | void *priv) | |
414 | { | |
415 | return NULL; | |
416 | } | |
417 | ||
10905d70 HV |
418 | static inline void media_request_object_init(struct media_request_object *obj) |
419 | { | |
420 | obj->ops = NULL; | |
421 | obj->req = NULL; | |
422 | } | |
423 | ||
424 | static inline int media_request_object_bind(struct media_request *req, | |
425 | const struct media_request_object_ops *ops, | |
426 | void *priv, bool is_buffer, | |
427 | struct media_request_object *obj) | |
428 | { | |
429 | return 0; | |
430 | } | |
431 | ||
432 | static inline void media_request_object_unbind(struct media_request_object *obj) | |
433 | { | |
434 | } | |
435 | ||
436 | static inline void media_request_object_complete(struct media_request_object *obj) | |
437 | { | |
438 | } | |
439 | ||
440 | #endif | |
441 | ||
442 | #endif |