162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/iosys-map.h>
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
762306a36Sopenharmony_ci#include <drm/drm_device.h>
862306a36Sopenharmony_ci#include <drm/drm_drv.h>
962306a36Sopenharmony_ci#include <drm/drm_file.h>
1062306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
1162306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
1262306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
1362306a36Sopenharmony_ci#include <drm/drm_gem_ttm_helper.h>
1462306a36Sopenharmony_ci#include <drm/drm_gem_vram_helper.h>
1562306a36Sopenharmony_ci#include <drm/drm_managed.h>
1662306a36Sopenharmony_ci#include <drm/drm_mode.h>
1762306a36Sopenharmony_ci#include <drm/drm_plane.h>
1862306a36Sopenharmony_ci#include <drm/drm_prime.h>
1962306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <drm/ttm/ttm_range_manager.h>
2262306a36Sopenharmony_ci#include <drm/ttm/ttm_tt.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic const struct drm_gem_object_funcs drm_gem_vram_object_funcs;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/**
2762306a36Sopenharmony_ci * DOC: overview
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * This library provides &struct drm_gem_vram_object (GEM VRAM), a GEM
3062306a36Sopenharmony_ci * buffer object that is backed by video RAM (VRAM). It can be used for
3162306a36Sopenharmony_ci * framebuffer devices with dedicated memory.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * The data structure &struct drm_vram_mm and its helpers implement a memory
3462306a36Sopenharmony_ci * manager for simple framebuffer devices with dedicated video memory. GEM
3562306a36Sopenharmony_ci * VRAM buffer objects are either placed in the video memory or remain evicted
3662306a36Sopenharmony_ci * to system memory.
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci * With the GEM interface userspace applications create, manage and destroy
3962306a36Sopenharmony_ci * graphics buffers, such as an on-screen framebuffer. GEM does not provide
4062306a36Sopenharmony_ci * an implementation of these interfaces. It's up to the DRM driver to
4162306a36Sopenharmony_ci * provide an implementation that suits the hardware. If the hardware device
4262306a36Sopenharmony_ci * contains dedicated video memory, the DRM driver can use the VRAM helper
4362306a36Sopenharmony_ci * library. Each active buffer object is stored in video RAM. Active
4462306a36Sopenharmony_ci * buffer are used for drawing the current frame, typically something like
4562306a36Sopenharmony_ci * the frame's scanout buffer or the cursor image. If there's no more space
4662306a36Sopenharmony_ci * left in VRAM, inactive GEM objects can be moved to system memory.
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * To initialize the VRAM helper library call drmm_vram_helper_init().
4962306a36Sopenharmony_ci * The function allocates and initializes an instance of &struct drm_vram_mm
5062306a36Sopenharmony_ci * in &struct drm_device.vram_mm . Use &DRM_GEM_VRAM_DRIVER to initialize
5162306a36Sopenharmony_ci * &struct drm_driver and  &DRM_VRAM_MM_FILE_OPERATIONS to initialize
5262306a36Sopenharmony_ci * &struct file_operations; as illustrated below.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * .. code-block:: c
5562306a36Sopenharmony_ci *
5662306a36Sopenharmony_ci *	struct file_operations fops ={
5762306a36Sopenharmony_ci *		.owner = THIS_MODULE,
5862306a36Sopenharmony_ci *		DRM_VRAM_MM_FILE_OPERATION
5962306a36Sopenharmony_ci *	};
6062306a36Sopenharmony_ci *	struct drm_driver drv = {
6162306a36Sopenharmony_ci *		.driver_feature = DRM_ ... ,
6262306a36Sopenharmony_ci *		.fops = &fops,
6362306a36Sopenharmony_ci *		DRM_GEM_VRAM_DRIVER
6462306a36Sopenharmony_ci *	};
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci *	int init_drm_driver()
6762306a36Sopenharmony_ci *	{
6862306a36Sopenharmony_ci *		struct drm_device *dev;
6962306a36Sopenharmony_ci *		uint64_t vram_base;
7062306a36Sopenharmony_ci *		unsigned long vram_size;
7162306a36Sopenharmony_ci *		int ret;
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci *		// setup device, vram base and size
7462306a36Sopenharmony_ci *		// ...
7562306a36Sopenharmony_ci *
7662306a36Sopenharmony_ci *		ret = drmm_vram_helper_init(dev, vram_base, vram_size);
7762306a36Sopenharmony_ci *		if (ret)
7862306a36Sopenharmony_ci *			return ret;
7962306a36Sopenharmony_ci *		return 0;
8062306a36Sopenharmony_ci *	}
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * This creates an instance of &struct drm_vram_mm, exports DRM userspace
8362306a36Sopenharmony_ci * interfaces for GEM buffer management and initializes file operations to
8462306a36Sopenharmony_ci * allow for accessing created GEM buffers. With this setup, the DRM driver
8562306a36Sopenharmony_ci * manages an area of video RAM with VRAM MM and provides GEM VRAM objects
8662306a36Sopenharmony_ci * to userspace.
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * You don't have to clean up the instance of VRAM MM.
8962306a36Sopenharmony_ci * drmm_vram_helper_init() is a managed interface that installs a
9062306a36Sopenharmony_ci * clean-up handler to run during the DRM device's release.
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci * For drawing or scanout operations, rsp. buffer objects have to be pinned
9362306a36Sopenharmony_ci * in video RAM. Call drm_gem_vram_pin() with &DRM_GEM_VRAM_PL_FLAG_VRAM or
9462306a36Sopenharmony_ci * &DRM_GEM_VRAM_PL_FLAG_SYSTEM to pin a buffer object in video RAM or system
9562306a36Sopenharmony_ci * memory. Call drm_gem_vram_unpin() to release the pinned object afterwards.
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * A buffer object that is pinned in video RAM has a fixed address within that
9862306a36Sopenharmony_ci * memory region. Call drm_gem_vram_offset() to retrieve this value. Typically
9962306a36Sopenharmony_ci * it's used to program the hardware's scanout engine for framebuffers, set
10062306a36Sopenharmony_ci * the cursor overlay's image for a mouse cursor, or use it as input to the
10162306a36Sopenharmony_ci * hardware's drawing engine.
10262306a36Sopenharmony_ci *
10362306a36Sopenharmony_ci * To access a buffer object's memory from the DRM driver, call
10462306a36Sopenharmony_ci * drm_gem_vram_vmap(). It maps the buffer into kernel address
10562306a36Sopenharmony_ci * space and returns the memory address. Use drm_gem_vram_vunmap() to
10662306a36Sopenharmony_ci * release the mapping.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/*
11062306a36Sopenharmony_ci * Buffer-objects helpers
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic void drm_gem_vram_cleanup(struct drm_gem_vram_object *gbo)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	/* We got here via ttm_bo_put(), which means that the
11662306a36Sopenharmony_ci	 * TTM buffer object in 'bo' has already been cleaned
11762306a36Sopenharmony_ci	 * up; only release the GEM object.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	WARN_ON(gbo->vmap_use_count);
12162306a36Sopenharmony_ci	WARN_ON(iosys_map_is_set(&gbo->map));
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	drm_gem_object_release(&gbo->bo.base);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void drm_gem_vram_destroy(struct drm_gem_vram_object *gbo)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	drm_gem_vram_cleanup(gbo);
12962306a36Sopenharmony_ci	kfree(gbo);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void ttm_buffer_object_destroy(struct ttm_buffer_object *bo)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo = drm_gem_vram_of_bo(bo);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	drm_gem_vram_destroy(gbo);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void drm_gem_vram_placement(struct drm_gem_vram_object *gbo,
14062306a36Sopenharmony_ci				   unsigned long pl_flag)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	u32 invariant_flags = 0;
14362306a36Sopenharmony_ci	unsigned int i;
14462306a36Sopenharmony_ci	unsigned int c = 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (pl_flag & DRM_GEM_VRAM_PL_FLAG_TOPDOWN)
14762306a36Sopenharmony_ci		invariant_flags = TTM_PL_FLAG_TOPDOWN;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	gbo->placement.placement = gbo->placements;
15062306a36Sopenharmony_ci	gbo->placement.busy_placement = gbo->placements;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (pl_flag & DRM_GEM_VRAM_PL_FLAG_VRAM) {
15362306a36Sopenharmony_ci		gbo->placements[c].mem_type = TTM_PL_VRAM;
15462306a36Sopenharmony_ci		gbo->placements[c++].flags = invariant_flags;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (pl_flag & DRM_GEM_VRAM_PL_FLAG_SYSTEM || !c) {
15862306a36Sopenharmony_ci		gbo->placements[c].mem_type = TTM_PL_SYSTEM;
15962306a36Sopenharmony_ci		gbo->placements[c++].flags = invariant_flags;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	gbo->placement.num_placement = c;
16362306a36Sopenharmony_ci	gbo->placement.num_busy_placement = c;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	for (i = 0; i < c; ++i) {
16662306a36Sopenharmony_ci		gbo->placements[i].fpfn = 0;
16762306a36Sopenharmony_ci		gbo->placements[i].lpfn = 0;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/**
17262306a36Sopenharmony_ci * drm_gem_vram_create() - Creates a VRAM-backed GEM object
17362306a36Sopenharmony_ci * @dev:		the DRM device
17462306a36Sopenharmony_ci * @size:		the buffer size in bytes
17562306a36Sopenharmony_ci * @pg_align:		the buffer's alignment in multiples of the page size
17662306a36Sopenharmony_ci *
17762306a36Sopenharmony_ci * GEM objects are allocated by calling struct drm_driver.gem_create_object,
17862306a36Sopenharmony_ci * if set. Otherwise kzalloc() will be used. Drivers can set their own GEM
17962306a36Sopenharmony_ci * object functions in struct drm_driver.gem_create_object. If no functions
18062306a36Sopenharmony_ci * are set, the new GEM object will use the default functions from GEM VRAM
18162306a36Sopenharmony_ci * helpers.
18262306a36Sopenharmony_ci *
18362306a36Sopenharmony_ci * Returns:
18462306a36Sopenharmony_ci * A new instance of &struct drm_gem_vram_object on success, or
18562306a36Sopenharmony_ci * an ERR_PTR()-encoded error code otherwise.
18662306a36Sopenharmony_ci */
18762306a36Sopenharmony_cistruct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev,
18862306a36Sopenharmony_ci						size_t size,
18962306a36Sopenharmony_ci						unsigned long pg_align)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo;
19262306a36Sopenharmony_ci	struct drm_gem_object *gem;
19362306a36Sopenharmony_ci	struct drm_vram_mm *vmm = dev->vram_mm;
19462306a36Sopenharmony_ci	struct ttm_device *bdev;
19562306a36Sopenharmony_ci	int ret;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (WARN_ONCE(!vmm, "VRAM MM not initialized"))
19862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (dev->driver->gem_create_object) {
20162306a36Sopenharmony_ci		gem = dev->driver->gem_create_object(dev, size);
20262306a36Sopenharmony_ci		if (IS_ERR(gem))
20362306a36Sopenharmony_ci			return ERR_CAST(gem);
20462306a36Sopenharmony_ci		gbo = drm_gem_vram_of_gem(gem);
20562306a36Sopenharmony_ci	} else {
20662306a36Sopenharmony_ci		gbo = kzalloc(sizeof(*gbo), GFP_KERNEL);
20762306a36Sopenharmony_ci		if (!gbo)
20862306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
20962306a36Sopenharmony_ci		gem = &gbo->bo.base;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (!gem->funcs)
21362306a36Sopenharmony_ci		gem->funcs = &drm_gem_vram_object_funcs;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ret = drm_gem_object_init(dev, gem, size);
21662306a36Sopenharmony_ci	if (ret) {
21762306a36Sopenharmony_ci		kfree(gbo);
21862306a36Sopenharmony_ci		return ERR_PTR(ret);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	bdev = &vmm->bdev;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	gbo->bo.bdev = bdev;
22462306a36Sopenharmony_ci	drm_gem_vram_placement(gbo, DRM_GEM_VRAM_PL_FLAG_SYSTEM);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/*
22762306a36Sopenharmony_ci	 * A failing ttm_bo_init will call ttm_buffer_object_destroy
22862306a36Sopenharmony_ci	 * to release gbo->bo.base and kfree gbo.
22962306a36Sopenharmony_ci	 */
23062306a36Sopenharmony_ci	ret = ttm_bo_init_validate(bdev, &gbo->bo, ttm_bo_type_device,
23162306a36Sopenharmony_ci				   &gbo->placement, pg_align, false, NULL, NULL,
23262306a36Sopenharmony_ci				   ttm_buffer_object_destroy);
23362306a36Sopenharmony_ci	if (ret)
23462306a36Sopenharmony_ci		return ERR_PTR(ret);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	return gbo;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_create);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/**
24162306a36Sopenharmony_ci * drm_gem_vram_put() - Releases a reference to a VRAM-backed GEM object
24262306a36Sopenharmony_ci * @gbo:	the GEM VRAM object
24362306a36Sopenharmony_ci *
24462306a36Sopenharmony_ci * See ttm_bo_put() for more information.
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_civoid drm_gem_vram_put(struct drm_gem_vram_object *gbo)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	ttm_bo_put(&gbo->bo);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_put);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic u64 drm_gem_vram_pg_offset(struct drm_gem_vram_object *gbo)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	/* Keep TTM behavior for now, remove when drivers are audited */
25562306a36Sopenharmony_ci	if (WARN_ON_ONCE(!gbo->bo.resource ||
25662306a36Sopenharmony_ci			 gbo->bo.resource->mem_type == TTM_PL_SYSTEM))
25762306a36Sopenharmony_ci		return 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return gbo->bo.resource->start;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/**
26362306a36Sopenharmony_ci * drm_gem_vram_offset() - \
26462306a36Sopenharmony_ci	Returns a GEM VRAM object's offset in video memory
26562306a36Sopenharmony_ci * @gbo:	the GEM VRAM object
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * This function returns the buffer object's offset in the device's video
26862306a36Sopenharmony_ci * memory. The buffer object has to be pinned to %TTM_PL_VRAM.
26962306a36Sopenharmony_ci *
27062306a36Sopenharmony_ci * Returns:
27162306a36Sopenharmony_ci * The buffer object's offset in video memory on success, or
27262306a36Sopenharmony_ci * a negative errno code otherwise.
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_cis64 drm_gem_vram_offset(struct drm_gem_vram_object *gbo)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	if (WARN_ON_ONCE(!gbo->bo.pin_count))
27762306a36Sopenharmony_ci		return (s64)-ENODEV;
27862306a36Sopenharmony_ci	return drm_gem_vram_pg_offset(gbo) << PAGE_SHIFT;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_offset);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic int drm_gem_vram_pin_locked(struct drm_gem_vram_object *gbo,
28362306a36Sopenharmony_ci				   unsigned long pl_flag)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct ttm_operation_ctx ctx = { false, false };
28662306a36Sopenharmony_ci	int ret;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (gbo->bo.pin_count)
28962306a36Sopenharmony_ci		goto out;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (pl_flag)
29262306a36Sopenharmony_ci		drm_gem_vram_placement(gbo, pl_flag);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
29562306a36Sopenharmony_ci	if (ret < 0)
29662306a36Sopenharmony_ci		return ret;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ciout:
29962306a36Sopenharmony_ci	ttm_bo_pin(&gbo->bo);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return 0;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci/**
30562306a36Sopenharmony_ci * drm_gem_vram_pin() - Pins a GEM VRAM object in a region.
30662306a36Sopenharmony_ci * @gbo:	the GEM VRAM object
30762306a36Sopenharmony_ci * @pl_flag:	a bitmask of possible memory regions
30862306a36Sopenharmony_ci *
30962306a36Sopenharmony_ci * Pinning a buffer object ensures that it is not evicted from
31062306a36Sopenharmony_ci * a memory region. A pinned buffer object has to be unpinned before
31162306a36Sopenharmony_ci * it can be pinned to another region. If the pl_flag argument is 0,
31262306a36Sopenharmony_ci * the buffer is pinned at its current location (video RAM or system
31362306a36Sopenharmony_ci * memory).
31462306a36Sopenharmony_ci *
31562306a36Sopenharmony_ci * Small buffer objects, such as cursor images, can lead to memory
31662306a36Sopenharmony_ci * fragmentation if they are pinned in the middle of video RAM. This
31762306a36Sopenharmony_ci * is especially a problem on devices with only a small amount of
31862306a36Sopenharmony_ci * video RAM. Fragmentation can prevent the primary framebuffer from
31962306a36Sopenharmony_ci * fitting in, even though there's enough memory overall. The modifier
32062306a36Sopenharmony_ci * DRM_GEM_VRAM_PL_FLAG_TOPDOWN marks the buffer object to be pinned
32162306a36Sopenharmony_ci * at the high end of the memory region to avoid fragmentation.
32262306a36Sopenharmony_ci *
32362306a36Sopenharmony_ci * Returns:
32462306a36Sopenharmony_ci * 0 on success, or
32562306a36Sopenharmony_ci * a negative error code otherwise.
32662306a36Sopenharmony_ci */
32762306a36Sopenharmony_ciint drm_gem_vram_pin(struct drm_gem_vram_object *gbo, unsigned long pl_flag)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	int ret;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	ret = ttm_bo_reserve(&gbo->bo, true, false, NULL);
33262306a36Sopenharmony_ci	if (ret)
33362306a36Sopenharmony_ci		return ret;
33462306a36Sopenharmony_ci	ret = drm_gem_vram_pin_locked(gbo, pl_flag);
33562306a36Sopenharmony_ci	ttm_bo_unreserve(&gbo->bo);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return ret;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_pin);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic void drm_gem_vram_unpin_locked(struct drm_gem_vram_object *gbo)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	ttm_bo_unpin(&gbo->bo);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/**
34762306a36Sopenharmony_ci * drm_gem_vram_unpin() - Unpins a GEM VRAM object
34862306a36Sopenharmony_ci * @gbo:	the GEM VRAM object
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * Returns:
35162306a36Sopenharmony_ci * 0 on success, or
35262306a36Sopenharmony_ci * a negative error code otherwise.
35362306a36Sopenharmony_ci */
35462306a36Sopenharmony_ciint drm_gem_vram_unpin(struct drm_gem_vram_object *gbo)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	int ret;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	ret = ttm_bo_reserve(&gbo->bo, true, false, NULL);
35962306a36Sopenharmony_ci	if (ret)
36062306a36Sopenharmony_ci		return ret;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	drm_gem_vram_unpin_locked(gbo);
36362306a36Sopenharmony_ci	ttm_bo_unreserve(&gbo->bo);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return 0;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_unpin);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic int drm_gem_vram_kmap_locked(struct drm_gem_vram_object *gbo,
37062306a36Sopenharmony_ci				    struct iosys_map *map)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	int ret;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (gbo->vmap_use_count > 0)
37562306a36Sopenharmony_ci		goto out;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/*
37862306a36Sopenharmony_ci	 * VRAM helpers unmap the BO only on demand. So the previous
37962306a36Sopenharmony_ci	 * page mapping might still be around. Only vmap if the there's
38062306a36Sopenharmony_ci	 * no mapping present.
38162306a36Sopenharmony_ci	 */
38262306a36Sopenharmony_ci	if (iosys_map_is_null(&gbo->map)) {
38362306a36Sopenharmony_ci		ret = ttm_bo_vmap(&gbo->bo, &gbo->map);
38462306a36Sopenharmony_ci		if (ret)
38562306a36Sopenharmony_ci			return ret;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ciout:
38962306a36Sopenharmony_ci	++gbo->vmap_use_count;
39062306a36Sopenharmony_ci	*map = gbo->map;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void drm_gem_vram_kunmap_locked(struct drm_gem_vram_object *gbo,
39662306a36Sopenharmony_ci				       struct iosys_map *map)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct drm_device *dev = gbo->bo.base.dev;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (drm_WARN_ON_ONCE(dev, !gbo->vmap_use_count))
40162306a36Sopenharmony_ci		return;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (drm_WARN_ON_ONCE(dev, !iosys_map_is_equal(&gbo->map, map)))
40462306a36Sopenharmony_ci		return; /* BUG: map not mapped from this BO */
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (--gbo->vmap_use_count > 0)
40762306a36Sopenharmony_ci		return;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/*
41062306a36Sopenharmony_ci	 * Permanently mapping and unmapping buffers adds overhead from
41162306a36Sopenharmony_ci	 * updating the page tables and creates debugging output. Therefore,
41262306a36Sopenharmony_ci	 * we delay the actual unmap operation until the BO gets evicted
41362306a36Sopenharmony_ci	 * from memory. See drm_gem_vram_bo_driver_move_notify().
41462306a36Sopenharmony_ci	 */
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/**
41862306a36Sopenharmony_ci * drm_gem_vram_vmap() - Pins and maps a GEM VRAM object into kernel address
41962306a36Sopenharmony_ci *                       space
42062306a36Sopenharmony_ci * @gbo: The GEM VRAM object to map
42162306a36Sopenharmony_ci * @map: Returns the kernel virtual address of the VRAM GEM object's backing
42262306a36Sopenharmony_ci *       store.
42362306a36Sopenharmony_ci *
42462306a36Sopenharmony_ci * The vmap function pins a GEM VRAM object to its current location, either
42562306a36Sopenharmony_ci * system or video memory, and maps its buffer into kernel address space.
42662306a36Sopenharmony_ci * As pinned object cannot be relocated, you should avoid pinning objects
42762306a36Sopenharmony_ci * permanently. Call drm_gem_vram_vunmap() with the returned address to
42862306a36Sopenharmony_ci * unmap and unpin the GEM VRAM object.
42962306a36Sopenharmony_ci *
43062306a36Sopenharmony_ci * Returns:
43162306a36Sopenharmony_ci * 0 on success, or a negative error code otherwise.
43262306a36Sopenharmony_ci */
43362306a36Sopenharmony_ciint drm_gem_vram_vmap(struct drm_gem_vram_object *gbo, struct iosys_map *map)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	int ret;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	dma_resv_assert_held(gbo->bo.base.resv);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	ret = drm_gem_vram_pin_locked(gbo, 0);
44062306a36Sopenharmony_ci	if (ret)
44162306a36Sopenharmony_ci		return ret;
44262306a36Sopenharmony_ci	ret = drm_gem_vram_kmap_locked(gbo, map);
44362306a36Sopenharmony_ci	if (ret)
44462306a36Sopenharmony_ci		goto err_drm_gem_vram_unpin_locked;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return 0;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cierr_drm_gem_vram_unpin_locked:
44962306a36Sopenharmony_ci	drm_gem_vram_unpin_locked(gbo);
45062306a36Sopenharmony_ci	return ret;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_vmap);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/**
45562306a36Sopenharmony_ci * drm_gem_vram_vunmap() - Unmaps and unpins a GEM VRAM object
45662306a36Sopenharmony_ci * @gbo: The GEM VRAM object to unmap
45762306a36Sopenharmony_ci * @map: Kernel virtual address where the VRAM GEM object was mapped
45862306a36Sopenharmony_ci *
45962306a36Sopenharmony_ci * A call to drm_gem_vram_vunmap() unmaps and unpins a GEM VRAM buffer. See
46062306a36Sopenharmony_ci * the documentation for drm_gem_vram_vmap() for more information.
46162306a36Sopenharmony_ci */
46262306a36Sopenharmony_civoid drm_gem_vram_vunmap(struct drm_gem_vram_object *gbo,
46362306a36Sopenharmony_ci			 struct iosys_map *map)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	dma_resv_assert_held(gbo->bo.base.resv);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	drm_gem_vram_kunmap_locked(gbo, map);
46862306a36Sopenharmony_ci	drm_gem_vram_unpin_locked(gbo);
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_vunmap);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci/**
47362306a36Sopenharmony_ci * drm_gem_vram_fill_create_dumb() - \
47462306a36Sopenharmony_ci	Helper for implementing &struct drm_driver.dumb_create
47562306a36Sopenharmony_ci * @file:		the DRM file
47662306a36Sopenharmony_ci * @dev:		the DRM device
47762306a36Sopenharmony_ci * @pg_align:		the buffer's alignment in multiples of the page size
47862306a36Sopenharmony_ci * @pitch_align:	the scanline's alignment in powers of 2
47962306a36Sopenharmony_ci * @args:		the arguments as provided to \
48062306a36Sopenharmony_ci				&struct drm_driver.dumb_create
48162306a36Sopenharmony_ci *
48262306a36Sopenharmony_ci * This helper function fills &struct drm_mode_create_dumb, which is used
48362306a36Sopenharmony_ci * by &struct drm_driver.dumb_create. Implementations of this interface
48462306a36Sopenharmony_ci * should forwards their arguments to this helper, plus the driver-specific
48562306a36Sopenharmony_ci * parameters.
48662306a36Sopenharmony_ci *
48762306a36Sopenharmony_ci * Returns:
48862306a36Sopenharmony_ci * 0 on success, or
48962306a36Sopenharmony_ci * a negative error code otherwise.
49062306a36Sopenharmony_ci */
49162306a36Sopenharmony_ciint drm_gem_vram_fill_create_dumb(struct drm_file *file,
49262306a36Sopenharmony_ci				  struct drm_device *dev,
49362306a36Sopenharmony_ci				  unsigned long pg_align,
49462306a36Sopenharmony_ci				  unsigned long pitch_align,
49562306a36Sopenharmony_ci				  struct drm_mode_create_dumb *args)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	size_t pitch, size;
49862306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo;
49962306a36Sopenharmony_ci	int ret;
50062306a36Sopenharmony_ci	u32 handle;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
50362306a36Sopenharmony_ci	if (pitch_align) {
50462306a36Sopenharmony_ci		if (WARN_ON_ONCE(!is_power_of_2(pitch_align)))
50562306a36Sopenharmony_ci			return -EINVAL;
50662306a36Sopenharmony_ci		pitch = ALIGN(pitch, pitch_align);
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci	size = pitch * args->height;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	size = roundup(size, PAGE_SIZE);
51162306a36Sopenharmony_ci	if (!size)
51262306a36Sopenharmony_ci		return -EINVAL;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	gbo = drm_gem_vram_create(dev, size, pg_align);
51562306a36Sopenharmony_ci	if (IS_ERR(gbo))
51662306a36Sopenharmony_ci		return PTR_ERR(gbo);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	ret = drm_gem_handle_create(file, &gbo->bo.base, &handle);
51962306a36Sopenharmony_ci	if (ret)
52062306a36Sopenharmony_ci		goto err_drm_gem_object_put;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	drm_gem_object_put(&gbo->bo.base);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	args->pitch = pitch;
52562306a36Sopenharmony_ci	args->size = size;
52662306a36Sopenharmony_ci	args->handle = handle;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cierr_drm_gem_object_put:
53162306a36Sopenharmony_ci	drm_gem_object_put(&gbo->bo.base);
53262306a36Sopenharmony_ci	return ret;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_fill_create_dumb);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci/*
53762306a36Sopenharmony_ci * Helpers for struct ttm_device_funcs
53862306a36Sopenharmony_ci */
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic bool drm_is_gem_vram(struct ttm_buffer_object *bo)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	return (bo->destroy == ttm_buffer_object_destroy);
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic void drm_gem_vram_bo_driver_evict_flags(struct drm_gem_vram_object *gbo,
54662306a36Sopenharmony_ci					       struct ttm_placement *pl)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	drm_gem_vram_placement(gbo, DRM_GEM_VRAM_PL_FLAG_SYSTEM);
54962306a36Sopenharmony_ci	*pl = gbo->placement;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic void drm_gem_vram_bo_driver_move_notify(struct drm_gem_vram_object *gbo)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct ttm_buffer_object *bo = &gbo->bo;
55562306a36Sopenharmony_ci	struct drm_device *dev = bo->base.dev;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (drm_WARN_ON_ONCE(dev, gbo->vmap_use_count))
55862306a36Sopenharmony_ci		return;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	ttm_bo_vunmap(bo, &gbo->map);
56162306a36Sopenharmony_ci	iosys_map_clear(&gbo->map); /* explicitly clear mapping for next vmap call */
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic int drm_gem_vram_bo_driver_move(struct drm_gem_vram_object *gbo,
56562306a36Sopenharmony_ci				       bool evict,
56662306a36Sopenharmony_ci				       struct ttm_operation_ctx *ctx,
56762306a36Sopenharmony_ci				       struct ttm_resource *new_mem)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	drm_gem_vram_bo_driver_move_notify(gbo);
57062306a36Sopenharmony_ci	return ttm_bo_move_memcpy(&gbo->bo, ctx, new_mem);
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/*
57462306a36Sopenharmony_ci * Helpers for struct drm_gem_object_funcs
57562306a36Sopenharmony_ci */
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci/**
57862306a36Sopenharmony_ci * drm_gem_vram_object_free() - \
57962306a36Sopenharmony_ci	Implements &struct drm_gem_object_funcs.free
58062306a36Sopenharmony_ci * @gem:       GEM object. Refers to &struct drm_gem_vram_object.gem
58162306a36Sopenharmony_ci */
58262306a36Sopenharmony_cistatic void drm_gem_vram_object_free(struct drm_gem_object *gem)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	drm_gem_vram_put(gbo);
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci/*
59062306a36Sopenharmony_ci * Helpers for dump buffers
59162306a36Sopenharmony_ci */
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci/**
59462306a36Sopenharmony_ci * drm_gem_vram_driver_dumb_create() - \
59562306a36Sopenharmony_ci	Implements &struct drm_driver.dumb_create
59662306a36Sopenharmony_ci * @file:		the DRM file
59762306a36Sopenharmony_ci * @dev:		the DRM device
59862306a36Sopenharmony_ci * @args:		the arguments as provided to \
59962306a36Sopenharmony_ci				&struct drm_driver.dumb_create
60062306a36Sopenharmony_ci *
60162306a36Sopenharmony_ci * This function requires the driver to use @drm_device.vram_mm for its
60262306a36Sopenharmony_ci * instance of VRAM MM.
60362306a36Sopenharmony_ci *
60462306a36Sopenharmony_ci * Returns:
60562306a36Sopenharmony_ci * 0 on success, or
60662306a36Sopenharmony_ci * a negative error code otherwise.
60762306a36Sopenharmony_ci */
60862306a36Sopenharmony_ciint drm_gem_vram_driver_dumb_create(struct drm_file *file,
60962306a36Sopenharmony_ci				    struct drm_device *dev,
61062306a36Sopenharmony_ci				    struct drm_mode_create_dumb *args)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	if (WARN_ONCE(!dev->vram_mm, "VRAM MM not initialized"))
61362306a36Sopenharmony_ci		return -EINVAL;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return drm_gem_vram_fill_create_dumb(file, dev, 0, 0, args);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_driver_dumb_create);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci/*
62062306a36Sopenharmony_ci * Helpers for struct drm_plane_helper_funcs
62162306a36Sopenharmony_ci */
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic void __drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane,
62462306a36Sopenharmony_ci						   struct drm_plane_state *state,
62562306a36Sopenharmony_ci						   unsigned int num_planes)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct drm_gem_object *obj;
62862306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo;
62962306a36Sopenharmony_ci	struct drm_framebuffer *fb = state->fb;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	while (num_planes) {
63262306a36Sopenharmony_ci		--num_planes;
63362306a36Sopenharmony_ci		obj = drm_gem_fb_get_obj(fb, num_planes);
63462306a36Sopenharmony_ci		if (!obj)
63562306a36Sopenharmony_ci			continue;
63662306a36Sopenharmony_ci		gbo = drm_gem_vram_of_gem(obj);
63762306a36Sopenharmony_ci		drm_gem_vram_unpin(gbo);
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci/**
64262306a36Sopenharmony_ci * drm_gem_vram_plane_helper_prepare_fb() - \
64362306a36Sopenharmony_ci *	Implements &struct drm_plane_helper_funcs.prepare_fb
64462306a36Sopenharmony_ci * @plane:	a DRM plane
64562306a36Sopenharmony_ci * @new_state:	the plane's new state
64662306a36Sopenharmony_ci *
64762306a36Sopenharmony_ci * During plane updates, this function sets the plane's fence and
64862306a36Sopenharmony_ci * pins the GEM VRAM objects of the plane's new framebuffer to VRAM.
64962306a36Sopenharmony_ci * Call drm_gem_vram_plane_helper_cleanup_fb() to unpin them.
65062306a36Sopenharmony_ci *
65162306a36Sopenharmony_ci * Returns:
65262306a36Sopenharmony_ci *	0 on success, or
65362306a36Sopenharmony_ci *	a negative errno code otherwise.
65462306a36Sopenharmony_ci */
65562306a36Sopenharmony_ciint
65662306a36Sopenharmony_cidrm_gem_vram_plane_helper_prepare_fb(struct drm_plane *plane,
65762306a36Sopenharmony_ci				     struct drm_plane_state *new_state)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_state->fb;
66062306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo;
66162306a36Sopenharmony_ci	struct drm_gem_object *obj;
66262306a36Sopenharmony_ci	unsigned int i;
66362306a36Sopenharmony_ci	int ret;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (!fb)
66662306a36Sopenharmony_ci		return 0;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	for (i = 0; i < fb->format->num_planes; ++i) {
66962306a36Sopenharmony_ci		obj = drm_gem_fb_get_obj(fb, i);
67062306a36Sopenharmony_ci		if (!obj) {
67162306a36Sopenharmony_ci			ret = -EINVAL;
67262306a36Sopenharmony_ci			goto err_drm_gem_vram_unpin;
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci		gbo = drm_gem_vram_of_gem(obj);
67562306a36Sopenharmony_ci		ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM);
67662306a36Sopenharmony_ci		if (ret)
67762306a36Sopenharmony_ci			goto err_drm_gem_vram_unpin;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	ret = drm_gem_plane_helper_prepare_fb(plane, new_state);
68162306a36Sopenharmony_ci	if (ret)
68262306a36Sopenharmony_ci		goto err_drm_gem_vram_unpin;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	return 0;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cierr_drm_gem_vram_unpin:
68762306a36Sopenharmony_ci	__drm_gem_vram_plane_helper_cleanup_fb(plane, new_state, i);
68862306a36Sopenharmony_ci	return ret;
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_plane_helper_prepare_fb);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci/**
69362306a36Sopenharmony_ci * drm_gem_vram_plane_helper_cleanup_fb() - \
69462306a36Sopenharmony_ci *	Implements &struct drm_plane_helper_funcs.cleanup_fb
69562306a36Sopenharmony_ci * @plane:	a DRM plane
69662306a36Sopenharmony_ci * @old_state:	the plane's old state
69762306a36Sopenharmony_ci *
69862306a36Sopenharmony_ci * During plane updates, this function unpins the GEM VRAM
69962306a36Sopenharmony_ci * objects of the plane's old framebuffer from VRAM. Complements
70062306a36Sopenharmony_ci * drm_gem_vram_plane_helper_prepare_fb().
70162306a36Sopenharmony_ci */
70262306a36Sopenharmony_civoid
70362306a36Sopenharmony_cidrm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane,
70462306a36Sopenharmony_ci				     struct drm_plane_state *old_state)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct drm_framebuffer *fb = old_state->fb;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (!fb)
70962306a36Sopenharmony_ci		return;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	__drm_gem_vram_plane_helper_cleanup_fb(plane, old_state, fb->format->num_planes);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_plane_helper_cleanup_fb);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci/*
71662306a36Sopenharmony_ci * Helpers for struct drm_simple_display_pipe_funcs
71762306a36Sopenharmony_ci */
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci/**
72062306a36Sopenharmony_ci * drm_gem_vram_simple_display_pipe_prepare_fb() - \
72162306a36Sopenharmony_ci *	Implements &struct drm_simple_display_pipe_funcs.prepare_fb
72262306a36Sopenharmony_ci * @pipe:	a simple display pipe
72362306a36Sopenharmony_ci * @new_state:	the plane's new state
72462306a36Sopenharmony_ci *
72562306a36Sopenharmony_ci * During plane updates, this function pins the GEM VRAM
72662306a36Sopenharmony_ci * objects of the plane's new framebuffer to VRAM. Call
72762306a36Sopenharmony_ci * drm_gem_vram_simple_display_pipe_cleanup_fb() to unpin them.
72862306a36Sopenharmony_ci *
72962306a36Sopenharmony_ci * Returns:
73062306a36Sopenharmony_ci *	0 on success, or
73162306a36Sopenharmony_ci *	a negative errno code otherwise.
73262306a36Sopenharmony_ci */
73362306a36Sopenharmony_ciint drm_gem_vram_simple_display_pipe_prepare_fb(
73462306a36Sopenharmony_ci	struct drm_simple_display_pipe *pipe,
73562306a36Sopenharmony_ci	struct drm_plane_state *new_state)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	return drm_gem_vram_plane_helper_prepare_fb(&pipe->plane, new_state);
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_simple_display_pipe_prepare_fb);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci/**
74262306a36Sopenharmony_ci * drm_gem_vram_simple_display_pipe_cleanup_fb() - \
74362306a36Sopenharmony_ci *	Implements &struct drm_simple_display_pipe_funcs.cleanup_fb
74462306a36Sopenharmony_ci * @pipe:	a simple display pipe
74562306a36Sopenharmony_ci * @old_state:	the plane's old state
74662306a36Sopenharmony_ci *
74762306a36Sopenharmony_ci * During plane updates, this function unpins the GEM VRAM
74862306a36Sopenharmony_ci * objects of the plane's old framebuffer from VRAM. Complements
74962306a36Sopenharmony_ci * drm_gem_vram_simple_display_pipe_prepare_fb().
75062306a36Sopenharmony_ci */
75162306a36Sopenharmony_civoid drm_gem_vram_simple_display_pipe_cleanup_fb(
75262306a36Sopenharmony_ci	struct drm_simple_display_pipe *pipe,
75362306a36Sopenharmony_ci	struct drm_plane_state *old_state)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	drm_gem_vram_plane_helper_cleanup_fb(&pipe->plane, old_state);
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_gem_vram_simple_display_pipe_cleanup_fb);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci/*
76062306a36Sopenharmony_ci * PRIME helpers
76162306a36Sopenharmony_ci */
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci/**
76462306a36Sopenharmony_ci * drm_gem_vram_object_pin() - \
76562306a36Sopenharmony_ci	Implements &struct drm_gem_object_funcs.pin
76662306a36Sopenharmony_ci * @gem:	The GEM object to pin
76762306a36Sopenharmony_ci *
76862306a36Sopenharmony_ci * Returns:
76962306a36Sopenharmony_ci * 0 on success, or
77062306a36Sopenharmony_ci * a negative errno code otherwise.
77162306a36Sopenharmony_ci */
77262306a36Sopenharmony_cistatic int drm_gem_vram_object_pin(struct drm_gem_object *gem)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/* Fbdev console emulation is the use case of these PRIME
77762306a36Sopenharmony_ci	 * helpers. This may involve updating a hardware buffer from
77862306a36Sopenharmony_ci	 * a shadow FB. We pin the buffer to it's current location
77962306a36Sopenharmony_ci	 * (either video RAM or system memory) to prevent it from
78062306a36Sopenharmony_ci	 * being relocated during the update operation. If you require
78162306a36Sopenharmony_ci	 * the buffer to be pinned to VRAM, implement a callback that
78262306a36Sopenharmony_ci	 * sets the flags accordingly.
78362306a36Sopenharmony_ci	 */
78462306a36Sopenharmony_ci	return drm_gem_vram_pin(gbo, 0);
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci/**
78862306a36Sopenharmony_ci * drm_gem_vram_object_unpin() - \
78962306a36Sopenharmony_ci	Implements &struct drm_gem_object_funcs.unpin
79062306a36Sopenharmony_ci * @gem:	The GEM object to unpin
79162306a36Sopenharmony_ci */
79262306a36Sopenharmony_cistatic void drm_gem_vram_object_unpin(struct drm_gem_object *gem)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	drm_gem_vram_unpin(gbo);
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci/**
80062306a36Sopenharmony_ci * drm_gem_vram_object_vmap() -
80162306a36Sopenharmony_ci *	Implements &struct drm_gem_object_funcs.vmap
80262306a36Sopenharmony_ci * @gem: The GEM object to map
80362306a36Sopenharmony_ci * @map: Returns the kernel virtual address of the VRAM GEM object's backing
80462306a36Sopenharmony_ci *       store.
80562306a36Sopenharmony_ci *
80662306a36Sopenharmony_ci * Returns:
80762306a36Sopenharmony_ci * 0 on success, or a negative error code otherwise.
80862306a36Sopenharmony_ci */
80962306a36Sopenharmony_cistatic int drm_gem_vram_object_vmap(struct drm_gem_object *gem,
81062306a36Sopenharmony_ci				    struct iosys_map *map)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	return drm_gem_vram_vmap(gbo, map);
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci/**
81862306a36Sopenharmony_ci * drm_gem_vram_object_vunmap() -
81962306a36Sopenharmony_ci *	Implements &struct drm_gem_object_funcs.vunmap
82062306a36Sopenharmony_ci * @gem: The GEM object to unmap
82162306a36Sopenharmony_ci * @map: Kernel virtual address where the VRAM GEM object was mapped
82262306a36Sopenharmony_ci */
82362306a36Sopenharmony_cistatic void drm_gem_vram_object_vunmap(struct drm_gem_object *gem,
82462306a36Sopenharmony_ci				       struct iosys_map *map)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo = drm_gem_vram_of_gem(gem);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	drm_gem_vram_vunmap(gbo, map);
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci/*
83262306a36Sopenharmony_ci * GEM object funcs
83362306a36Sopenharmony_ci */
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic const struct drm_gem_object_funcs drm_gem_vram_object_funcs = {
83662306a36Sopenharmony_ci	.free	= drm_gem_vram_object_free,
83762306a36Sopenharmony_ci	.pin	= drm_gem_vram_object_pin,
83862306a36Sopenharmony_ci	.unpin	= drm_gem_vram_object_unpin,
83962306a36Sopenharmony_ci	.vmap	= drm_gem_vram_object_vmap,
84062306a36Sopenharmony_ci	.vunmap	= drm_gem_vram_object_vunmap,
84162306a36Sopenharmony_ci	.mmap   = drm_gem_ttm_mmap,
84262306a36Sopenharmony_ci	.print_info = drm_gem_ttm_print_info,
84362306a36Sopenharmony_ci};
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci/*
84662306a36Sopenharmony_ci * VRAM memory manager
84762306a36Sopenharmony_ci */
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci/*
85062306a36Sopenharmony_ci * TTM TT
85162306a36Sopenharmony_ci */
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic void bo_driver_ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *tt)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	ttm_tt_fini(tt);
85662306a36Sopenharmony_ci	kfree(tt);
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci/*
86062306a36Sopenharmony_ci * TTM BO device
86162306a36Sopenharmony_ci */
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic struct ttm_tt *bo_driver_ttm_tt_create(struct ttm_buffer_object *bo,
86462306a36Sopenharmony_ci					      uint32_t page_flags)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct ttm_tt *tt;
86762306a36Sopenharmony_ci	int ret;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	tt = kzalloc(sizeof(*tt), GFP_KERNEL);
87062306a36Sopenharmony_ci	if (!tt)
87162306a36Sopenharmony_ci		return NULL;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	ret = ttm_tt_init(tt, bo, page_flags, ttm_cached, 0);
87462306a36Sopenharmony_ci	if (ret < 0)
87562306a36Sopenharmony_ci		goto err_ttm_tt_init;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return tt;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cierr_ttm_tt_init:
88062306a36Sopenharmony_ci	kfree(tt);
88162306a36Sopenharmony_ci	return NULL;
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic void bo_driver_evict_flags(struct ttm_buffer_object *bo,
88562306a36Sopenharmony_ci				  struct ttm_placement *placement)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/* TTM may pass BOs that are not GEM VRAM BOs. */
89062306a36Sopenharmony_ci	if (!drm_is_gem_vram(bo))
89162306a36Sopenharmony_ci		return;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	gbo = drm_gem_vram_of_bo(bo);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	drm_gem_vram_bo_driver_evict_flags(gbo, placement);
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic void bo_driver_delete_mem_notify(struct ttm_buffer_object *bo)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/* TTM may pass BOs that are not GEM VRAM BOs. */
90362306a36Sopenharmony_ci	if (!drm_is_gem_vram(bo))
90462306a36Sopenharmony_ci		return;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	gbo = drm_gem_vram_of_bo(bo);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	drm_gem_vram_bo_driver_move_notify(gbo);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic int bo_driver_move(struct ttm_buffer_object *bo,
91262306a36Sopenharmony_ci			  bool evict,
91362306a36Sopenharmony_ci			  struct ttm_operation_ctx *ctx,
91462306a36Sopenharmony_ci			  struct ttm_resource *new_mem,
91562306a36Sopenharmony_ci			  struct ttm_place *hop)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	struct drm_gem_vram_object *gbo;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (!bo->resource) {
92062306a36Sopenharmony_ci		if (new_mem->mem_type != TTM_PL_SYSTEM) {
92162306a36Sopenharmony_ci			hop->mem_type = TTM_PL_SYSTEM;
92262306a36Sopenharmony_ci			hop->flags = TTM_PL_FLAG_TEMPORARY;
92362306a36Sopenharmony_ci			return -EMULTIHOP;
92462306a36Sopenharmony_ci		}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		ttm_bo_move_null(bo, new_mem);
92762306a36Sopenharmony_ci		return 0;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	gbo = drm_gem_vram_of_bo(bo);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	return drm_gem_vram_bo_driver_move(gbo, evict, ctx, new_mem);
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic int bo_driver_io_mem_reserve(struct ttm_device *bdev,
93662306a36Sopenharmony_ci				    struct ttm_resource *mem)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bdev);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	switch (mem->mem_type) {
94162306a36Sopenharmony_ci	case TTM_PL_SYSTEM:	/* nothing to do */
94262306a36Sopenharmony_ci		break;
94362306a36Sopenharmony_ci	case TTM_PL_VRAM:
94462306a36Sopenharmony_ci		mem->bus.offset = (mem->start << PAGE_SHIFT) + vmm->vram_base;
94562306a36Sopenharmony_ci		mem->bus.is_iomem = true;
94662306a36Sopenharmony_ci		mem->bus.caching = ttm_write_combined;
94762306a36Sopenharmony_ci		break;
94862306a36Sopenharmony_ci	default:
94962306a36Sopenharmony_ci		return -EINVAL;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	return 0;
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic struct ttm_device_funcs bo_driver = {
95662306a36Sopenharmony_ci	.ttm_tt_create = bo_driver_ttm_tt_create,
95762306a36Sopenharmony_ci	.ttm_tt_destroy = bo_driver_ttm_tt_destroy,
95862306a36Sopenharmony_ci	.eviction_valuable = ttm_bo_eviction_valuable,
95962306a36Sopenharmony_ci	.evict_flags = bo_driver_evict_flags,
96062306a36Sopenharmony_ci	.move = bo_driver_move,
96162306a36Sopenharmony_ci	.delete_mem_notify = bo_driver_delete_mem_notify,
96262306a36Sopenharmony_ci	.io_mem_reserve = bo_driver_io_mem_reserve,
96362306a36Sopenharmony_ci};
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci/*
96662306a36Sopenharmony_ci * struct drm_vram_mm
96762306a36Sopenharmony_ci */
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic int drm_vram_mm_debugfs(struct seq_file *m, void *data)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	struct drm_debugfs_entry *entry = m->private;
97262306a36Sopenharmony_ci	struct drm_vram_mm *vmm = entry->dev->vram_mm;
97362306a36Sopenharmony_ci	struct ttm_resource_manager *man = ttm_manager_type(&vmm->bdev, TTM_PL_VRAM);
97462306a36Sopenharmony_ci	struct drm_printer p = drm_seq_file_printer(m);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	ttm_resource_manager_debug(man, &p);
97762306a36Sopenharmony_ci	return 0;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic const struct drm_debugfs_info drm_vram_mm_debugfs_list[] = {
98162306a36Sopenharmony_ci	{ "vram-mm", drm_vram_mm_debugfs, 0, NULL },
98262306a36Sopenharmony_ci};
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci/**
98562306a36Sopenharmony_ci * drm_vram_mm_debugfs_init() - Register VRAM MM debugfs file.
98662306a36Sopenharmony_ci *
98762306a36Sopenharmony_ci * @minor: drm minor device.
98862306a36Sopenharmony_ci *
98962306a36Sopenharmony_ci */
99062306a36Sopenharmony_civoid drm_vram_mm_debugfs_init(struct drm_minor *minor)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	drm_debugfs_add_files(minor->dev, drm_vram_mm_debugfs_list,
99362306a36Sopenharmony_ci			      ARRAY_SIZE(drm_vram_mm_debugfs_list));
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vram_mm_debugfs_init);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev,
99862306a36Sopenharmony_ci			    uint64_t vram_base, size_t vram_size)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	int ret;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	vmm->vram_base = vram_base;
100362306a36Sopenharmony_ci	vmm->vram_size = vram_size;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	ret = ttm_device_init(&vmm->bdev, &bo_driver, dev->dev,
100662306a36Sopenharmony_ci				 dev->anon_inode->i_mapping,
100762306a36Sopenharmony_ci				 dev->vma_offset_manager,
100862306a36Sopenharmony_ci				 false, true);
100962306a36Sopenharmony_ci	if (ret)
101062306a36Sopenharmony_ci		return ret;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	ret = ttm_range_man_init(&vmm->bdev, TTM_PL_VRAM,
101362306a36Sopenharmony_ci				 false, vram_size >> PAGE_SHIFT);
101462306a36Sopenharmony_ci	if (ret)
101562306a36Sopenharmony_ci		return ret;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	return 0;
101862306a36Sopenharmony_ci}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_cistatic void drm_vram_mm_cleanup(struct drm_vram_mm *vmm)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	ttm_range_man_fini(&vmm->bdev, TTM_PL_VRAM);
102362306a36Sopenharmony_ci	ttm_device_fini(&vmm->bdev);
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci/*
102762306a36Sopenharmony_ci * Helpers for integration with struct drm_device
102862306a36Sopenharmony_ci */
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic struct drm_vram_mm *drm_vram_helper_alloc_mm(struct drm_device *dev, uint64_t vram_base,
103162306a36Sopenharmony_ci						    size_t vram_size)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	int ret;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (WARN_ON(dev->vram_mm))
103662306a36Sopenharmony_ci		return dev->vram_mm;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	dev->vram_mm = kzalloc(sizeof(*dev->vram_mm), GFP_KERNEL);
103962306a36Sopenharmony_ci	if (!dev->vram_mm)
104062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	ret = drm_vram_mm_init(dev->vram_mm, dev, vram_base, vram_size);
104362306a36Sopenharmony_ci	if (ret)
104462306a36Sopenharmony_ci		goto err_kfree;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	return dev->vram_mm;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_cierr_kfree:
104962306a36Sopenharmony_ci	kfree(dev->vram_mm);
105062306a36Sopenharmony_ci	dev->vram_mm = NULL;
105162306a36Sopenharmony_ci	return ERR_PTR(ret);
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_cistatic void drm_vram_helper_release_mm(struct drm_device *dev)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	if (!dev->vram_mm)
105762306a36Sopenharmony_ci		return;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	drm_vram_mm_cleanup(dev->vram_mm);
106062306a36Sopenharmony_ci	kfree(dev->vram_mm);
106162306a36Sopenharmony_ci	dev->vram_mm = NULL;
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic void drm_vram_mm_release(struct drm_device *dev, void *ptr)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	drm_vram_helper_release_mm(dev);
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci/**
107062306a36Sopenharmony_ci * drmm_vram_helper_init - Initializes a device's instance of
107162306a36Sopenharmony_ci *                         &struct drm_vram_mm
107262306a36Sopenharmony_ci * @dev:	the DRM device
107362306a36Sopenharmony_ci * @vram_base:	the base address of the video memory
107462306a36Sopenharmony_ci * @vram_size:	the size of the video memory in bytes
107562306a36Sopenharmony_ci *
107662306a36Sopenharmony_ci * Creates a new instance of &struct drm_vram_mm and stores it in
107762306a36Sopenharmony_ci * struct &drm_device.vram_mm. The instance is auto-managed and cleaned
107862306a36Sopenharmony_ci * up as part of device cleanup. Calling this function multiple times
107962306a36Sopenharmony_ci * will generate an error message.
108062306a36Sopenharmony_ci *
108162306a36Sopenharmony_ci * Returns:
108262306a36Sopenharmony_ci * 0 on success, or a negative errno code otherwise.
108362306a36Sopenharmony_ci */
108462306a36Sopenharmony_ciint drmm_vram_helper_init(struct drm_device *dev, uint64_t vram_base,
108562306a36Sopenharmony_ci			  size_t vram_size)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	struct drm_vram_mm *vram_mm;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	if (drm_WARN_ON_ONCE(dev, dev->vram_mm))
109062306a36Sopenharmony_ci		return 0;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	vram_mm = drm_vram_helper_alloc_mm(dev, vram_base, vram_size);
109362306a36Sopenharmony_ci	if (IS_ERR(vram_mm))
109462306a36Sopenharmony_ci		return PTR_ERR(vram_mm);
109562306a36Sopenharmony_ci	return drmm_add_action_or_reset(dev, drm_vram_mm_release, NULL);
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ciEXPORT_SYMBOL(drmm_vram_helper_init);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci/*
110062306a36Sopenharmony_ci * Mode-config helpers
110162306a36Sopenharmony_ci */
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cistatic enum drm_mode_status
110462306a36Sopenharmony_cidrm_vram_helper_mode_valid_internal(struct drm_device *dev,
110562306a36Sopenharmony_ci				    const struct drm_display_mode *mode,
110662306a36Sopenharmony_ci				    unsigned long max_bpp)
110762306a36Sopenharmony_ci{
110862306a36Sopenharmony_ci	struct drm_vram_mm *vmm = dev->vram_mm;
110962306a36Sopenharmony_ci	unsigned long fbsize, fbpages, max_fbpages;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	if (WARN_ON(!dev->vram_mm))
111262306a36Sopenharmony_ci		return MODE_BAD;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	max_fbpages = (vmm->vram_size / 2) >> PAGE_SHIFT;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	fbsize = mode->hdisplay * mode->vdisplay * max_bpp;
111762306a36Sopenharmony_ci	fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	if (fbpages > max_fbpages)
112062306a36Sopenharmony_ci		return MODE_MEM;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	return MODE_OK;
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci/**
112662306a36Sopenharmony_ci * drm_vram_helper_mode_valid - Tests if a display mode's
112762306a36Sopenharmony_ci *	framebuffer fits into the available video memory.
112862306a36Sopenharmony_ci * @dev:	the DRM device
112962306a36Sopenharmony_ci * @mode:	the mode to test
113062306a36Sopenharmony_ci *
113162306a36Sopenharmony_ci * This function tests if enough video memory is available for using the
113262306a36Sopenharmony_ci * specified display mode. Atomic modesetting requires importing the
113362306a36Sopenharmony_ci * designated framebuffer into video memory before evicting the active
113462306a36Sopenharmony_ci * one. Hence, any framebuffer may consume at most half of the available
113562306a36Sopenharmony_ci * VRAM. Display modes that require a larger framebuffer can not be used,
113662306a36Sopenharmony_ci * even if the CRTC does support them. Each framebuffer is assumed to
113762306a36Sopenharmony_ci * have 32-bit color depth.
113862306a36Sopenharmony_ci *
113962306a36Sopenharmony_ci * Note:
114062306a36Sopenharmony_ci * The function can only test if the display mode is supported in
114162306a36Sopenharmony_ci * general. If there are too many framebuffers pinned to video memory,
114262306a36Sopenharmony_ci * a display mode may still not be usable in practice. The color depth of
114362306a36Sopenharmony_ci * 32-bit fits all current use case. A more flexible test can be added
114462306a36Sopenharmony_ci * when necessary.
114562306a36Sopenharmony_ci *
114662306a36Sopenharmony_ci * Returns:
114762306a36Sopenharmony_ci * MODE_OK if the display mode is supported, or an error code of type
114862306a36Sopenharmony_ci * enum drm_mode_status otherwise.
114962306a36Sopenharmony_ci */
115062306a36Sopenharmony_cienum drm_mode_status
115162306a36Sopenharmony_cidrm_vram_helper_mode_valid(struct drm_device *dev,
115262306a36Sopenharmony_ci			   const struct drm_display_mode *mode)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	static const unsigned long max_bpp = 4; /* DRM_FORMAT_XRGB8888 */
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	return drm_vram_helper_mode_valid_internal(dev, mode, max_bpp);
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_vram_helper_mode_valid);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ciMODULE_DESCRIPTION("DRM VRAM memory-management helpers");
116162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1162