1
2
3
4
5
6
7
8
9
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
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#define fence_to_wb_connector(x) container_of(x->lock, \
80 struct drm_writeback_connector, \
81 fence_lock)
82
83static 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
91static const char *
92drm_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
100static bool drm_writeback_fence_enable_signaling(struct dma_fence *fence)
101{
102 return true;
103}
104
105static 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
112static 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
147static const struct drm_encoder_funcs drm_writeback_encoder_funcs = {
148 .destroy = drm_encoder_cleanup,
149};
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172int 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
232attach_fail:
233 drm_connector_cleanup(connector);
234connector_fail:
235 drm_encoder_cleanup(&wb_connector->encoder);
236fail:
237 drm_property_blob_put(blob);
238 return ret;
239}
240EXPORT_SYMBOL(drm_writeback_connector_init);
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260void 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}
269EXPORT_SYMBOL(drm_writeback_queue_job);
270
271void drm_writeback_cleanup_job(struct drm_writeback_job *job)
272{
273 if (job->fb)
274 drm_framebuffer_put(job->fb);
275
276 kfree(job);
277}
278EXPORT_SYMBOL(drm_writeback_cleanup_job);
279
280
281
282
283
284
285
286
287static void cleanup_work(struct work_struct *work)
288{
289 struct drm_writeback_job *job = container_of(work,
290 struct drm_writeback_job,
291 cleanup_work);
292
293 drm_writeback_cleanup_job(job);
294}
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312void
313drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector,
314 int status)
315{
316 unsigned long flags;
317 struct drm_writeback_job *job;
318
319 spin_lock_irqsave(&wb_connector->job_lock, flags);
320 job = list_first_entry_or_null(&wb_connector->job_queue,
321 struct drm_writeback_job,
322 list_entry);
323 if (job) {
324 list_del(&job->list_entry);
325 if (job->out_fence) {
326 if (status)
327 dma_fence_set_error(job->out_fence, status);
328 dma_fence_signal(job->out_fence);
329 dma_fence_put(job->out_fence);
330 }
331 }
332 spin_unlock_irqrestore(&wb_connector->job_lock, flags);
333
334 if (WARN_ON(!job))
335 return;
336
337 INIT_WORK(&job->cleanup_work, cleanup_work);
338 queue_work(system_long_wq, &job->cleanup_work);
339}
340EXPORT_SYMBOL(drm_writeback_signal_completion);
341
342struct dma_fence *
343drm_writeback_get_out_fence(struct drm_writeback_connector *wb_connector)
344{
345 struct dma_fence *fence;
346
347 if (WARN_ON(wb_connector->base.connector_type !=
348 DRM_MODE_CONNECTOR_WRITEBACK))
349 return NULL;
350
351 fence = kzalloc(sizeof(*fence), GFP_KERNEL);
352 if (!fence)
353 return NULL;
354
355 dma_fence_init(fence, &drm_writeback_fence_ops,
356 &wb_connector->fence_lock, wb_connector->fence_context,
357 ++wb_connector->fence_seqno);
358
359 return fence;
360}
361EXPORT_SYMBOL(drm_writeback_get_out_fence);
362