162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 OR MIT */
262306a36Sopenharmony_ci/**************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
562306a36Sopenharmony_ci * All Rights Reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
862306a36Sopenharmony_ci * copy of this software and associated documentation files (the
962306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
1062306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
1162306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
1262306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
1362306a36Sopenharmony_ci * the following conditions:
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the
1662306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
1762306a36Sopenharmony_ci * of the Software.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2062306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
2262306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2362306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2462306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2562306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci **************************************************************************/
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <linux/vmalloc.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <drm/ttm/ttm_bo.h>
3562306a36Sopenharmony_ci#include <drm/ttm/ttm_placement.h>
3662306a36Sopenharmony_ci#include <drm/ttm/ttm_tt.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <drm/drm_cache.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct ttm_transfer_obj {
4162306a36Sopenharmony_ci	struct ttm_buffer_object base;
4262306a36Sopenharmony_ci	struct ttm_buffer_object *bo;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciint ttm_mem_io_reserve(struct ttm_device *bdev,
4662306a36Sopenharmony_ci		       struct ttm_resource *mem)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	if (mem->bus.offset || mem->bus.addr)
4962306a36Sopenharmony_ci		return 0;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	mem->bus.is_iomem = false;
5262306a36Sopenharmony_ci	if (!bdev->funcs->io_mem_reserve)
5362306a36Sopenharmony_ci		return 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return bdev->funcs->io_mem_reserve(bdev, mem);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_civoid ttm_mem_io_free(struct ttm_device *bdev,
5962306a36Sopenharmony_ci		     struct ttm_resource *mem)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	if (!mem)
6262306a36Sopenharmony_ci		return;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (!mem->bus.offset && !mem->bus.addr)
6562306a36Sopenharmony_ci		return;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (bdev->funcs->io_mem_free)
6862306a36Sopenharmony_ci		bdev->funcs->io_mem_free(bdev, mem);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	mem->bus.offset = 0;
7162306a36Sopenharmony_ci	mem->bus.addr = NULL;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/**
7562306a36Sopenharmony_ci * ttm_move_memcpy - Helper to perform a memcpy ttm move operation.
7662306a36Sopenharmony_ci * @clear: Whether to clear rather than copy.
7762306a36Sopenharmony_ci * @num_pages: Number of pages of the operation.
7862306a36Sopenharmony_ci * @dst_iter: A struct ttm_kmap_iter representing the destination resource.
7962306a36Sopenharmony_ci * @src_iter: A struct ttm_kmap_iter representing the source resource.
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * This function is intended to be able to move out async under a
8262306a36Sopenharmony_ci * dma-fence if desired.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_civoid ttm_move_memcpy(bool clear,
8562306a36Sopenharmony_ci		     u32 num_pages,
8662306a36Sopenharmony_ci		     struct ttm_kmap_iter *dst_iter,
8762306a36Sopenharmony_ci		     struct ttm_kmap_iter *src_iter)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	const struct ttm_kmap_iter_ops *dst_ops = dst_iter->ops;
9062306a36Sopenharmony_ci	const struct ttm_kmap_iter_ops *src_ops = src_iter->ops;
9162306a36Sopenharmony_ci	struct iosys_map src_map, dst_map;
9262306a36Sopenharmony_ci	pgoff_t i;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Single TTM move. NOP */
9562306a36Sopenharmony_ci	if (dst_ops->maps_tt && src_ops->maps_tt)
9662306a36Sopenharmony_ci		return;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Don't move nonexistent data. Clear destination instead. */
9962306a36Sopenharmony_ci	if (clear) {
10062306a36Sopenharmony_ci		for (i = 0; i < num_pages; ++i) {
10162306a36Sopenharmony_ci			dst_ops->map_local(dst_iter, &dst_map, i);
10262306a36Sopenharmony_ci			if (dst_map.is_iomem)
10362306a36Sopenharmony_ci				memset_io(dst_map.vaddr_iomem, 0, PAGE_SIZE);
10462306a36Sopenharmony_ci			else
10562306a36Sopenharmony_ci				memset(dst_map.vaddr, 0, PAGE_SIZE);
10662306a36Sopenharmony_ci			if (dst_ops->unmap_local)
10762306a36Sopenharmony_ci				dst_ops->unmap_local(dst_iter, &dst_map);
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci		return;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	for (i = 0; i < num_pages; ++i) {
11362306a36Sopenharmony_ci		dst_ops->map_local(dst_iter, &dst_map, i);
11462306a36Sopenharmony_ci		src_ops->map_local(src_iter, &src_map, i);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		drm_memcpy_from_wc(&dst_map, &src_map, PAGE_SIZE);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		if (src_ops->unmap_local)
11962306a36Sopenharmony_ci			src_ops->unmap_local(src_iter, &src_map);
12062306a36Sopenharmony_ci		if (dst_ops->unmap_local)
12162306a36Sopenharmony_ci			dst_ops->unmap_local(dst_iter, &dst_map);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_move_memcpy);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * ttm_bo_move_memcpy
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * @bo: A pointer to a struct ttm_buffer_object.
13062306a36Sopenharmony_ci * @ctx: operation context
13162306a36Sopenharmony_ci * @dst_mem: struct ttm_resource indicating where to move.
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci * Fallback move function for a mappable buffer object in mappable memory.
13462306a36Sopenharmony_ci * The function will, if successful,
13562306a36Sopenharmony_ci * free any old aperture space, and set (@new_mem)->mm_node to NULL,
13662306a36Sopenharmony_ci * and update the (@bo)->mem placement flags. If unsuccessful, the old
13762306a36Sopenharmony_ci * data remains untouched, and it's up to the caller to free the
13862306a36Sopenharmony_ci * memory space indicated by @new_mem.
13962306a36Sopenharmony_ci * Returns:
14062306a36Sopenharmony_ci * !0: Failure.
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_ciint ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
14362306a36Sopenharmony_ci		       struct ttm_operation_ctx *ctx,
14462306a36Sopenharmony_ci		       struct ttm_resource *dst_mem)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct ttm_device *bdev = bo->bdev;
14762306a36Sopenharmony_ci	struct ttm_resource_manager *dst_man =
14862306a36Sopenharmony_ci		ttm_manager_type(bo->bdev, dst_mem->mem_type);
14962306a36Sopenharmony_ci	struct ttm_tt *ttm = bo->ttm;
15062306a36Sopenharmony_ci	struct ttm_resource *src_mem = bo->resource;
15162306a36Sopenharmony_ci	struct ttm_resource_manager *src_man;
15262306a36Sopenharmony_ci	union {
15362306a36Sopenharmony_ci		struct ttm_kmap_iter_tt tt;
15462306a36Sopenharmony_ci		struct ttm_kmap_iter_linear_io io;
15562306a36Sopenharmony_ci	} _dst_iter, _src_iter;
15662306a36Sopenharmony_ci	struct ttm_kmap_iter *dst_iter, *src_iter;
15762306a36Sopenharmony_ci	bool clear;
15862306a36Sopenharmony_ci	int ret = 0;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (WARN_ON(!src_mem))
16162306a36Sopenharmony_ci		return -EINVAL;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	src_man = ttm_manager_type(bdev, src_mem->mem_type);
16462306a36Sopenharmony_ci	if (ttm && ((ttm->page_flags & TTM_TT_FLAG_SWAPPED) ||
16562306a36Sopenharmony_ci		    dst_man->use_tt)) {
16662306a36Sopenharmony_ci		ret = ttm_tt_populate(bdev, ttm, ctx);
16762306a36Sopenharmony_ci		if (ret)
16862306a36Sopenharmony_ci			return ret;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	dst_iter = ttm_kmap_iter_linear_io_init(&_dst_iter.io, bdev, dst_mem);
17262306a36Sopenharmony_ci	if (PTR_ERR(dst_iter) == -EINVAL && dst_man->use_tt)
17362306a36Sopenharmony_ci		dst_iter = ttm_kmap_iter_tt_init(&_dst_iter.tt, bo->ttm);
17462306a36Sopenharmony_ci	if (IS_ERR(dst_iter))
17562306a36Sopenharmony_ci		return PTR_ERR(dst_iter);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	src_iter = ttm_kmap_iter_linear_io_init(&_src_iter.io, bdev, src_mem);
17862306a36Sopenharmony_ci	if (PTR_ERR(src_iter) == -EINVAL && src_man->use_tt)
17962306a36Sopenharmony_ci		src_iter = ttm_kmap_iter_tt_init(&_src_iter.tt, bo->ttm);
18062306a36Sopenharmony_ci	if (IS_ERR(src_iter)) {
18162306a36Sopenharmony_ci		ret = PTR_ERR(src_iter);
18262306a36Sopenharmony_ci		goto out_src_iter;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	clear = src_iter->ops->maps_tt && (!ttm || !ttm_tt_is_populated(ttm));
18662306a36Sopenharmony_ci	if (!(clear && ttm && !(ttm->page_flags & TTM_TT_FLAG_ZERO_ALLOC)))
18762306a36Sopenharmony_ci		ttm_move_memcpy(clear, PFN_UP(dst_mem->size), dst_iter, src_iter);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (!src_iter->ops->maps_tt)
19062306a36Sopenharmony_ci		ttm_kmap_iter_linear_io_fini(&_src_iter.io, bdev, src_mem);
19162306a36Sopenharmony_ci	ttm_bo_move_sync_cleanup(bo, dst_mem);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ciout_src_iter:
19462306a36Sopenharmony_ci	if (!dst_iter->ops->maps_tt)
19562306a36Sopenharmony_ci		ttm_kmap_iter_linear_io_fini(&_dst_iter.io, bdev, dst_mem);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return ret;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_move_memcpy);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void ttm_transfered_destroy(struct ttm_buffer_object *bo)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct ttm_transfer_obj *fbo;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	fbo = container_of(bo, struct ttm_transfer_obj, base);
20662306a36Sopenharmony_ci	dma_resv_fini(&fbo->base.base._resv);
20762306a36Sopenharmony_ci	ttm_bo_put(fbo->bo);
20862306a36Sopenharmony_ci	kfree(fbo);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/**
21262306a36Sopenharmony_ci * ttm_buffer_object_transfer
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci * @bo: A pointer to a struct ttm_buffer_object.
21562306a36Sopenharmony_ci * @new_obj: A pointer to a pointer to a newly created ttm_buffer_object,
21662306a36Sopenharmony_ci * holding the data of @bo with the old placement.
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * This is a utility function that may be called after an accelerated move
21962306a36Sopenharmony_ci * has been scheduled. A new buffer object is created as a placeholder for
22062306a36Sopenharmony_ci * the old data while it's being copied. When that buffer object is idle,
22162306a36Sopenharmony_ci * it can be destroyed, releasing the space of the old placement.
22262306a36Sopenharmony_ci * Returns:
22362306a36Sopenharmony_ci * !0: Failure.
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
22762306a36Sopenharmony_ci				      struct ttm_buffer_object **new_obj)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct ttm_transfer_obj *fbo;
23062306a36Sopenharmony_ci	int ret;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	fbo = kmalloc(sizeof(*fbo), GFP_KERNEL);
23362306a36Sopenharmony_ci	if (!fbo)
23462306a36Sopenharmony_ci		return -ENOMEM;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	fbo->base = *bo;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/**
23962306a36Sopenharmony_ci	 * Fix up members that we shouldn't copy directly:
24062306a36Sopenharmony_ci	 * TODO: Explicit member copy would probably be better here.
24162306a36Sopenharmony_ci	 */
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	atomic_inc(&ttm_glob.bo_count);
24462306a36Sopenharmony_ci	drm_vma_node_reset(&fbo->base.base.vma_node);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	kref_init(&fbo->base.kref);
24762306a36Sopenharmony_ci	fbo->base.destroy = &ttm_transfered_destroy;
24862306a36Sopenharmony_ci	fbo->base.pin_count = 0;
24962306a36Sopenharmony_ci	if (bo->type != ttm_bo_type_sg)
25062306a36Sopenharmony_ci		fbo->base.base.resv = &fbo->base.base._resv;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	dma_resv_init(&fbo->base.base._resv);
25362306a36Sopenharmony_ci	fbo->base.base.dev = NULL;
25462306a36Sopenharmony_ci	ret = dma_resv_trylock(&fbo->base.base._resv);
25562306a36Sopenharmony_ci	WARN_ON(!ret);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (fbo->base.resource) {
25862306a36Sopenharmony_ci		ttm_resource_set_bo(fbo->base.resource, &fbo->base);
25962306a36Sopenharmony_ci		bo->resource = NULL;
26062306a36Sopenharmony_ci		ttm_bo_set_bulk_move(&fbo->base, NULL);
26162306a36Sopenharmony_ci	} else {
26262306a36Sopenharmony_ci		fbo->base.bulk_move = NULL;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	ret = dma_resv_reserve_fences(&fbo->base.base._resv, 1);
26662306a36Sopenharmony_ci	if (ret) {
26762306a36Sopenharmony_ci		kfree(fbo);
26862306a36Sopenharmony_ci		return ret;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	ttm_bo_get(bo);
27262306a36Sopenharmony_ci	fbo->bo = bo;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ttm_bo_move_to_lru_tail_unlocked(&fbo->base);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	*new_obj = &fbo->base;
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/**
28162306a36Sopenharmony_ci * ttm_io_prot
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci * @bo: ttm buffer object
28462306a36Sopenharmony_ci * @res: ttm resource object
28562306a36Sopenharmony_ci * @tmp: Page protection flag for a normal, cached mapping.
28662306a36Sopenharmony_ci *
28762306a36Sopenharmony_ci * Utility function that returns the pgprot_t that should be used for
28862306a36Sopenharmony_ci * setting up a PTE with the caching model indicated by @c_state.
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cipgprot_t ttm_io_prot(struct ttm_buffer_object *bo, struct ttm_resource *res,
29162306a36Sopenharmony_ci		     pgprot_t tmp)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct ttm_resource_manager *man;
29462306a36Sopenharmony_ci	enum ttm_caching caching;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	man = ttm_manager_type(bo->bdev, res->mem_type);
29762306a36Sopenharmony_ci	caching = man->use_tt ? bo->ttm->caching : res->bus.caching;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	return ttm_prot_from_caching(caching, tmp);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_io_prot);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int ttm_bo_ioremap(struct ttm_buffer_object *bo,
30462306a36Sopenharmony_ci			  unsigned long offset,
30562306a36Sopenharmony_ci			  unsigned long size,
30662306a36Sopenharmony_ci			  struct ttm_bo_kmap_obj *map)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct ttm_resource *mem = bo->resource;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (bo->resource->bus.addr) {
31162306a36Sopenharmony_ci		map->bo_kmap_type = ttm_bo_map_premapped;
31262306a36Sopenharmony_ci		map->virtual = ((u8 *)bo->resource->bus.addr) + offset;
31362306a36Sopenharmony_ci	} else {
31462306a36Sopenharmony_ci		resource_size_t res = bo->resource->bus.offset + offset;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		map->bo_kmap_type = ttm_bo_map_iomap;
31762306a36Sopenharmony_ci		if (mem->bus.caching == ttm_write_combined)
31862306a36Sopenharmony_ci			map->virtual = ioremap_wc(res, size);
31962306a36Sopenharmony_ci#ifdef CONFIG_X86
32062306a36Sopenharmony_ci		else if (mem->bus.caching == ttm_cached)
32162306a36Sopenharmony_ci			map->virtual = ioremap_cache(res, size);
32262306a36Sopenharmony_ci#endif
32362306a36Sopenharmony_ci		else
32462306a36Sopenharmony_ci			map->virtual = ioremap(res, size);
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci	return (!map->virtual) ? -ENOMEM : 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo,
33062306a36Sopenharmony_ci			   unsigned long start_page,
33162306a36Sopenharmony_ci			   unsigned long num_pages,
33262306a36Sopenharmony_ci			   struct ttm_bo_kmap_obj *map)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct ttm_resource *mem = bo->resource;
33562306a36Sopenharmony_ci	struct ttm_operation_ctx ctx = {
33662306a36Sopenharmony_ci		.interruptible = false,
33762306a36Sopenharmony_ci		.no_wait_gpu = false
33862306a36Sopenharmony_ci	};
33962306a36Sopenharmony_ci	struct ttm_tt *ttm = bo->ttm;
34062306a36Sopenharmony_ci	pgprot_t prot;
34162306a36Sopenharmony_ci	int ret;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	BUG_ON(!ttm);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	ret = ttm_tt_populate(bo->bdev, ttm, &ctx);
34662306a36Sopenharmony_ci	if (ret)
34762306a36Sopenharmony_ci		return ret;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (num_pages == 1 && ttm->caching == ttm_cached) {
35062306a36Sopenharmony_ci		/*
35162306a36Sopenharmony_ci		 * We're mapping a single page, and the desired
35262306a36Sopenharmony_ci		 * page protection is consistent with the bo.
35362306a36Sopenharmony_ci		 */
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		map->bo_kmap_type = ttm_bo_map_kmap;
35662306a36Sopenharmony_ci		map->page = ttm->pages[start_page];
35762306a36Sopenharmony_ci		map->virtual = kmap(map->page);
35862306a36Sopenharmony_ci	} else {
35962306a36Sopenharmony_ci		/*
36062306a36Sopenharmony_ci		 * We need to use vmap to get the desired page protection
36162306a36Sopenharmony_ci		 * or to make the buffer object look contiguous.
36262306a36Sopenharmony_ci		 */
36362306a36Sopenharmony_ci		prot = ttm_io_prot(bo, mem, PAGE_KERNEL);
36462306a36Sopenharmony_ci		map->bo_kmap_type = ttm_bo_map_vmap;
36562306a36Sopenharmony_ci		map->virtual = vmap(ttm->pages + start_page, num_pages,
36662306a36Sopenharmony_ci				    0, prot);
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	return (!map->virtual) ? -ENOMEM : 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/**
37262306a36Sopenharmony_ci * ttm_bo_kmap
37362306a36Sopenharmony_ci *
37462306a36Sopenharmony_ci * @bo: The buffer object.
37562306a36Sopenharmony_ci * @start_page: The first page to map.
37662306a36Sopenharmony_ci * @num_pages: Number of pages to map.
37762306a36Sopenharmony_ci * @map: pointer to a struct ttm_bo_kmap_obj representing the map.
37862306a36Sopenharmony_ci *
37962306a36Sopenharmony_ci * Sets up a kernel virtual mapping, using ioremap, vmap or kmap to the
38062306a36Sopenharmony_ci * data in the buffer object. The ttm_kmap_obj_virtual function can then be
38162306a36Sopenharmony_ci * used to obtain a virtual address to the data.
38262306a36Sopenharmony_ci *
38362306a36Sopenharmony_ci * Returns
38462306a36Sopenharmony_ci * -ENOMEM: Out of memory.
38562306a36Sopenharmony_ci * -EINVAL: Invalid range.
38662306a36Sopenharmony_ci */
38762306a36Sopenharmony_ciint ttm_bo_kmap(struct ttm_buffer_object *bo,
38862306a36Sopenharmony_ci		unsigned long start_page, unsigned long num_pages,
38962306a36Sopenharmony_ci		struct ttm_bo_kmap_obj *map)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	unsigned long offset, size;
39262306a36Sopenharmony_ci	int ret;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	map->virtual = NULL;
39562306a36Sopenharmony_ci	map->bo = bo;
39662306a36Sopenharmony_ci	if (num_pages > PFN_UP(bo->resource->size))
39762306a36Sopenharmony_ci		return -EINVAL;
39862306a36Sopenharmony_ci	if ((start_page + num_pages) > PFN_UP(bo->resource->size))
39962306a36Sopenharmony_ci		return -EINVAL;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	ret = ttm_mem_io_reserve(bo->bdev, bo->resource);
40262306a36Sopenharmony_ci	if (ret)
40362306a36Sopenharmony_ci		return ret;
40462306a36Sopenharmony_ci	if (!bo->resource->bus.is_iomem) {
40562306a36Sopenharmony_ci		return ttm_bo_kmap_ttm(bo, start_page, num_pages, map);
40662306a36Sopenharmony_ci	} else {
40762306a36Sopenharmony_ci		offset = start_page << PAGE_SHIFT;
40862306a36Sopenharmony_ci		size = num_pages << PAGE_SHIFT;
40962306a36Sopenharmony_ci		return ttm_bo_ioremap(bo, offset, size, map);
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_kmap);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/**
41562306a36Sopenharmony_ci * ttm_bo_kunmap
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * @map: Object describing the map to unmap.
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * Unmaps a kernel map set up by ttm_bo_kmap.
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_civoid ttm_bo_kunmap(struct ttm_bo_kmap_obj *map)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	if (!map->virtual)
42462306a36Sopenharmony_ci		return;
42562306a36Sopenharmony_ci	switch (map->bo_kmap_type) {
42662306a36Sopenharmony_ci	case ttm_bo_map_iomap:
42762306a36Sopenharmony_ci		iounmap(map->virtual);
42862306a36Sopenharmony_ci		break;
42962306a36Sopenharmony_ci	case ttm_bo_map_vmap:
43062306a36Sopenharmony_ci		vunmap(map->virtual);
43162306a36Sopenharmony_ci		break;
43262306a36Sopenharmony_ci	case ttm_bo_map_kmap:
43362306a36Sopenharmony_ci		kunmap(map->page);
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci	case ttm_bo_map_premapped:
43662306a36Sopenharmony_ci		break;
43762306a36Sopenharmony_ci	default:
43862306a36Sopenharmony_ci		BUG();
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci	ttm_mem_io_free(map->bo->bdev, map->bo->resource);
44162306a36Sopenharmony_ci	map->virtual = NULL;
44262306a36Sopenharmony_ci	map->page = NULL;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_kunmap);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci/**
44762306a36Sopenharmony_ci * ttm_bo_vmap
44862306a36Sopenharmony_ci *
44962306a36Sopenharmony_ci * @bo: The buffer object.
45062306a36Sopenharmony_ci * @map: pointer to a struct iosys_map representing the map.
45162306a36Sopenharmony_ci *
45262306a36Sopenharmony_ci * Sets up a kernel virtual mapping, using ioremap or vmap to the
45362306a36Sopenharmony_ci * data in the buffer object. The parameter @map returns the virtual
45462306a36Sopenharmony_ci * address as struct iosys_map. Unmap the buffer with ttm_bo_vunmap().
45562306a36Sopenharmony_ci *
45662306a36Sopenharmony_ci * Returns
45762306a36Sopenharmony_ci * -ENOMEM: Out of memory.
45862306a36Sopenharmony_ci * -EINVAL: Invalid range.
45962306a36Sopenharmony_ci */
46062306a36Sopenharmony_ciint ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct ttm_resource *mem = bo->resource;
46362306a36Sopenharmony_ci	int ret;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	dma_resv_assert_held(bo->base.resv);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	ret = ttm_mem_io_reserve(bo->bdev, mem);
46862306a36Sopenharmony_ci	if (ret)
46962306a36Sopenharmony_ci		return ret;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (mem->bus.is_iomem) {
47262306a36Sopenharmony_ci		void __iomem *vaddr_iomem;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		if (mem->bus.addr)
47562306a36Sopenharmony_ci			vaddr_iomem = (void __iomem *)mem->bus.addr;
47662306a36Sopenharmony_ci		else if (mem->bus.caching == ttm_write_combined)
47762306a36Sopenharmony_ci			vaddr_iomem = ioremap_wc(mem->bus.offset,
47862306a36Sopenharmony_ci						 bo->base.size);
47962306a36Sopenharmony_ci#ifdef CONFIG_X86
48062306a36Sopenharmony_ci		else if (mem->bus.caching == ttm_cached)
48162306a36Sopenharmony_ci			vaddr_iomem = ioremap_cache(mem->bus.offset,
48262306a36Sopenharmony_ci						  bo->base.size);
48362306a36Sopenharmony_ci#endif
48462306a36Sopenharmony_ci		else
48562306a36Sopenharmony_ci			vaddr_iomem = ioremap(mem->bus.offset, bo->base.size);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		if (!vaddr_iomem)
48862306a36Sopenharmony_ci			return -ENOMEM;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		iosys_map_set_vaddr_iomem(map, vaddr_iomem);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	} else {
49362306a36Sopenharmony_ci		struct ttm_operation_ctx ctx = {
49462306a36Sopenharmony_ci			.interruptible = false,
49562306a36Sopenharmony_ci			.no_wait_gpu = false
49662306a36Sopenharmony_ci		};
49762306a36Sopenharmony_ci		struct ttm_tt *ttm = bo->ttm;
49862306a36Sopenharmony_ci		pgprot_t prot;
49962306a36Sopenharmony_ci		void *vaddr;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		ret = ttm_tt_populate(bo->bdev, ttm, &ctx);
50262306a36Sopenharmony_ci		if (ret)
50362306a36Sopenharmony_ci			return ret;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci		/*
50662306a36Sopenharmony_ci		 * We need to use vmap to get the desired page protection
50762306a36Sopenharmony_ci		 * or to make the buffer object look contiguous.
50862306a36Sopenharmony_ci		 */
50962306a36Sopenharmony_ci		prot = ttm_io_prot(bo, mem, PAGE_KERNEL);
51062306a36Sopenharmony_ci		vaddr = vmap(ttm->pages, ttm->num_pages, 0, prot);
51162306a36Sopenharmony_ci		if (!vaddr)
51262306a36Sopenharmony_ci			return -ENOMEM;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		iosys_map_set_vaddr(map, vaddr);
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_vmap);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci/**
52262306a36Sopenharmony_ci * ttm_bo_vunmap
52362306a36Sopenharmony_ci *
52462306a36Sopenharmony_ci * @bo: The buffer object.
52562306a36Sopenharmony_ci * @map: Object describing the map to unmap.
52662306a36Sopenharmony_ci *
52762306a36Sopenharmony_ci * Unmaps a kernel map set up by ttm_bo_vmap().
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_civoid ttm_bo_vunmap(struct ttm_buffer_object *bo, struct iosys_map *map)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct ttm_resource *mem = bo->resource;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	dma_resv_assert_held(bo->base.resv);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (iosys_map_is_null(map))
53662306a36Sopenharmony_ci		return;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (!map->is_iomem)
53962306a36Sopenharmony_ci		vunmap(map->vaddr);
54062306a36Sopenharmony_ci	else if (!mem->bus.addr)
54162306a36Sopenharmony_ci		iounmap(map->vaddr_iomem);
54262306a36Sopenharmony_ci	iosys_map_clear(map);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	ttm_mem_io_free(bo->bdev, bo->resource);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_vunmap);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int ttm_bo_wait_free_node(struct ttm_buffer_object *bo,
54962306a36Sopenharmony_ci				 bool dst_use_tt)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	long ret;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	ret = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP,
55462306a36Sopenharmony_ci				    false, 15 * HZ);
55562306a36Sopenharmony_ci	if (ret == 0)
55662306a36Sopenharmony_ci		return -EBUSY;
55762306a36Sopenharmony_ci	if (ret < 0)
55862306a36Sopenharmony_ci		return ret;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (!dst_use_tt)
56162306a36Sopenharmony_ci		ttm_bo_tt_destroy(bo);
56262306a36Sopenharmony_ci	ttm_resource_free(bo, &bo->resource);
56362306a36Sopenharmony_ci	return 0;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic int ttm_bo_move_to_ghost(struct ttm_buffer_object *bo,
56762306a36Sopenharmony_ci				struct dma_fence *fence,
56862306a36Sopenharmony_ci				bool dst_use_tt)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct ttm_buffer_object *ghost_obj;
57162306a36Sopenharmony_ci	int ret;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/**
57462306a36Sopenharmony_ci	 * This should help pipeline ordinary buffer moves.
57562306a36Sopenharmony_ci	 *
57662306a36Sopenharmony_ci	 * Hang old buffer memory on a new buffer object,
57762306a36Sopenharmony_ci	 * and leave it to be released when the GPU
57862306a36Sopenharmony_ci	 * operation has completed.
57962306a36Sopenharmony_ci	 */
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	ret = ttm_buffer_object_transfer(bo, &ghost_obj);
58262306a36Sopenharmony_ci	if (ret)
58362306a36Sopenharmony_ci		return ret;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	dma_resv_add_fence(&ghost_obj->base._resv, fence,
58662306a36Sopenharmony_ci			   DMA_RESV_USAGE_KERNEL);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	/**
58962306a36Sopenharmony_ci	 * If we're not moving to fixed memory, the TTM object
59062306a36Sopenharmony_ci	 * needs to stay alive. Otherwhise hang it on the ghost
59162306a36Sopenharmony_ci	 * bo to be unbound and destroyed.
59262306a36Sopenharmony_ci	 */
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (dst_use_tt)
59562306a36Sopenharmony_ci		ghost_obj->ttm = NULL;
59662306a36Sopenharmony_ci	else
59762306a36Sopenharmony_ci		bo->ttm = NULL;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	dma_resv_unlock(&ghost_obj->base._resv);
60062306a36Sopenharmony_ci	ttm_bo_put(ghost_obj);
60162306a36Sopenharmony_ci	return 0;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic void ttm_bo_move_pipeline_evict(struct ttm_buffer_object *bo,
60562306a36Sopenharmony_ci				       struct dma_fence *fence)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct ttm_device *bdev = bo->bdev;
60862306a36Sopenharmony_ci	struct ttm_resource_manager *from;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	from = ttm_manager_type(bdev, bo->resource->mem_type);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/**
61362306a36Sopenharmony_ci	 * BO doesn't have a TTM we need to bind/unbind. Just remember
61462306a36Sopenharmony_ci	 * this eviction and free up the allocation
61562306a36Sopenharmony_ci	 */
61662306a36Sopenharmony_ci	spin_lock(&from->move_lock);
61762306a36Sopenharmony_ci	if (!from->move || dma_fence_is_later(fence, from->move)) {
61862306a36Sopenharmony_ci		dma_fence_put(from->move);
61962306a36Sopenharmony_ci		from->move = dma_fence_get(fence);
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	spin_unlock(&from->move_lock);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	ttm_resource_free(bo, &bo->resource);
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci/**
62762306a36Sopenharmony_ci * ttm_bo_move_accel_cleanup - cleanup helper for hw copies
62862306a36Sopenharmony_ci *
62962306a36Sopenharmony_ci * @bo: A pointer to a struct ttm_buffer_object.
63062306a36Sopenharmony_ci * @fence: A fence object that signals when moving is complete.
63162306a36Sopenharmony_ci * @evict: This is an evict move. Don't return until the buffer is idle.
63262306a36Sopenharmony_ci * @pipeline: evictions are to be pipelined.
63362306a36Sopenharmony_ci * @new_mem: struct ttm_resource indicating where to move.
63462306a36Sopenharmony_ci *
63562306a36Sopenharmony_ci * Accelerated move function to be called when an accelerated move
63662306a36Sopenharmony_ci * has been scheduled. The function will create a new temporary buffer object
63762306a36Sopenharmony_ci * representing the old placement, and put the sync object on both buffer
63862306a36Sopenharmony_ci * objects. After that the newly created buffer object is unref'd to be
63962306a36Sopenharmony_ci * destroyed when the move is complete. This will help pipeline
64062306a36Sopenharmony_ci * buffer moves.
64162306a36Sopenharmony_ci */
64262306a36Sopenharmony_ciint ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
64362306a36Sopenharmony_ci			      struct dma_fence *fence,
64462306a36Sopenharmony_ci			      bool evict,
64562306a36Sopenharmony_ci			      bool pipeline,
64662306a36Sopenharmony_ci			      struct ttm_resource *new_mem)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct ttm_device *bdev = bo->bdev;
64962306a36Sopenharmony_ci	struct ttm_resource_manager *from = ttm_manager_type(bdev, bo->resource->mem_type);
65062306a36Sopenharmony_ci	struct ttm_resource_manager *man = ttm_manager_type(bdev, new_mem->mem_type);
65162306a36Sopenharmony_ci	int ret = 0;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	dma_resv_add_fence(bo->base.resv, fence, DMA_RESV_USAGE_KERNEL);
65462306a36Sopenharmony_ci	if (!evict)
65562306a36Sopenharmony_ci		ret = ttm_bo_move_to_ghost(bo, fence, man->use_tt);
65662306a36Sopenharmony_ci	else if (!from->use_tt && pipeline)
65762306a36Sopenharmony_ci		ttm_bo_move_pipeline_evict(bo, fence);
65862306a36Sopenharmony_ci	else
65962306a36Sopenharmony_ci		ret = ttm_bo_wait_free_node(bo, man->use_tt);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	if (ret)
66262306a36Sopenharmony_ci		return ret;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	ttm_bo_assign_mem(bo, new_mem);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return 0;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_move_accel_cleanup);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci/**
67162306a36Sopenharmony_ci * ttm_bo_move_sync_cleanup - cleanup by waiting for the move to finish
67262306a36Sopenharmony_ci *
67362306a36Sopenharmony_ci * @bo: A pointer to a struct ttm_buffer_object.
67462306a36Sopenharmony_ci * @new_mem: struct ttm_resource indicating where to move.
67562306a36Sopenharmony_ci *
67662306a36Sopenharmony_ci * Special case of ttm_bo_move_accel_cleanup where the bo is guaranteed
67762306a36Sopenharmony_ci * by the caller to be idle. Typically used after memcpy buffer moves.
67862306a36Sopenharmony_ci */
67962306a36Sopenharmony_civoid ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo,
68062306a36Sopenharmony_ci			      struct ttm_resource *new_mem)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	struct ttm_device *bdev = bo->bdev;
68362306a36Sopenharmony_ci	struct ttm_resource_manager *man = ttm_manager_type(bdev, new_mem->mem_type);
68462306a36Sopenharmony_ci	int ret;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	ret = ttm_bo_wait_free_node(bo, man->use_tt);
68762306a36Sopenharmony_ci	if (WARN_ON(ret))
68862306a36Sopenharmony_ci		return;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	ttm_bo_assign_mem(bo, new_mem);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ciEXPORT_SYMBOL(ttm_bo_move_sync_cleanup);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci/**
69562306a36Sopenharmony_ci * ttm_bo_pipeline_gutting - purge the contents of a bo
69662306a36Sopenharmony_ci * @bo: The buffer object
69762306a36Sopenharmony_ci *
69862306a36Sopenharmony_ci * Purge the contents of a bo, async if the bo is not idle.
69962306a36Sopenharmony_ci * After a successful call, the bo is left unpopulated in
70062306a36Sopenharmony_ci * system placement. The function may wait uninterruptible
70162306a36Sopenharmony_ci * for idle on OOM.
70262306a36Sopenharmony_ci *
70362306a36Sopenharmony_ci * Return: 0 if successful, negative error code on failure.
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_ciint ttm_bo_pipeline_gutting(struct ttm_buffer_object *bo)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	struct ttm_buffer_object *ghost;
70862306a36Sopenharmony_ci	struct ttm_tt *ttm;
70962306a36Sopenharmony_ci	int ret;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* If already idle, no need for ghost object dance. */
71262306a36Sopenharmony_ci	if (dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP)) {
71362306a36Sopenharmony_ci		if (!bo->ttm) {
71462306a36Sopenharmony_ci			/* See comment below about clearing. */
71562306a36Sopenharmony_ci			ret = ttm_tt_create(bo, true);
71662306a36Sopenharmony_ci			if (ret)
71762306a36Sopenharmony_ci				return ret;
71862306a36Sopenharmony_ci		} else {
71962306a36Sopenharmony_ci			ttm_tt_unpopulate(bo->bdev, bo->ttm);
72062306a36Sopenharmony_ci			if (bo->type == ttm_bo_type_device)
72162306a36Sopenharmony_ci				ttm_tt_mark_for_clear(bo->ttm);
72262306a36Sopenharmony_ci		}
72362306a36Sopenharmony_ci		ttm_resource_free(bo, &bo->resource);
72462306a36Sopenharmony_ci		return 0;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/*
72862306a36Sopenharmony_ci	 * We need an unpopulated ttm_tt after giving our current one,
72962306a36Sopenharmony_ci	 * if any, to the ghost object. And we can't afford to fail
73062306a36Sopenharmony_ci	 * creating one *after* the operation. If the bo subsequently gets
73162306a36Sopenharmony_ci	 * resurrected, make sure it's cleared (if ttm_bo_type_device)
73262306a36Sopenharmony_ci	 * to avoid leaking sensitive information to user-space.
73362306a36Sopenharmony_ci	 */
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	ttm = bo->ttm;
73662306a36Sopenharmony_ci	bo->ttm = NULL;
73762306a36Sopenharmony_ci	ret = ttm_tt_create(bo, true);
73862306a36Sopenharmony_ci	swap(bo->ttm, ttm);
73962306a36Sopenharmony_ci	if (ret)
74062306a36Sopenharmony_ci		return ret;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	ret = ttm_buffer_object_transfer(bo, &ghost);
74362306a36Sopenharmony_ci	if (ret)
74462306a36Sopenharmony_ci		goto error_destroy_tt;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	ret = dma_resv_copy_fences(&ghost->base._resv, bo->base.resv);
74762306a36Sopenharmony_ci	/* Last resort, wait for the BO to be idle when we are OOM */
74862306a36Sopenharmony_ci	if (ret) {
74962306a36Sopenharmony_ci		dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP,
75062306a36Sopenharmony_ci				      false, MAX_SCHEDULE_TIMEOUT);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	dma_resv_unlock(&ghost->base._resv);
75462306a36Sopenharmony_ci	ttm_bo_put(ghost);
75562306a36Sopenharmony_ci	bo->ttm = ttm;
75662306a36Sopenharmony_ci	return 0;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cierror_destroy_tt:
75962306a36Sopenharmony_ci	ttm_tt_destroy(bo->bdev, ttm);
76062306a36Sopenharmony_ci	return ret;
76162306a36Sopenharmony_ci}
762