diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 953b744696bb..267a4db8e7f2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -528,6 +530,46 @@ err_unlock: drm_modeset_unlock_all(drm); } +static int rockchip_gem_pool_init(struct drm_device *drm) +{ + struct rockchip_drm_private *private = drm->dev_private; + struct device_node *np = drm->dev->of_node; + struct device_node *node; + phys_addr_t start, size; + struct resource res; + int ret; + + node = of_parse_phandle(np, "secure-memory-region", 0); + if (!node) + return -ENXIO; + + ret = of_address_to_resource(node, 0, &res); + if (ret) + return ret; + start = res.start; + size = resource_size(&res); + if (!size) + return -ENOMEM; + + private->secure_buffer_pool = gen_pool_create(PAGE_SHIFT, -1); + if (!private->secure_buffer_pool) + return -ENOMEM; + + gen_pool_add(private->secure_buffer_pool, start, size, -1); + + return 0; +} + +static void rockchip_gem_pool_destroy(struct drm_device *drm) +{ + struct rockchip_drm_private *private = drm->dev_private; + + if (!private->secure_buffer_pool) + return; + + gen_pool_destroy(private->secure_buffer_pool); +} + static int rockchip_drm_bind(struct device *dev) { struct drm_device *drm_dev; @@ -587,12 +629,15 @@ static int rockchip_drm_bind(struct device *dev) /* init kms poll for handling hpd */ drm_kms_helper_poll_init(drm_dev); + rockchip_gem_pool_init(drm_dev); + ret = drm_dev_register(drm_dev, 0); if (ret) goto err_kms_helper_poll_fini; return 0; err_kms_helper_poll_fini: + rockchip_gem_pool_destroy(drm_dev); drm_kms_helper_poll_fini(drm_dev); rockchip_drm_fbdev_fini(drm_dev); err_unbind_all: @@ -611,6 +656,7 @@ static void rockchip_drm_unbind(struct device *dev) drm_dev_unregister(drm_dev); rockchip_drm_fbdev_fini(drm_dev); + rockchip_gem_pool_destroy(drm_dev); drm_kms_helper_poll_fini(drm_dev); drm_atomic_helper_shutdown(drm_dev); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index aa974f03d3fc..d96649352b6a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -165,6 +165,7 @@ struct rockchip_drm_private { struct drm_fb_helper *fbdev_helper; struct drm_gem_object *fbdev_bo; struct iommu_domain *domain; + struct gen_pool *secure_buffer_pool; struct mutex mm_lock; struct drm_mm mm; struct list_head psr_list; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 7614916dc230..b0de69f5cdb1 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -13,6 +13,12 @@ #include #include +#include +#include +#include +#include +#include + #include "rockchip_drm_drv.h" #include "rockchip_drm_gem.h" @@ -287,6 +293,91 @@ static int rockchip_gem_alloc_dma(struct rockchip_gem_object *rk_obj, return 0; } +static inline void *drm_calloc_large(size_t nmemb, size_t size) +{ + if (size != 0 && nmemb > SIZE_MAX / size) + return NULL; + + if (size * nmemb <= PAGE_SIZE) + return kcalloc(nmemb, size, GFP_KERNEL); + + return __vmalloc(size * nmemb, + GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); +} + +static inline void drm_free_large(void *ptr) +{ + kvfree(ptr); +} + +static int rockchip_gem_alloc_secure(struct rockchip_gem_object *rk_obj) +{ + struct drm_gem_object *obj = &rk_obj->base; + struct drm_device *drm = obj->dev; + struct rockchip_drm_private *private = drm->dev_private; + unsigned long paddr; + struct sg_table *sgt; + int ret = 0, i; + + if (!private->secure_buffer_pool) { + DRM_ERROR("No secure buffer pool found\n"); + return -ENOMEM; + } + + paddr = gen_pool_alloc(private->secure_buffer_pool, rk_obj->base.size); + if (!paddr) { + DRM_ERROR("failed to allocate secure buffer\n"); + return -ENOMEM; + } + + rk_obj->dma_handle = paddr; + rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT; + + rk_obj->pages = drm_calloc_large(rk_obj->num_pages, + sizeof(*rk_obj->pages)); + if (!rk_obj->pages) { + DRM_ERROR("failed to allocate pages.\n"); + ret = -ENOMEM; + goto err_buf_free; + } + + i = 0; + while (i < rk_obj->num_pages) { + rk_obj->pages[i] = phys_to_page(paddr); + paddr += PAGE_SIZE; + i++; + } + sgt = drm_prime_pages_to_sg(obj->dev, rk_obj->pages, rk_obj->num_pages); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto err_free_pages; + } + + rk_obj->sgt = sgt; + + return 0; + +err_free_pages: + drm_free_large(rk_obj->pages); +err_buf_free: + gen_pool_free(private->secure_buffer_pool, paddr, rk_obj->base.size); + + return ret; +} + +static void rockchip_gem_free_secure(struct rockchip_gem_object *rk_obj) +{ + struct drm_gem_object *obj = &rk_obj->base; + struct drm_device *drm = obj->dev; + struct rockchip_drm_private *private = drm->dev_private; + + drm_free_large(rk_obj->pages); + sg_free_table(rk_obj->sgt); + kfree(rk_obj->sgt); + gen_pool_free(private->secure_buffer_pool, rk_obj->dma_handle, + rk_obj->base.size); +} + static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj, bool alloc_kmap) { @@ -294,10 +385,24 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj, struct drm_device *drm = obj->dev; struct rockchip_drm_private *private = drm->dev_private; - if (private->domain) - return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap); - else + if (!private->domain) + rk_obj->flags |= ROCKCHIP_BO_CONTIG; + + if (rk_obj->flags & ROCKCHIP_BO_SECURE) { + rk_obj->buf_type = ROCKCHIP_GEM_BUF_TYPE_SECURE; + rk_obj->flags |= ROCKCHIP_BO_CONTIG; + if (alloc_kmap) { + DRM_ERROR("Not allow alloc secure buffer with kmap\n"); + return -EINVAL; + } + return rockchip_gem_alloc_secure(rk_obj); + } else if (rk_obj->flags & ROCKCHIP_BO_CONTIG) { + rk_obj->buf_type = ROCKCHIP_GEM_BUF_TYPE_CMA; return rockchip_gem_alloc_dma(rk_obj, alloc_kmap); + } else { + rk_obj->buf_type = ROCKCHIP_GEM_BUF_TYPE_SHMEM; + return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap); + } } static void rockchip_gem_free_iommu(struct rockchip_gem_object *rk_obj) @@ -318,7 +423,9 @@ static void rockchip_gem_free_dma(struct rockchip_gem_object *rk_obj) static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj) { - if (rk_obj->pages) + if (rk_obj->buf_type == ROCKCHIP_GEM_BUF_TYPE_SECURE) + rockchip_gem_free_secure(rk_obj); + else if (rk_obj->pages) rockchip_gem_free_iommu(rk_obj); else rockchip_gem_free_dma(rk_obj); @@ -359,10 +466,14 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj, */ vma->vm_flags &= ~VM_PFNMAP; - if (rk_obj->pages) + if (rk_obj->buf_type == ROCKCHIP_GEM_BUF_TYPE_SECURE) { + DRM_ERROR("Disallow mmap for secure buffer\n"); + ret = -EINVAL; + } else if (rk_obj->pages) { ret = rockchip_drm_gem_object_mmap_iommu(obj, vma); - else + } else { ret = rockchip_drm_gem_object_mmap_dma(obj, vma); + } if (ret) drm_gem_vm_close(vma); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h index df12706031c6..7a1c591ffc74 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h @@ -11,12 +11,20 @@ #define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base) +enum rockchip_gem_buf_type { + ROCKCHIP_GEM_BUF_TYPE_CMA, + ROCKCHIP_GEM_BUF_TYPE_SHMEM, + ROCKCHIP_GEM_BUF_TYPE_SECURE, +}; + struct rockchip_gem_object { struct drm_gem_object base; unsigned int flags; + enum rockchip_gem_buf_type buf_type; void *kvaddr; - dma_addr_t dma_addr; + dma_addr_t dma_addr; /* iova if iommu enable, otherwise physical address */ + dma_addr_t dma_handle; /* physical address */ /* Used when IOMMU is disabled */ unsigned long dma_attrs;