162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 2009 Jerome Glisse.
362306a36Sopenharmony_ci * All Rights Reserved.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
662306a36Sopenharmony_ci * copy of this software and associated documentation files (the
762306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
862306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
962306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
1062306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
1162306a36Sopenharmony_ci * the following conditions:
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1462306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1562306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
1662306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
1762306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
1862306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
1962306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the
2262306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
2362306a36Sopenharmony_ci * of the Software.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * Authors:
2862306a36Sopenharmony_ci *    Jerome Glisse <glisse@freedesktop.org>
2962306a36Sopenharmony_ci *    Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
3062306a36Sopenharmony_ci *    Dave Airlie
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3462306a36Sopenharmony_ci#include <linux/pagemap.h>
3562306a36Sopenharmony_ci#include <linux/pci.h>
3662306a36Sopenharmony_ci#include <linux/seq_file.h>
3762306a36Sopenharmony_ci#include <linux/slab.h>
3862306a36Sopenharmony_ci#include <linux/swap.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <drm/drm_device.h>
4162306a36Sopenharmony_ci#include <drm/drm_file.h>
4262306a36Sopenharmony_ci#include <drm/drm_prime.h>
4362306a36Sopenharmony_ci#include <drm/radeon_drm.h>
4462306a36Sopenharmony_ci#include <drm/ttm/ttm_bo.h>
4562306a36Sopenharmony_ci#include <drm/ttm/ttm_placement.h>
4662306a36Sopenharmony_ci#include <drm/ttm/ttm_range_manager.h>
4762306a36Sopenharmony_ci#include <drm/ttm/ttm_tt.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include "radeon_reg.h"
5062306a36Sopenharmony_ci#include "radeon.h"
5162306a36Sopenharmony_ci#include "radeon_ttm.h"
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void radeon_ttm_debugfs_init(struct radeon_device *rdev);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int radeon_ttm_tt_bind(struct ttm_device *bdev, struct ttm_tt *ttm,
5662306a36Sopenharmony_ci			      struct ttm_resource *bo_mem);
5762306a36Sopenharmony_cistatic void radeon_ttm_tt_unbind(struct ttm_device *bdev, struct ttm_tt *ttm);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct radeon_device *radeon_get_rdev(struct ttm_device *bdev)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct radeon_mman *mman;
6262306a36Sopenharmony_ci	struct radeon_device *rdev;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	mman = container_of(bdev, struct radeon_mman, bdev);
6562306a36Sopenharmony_ci	rdev = container_of(mman, struct radeon_device, mman);
6662306a36Sopenharmony_ci	return rdev;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int radeon_ttm_init_vram(struct radeon_device *rdev)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	return ttm_range_man_init(&rdev->mman.bdev, TTM_PL_VRAM,
7262306a36Sopenharmony_ci				  false, rdev->mc.real_vram_size >> PAGE_SHIFT);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int radeon_ttm_init_gtt(struct radeon_device *rdev)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	return ttm_range_man_init(&rdev->mman.bdev, TTM_PL_TT,
7862306a36Sopenharmony_ci				  true, rdev->mc.gtt_size >> PAGE_SHIFT);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void radeon_evict_flags(struct ttm_buffer_object *bo,
8262306a36Sopenharmony_ci				struct ttm_placement *placement)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	static const struct ttm_place placements = {
8562306a36Sopenharmony_ci		.fpfn = 0,
8662306a36Sopenharmony_ci		.lpfn = 0,
8762306a36Sopenharmony_ci		.mem_type = TTM_PL_SYSTEM,
8862306a36Sopenharmony_ci		.flags = 0
8962306a36Sopenharmony_ci	};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	struct radeon_bo *rbo;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (!radeon_ttm_bo_is_radeon_bo(bo)) {
9462306a36Sopenharmony_ci		placement->placement = &placements;
9562306a36Sopenharmony_ci		placement->busy_placement = &placements;
9662306a36Sopenharmony_ci		placement->num_placement = 1;
9762306a36Sopenharmony_ci		placement->num_busy_placement = 1;
9862306a36Sopenharmony_ci		return;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	rbo = container_of(bo, struct radeon_bo, tbo);
10162306a36Sopenharmony_ci	switch (bo->resource->mem_type) {
10262306a36Sopenharmony_ci	case TTM_PL_VRAM:
10362306a36Sopenharmony_ci		if (rbo->rdev->ring[radeon_copy_ring_index(rbo->rdev)].ready == false)
10462306a36Sopenharmony_ci			radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_CPU);
10562306a36Sopenharmony_ci		else if (rbo->rdev->mc.visible_vram_size < rbo->rdev->mc.real_vram_size &&
10662306a36Sopenharmony_ci			 bo->resource->start < (rbo->rdev->mc.visible_vram_size >> PAGE_SHIFT)) {
10762306a36Sopenharmony_ci			unsigned fpfn = rbo->rdev->mc.visible_vram_size >> PAGE_SHIFT;
10862306a36Sopenharmony_ci			int i;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci			/* Try evicting to the CPU inaccessible part of VRAM
11162306a36Sopenharmony_ci			 * first, but only set GTT as busy placement, so this
11262306a36Sopenharmony_ci			 * BO will be evicted to GTT rather than causing other
11362306a36Sopenharmony_ci			 * BOs to be evicted from VRAM
11462306a36Sopenharmony_ci			 */
11562306a36Sopenharmony_ci			radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_VRAM |
11662306a36Sopenharmony_ci							 RADEON_GEM_DOMAIN_GTT);
11762306a36Sopenharmony_ci			rbo->placement.num_busy_placement = 0;
11862306a36Sopenharmony_ci			for (i = 0; i < rbo->placement.num_placement; i++) {
11962306a36Sopenharmony_ci				if (rbo->placements[i].mem_type == TTM_PL_VRAM) {
12062306a36Sopenharmony_ci					if (rbo->placements[i].fpfn < fpfn)
12162306a36Sopenharmony_ci						rbo->placements[i].fpfn = fpfn;
12262306a36Sopenharmony_ci				} else {
12362306a36Sopenharmony_ci					rbo->placement.busy_placement =
12462306a36Sopenharmony_ci						&rbo->placements[i];
12562306a36Sopenharmony_ci					rbo->placement.num_busy_placement = 1;
12662306a36Sopenharmony_ci				}
12762306a36Sopenharmony_ci			}
12862306a36Sopenharmony_ci		} else
12962306a36Sopenharmony_ci			radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT);
13062306a36Sopenharmony_ci		break;
13162306a36Sopenharmony_ci	case TTM_PL_TT:
13262306a36Sopenharmony_ci	default:
13362306a36Sopenharmony_ci		radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_CPU);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci	*placement = rbo->placement;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int radeon_move_blit(struct ttm_buffer_object *bo,
13962306a36Sopenharmony_ci			bool evict,
14062306a36Sopenharmony_ci			struct ttm_resource *new_mem,
14162306a36Sopenharmony_ci			struct ttm_resource *old_mem)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct radeon_device *rdev;
14462306a36Sopenharmony_ci	uint64_t old_start, new_start;
14562306a36Sopenharmony_ci	struct radeon_fence *fence;
14662306a36Sopenharmony_ci	unsigned num_pages;
14762306a36Sopenharmony_ci	int r, ridx;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	rdev = radeon_get_rdev(bo->bdev);
15062306a36Sopenharmony_ci	ridx = radeon_copy_ring_index(rdev);
15162306a36Sopenharmony_ci	old_start = (u64)old_mem->start << PAGE_SHIFT;
15262306a36Sopenharmony_ci	new_start = (u64)new_mem->start << PAGE_SHIFT;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	switch (old_mem->mem_type) {
15562306a36Sopenharmony_ci	case TTM_PL_VRAM:
15662306a36Sopenharmony_ci		old_start += rdev->mc.vram_start;
15762306a36Sopenharmony_ci		break;
15862306a36Sopenharmony_ci	case TTM_PL_TT:
15962306a36Sopenharmony_ci		old_start += rdev->mc.gtt_start;
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	default:
16262306a36Sopenharmony_ci		DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
16362306a36Sopenharmony_ci		return -EINVAL;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci	switch (new_mem->mem_type) {
16662306a36Sopenharmony_ci	case TTM_PL_VRAM:
16762306a36Sopenharmony_ci		new_start += rdev->mc.vram_start;
16862306a36Sopenharmony_ci		break;
16962306a36Sopenharmony_ci	case TTM_PL_TT:
17062306a36Sopenharmony_ci		new_start += rdev->mc.gtt_start;
17162306a36Sopenharmony_ci		break;
17262306a36Sopenharmony_ci	default:
17362306a36Sopenharmony_ci		DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
17462306a36Sopenharmony_ci		return -EINVAL;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	if (!rdev->ring[ridx].ready) {
17762306a36Sopenharmony_ci		DRM_ERROR("Trying to move memory with ring turned off.\n");
17862306a36Sopenharmony_ci		return -EINVAL;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	BUILD_BUG_ON((PAGE_SIZE % RADEON_GPU_PAGE_SIZE) != 0);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	num_pages = PFN_UP(new_mem->size) * (PAGE_SIZE / RADEON_GPU_PAGE_SIZE);
18462306a36Sopenharmony_ci	fence = radeon_copy(rdev, old_start, new_start, num_pages, bo->base.resv);
18562306a36Sopenharmony_ci	if (IS_ERR(fence))
18662306a36Sopenharmony_ci		return PTR_ERR(fence);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	r = ttm_bo_move_accel_cleanup(bo, &fence->base, evict, false, new_mem);
18962306a36Sopenharmony_ci	radeon_fence_unref(&fence);
19062306a36Sopenharmony_ci	return r;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic int radeon_bo_move(struct ttm_buffer_object *bo, bool evict,
19462306a36Sopenharmony_ci			  struct ttm_operation_ctx *ctx,
19562306a36Sopenharmony_ci			  struct ttm_resource *new_mem,
19662306a36Sopenharmony_ci			  struct ttm_place *hop)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct ttm_resource *old_mem = bo->resource;
19962306a36Sopenharmony_ci	struct radeon_device *rdev;
20062306a36Sopenharmony_ci	struct radeon_bo *rbo;
20162306a36Sopenharmony_ci	int r;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (new_mem->mem_type == TTM_PL_TT) {
20462306a36Sopenharmony_ci		r = radeon_ttm_tt_bind(bo->bdev, bo->ttm, new_mem);
20562306a36Sopenharmony_ci		if (r)
20662306a36Sopenharmony_ci			return r;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	r = ttm_bo_wait_ctx(bo, ctx);
21062306a36Sopenharmony_ci	if (r)
21162306a36Sopenharmony_ci		return r;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	rbo = container_of(bo, struct radeon_bo, tbo);
21462306a36Sopenharmony_ci	rdev = radeon_get_rdev(bo->bdev);
21562306a36Sopenharmony_ci	if (!old_mem || (old_mem->mem_type == TTM_PL_SYSTEM &&
21662306a36Sopenharmony_ci			 bo->ttm == NULL)) {
21762306a36Sopenharmony_ci		ttm_bo_move_null(bo, new_mem);
21862306a36Sopenharmony_ci		goto out;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci	if (old_mem->mem_type == TTM_PL_SYSTEM &&
22162306a36Sopenharmony_ci	    new_mem->mem_type == TTM_PL_TT) {
22262306a36Sopenharmony_ci		ttm_bo_move_null(bo, new_mem);
22362306a36Sopenharmony_ci		goto out;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (old_mem->mem_type == TTM_PL_TT &&
22762306a36Sopenharmony_ci	    new_mem->mem_type == TTM_PL_SYSTEM) {
22862306a36Sopenharmony_ci		radeon_ttm_tt_unbind(bo->bdev, bo->ttm);
22962306a36Sopenharmony_ci		ttm_resource_free(bo, &bo->resource);
23062306a36Sopenharmony_ci		ttm_bo_assign_mem(bo, new_mem);
23162306a36Sopenharmony_ci		goto out;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci	if (rdev->ring[radeon_copy_ring_index(rdev)].ready &&
23462306a36Sopenharmony_ci	    rdev->asic->copy.copy != NULL) {
23562306a36Sopenharmony_ci		if ((old_mem->mem_type == TTM_PL_SYSTEM &&
23662306a36Sopenharmony_ci		     new_mem->mem_type == TTM_PL_VRAM) ||
23762306a36Sopenharmony_ci		    (old_mem->mem_type == TTM_PL_VRAM &&
23862306a36Sopenharmony_ci		     new_mem->mem_type == TTM_PL_SYSTEM)) {
23962306a36Sopenharmony_ci			hop->fpfn = 0;
24062306a36Sopenharmony_ci			hop->lpfn = 0;
24162306a36Sopenharmony_ci			hop->mem_type = TTM_PL_TT;
24262306a36Sopenharmony_ci			hop->flags = 0;
24362306a36Sopenharmony_ci			return -EMULTIHOP;
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		r = radeon_move_blit(bo, evict, new_mem, old_mem);
24762306a36Sopenharmony_ci	} else {
24862306a36Sopenharmony_ci		r = -ENODEV;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (r) {
25262306a36Sopenharmony_ci		r = ttm_bo_move_memcpy(bo, ctx, new_mem);
25362306a36Sopenharmony_ci		if (r)
25462306a36Sopenharmony_ci			return r;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciout:
25862306a36Sopenharmony_ci	/* update statistics */
25962306a36Sopenharmony_ci	atomic64_add(bo->base.size, &rdev->num_bytes_moved);
26062306a36Sopenharmony_ci	radeon_bo_move_notify(bo);
26162306a36Sopenharmony_ci	return 0;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int radeon_ttm_io_mem_reserve(struct ttm_device *bdev, struct ttm_resource *mem)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
26762306a36Sopenharmony_ci	size_t bus_size = (size_t)mem->size;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	switch (mem->mem_type) {
27062306a36Sopenharmony_ci	case TTM_PL_SYSTEM:
27162306a36Sopenharmony_ci		/* system memory */
27262306a36Sopenharmony_ci		return 0;
27362306a36Sopenharmony_ci	case TTM_PL_TT:
27462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
27562306a36Sopenharmony_ci		if (rdev->flags & RADEON_IS_AGP) {
27662306a36Sopenharmony_ci			/* RADEON_IS_AGP is set only if AGP is active */
27762306a36Sopenharmony_ci			mem->bus.offset = (mem->start << PAGE_SHIFT) +
27862306a36Sopenharmony_ci				rdev->mc.agp_base;
27962306a36Sopenharmony_ci			mem->bus.is_iomem = !rdev->agp->cant_use_aperture;
28062306a36Sopenharmony_ci			mem->bus.caching = ttm_write_combined;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci#endif
28362306a36Sopenharmony_ci		break;
28462306a36Sopenharmony_ci	case TTM_PL_VRAM:
28562306a36Sopenharmony_ci		mem->bus.offset = mem->start << PAGE_SHIFT;
28662306a36Sopenharmony_ci		/* check if it's visible */
28762306a36Sopenharmony_ci		if ((mem->bus.offset + bus_size) > rdev->mc.visible_vram_size)
28862306a36Sopenharmony_ci			return -EINVAL;
28962306a36Sopenharmony_ci		mem->bus.offset += rdev->mc.aper_base;
29062306a36Sopenharmony_ci		mem->bus.is_iomem = true;
29162306a36Sopenharmony_ci		mem->bus.caching = ttm_write_combined;
29262306a36Sopenharmony_ci#ifdef __alpha__
29362306a36Sopenharmony_ci		/*
29462306a36Sopenharmony_ci		 * Alpha: use bus.addr to hold the ioremap() return,
29562306a36Sopenharmony_ci		 * so we can modify bus.base below.
29662306a36Sopenharmony_ci		 */
29762306a36Sopenharmony_ci		mem->bus.addr = ioremap_wc(mem->bus.offset, bus_size);
29862306a36Sopenharmony_ci		if (!mem->bus.addr)
29962306a36Sopenharmony_ci			return -ENOMEM;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		/*
30262306a36Sopenharmony_ci		 * Alpha: Use just the bus offset plus
30362306a36Sopenharmony_ci		 * the hose/domain memory base for bus.base.
30462306a36Sopenharmony_ci		 * It then can be used to build PTEs for VRAM
30562306a36Sopenharmony_ci		 * access, as done in ttm_bo_vm_fault().
30662306a36Sopenharmony_ci		 */
30762306a36Sopenharmony_ci		mem->bus.offset = (mem->bus.offset & 0x0ffffffffUL) +
30862306a36Sopenharmony_ci			rdev->hose->dense_mem_base;
30962306a36Sopenharmony_ci#endif
31062306a36Sopenharmony_ci		break;
31162306a36Sopenharmony_ci	default:
31262306a36Sopenharmony_ci		return -EINVAL;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/*
31862306a36Sopenharmony_ci * TTM backend functions.
31962306a36Sopenharmony_ci */
32062306a36Sopenharmony_cistruct radeon_ttm_tt {
32162306a36Sopenharmony_ci	struct ttm_tt		ttm;
32262306a36Sopenharmony_ci	u64				offset;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	uint64_t			userptr;
32562306a36Sopenharmony_ci	struct mm_struct		*usermm;
32662306a36Sopenharmony_ci	uint32_t			userflags;
32762306a36Sopenharmony_ci	bool bound;
32862306a36Sopenharmony_ci};
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/* prepare the sg table with the user pages */
33162306a36Sopenharmony_cistatic int radeon_ttm_tt_pin_userptr(struct ttm_device *bdev, struct ttm_tt *ttm)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
33462306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = (void *)ttm;
33562306a36Sopenharmony_ci	unsigned pinned = 0;
33662306a36Sopenharmony_ci	int r;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
33962306a36Sopenharmony_ci	enum dma_data_direction direction = write ?
34062306a36Sopenharmony_ci		DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (current->mm != gtt->usermm)
34362306a36Sopenharmony_ci		return -EPERM;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (gtt->userflags & RADEON_GEM_USERPTR_ANONONLY) {
34662306a36Sopenharmony_ci		/* check that we only pin down anonymous memory
34762306a36Sopenharmony_ci		   to prevent problems with writeback */
34862306a36Sopenharmony_ci		unsigned long end = gtt->userptr + (u64)ttm->num_pages * PAGE_SIZE;
34962306a36Sopenharmony_ci		struct vm_area_struct *vma;
35062306a36Sopenharmony_ci		vma = find_vma(gtt->usermm, gtt->userptr);
35162306a36Sopenharmony_ci		if (!vma || vma->vm_file || vma->vm_end < end)
35262306a36Sopenharmony_ci			return -EPERM;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	do {
35662306a36Sopenharmony_ci		unsigned num_pages = ttm->num_pages - pinned;
35762306a36Sopenharmony_ci		uint64_t userptr = gtt->userptr + pinned * PAGE_SIZE;
35862306a36Sopenharmony_ci		struct page **pages = ttm->pages + pinned;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		r = get_user_pages(userptr, num_pages, write ? FOLL_WRITE : 0,
36162306a36Sopenharmony_ci				   pages);
36262306a36Sopenharmony_ci		if (r < 0)
36362306a36Sopenharmony_ci			goto release_pages;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		pinned += r;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	} while (pinned < ttm->num_pages);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	r = sg_alloc_table_from_pages(ttm->sg, ttm->pages, ttm->num_pages, 0,
37062306a36Sopenharmony_ci				      (u64)ttm->num_pages << PAGE_SHIFT,
37162306a36Sopenharmony_ci				      GFP_KERNEL);
37262306a36Sopenharmony_ci	if (r)
37362306a36Sopenharmony_ci		goto release_sg;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	r = dma_map_sgtable(rdev->dev, ttm->sg, direction, 0);
37662306a36Sopenharmony_ci	if (r)
37762306a36Sopenharmony_ci		goto release_sg;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	drm_prime_sg_to_dma_addr_array(ttm->sg, gtt->ttm.dma_address,
38062306a36Sopenharmony_ci				       ttm->num_pages);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cirelease_sg:
38562306a36Sopenharmony_ci	kfree(ttm->sg);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cirelease_pages:
38862306a36Sopenharmony_ci	release_pages(ttm->pages, pinned);
38962306a36Sopenharmony_ci	return r;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic void radeon_ttm_tt_unpin_userptr(struct ttm_device *bdev, struct ttm_tt *ttm)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
39562306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = (void *)ttm;
39662306a36Sopenharmony_ci	struct sg_page_iter sg_iter;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
39962306a36Sopenharmony_ci	enum dma_data_direction direction = write ?
40062306a36Sopenharmony_ci		DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* double check that we don't free the table twice */
40362306a36Sopenharmony_ci	if (!ttm->sg || !ttm->sg->sgl)
40462306a36Sopenharmony_ci		return;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* free the sg table and pages again */
40762306a36Sopenharmony_ci	dma_unmap_sgtable(rdev->dev, ttm->sg, direction, 0);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	for_each_sgtable_page(ttm->sg, &sg_iter, 0) {
41062306a36Sopenharmony_ci		struct page *page = sg_page_iter_page(&sg_iter);
41162306a36Sopenharmony_ci		if (!(gtt->userflags & RADEON_GEM_USERPTR_READONLY))
41262306a36Sopenharmony_ci			set_page_dirty(page);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		mark_page_accessed(page);
41562306a36Sopenharmony_ci		put_page(page);
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	sg_free_table(ttm->sg);
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_cistatic bool radeon_ttm_backend_is_bound(struct ttm_tt *ttm)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = (void*)ttm;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return (gtt->bound);
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int radeon_ttm_backend_bind(struct ttm_device *bdev,
42962306a36Sopenharmony_ci				   struct ttm_tt *ttm,
43062306a36Sopenharmony_ci				   struct ttm_resource *bo_mem)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = (void*)ttm;
43362306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
43462306a36Sopenharmony_ci	uint32_t flags = RADEON_GART_PAGE_VALID | RADEON_GART_PAGE_READ |
43562306a36Sopenharmony_ci		RADEON_GART_PAGE_WRITE;
43662306a36Sopenharmony_ci	int r;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (gtt->bound)
43962306a36Sopenharmony_ci		return 0;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (gtt->userptr) {
44262306a36Sopenharmony_ci		radeon_ttm_tt_pin_userptr(bdev, ttm);
44362306a36Sopenharmony_ci		flags &= ~RADEON_GART_PAGE_WRITE;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT);
44762306a36Sopenharmony_ci	if (!ttm->num_pages) {
44862306a36Sopenharmony_ci		WARN(1, "nothing to bind %u pages for mreg %p back %p!\n",
44962306a36Sopenharmony_ci		     ttm->num_pages, bo_mem, ttm);
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci	if (ttm->caching == ttm_cached)
45262306a36Sopenharmony_ci		flags |= RADEON_GART_PAGE_SNOOP;
45362306a36Sopenharmony_ci	r = radeon_gart_bind(rdev, gtt->offset, ttm->num_pages,
45462306a36Sopenharmony_ci			     ttm->pages, gtt->ttm.dma_address, flags);
45562306a36Sopenharmony_ci	if (r) {
45662306a36Sopenharmony_ci		DRM_ERROR("failed to bind %u pages at 0x%08X\n",
45762306a36Sopenharmony_ci			  ttm->num_pages, (unsigned)gtt->offset);
45862306a36Sopenharmony_ci		return r;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci	gtt->bound = true;
46162306a36Sopenharmony_ci	return 0;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic void radeon_ttm_backend_unbind(struct ttm_device *bdev, struct ttm_tt *ttm)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = (void *)ttm;
46762306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (gtt->userptr)
47062306a36Sopenharmony_ci		radeon_ttm_tt_unpin_userptr(bdev, ttm);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (!gtt->bound)
47362306a36Sopenharmony_ci		return;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	radeon_gart_unbind(rdev, gtt->offset, ttm->num_pages);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	gtt->bound = false;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic void radeon_ttm_backend_destroy(struct ttm_device *bdev, struct ttm_tt *ttm)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = (void *)ttm;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	ttm_tt_fini(&gtt->ttm);
48562306a36Sopenharmony_ci	kfree(gtt);
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic struct ttm_tt *radeon_ttm_tt_create(struct ttm_buffer_object *bo,
48962306a36Sopenharmony_ci					   uint32_t page_flags)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt;
49262306a36Sopenharmony_ci	enum ttm_caching caching;
49362306a36Sopenharmony_ci	struct radeon_bo *rbo;
49462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
49562306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bo->bdev);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (rdev->flags & RADEON_IS_AGP) {
49862306a36Sopenharmony_ci		return ttm_agp_tt_create(bo, rdev->agp->bridge, page_flags);
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci#endif
50162306a36Sopenharmony_ci	rbo = container_of(bo, struct radeon_bo, tbo);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	gtt = kzalloc(sizeof(struct radeon_ttm_tt), GFP_KERNEL);
50462306a36Sopenharmony_ci	if (gtt == NULL) {
50562306a36Sopenharmony_ci		return NULL;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	if (rbo->flags & RADEON_GEM_GTT_UC)
50962306a36Sopenharmony_ci		caching = ttm_uncached;
51062306a36Sopenharmony_ci	else if (rbo->flags & RADEON_GEM_GTT_WC)
51162306a36Sopenharmony_ci		caching = ttm_write_combined;
51262306a36Sopenharmony_ci	else
51362306a36Sopenharmony_ci		caching = ttm_cached;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (ttm_sg_tt_init(&gtt->ttm, bo, page_flags, caching)) {
51662306a36Sopenharmony_ci		kfree(gtt);
51762306a36Sopenharmony_ci		return NULL;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci	return &gtt->ttm;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic struct radeon_ttm_tt *radeon_ttm_tt_to_gtt(struct radeon_device *rdev,
52362306a36Sopenharmony_ci						  struct ttm_tt *ttm)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
52662306a36Sopenharmony_ci	if (rdev->flags & RADEON_IS_AGP)
52762306a36Sopenharmony_ci		return NULL;
52862306a36Sopenharmony_ci#endif
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (!ttm)
53162306a36Sopenharmony_ci		return NULL;
53262306a36Sopenharmony_ci	return container_of(ttm, struct radeon_ttm_tt, ttm);
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int radeon_ttm_tt_populate(struct ttm_device *bdev,
53662306a36Sopenharmony_ci				  struct ttm_tt *ttm,
53762306a36Sopenharmony_ci				  struct ttm_operation_ctx *ctx)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
54062306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
54162306a36Sopenharmony_ci	bool slave = !!(ttm->page_flags & TTM_TT_FLAG_EXTERNAL);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (gtt && gtt->userptr) {
54462306a36Sopenharmony_ci		ttm->sg = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
54562306a36Sopenharmony_ci		if (!ttm->sg)
54662306a36Sopenharmony_ci			return -ENOMEM;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		ttm->page_flags |= TTM_TT_FLAG_EXTERNAL;
54962306a36Sopenharmony_ci		return 0;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (slave && ttm->sg) {
55362306a36Sopenharmony_ci		drm_prime_sg_to_dma_addr_array(ttm->sg, gtt->ttm.dma_address,
55462306a36Sopenharmony_ci					       ttm->num_pages);
55562306a36Sopenharmony_ci		return 0;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return ttm_pool_alloc(&rdev->mman.bdev.pool, ttm, ctx);
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic void radeon_ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
56462306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
56562306a36Sopenharmony_ci	bool slave = !!(ttm->page_flags & TTM_TT_FLAG_EXTERNAL);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	radeon_ttm_tt_unbind(bdev, ttm);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (gtt && gtt->userptr) {
57062306a36Sopenharmony_ci		kfree(ttm->sg);
57162306a36Sopenharmony_ci		ttm->page_flags &= ~TTM_TT_FLAG_EXTERNAL;
57262306a36Sopenharmony_ci		return;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (slave)
57662306a36Sopenharmony_ci		return;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	return ttm_pool_free(&rdev->mman.bdev.pool, ttm);
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ciint radeon_ttm_tt_set_userptr(struct radeon_device *rdev,
58262306a36Sopenharmony_ci			      struct ttm_tt *ttm, uint64_t addr,
58362306a36Sopenharmony_ci			      uint32_t flags)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (gtt == NULL)
58862306a36Sopenharmony_ci		return -EINVAL;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	gtt->userptr = addr;
59162306a36Sopenharmony_ci	gtt->usermm = current->mm;
59262306a36Sopenharmony_ci	gtt->userflags = flags;
59362306a36Sopenharmony_ci	return 0;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cibool radeon_ttm_tt_is_bound(struct ttm_device *bdev,
59762306a36Sopenharmony_ci			    struct ttm_tt *ttm)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
60062306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
60162306a36Sopenharmony_ci	if (rdev->flags & RADEON_IS_AGP)
60262306a36Sopenharmony_ci		return ttm_agp_is_bound(ttm);
60362306a36Sopenharmony_ci#endif
60462306a36Sopenharmony_ci	return radeon_ttm_backend_is_bound(ttm);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int radeon_ttm_tt_bind(struct ttm_device *bdev,
60862306a36Sopenharmony_ci			      struct ttm_tt *ttm,
60962306a36Sopenharmony_ci			      struct ttm_resource *bo_mem)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
61262306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
61362306a36Sopenharmony_ci#endif
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (!bo_mem)
61662306a36Sopenharmony_ci		return -EINVAL;
61762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
61862306a36Sopenharmony_ci	if (rdev->flags & RADEON_IS_AGP)
61962306a36Sopenharmony_ci		return ttm_agp_bind(ttm, bo_mem);
62062306a36Sopenharmony_ci#endif
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return radeon_ttm_backend_bind(bdev, ttm, bo_mem);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic void radeon_ttm_tt_unbind(struct ttm_device *bdev,
62662306a36Sopenharmony_ci				 struct ttm_tt *ttm)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
62962306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (rdev->flags & RADEON_IS_AGP) {
63262306a36Sopenharmony_ci		ttm_agp_unbind(ttm);
63362306a36Sopenharmony_ci		return;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci#endif
63662306a36Sopenharmony_ci	radeon_ttm_backend_unbind(bdev, ttm);
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic void radeon_ttm_tt_destroy(struct ttm_device *bdev,
64062306a36Sopenharmony_ci				  struct ttm_tt *ttm)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
64362306a36Sopenharmony_ci	struct radeon_device *rdev = radeon_get_rdev(bdev);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if (rdev->flags & RADEON_IS_AGP) {
64662306a36Sopenharmony_ci		ttm_agp_destroy(ttm);
64762306a36Sopenharmony_ci		return;
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci#endif
65062306a36Sopenharmony_ci	radeon_ttm_backend_destroy(bdev, ttm);
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cibool radeon_ttm_tt_has_userptr(struct radeon_device *rdev,
65462306a36Sopenharmony_ci			       struct ttm_tt *ttm)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (gtt == NULL)
65962306a36Sopenharmony_ci		return false;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	return !!gtt->userptr;
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cibool radeon_ttm_tt_is_readonly(struct radeon_device *rdev,
66562306a36Sopenharmony_ci			       struct ttm_tt *ttm)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (gtt == NULL)
67062306a36Sopenharmony_ci		return false;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	return !!(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic struct ttm_device_funcs radeon_bo_driver = {
67662306a36Sopenharmony_ci	.ttm_tt_create = &radeon_ttm_tt_create,
67762306a36Sopenharmony_ci	.ttm_tt_populate = &radeon_ttm_tt_populate,
67862306a36Sopenharmony_ci	.ttm_tt_unpopulate = &radeon_ttm_tt_unpopulate,
67962306a36Sopenharmony_ci	.ttm_tt_destroy = &radeon_ttm_tt_destroy,
68062306a36Sopenharmony_ci	.eviction_valuable = ttm_bo_eviction_valuable,
68162306a36Sopenharmony_ci	.evict_flags = &radeon_evict_flags,
68262306a36Sopenharmony_ci	.move = &radeon_bo_move,
68362306a36Sopenharmony_ci	.io_mem_reserve = &radeon_ttm_io_mem_reserve,
68462306a36Sopenharmony_ci};
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ciint radeon_ttm_init(struct radeon_device *rdev)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	int r;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/* No others user of address space so set it to 0 */
69162306a36Sopenharmony_ci	r = ttm_device_init(&rdev->mman.bdev, &radeon_bo_driver, rdev->dev,
69262306a36Sopenharmony_ci			       rdev->ddev->anon_inode->i_mapping,
69362306a36Sopenharmony_ci			       rdev->ddev->vma_offset_manager,
69462306a36Sopenharmony_ci			       rdev->need_swiotlb,
69562306a36Sopenharmony_ci			       dma_addressing_limited(&rdev->pdev->dev));
69662306a36Sopenharmony_ci	if (r) {
69762306a36Sopenharmony_ci		DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
69862306a36Sopenharmony_ci		return r;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci	rdev->mman.initialized = true;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	r = radeon_ttm_init_vram(rdev);
70362306a36Sopenharmony_ci	if (r) {
70462306a36Sopenharmony_ci		DRM_ERROR("Failed initializing VRAM heap.\n");
70562306a36Sopenharmony_ci		return r;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci	/* Change the size here instead of the init above so only lpfn is affected */
70862306a36Sopenharmony_ci	radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	r = radeon_bo_create(rdev, 256 * 1024, PAGE_SIZE, true,
71162306a36Sopenharmony_ci			     RADEON_GEM_DOMAIN_VRAM, 0, NULL,
71262306a36Sopenharmony_ci			     NULL, &rdev->stolen_vga_memory);
71362306a36Sopenharmony_ci	if (r) {
71462306a36Sopenharmony_ci		return r;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci	r = radeon_bo_reserve(rdev->stolen_vga_memory, false);
71762306a36Sopenharmony_ci	if (r)
71862306a36Sopenharmony_ci		return r;
71962306a36Sopenharmony_ci	r = radeon_bo_pin(rdev->stolen_vga_memory, RADEON_GEM_DOMAIN_VRAM, NULL);
72062306a36Sopenharmony_ci	radeon_bo_unreserve(rdev->stolen_vga_memory);
72162306a36Sopenharmony_ci	if (r) {
72262306a36Sopenharmony_ci		radeon_bo_unref(&rdev->stolen_vga_memory);
72362306a36Sopenharmony_ci		return r;
72462306a36Sopenharmony_ci	}
72562306a36Sopenharmony_ci	DRM_INFO("radeon: %uM of VRAM memory ready\n",
72662306a36Sopenharmony_ci		 (unsigned) (rdev->mc.real_vram_size / (1024 * 1024)));
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	r = radeon_ttm_init_gtt(rdev);
72962306a36Sopenharmony_ci	if (r) {
73062306a36Sopenharmony_ci		DRM_ERROR("Failed initializing GTT heap.\n");
73162306a36Sopenharmony_ci		return r;
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci	DRM_INFO("radeon: %uM of GTT memory ready.\n",
73462306a36Sopenharmony_ci		 (unsigned)(rdev->mc.gtt_size / (1024 * 1024)));
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	radeon_ttm_debugfs_init(rdev);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	return 0;
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_civoid radeon_ttm_fini(struct radeon_device *rdev)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	int r;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	if (!rdev->mman.initialized)
74662306a36Sopenharmony_ci		return;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (rdev->stolen_vga_memory) {
74962306a36Sopenharmony_ci		r = radeon_bo_reserve(rdev->stolen_vga_memory, false);
75062306a36Sopenharmony_ci		if (r == 0) {
75162306a36Sopenharmony_ci			radeon_bo_unpin(rdev->stolen_vga_memory);
75262306a36Sopenharmony_ci			radeon_bo_unreserve(rdev->stolen_vga_memory);
75362306a36Sopenharmony_ci		}
75462306a36Sopenharmony_ci		radeon_bo_unref(&rdev->stolen_vga_memory);
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci	ttm_range_man_fini(&rdev->mman.bdev, TTM_PL_VRAM);
75762306a36Sopenharmony_ci	ttm_range_man_fini(&rdev->mman.bdev, TTM_PL_TT);
75862306a36Sopenharmony_ci	ttm_device_fini(&rdev->mman.bdev);
75962306a36Sopenharmony_ci	radeon_gart_fini(rdev);
76062306a36Sopenharmony_ci	rdev->mman.initialized = false;
76162306a36Sopenharmony_ci	DRM_INFO("radeon: ttm finalized\n");
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci/* this should only be called at bootup or when userspace
76562306a36Sopenharmony_ci * isn't running */
76662306a36Sopenharmony_civoid radeon_ttm_set_active_vram_size(struct radeon_device *rdev, u64 size)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	struct ttm_resource_manager *man;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (!rdev->mman.initialized)
77162306a36Sopenharmony_ci		return;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	man = ttm_manager_type(&rdev->mman.bdev, TTM_PL_VRAM);
77462306a36Sopenharmony_ci	/* this just adjusts TTM size idea, which sets lpfn to the correct value */
77562306a36Sopenharmony_ci	man->size = size >> PAGE_SHIFT;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic int radeon_ttm_page_pool_show(struct seq_file *m, void *data)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct radeon_device *rdev = m->private;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return ttm_pool_debugfs(&rdev->mman.bdev.pool, m);
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(radeon_ttm_page_pool);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic int radeon_ttm_vram_open(struct inode *inode, struct file *filep)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct radeon_device *rdev = inode->i_private;
79262306a36Sopenharmony_ci	i_size_write(inode, rdev->mc.mc_vram_size);
79362306a36Sopenharmony_ci	filep->private_data = inode->i_private;
79462306a36Sopenharmony_ci	return 0;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cistatic ssize_t radeon_ttm_vram_read(struct file *f, char __user *buf,
79862306a36Sopenharmony_ci				    size_t size, loff_t *pos)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct radeon_device *rdev = f->private_data;
80162306a36Sopenharmony_ci	ssize_t result = 0;
80262306a36Sopenharmony_ci	int r;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (size & 0x3 || *pos & 0x3)
80562306a36Sopenharmony_ci		return -EINVAL;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	while (size) {
80862306a36Sopenharmony_ci		unsigned long flags;
80962306a36Sopenharmony_ci		uint32_t value;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		if (*pos >= rdev->mc.mc_vram_size)
81262306a36Sopenharmony_ci			return result;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		spin_lock_irqsave(&rdev->mmio_idx_lock, flags);
81562306a36Sopenharmony_ci		WREG32(RADEON_MM_INDEX, ((uint32_t)*pos) | 0x80000000);
81662306a36Sopenharmony_ci		if (rdev->family >= CHIP_CEDAR)
81762306a36Sopenharmony_ci			WREG32(EVERGREEN_MM_INDEX_HI, *pos >> 31);
81862306a36Sopenharmony_ci		value = RREG32(RADEON_MM_DATA);
81962306a36Sopenharmony_ci		spin_unlock_irqrestore(&rdev->mmio_idx_lock, flags);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci		r = put_user(value, (uint32_t __user *)buf);
82262306a36Sopenharmony_ci		if (r)
82362306a36Sopenharmony_ci			return r;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci		result += 4;
82662306a36Sopenharmony_ci		buf += 4;
82762306a36Sopenharmony_ci		*pos += 4;
82862306a36Sopenharmony_ci		size -= 4;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	return result;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic const struct file_operations radeon_ttm_vram_fops = {
83562306a36Sopenharmony_ci	.owner = THIS_MODULE,
83662306a36Sopenharmony_ci	.open = radeon_ttm_vram_open,
83762306a36Sopenharmony_ci	.read = radeon_ttm_vram_read,
83862306a36Sopenharmony_ci	.llseek = default_llseek
83962306a36Sopenharmony_ci};
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic int radeon_ttm_gtt_open(struct inode *inode, struct file *filep)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct radeon_device *rdev = inode->i_private;
84462306a36Sopenharmony_ci	i_size_write(inode, rdev->mc.gtt_size);
84562306a36Sopenharmony_ci	filep->private_data = inode->i_private;
84662306a36Sopenharmony_ci	return 0;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic ssize_t radeon_ttm_gtt_read(struct file *f, char __user *buf,
85062306a36Sopenharmony_ci				   size_t size, loff_t *pos)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	struct radeon_device *rdev = f->private_data;
85362306a36Sopenharmony_ci	ssize_t result = 0;
85462306a36Sopenharmony_ci	int r;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	while (size) {
85762306a36Sopenharmony_ci		loff_t p = *pos / PAGE_SIZE;
85862306a36Sopenharmony_ci		unsigned off = *pos & ~PAGE_MASK;
85962306a36Sopenharmony_ci		size_t cur_size = min_t(size_t, size, PAGE_SIZE - off);
86062306a36Sopenharmony_ci		struct page *page;
86162306a36Sopenharmony_ci		void *ptr;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		if (p >= rdev->gart.num_cpu_pages)
86462306a36Sopenharmony_ci			return result;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci		page = rdev->gart.pages[p];
86762306a36Sopenharmony_ci		if (page) {
86862306a36Sopenharmony_ci			ptr = kmap_local_page(page);
86962306a36Sopenharmony_ci			ptr += off;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci			r = copy_to_user(buf, ptr, cur_size);
87262306a36Sopenharmony_ci			kunmap_local(ptr);
87362306a36Sopenharmony_ci		} else
87462306a36Sopenharmony_ci			r = clear_user(buf, cur_size);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		if (r)
87762306a36Sopenharmony_ci			return -EFAULT;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		result += cur_size;
88062306a36Sopenharmony_ci		buf += cur_size;
88162306a36Sopenharmony_ci		*pos += cur_size;
88262306a36Sopenharmony_ci		size -= cur_size;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	return result;
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic const struct file_operations radeon_ttm_gtt_fops = {
88962306a36Sopenharmony_ci	.owner = THIS_MODULE,
89062306a36Sopenharmony_ci	.open = radeon_ttm_gtt_open,
89162306a36Sopenharmony_ci	.read = radeon_ttm_gtt_read,
89262306a36Sopenharmony_ci	.llseek = default_llseek
89362306a36Sopenharmony_ci};
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci#endif
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic void radeon_ttm_debugfs_init(struct radeon_device *rdev)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
90062306a36Sopenharmony_ci	struct drm_minor *minor = rdev->ddev->primary;
90162306a36Sopenharmony_ci	struct dentry *root = minor->debugfs_root;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	debugfs_create_file("radeon_vram", 0444, root, rdev,
90462306a36Sopenharmony_ci			    &radeon_ttm_vram_fops);
90562306a36Sopenharmony_ci	debugfs_create_file("radeon_gtt", 0444, root, rdev,
90662306a36Sopenharmony_ci			    &radeon_ttm_gtt_fops);
90762306a36Sopenharmony_ci	debugfs_create_file("ttm_page_pool", 0444, root, rdev,
90862306a36Sopenharmony_ci			    &radeon_ttm_page_pool_fops);
90962306a36Sopenharmony_ci	ttm_resource_manager_create_debugfs(ttm_manager_type(&rdev->mman.bdev,
91062306a36Sopenharmony_ci							     TTM_PL_VRAM),
91162306a36Sopenharmony_ci					    root, "radeon_vram_mm");
91262306a36Sopenharmony_ci	ttm_resource_manager_create_debugfs(ttm_manager_type(&rdev->mman.bdev,
91362306a36Sopenharmony_ci							     TTM_PL_TT),
91462306a36Sopenharmony_ci					    root, "radeon_gtt_mm");
91562306a36Sopenharmony_ci#endif
91662306a36Sopenharmony_ci}
917