diff options
author | Rodrigo Vivi <rodrigo.vivi@intel.com> | 2018-07-23 12:13:12 -0400 |
---|---|---|
committer | Rodrigo Vivi <rodrigo.vivi@intel.com> | 2018-07-23 12:13:12 -0400 |
commit | c74a7469f97c0f40b46e82ee979f9fb1bb6e847c (patch) | |
tree | f2690a1a916b73ef94657fbf0e0141ae57701825 /drivers/gpu/drm/drm_writeback.c | |
parent | 6f15a7de86c8cf2dc09fc9e6d07047efa40ef809 (diff) | |
parent | 500775074f88d9cf5416bed2ca19592812d62c41 (diff) |
Merge drm/drm-next into drm-intel-next-queued
We need a backmerge to get DP_DPCD_REV_14 before we push other
i915 changes to dinq that could break compilation.
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Diffstat (limited to 'drivers/gpu/drm/drm_writeback.c')
-rw-r--r-- | drivers/gpu/drm/drm_writeback.c | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c new file mode 100644 index 000000000000..c20e6fe00cb3 --- /dev/null +++ b/drivers/gpu/drm/drm_writeback.c | |||
@@ -0,0 +1,353 @@ | |||
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 <drm/drm_crtc.h> | ||
13 | #include <drm/drm_modeset_helper_vtables.h> | ||
14 | #include <drm/drm_property.h> | ||
15 | #include <drm/drm_writeback.h> | ||
16 | #include <drm/drmP.h> | ||
17 | #include <linux/dma-fence.h> | ||
18 | |||
19 | /** | ||
20 | * DOC: overview | ||
21 | * | ||
22 | * Writeback connectors are used to expose hardware which can write the output | ||
23 | * from a CRTC to a memory buffer. They are used and act similarly to other | ||
24 | * types of connectors, with some important differences: | ||
25 | * | ||
26 | * * Writeback connectors don't provide a way to output visually to the user. | ||
27 | * | ||
28 | * * Writeback connectors are visible to userspace only when the client sets | ||
29 | * DRM_CLIENT_CAP_WRITEBACK_CONNECTORS. | ||
30 | * | ||
31 | * * Writeback connectors don't have EDID. | ||
32 | * | ||
33 | * A framebuffer may only be attached to a writeback connector when the | ||
34 | * connector is attached to a CRTC. The WRITEBACK_FB_ID property which sets the | ||
35 | * framebuffer applies only to a single commit (see below). A framebuffer may | ||
36 | * not be attached while the CRTC is off. | ||
37 | * | ||
38 | * Unlike with planes, when a writeback framebuffer is removed by userspace DRM | ||
39 | * makes no attempt to remove it from active use by the connector. This is | ||
40 | * because no method is provided to abort a writeback operation, and in any | ||
41 | * case making a new commit whilst a writeback is ongoing is undefined (see | ||
42 | * WRITEBACK_OUT_FENCE_PTR below). As soon as the current writeback is finished, | ||
43 | * the framebuffer will automatically no longer be in active use. As it will | ||
44 | * also have already been removed from the framebuffer list, there will be no | ||
45 | * way for any userspace application to retrieve a reference to it in the | ||
46 | * intervening period. | ||
47 | * | ||
48 | * Writeback connectors have some additional properties, which userspace | ||
49 | * can use to query and control them: | ||
50 | * | ||
51 | * "WRITEBACK_FB_ID": | ||
52 | * Write-only object property storing a DRM_MODE_OBJECT_FB: it stores the | ||
53 | * framebuffer to be written by the writeback connector. This property is | ||
54 | * similar to the FB_ID property on planes, but will always read as zero | ||
55 | * and is not preserved across commits. | ||
56 | * Userspace must set this property to an output buffer every time it | ||
57 | * wishes the buffer to get filled. | ||
58 | * | ||
59 | * "WRITEBACK_PIXEL_FORMATS": | ||
60 | * Immutable blob property to store the supported pixel formats table. The | ||
61 | * data is an array of u32 DRM_FORMAT_* fourcc values. | ||
62 | * Userspace can use this blob to find out what pixel formats are supported | ||
63 | * by the connector's writeback engine. | ||
64 | * | ||
65 | * "WRITEBACK_OUT_FENCE_PTR": | ||
66 | * Userspace can use this property to provide a pointer for the kernel to | ||
67 | * fill with a sync_file file descriptor, which will signal once the | ||
68 | * writeback is finished. The value should be the address of a 32-bit | ||
69 | * signed integer, cast to a u64. | ||
70 | * Userspace should wait for this fence to signal before making another | ||
71 | * commit affecting any of the same CRTCs, Planes or Connectors. | ||
72 | * **Failure to do so will result in undefined behaviour.** | ||
73 | * For this reason it is strongly recommended that all userspace | ||
74 | * applications making use of writeback connectors *always* retrieve an | ||
75 | * out-fence for the commit and use it appropriately. | ||
76 | * From userspace, this property will always read as zero. | ||
77 | */ | ||
78 | |||
79 | #define fence_to_wb_connector(x) container_of(x->lock, \ | ||
80 | struct drm_writeback_connector, \ | ||
81 | fence_lock) | ||
82 | |||
83 | static const char *drm_writeback_fence_get_driver_name(struct dma_fence *fence) | ||
84 | { | ||
85 | struct drm_writeback_connector *wb_connector = | ||
86 | fence_to_wb_connector(fence); | ||
87 | |||
88 | return wb_connector->base.dev->driver->name; | ||
89 | } | ||
90 | |||
91 | static const char * | ||
92 | drm_writeback_fence_get_timeline_name(struct dma_fence *fence) | ||
93 | { | ||
94 | struct drm_writeback_connector *wb_connector = | ||
95 | fence_to_wb_connector(fence); | ||
96 | |||
97 | return wb_connector->timeline_name; | ||
98 | } | ||
99 | |||
100 | static bool drm_writeback_fence_enable_signaling(struct dma_fence *fence) | ||
101 | { | ||
102 | return true; | ||
103 | } | ||
104 | |||
105 | static const struct dma_fence_ops drm_writeback_fence_ops = { | ||
106 | .get_driver_name = drm_writeback_fence_get_driver_name, | ||
107 | .get_timeline_name = drm_writeback_fence_get_timeline_name, | ||
108 | .enable_signaling = drm_writeback_fence_enable_signaling, | ||
109 | .wait = dma_fence_default_wait, | ||
110 | }; | ||
111 | |||
112 | static int create_writeback_properties(struct drm_device *dev) | ||
113 | { | ||
114 | struct drm_property *prop; | ||
115 | |||
116 | if (!dev->mode_config.writeback_fb_id_property) { | ||
117 | prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, | ||
118 | "WRITEBACK_FB_ID", | ||
119 | DRM_MODE_OBJECT_FB); | ||
120 | if (!prop) | ||
121 | return -ENOMEM; | ||
122 | dev->mode_config.writeback_fb_id_property = prop; | ||
123 | } | ||
124 | |||
125 | if (!dev->mode_config.writeback_pixel_formats_property) { | ||
126 | prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | | ||
127 | DRM_MODE_PROP_ATOMIC | | ||
128 | DRM_MODE_PROP_IMMUTABLE, | ||
129 | "WRITEBACK_PIXEL_FORMATS", 0); | ||
130 | if (!prop) | ||
131 | return -ENOMEM; | ||
132 | dev->mode_config.writeback_pixel_formats_property = prop; | ||
133 | } | ||
134 | |||
135 | if (!dev->mode_config.writeback_out_fence_ptr_property) { | ||
136 | prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, | ||
137 | "WRITEBACK_OUT_FENCE_PTR", 0, | ||
138 | U64_MAX); | ||
139 | if (!prop) | ||
140 | return -ENOMEM; | ||
141 | dev->mode_config.writeback_out_fence_ptr_property = prop; | ||
142 | } | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static const struct drm_encoder_funcs drm_writeback_encoder_funcs = { | ||
148 | .destroy = drm_encoder_cleanup, | ||
149 | }; | ||
150 | |||
151 | /** | ||
152 | * drm_writeback_connector_init - Initialize a writeback connector and its properties | ||
153 | * @dev: DRM device | ||
154 | * @wb_connector: Writeback connector to initialize | ||
155 | * @con_funcs: Connector funcs vtable | ||
156 | * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder | ||
157 | * @formats: Array of supported pixel formats for the writeback engine | ||
158 | * @n_formats: Length of the formats array | ||
159 | * | ||
160 | * This function creates the writeback-connector-specific properties if they | ||
161 | * have not been already created, initializes the connector as | ||
162 | * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property | ||
163 | * values. It will also create an internal encoder associated with the | ||
164 | * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for | ||
165 | * the encoder helper. | ||
166 | * | ||
167 | * Drivers should always use this function instead of drm_connector_init() to | ||
168 | * set up writeback connectors. | ||
169 | * | ||
170 | * Returns: 0 on success, or a negative error code | ||
171 | */ | ||
172 | int drm_writeback_connector_init(struct drm_device *dev, | ||
173 | struct drm_writeback_connector *wb_connector, | ||
174 | const struct drm_connector_funcs *con_funcs, | ||
175 | const struct drm_encoder_helper_funcs *enc_helper_funcs, | ||
176 | const u32 *formats, int n_formats) | ||
177 | { | ||
178 | struct drm_property_blob *blob; | ||
179 | struct drm_connector *connector = &wb_connector->base; | ||
180 | struct drm_mode_config *config = &dev->mode_config; | ||
181 | int ret = create_writeback_properties(dev); | ||
182 | |||
183 | if (ret != 0) | ||
184 | return ret; | ||
185 | |||
186 | blob = drm_property_create_blob(dev, n_formats * sizeof(*formats), | ||
187 | formats); | ||
188 | if (IS_ERR(blob)) | ||
189 | return PTR_ERR(blob); | ||
190 | |||
191 | drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs); | ||
192 | ret = drm_encoder_init(dev, &wb_connector->encoder, | ||
193 | &drm_writeback_encoder_funcs, | ||
194 | DRM_MODE_ENCODER_VIRTUAL, NULL); | ||
195 | if (ret) | ||
196 | goto fail; | ||
197 | |||
198 | connector->interlace_allowed = 0; | ||
199 | |||
200 | ret = drm_connector_init(dev, connector, con_funcs, | ||
201 | DRM_MODE_CONNECTOR_WRITEBACK); | ||
202 | if (ret) | ||
203 | goto connector_fail; | ||
204 | |||
205 | ret = drm_connector_attach_encoder(connector, | ||
206 | &wb_connector->encoder); | ||
207 | if (ret) | ||
208 | goto attach_fail; | ||
209 | |||
210 | INIT_LIST_HEAD(&wb_connector->job_queue); | ||
211 | spin_lock_init(&wb_connector->job_lock); | ||
212 | |||
213 | wb_connector->fence_context = dma_fence_context_alloc(1); | ||
214 | spin_lock_init(&wb_connector->fence_lock); | ||
215 | snprintf(wb_connector->timeline_name, | ||
216 | sizeof(wb_connector->timeline_name), | ||
217 | "CONNECTOR:%d-%s", connector->base.id, connector->name); | ||
218 | |||
219 | drm_object_attach_property(&connector->base, | ||
220 | config->writeback_out_fence_ptr_property, 0); | ||
221 | |||
222 | drm_object_attach_property(&connector->base, | ||
223 | config->writeback_fb_id_property, 0); | ||
224 | |||
225 | drm_object_attach_property(&connector->base, | ||
226 | config->writeback_pixel_formats_property, | ||
227 | blob->base.id); | ||
228 | wb_connector->pixel_formats_blob_ptr = blob; | ||
229 | |||
230 | return 0; | ||
231 | |||
232 | attach_fail: | ||
233 | drm_connector_cleanup(connector); | ||
234 | connector_fail: | ||
235 | drm_encoder_cleanup(&wb_connector->encoder); | ||
236 | fail: | ||
237 | drm_property_blob_put(blob); | ||
238 | return ret; | ||
239 | } | ||
240 | EXPORT_SYMBOL(drm_writeback_connector_init); | ||
241 | |||
242 | /** | ||
243 | * drm_writeback_queue_job - Queue a writeback job for later signalling | ||
244 | * @wb_connector: The writeback connector to queue a job on | ||
245 | * @job: The job to queue | ||
246 | * | ||
247 | * This function adds a job to the job_queue for a writeback connector. It | ||
248 | * should be considered to take ownership of the writeback job, and so any other | ||
249 | * references to the job must be cleared after calling this function. | ||
250 | * | ||
251 | * Drivers must ensure that for a given writeback connector, jobs are queued in | ||
252 | * exactly the same order as they will be completed by the hardware (and | ||
253 | * signaled via drm_writeback_signal_completion). | ||
254 | * | ||
255 | * For every call to drm_writeback_queue_job() there must be exactly one call to | ||
256 | * drm_writeback_signal_completion() | ||
257 | * | ||
258 | * See also: drm_writeback_signal_completion() | ||
259 | */ | ||
260 | void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector, | ||
261 | struct drm_writeback_job *job) | ||
262 | { | ||
263 | unsigned long flags; | ||
264 | |||
265 | spin_lock_irqsave(&wb_connector->job_lock, flags); | ||
266 | list_add_tail(&job->list_entry, &wb_connector->job_queue); | ||
267 | spin_unlock_irqrestore(&wb_connector->job_lock, flags); | ||
268 | } | ||
269 | EXPORT_SYMBOL(drm_writeback_queue_job); | ||
270 | |||
271 | /* | ||
272 | * @cleanup_work: deferred cleanup of a writeback job | ||
273 | * | ||
274 | * The job cannot be cleaned up directly in drm_writeback_signal_completion, | ||
275 | * because it may be called in interrupt context. Dropping the framebuffer | ||
276 | * reference can sleep, and so the cleanup is deferred to a workqueue. | ||
277 | */ | ||
278 | static void cleanup_work(struct work_struct *work) | ||
279 | { | ||
280 | struct drm_writeback_job *job = container_of(work, | ||
281 | struct drm_writeback_job, | ||
282 | cleanup_work); | ||
283 | drm_framebuffer_put(job->fb); | ||
284 | kfree(job); | ||
285 | } | ||
286 | |||
287 | |||
288 | /** | ||
289 | * drm_writeback_signal_completion - Signal the completion of a writeback job | ||
290 | * @wb_connector: The writeback connector whose job is complete | ||
291 | * @status: Status code to set in the writeback out_fence (0 for success) | ||
292 | * | ||
293 | * Drivers should call this to signal the completion of a previously queued | ||
294 | * writeback job. It should be called as soon as possible after the hardware | ||
295 | * has finished writing, and may be called from interrupt context. | ||
296 | * It is the driver's responsibility to ensure that for a given connector, the | ||
297 | * hardware completes writeback jobs in the same order as they are queued. | ||
298 | * | ||
299 | * Unless the driver is holding its own reference to the framebuffer, it must | ||
300 | * not be accessed after calling this function. | ||
301 | * | ||
302 | * See also: drm_writeback_queue_job() | ||
303 | */ | ||
304 | void | ||
305 | drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector, | ||
306 | int status) | ||
307 | { | ||
308 | unsigned long flags; | ||
309 | struct drm_writeback_job *job; | ||
310 | |||
311 | spin_lock_irqsave(&wb_connector->job_lock, flags); | ||
312 | job = list_first_entry_or_null(&wb_connector->job_queue, | ||
313 | struct drm_writeback_job, | ||
314 | list_entry); | ||
315 | if (job) { | ||
316 | list_del(&job->list_entry); | ||
317 | if (job->out_fence) { | ||
318 | if (status) | ||
319 | dma_fence_set_error(job->out_fence, status); | ||
320 | dma_fence_signal(job->out_fence); | ||
321 | dma_fence_put(job->out_fence); | ||
322 | } | ||
323 | } | ||
324 | spin_unlock_irqrestore(&wb_connector->job_lock, flags); | ||
325 | |||
326 | if (WARN_ON(!job)) | ||
327 | return; | ||
328 | |||
329 | INIT_WORK(&job->cleanup_work, cleanup_work); | ||
330 | queue_work(system_long_wq, &job->cleanup_work); | ||
331 | } | ||
332 | EXPORT_SYMBOL(drm_writeback_signal_completion); | ||
333 | |||
334 | struct dma_fence * | ||
335 | drm_writeback_get_out_fence(struct drm_writeback_connector *wb_connector) | ||
336 | { | ||
337 | struct dma_fence *fence; | ||
338 | |||
339 | if (WARN_ON(wb_connector->base.connector_type != | ||
340 | DRM_MODE_CONNECTOR_WRITEBACK)) | ||
341 | return NULL; | ||
342 | |||
343 | fence = kzalloc(sizeof(*fence), GFP_KERNEL); | ||
344 | if (!fence) | ||
345 | return NULL; | ||
346 | |||
347 | dma_fence_init(fence, &drm_writeback_fence_ops, | ||
348 | &wb_connector->fence_lock, wb_connector->fence_context, | ||
349 | ++wb_connector->fence_seqno); | ||
350 | |||
351 | return fence; | ||
352 | } | ||
353 | EXPORT_SYMBOL(drm_writeback_get_out_fence); | ||