1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. |
4 | * Author: Brian Starkey <brian.starkey@arm.com> |
5 | * |
6 | * This program is free software and is provided to you under the terms of the |
7 | * GNU General Public License version 2 as published by the Free Software |
8 | * Foundation, and any use by you of this program is subject to the terms |
9 | * of such GNU licence. |
10 | */ |
11 | |
12 | #include <linux/dma-fence.h> |
13 | |
14 | #include <drm/drm_crtc.h> |
15 | #include <drm/drm_device.h> |
16 | #include <drm/drm_drv.h> |
17 | #include <drm/drm_framebuffer.h> |
18 | #include <drm/drm_modeset_helper_vtables.h> |
19 | #include <drm/drm_property.h> |
20 | #include <drm/drm_writeback.h> |
21 | |
22 | /** |
23 | * DOC: overview |
24 | * |
25 | * Writeback connectors are used to expose hardware which can write the output |
26 | * from a CRTC to a memory buffer. They are used and act similarly to other |
27 | * types of connectors, with some important differences: |
28 | * |
29 | * * Writeback connectors don't provide a way to output visually to the user. |
30 | * |
31 | * * Writeback connectors are visible to userspace only when the client sets |
32 | * DRM_CLIENT_CAP_WRITEBACK_CONNECTORS. |
33 | * |
34 | * * Writeback connectors don't have EDID. |
35 | * |
36 | * A framebuffer may only be attached to a writeback connector when the |
37 | * connector is attached to a CRTC. The WRITEBACK_FB_ID property which sets the |
38 | * framebuffer applies only to a single commit (see below). A framebuffer may |
39 | * not be attached while the CRTC is off. |
40 | * |
41 | * Unlike with planes, when a writeback framebuffer is removed by userspace DRM |
42 | * makes no attempt to remove it from active use by the connector. This is |
43 | * because no method is provided to abort a writeback operation, and in any |
44 | * case making a new commit whilst a writeback is ongoing is undefined (see |
45 | * WRITEBACK_OUT_FENCE_PTR below). As soon as the current writeback is finished, |
46 | * the framebuffer will automatically no longer be in active use. As it will |
47 | * also have already been removed from the framebuffer list, there will be no |
48 | * way for any userspace application to retrieve a reference to it in the |
49 | * intervening period. |
50 | * |
51 | * Writeback connectors have some additional properties, which userspace |
52 | * can use to query and control them: |
53 | * |
54 | * "WRITEBACK_FB_ID": |
55 | * Write-only object property storing a DRM_MODE_OBJECT_FB: it stores the |
56 | * framebuffer to be written by the writeback connector. This property is |
57 | * similar to the FB_ID property on planes, but will always read as zero |
58 | * and is not preserved across commits. |
59 | * Userspace must set this property to an output buffer every time it |
60 | * wishes the buffer to get filled. |
61 | * |
62 | * "WRITEBACK_PIXEL_FORMATS": |
63 | * Immutable blob property to store the supported pixel formats table. The |
64 | * data is an array of u32 DRM_FORMAT_* fourcc values. |
65 | * Userspace can use this blob to find out what pixel formats are supported |
66 | * by the connector's writeback engine. |
67 | * |
68 | * "WRITEBACK_OUT_FENCE_PTR": |
69 | * Userspace can use this property to provide a pointer for the kernel to |
70 | * fill with a sync_file file descriptor, which will signal once the |
71 | * writeback is finished. The value should be the address of a 32-bit |
72 | * signed integer, cast to a u64. |
73 | * Userspace should wait for this fence to signal before making another |
74 | * commit affecting any of the same CRTCs, Planes or Connectors. |
75 | * **Failure to do so will result in undefined behaviour.** |
76 | * For this reason it is strongly recommended that all userspace |
77 | * applications making use of writeback connectors *always* retrieve an |
78 | * out-fence for the commit and use it appropriately. |
79 | * From userspace, this property will always read as zero. |
80 | */ |
81 | |
82 | #define fence_to_wb_connector(x) container_of(x->lock, \ |
83 | struct drm_writeback_connector, \ |
84 | fence_lock) |
85 | |
86 | static const char *drm_writeback_fence_get_driver_name(struct dma_fence *fence) |
87 | { |
88 | struct drm_writeback_connector *wb_connector = |
89 | fence_to_wb_connector(fence); |
90 | |
91 | return wb_connector->base.dev->driver->name; |
92 | } |
93 | |
94 | static const char * |
95 | drm_writeback_fence_get_timeline_name(struct dma_fence *fence) |
96 | { |
97 | struct drm_writeback_connector *wb_connector = |
98 | fence_to_wb_connector(fence); |
99 | |
100 | return wb_connector->timeline_name; |
101 | } |
102 | |
103 | static bool drm_writeback_fence_enable_signaling(struct dma_fence *fence) |
104 | { |
105 | return true; |
106 | } |
107 | |
108 | static const struct dma_fence_ops drm_writeback_fence_ops = { |
109 | .get_driver_name = drm_writeback_fence_get_driver_name, |
110 | .get_timeline_name = drm_writeback_fence_get_timeline_name, |
111 | .enable_signaling = drm_writeback_fence_enable_signaling, |
112 | }; |
113 | |
114 | static int create_writeback_properties(struct drm_device *dev) |
115 | { |
116 | struct drm_property *prop; |
117 | |
118 | if (!dev->mode_config.writeback_fb_id_property) { |
119 | prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, |
120 | name: "WRITEBACK_FB_ID" , |
121 | DRM_MODE_OBJECT_FB); |
122 | if (!prop) |
123 | return -ENOMEM; |
124 | dev->mode_config.writeback_fb_id_property = prop; |
125 | } |
126 | |
127 | if (!dev->mode_config.writeback_pixel_formats_property) { |
128 | prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | |
129 | DRM_MODE_PROP_ATOMIC | |
130 | DRM_MODE_PROP_IMMUTABLE, |
131 | name: "WRITEBACK_PIXEL_FORMATS" , num_values: 0); |
132 | if (!prop) |
133 | return -ENOMEM; |
134 | dev->mode_config.writeback_pixel_formats_property = prop; |
135 | } |
136 | |
137 | if (!dev->mode_config.writeback_out_fence_ptr_property) { |
138 | prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, |
139 | name: "WRITEBACK_OUT_FENCE_PTR" , min: 0, |
140 | U64_MAX); |
141 | if (!prop) |
142 | return -ENOMEM; |
143 | dev->mode_config.writeback_out_fence_ptr_property = prop; |
144 | } |
145 | |
146 | return 0; |
147 | } |
148 | |
149 | static const struct drm_encoder_funcs drm_writeback_encoder_funcs = { |
150 | .destroy = drm_encoder_cleanup, |
151 | }; |
152 | |
153 | /** |
154 | * drm_writeback_connector_init - Initialize a writeback connector and its properties |
155 | * @dev: DRM device |
156 | * @wb_connector: Writeback connector to initialize |
157 | * @con_funcs: Connector funcs vtable |
158 | * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder |
159 | * @formats: Array of supported pixel formats for the writeback engine |
160 | * @n_formats: Length of the formats array |
161 | * @possible_crtcs: possible crtcs for the internal writeback encoder |
162 | * |
163 | * This function creates the writeback-connector-specific properties if they |
164 | * have not been already created, initializes the connector as |
165 | * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property |
166 | * values. It will also create an internal encoder associated with the |
167 | * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for |
168 | * the encoder helper. |
169 | * |
170 | * Drivers should always use this function instead of drm_connector_init() to |
171 | * set up writeback connectors. |
172 | * |
173 | * Returns: 0 on success, or a negative error code |
174 | */ |
175 | int drm_writeback_connector_init(struct drm_device *dev, |
176 | struct drm_writeback_connector *wb_connector, |
177 | const struct drm_connector_funcs *con_funcs, |
178 | const struct drm_encoder_helper_funcs *enc_helper_funcs, |
179 | const u32 *formats, int n_formats, |
180 | u32 possible_crtcs) |
181 | { |
182 | int ret = 0; |
183 | |
184 | drm_encoder_helper_add(encoder: &wb_connector->encoder, funcs: enc_helper_funcs); |
185 | |
186 | wb_connector->encoder.possible_crtcs = possible_crtcs; |
187 | |
188 | ret = drm_encoder_init(dev, encoder: &wb_connector->encoder, |
189 | funcs: &drm_writeback_encoder_funcs, |
190 | DRM_MODE_ENCODER_VIRTUAL, NULL); |
191 | if (ret) |
192 | return ret; |
193 | |
194 | ret = drm_writeback_connector_init_with_encoder(dev, wb_connector, enc: &wb_connector->encoder, |
195 | con_funcs, formats, n_formats); |
196 | |
197 | if (ret) |
198 | drm_encoder_cleanup(encoder: &wb_connector->encoder); |
199 | |
200 | return ret; |
201 | } |
202 | EXPORT_SYMBOL(drm_writeback_connector_init); |
203 | |
204 | /** |
205 | * drm_writeback_connector_init_with_encoder - Initialize a writeback connector with |
206 | * a custom encoder |
207 | * |
208 | * @dev: DRM device |
209 | * @wb_connector: Writeback connector to initialize |
210 | * @enc: handle to the already initialized drm encoder |
211 | * @con_funcs: Connector funcs vtable |
212 | * @formats: Array of supported pixel formats for the writeback engine |
213 | * @n_formats: Length of the formats array |
214 | * |
215 | * This function creates the writeback-connector-specific properties if they |
216 | * have not been already created, initializes the connector as |
217 | * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property |
218 | * values. |
219 | * |
220 | * This function assumes that the drm_writeback_connector's encoder has already been |
221 | * created and initialized before invoking this function. |
222 | * |
223 | * In addition, this function also assumes that callers of this API will manage |
224 | * assigning the encoder helper functions, possible_crtcs and any other encoder |
225 | * specific operation. |
226 | * |
227 | * Drivers should always use this function instead of drm_connector_init() to |
228 | * set up writeback connectors if they want to manage themselves the lifetime of the |
229 | * associated encoder. |
230 | * |
231 | * Returns: 0 on success, or a negative error code |
232 | */ |
233 | int drm_writeback_connector_init_with_encoder(struct drm_device *dev, |
234 | struct drm_writeback_connector *wb_connector, struct drm_encoder *enc, |
235 | const struct drm_connector_funcs *con_funcs, const u32 *formats, |
236 | int n_formats) |
237 | { |
238 | struct drm_property_blob *blob; |
239 | struct drm_connector *connector = &wb_connector->base; |
240 | struct drm_mode_config *config = &dev->mode_config; |
241 | int ret = create_writeback_properties(dev); |
242 | |
243 | if (ret != 0) |
244 | return ret; |
245 | |
246 | blob = drm_property_create_blob(dev, length: n_formats * sizeof(*formats), |
247 | data: formats); |
248 | if (IS_ERR(ptr: blob)) |
249 | return PTR_ERR(ptr: blob); |
250 | |
251 | |
252 | connector->interlace_allowed = 0; |
253 | |
254 | ret = drm_connector_init(dev, connector, funcs: con_funcs, |
255 | DRM_MODE_CONNECTOR_WRITEBACK); |
256 | if (ret) |
257 | goto connector_fail; |
258 | |
259 | ret = drm_connector_attach_encoder(connector, encoder: enc); |
260 | if (ret) |
261 | goto attach_fail; |
262 | |
263 | INIT_LIST_HEAD(list: &wb_connector->job_queue); |
264 | spin_lock_init(&wb_connector->job_lock); |
265 | |
266 | wb_connector->fence_context = dma_fence_context_alloc(num: 1); |
267 | spin_lock_init(&wb_connector->fence_lock); |
268 | snprintf(buf: wb_connector->timeline_name, |
269 | size: sizeof(wb_connector->timeline_name), |
270 | fmt: "CONNECTOR:%d-%s" , connector->base.id, connector->name); |
271 | |
272 | drm_object_attach_property(obj: &connector->base, |
273 | property: config->writeback_out_fence_ptr_property, init_val: 0); |
274 | |
275 | drm_object_attach_property(obj: &connector->base, |
276 | property: config->writeback_fb_id_property, init_val: 0); |
277 | |
278 | drm_object_attach_property(obj: &connector->base, |
279 | property: config->writeback_pixel_formats_property, |
280 | init_val: blob->base.id); |
281 | wb_connector->pixel_formats_blob_ptr = blob; |
282 | |
283 | return 0; |
284 | |
285 | attach_fail: |
286 | drm_connector_cleanup(connector); |
287 | connector_fail: |
288 | drm_property_blob_put(blob); |
289 | return ret; |
290 | } |
291 | EXPORT_SYMBOL(drm_writeback_connector_init_with_encoder); |
292 | |
293 | int drm_writeback_set_fb(struct drm_connector_state *conn_state, |
294 | struct drm_framebuffer *fb) |
295 | { |
296 | WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK); |
297 | |
298 | if (!conn_state->writeback_job) { |
299 | conn_state->writeback_job = |
300 | kzalloc(size: sizeof(*conn_state->writeback_job), GFP_KERNEL); |
301 | if (!conn_state->writeback_job) |
302 | return -ENOMEM; |
303 | |
304 | conn_state->writeback_job->connector = |
305 | drm_connector_to_writeback(connector: conn_state->connector); |
306 | } |
307 | |
308 | drm_framebuffer_assign(p: &conn_state->writeback_job->fb, fb); |
309 | return 0; |
310 | } |
311 | |
312 | int drm_writeback_prepare_job(struct drm_writeback_job *job) |
313 | { |
314 | struct drm_writeback_connector *connector = job->connector; |
315 | const struct drm_connector_helper_funcs *funcs = |
316 | connector->base.helper_private; |
317 | int ret; |
318 | |
319 | if (funcs->prepare_writeback_job) { |
320 | ret = funcs->prepare_writeback_job(connector, job); |
321 | if (ret < 0) |
322 | return ret; |
323 | } |
324 | |
325 | job->prepared = true; |
326 | return 0; |
327 | } |
328 | EXPORT_SYMBOL(drm_writeback_prepare_job); |
329 | |
330 | /** |
331 | * drm_writeback_queue_job - Queue a writeback job for later signalling |
332 | * @wb_connector: The writeback connector to queue a job on |
333 | * @conn_state: The connector state containing the job to queue |
334 | * |
335 | * This function adds the job contained in @conn_state to the job_queue for a |
336 | * writeback connector. It takes ownership of the writeback job and sets the |
337 | * @conn_state->writeback_job to NULL, and so no access to the job may be |
338 | * performed by the caller after this function returns. |
339 | * |
340 | * Drivers must ensure that for a given writeback connector, jobs are queued in |
341 | * exactly the same order as they will be completed by the hardware (and |
342 | * signaled via drm_writeback_signal_completion). |
343 | * |
344 | * For every call to drm_writeback_queue_job() there must be exactly one call to |
345 | * drm_writeback_signal_completion() |
346 | * |
347 | * See also: drm_writeback_signal_completion() |
348 | */ |
349 | void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector, |
350 | struct drm_connector_state *conn_state) |
351 | { |
352 | struct drm_writeback_job *job; |
353 | unsigned long flags; |
354 | |
355 | job = conn_state->writeback_job; |
356 | conn_state->writeback_job = NULL; |
357 | |
358 | spin_lock_irqsave(&wb_connector->job_lock, flags); |
359 | list_add_tail(new: &job->list_entry, head: &wb_connector->job_queue); |
360 | spin_unlock_irqrestore(lock: &wb_connector->job_lock, flags); |
361 | } |
362 | EXPORT_SYMBOL(drm_writeback_queue_job); |
363 | |
364 | void drm_writeback_cleanup_job(struct drm_writeback_job *job) |
365 | { |
366 | struct drm_writeback_connector *connector = job->connector; |
367 | const struct drm_connector_helper_funcs *funcs = |
368 | connector->base.helper_private; |
369 | |
370 | if (job->prepared && funcs->cleanup_writeback_job) |
371 | funcs->cleanup_writeback_job(connector, job); |
372 | |
373 | if (job->fb) |
374 | drm_framebuffer_put(fb: job->fb); |
375 | |
376 | if (job->out_fence) |
377 | dma_fence_put(fence: job->out_fence); |
378 | |
379 | kfree(objp: job); |
380 | } |
381 | EXPORT_SYMBOL(drm_writeback_cleanup_job); |
382 | |
383 | /* |
384 | * @cleanup_work: deferred cleanup of a writeback job |
385 | * |
386 | * The job cannot be cleaned up directly in drm_writeback_signal_completion, |
387 | * because it may be called in interrupt context. Dropping the framebuffer |
388 | * reference can sleep, and so the cleanup is deferred to a workqueue. |
389 | */ |
390 | static void cleanup_work(struct work_struct *work) |
391 | { |
392 | struct drm_writeback_job *job = container_of(work, |
393 | struct drm_writeback_job, |
394 | cleanup_work); |
395 | |
396 | drm_writeback_cleanup_job(job); |
397 | } |
398 | |
399 | /** |
400 | * drm_writeback_signal_completion - Signal the completion of a writeback job |
401 | * @wb_connector: The writeback connector whose job is complete |
402 | * @status: Status code to set in the writeback out_fence (0 for success) |
403 | * |
404 | * Drivers should call this to signal the completion of a previously queued |
405 | * writeback job. It should be called as soon as possible after the hardware |
406 | * has finished writing, and may be called from interrupt context. |
407 | * It is the driver's responsibility to ensure that for a given connector, the |
408 | * hardware completes writeback jobs in the same order as they are queued. |
409 | * |
410 | * Unless the driver is holding its own reference to the framebuffer, it must |
411 | * not be accessed after calling this function. |
412 | * |
413 | * See also: drm_writeback_queue_job() |
414 | */ |
415 | void |
416 | drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector, |
417 | int status) |
418 | { |
419 | unsigned long flags; |
420 | struct drm_writeback_job *job; |
421 | struct dma_fence *out_fence; |
422 | |
423 | spin_lock_irqsave(&wb_connector->job_lock, flags); |
424 | job = list_first_entry_or_null(&wb_connector->job_queue, |
425 | struct drm_writeback_job, |
426 | list_entry); |
427 | if (job) |
428 | list_del(entry: &job->list_entry); |
429 | |
430 | spin_unlock_irqrestore(lock: &wb_connector->job_lock, flags); |
431 | |
432 | if (WARN_ON(!job)) |
433 | return; |
434 | |
435 | out_fence = job->out_fence; |
436 | if (out_fence) { |
437 | if (status) |
438 | dma_fence_set_error(fence: out_fence, error: status); |
439 | dma_fence_signal(fence: out_fence); |
440 | dma_fence_put(fence: out_fence); |
441 | job->out_fence = NULL; |
442 | } |
443 | |
444 | INIT_WORK(&job->cleanup_work, cleanup_work); |
445 | queue_work(wq: system_long_wq, work: &job->cleanup_work); |
446 | } |
447 | EXPORT_SYMBOL(drm_writeback_signal_completion); |
448 | |
449 | struct dma_fence * |
450 | drm_writeback_get_out_fence(struct drm_writeback_connector *wb_connector) |
451 | { |
452 | struct dma_fence *fence; |
453 | |
454 | if (WARN_ON(wb_connector->base.connector_type != |
455 | DRM_MODE_CONNECTOR_WRITEBACK)) |
456 | return NULL; |
457 | |
458 | fence = kzalloc(size: sizeof(*fence), GFP_KERNEL); |
459 | if (!fence) |
460 | return NULL; |
461 | |
462 | dma_fence_init(fence, ops: &drm_writeback_fence_ops, |
463 | lock: &wb_connector->fence_lock, context: wb_connector->fence_context, |
464 | seqno: ++wb_connector->fence_seqno); |
465 | |
466 | return fence; |
467 | } |
468 | EXPORT_SYMBOL(drm_writeback_get_out_fence); |
469 | |