| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * drm gem framebuffer helper functions |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * 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. |
|---|
| 10 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #include <linux/dma-buf.h> |
|---|
| 13 | 9 | #include <linux/dma-fence.h> |
|---|
| 14 | | -#include <linux/reservation.h> |
|---|
| 10 | +#include <linux/dma-resv.h> |
|---|
| 15 | 11 | #include <linux/slab.h> |
|---|
| 16 | 12 | |
|---|
| 17 | | -#include <drm/drmP.h> |
|---|
| 18 | 13 | #include <drm/drm_atomic.h> |
|---|
| 14 | +#include <drm/drm_atomic_uapi.h> |
|---|
| 15 | +#include <drm/drm_damage_helper.h> |
|---|
| 19 | 16 | #include <drm/drm_fb_helper.h> |
|---|
| 20 | 17 | #include <drm/drm_fourcc.h> |
|---|
| 21 | 18 | #include <drm/drm_framebuffer.h> |
|---|
| .. | .. |
|---|
| 23 | 20 | #include <drm/drm_gem_framebuffer_helper.h> |
|---|
| 24 | 21 | #include <drm/drm_modeset_helper.h> |
|---|
| 25 | 22 | #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 |
|---|
| 26 | 30 | |
|---|
| 27 | 31 | /** |
|---|
| 28 | 32 | * DOC: overview |
|---|
| .. | .. |
|---|
| 57 | 61 | } |
|---|
| 58 | 62 | EXPORT_SYMBOL_GPL(drm_gem_fb_get_obj); |
|---|
| 59 | 63 | |
|---|
| 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, |
|---|
| 62 | 67 | const struct drm_mode_fb_cmd2 *mode_cmd, |
|---|
| 63 | 68 | struct drm_gem_object **obj, unsigned int num_planes, |
|---|
| 64 | 69 | const struct drm_framebuffer_funcs *funcs) |
|---|
| 65 | 70 | { |
|---|
| 66 | | - struct drm_framebuffer *fb; |
|---|
| 67 | 71 | int ret, i; |
|---|
| 68 | | - |
|---|
| 69 | | - fb = kzalloc(sizeof(*fb), GFP_KERNEL); |
|---|
| 70 | | - if (!fb) |
|---|
| 71 | | - return ERR_PTR(-ENOMEM); |
|---|
| 72 | 72 | |
|---|
| 73 | 73 | drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); |
|---|
| 74 | 74 | |
|---|
| .. | .. |
|---|
| 76 | 76 | fb->obj[i] = obj[i]; |
|---|
| 77 | 77 | |
|---|
| 78 | 78 | 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); |
|---|
| 85 | 81 | |
|---|
| 86 | | - return fb; |
|---|
| 82 | + return ret; |
|---|
| 87 | 83 | } |
|---|
| 88 | 84 | |
|---|
| 89 | 85 | /** |
|---|
| .. | .. |
|---|
| 99 | 95 | int i; |
|---|
| 100 | 96 | |
|---|
| 101 | 97 | for (i = 0; i < 4; i++) |
|---|
| 102 | | - drm_gem_object_put_unlocked(fb->obj[i]); |
|---|
| 98 | + drm_gem_object_put(fb->obj[i]); |
|---|
| 103 | 99 | |
|---|
| 104 | 100 | drm_framebuffer_cleanup(fb); |
|---|
| 105 | 101 | kfree(fb); |
|---|
| .. | .. |
|---|
| 127 | 123 | EXPORT_SYMBOL(drm_gem_fb_create_handle); |
|---|
| 128 | 124 | |
|---|
| 129 | 125 | /** |
|---|
| 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 | +/** |
|---|
| 130 | 204 | * drm_gem_fb_create_with_funcs() - Helper function for the |
|---|
| 131 | 205 | * &drm_mode_config_funcs.fb_create |
|---|
| 132 | 206 | * callback |
|---|
| .. | .. |
|---|
| 135 | 209 | * @mode_cmd: Metadata from the userspace framebuffer creation request |
|---|
| 136 | 210 | * @funcs: vtable to be used for the new framebuffer object |
|---|
| 137 | 211 | * |
|---|
| 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. |
|---|
| 142 | 215 | * |
|---|
| 143 | 216 | * Returns: |
|---|
| 144 | 217 | * Pointer to a &drm_framebuffer on success or an error pointer on failure. |
|---|
| .. | .. |
|---|
| 148 | 221 | const struct drm_mode_fb_cmd2 *mode_cmd, |
|---|
| 149 | 222 | const struct drm_framebuffer_funcs *funcs) |
|---|
| 150 | 223 | { |
|---|
| 151 | | - const struct drm_format_info *info; |
|---|
| 152 | | - struct drm_gem_object *objs[4]; |
|---|
| 153 | 224 | struct drm_framebuffer *fb; |
|---|
| 154 | | - int ret, i; |
|---|
| 225 | + int ret; |
|---|
| 155 | 226 | |
|---|
| 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); |
|---|
| 159 | 230 | |
|---|
| 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); |
|---|
| 187 | 235 | } |
|---|
| 188 | 236 | |
|---|
| 189 | 237 | 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); |
|---|
| 196 | 238 | } |
|---|
| 197 | 239 | EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_funcs); |
|---|
| 198 | 240 | |
|---|
| .. | .. |
|---|
| 214 | 256 | * |
|---|
| 215 | 257 | * If your hardware has special alignment or pitch requirements these should be |
|---|
| 216 | 258 | * 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. |
|---|
| 219 | 261 | * |
|---|
| 220 | 262 | * Drivers can use this as their &drm_mode_config_funcs.fb_create callback. |
|---|
| 221 | 263 | * The ADDFB2 IOCTL calls into this callback. |
|---|
| .. | .. |
|---|
| 232 | 274 | } |
|---|
| 233 | 275 | EXPORT_SYMBOL_GPL(drm_gem_fb_create); |
|---|
| 234 | 276 | |
|---|
| 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 | + |
|---|
| 235 | 441 | /** |
|---|
| 236 | 442 | * drm_gem_fb_prepare_fb() - Prepare a GEM backed framebuffer |
|---|
| 237 | 443 | * @plane: Plane |
|---|
| 238 | 444 | * @state: Plane state the fence will be attached to |
|---|
| 239 | 445 | * |
|---|
| 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. |
|---|
| 245 | 451 | * |
|---|
| 246 | 452 | * There is no need for &drm_plane_helper_funcs.cleanup_fb hook for simple |
|---|
| 247 | 453 | * gem based framebuffer drivers which have their buffers always pinned in |
|---|
| 248 | 454 | * memory. |
|---|
| 455 | + * |
|---|
| 456 | + * See drm_atomic_set_fence_for_plane() for a discussion of implicit and |
|---|
| 457 | + * explicit fencing in atomic modeset updates. |
|---|
| 249 | 458 | */ |
|---|
| 250 | 459 | int drm_gem_fb_prepare_fb(struct drm_plane *plane, |
|---|
| 251 | 460 | struct drm_plane_state *state) |
|---|
| 252 | 461 | { |
|---|
| 253 | | - struct dma_buf *dma_buf; |
|---|
| 462 | + struct drm_gem_object *obj; |
|---|
| 254 | 463 | struct dma_fence *fence; |
|---|
| 255 | 464 | |
|---|
| 256 | 465 | if (!state->fb) |
|---|
| 257 | 466 | return 0; |
|---|
| 258 | 467 | |
|---|
| 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); |
|---|
| 264 | 471 | |
|---|
| 265 | 472 | return 0; |
|---|
| 266 | 473 | } |
|---|
| .. | .. |
|---|
| 272 | 479 | * @pipe: Simple display pipe |
|---|
| 273 | 480 | * @plane_state: Plane state |
|---|
| 274 | 481 | * |
|---|
| 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. |
|---|
| 279 | 490 | */ |
|---|
| 280 | 491 | int drm_gem_fb_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, |
|---|
| 281 | 492 | struct drm_plane_state *plane_state) |
|---|
| .. | .. |
|---|
| 283 | 494 | return drm_gem_fb_prepare_fb(&pipe->plane, plane_state); |
|---|
| 284 | 495 | } |
|---|
| 285 | 496 | 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); |
|---|