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