Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 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 |
Hans Verkuil | 6736f4e | 2018-08-27 11:10:38 -0400 | [diff] [blame] | 56 | * @access_count: count the number of request accesses that are in progress |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 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; |
Hans Verkuil | 6736f4e | 2018-08-27 11:10:38 -0400 | [diff] [blame] | 68 | unsigned int access_count; |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 69 | struct list_head objects; |
| 70 | unsigned int num_incomplete_objects; |
Jasmin Jessich | 4212368 | 2018-11-09 16:06:05 -0500 | [diff] [blame] | 71 | wait_queue_head_t poll_wait; |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 72 | spinlock_t lock; |
| 73 | }; |
| 74 | |
| 75 | #ifdef CONFIG_MEDIA_CONTROLLER |
| 76 | |
| 77 | /** |
Hans Verkuil | 6736f4e | 2018-08-27 11:10:38 -0400 | [diff] [blame] | 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 | |
| 121 | /** |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 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 | |
| 192 | /** |
Hans Verkuil | be9d6d4 | 2018-05-21 04:54:28 -0400 | [diff] [blame] | 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 | * |
Hans Verkuil | e79c715 | 2019-03-20 10:31:18 -0400 | [diff] [blame] | 201 | * Return a -EBADR error pointer if requests are not supported |
Hans Verkuil | 34b4147 | 2018-08-23 06:14:12 -0400 | [diff] [blame] | 202 | * by this driver. Return -EINVAL if the request was not found. |
Hans Verkuil | be9d6d4 | 2018-05-21 04:54:28 -0400 | [diff] [blame] | 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 | |
| 210 | /** |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 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 | |
Hans Verkuil | be9d6d4 | 2018-05-21 04:54:28 -0400 | [diff] [blame] | 231 | static inline struct media_request * |
| 232 | media_request_get_by_fd(struct media_device *mdev, int request_fd) |
| 233 | { |
Hans Verkuil | e79c715 | 2019-03-20 10:31:18 -0400 | [diff] [blame] | 234 | return ERR_PTR(-EBADR); |
Hans Verkuil | be9d6d4 | 2018-05-21 04:54:28 -0400 | [diff] [blame] | 235 | } |
| 236 | |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 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 | |
| 302 | /** |
Hans Verkuil | 0ca0e84 | 2018-05-21 04:54:29 -0400 | [diff] [blame] | 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 | |
| 322 | /** |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 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 | |
| 382 | static inline int __must_check |
Hans Verkuil | 6736f4e | 2018-08-27 11:10:38 -0400 | [diff] [blame] | 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 | |
| 392 | static inline int __must_check |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 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 | |
Hans Verkuil | 0ca0e84 | 2018-05-21 04:54:29 -0400 | [diff] [blame] | 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 | |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 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 |