forked from Minki/linux
edf5ceac31
drm_mm_init() takes the start and length of the intended virtual memory address region but the msm code is passing the end of the region instead. That would work out if the region started at 0 but it doesn't so the top of the region sneaks above the 32 bit boundary which won't work because the driver doesn't support 64 bit addresses for the GPU yet. Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
117 lines
2.8 KiB
C
117 lines
2.8 KiB
C
/*
|
|
* Copyright (C) 2016 Red Hat
|
|
* Author: Rob Clark <robdclark@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
* the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "msm_drv.h"
|
|
#include "msm_gem.h"
|
|
#include "msm_mmu.h"
|
|
|
|
static void
|
|
msm_gem_address_space_destroy(struct kref *kref)
|
|
{
|
|
struct msm_gem_address_space *aspace = container_of(kref,
|
|
struct msm_gem_address_space, kref);
|
|
|
|
drm_mm_takedown(&aspace->mm);
|
|
if (aspace->mmu)
|
|
aspace->mmu->funcs->destroy(aspace->mmu);
|
|
kfree(aspace);
|
|
}
|
|
|
|
|
|
void msm_gem_address_space_put(struct msm_gem_address_space *aspace)
|
|
{
|
|
if (aspace)
|
|
kref_put(&aspace->kref, msm_gem_address_space_destroy);
|
|
}
|
|
|
|
void
|
|
msm_gem_unmap_vma(struct msm_gem_address_space *aspace,
|
|
struct msm_gem_vma *vma, struct sg_table *sgt)
|
|
{
|
|
if (!aspace || !vma->iova)
|
|
return;
|
|
|
|
if (aspace->mmu) {
|
|
unsigned size = vma->node.size << PAGE_SHIFT;
|
|
aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, size);
|
|
}
|
|
|
|
spin_lock(&aspace->lock);
|
|
drm_mm_remove_node(&vma->node);
|
|
spin_unlock(&aspace->lock);
|
|
|
|
vma->iova = 0;
|
|
|
|
msm_gem_address_space_put(aspace);
|
|
}
|
|
|
|
int
|
|
msm_gem_map_vma(struct msm_gem_address_space *aspace,
|
|
struct msm_gem_vma *vma, struct sg_table *sgt, int npages)
|
|
{
|
|
int ret;
|
|
|
|
spin_lock(&aspace->lock);
|
|
if (WARN_ON(drm_mm_node_allocated(&vma->node))) {
|
|
spin_unlock(&aspace->lock);
|
|
return 0;
|
|
}
|
|
|
|
ret = drm_mm_insert_node(&aspace->mm, &vma->node, npages);
|
|
spin_unlock(&aspace->lock);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
vma->iova = vma->node.start << PAGE_SHIFT;
|
|
|
|
if (aspace->mmu) {
|
|
unsigned size = npages << PAGE_SHIFT;
|
|
ret = aspace->mmu->funcs->map(aspace->mmu, vma->iova, sgt,
|
|
size, IOMMU_READ | IOMMU_WRITE);
|
|
}
|
|
|
|
/* Get a reference to the aspace to keep it around */
|
|
kref_get(&aspace->kref);
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct msm_gem_address_space *
|
|
msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain,
|
|
const char *name)
|
|
{
|
|
struct msm_gem_address_space *aspace;
|
|
u64 size = domain->geometry.aperture_end -
|
|
domain->geometry.aperture_start;
|
|
|
|
aspace = kzalloc(sizeof(*aspace), GFP_KERNEL);
|
|
if (!aspace)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
spin_lock_init(&aspace->lock);
|
|
aspace->name = name;
|
|
aspace->mmu = msm_iommu_new(dev, domain);
|
|
|
|
drm_mm_init(&aspace->mm, (domain->geometry.aperture_start >> PAGE_SHIFT),
|
|
size >> PAGE_SHIFT);
|
|
|
|
kref_init(&aspace->kref);
|
|
|
|
return aspace;
|
|
}
|