1
2
3
4
5
6
7
8
9
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
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81#define fence_to_wb_connector(x) container_of(x->lock, \
82 struct drm_writeback_connector, \
83 fence_lock)
84
85static const char *drm_writeback_fence_get_driver_name(struct dma_fence *fence)
86{
87 struct drm_writeback_connector *wb_connector =
88 fence_to_wb_connector(fence);
89
90 return wb_connector->base.dev->driver->name;
91}
92
93static const char *
94drm_writeback_fence_get_timeline_name(struct dma_fence *fence)
95{
96 struct drm_writeback_connector *wb_connector =
97 fence_to_wb_connector(fence);
98
99 return wb_connector->timeline_name;
100}
101
102static bool drm_writeback_fence_enable_signaling(struct dma_fence *fence)
103{
104 return true;
105}
106
107static 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};
112
113static int create_writeback_properties(struct drm_device *dev)
114{
115 struct drm_property *prop;
116
117 if (!dev->mode_config.writeback_fb_id_property) {
118 prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
119 "WRITEBACK_FB_ID",
120 DRM_MODE_OBJECT_FB);
121 if (!prop)
122 return -ENOMEM;
123 dev->mode_config.writeback_fb_id_property = prop;
124 }
125
126 if (!dev->mode_config.writeback_pixel_formats_property) {
127 prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
128 DRM_MODE_PROP_ATOMIC |
129 DRM_MODE_PROP_IMMUTABLE,
130 "WRITEBACK_PIXEL_FORMATS", 0);
131 if (!prop)
132 return -ENOMEM;
133 dev->mode_config.writeback_pixel_formats_property = prop;
134 }
135
136 if (!dev->mode_config.writeback_out_fence_ptr_property) {
137 prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
138 "WRITEBACK_OUT_FENCE_PTR", 0,
139 U64_MAX);
140 if (!prop)
141 return -ENOMEM;
142 dev->mode_config.writeback_out_fence_ptr_property = prop;
143 }
144
145 return 0;
146}
147
148static const struct drm_encoder_funcs drm_writeback_encoder_funcs = {
149 .destroy = drm_encoder_cleanup,
150};
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173int drm_writeback_connector_init(struct drm_device *dev,
174 struct drm_writeback_connector *wb_connector,
175 const struct drm_connector_funcs *con_funcs,
176 const struct drm_encoder_helper_funcs *enc_helper_funcs,
177 const u32 *formats, int n_formats)
178{
179 struct drm_property_blob *blob;
180 struct drm_connector *connector = &wb_connector->base;
181 struct drm_mode_config *config = &dev->mode_config;
182 int ret = create_writeback_properties(dev);
183
184 if (ret != 0)
185 return ret;
186
187 blob = drm_property_create_blob(dev, n_formats * sizeof(*formats),
188 formats);
189 if (IS_ERR(blob))
190 return PTR_ERR(blob);
191
192 drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs);
193 ret = drm_encoder_init(dev, &wb_connector->encoder,
194 &drm_writeback_encoder_funcs,
195 DRM_MODE_ENCODER_VIRTUAL, NULL);
196 if (ret)
197 goto fail;
198
199 connector->interlace_allowed = 0;
200
201 ret = drm_connector_init(dev, connector, con_funcs,
202 DRM_MODE_CONNECTOR_WRITEBACK);
203 if (ret)
204 goto connector_fail;
205
206 ret = drm_connector_attach_encoder(connector,
207 &wb_connector->encoder);
208 if (ret)
209 goto attach_fail;
210
211 INIT_LIST_HEAD(&wb_connector->job_queue);
212 spin_lock_init(&wb_connector->job_lock);
213
214 wb_connector->fence_context = dma_fence_context_alloc(1);
215 spin_lock_init(&wb_connector->fence_lock);
216 snprintf(wb_connector->timeline_name,
217 sizeof(wb_connector->timeline_name),
218 "CONNECTOR:%d-%s", connector->base.id, connector->name);
219
220 drm_object_attach_property(&connector->base,
221 config->writeback_out_fence_ptr_property, 0);
222
223 drm_object_attach_property(&connector->base,
224 config->writeback_fb_id_property, 0);
225
226 drm_object_attach_property(&connector->base,
227 config->writeback_pixel_formats_property,
228 blob->base.id);
229 wb_connector->pixel_formats_blob_ptr = blob;
230
231 return 0;
232
233attach_fail:
234 drm_connector_cleanup(connector);
235connector_fail:
236 drm_encoder_cleanup(&wb_connector->encoder);
237fail:
238 drm_property_blob_put(blob);
239 return ret;
240}
241EXPORT_SYMBOL(drm_writeback_connector_init);
242
243int drm_writeback_set_fb(struct drm_connector_state *conn_state,
244 struct drm_framebuffer *fb)
245{
246 WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
247
248 if (!conn_state->writeback_job) {
249 conn_state->writeback_job =
250 kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL);
251 if (!conn_state->writeback_job)
252 return -ENOMEM;
253
254 conn_state->writeback_job->connector =
255 drm_connector_to_writeback(conn_state->connector);
256 }
257
258 drm_framebuffer_assign(&conn_state->writeback_job->fb, fb);
259 return 0;
260}
261
262int drm_writeback_prepare_job(struct drm_writeback_job *job)
263{
264 struct drm_writeback_connector *connector = job->connector;
265 const struct drm_connector_helper_funcs *funcs =
266 connector->base.helper_private;
267 int ret;
268
269 if (funcs->prepare_writeback_job) {
270 ret = funcs->prepare_writeback_job(connector, job);
271 if (ret < 0)
272 return ret;
273 }
274
275 job->prepared = true;
276 return 0;
277}
278EXPORT_SYMBOL(drm_writeback_prepare_job);
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector,
300 struct drm_connector_state *conn_state)
301{
302 struct drm_writeback_job *job;
303 unsigned long flags;
304
305 job = conn_state->writeback_job;
306 conn_state->writeback_job = NULL;
307
308 spin_lock_irqsave(&wb_connector->job_lock, flags);
309 list_add_tail(&job->list_entry, &wb_connector->job_queue);
310 spin_unlock_irqrestore(&wb_connector->job_lock, flags);
311}
312EXPORT_SYMBOL(drm_writeback_queue_job);
313
314void drm_writeback_cleanup_job(struct drm_writeback_job *job)
315{
316 struct drm_writeback_connector *connector = job->connector;
317 const struct drm_connector_helper_funcs *funcs =
318 connector->base.helper_private;
319
320 if (job->prepared && funcs->cleanup_writeback_job)
321 funcs->cleanup_writeback_job(connector, job);
322
323 if (job->fb)
324 drm_framebuffer_put(job->fb);
325
326 if (job->out_fence)
327 dma_fence_put(job->out_fence);
328
329 kfree(job);
330}
331EXPORT_SYMBOL(drm_writeback_cleanup_job);
332
333
334
335
336
337
338
339
340static void cleanup_work(struct work_struct *work)
341{
342 struct drm_writeback_job *job = container_of(work,
343 struct drm_writeback_job,
344 cleanup_work);
345
346 drm_writeback_cleanup_job(job);
347}
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365void
366drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector,
367 int status)
368{
369 unsigned long flags;
370 struct drm_writeback_job *job;
371 struct dma_fence *out_fence;
372
373 spin_lock_irqsave(&wb_connector->job_lock, flags);
374 job = list_first_entry_or_null(&wb_connector->job_queue,
375 struct drm_writeback_job,
376 list_entry);
377 if (job)
378 list_del(&job->list_entry);
379
380 spin_unlock_irqrestore(&wb_connector->job_lock, flags);
381
382 if (WARN_ON(!job))
383 return;
384
385 out_fence = job->out_fence;
386 if (out_fence) {
387 if (status)
388 dma_fence_set_error(out_fence, status);
389 dma_fence_signal(out_fence);
390 dma_fence_put(out_fence);
391 job->out_fence = NULL;
392 }
393
394 INIT_WORK(&job->cleanup_work, cleanup_work);
395 queue_work(system_long_wq, &job->cleanup_work);
396}
397EXPORT_SYMBOL(drm_writeback_signal_completion);
398
399struct dma_fence *
400drm_writeback_get_out_fence(struct drm_writeback_connector *wb_connector)
401{
402 struct dma_fence *fence;
403
404 if (WARN_ON(wb_connector->base.connector_type !=
405 DRM_MODE_CONNECTOR_WRITEBACK))
406 return NULL;
407
408 fence = kzalloc(sizeof(*fence), GFP_KERNEL);
409 if (!fence)
410 return NULL;
411
412 dma_fence_init(fence, &drm_writeback_fence_ops,
413 &wb_connector->fence_lock, wb_connector->fence_context,
414 ++wb_connector->fence_seqno);
415
416 return fence;
417}
418EXPORT_SYMBOL(drm_writeback_get_out_fence);
419