linux/drivers/gpu/drm/drm_gem_framebuffer_helper.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * drm gem framebuffer helper functions
   4 *
   5 * Copyright (C) 2017 Noralf Trønnes
   6 */
   7
   8#include <linux/slab.h>
   9
  10#include <drm/drm_damage_helper.h>
  11#include <drm/drm_fb_helper.h>
  12#include <drm/drm_fourcc.h>
  13#include <drm/drm_framebuffer.h>
  14#include <drm/drm_gem.h>
  15#include <drm/drm_gem_framebuffer_helper.h>
  16#include <drm/drm_modeset_helper.h>
  17
  18#include "drm_internal.h"
  19
  20#define AFBC_HEADER_SIZE                16
  21#define AFBC_TH_LAYOUT_ALIGNMENT        8
  22#define AFBC_HDR_ALIGN                  64
  23#define AFBC_SUPERBLOCK_PIXELS          256
  24#define AFBC_SUPERBLOCK_ALIGNMENT       128
  25#define AFBC_TH_BODY_START_ALIGNMENT    4096
  26
  27/**
  28 * DOC: overview
  29 *
  30 * This library provides helpers for drivers that don't subclass
  31 * &drm_framebuffer and use &drm_gem_object for their backing storage.
  32 *
  33 * Drivers without additional needs to validate framebuffers can simply use
  34 * drm_gem_fb_create() and everything is wired up automatically. Other drivers
  35 * can use all parts independently.
  36 */
  37
  38/**
  39 * drm_gem_fb_get_obj() - Get GEM object backing the framebuffer
  40 * @fb: Framebuffer
  41 * @plane: Plane index
  42 *
  43 * No additional reference is taken beyond the one that the &drm_frambuffer
  44 * already holds.
  45 *
  46 * Returns:
  47 * Pointer to &drm_gem_object for the given framebuffer and plane index or NULL
  48 * if it does not exist.
  49 */
  50struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,
  51                                          unsigned int plane)
  52{
  53        if (plane >= ARRAY_SIZE(fb->obj))
  54                return NULL;
  55
  56        return fb->obj[plane];
  57}
  58EXPORT_SYMBOL_GPL(drm_gem_fb_get_obj);
  59
  60static int
  61drm_gem_fb_init(struct drm_device *dev,
  62                 struct drm_framebuffer *fb,
  63                 const struct drm_mode_fb_cmd2 *mode_cmd,
  64                 struct drm_gem_object **obj, unsigned int num_planes,
  65                 const struct drm_framebuffer_funcs *funcs)
  66{
  67        unsigned int i;
  68        int ret;
  69
  70        drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
  71
  72        for (i = 0; i < num_planes; i++)
  73                fb->obj[i] = obj[i];
  74
  75        ret = drm_framebuffer_init(dev, fb, funcs);
  76        if (ret)
  77                drm_err(dev, "Failed to init framebuffer: %d\n", ret);
  78
  79        return ret;
  80}
  81
  82/**
  83 * drm_gem_fb_destroy - Free GEM backed framebuffer
  84 * @fb: Framebuffer
  85 *
  86 * Frees a GEM backed framebuffer with its backing buffer(s) and the structure
  87 * itself. Drivers can use this as their &drm_framebuffer_funcs->destroy
  88 * callback.
  89 */
  90void drm_gem_fb_destroy(struct drm_framebuffer *fb)
  91{
  92        size_t i;
  93
  94        for (i = 0; i < ARRAY_SIZE(fb->obj); i++)
  95                drm_gem_object_put(fb->obj[i]);
  96
  97        drm_framebuffer_cleanup(fb);
  98        kfree(fb);
  99}
 100EXPORT_SYMBOL(drm_gem_fb_destroy);
 101
 102/**
 103 * drm_gem_fb_create_handle - Create handle for GEM backed framebuffer
 104 * @fb: Framebuffer
 105 * @file: DRM file to register the handle for
 106 * @handle: Pointer to return the created handle
 107 *
 108 * This function creates a handle for the GEM object backing the framebuffer.
 109 * Drivers can use this as their &drm_framebuffer_funcs->create_handle
 110 * callback. The GETFB IOCTL calls into this callback.
 111 *
 112 * Returns:
 113 * 0 on success or a negative error code on failure.
 114 */
 115int drm_gem_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file,
 116                             unsigned int *handle)
 117{
 118        return drm_gem_handle_create(file, fb->obj[0], handle);
 119}
 120EXPORT_SYMBOL(drm_gem_fb_create_handle);
 121
 122/**
 123 * drm_gem_fb_init_with_funcs() - Helper function for implementing
 124 *                                &drm_mode_config_funcs.fb_create
 125 *                                callback in cases when the driver
 126 *                                allocates a subclass of
 127 *                                struct drm_framebuffer
 128 * @dev: DRM device
 129 * @fb: framebuffer object
 130 * @file: DRM file that holds the GEM handle(s) backing the framebuffer
 131 * @mode_cmd: Metadata from the userspace framebuffer creation request
 132 * @funcs: vtable to be used for the new framebuffer object
 133 *
 134 * This function can be used to set &drm_framebuffer_funcs for drivers that need
 135 * custom framebuffer callbacks. Use drm_gem_fb_create() if you don't need to
 136 * change &drm_framebuffer_funcs. The function does buffer size validation.
 137 * The buffer size validation is for a general case, though, so users should
 138 * pay attention to the checks being appropriate for them or, at least,
 139 * non-conflicting.
 140 *
 141 * Returns:
 142 * Zero or a negative error code.
 143 */
 144int drm_gem_fb_init_with_funcs(struct drm_device *dev,
 145                               struct drm_framebuffer *fb,
 146                               struct drm_file *file,
 147                               const struct drm_mode_fb_cmd2 *mode_cmd,
 148                               const struct drm_framebuffer_funcs *funcs)
 149{
 150        const struct drm_format_info *info;
 151        struct drm_gem_object *objs[DRM_FORMAT_MAX_PLANES];
 152        unsigned int i;
 153        int ret;
 154
 155        info = drm_get_format_info(dev, mode_cmd);
 156        if (!info) {
 157                drm_dbg_kms(dev, "Failed to get FB format info\n");
 158                return -EINVAL;
 159        }
 160
 161        for (i = 0; i < info->num_planes; i++) {
 162                unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
 163                unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
 164                unsigned int min_size;
 165
 166                objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
 167                if (!objs[i]) {
 168                        drm_dbg_kms(dev, "Failed to lookup GEM object\n");
 169                        ret = -ENOENT;
 170                        goto err_gem_object_put;
 171                }
 172
 173                min_size = (height - 1) * mode_cmd->pitches[i]
 174                         + drm_format_info_min_pitch(info, i, width)
 175                         + mode_cmd->offsets[i];
 176
 177                if (objs[i]->size < min_size) {
 178                        drm_dbg_kms(dev,
 179                                    "GEM object size (%zu) smaller than minimum size (%u) for plane %d\n",
 180                                    objs[i]->size, min_size, i);
 181                        drm_gem_object_put(objs[i]);
 182                        ret = -EINVAL;
 183                        goto err_gem_object_put;
 184                }
 185        }
 186
 187        ret = drm_gem_fb_init(dev, fb, mode_cmd, objs, i, funcs);
 188        if (ret)
 189                goto err_gem_object_put;
 190
 191        return 0;
 192
 193err_gem_object_put:
 194        while (i > 0) {
 195                --i;
 196                drm_gem_object_put(objs[i]);
 197        }
 198        return ret;
 199}
 200EXPORT_SYMBOL_GPL(drm_gem_fb_init_with_funcs);
 201
 202/**
 203 * drm_gem_fb_create_with_funcs() - Helper function for the
 204 *                                  &drm_mode_config_funcs.fb_create
 205 *                                  callback
 206 * @dev: DRM device
 207 * @file: DRM file that holds the GEM handle(s) backing the framebuffer
 208 * @mode_cmd: Metadata from the userspace framebuffer creation request
 209 * @funcs: vtable to be used for the new framebuffer object
 210 *
 211 * This function can be used to set &drm_framebuffer_funcs for drivers that need
 212 * custom framebuffer callbacks. Use drm_gem_fb_create() if you don't need to
 213 * change &drm_framebuffer_funcs. The function does buffer size validation.
 214 *
 215 * Returns:
 216 * Pointer to a &drm_framebuffer on success or an error pointer on failure.
 217 */
 218struct drm_framebuffer *
 219drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
 220                             const struct drm_mode_fb_cmd2 *mode_cmd,
 221                             const struct drm_framebuffer_funcs *funcs)
 222{
 223        struct drm_framebuffer *fb;
 224        int ret;
 225
 226        fb = kzalloc(sizeof(*fb), GFP_KERNEL);
 227        if (!fb)
 228                return ERR_PTR(-ENOMEM);
 229
 230        ret = drm_gem_fb_init_with_funcs(dev, fb, file, mode_cmd, funcs);
 231        if (ret) {
 232                kfree(fb);
 233                return ERR_PTR(ret);
 234        }
 235
 236        return fb;
 237}
 238EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_funcs);
 239
 240static const struct drm_framebuffer_funcs drm_gem_fb_funcs = {
 241        .destroy        = drm_gem_fb_destroy,
 242        .create_handle  = drm_gem_fb_create_handle,
 243};
 244
 245/**
 246 * drm_gem_fb_create() - Helper function for the
 247 *                       &drm_mode_config_funcs.fb_create callback
 248 * @dev: DRM device
 249 * @file: DRM file that holds the GEM handle(s) backing the framebuffer
 250 * @mode_cmd: Metadata from the userspace framebuffer creation request
 251 *
 252 * This function creates a new framebuffer object described by
 253 * &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
 254 * backing the framebuffer.
 255 *
 256 * If your hardware has special alignment or pitch requirements these should be
 257 * checked before calling this function. The function does buffer size
 258 * validation. Use drm_gem_fb_create_with_dirty() if you need framebuffer
 259 * flushing.
 260 *
 261 * Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
 262 * The ADDFB2 IOCTL calls into this callback.
 263 *
 264 * Returns:
 265 * Pointer to a &drm_framebuffer on success or an error pointer on failure.
 266 */
 267struct drm_framebuffer *
 268drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
 269                  const struct drm_mode_fb_cmd2 *mode_cmd)
 270{
 271        return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
 272                                            &drm_gem_fb_funcs);
 273}
 274EXPORT_SYMBOL_GPL(drm_gem_fb_create);
 275
 276static const struct drm_framebuffer_funcs drm_gem_fb_funcs_dirtyfb = {
 277        .destroy        = drm_gem_fb_destroy,
 278        .create_handle  = drm_gem_fb_create_handle,
 279        .dirty          = drm_atomic_helper_dirtyfb,
 280};
 281
 282/**
 283 * drm_gem_fb_create_with_dirty() - Helper function for the
 284 *                       &drm_mode_config_funcs.fb_create callback
 285 * @dev: DRM device
 286 * @file: DRM file that holds the GEM handle(s) backing the framebuffer
 287 * @mode_cmd: Metadata from the userspace framebuffer creation request
 288 *
 289 * This function creates a new framebuffer object described by
 290 * &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
 291 * backing the framebuffer. drm_atomic_helper_dirtyfb() is used for the dirty
 292 * callback giving framebuffer flushing through the atomic machinery. Use
 293 * drm_gem_fb_create() if you don't need the dirty callback.
 294 * The function does buffer size validation.
 295 *
 296 * Drivers should also call drm_plane_enable_fb_damage_clips() on all planes
 297 * to enable userspace to use damage clips also with the ATOMIC IOCTL.
 298 *
 299 * Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
 300 * The ADDFB2 IOCTL calls into this callback.
 301 *
 302 * Returns:
 303 * Pointer to a &drm_framebuffer on success or an error pointer on failure.
 304 */
 305struct drm_framebuffer *
 306drm_gem_fb_create_with_dirty(struct drm_device *dev, struct drm_file *file,
 307                             const struct drm_mode_fb_cmd2 *mode_cmd)
 308{
 309        return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
 310                                            &drm_gem_fb_funcs_dirtyfb);
 311}
 312EXPORT_SYMBOL_GPL(drm_gem_fb_create_with_dirty);
 313
 314/**
 315 * drm_gem_fb_vmap - maps all framebuffer BOs into kernel address space
 316 * @fb: the framebuffer
 317 * @map: returns the mapping's address for each BO
 318 * @data: returns the data address for each BO, can be NULL
 319 *
 320 * This function maps all buffer objects of the given framebuffer into
 321 * kernel address space and stores them in struct dma_buf_map. If the
 322 * mapping operation fails for one of the BOs, the function unmaps the
 323 * already established mappings automatically.
 324 *
 325 * Callers that want to access a BO's stored data should pass @data.
 326 * The argument returns the addresses of the data stored in each BO. This
 327 * is different from @map if the framebuffer's offsets field is non-zero.
 328 *
 329 * See drm_gem_fb_vunmap() for unmapping.
 330 *
 331 * Returns:
 332 * 0 on success, or a negative errno code otherwise.
 333 */
 334int drm_gem_fb_vmap(struct drm_framebuffer *fb,
 335                    struct dma_buf_map map[static DRM_FORMAT_MAX_PLANES],
 336                    struct dma_buf_map data[DRM_FORMAT_MAX_PLANES])
 337{
 338        struct drm_gem_object *obj;
 339        unsigned int i;
 340        int ret;
 341
 342        for (i = 0; i < DRM_FORMAT_MAX_PLANES; ++i) {
 343                obj = drm_gem_fb_get_obj(fb, i);
 344                if (!obj) {
 345                        dma_buf_map_clear(&map[i]);
 346                        continue;
 347                }
 348                ret = drm_gem_vmap(obj, &map[i]);
 349                if (ret)
 350                        goto err_drm_gem_vunmap;
 351        }
 352
 353        if (data) {
 354                for (i = 0; i < DRM_FORMAT_MAX_PLANES; ++i) {
 355                        memcpy(&data[i], &map[i], sizeof(data[i]));
 356                        if (dma_buf_map_is_null(&data[i]))
 357                                continue;
 358                        dma_buf_map_incr(&data[i], fb->offsets[i]);
 359                }
 360        }
 361
 362        return 0;
 363
 364err_drm_gem_vunmap:
 365        while (i) {
 366                --i;
 367                obj = drm_gem_fb_get_obj(fb, i);
 368                if (!obj)
 369                        continue;
 370                drm_gem_vunmap(obj, &map[i]);
 371        }
 372        return ret;
 373}
 374EXPORT_SYMBOL(drm_gem_fb_vmap);
 375
 376/**
 377 * drm_gem_fb_vunmap - unmaps framebuffer BOs from kernel address space
 378 * @fb: the framebuffer
 379 * @map: mapping addresses as returned by drm_gem_fb_vmap()
 380 *
 381 * This function unmaps all buffer objects of the given framebuffer.
 382 *
 383 * See drm_gem_fb_vmap() for more information.
 384 */
 385void drm_gem_fb_vunmap(struct drm_framebuffer *fb,
 386                       struct dma_buf_map map[static DRM_FORMAT_MAX_PLANES])
 387{
 388        unsigned int i = DRM_FORMAT_MAX_PLANES;
 389        struct drm_gem_object *obj;
 390
 391        while (i) {
 392                --i;
 393                obj = drm_gem_fb_get_obj(fb, i);
 394                if (!obj)
 395                        continue;
 396                if (dma_buf_map_is_null(&map[i]))
 397                        continue;
 398                drm_gem_vunmap(obj, &map[i]);
 399        }
 400}
 401EXPORT_SYMBOL(drm_gem_fb_vunmap);
 402
 403/**
 404 * drm_gem_fb_begin_cpu_access - prepares GEM buffer objects for CPU access
 405 * @fb: the framebuffer
 406 * @dir: access mode
 407 *
 408 * Prepares a framebuffer's GEM buffer objects for CPU access. This function
 409 * must be called before accessing the BO data within the kernel. For imported
 410 * BOs, the function calls dma_buf_begin_cpu_access().
 411 *
 412 * See drm_gem_fb_end_cpu_access() for signalling the end of CPU access.
 413 *
 414 * Returns:
 415 * 0 on success, or a negative errno code otherwise.
 416 */
 417int drm_gem_fb_begin_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir)
 418{
 419        struct dma_buf_attachment *import_attach;
 420        struct drm_gem_object *obj;
 421        size_t i;
 422        int ret, ret2;
 423
 424        for (i = 0; i < ARRAY_SIZE(fb->obj); ++i) {
 425                obj = drm_gem_fb_get_obj(fb, i);
 426                if (!obj)
 427                        continue;
 428                import_attach = obj->import_attach;
 429                if (!import_attach)
 430                        continue;
 431                ret = dma_buf_begin_cpu_access(import_attach->dmabuf, dir);
 432                if (ret)
 433                        goto err_dma_buf_end_cpu_access;
 434        }
 435
 436        return 0;
 437
 438err_dma_buf_end_cpu_access:
 439        while (i) {
 440                --i;
 441                obj = drm_gem_fb_get_obj(fb, i);
 442                if (!obj)
 443                        continue;
 444                import_attach = obj->import_attach;
 445                if (!import_attach)
 446                        continue;
 447                ret2 = dma_buf_end_cpu_access(import_attach->dmabuf, dir);
 448                if (ret2) {
 449                        drm_err(fb->dev,
 450                                "dma_buf_end_cpu_access() failed during error handling: %d\n",
 451                                ret2);
 452                }
 453        }
 454
 455        return ret;
 456}
 457EXPORT_SYMBOL(drm_gem_fb_begin_cpu_access);
 458
 459/**
 460 * drm_gem_fb_end_cpu_access - signals end of CPU access to GEM buffer objects
 461 * @fb: the framebuffer
 462 * @dir: access mode
 463 *
 464 * Signals the end of CPU access to the given framebuffer's GEM buffer objects. This
 465 * function must be paired with a corresponding call to drm_gem_fb_begin_cpu_access().
 466 * For imported BOs, the function calls dma_buf_end_cpu_access().
 467 *
 468 * See also drm_gem_fb_begin_cpu_access().
 469 */
 470void drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_direction dir)
 471{
 472        size_t i = ARRAY_SIZE(fb->obj);
 473        struct dma_buf_attachment *import_attach;
 474        struct drm_gem_object *obj;
 475        int ret;
 476
 477        while (i) {
 478                --i;
 479                obj = drm_gem_fb_get_obj(fb, i);
 480                if (!obj)
 481                        continue;
 482                import_attach = obj->import_attach;
 483                if (!import_attach)
 484                        continue;
 485                ret = dma_buf_end_cpu_access(import_attach->dmabuf, dir);
 486                if (ret)
 487                        drm_err(fb->dev, "dma_buf_end_cpu_access() failed: %d\n", ret);
 488        }
 489}
 490EXPORT_SYMBOL(drm_gem_fb_end_cpu_access);
 491
 492static __u32 drm_gem_afbc_get_bpp(struct drm_device *dev,
 493                                  const struct drm_mode_fb_cmd2 *mode_cmd)
 494{
 495        const struct drm_format_info *info;
 496
 497        info = drm_get_format_info(dev, mode_cmd);
 498
 499        /* use whatever a driver has set */
 500        if (info->cpp[0])
 501                return info->cpp[0] * 8;
 502
 503        /* guess otherwise */
 504        switch (info->format) {
 505        case DRM_FORMAT_YUV420_8BIT:
 506                return 12;
 507        case DRM_FORMAT_YUV420_10BIT:
 508                return 15;
 509        case DRM_FORMAT_VUY101010:
 510                return 30;
 511        default:
 512                break;
 513        }
 514
 515        /* all attempts failed */
 516        return 0;
 517}
 518
 519static int drm_gem_afbc_min_size(struct drm_device *dev,
 520                                 const struct drm_mode_fb_cmd2 *mode_cmd,
 521                                 struct drm_afbc_framebuffer *afbc_fb)
 522{
 523        __u32 n_blocks, w_alignment, h_alignment, hdr_alignment;
 524        /* remove bpp when all users properly encode cpp in drm_format_info */
 525        __u32 bpp;
 526
 527        switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
 528        case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
 529                afbc_fb->block_width = 16;
 530                afbc_fb->block_height = 16;
 531                break;
 532        case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8:
 533                afbc_fb->block_width = 32;
 534                afbc_fb->block_height = 8;
 535                break;
 536        /* no user exists yet - fall through */
 537        case AFBC_FORMAT_MOD_BLOCK_SIZE_64x4:
 538        case AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4:
 539        default:
 540                drm_dbg_kms(dev, "Invalid AFBC_FORMAT_MOD_BLOCK_SIZE: %lld.\n",
 541                            mode_cmd->modifier[0]
 542                            & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK);
 543                return -EINVAL;
 544        }
 545
 546        /* tiled header afbc */
 547        w_alignment = afbc_fb->block_width;
 548        h_alignment = afbc_fb->block_height;
 549        hdr_alignment = AFBC_HDR_ALIGN;
 550        if (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_TILED) {
 551                w_alignment *= AFBC_TH_LAYOUT_ALIGNMENT;
 552                h_alignment *= AFBC_TH_LAYOUT_ALIGNMENT;
 553                hdr_alignment = AFBC_TH_BODY_START_ALIGNMENT;
 554        }
 555
 556        afbc_fb->aligned_width = ALIGN(mode_cmd->width, w_alignment);
 557        afbc_fb->aligned_height = ALIGN(mode_cmd->height, h_alignment);
 558        afbc_fb->offset = mode_cmd->offsets[0];
 559
 560        bpp = drm_gem_afbc_get_bpp(dev, mode_cmd);
 561        if (!bpp) {
 562                drm_dbg_kms(dev, "Invalid AFBC bpp value: %d\n", bpp);
 563                return -EINVAL;
 564        }
 565
 566        n_blocks = (afbc_fb->aligned_width * afbc_fb->aligned_height)
 567                   / AFBC_SUPERBLOCK_PIXELS;
 568        afbc_fb->afbc_size = ALIGN(n_blocks * AFBC_HEADER_SIZE, hdr_alignment);
 569        afbc_fb->afbc_size += n_blocks * ALIGN(bpp * AFBC_SUPERBLOCK_PIXELS / 8,
 570                                               AFBC_SUPERBLOCK_ALIGNMENT);
 571
 572        return 0;
 573}
 574
 575/**
 576 * drm_gem_fb_afbc_init() - Helper function for drivers using afbc to
 577 *                          fill and validate all the afbc-specific
 578 *                          struct drm_afbc_framebuffer members
 579 *
 580 * @dev: DRM device
 581 * @afbc_fb: afbc-specific framebuffer
 582 * @mode_cmd: Metadata from the userspace framebuffer creation request
 583 * @afbc_fb: afbc framebuffer
 584 *
 585 * This function can be used by drivers which support afbc to complete
 586 * the preparation of struct drm_afbc_framebuffer. It must be called after
 587 * allocating the said struct and calling drm_gem_fb_init_with_funcs().
 588 * It is caller's responsibility to put afbc_fb->base.obj objects in case
 589 * the call is unsuccessful.
 590 *
 591 * Returns:
 592 * Zero on success or a negative error value on failure.
 593 */
 594int drm_gem_fb_afbc_init(struct drm_device *dev,
 595                         const struct drm_mode_fb_cmd2 *mode_cmd,
 596                         struct drm_afbc_framebuffer *afbc_fb)
 597{
 598        const struct drm_format_info *info;
 599        struct drm_gem_object **objs;
 600        int ret;
 601
 602        objs = afbc_fb->base.obj;
 603        info = drm_get_format_info(dev, mode_cmd);
 604        if (!info)
 605                return -EINVAL;
 606
 607        ret = drm_gem_afbc_min_size(dev, mode_cmd, afbc_fb);
 608        if (ret < 0)
 609                return ret;
 610
 611        if (objs[0]->size < afbc_fb->afbc_size)
 612                return -EINVAL;
 613
 614        return 0;
 615}
 616EXPORT_SYMBOL_GPL(drm_gem_fb_afbc_init);
 617