0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/slab.h>
0009 #include <linux/module.h>
0010
0011 #include <drm/drm_damage_helper.h>
0012 #include <drm/drm_fb_helper.h>
0013 #include <drm/drm_fourcc.h>
0014 #include <drm/drm_framebuffer.h>
0015 #include <drm/drm_gem.h>
0016 #include <drm/drm_gem_framebuffer_helper.h>
0017 #include <drm/drm_modeset_helper.h>
0018
0019 #include "drm_internal.h"
0020
0021 MODULE_IMPORT_NS(DMA_BUF);
0022
0023 #define AFBC_HEADER_SIZE 16
0024 #define AFBC_TH_LAYOUT_ALIGNMENT 8
0025 #define AFBC_HDR_ALIGN 64
0026 #define AFBC_SUPERBLOCK_PIXELS 256
0027 #define AFBC_SUPERBLOCK_ALIGNMENT 128
0028 #define AFBC_TH_BODY_START_ALIGNMENT 4096
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053 struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,
0054 unsigned int plane)
0055 {
0056 struct drm_device *dev = fb->dev;
0057
0058 if (drm_WARN_ON_ONCE(dev, plane >= ARRAY_SIZE(fb->obj)))
0059 return NULL;
0060 else if (drm_WARN_ON_ONCE(dev, !fb->obj[plane]))
0061 return NULL;
0062
0063 return fb->obj[plane];
0064 }
0065 EXPORT_SYMBOL_GPL(drm_gem_fb_get_obj);
0066
0067 static int
0068 drm_gem_fb_init(struct drm_device *dev,
0069 struct drm_framebuffer *fb,
0070 const struct drm_mode_fb_cmd2 *mode_cmd,
0071 struct drm_gem_object **obj, unsigned int num_planes,
0072 const struct drm_framebuffer_funcs *funcs)
0073 {
0074 unsigned int i;
0075 int ret;
0076
0077 drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
0078
0079 for (i = 0; i < num_planes; i++)
0080 fb->obj[i] = obj[i];
0081
0082 ret = drm_framebuffer_init(dev, fb, funcs);
0083 if (ret)
0084 drm_err(dev, "Failed to init framebuffer: %d\n", ret);
0085
0086 return ret;
0087 }
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097 void drm_gem_fb_destroy(struct drm_framebuffer *fb)
0098 {
0099 unsigned int i;
0100
0101 for (i = 0; i < fb->format->num_planes; i++)
0102 drm_gem_object_put(fb->obj[i]);
0103
0104 drm_framebuffer_cleanup(fb);
0105 kfree(fb);
0106 }
0107 EXPORT_SYMBOL(drm_gem_fb_destroy);
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122 int drm_gem_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file,
0123 unsigned int *handle)
0124 {
0125 return drm_gem_handle_create(file, fb->obj[0], handle);
0126 }
0127 EXPORT_SYMBOL(drm_gem_fb_create_handle);
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151 int drm_gem_fb_init_with_funcs(struct drm_device *dev,
0152 struct drm_framebuffer *fb,
0153 struct drm_file *file,
0154 const struct drm_mode_fb_cmd2 *mode_cmd,
0155 const struct drm_framebuffer_funcs *funcs)
0156 {
0157 const struct drm_format_info *info;
0158 struct drm_gem_object *objs[DRM_FORMAT_MAX_PLANES];
0159 unsigned int i;
0160 int ret;
0161
0162 info = drm_get_format_info(dev, mode_cmd);
0163 if (!info) {
0164 drm_dbg_kms(dev, "Failed to get FB format info\n");
0165 return -EINVAL;
0166 }
0167
0168 for (i = 0; i < info->num_planes; i++) {
0169 unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
0170 unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
0171 unsigned int min_size;
0172
0173 objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
0174 if (!objs[i]) {
0175 drm_dbg_kms(dev, "Failed to lookup GEM object\n");
0176 ret = -ENOENT;
0177 goto err_gem_object_put;
0178 }
0179
0180 min_size = (height - 1) * mode_cmd->pitches[i]
0181 + drm_format_info_min_pitch(info, i, width)
0182 + mode_cmd->offsets[i];
0183
0184 if (objs[i]->size < min_size) {
0185 drm_dbg_kms(dev,
0186 "GEM object size (%zu) smaller than minimum size (%u) for plane %d\n",
0187 objs[i]->size, min_size, i);
0188 drm_gem_object_put(objs[i]);
0189 ret = -EINVAL;
0190 goto err_gem_object_put;
0191 }
0192 }
0193
0194 ret = drm_gem_fb_init(dev, fb, mode_cmd, objs, i, funcs);
0195 if (ret)
0196 goto err_gem_object_put;
0197
0198 return 0;
0199
0200 err_gem_object_put:
0201 while (i > 0) {
0202 --i;
0203 drm_gem_object_put(objs[i]);
0204 }
0205 return ret;
0206 }
0207 EXPORT_SYMBOL_GPL(drm_gem_fb_init_with_funcs);
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225 struct drm_framebuffer *
0226 drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
0227 const struct drm_mode_fb_cmd2 *mode_cmd,
0228 const struct drm_framebuffer_funcs *funcs)
0229 {
0230 struct drm_framebuffer *fb;
0231 int ret;
0232
0233 fb = kzalloc(sizeof(*fb), GFP_KERNEL);
0234 if (!fb)
0235 return ERR_PTR(-ENOMEM);
0236
0237 ret = drm_gem_fb_init_with_funcs(dev, fb, file, mode_cmd, funcs);
0238 if (ret) {
0239 kfree(fb);
0240 return ERR_PTR(ret);
0241 }
0242
0243 return fb;
0244 }
0245 EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_funcs);
0246
0247 static const struct drm_framebuffer_funcs drm_gem_fb_funcs = {
0248 .destroy = drm_gem_fb_destroy,
0249 .create_handle = drm_gem_fb_create_handle,
0250 };
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273
0274 struct drm_framebuffer *
0275 drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
0276 const struct drm_mode_fb_cmd2 *mode_cmd)
0277 {
0278 return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
0279 &drm_gem_fb_funcs);
0280 }
0281 EXPORT_SYMBOL_GPL(drm_gem_fb_create);
0282
0283 static const struct drm_framebuffer_funcs drm_gem_fb_funcs_dirtyfb = {
0284 .destroy = drm_gem_fb_destroy,
0285 .create_handle = drm_gem_fb_create_handle,
0286 .dirty = drm_atomic_helper_dirtyfb,
0287 };
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312 struct drm_framebuffer *
0313 drm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file,
0314 const struct drm_mode_fb_cmd2 *mode_cmd)
0315 {
0316 return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
0317 &drm_gem_fb_funcs_dirtyfb);
0318 }
0319 EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty);
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344 int drm_gem_fb_vmap(struct drm_framebuffer *fb, struct iosys_map *map,
0345 struct iosys_map *data)
0346 {
0347 struct drm_gem_object *obj;
0348 unsigned int i;
0349 int ret;
0350
0351 for (i = 0; i < fb->format->num_planes; ++i) {
0352 obj = drm_gem_fb_get_obj(fb, i);
0353 if (!obj) {
0354 ret = -EINVAL;
0355 goto err_drm_gem_vunmap;
0356 }
0357 ret = drm_gem_vmap(obj, &map[i]);
0358 if (ret)
0359 goto err_drm_gem_vunmap;
0360 }
0361
0362 if (data) {
0363 for (i = 0; i < fb->format->num_planes; ++i) {
0364 memcpy(&data[i], &map[i], sizeof(data[i]));
0365 if (iosys_map_is_null(&data[i]))
0366 continue;
0367 iosys_map_incr(&data[i], fb->offsets[i]);
0368 }
0369 }
0370
0371 return 0;
0372
0373 err_drm_gem_vunmap:
0374 while (i) {
0375 --i;
0376 obj = drm_gem_fb_get_obj(fb, i);
0377 if (!obj)
0378 continue;
0379 drm_gem_vunmap(obj, &map[i]);
0380 }
0381 return ret;
0382 }
0383 EXPORT_SYMBOL(drm_gem_fb_vmap);
0384
0385
0386
0387
0388
0389
0390
0391
0392
0393
0394 void drm_gem_fb_vunmap(struct drm_framebuffer *fb, struct iosys_map *map)
0395 {
0396 unsigned int i = fb->format->num_planes;
0397 struct drm_gem_object *obj;
0398
0399 while (i) {
0400 --i;
0401 obj = drm_gem_fb_get_obj(fb, i);
0402 if (!obj)
0403 continue;
0404 if (iosys_map_is_null(&map[i]))
0405 continue;
0406 drm_gem_vunmap(obj, &map[i]);
0407 }
0408 }
0409 EXPORT_SYMBOL(drm_gem_fb_vunmap);
0410
0411 static void __drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir,
0412 unsigned int num_planes)
0413 {
0414 struct dma_buf_attachment *import_attach;
0415 struct drm_gem_object *obj;
0416 int ret;
0417
0418 while (num_planes) {
0419 --num_planes;
0420 obj = drm_gem_fb_get_obj(fb, num_planes);
0421 if (!obj)
0422 continue;
0423 import_attach = obj->import_attach;
0424 if (!import_attach)
0425 continue;
0426 ret = dma_buf_end_cpu_access(import_attach->dmabuf, dir);
0427 if (ret)
0428 drm_err(fb->dev, "dma_buf_end_cpu_access(%u, %d) failed: %d\n",
0429 ret, num_planes, dir);
0430 }
0431 }
0432
0433
0434
0435
0436
0437
0438
0439
0440
0441
0442
0443
0444
0445
0446
0447 int drm_gem_fb_begin_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir)
0448 {
0449 struct dma_buf_attachment *import_attach;
0450 struct drm_gem_object *obj;
0451 unsigned int i;
0452 int ret;
0453
0454 for (i = 0; i < fb->format->num_planes; ++i) {
0455 obj = drm_gem_fb_get_obj(fb, i);
0456 if (!obj) {
0457 ret = -EINVAL;
0458 goto err___drm_gem_fb_end_cpu_access;
0459 }
0460 import_attach = obj->import_attach;
0461 if (!import_attach)
0462 continue;
0463 ret = dma_buf_begin_cpu_access(import_attach->dmabuf, dir);
0464 if (ret)
0465 goto err___drm_gem_fb_end_cpu_access;
0466 }
0467
0468 return 0;
0469
0470 err___drm_gem_fb_end_cpu_access:
0471 __drm_gem_fb_end_cpu_access(fb, dir, i);
0472 return ret;
0473 }
0474 EXPORT_SYMBOL(drm_gem_fb_begin_cpu_access);
0475
0476
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486
0487 void drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir)
0488 {
0489 __drm_gem_fb_end_cpu_access(fb, dir, fb->format->num_planes);
0490 }
0491 EXPORT_SYMBOL(drm_gem_fb_end_cpu_access);
0492
0493 static __u32 drm_gem_afbc_get_bpp(struct drm_device *dev,
0494 const struct drm_mode_fb_cmd2 *mode_cmd)
0495 {
0496 const struct drm_format_info *info;
0497
0498 info = drm_get_format_info(dev, mode_cmd);
0499
0500
0501 if (info->cpp[0])
0502 return info->cpp[0] * 8;
0503
0504
0505 switch (info->format) {
0506 case DRM_FORMAT_YUV420_8BIT:
0507 return 12;
0508 case DRM_FORMAT_YUV420_10BIT:
0509 return 15;
0510 case DRM_FORMAT_VUY101010:
0511 return 30;
0512 default:
0513 break;
0514 }
0515
0516
0517 return 0;
0518 }
0519
0520 static int drm_gem_afbc_min_size(struct drm_device *dev,
0521 const struct drm_mode_fb_cmd2 *mode_cmd,
0522 struct drm_afbc_framebuffer *afbc_fb)
0523 {
0524 __u32 n_blocks, w_alignment, h_alignment, hdr_alignment;
0525
0526 __u32 bpp;
0527
0528 switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
0529 case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
0530 afbc_fb->block_width = 16;
0531 afbc_fb->block_height = 16;
0532 break;
0533 case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8:
0534 afbc_fb->block_width = 32;
0535 afbc_fb->block_height = 8;
0536 break;
0537
0538 case AFBC_FORMAT_MOD_BLOCK_SIZE_64x4:
0539 case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4:
0540 default:
0541 drm_dbg_kms(dev, "Invalid AFBC_FORMAT_MOD_BLOCK_SIZE: %lld.\n",
0542 mode_cmd->modifier[0]
0543 & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK);
0544 return -EINVAL;
0545 }
0546
0547
0548 w_alignment = afbc_fb->block_width;
0549 h_alignment = afbc_fb->block_height;
0550 hdr_alignment = AFBC_HDR_ALIGN;
0551 if (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_TILED) {
0552 w_alignment *= AFBC_TH_LAYOUT_ALIGNMENT;
0553 h_alignment *= AFBC_TH_LAYOUT_ALIGNMENT;
0554 hdr_alignment = AFBC_TH_BODY_START_ALIGNMENT;
0555 }
0556
0557 afbc_fb->aligned_width = ALIGN(mode_cmd->width, w_alignment);
0558 afbc_fb->aligned_height = ALIGN(mode_cmd->height, h_alignment);
0559 afbc_fb->offset = mode_cmd->offsets[0];
0560
0561 bpp = drm_gem_afbc_get_bpp(dev, mode_cmd);
0562 if (!bpp) {
0563 drm_dbg_kms(dev, "Invalid AFBC bpp value: %d\n", bpp);
0564 return -EINVAL;
0565 }
0566
0567 n_blocks = (afbc_fb->aligned_width * afbc_fb->aligned_height)
0568 / AFBC_SUPERBLOCK_PIXELS;
0569 afbc_fb->afbc_size = ALIGN(n_blocks * AFBC_HEADER_SIZE, hdr_alignment);
0570 afbc_fb->afbc_size += n_blocks * ALIGN(bpp * AFBC_SUPERBLOCK_PIXELS / 8,
0571 AFBC_SUPERBLOCK_ALIGNMENT);
0572
0573 return 0;
0574 }
0575
0576
0577
0578
0579
0580
0581
0582
0583
0584
0585
0586
0587
0588
0589
0590
0591
0592
0593
0594
0595 int drm_gem_fb_afbc_init(struct drm_device *dev,
0596 const struct drm_mode_fb_cmd2 *mode_cmd,
0597 struct drm_afbc_framebuffer *afbc_fb)
0598 {
0599 const struct drm_format_info *info;
0600 struct drm_gem_object **objs;
0601 int ret;
0602
0603 objs = afbc_fb->base.obj;
0604 info = drm_get_format_info(dev, mode_cmd);
0605 if (!info)
0606 return -EINVAL;
0607
0608 ret = drm_gem_afbc_min_size(dev, mode_cmd, afbc_fb);
0609 if (ret < 0)
0610 return ret;
0611
0612 if (objs[0]->size < afbc_fb->afbc_size)
0613 return -EINVAL;
0614
0615 return 0;
0616 }
0617 EXPORT_SYMBOL_GPL(drm_gem_fb_afbc_init);