2016-09-28 19:58:32 -04:00
/*
* 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"
2017-03-07 10:02:52 -07:00
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 ) ;
}
2016-09-28 19:58:32 -04:00
void
msm_gem_unmap_vma ( struct msm_gem_address_space * aspace ,
struct msm_gem_vma * vma , struct sg_table * sgt )
{
2017-07-30 14:46:56 +02:00
if ( ! aspace | | ! vma - > iova )
2016-09-28 19:58:32 -04:00
return ;
if ( aspace - > mmu ) {
unsigned size = vma - > node . size < < PAGE_SHIFT ;
aspace - > mmu - > funcs - > unmap ( aspace - > mmu , vma - > iova , sgt , size ) ;
}
2017-06-13 16:52:54 -06:00
spin_lock ( & aspace - > lock ) ;
2016-09-28 19:58:32 -04:00
drm_mm_remove_node ( & vma - > node ) ;
2017-06-13 16:52:54 -06:00
spin_unlock ( & aspace - > lock ) ;
2016-09-28 19:58:32 -04:00
vma - > iova = 0 ;
2017-03-07 10:02:52 -07:00
msm_gem_address_space_put ( aspace ) ;
2016-09-28 19:58:32 -04:00
}
int
msm_gem_map_vma ( struct msm_gem_address_space * aspace ,
struct msm_gem_vma * vma , struct sg_table * sgt , int npages )
{
int ret ;
2017-06-13 16:52:54 -06:00
spin_lock ( & aspace - > lock ) ;
if ( WARN_ON ( drm_mm_node_allocated ( & vma - > node ) ) ) {
spin_unlock ( & aspace - > lock ) ;
2016-09-28 19:58:32 -04:00
return 0 ;
2017-06-13 16:52:54 -06:00
}
2016-09-28 19:58:32 -04:00
2017-02-02 21:04:38 +00:00
ret = drm_mm_insert_node ( & aspace - > mm , & vma - > node , npages ) ;
2017-06-13 16:52:54 -06:00
spin_unlock ( & aspace - > lock ) ;
2016-09-28 19:58:32 -04:00
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 ) ;
}
2017-03-07 10:02:52 -07:00
/* Get a reference to the aspace to keep it around */
kref_get ( & aspace - > kref ) ;
2016-09-28 19:58:32 -04:00
2017-03-07 10:02:52 -07:00
return ret ;
2016-09-28 19:58:32 -04:00
}
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 ;
aspace = kzalloc ( sizeof ( * aspace ) , GFP_KERNEL ) ;
if ( ! aspace )
return ERR_PTR ( - ENOMEM ) ;
2017-06-13 16:52:54 -06:00
spin_lock_init ( & aspace - > lock ) ;
2016-09-28 19:58:32 -04:00
aspace - > name = name ;
aspace - > mmu = msm_iommu_new ( dev , domain ) ;
drm_mm_init ( & aspace - > mm , ( domain - > geometry . aperture_start > > PAGE_SHIFT ) ,
( domain - > geometry . aperture_end > > PAGE_SHIFT ) - 1 ) ;
2017-03-07 10:02:52 -07:00
kref_init ( & aspace - > kref ) ;
2016-09-28 19:58:32 -04:00
return aspace ;
}