// SPDX-License-Identifier: GPL-2.0-or-later
#include <drm/drm_debugfs.h>
#include <drm/drm_device.h>
#include <drm/drm_file.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_ttm_helper.h>
#include <drm/drm_gem_vram_helper.h>
#include <drm/drm_mode.h>
#include <drm/drm_plane.h>
#include <drm/drm_prime.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/ttm/ttm_page_alloc.h>
static const struct drm_gem_object_funcs drm_gem_vram_object_funcs;
/**
* DOC: overview
*
* This library provides a GEM buffer object that is backed by video RAM
* (VRAM). It can be used for framebuffer devices with dedicated memory.
*
* The data structure &struct drm_vram_mm and its helpers implement a memory
* manager for simple framebuffer devices with dedicated video memory. Buffer
* objects are either placed in video RAM or evicted to system memory. The rsp.
* buffer object is provided by &struct drm_gem_vram_object.
*/
/*
* Buffer-objects helpers
*/
static void drm_gem_vram_cleanup(struct drm_gem_vram_object *gbo)
{
/* We got here via ttm_bo_put(), which means that the
* TTM buffer object in 'bo' has already been cleaned
* up; only release the GEM object.
*/
WARN_ON(gbo->kmap_use_count);
WARN_ON(gbo->kmap.virtual);
drm_gem_object_release(&gbo->bo.base);
}
static void drm_gem_vram_destroy(struct drm_gem_vram_object *gbo)
{
drm_gem_vram_cleanup(gbo);
kfree(gbo);
}
static void ttm_buffer_object_destroy(struct ttm_buffer_object *bo)
{
struct drm_gem_vram_object *gbo = drm_gem_vram_of_bo(bo);
drm_gem_vram_destroy(gbo);
}
static void drm_gem_vram_placement(struct drm_gem_vram_object *gbo,
unsigned long pl_flag)
{
unsigned int i;
unsigned int c = 0;
u32 invariant_flags = pl_flag & TTM_PL_FLAG_TOPDOWN;
gbo->placement.placement = gbo->placements;
gbo->placement.busy_placement = gbo->placements;
if (pl_flag & TTM_PL_FLAG_VRAM)
gbo->placements[c++].flags = TTM_PL_FLAG_WC |
TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_VRAM |
invariant_flags;
if (pl_flag & TTM_PL_FLAG_SYSTEM)
gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
TTM_PL_FLAG_SYSTEM |
invariant_flags;
if (!c)
gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
TTM_PL_FLAG_SYSTEM |
invariant_flags;
gbo->placement.num_placement = c;
gbo->placement.num_busy_placement = c;
for (i = 0; i < c; ++i) {
gbo->placements[i].fpfn = 0;
gbo->placements[i].lpfn = 0;
}
}
static int drm_gem_vram_init(struct drm_device *dev,
struct ttm_bo_device *bdev,
struct drm_gem_vram_object *gbo,
size_t size, unsigned long pg_align,
bool interruptible)
{
int ret;
size_t acc_size;
gbo->bo.base.funcs = &drm_gem_vram_object_funcs;
ret = drm_gem_object_init(dev, &gbo->bo.base, size);
if (ret)
return ret;
acc_size = ttm_bo_dma_acc_size(bdev, size, sizeof(*gbo));
gbo->bo.bdev = bdev;
drm_gem_vram_placement(gbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
ret = ttm_bo_init(bdev, &gbo->bo, size, ttm_bo_type_device,
&gbo->placement, pg_align, interruptible, acc_size,
NULL, NULL, ttm_buffer_object_destroy);
if (ret)
goto err_drm_gem_object_release;
return 0;
err_drm_gem_object_release:
drm_gem_object_release(&gbo->bo.base);
return ret;
}
/**
* drm_gem_vram_create() - Creates a VRAM-backed GEM object
* @dev: the DRM device
* @bdev: the TTM BO device backing the object
* @size: the buffer size in bytes
* @pg_align: the buffer's alignment in multiples of the page size
* @interruptible: sleep interruptible if waiting for memory
*
* Returns:
* A new instance of &struct drm_gem_vram_object on success, or
* an ERR_PTR()-encoded error code otherwise.
*/
struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev,
struct ttm_bo_device *bdev,
size_t size,
unsigned long pg_align,
bool interruptible)
{
struct drm_gem_vram_object *gbo;
int ret;
gbo = kzalloc(sizeof(*gbo), GFP_KERNEL);
if (!gbo)
return ERR_PTR(-ENOMEM);
ret = drm_gem_vram_init(dev, bdev, gbo, size, pg_align, interruptible);
if (ret < 0)
goto err_kfree;
return gbo;
err_kfree:
kfree(gbo);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(drm_gem_vram_create);
/*