162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat
462306a36Sopenharmony_ci * Author: Rob Clark <robdclark@gmail.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/dma-map-ops.h>
862306a36Sopenharmony_ci#include <linux/vmalloc.h>
962306a36Sopenharmony_ci#include <linux/spinlock.h>
1062306a36Sopenharmony_ci#include <linux/shmem_fs.h>
1162306a36Sopenharmony_ci#include <linux/dma-buf.h>
1262306a36Sopenharmony_ci#include <linux/pfn_t.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <drm/drm_prime.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "msm_drv.h"
1762306a36Sopenharmony_ci#include "msm_fence.h"
1862306a36Sopenharmony_ci#include "msm_gem.h"
1962306a36Sopenharmony_ci#include "msm_gpu.h"
2062306a36Sopenharmony_ci#include "msm_mmu.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic dma_addr_t physaddr(struct drm_gem_object *obj)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
2562306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
2662306a36Sopenharmony_ci	return (((dma_addr_t)msm_obj->vram_node->start) << PAGE_SHIFT) +
2762306a36Sopenharmony_ci			priv->vram.paddr;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic bool use_pages(struct drm_gem_object *obj)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
3362306a36Sopenharmony_ci	return !msm_obj->vram_node;
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci * Cache sync.. this is a bit over-complicated, to fit dma-mapping
3862306a36Sopenharmony_ci * API.  Really GPU cache is out of scope here (handled on cmdstream)
3962306a36Sopenharmony_ci * and all we need to do is invalidate newly allocated pages before
4062306a36Sopenharmony_ci * mapping to CPU as uncached/writecombine.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * On top of this, we have the added headache, that depending on
4362306a36Sopenharmony_ci * display generation, the display's iommu may be wired up to either
4462306a36Sopenharmony_ci * the toplevel drm device (mdss), or to the mdp sub-node, meaning
4562306a36Sopenharmony_ci * that here we either have dma-direct or iommu ops.
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * Let this be a cautionary tail of abstraction gone wrong.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void sync_for_device(struct msm_gem_object *msm_obj)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct device *dev = msm_obj->base.dev->dev;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	dma_map_sgtable(dev, msm_obj->sgt, DMA_BIDIRECTIONAL, 0);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void sync_for_cpu(struct msm_gem_object *msm_obj)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct device *dev = msm_obj->base.dev->dev;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	dma_unmap_sgtable(dev, msm_obj->sgt, DMA_BIDIRECTIONAL, 0);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void update_lru_active(struct drm_gem_object *obj)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
6762306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	GEM_WARN_ON(!msm_obj->pages);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (msm_obj->pin_count) {
7262306a36Sopenharmony_ci		drm_gem_lru_move_tail_locked(&priv->lru.pinned, obj);
7362306a36Sopenharmony_ci	} else if (msm_obj->madv == MSM_MADV_WILLNEED) {
7462306a36Sopenharmony_ci		drm_gem_lru_move_tail_locked(&priv->lru.willneed, obj);
7562306a36Sopenharmony_ci	} else {
7662306a36Sopenharmony_ci		GEM_WARN_ON(msm_obj->madv != MSM_MADV_DONTNEED);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		drm_gem_lru_move_tail_locked(&priv->lru.dontneed, obj);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic void update_lru_locked(struct drm_gem_object *obj)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
8562306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	msm_gem_assert_locked(&msm_obj->base);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (!msm_obj->pages) {
9062306a36Sopenharmony_ci		GEM_WARN_ON(msm_obj->pin_count);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		drm_gem_lru_move_tail_locked(&priv->lru.unbacked, obj);
9362306a36Sopenharmony_ci	} else {
9462306a36Sopenharmony_ci		update_lru_active(obj);
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void update_lru(struct drm_gem_object *obj)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	mutex_lock(&priv->lru.lock);
10362306a36Sopenharmony_ci	update_lru_locked(obj);
10462306a36Sopenharmony_ci	mutex_unlock(&priv->lru.lock);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/* allocate pages from VRAM carveout, used when no IOMMU: */
10862306a36Sopenharmony_cistatic struct page **get_pages_vram(struct drm_gem_object *obj, int npages)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
11162306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
11262306a36Sopenharmony_ci	dma_addr_t paddr;
11362306a36Sopenharmony_ci	struct page **p;
11462306a36Sopenharmony_ci	int ret, i;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	p = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
11762306a36Sopenharmony_ci	if (!p)
11862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	spin_lock(&priv->vram.lock);
12162306a36Sopenharmony_ci	ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node, npages);
12262306a36Sopenharmony_ci	spin_unlock(&priv->vram.lock);
12362306a36Sopenharmony_ci	if (ret) {
12462306a36Sopenharmony_ci		kvfree(p);
12562306a36Sopenharmony_ci		return ERR_PTR(ret);
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	paddr = physaddr(obj);
12962306a36Sopenharmony_ci	for (i = 0; i < npages; i++) {
13062306a36Sopenharmony_ci		p[i] = pfn_to_page(__phys_to_pfn(paddr));
13162306a36Sopenharmony_ci		paddr += PAGE_SIZE;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return p;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic struct page **get_pages(struct drm_gem_object *obj)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (!msm_obj->pages) {
14462306a36Sopenharmony_ci		struct drm_device *dev = obj->dev;
14562306a36Sopenharmony_ci		struct page **p;
14662306a36Sopenharmony_ci		int npages = obj->size >> PAGE_SHIFT;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		if (use_pages(obj))
14962306a36Sopenharmony_ci			p = drm_gem_get_pages(obj);
15062306a36Sopenharmony_ci		else
15162306a36Sopenharmony_ci			p = get_pages_vram(obj, npages);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		if (IS_ERR(p)) {
15462306a36Sopenharmony_ci			DRM_DEV_ERROR(dev->dev, "could not get pages: %ld\n",
15562306a36Sopenharmony_ci					PTR_ERR(p));
15662306a36Sopenharmony_ci			return p;
15762306a36Sopenharmony_ci		}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		msm_obj->pages = p;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		msm_obj->sgt = drm_prime_pages_to_sg(obj->dev, p, npages);
16262306a36Sopenharmony_ci		if (IS_ERR(msm_obj->sgt)) {
16362306a36Sopenharmony_ci			void *ptr = ERR_CAST(msm_obj->sgt);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci			DRM_DEV_ERROR(dev->dev, "failed to allocate sgt\n");
16662306a36Sopenharmony_ci			msm_obj->sgt = NULL;
16762306a36Sopenharmony_ci			return ptr;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		/* For non-cached buffers, ensure the new pages are clean
17162306a36Sopenharmony_ci		 * because display controller, GPU, etc. are not coherent:
17262306a36Sopenharmony_ci		 */
17362306a36Sopenharmony_ci		if (msm_obj->flags & MSM_BO_WC)
17462306a36Sopenharmony_ci			sync_for_device(msm_obj);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		update_lru(obj);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return msm_obj->pages;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void put_pages_vram(struct drm_gem_object *obj)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
18562306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	spin_lock(&priv->vram.lock);
18862306a36Sopenharmony_ci	drm_mm_remove_node(msm_obj->vram_node);
18962306a36Sopenharmony_ci	spin_unlock(&priv->vram.lock);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	kvfree(msm_obj->pages);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void put_pages(struct drm_gem_object *obj)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (msm_obj->pages) {
19962306a36Sopenharmony_ci		if (msm_obj->sgt) {
20062306a36Sopenharmony_ci			/* For non-cached buffers, ensure the new
20162306a36Sopenharmony_ci			 * pages are clean because display controller,
20262306a36Sopenharmony_ci			 * GPU, etc. are not coherent:
20362306a36Sopenharmony_ci			 */
20462306a36Sopenharmony_ci			if (msm_obj->flags & MSM_BO_WC)
20562306a36Sopenharmony_ci				sync_for_cpu(msm_obj);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci			sg_free_table(msm_obj->sgt);
20862306a36Sopenharmony_ci			kfree(msm_obj->sgt);
20962306a36Sopenharmony_ci			msm_obj->sgt = NULL;
21062306a36Sopenharmony_ci		}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		if (use_pages(obj))
21362306a36Sopenharmony_ci			drm_gem_put_pages(obj, msm_obj->pages, true, false);
21462306a36Sopenharmony_ci		else
21562306a36Sopenharmony_ci			put_pages_vram(obj);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		msm_obj->pages = NULL;
21862306a36Sopenharmony_ci		update_lru(obj);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic struct page **msm_gem_pin_pages_locked(struct drm_gem_object *obj,
22362306a36Sopenharmony_ci					      unsigned madv)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (GEM_WARN_ON(msm_obj->madv > madv)) {
23062306a36Sopenharmony_ci		DRM_DEV_ERROR(obj->dev->dev, "Invalid madv state: %u vs %u\n",
23162306a36Sopenharmony_ci			msm_obj->madv, madv);
23262306a36Sopenharmony_ci		return ERR_PTR(-EBUSY);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return get_pages(obj);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/*
23962306a36Sopenharmony_ci * Update the pin count of the object, call under lru.lock
24062306a36Sopenharmony_ci */
24162306a36Sopenharmony_civoid msm_gem_pin_obj_locked(struct drm_gem_object *obj)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	to_msm_bo(obj)->pin_count++;
24862306a36Sopenharmony_ci	drm_gem_lru_move_tail_locked(&priv->lru.pinned, obj);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void pin_obj_locked(struct drm_gem_object *obj)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	mutex_lock(&priv->lru.lock);
25662306a36Sopenharmony_ci	msm_gem_pin_obj_locked(obj);
25762306a36Sopenharmony_ci	mutex_unlock(&priv->lru.lock);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistruct page **msm_gem_pin_pages(struct drm_gem_object *obj)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct page **p;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	msm_gem_lock(obj);
26562306a36Sopenharmony_ci	p = msm_gem_pin_pages_locked(obj, MSM_MADV_WILLNEED);
26662306a36Sopenharmony_ci	if (!IS_ERR(p))
26762306a36Sopenharmony_ci		pin_obj_locked(obj);
26862306a36Sopenharmony_ci	msm_gem_unlock(obj);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return p;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_civoid msm_gem_unpin_pages(struct drm_gem_object *obj)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	msm_gem_lock(obj);
27662306a36Sopenharmony_ci	msm_gem_unpin_locked(obj);
27762306a36Sopenharmony_ci	msm_gem_unlock(obj);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic pgprot_t msm_gem_pgprot(struct msm_gem_object *msm_obj, pgprot_t prot)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	if (msm_obj->flags & MSM_BO_WC)
28362306a36Sopenharmony_ci		return pgprot_writecombine(prot);
28462306a36Sopenharmony_ci	return prot;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic vm_fault_t msm_gem_fault(struct vm_fault *vmf)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct vm_area_struct *vma = vmf->vma;
29062306a36Sopenharmony_ci	struct drm_gem_object *obj = vma->vm_private_data;
29162306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
29262306a36Sopenharmony_ci	struct page **pages;
29362306a36Sopenharmony_ci	unsigned long pfn;
29462306a36Sopenharmony_ci	pgoff_t pgoff;
29562306a36Sopenharmony_ci	int err;
29662306a36Sopenharmony_ci	vm_fault_t ret;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/*
29962306a36Sopenharmony_ci	 * vm_ops.open/drm_gem_mmap_obj and close get and put
30062306a36Sopenharmony_ci	 * a reference on obj. So, we dont need to hold one here.
30162306a36Sopenharmony_ci	 */
30262306a36Sopenharmony_ci	err = msm_gem_lock_interruptible(obj);
30362306a36Sopenharmony_ci	if (err) {
30462306a36Sopenharmony_ci		ret = VM_FAULT_NOPAGE;
30562306a36Sopenharmony_ci		goto out;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	if (GEM_WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED)) {
30962306a36Sopenharmony_ci		msm_gem_unlock(obj);
31062306a36Sopenharmony_ci		return VM_FAULT_SIGBUS;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* make sure we have pages attached now */
31462306a36Sopenharmony_ci	pages = get_pages(obj);
31562306a36Sopenharmony_ci	if (IS_ERR(pages)) {
31662306a36Sopenharmony_ci		ret = vmf_error(PTR_ERR(pages));
31762306a36Sopenharmony_ci		goto out_unlock;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* We don't use vmf->pgoff since that has the fake offset: */
32162306a36Sopenharmony_ci	pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	pfn = page_to_pfn(pages[pgoff]);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	VERB("Inserting %p pfn %lx, pa %lx", (void *)vmf->address,
32662306a36Sopenharmony_ci			pfn, pfn << PAGE_SHIFT);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	ret = vmf_insert_pfn(vma, vmf->address, pfn);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ciout_unlock:
33162306a36Sopenharmony_ci	msm_gem_unlock(obj);
33262306a36Sopenharmony_ciout:
33362306a36Sopenharmony_ci	return ret;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/** get mmap offset */
33762306a36Sopenharmony_cistatic uint64_t mmap_offset(struct drm_gem_object *obj)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct drm_device *dev = obj->dev;
34062306a36Sopenharmony_ci	int ret;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* Make it mmapable */
34562306a36Sopenharmony_ci	ret = drm_gem_create_mmap_offset(obj);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (ret) {
34862306a36Sopenharmony_ci		DRM_DEV_ERROR(dev->dev, "could not allocate mmap offset\n");
34962306a36Sopenharmony_ci		return 0;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return drm_vma_node_offset_addr(&obj->vma_node);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ciuint64_t msm_gem_mmap_offset(struct drm_gem_object *obj)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	uint64_t offset;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	msm_gem_lock(obj);
36062306a36Sopenharmony_ci	offset = mmap_offset(obj);
36162306a36Sopenharmony_ci	msm_gem_unlock(obj);
36262306a36Sopenharmony_ci	return offset;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic struct msm_gem_vma *add_vma(struct drm_gem_object *obj,
36662306a36Sopenharmony_ci		struct msm_gem_address_space *aspace)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
36962306a36Sopenharmony_ci	struct msm_gem_vma *vma;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	vma = msm_gem_vma_new(aspace);
37462306a36Sopenharmony_ci	if (!vma)
37562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	list_add_tail(&vma->list, &msm_obj->vmas);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return vma;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic struct msm_gem_vma *lookup_vma(struct drm_gem_object *obj,
38362306a36Sopenharmony_ci		struct msm_gem_address_space *aspace)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
38662306a36Sopenharmony_ci	struct msm_gem_vma *vma;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	list_for_each_entry(vma, &msm_obj->vmas, list) {
39162306a36Sopenharmony_ci		if (vma->aspace == aspace)
39262306a36Sopenharmony_ci			return vma;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return NULL;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic void del_vma(struct msm_gem_vma *vma)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	if (!vma)
40162306a36Sopenharmony_ci		return;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	list_del(&vma->list);
40462306a36Sopenharmony_ci	kfree(vma);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci/*
40862306a36Sopenharmony_ci * If close is true, this also closes the VMA (releasing the allocated
40962306a36Sopenharmony_ci * iova range) in addition to removing the iommu mapping.  In the eviction
41062306a36Sopenharmony_ci * case (!close), we keep the iova allocated, but only remove the iommu
41162306a36Sopenharmony_ci * mapping.
41262306a36Sopenharmony_ci */
41362306a36Sopenharmony_cistatic void
41462306a36Sopenharmony_ciput_iova_spaces(struct drm_gem_object *obj, bool close)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
41762306a36Sopenharmony_ci	struct msm_gem_vma *vma;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	list_for_each_entry(vma, &msm_obj->vmas, list) {
42262306a36Sopenharmony_ci		if (vma->aspace) {
42362306a36Sopenharmony_ci			msm_gem_vma_purge(vma);
42462306a36Sopenharmony_ci			if (close)
42562306a36Sopenharmony_ci				msm_gem_vma_close(vma);
42662306a36Sopenharmony_ci		}
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci/* Called with msm_obj locked */
43162306a36Sopenharmony_cistatic void
43262306a36Sopenharmony_ciput_iova_vmas(struct drm_gem_object *obj)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
43562306a36Sopenharmony_ci	struct msm_gem_vma *vma, *tmp;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	list_for_each_entry_safe(vma, tmp, &msm_obj->vmas, list) {
44062306a36Sopenharmony_ci		del_vma(vma);
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic struct msm_gem_vma *get_vma_locked(struct drm_gem_object *obj,
44562306a36Sopenharmony_ci		struct msm_gem_address_space *aspace,
44662306a36Sopenharmony_ci		u64 range_start, u64 range_end)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct msm_gem_vma *vma;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	vma = lookup_vma(obj, aspace);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (!vma) {
45562306a36Sopenharmony_ci		int ret;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		vma = add_vma(obj, aspace);
45862306a36Sopenharmony_ci		if (IS_ERR(vma))
45962306a36Sopenharmony_ci			return vma;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		ret = msm_gem_vma_init(vma, obj->size,
46262306a36Sopenharmony_ci			range_start, range_end);
46362306a36Sopenharmony_ci		if (ret) {
46462306a36Sopenharmony_ci			del_vma(vma);
46562306a36Sopenharmony_ci			return ERR_PTR(ret);
46662306a36Sopenharmony_ci		}
46762306a36Sopenharmony_ci	} else {
46862306a36Sopenharmony_ci		GEM_WARN_ON(vma->iova < range_start);
46962306a36Sopenharmony_ci		GEM_WARN_ON((vma->iova + obj->size) > range_end);
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	return vma;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ciint msm_gem_pin_vma_locked(struct drm_gem_object *obj, struct msm_gem_vma *vma)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
47862306a36Sopenharmony_ci	struct page **pages;
47962306a36Sopenharmony_ci	int prot = IOMMU_READ;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (!(msm_obj->flags & MSM_BO_GPU_READONLY))
48262306a36Sopenharmony_ci		prot |= IOMMU_WRITE;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (msm_obj->flags & MSM_BO_MAP_PRIV)
48562306a36Sopenharmony_ci		prot |= IOMMU_PRIV;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (msm_obj->flags & MSM_BO_CACHED_COHERENT)
48862306a36Sopenharmony_ci		prot |= IOMMU_CACHE;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	pages = msm_gem_pin_pages_locked(obj, MSM_MADV_WILLNEED);
49362306a36Sopenharmony_ci	if (IS_ERR(pages))
49462306a36Sopenharmony_ci		return PTR_ERR(pages);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return msm_gem_vma_map(vma, prot, msm_obj->sgt, obj->size);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_civoid msm_gem_unpin_locked(struct drm_gem_object *obj)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
50262306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	mutex_lock(&priv->lru.lock);
50762306a36Sopenharmony_ci	msm_obj->pin_count--;
50862306a36Sopenharmony_ci	GEM_WARN_ON(msm_obj->pin_count < 0);
50962306a36Sopenharmony_ci	update_lru_locked(obj);
51062306a36Sopenharmony_ci	mutex_unlock(&priv->lru.lock);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci/* Special unpin path for use in fence-signaling path, avoiding the need
51462306a36Sopenharmony_ci * to hold the obj lock by only depending on things that a protected by
51562306a36Sopenharmony_ci * the LRU lock.  In particular we know that that we already have backing
51662306a36Sopenharmony_ci * and and that the object's dma_resv has the fence for the current
51762306a36Sopenharmony_ci * submit/job which will prevent us racing against page eviction.
51862306a36Sopenharmony_ci */
51962306a36Sopenharmony_civoid msm_gem_unpin_active(struct drm_gem_object *obj)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	msm_obj->pin_count--;
52462306a36Sopenharmony_ci	GEM_WARN_ON(msm_obj->pin_count < 0);
52562306a36Sopenharmony_ci	update_lru_active(obj);
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistruct msm_gem_vma *msm_gem_get_vma_locked(struct drm_gem_object *obj,
52962306a36Sopenharmony_ci					   struct msm_gem_address_space *aspace)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	return get_vma_locked(obj, aspace, 0, U64_MAX);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic int get_and_pin_iova_range_locked(struct drm_gem_object *obj,
53562306a36Sopenharmony_ci		struct msm_gem_address_space *aspace, uint64_t *iova,
53662306a36Sopenharmony_ci		u64 range_start, u64 range_end)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct msm_gem_vma *vma;
53962306a36Sopenharmony_ci	int ret;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	vma = get_vma_locked(obj, aspace, range_start, range_end);
54462306a36Sopenharmony_ci	if (IS_ERR(vma))
54562306a36Sopenharmony_ci		return PTR_ERR(vma);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	ret = msm_gem_pin_vma_locked(obj, vma);
54862306a36Sopenharmony_ci	if (!ret) {
54962306a36Sopenharmony_ci		*iova = vma->iova;
55062306a36Sopenharmony_ci		pin_obj_locked(obj);
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	return ret;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci/*
55762306a36Sopenharmony_ci * get iova and pin it. Should have a matching put
55862306a36Sopenharmony_ci * limits iova to specified range (in pages)
55962306a36Sopenharmony_ci */
56062306a36Sopenharmony_ciint msm_gem_get_and_pin_iova_range(struct drm_gem_object *obj,
56162306a36Sopenharmony_ci		struct msm_gem_address_space *aspace, uint64_t *iova,
56262306a36Sopenharmony_ci		u64 range_start, u64 range_end)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	int ret;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	msm_gem_lock(obj);
56762306a36Sopenharmony_ci	ret = get_and_pin_iova_range_locked(obj, aspace, iova, range_start, range_end);
56862306a36Sopenharmony_ci	msm_gem_unlock(obj);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return ret;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/* get iova and pin it. Should have a matching put */
57462306a36Sopenharmony_ciint msm_gem_get_and_pin_iova(struct drm_gem_object *obj,
57562306a36Sopenharmony_ci		struct msm_gem_address_space *aspace, uint64_t *iova)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	return msm_gem_get_and_pin_iova_range(obj, aspace, iova, 0, U64_MAX);
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci/*
58162306a36Sopenharmony_ci * Get an iova but don't pin it. Doesn't need a put because iovas are currently
58262306a36Sopenharmony_ci * valid for the life of the object
58362306a36Sopenharmony_ci */
58462306a36Sopenharmony_ciint msm_gem_get_iova(struct drm_gem_object *obj,
58562306a36Sopenharmony_ci		struct msm_gem_address_space *aspace, uint64_t *iova)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	struct msm_gem_vma *vma;
58862306a36Sopenharmony_ci	int ret = 0;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	msm_gem_lock(obj);
59162306a36Sopenharmony_ci	vma = get_vma_locked(obj, aspace, 0, U64_MAX);
59262306a36Sopenharmony_ci	if (IS_ERR(vma)) {
59362306a36Sopenharmony_ci		ret = PTR_ERR(vma);
59462306a36Sopenharmony_ci	} else {
59562306a36Sopenharmony_ci		*iova = vma->iova;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci	msm_gem_unlock(obj);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	return ret;
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic int clear_iova(struct drm_gem_object *obj,
60362306a36Sopenharmony_ci		      struct msm_gem_address_space *aspace)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct msm_gem_vma *vma = lookup_vma(obj, aspace);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	if (!vma)
60862306a36Sopenharmony_ci		return 0;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	msm_gem_vma_purge(vma);
61162306a36Sopenharmony_ci	msm_gem_vma_close(vma);
61262306a36Sopenharmony_ci	del_vma(vma);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	return 0;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/*
61862306a36Sopenharmony_ci * Get the requested iova but don't pin it.  Fails if the requested iova is
61962306a36Sopenharmony_ci * not available.  Doesn't need a put because iovas are currently valid for
62062306a36Sopenharmony_ci * the life of the object.
62162306a36Sopenharmony_ci *
62262306a36Sopenharmony_ci * Setting an iova of zero will clear the vma.
62362306a36Sopenharmony_ci */
62462306a36Sopenharmony_ciint msm_gem_set_iova(struct drm_gem_object *obj,
62562306a36Sopenharmony_ci		     struct msm_gem_address_space *aspace, uint64_t iova)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	int ret = 0;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	msm_gem_lock(obj);
63062306a36Sopenharmony_ci	if (!iova) {
63162306a36Sopenharmony_ci		ret = clear_iova(obj, aspace);
63262306a36Sopenharmony_ci	} else {
63362306a36Sopenharmony_ci		struct msm_gem_vma *vma;
63462306a36Sopenharmony_ci		vma = get_vma_locked(obj, aspace, iova, iova + obj->size);
63562306a36Sopenharmony_ci		if (IS_ERR(vma)) {
63662306a36Sopenharmony_ci			ret = PTR_ERR(vma);
63762306a36Sopenharmony_ci		} else if (GEM_WARN_ON(vma->iova != iova)) {
63862306a36Sopenharmony_ci			clear_iova(obj, aspace);
63962306a36Sopenharmony_ci			ret = -EBUSY;
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci	msm_gem_unlock(obj);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	return ret;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci/*
64862306a36Sopenharmony_ci * Unpin a iova by updating the reference counts. The memory isn't actually
64962306a36Sopenharmony_ci * purged until something else (shrinker, mm_notifier, destroy, etc) decides
65062306a36Sopenharmony_ci * to get rid of it
65162306a36Sopenharmony_ci */
65262306a36Sopenharmony_civoid msm_gem_unpin_iova(struct drm_gem_object *obj,
65362306a36Sopenharmony_ci		struct msm_gem_address_space *aspace)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	struct msm_gem_vma *vma;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	msm_gem_lock(obj);
65862306a36Sopenharmony_ci	vma = lookup_vma(obj, aspace);
65962306a36Sopenharmony_ci	if (!GEM_WARN_ON(!vma)) {
66062306a36Sopenharmony_ci		msm_gem_unpin_locked(obj);
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci	msm_gem_unlock(obj);
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ciint msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
66662306a36Sopenharmony_ci		struct drm_mode_create_dumb *args)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	args->pitch = align_pitch(args->width, args->bpp);
66962306a36Sopenharmony_ci	args->size  = PAGE_ALIGN(args->pitch * args->height);
67062306a36Sopenharmony_ci	return msm_gem_new_handle(dev, file, args->size,
67162306a36Sopenharmony_ci			MSM_BO_SCANOUT | MSM_BO_WC, &args->handle, "dumb");
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ciint msm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
67562306a36Sopenharmony_ci		uint32_t handle, uint64_t *offset)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct drm_gem_object *obj;
67862306a36Sopenharmony_ci	int ret = 0;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/* GEM does all our handle to object mapping */
68162306a36Sopenharmony_ci	obj = drm_gem_object_lookup(file, handle);
68262306a36Sopenharmony_ci	if (obj == NULL) {
68362306a36Sopenharmony_ci		ret = -ENOENT;
68462306a36Sopenharmony_ci		goto fail;
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	*offset = msm_gem_mmap_offset(obj);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	drm_gem_object_put(obj);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cifail:
69262306a36Sopenharmony_ci	return ret;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic void *get_vaddr(struct drm_gem_object *obj, unsigned madv)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
69862306a36Sopenharmony_ci	struct page **pages;
69962306a36Sopenharmony_ci	int ret = 0;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (obj->import_attach)
70462306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	pages = msm_gem_pin_pages_locked(obj, madv);
70762306a36Sopenharmony_ci	if (IS_ERR(pages))
70862306a36Sopenharmony_ci		return ERR_CAST(pages);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	pin_obj_locked(obj);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* increment vmap_count *before* vmap() call, so shrinker can
71362306a36Sopenharmony_ci	 * check vmap_count (is_vunmapable()) outside of msm_obj lock.
71462306a36Sopenharmony_ci	 * This guarantees that we won't try to msm_gem_vunmap() this
71562306a36Sopenharmony_ci	 * same object from within the vmap() call (while we already
71662306a36Sopenharmony_ci	 * hold msm_obj lock)
71762306a36Sopenharmony_ci	 */
71862306a36Sopenharmony_ci	msm_obj->vmap_count++;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (!msm_obj->vaddr) {
72162306a36Sopenharmony_ci		msm_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT,
72262306a36Sopenharmony_ci				VM_MAP, msm_gem_pgprot(msm_obj, PAGE_KERNEL));
72362306a36Sopenharmony_ci		if (msm_obj->vaddr == NULL) {
72462306a36Sopenharmony_ci			ret = -ENOMEM;
72562306a36Sopenharmony_ci			goto fail;
72662306a36Sopenharmony_ci		}
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return msm_obj->vaddr;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cifail:
73262306a36Sopenharmony_ci	msm_obj->vmap_count--;
73362306a36Sopenharmony_ci	msm_gem_unpin_locked(obj);
73462306a36Sopenharmony_ci	return ERR_PTR(ret);
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_civoid *msm_gem_get_vaddr_locked(struct drm_gem_object *obj)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	return get_vaddr(obj, MSM_MADV_WILLNEED);
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_civoid *msm_gem_get_vaddr(struct drm_gem_object *obj)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	void *ret;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	msm_gem_lock(obj);
74762306a36Sopenharmony_ci	ret = msm_gem_get_vaddr_locked(obj);
74862306a36Sopenharmony_ci	msm_gem_unlock(obj);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	return ret;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci/*
75462306a36Sopenharmony_ci * Don't use this!  It is for the very special case of dumping
75562306a36Sopenharmony_ci * submits from GPU hangs or faults, were the bo may already
75662306a36Sopenharmony_ci * be MSM_MADV_DONTNEED, but we know the buffer is still on the
75762306a36Sopenharmony_ci * active list.
75862306a36Sopenharmony_ci */
75962306a36Sopenharmony_civoid *msm_gem_get_vaddr_active(struct drm_gem_object *obj)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	return get_vaddr(obj, __MSM_MADV_PURGED);
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_civoid msm_gem_put_vaddr_locked(struct drm_gem_object *obj)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
76962306a36Sopenharmony_ci	GEM_WARN_ON(msm_obj->vmap_count < 1);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	msm_obj->vmap_count--;
77262306a36Sopenharmony_ci	msm_gem_unpin_locked(obj);
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_civoid msm_gem_put_vaddr(struct drm_gem_object *obj)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	msm_gem_lock(obj);
77862306a36Sopenharmony_ci	msm_gem_put_vaddr_locked(obj);
77962306a36Sopenharmony_ci	msm_gem_unlock(obj);
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci/* Update madvise status, returns true if not purged, else
78362306a36Sopenharmony_ci * false or -errno.
78462306a36Sopenharmony_ci */
78562306a36Sopenharmony_ciint msm_gem_madvise(struct drm_gem_object *obj, unsigned madv)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
78862306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	msm_gem_lock(obj);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	mutex_lock(&priv->lru.lock);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (msm_obj->madv != __MSM_MADV_PURGED)
79562306a36Sopenharmony_ci		msm_obj->madv = madv;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	madv = msm_obj->madv;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* If the obj is inactive, we might need to move it
80062306a36Sopenharmony_ci	 * between inactive lists
80162306a36Sopenharmony_ci	 */
80262306a36Sopenharmony_ci	update_lru_locked(obj);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	mutex_unlock(&priv->lru.lock);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	msm_gem_unlock(obj);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return (madv != __MSM_MADV_PURGED);
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_civoid msm_gem_purge(struct drm_gem_object *obj)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct drm_device *dev = obj->dev;
81462306a36Sopenharmony_ci	struct msm_drm_private *priv = obj->dev->dev_private;
81562306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
81862306a36Sopenharmony_ci	GEM_WARN_ON(!is_purgeable(msm_obj));
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* Get rid of any iommu mapping(s): */
82162306a36Sopenharmony_ci	put_iova_spaces(obj, true);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	msm_gem_vunmap(obj);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	put_pages(obj);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	put_iova_vmas(obj);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	mutex_lock(&priv->lru.lock);
83262306a36Sopenharmony_ci	/* A one-way transition: */
83362306a36Sopenharmony_ci	msm_obj->madv = __MSM_MADV_PURGED;
83462306a36Sopenharmony_ci	mutex_unlock(&priv->lru.lock);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	drm_gem_free_mmap_offset(obj);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/* Our goal here is to return as much of the memory as
83962306a36Sopenharmony_ci	 * is possible back to the system as we are called from OOM.
84062306a36Sopenharmony_ci	 * To do this we must instruct the shmfs to drop all of its
84162306a36Sopenharmony_ci	 * backing pages, *now*.
84262306a36Sopenharmony_ci	 */
84362306a36Sopenharmony_ci	shmem_truncate_range(file_inode(obj->filp), 0, (loff_t)-1);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	invalidate_mapping_pages(file_inode(obj->filp)->i_mapping,
84662306a36Sopenharmony_ci			0, (loff_t)-1);
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci/*
85062306a36Sopenharmony_ci * Unpin the backing pages and make them available to be swapped out.
85162306a36Sopenharmony_ci */
85262306a36Sopenharmony_civoid msm_gem_evict(struct drm_gem_object *obj)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	struct drm_device *dev = obj->dev;
85562306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
85862306a36Sopenharmony_ci	GEM_WARN_ON(is_unevictable(msm_obj));
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	/* Get rid of any iommu mapping(s): */
86162306a36Sopenharmony_ci	put_iova_spaces(obj, false);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	put_pages(obj);
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_civoid msm_gem_vunmap(struct drm_gem_object *obj)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (!msm_obj->vaddr || GEM_WARN_ON(!is_vunmapable(msm_obj)))
87562306a36Sopenharmony_ci		return;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	vunmap(msm_obj->vaddr);
87862306a36Sopenharmony_ci	msm_obj->vaddr = NULL;
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cibool msm_gem_active(struct drm_gem_object *obj)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	msm_gem_assert_locked(obj);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if (to_msm_bo(obj)->pin_count)
88662306a36Sopenharmony_ci		return true;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	return !dma_resv_test_signaled(obj->resv, dma_resv_usage_rw(true));
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ciint msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	bool write = !!(op & MSM_PREP_WRITE);
89462306a36Sopenharmony_ci	unsigned long remain =
89562306a36Sopenharmony_ci		op & MSM_PREP_NOSYNC ? 0 : timeout_to_jiffies(timeout);
89662306a36Sopenharmony_ci	long ret;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (op & MSM_PREP_BOOST) {
89962306a36Sopenharmony_ci		dma_resv_set_deadline(obj->resv, dma_resv_usage_rw(write),
90062306a36Sopenharmony_ci				      ktime_get());
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	ret = dma_resv_wait_timeout(obj->resv, dma_resv_usage_rw(write),
90462306a36Sopenharmony_ci				    true,  remain);
90562306a36Sopenharmony_ci	if (ret == 0)
90662306a36Sopenharmony_ci		return remain == 0 ? -EBUSY : -ETIMEDOUT;
90762306a36Sopenharmony_ci	else if (ret < 0)
90862306a36Sopenharmony_ci		return ret;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	/* TODO cache maintenance */
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	return 0;
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ciint msm_gem_cpu_fini(struct drm_gem_object *obj)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	/* TODO cache maintenance */
91862306a36Sopenharmony_ci	return 0;
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
92262306a36Sopenharmony_civoid msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m,
92362306a36Sopenharmony_ci		struct msm_gem_stats *stats)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
92662306a36Sopenharmony_ci	struct dma_resv *robj = obj->resv;
92762306a36Sopenharmony_ci	struct msm_gem_vma *vma;
92862306a36Sopenharmony_ci	uint64_t off = drm_vma_node_start(&obj->vma_node);
92962306a36Sopenharmony_ci	const char *madv;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	msm_gem_lock(obj);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	stats->all.count++;
93462306a36Sopenharmony_ci	stats->all.size += obj->size;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (msm_gem_active(obj)) {
93762306a36Sopenharmony_ci		stats->active.count++;
93862306a36Sopenharmony_ci		stats->active.size += obj->size;
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (msm_obj->pages) {
94262306a36Sopenharmony_ci		stats->resident.count++;
94362306a36Sopenharmony_ci		stats->resident.size += obj->size;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	switch (msm_obj->madv) {
94762306a36Sopenharmony_ci	case __MSM_MADV_PURGED:
94862306a36Sopenharmony_ci		stats->purged.count++;
94962306a36Sopenharmony_ci		stats->purged.size += obj->size;
95062306a36Sopenharmony_ci		madv = " purged";
95162306a36Sopenharmony_ci		break;
95262306a36Sopenharmony_ci	case MSM_MADV_DONTNEED:
95362306a36Sopenharmony_ci		stats->purgeable.count++;
95462306a36Sopenharmony_ci		stats->purgeable.size += obj->size;
95562306a36Sopenharmony_ci		madv = " purgeable";
95662306a36Sopenharmony_ci		break;
95762306a36Sopenharmony_ci	case MSM_MADV_WILLNEED:
95862306a36Sopenharmony_ci	default:
95962306a36Sopenharmony_ci		madv = "";
96062306a36Sopenharmony_ci		break;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	seq_printf(m, "%08x: %c %2d (%2d) %08llx %p",
96462306a36Sopenharmony_ci			msm_obj->flags, msm_gem_active(obj) ? 'A' : 'I',
96562306a36Sopenharmony_ci			obj->name, kref_read(&obj->refcount),
96662306a36Sopenharmony_ci			off, msm_obj->vaddr);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	seq_printf(m, " %08zu %9s %-32s\n", obj->size, madv, msm_obj->name);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	if (!list_empty(&msm_obj->vmas)) {
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci		seq_puts(m, "      vmas:");
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		list_for_each_entry(vma, &msm_obj->vmas, list) {
97562306a36Sopenharmony_ci			const char *name, *comm;
97662306a36Sopenharmony_ci			if (vma->aspace) {
97762306a36Sopenharmony_ci				struct msm_gem_address_space *aspace = vma->aspace;
97862306a36Sopenharmony_ci				struct task_struct *task =
97962306a36Sopenharmony_ci					get_pid_task(aspace->pid, PIDTYPE_PID);
98062306a36Sopenharmony_ci				if (task) {
98162306a36Sopenharmony_ci					comm = kstrdup(task->comm, GFP_KERNEL);
98262306a36Sopenharmony_ci					put_task_struct(task);
98362306a36Sopenharmony_ci				} else {
98462306a36Sopenharmony_ci					comm = NULL;
98562306a36Sopenharmony_ci				}
98662306a36Sopenharmony_ci				name = aspace->name;
98762306a36Sopenharmony_ci			} else {
98862306a36Sopenharmony_ci				name = comm = NULL;
98962306a36Sopenharmony_ci			}
99062306a36Sopenharmony_ci			seq_printf(m, " [%s%s%s: aspace=%p, %08llx,%s]",
99162306a36Sopenharmony_ci				name, comm ? ":" : "", comm ? comm : "",
99262306a36Sopenharmony_ci				vma->aspace, vma->iova,
99362306a36Sopenharmony_ci				vma->mapped ? "mapped" : "unmapped");
99462306a36Sopenharmony_ci			kfree(comm);
99562306a36Sopenharmony_ci		}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci		seq_puts(m, "\n");
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	dma_resv_describe(robj, m);
100162306a36Sopenharmony_ci	msm_gem_unlock(obj);
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_civoid msm_gem_describe_objects(struct list_head *list, struct seq_file *m)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct msm_gem_stats stats = {};
100762306a36Sopenharmony_ci	struct msm_gem_object *msm_obj;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	seq_puts(m, "   flags       id ref  offset   kaddr            size     madv      name\n");
101062306a36Sopenharmony_ci	list_for_each_entry(msm_obj, list, node) {
101162306a36Sopenharmony_ci		struct drm_gem_object *obj = &msm_obj->base;
101262306a36Sopenharmony_ci		seq_puts(m, "   ");
101362306a36Sopenharmony_ci		msm_gem_describe(obj, m, &stats);
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	seq_printf(m, "Total:     %4d objects, %9zu bytes\n",
101762306a36Sopenharmony_ci			stats.all.count, stats.all.size);
101862306a36Sopenharmony_ci	seq_printf(m, "Active:    %4d objects, %9zu bytes\n",
101962306a36Sopenharmony_ci			stats.active.count, stats.active.size);
102062306a36Sopenharmony_ci	seq_printf(m, "Resident:  %4d objects, %9zu bytes\n",
102162306a36Sopenharmony_ci			stats.resident.count, stats.resident.size);
102262306a36Sopenharmony_ci	seq_printf(m, "Purgeable: %4d objects, %9zu bytes\n",
102362306a36Sopenharmony_ci			stats.purgeable.count, stats.purgeable.size);
102462306a36Sopenharmony_ci	seq_printf(m, "Purged:    %4d objects, %9zu bytes\n",
102562306a36Sopenharmony_ci			stats.purged.count, stats.purged.size);
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci#endif
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci/* don't call directly!  Use drm_gem_object_put() */
103062306a36Sopenharmony_cistatic void msm_gem_free_object(struct drm_gem_object *obj)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
103362306a36Sopenharmony_ci	struct drm_device *dev = obj->dev;
103462306a36Sopenharmony_ci	struct msm_drm_private *priv = dev->dev_private;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	mutex_lock(&priv->obj_lock);
103762306a36Sopenharmony_ci	list_del(&msm_obj->node);
103862306a36Sopenharmony_ci	mutex_unlock(&priv->obj_lock);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	put_iova_spaces(obj, true);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	if (obj->import_attach) {
104362306a36Sopenharmony_ci		GEM_WARN_ON(msm_obj->vaddr);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		/* Don't drop the pages for imported dmabuf, as they are not
104662306a36Sopenharmony_ci		 * ours, just free the array we allocated:
104762306a36Sopenharmony_ci		 */
104862306a36Sopenharmony_ci		kvfree(msm_obj->pages);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		put_iova_vmas(obj);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		drm_prime_gem_destroy(obj, msm_obj->sgt);
105362306a36Sopenharmony_ci	} else {
105462306a36Sopenharmony_ci		msm_gem_vunmap(obj);
105562306a36Sopenharmony_ci		put_pages(obj);
105662306a36Sopenharmony_ci		put_iova_vmas(obj);
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	drm_gem_object_release(obj);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	kfree(msm_obj);
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic int msm_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
106962306a36Sopenharmony_ci	vma->vm_page_prot = msm_gem_pgprot(msm_obj, vm_get_page_prot(vma->vm_flags));
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	return 0;
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci/* convenience method to construct a GEM buffer object, and userspace handle */
107562306a36Sopenharmony_ciint msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
107662306a36Sopenharmony_ci		uint32_t size, uint32_t flags, uint32_t *handle,
107762306a36Sopenharmony_ci		char *name)
107862306a36Sopenharmony_ci{
107962306a36Sopenharmony_ci	struct drm_gem_object *obj;
108062306a36Sopenharmony_ci	int ret;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	obj = msm_gem_new(dev, size, flags);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	if (IS_ERR(obj))
108562306a36Sopenharmony_ci		return PTR_ERR(obj);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	if (name)
108862306a36Sopenharmony_ci		msm_gem_object_set_name(obj, "%s", name);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	ret = drm_gem_handle_create(file, obj, handle);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/* drop reference from allocate - handle holds it now */
109362306a36Sopenharmony_ci	drm_gem_object_put(obj);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	return ret;
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic enum drm_gem_object_status msm_gem_status(struct drm_gem_object *obj)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(obj);
110162306a36Sopenharmony_ci	enum drm_gem_object_status status = 0;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	if (msm_obj->pages)
110462306a36Sopenharmony_ci		status |= DRM_GEM_OBJECT_RESIDENT;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (msm_obj->madv == MSM_MADV_DONTNEED)
110762306a36Sopenharmony_ci		status |= DRM_GEM_OBJECT_PURGEABLE;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return status;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic const struct vm_operations_struct vm_ops = {
111362306a36Sopenharmony_ci	.fault = msm_gem_fault,
111462306a36Sopenharmony_ci	.open = drm_gem_vm_open,
111562306a36Sopenharmony_ci	.close = drm_gem_vm_close,
111662306a36Sopenharmony_ci};
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cistatic const struct drm_gem_object_funcs msm_gem_object_funcs = {
111962306a36Sopenharmony_ci	.free = msm_gem_free_object,
112062306a36Sopenharmony_ci	.pin = msm_gem_prime_pin,
112162306a36Sopenharmony_ci	.unpin = msm_gem_prime_unpin,
112262306a36Sopenharmony_ci	.get_sg_table = msm_gem_prime_get_sg_table,
112362306a36Sopenharmony_ci	.vmap = msm_gem_prime_vmap,
112462306a36Sopenharmony_ci	.vunmap = msm_gem_prime_vunmap,
112562306a36Sopenharmony_ci	.mmap = msm_gem_object_mmap,
112662306a36Sopenharmony_ci	.status = msm_gem_status,
112762306a36Sopenharmony_ci	.vm_ops = &vm_ops,
112862306a36Sopenharmony_ci};
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic int msm_gem_new_impl(struct drm_device *dev,
113162306a36Sopenharmony_ci		uint32_t size, uint32_t flags,
113262306a36Sopenharmony_ci		struct drm_gem_object **obj)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	struct msm_drm_private *priv = dev->dev_private;
113562306a36Sopenharmony_ci	struct msm_gem_object *msm_obj;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	switch (flags & MSM_BO_CACHE_MASK) {
113862306a36Sopenharmony_ci	case MSM_BO_CACHED:
113962306a36Sopenharmony_ci	case MSM_BO_WC:
114062306a36Sopenharmony_ci		break;
114162306a36Sopenharmony_ci	case MSM_BO_CACHED_COHERENT:
114262306a36Sopenharmony_ci		if (priv->has_cached_coherent)
114362306a36Sopenharmony_ci			break;
114462306a36Sopenharmony_ci		fallthrough;
114562306a36Sopenharmony_ci	default:
114662306a36Sopenharmony_ci		DRM_DEV_DEBUG(dev->dev, "invalid cache flag: %x\n",
114762306a36Sopenharmony_ci				(flags & MSM_BO_CACHE_MASK));
114862306a36Sopenharmony_ci		return -EINVAL;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL);
115262306a36Sopenharmony_ci	if (!msm_obj)
115362306a36Sopenharmony_ci		return -ENOMEM;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	msm_obj->flags = flags;
115662306a36Sopenharmony_ci	msm_obj->madv = MSM_MADV_WILLNEED;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	INIT_LIST_HEAD(&msm_obj->node);
115962306a36Sopenharmony_ci	INIT_LIST_HEAD(&msm_obj->vmas);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	*obj = &msm_obj->base;
116262306a36Sopenharmony_ci	(*obj)->funcs = &msm_gem_object_funcs;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	return 0;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_cistruct drm_gem_object *msm_gem_new(struct drm_device *dev, uint32_t size, uint32_t flags)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	struct msm_drm_private *priv = dev->dev_private;
117062306a36Sopenharmony_ci	struct msm_gem_object *msm_obj;
117162306a36Sopenharmony_ci	struct drm_gem_object *obj = NULL;
117262306a36Sopenharmony_ci	bool use_vram = false;
117362306a36Sopenharmony_ci	int ret;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (!msm_use_mmu(dev))
117862306a36Sopenharmony_ci		use_vram = true;
117962306a36Sopenharmony_ci	else if ((flags & (MSM_BO_STOLEN | MSM_BO_SCANOUT)) && priv->vram.size)
118062306a36Sopenharmony_ci		use_vram = true;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	if (GEM_WARN_ON(use_vram && !priv->vram.size))
118362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	/* Disallow zero sized objects as they make the underlying
118662306a36Sopenharmony_ci	 * infrastructure grumpy
118762306a36Sopenharmony_ci	 */
118862306a36Sopenharmony_ci	if (size == 0)
118962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	ret = msm_gem_new_impl(dev, size, flags, &obj);
119262306a36Sopenharmony_ci	if (ret)
119362306a36Sopenharmony_ci		return ERR_PTR(ret);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	msm_obj = to_msm_bo(obj);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (use_vram) {
119862306a36Sopenharmony_ci		struct msm_gem_vma *vma;
119962306a36Sopenharmony_ci		struct page **pages;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		drm_gem_private_object_init(dev, obj, size);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci		msm_gem_lock(obj);
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		vma = add_vma(obj, NULL);
120662306a36Sopenharmony_ci		msm_gem_unlock(obj);
120762306a36Sopenharmony_ci		if (IS_ERR(vma)) {
120862306a36Sopenharmony_ci			ret = PTR_ERR(vma);
120962306a36Sopenharmony_ci			goto fail;
121062306a36Sopenharmony_ci		}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci		to_msm_bo(obj)->vram_node = &vma->node;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci		msm_gem_lock(obj);
121562306a36Sopenharmony_ci		pages = get_pages(obj);
121662306a36Sopenharmony_ci		msm_gem_unlock(obj);
121762306a36Sopenharmony_ci		if (IS_ERR(pages)) {
121862306a36Sopenharmony_ci			ret = PTR_ERR(pages);
121962306a36Sopenharmony_ci			goto fail;
122062306a36Sopenharmony_ci		}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		vma->iova = physaddr(obj);
122362306a36Sopenharmony_ci	} else {
122462306a36Sopenharmony_ci		ret = drm_gem_object_init(dev, obj, size);
122562306a36Sopenharmony_ci		if (ret)
122662306a36Sopenharmony_ci			goto fail;
122762306a36Sopenharmony_ci		/*
122862306a36Sopenharmony_ci		 * Our buffers are kept pinned, so allocating them from the
122962306a36Sopenharmony_ci		 * MOVABLE zone is a really bad idea, and conflicts with CMA.
123062306a36Sopenharmony_ci		 * See comments above new_inode() why this is required _and_
123162306a36Sopenharmony_ci		 * expected if you're going to pin these pages.
123262306a36Sopenharmony_ci		 */
123362306a36Sopenharmony_ci		mapping_set_gfp_mask(obj->filp->f_mapping, GFP_HIGHUSER);
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	drm_gem_lru_move_tail(&priv->lru.unbacked, obj);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	mutex_lock(&priv->obj_lock);
123962306a36Sopenharmony_ci	list_add_tail(&msm_obj->node, &priv->objects);
124062306a36Sopenharmony_ci	mutex_unlock(&priv->obj_lock);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	ret = drm_gem_create_mmap_offset(obj);
124362306a36Sopenharmony_ci	if (ret)
124462306a36Sopenharmony_ci		goto fail;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	return obj;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cifail:
124962306a36Sopenharmony_ci	drm_gem_object_put(obj);
125062306a36Sopenharmony_ci	return ERR_PTR(ret);
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistruct drm_gem_object *msm_gem_import(struct drm_device *dev,
125462306a36Sopenharmony_ci		struct dma_buf *dmabuf, struct sg_table *sgt)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct msm_drm_private *priv = dev->dev_private;
125762306a36Sopenharmony_ci	struct msm_gem_object *msm_obj;
125862306a36Sopenharmony_ci	struct drm_gem_object *obj;
125962306a36Sopenharmony_ci	uint32_t size;
126062306a36Sopenharmony_ci	int ret, npages;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/* if we don't have IOMMU, don't bother pretending we can import: */
126362306a36Sopenharmony_ci	if (!msm_use_mmu(dev)) {
126462306a36Sopenharmony_ci		DRM_DEV_ERROR(dev->dev, "cannot import without IOMMU\n");
126562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	size = PAGE_ALIGN(dmabuf->size);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj);
127162306a36Sopenharmony_ci	if (ret)
127262306a36Sopenharmony_ci		return ERR_PTR(ret);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	drm_gem_private_object_init(dev, obj, size);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	npages = size / PAGE_SIZE;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	msm_obj = to_msm_bo(obj);
127962306a36Sopenharmony_ci	msm_gem_lock(obj);
128062306a36Sopenharmony_ci	msm_obj->sgt = sgt;
128162306a36Sopenharmony_ci	msm_obj->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
128262306a36Sopenharmony_ci	if (!msm_obj->pages) {
128362306a36Sopenharmony_ci		msm_gem_unlock(obj);
128462306a36Sopenharmony_ci		ret = -ENOMEM;
128562306a36Sopenharmony_ci		goto fail;
128662306a36Sopenharmony_ci	}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	ret = drm_prime_sg_to_page_array(sgt, msm_obj->pages, npages);
128962306a36Sopenharmony_ci	if (ret) {
129062306a36Sopenharmony_ci		msm_gem_unlock(obj);
129162306a36Sopenharmony_ci		goto fail;
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	msm_gem_unlock(obj);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	drm_gem_lru_move_tail(&priv->lru.pinned, obj);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	mutex_lock(&priv->obj_lock);
129962306a36Sopenharmony_ci	list_add_tail(&msm_obj->node, &priv->objects);
130062306a36Sopenharmony_ci	mutex_unlock(&priv->obj_lock);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	ret = drm_gem_create_mmap_offset(obj);
130362306a36Sopenharmony_ci	if (ret)
130462306a36Sopenharmony_ci		goto fail;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	return obj;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_cifail:
130962306a36Sopenharmony_ci	drm_gem_object_put(obj);
131062306a36Sopenharmony_ci	return ERR_PTR(ret);
131162306a36Sopenharmony_ci}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_civoid *msm_gem_kernel_new(struct drm_device *dev, uint32_t size,
131462306a36Sopenharmony_ci		uint32_t flags, struct msm_gem_address_space *aspace,
131562306a36Sopenharmony_ci		struct drm_gem_object **bo, uint64_t *iova)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	void *vaddr;
131862306a36Sopenharmony_ci	struct drm_gem_object *obj = msm_gem_new(dev, size, flags);
131962306a36Sopenharmony_ci	int ret;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (IS_ERR(obj))
132262306a36Sopenharmony_ci		return ERR_CAST(obj);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	if (iova) {
132562306a36Sopenharmony_ci		ret = msm_gem_get_and_pin_iova(obj, aspace, iova);
132662306a36Sopenharmony_ci		if (ret)
132762306a36Sopenharmony_ci			goto err;
132862306a36Sopenharmony_ci	}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	vaddr = msm_gem_get_vaddr(obj);
133162306a36Sopenharmony_ci	if (IS_ERR(vaddr)) {
133262306a36Sopenharmony_ci		msm_gem_unpin_iova(obj, aspace);
133362306a36Sopenharmony_ci		ret = PTR_ERR(vaddr);
133462306a36Sopenharmony_ci		goto err;
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if (bo)
133862306a36Sopenharmony_ci		*bo = obj;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	return vaddr;
134162306a36Sopenharmony_cierr:
134262306a36Sopenharmony_ci	drm_gem_object_put(obj);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	return ERR_PTR(ret);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_civoid msm_gem_kernel_put(struct drm_gem_object *bo,
134962306a36Sopenharmony_ci		struct msm_gem_address_space *aspace)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(bo))
135262306a36Sopenharmony_ci		return;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	msm_gem_put_vaddr(bo);
135562306a36Sopenharmony_ci	msm_gem_unpin_iova(bo, aspace);
135662306a36Sopenharmony_ci	drm_gem_object_put(bo);
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_civoid msm_gem_object_set_name(struct drm_gem_object *bo, const char *fmt, ...)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	struct msm_gem_object *msm_obj = to_msm_bo(bo);
136262306a36Sopenharmony_ci	va_list ap;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	if (!fmt)
136562306a36Sopenharmony_ci		return;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	va_start(ap, fmt);
136862306a36Sopenharmony_ci	vsnprintf(msm_obj->name, sizeof(msm_obj->name), fmt, ap);
136962306a36Sopenharmony_ci	va_end(ap);
137062306a36Sopenharmony_ci}
1371