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 |
| 56 | * @objects: List of @struct media_request_object request objects |
| 57 | * @num_incomplete_objects: The number of incomplete objects in the request |
| 58 | * @poll_wait: Wait queue for poll |
| 59 | * @lock: Serializes access to this struct |
| 60 | */ |
| 61 | struct media_request { |
| 62 | struct media_device *mdev; |
| 63 | struct kref kref; |
| 64 | char debug_str[TASK_COMM_LEN + 11]; |
| 65 | enum media_request_state state; |
| 66 | unsigned int updating_count; |
| 67 | struct list_head objects; |
| 68 | unsigned int num_incomplete_objects; |
| 69 | struct wait_queue_head poll_wait; |
| 70 | spinlock_t lock; |
| 71 | }; |
| 72 | |
| 73 | #ifdef CONFIG_MEDIA_CONTROLLER |
| 74 | |
| 75 | /** |
| 76 | * media_request_lock_for_update - Lock the request for updating its objects |
| 77 | * |
| 78 | * @req: The media request |
| 79 | * |
| 80 | * Use before updating a request, i.e. adding, modifying or removing a request |
| 81 | * object in it. A reference to the request must be held during the update. This |
| 82 | * usually takes place automatically through a file handle. Use |
| 83 | * @media_request_unlock_for_update when done. |
| 84 | */ |
| 85 | static inline int __must_check |
| 86 | media_request_lock_for_update(struct media_request *req) |
| 87 | { |
| 88 | unsigned long flags; |
| 89 | int ret = 0; |
| 90 | |
| 91 | spin_lock_irqsave(&req->lock, flags); |
| 92 | if (req->state == MEDIA_REQUEST_STATE_IDLE || |
| 93 | req->state == MEDIA_REQUEST_STATE_UPDATING) { |
| 94 | req->state = MEDIA_REQUEST_STATE_UPDATING; |
| 95 | req->updating_count++; |
| 96 | } else { |
| 97 | ret = -EBUSY; |
| 98 | } |
| 99 | spin_unlock_irqrestore(&req->lock, flags); |
| 100 | |
| 101 | return ret; |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * media_request_unlock_for_update - Unlock a request previously locked for |
| 106 | * update |
| 107 | * |
| 108 | * @req: The media request |
| 109 | * |
| 110 | * Unlock a request that has previously been locked using |
| 111 | * @media_request_lock_for_update. |
| 112 | */ |
| 113 | static inline void media_request_unlock_for_update(struct media_request *req) |
| 114 | { |
| 115 | unsigned long flags; |
| 116 | |
| 117 | spin_lock_irqsave(&req->lock, flags); |
| 118 | WARN_ON(req->updating_count <= 0); |
| 119 | if (!--req->updating_count) |
| 120 | req->state = MEDIA_REQUEST_STATE_IDLE; |
| 121 | spin_unlock_irqrestore(&req->lock, flags); |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * media_request_get - Get the media request |
| 126 | * |
| 127 | * @req: The media request |
| 128 | * |
| 129 | * Get the media request. |
| 130 | */ |
| 131 | static inline void media_request_get(struct media_request *req) |
| 132 | { |
| 133 | kref_get(&req->kref); |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * media_request_put - Put the media request |
| 138 | * |
| 139 | * @req: The media request |
| 140 | * |
| 141 | * Put the media request. The media request will be released |
| 142 | * when the refcount reaches 0. |
| 143 | */ |
| 144 | void media_request_put(struct media_request *req); |
| 145 | |
| 146 | /** |
Hans Verkuil | be9d6d4 | 2018-05-21 04:54:28 -0400 | [diff] [blame^] | 147 | * media_request_get_by_fd - Get a media request by fd |
| 148 | * |
| 149 | * @mdev: Media device this request belongs to |
| 150 | * @request_fd: The file descriptor of the request |
| 151 | * |
| 152 | * Get the request represented by @request_fd that is owned |
| 153 | * by the media device. |
| 154 | * |
| 155 | * Return a -EPERM error pointer if requests are not supported |
| 156 | * by this driver. Return -ENOENT if the request was not found. |
| 157 | * Return the pointer to the request if found: the caller will |
| 158 | * have to call @media_request_put when it finished using the |
| 159 | * request. |
| 160 | */ |
| 161 | struct media_request * |
| 162 | media_request_get_by_fd(struct media_device *mdev, int request_fd); |
| 163 | |
| 164 | /** |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 165 | * media_request_alloc - Allocate the media request |
| 166 | * |
| 167 | * @mdev: Media device this request belongs to |
| 168 | * @alloc_fd: Store the request's file descriptor in this int |
| 169 | * |
| 170 | * Allocated the media request and put the fd in @alloc_fd. |
| 171 | */ |
| 172 | int media_request_alloc(struct media_device *mdev, |
| 173 | int *alloc_fd); |
| 174 | |
| 175 | #else |
| 176 | |
| 177 | static inline void media_request_get(struct media_request *req) |
| 178 | { |
| 179 | } |
| 180 | |
| 181 | static inline void media_request_put(struct media_request *req) |
| 182 | { |
| 183 | } |
| 184 | |
Hans Verkuil | be9d6d4 | 2018-05-21 04:54:28 -0400 | [diff] [blame^] | 185 | static inline struct media_request * |
| 186 | media_request_get_by_fd(struct media_device *mdev, int request_fd) |
| 187 | { |
| 188 | return ERR_PTR(-EPERM); |
| 189 | } |
| 190 | |
Hans Verkuil | 10905d7 | 2018-05-21 04:54:27 -0400 | [diff] [blame] | 191 | #endif |
| 192 | |
| 193 | /** |
| 194 | * struct media_request_object_ops - Media request object operations |
| 195 | * @prepare: Validate and prepare the request object, optional. |
| 196 | * @unprepare: Unprepare the request object, optional. |
| 197 | * @queue: Queue the request object, optional. |
| 198 | * @unbind: Unbind the request object, optional. |
| 199 | * @release: Release the request object, required. |
| 200 | */ |
| 201 | struct media_request_object_ops { |
| 202 | int (*prepare)(struct media_request_object *object); |
| 203 | void (*unprepare)(struct media_request_object *object); |
| 204 | void (*queue)(struct media_request_object *object); |
| 205 | void (*unbind)(struct media_request_object *object); |
| 206 | void (*release)(struct media_request_object *object); |
| 207 | }; |
| 208 | |
| 209 | /** |
| 210 | * struct media_request_object - An opaque object that belongs to a media |
| 211 | * request |
| 212 | * |
| 213 | * @ops: object's operations |
| 214 | * @priv: object's priv pointer |
| 215 | * @req: the request this object belongs to (can be NULL) |
| 216 | * @list: List entry of the object for @struct media_request |
| 217 | * @kref: Reference count of the object, acquire before releasing req->lock |
| 218 | * @completed: If true, then this object was completed. |
| 219 | * |
| 220 | * An object related to the request. This struct is always embedded in |
| 221 | * another struct that contains the actual data for this request object. |
| 222 | */ |
| 223 | struct media_request_object { |
| 224 | const struct media_request_object_ops *ops; |
| 225 | void *priv; |
| 226 | struct media_request *req; |
| 227 | struct list_head list; |
| 228 | struct kref kref; |
| 229 | bool completed; |
| 230 | }; |
| 231 | |
| 232 | #ifdef CONFIG_MEDIA_CONTROLLER |
| 233 | |
| 234 | /** |
| 235 | * media_request_object_get - Get a media request object |
| 236 | * |
| 237 | * @obj: The object |
| 238 | * |
| 239 | * Get a media request object. |
| 240 | */ |
| 241 | static inline void media_request_object_get(struct media_request_object *obj) |
| 242 | { |
| 243 | kref_get(&obj->kref); |
| 244 | } |
| 245 | |
| 246 | /** |
| 247 | * media_request_object_put - Put a media request object |
| 248 | * |
| 249 | * @obj: The object |
| 250 | * |
| 251 | * Put a media request object. Once all references are gone, the |
| 252 | * object's memory is released. |
| 253 | */ |
| 254 | void media_request_object_put(struct media_request_object *obj); |
| 255 | |
| 256 | /** |
| 257 | * media_request_object_init - Initialise a media request object |
| 258 | * |
| 259 | * @obj: The object |
| 260 | * |
| 261 | * Initialise a media request object. The object will be released using the |
| 262 | * release callback of the ops once it has no references (this function |
| 263 | * initialises references to one). |
| 264 | */ |
| 265 | void media_request_object_init(struct media_request_object *obj); |
| 266 | |
| 267 | /** |
| 268 | * media_request_object_bind - Bind a media request object to a request |
| 269 | * |
| 270 | * @req: The media request |
| 271 | * @ops: The object ops for this object |
| 272 | * @priv: A driver-specific priv pointer associated with this object |
| 273 | * @is_buffer: Set to true if the object a buffer object. |
| 274 | * @obj: The object |
| 275 | * |
| 276 | * Bind this object to the request and set the ops and priv values of |
| 277 | * the object so it can be found later with media_request_object_find(). |
| 278 | * |
| 279 | * Every bound object must be unbound or completed by the kernel at some |
| 280 | * point in time, otherwise the request will never complete. When the |
| 281 | * request is released all completed objects will be unbound by the |
| 282 | * request core code. |
| 283 | * |
| 284 | * Buffer objects will be added to the end of the request's object |
| 285 | * list, non-buffer objects will be added to the front of the list. |
| 286 | * This ensures that all buffer objects are at the end of the list |
| 287 | * and that all non-buffer objects that they depend on are processed |
| 288 | * first. |
| 289 | */ |
| 290 | int media_request_object_bind(struct media_request *req, |
| 291 | const struct media_request_object_ops *ops, |
| 292 | void *priv, bool is_buffer, |
| 293 | struct media_request_object *obj); |
| 294 | |
| 295 | /** |
| 296 | * media_request_object_unbind - Unbind a media request object |
| 297 | * |
| 298 | * @obj: The object |
| 299 | * |
| 300 | * Unbind the media request object from the request. |
| 301 | */ |
| 302 | void media_request_object_unbind(struct media_request_object *obj); |
| 303 | |
| 304 | /** |
| 305 | * media_request_object_complete - Mark the media request object as complete |
| 306 | * |
| 307 | * @obj: The object |
| 308 | * |
| 309 | * Mark the media request object as complete. Only bound objects can |
| 310 | * be completed. |
| 311 | */ |
| 312 | void media_request_object_complete(struct media_request_object *obj); |
| 313 | |
| 314 | #else |
| 315 | |
| 316 | static inline int __must_check |
| 317 | media_request_lock_for_update(struct media_request *req) |
| 318 | { |
| 319 | return -EINVAL; |
| 320 | } |
| 321 | |
| 322 | static inline void media_request_unlock_for_update(struct media_request *req) |
| 323 | { |
| 324 | } |
| 325 | |
| 326 | static inline void media_request_object_get(struct media_request_object *obj) |
| 327 | { |
| 328 | } |
| 329 | |
| 330 | static inline void media_request_object_put(struct media_request_object *obj) |
| 331 | { |
| 332 | } |
| 333 | |
| 334 | static inline void media_request_object_init(struct media_request_object *obj) |
| 335 | { |
| 336 | obj->ops = NULL; |
| 337 | obj->req = NULL; |
| 338 | } |
| 339 | |
| 340 | static inline int media_request_object_bind(struct media_request *req, |
| 341 | const struct media_request_object_ops *ops, |
| 342 | void *priv, bool is_buffer, |
| 343 | struct media_request_object *obj) |
| 344 | { |
| 345 | return 0; |
| 346 | } |
| 347 | |
| 348 | static inline void media_request_object_unbind(struct media_request_object *obj) |
| 349 | { |
| 350 | } |
| 351 | |
| 352 | static inline void media_request_object_complete(struct media_request_object *obj) |
| 353 | { |
| 354 | } |
| 355 | |
| 356 | #endif |
| 357 | |
| 358 | #endif |