hc
2023-12-11 6778948f9de86c3cfaf36725a7c87dcff9ba247f
kernel/drivers/gpu/drm/drm_gem_framebuffer_helper.c
....@@ -1,21 +1,18 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * drm gem framebuffer helper functions
34 *
45 * Copyright (C) 2017 Noralf Trønnes
5
- *
6
- * This program is free software; you can redistribute it and/or modify
7
- * it under the terms of the GNU General Public License as published by
8
- * the Free Software Foundation; either version 2 of the License, or
9
- * (at your option) any later version.
106 */
117
128 #include <linux/dma-buf.h>
139 #include <linux/dma-fence.h>
14
-#include <linux/reservation.h>
10
+#include <linux/dma-resv.h>
1511 #include <linux/slab.h>
1612
17
-#include <drm/drmP.h>
1813 #include <drm/drm_atomic.h>
14
+#include <drm/drm_atomic_uapi.h>
15
+#include <drm/drm_damage_helper.h>
1916 #include <drm/drm_fb_helper.h>
2017 #include <drm/drm_fourcc.h>
2118 #include <drm/drm_framebuffer.h>
....@@ -23,6 +20,13 @@
2320 #include <drm/drm_gem_framebuffer_helper.h>
2421 #include <drm/drm_modeset_helper.h>
2522 #include <drm/drm_simple_kms_helper.h>
23
+
24
+#define AFBC_HEADER_SIZE 16
25
+#define AFBC_TH_LAYOUT_ALIGNMENT 8
26
+#define AFBC_HDR_ALIGN 64
27
+#define AFBC_SUPERBLOCK_PIXELS 256
28
+#define AFBC_SUPERBLOCK_ALIGNMENT 128
29
+#define AFBC_TH_BODY_START_ALIGNMENT 4096
2630
2731 /**
2832 * DOC: overview
....@@ -57,18 +61,14 @@
5761 }
5862 EXPORT_SYMBOL_GPL(drm_gem_fb_get_obj);
5963
60
-static struct drm_framebuffer *
61
-drm_gem_fb_alloc(struct drm_device *dev,
64
+static int
65
+drm_gem_fb_init(struct drm_device *dev,
66
+ struct drm_framebuffer *fb,
6267 const struct drm_mode_fb_cmd2 *mode_cmd,
6368 struct drm_gem_object **obj, unsigned int num_planes,
6469 const struct drm_framebuffer_funcs *funcs)
6570 {
66
- struct drm_framebuffer *fb;
6771 int ret, i;
68
-
69
- fb = kzalloc(sizeof(*fb), GFP_KERNEL);
70
- if (!fb)
71
- return ERR_PTR(-ENOMEM);
7272
7373 drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
7474
....@@ -76,14 +76,10 @@
7676 fb->obj[i] = obj[i];
7777
7878 ret = drm_framebuffer_init(dev, fb, funcs);
79
- if (ret) {
80
- DRM_DEV_ERROR(dev->dev, "Failed to init framebuffer: %d\n",
81
- ret);
82
- kfree(fb);
83
- return ERR_PTR(ret);
84
- }
79
+ if (ret)
80
+ drm_err(dev, "Failed to init framebuffer: %d\n", ret);
8581
86
- return fb;
82
+ return ret;
8783 }
8884
8985 /**
....@@ -99,7 +95,7 @@
9995 int i;
10096
10197 for (i = 0; i < 4; i++)
102
- drm_gem_object_put_unlocked(fb->obj[i]);
98
+ drm_gem_object_put(fb->obj[i]);
10399
104100 drm_framebuffer_cleanup(fb);
105101 kfree(fb);
....@@ -127,6 +123,84 @@
127123 EXPORT_SYMBOL(drm_gem_fb_create_handle);
128124
129125 /**
126
+ * drm_gem_fb_init_with_funcs() - Helper function for implementing
127
+ * &drm_mode_config_funcs.fb_create
128
+ * callback in cases when the driver
129
+ * allocates a subclass of
130
+ * struct drm_framebuffer
131
+ * @dev: DRM device
132
+ * @fb: framebuffer object
133
+ * @file: DRM file that holds the GEM handle(s) backing the framebuffer
134
+ * @mode_cmd: Metadata from the userspace framebuffer creation request
135
+ * @funcs: vtable to be used for the new framebuffer object
136
+ *
137
+ * This function can be used to set &drm_framebuffer_funcs for drivers that need
138
+ * custom framebuffer callbacks. Use drm_gem_fb_create() if you don't need to
139
+ * change &drm_framebuffer_funcs. The function does buffer size validation.
140
+ * The buffer size validation is for a general case, though, so users should
141
+ * pay attention to the checks being appropriate for them or, at least,
142
+ * non-conflicting.
143
+ *
144
+ * Returns:
145
+ * Zero or a negative error code.
146
+ */
147
+int drm_gem_fb_init_with_funcs(struct drm_device *dev,
148
+ struct drm_framebuffer *fb,
149
+ struct drm_file *file,
150
+ const struct drm_mode_fb_cmd2 *mode_cmd,
151
+ const struct drm_framebuffer_funcs *funcs)
152
+{
153
+ const struct drm_format_info *info;
154
+ struct drm_gem_object *objs[4];
155
+ int ret, i;
156
+
157
+ info = drm_get_format_info(dev, mode_cmd);
158
+ if (!info) {
159
+ drm_dbg_kms(dev, "Failed to get FB format info\n");
160
+ return -EINVAL;
161
+ }
162
+
163
+ for (i = 0; i < info->num_planes; i++) {
164
+ unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
165
+ unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
166
+ unsigned int min_size;
167
+
168
+ objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
169
+ if (!objs[i]) {
170
+ drm_dbg_kms(dev, "Failed to lookup GEM object\n");
171
+ ret = -ENOENT;
172
+ goto err_gem_object_put;
173
+ }
174
+
175
+ min_size = (height - 1) * mode_cmd->pitches[i]
176
+ + drm_format_info_min_pitch(info, i, width)
177
+ + mode_cmd->offsets[i];
178
+
179
+ if (objs[i]->size < min_size) {
180
+ drm_dbg_kms(dev,
181
+ "GEM object size (%zu) smaller than minimum size (%u) for plane %d\n",
182
+ objs[i]->size, min_size, i);
183
+ drm_gem_object_put(objs[i]);
184
+ ret = -EINVAL;
185
+ goto err_gem_object_put;
186
+ }
187
+ }
188
+
189
+ ret = drm_gem_fb_init(dev, fb, mode_cmd, objs, i, funcs);
190
+ if (ret)
191
+ goto err_gem_object_put;
192
+
193
+ return 0;
194
+
195
+err_gem_object_put:
196
+ for (i--; i >= 0; i--)
197
+ drm_gem_object_put(objs[i]);
198
+
199
+ return ret;
200
+}
201
+EXPORT_SYMBOL_GPL(drm_gem_fb_init_with_funcs);
202
+
203
+/**
130204 * drm_gem_fb_create_with_funcs() - Helper function for the
131205 * &drm_mode_config_funcs.fb_create
132206 * callback
....@@ -135,10 +209,9 @@
135209 * @mode_cmd: Metadata from the userspace framebuffer creation request
136210 * @funcs: vtable to be used for the new framebuffer object
137211 *
138
- * This can be used to set &drm_framebuffer_funcs for drivers that need the
139
- * &drm_framebuffer_funcs.dirty callback. Use drm_gem_fb_create() if you don't
140
- * need to change &drm_framebuffer_funcs.
141
- * The function does buffer size validation.
212
+ * This function can be used to set &drm_framebuffer_funcs for drivers that need
213
+ * custom framebuffer callbacks. Use drm_gem_fb_create() if you don't need to
214
+ * change &drm_framebuffer_funcs. The function does buffer size validation.
142215 *
143216 * Returns:
144217 * Pointer to a &drm_framebuffer on success or an error pointer on failure.
....@@ -148,51 +221,20 @@
148221 const struct drm_mode_fb_cmd2 *mode_cmd,
149222 const struct drm_framebuffer_funcs *funcs)
150223 {
151
- const struct drm_format_info *info;
152
- struct drm_gem_object *objs[4];
153224 struct drm_framebuffer *fb;
154
- int ret, i;
225
+ int ret;
155226
156
- info = drm_get_format_info(dev, mode_cmd);
157
- if (!info)
158
- return ERR_PTR(-EINVAL);
227
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
228
+ if (!fb)
229
+ return ERR_PTR(-ENOMEM);
159230
160
- for (i = 0; i < info->num_planes; i++) {
161
- unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
162
- unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
163
- unsigned int min_size;
164
-
165
- objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
166
- if (!objs[i]) {
167
- DRM_DEBUG_KMS("Failed to lookup GEM object\n");
168
- ret = -ENOENT;
169
- goto err_gem_object_put;
170
- }
171
-
172
- min_size = (height - 1) * mode_cmd->pitches[i]
173
- + width * info->bpp[i] / 8
174
- + mode_cmd->offsets[i];
175
-
176
- if (objs[i]->size < min_size) {
177
- drm_gem_object_put_unlocked(objs[i]);
178
- ret = -EINVAL;
179
- goto err_gem_object_put;
180
- }
181
- }
182
-
183
- fb = drm_gem_fb_alloc(dev, mode_cmd, objs, i, funcs);
184
- if (IS_ERR(fb)) {
185
- ret = PTR_ERR(fb);
186
- goto err_gem_object_put;
231
+ ret = drm_gem_fb_init_with_funcs(dev, fb, file, mode_cmd, funcs);
232
+ if (ret) {
233
+ kfree(fb);
234
+ return ERR_PTR(ret);
187235 }
188236
189237 return fb;
190
-
191
-err_gem_object_put:
192
- for (i--; i >= 0; i--)
193
- drm_gem_object_put_unlocked(objs[i]);
194
-
195
- return ERR_PTR(ret);
196238 }
197239 EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_funcs);
198240
....@@ -214,8 +256,8 @@
214256 *
215257 * If your hardware has special alignment or pitch requirements these should be
216258 * checked before calling this function. The function does buffer size
217
- * validation. Use drm_gem_fb_create_with_funcs() if you need to set
218
- * &drm_framebuffer_funcs.dirty.
259
+ * validation. Use drm_gem_fb_create_with_dirty() if you need framebuffer
260
+ * flushing.
219261 *
220262 * Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
221263 * The ADDFB2 IOCTL calls into this callback.
....@@ -232,35 +274,200 @@
232274 }
233275 EXPORT_SYMBOL_GPL(drm_gem_fb_create);
234276
277
+static const struct drm_framebuffer_funcs drm_gem_fb_funcs_dirtyfb = {
278
+ .destroy = drm_gem_fb_destroy,
279
+ .create_handle = drm_gem_fb_create_handle,
280
+ .dirty = drm_atomic_helper_dirtyfb,
281
+};
282
+
283
+/**
284
+ * drm_gem_fb_create_with_dirty() - Helper function for the
285
+ * &drm_mode_config_funcs.fb_create callback
286
+ * @dev: DRM device
287
+ * @file: DRM file that holds the GEM handle(s) backing the framebuffer
288
+ * @mode_cmd: Metadata from the userspace framebuffer creation request
289
+ *
290
+ * This function creates a new framebuffer object described by
291
+ * &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
292
+ * backing the framebuffer. drm_atomic_helper_dirtyfb() is used for the dirty
293
+ * callback giving framebuffer flushing through the atomic machinery. Use
294
+ * drm_gem_fb_create() if you don't need the dirty callback.
295
+ * The function does buffer size validation.
296
+ *
297
+ * Drivers should also call drm_plane_enable_fb_damage_clips() on all planes
298
+ * to enable userspace to use damage clips also with the ATOMIC IOCTL.
299
+ *
300
+ * Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
301
+ * The ADDFB2 IOCTL calls into this callback.
302
+ *
303
+ * Returns:
304
+ * Pointer to a &drm_framebuffer on success or an error pointer on failure.
305
+ */
306
+struct drm_framebuffer *
307
+drm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file,
308
+ const struct drm_mode_fb_cmd2 *mode_cmd)
309
+{
310
+ return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
311
+ &drm_gem_fb_funcs_dirtyfb);
312
+}
313
+EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty);
314
+
315
+static __u32 drm_gem_afbc_get_bpp(struct drm_device *dev,
316
+ const struct drm_mode_fb_cmd2 *mode_cmd)
317
+{
318
+ const struct drm_format_info *info;
319
+
320
+ info = drm_get_format_info(dev, mode_cmd);
321
+
322
+ /* use whatever a driver has set */
323
+ if (info->cpp[0])
324
+ return info->cpp[0] * 8;
325
+
326
+ /* guess otherwise */
327
+ switch (info->format) {
328
+ case DRM_FORMAT_YUV420_8BIT:
329
+ return 12;
330
+ case DRM_FORMAT_YUV420_10BIT:
331
+ return 15;
332
+ case DRM_FORMAT_VUY101010:
333
+ return 30;
334
+ default:
335
+ break;
336
+ }
337
+
338
+ /* all attempts failed */
339
+ return 0;
340
+}
341
+
342
+static int drm_gem_afbc_min_size(struct drm_device *dev,
343
+ const struct drm_mode_fb_cmd2 *mode_cmd,
344
+ struct drm_afbc_framebuffer *afbc_fb)
345
+{
346
+ __u32 n_blocks, w_alignment, h_alignment, hdr_alignment;
347
+ /* remove bpp when all users properly encode cpp in drm_format_info */
348
+ __u32 bpp;
349
+
350
+ switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
351
+ case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
352
+ afbc_fb->block_width = 16;
353
+ afbc_fb->block_height = 16;
354
+ break;
355
+ case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8:
356
+ afbc_fb->block_width = 32;
357
+ afbc_fb->block_height = 8;
358
+ break;
359
+ /* no user exists yet - fall through */
360
+ case AFBC_FORMAT_MOD_BLOCK_SIZE_64x4:
361
+ case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4:
362
+ default:
363
+ drm_dbg_kms(dev, "Invalid AFBC_FORMAT_MOD_BLOCK_SIZE: %lld.\n",
364
+ mode_cmd->modifier[0]
365
+ & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK);
366
+ return -EINVAL;
367
+ }
368
+
369
+ /* tiled header afbc */
370
+ w_alignment = afbc_fb->block_width;
371
+ h_alignment = afbc_fb->block_height;
372
+ hdr_alignment = AFBC_HDR_ALIGN;
373
+ if (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_TILED) {
374
+ w_alignment *= AFBC_TH_LAYOUT_ALIGNMENT;
375
+ h_alignment *= AFBC_TH_LAYOUT_ALIGNMENT;
376
+ hdr_alignment = AFBC_TH_BODY_START_ALIGNMENT;
377
+ }
378
+
379
+ afbc_fb->aligned_width = ALIGN(mode_cmd->width, w_alignment);
380
+ afbc_fb->aligned_height = ALIGN(mode_cmd->height, h_alignment);
381
+ afbc_fb->offset = mode_cmd->offsets[0];
382
+
383
+ bpp = drm_gem_afbc_get_bpp(dev, mode_cmd);
384
+ if (!bpp) {
385
+ drm_dbg_kms(dev, "Invalid AFBC bpp value: %d\n", bpp);
386
+ return -EINVAL;
387
+ }
388
+
389
+ n_blocks = (afbc_fb->aligned_width * afbc_fb->aligned_height)
390
+ / AFBC_SUPERBLOCK_PIXELS;
391
+ afbc_fb->afbc_size = ALIGN(n_blocks * AFBC_HEADER_SIZE, hdr_alignment);
392
+ afbc_fb->afbc_size += n_blocks * ALIGN(bpp * AFBC_SUPERBLOCK_PIXELS / 8,
393
+ AFBC_SUPERBLOCK_ALIGNMENT);
394
+
395
+ return 0;
396
+}
397
+
398
+/**
399
+ * drm_gem_fb_afbc_init() - Helper function for drivers using afbc to
400
+ * fill and validate all the afbc-specific
401
+ * struct drm_afbc_framebuffer members
402
+ *
403
+ * @dev: DRM device
404
+ * @afbc_fb: afbc-specific framebuffer
405
+ * @mode_cmd: Metadata from the userspace framebuffer creation request
406
+ * @afbc_fb: afbc framebuffer
407
+ *
408
+ * This function can be used by drivers which support afbc to complete
409
+ * the preparation of struct drm_afbc_framebuffer. It must be called after
410
+ * allocating the said struct and calling drm_gem_fb_init_with_funcs().
411
+ * It is caller's responsibility to put afbc_fb->base.obj objects in case
412
+ * the call is unsuccessful.
413
+ *
414
+ * Returns:
415
+ * Zero on success or a negative error value on failure.
416
+ */
417
+int drm_gem_fb_afbc_init(struct drm_device *dev,
418
+ const struct drm_mode_fb_cmd2 *mode_cmd,
419
+ struct drm_afbc_framebuffer *afbc_fb)
420
+{
421
+ const struct drm_format_info *info;
422
+ struct drm_gem_object **objs;
423
+ int ret;
424
+
425
+ objs = afbc_fb->base.obj;
426
+ info = drm_get_format_info(dev, mode_cmd);
427
+ if (!info)
428
+ return -EINVAL;
429
+
430
+ ret = drm_gem_afbc_min_size(dev, mode_cmd, afbc_fb);
431
+ if (ret < 0)
432
+ return ret;
433
+
434
+ if (objs[0]->size < afbc_fb->afbc_size)
435
+ return -EINVAL;
436
+
437
+ return 0;
438
+}
439
+EXPORT_SYMBOL_GPL(drm_gem_fb_afbc_init);
440
+
235441 /**
236442 * drm_gem_fb_prepare_fb() - Prepare a GEM backed framebuffer
237443 * @plane: Plane
238444 * @state: Plane state the fence will be attached to
239445 *
240
- * This function prepares a GEM backed framebuffer for scanout by checking if
241
- * the plane framebuffer has a DMA-BUF attached. If it does, it extracts the
242
- * exclusive fence and attaches it to the plane state for the atomic helper to
243
- * wait on. This function can be used as the &drm_plane_helper_funcs.prepare_fb
244
- * callback.
446
+ * This function extracts the exclusive fence from &drm_gem_object.resv and
447
+ * attaches it to plane state for the atomic helper to wait on. This is
448
+ * necessary to correctly implement implicit synchronization for any buffers
449
+ * shared as a struct &dma_buf. This function can be used as the
450
+ * &drm_plane_helper_funcs.prepare_fb callback.
245451 *
246452 * There is no need for &drm_plane_helper_funcs.cleanup_fb hook for simple
247453 * gem based framebuffer drivers which have their buffers always pinned in
248454 * memory.
455
+ *
456
+ * See drm_atomic_set_fence_for_plane() for a discussion of implicit and
457
+ * explicit fencing in atomic modeset updates.
249458 */
250459 int drm_gem_fb_prepare_fb(struct drm_plane *plane,
251460 struct drm_plane_state *state)
252461 {
253
- struct dma_buf *dma_buf;
462
+ struct drm_gem_object *obj;
254463 struct dma_fence *fence;
255464
256465 if (!state->fb)
257466 return 0;
258467
259
- dma_buf = drm_gem_fb_get_obj(state->fb, 0)->dma_buf;
260
- if (dma_buf) {
261
- fence = reservation_object_get_excl_rcu(dma_buf->resv);
262
- drm_atomic_set_fence_for_plane(state, fence);
263
- }
468
+ obj = drm_gem_fb_get_obj(state->fb, 0);
469
+ fence = dma_resv_get_excl_rcu(obj->resv);
470
+ drm_atomic_set_fence_for_plane(state, fence);
264471
265472 return 0;
266473 }
....@@ -272,10 +479,14 @@
272479 * @pipe: Simple display pipe
273480 * @plane_state: Plane state
274481 *
275
- * This function uses drm_gem_fb_prepare_fb() to check if the plane FB has a
276
- * &dma_buf attached, extracts the exclusive fence and attaches it to plane
277
- * state for the atomic helper to wait on. Drivers can use this as their
278
- * &drm_simple_display_pipe_funcs.prepare_fb callback.
482
+ * This function uses drm_gem_fb_prepare_fb() to extract the exclusive fence
483
+ * from &drm_gem_object.resv and attaches it to plane state for the atomic
484
+ * helper to wait on. This is necessary to correctly implement implicit
485
+ * synchronization for any buffers shared as a struct &dma_buf. Drivers can use
486
+ * this as their &drm_simple_display_pipe_funcs.prepare_fb callback.
487
+ *
488
+ * See drm_atomic_set_fence_for_plane() for a discussion of implicit and
489
+ * explicit fencing in atomic modeset updates.
279490 */
280491 int drm_gem_fb_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
281492 struct drm_plane_state *plane_state)
....@@ -283,46 +494,3 @@
283494 return drm_gem_fb_prepare_fb(&pipe->plane, plane_state);
284495 }
285496 EXPORT_SYMBOL(drm_gem_fb_simple_display_pipe_prepare_fb);
286
-
287
-/**
288
- * drm_gem_fbdev_fb_create - Create a GEM backed &drm_framebuffer for fbdev
289
- * emulation
290
- * @dev: DRM device
291
- * @sizes: fbdev size description
292
- * @pitch_align: Optional pitch alignment
293
- * @obj: GEM object backing the framebuffer
294
- * @funcs: Optional vtable to be used for the new framebuffer object when the
295
- * dirty callback is needed.
296
- *
297
- * This function creates a framebuffer from a &drm_fb_helper_surface_size
298
- * description for use in the &drm_fb_helper_funcs.fb_probe callback.
299
- *
300
- * Returns:
301
- * Pointer to a &drm_framebuffer on success or an error pointer on failure.
302
- */
303
-struct drm_framebuffer *
304
-drm_gem_fbdev_fb_create(struct drm_device *dev,
305
- struct drm_fb_helper_surface_size *sizes,
306
- unsigned int pitch_align, struct drm_gem_object *obj,
307
- const struct drm_framebuffer_funcs *funcs)
308
-{
309
- struct drm_mode_fb_cmd2 mode_cmd = { 0 };
310
-
311
- mode_cmd.width = sizes->surface_width;
312
- mode_cmd.height = sizes->surface_height;
313
- mode_cmd.pitches[0] = sizes->surface_width *
314
- DIV_ROUND_UP(sizes->surface_bpp, 8);
315
- if (pitch_align)
316
- mode_cmd.pitches[0] = roundup(mode_cmd.pitches[0],
317
- pitch_align);
318
- mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
319
- sizes->surface_depth);
320
- if (obj->size < mode_cmd.pitches[0] * mode_cmd.height)
321
- return ERR_PTR(-EINVAL);
322
-
323
- if (!funcs)
324
- funcs = &drm_gem_fb_funcs;
325
-
326
- return drm_gem_fb_alloc(dev, &mode_cmd, &obj, 1, funcs);
327
-}
328
-EXPORT_SYMBOL(drm_gem_fbdev_fb_create);