The dedicated VRAM is seen as a kind of device memory (IOMEM) at present, we should use ioremap() to make device memory CPU accessible, we should also need to implement mmap() driver for userspace. Therefore, we add a simple drm-mm based range allocator for the upper (GEM object function) layer. Signed-off-by: Sui Jingfeng <sui.jingfeng@xxxxxxxxx> --- drivers/gpu/drm/etnaviv/Makefile | 1 + drivers/gpu/drm/etnaviv/etnaviv_drv.c | 12 + drivers/gpu/drm/etnaviv/etnaviv_drv.h | 11 + drivers/gpu/drm/etnaviv/etnaviv_gem.h | 5 + drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c | 258 +++++++++++++++++++++ drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h | 12 + drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c | 13 ++ drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h | 6 + 8 files changed, 318 insertions(+) create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index 383f181bfc4c..aba2578966ff 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -6,6 +6,7 @@ etnaviv-y := \ etnaviv_drv.o \ etnaviv_dump.o \ etnaviv_gem_prime.o \ + etnaviv_gem_vram.o \ etnaviv_gem_submit.o \ etnaviv_gem.o \ etnaviv_gpu.o \ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 7fc654f051a3..f10661fe079f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -23,6 +23,7 @@ #include "etnaviv_drv.h" #include "etnaviv_gpu.h" #include "etnaviv_gem.h" +#include "etnaviv_gem_vram.h" #include "etnaviv_mmu.h" #include "etnaviv_pci_drv.h" #include "etnaviv_perfmon.h" @@ -46,6 +47,8 @@ static struct device_node *etnaviv_of_first_available_node(void) static int etnaviv_private_init(struct device *dev, struct etnaviv_drm_private *priv) { + int ret; + xa_init_flags(&priv->active_contexts, XA_FLAGS_ALLOC); mutex_init(&priv->gem_lock); @@ -61,6 +64,10 @@ static int etnaviv_private_init(struct device *dev, priv->cached_coherent = dev_is_dma_coherent(dev); + ret = etnaviv_init_dedicated_vram(dev, priv); + if (ret) + dev_err(dev, "Failed to init dedicated vram\n"); + return 0; } @@ -74,6 +81,11 @@ static void etnaviv_private_fini(struct etnaviv_drm_private *priv) xa_destroy(&priv->active_contexts); mutex_destroy(&priv->gem_lock); + + if (priv->dedicated_vram) { + drm_mm_takedown(&priv->vram.mm); + priv->dedicated_vram = false; + } } static void load_gpu(struct drm_device *dev) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 84f2e79f0a53..b093f832599f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -58,6 +58,17 @@ struct etnaviv_drm_private { */ bool cached_coherent; + /* + * dedicated VRAM (device memory) resource + */ + struct { + struct drm_mm mm; + u64 gpu_base; + u64 cpu_base; + u64 size; + } vram; + bool dedicated_vram; + /* list of GEM objects: */ struct mutex gem_lock; struct list_head gem_list; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index 8d8fc5b3a541..60bbbbc2dd19 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -59,6 +59,9 @@ struct etnaviv_gem_object { u32 last_cpu_prep_op; struct etnaviv_gem_userptr userptr; + + /* dedicated vram, which is physically contiguous */ + struct drm_mm_node *vram_np; }; static inline @@ -129,4 +132,6 @@ struct etnaviv_vram_mapping *etnaviv_gem_mapping_get( u64 va); void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping); +u64 etnaviv_obj_gpu_phys_addr(struct etnaviv_gem_object *etnaviv_obj); + #endif /* __ETNAVIV_GEM_H__ */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c new file mode 100644 index 000000000000..c2942317a64e --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_vram.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/pci.h> + +#include "etnaviv_drv.h" +#include "etnaviv_gem.h" +#include "etnaviv_pci_drv.h" + +static struct lock_class_key etnaviv_vram_lock_class; + +static u64 etnaviv_obj_cpu_phys_addr(struct etnaviv_gem_object *etnaviv_obj) +{ + struct drm_gem_object *obj = &etnaviv_obj->base; + struct etnaviv_drm_private *priv = to_etnaviv_priv(obj->dev); + + if (!etnaviv_obj->vram_np) { + drm_err(obj->dev, "No backing vram node, please pin it\n"); + return 0; + } + + return priv->vram.cpu_base + etnaviv_obj->vram_np->start; +} + +u64 etnaviv_obj_gpu_phys_addr(struct etnaviv_gem_object *etnaviv_obj) +{ + struct drm_gem_object *obj = &etnaviv_obj->base; + struct etnaviv_drm_private *priv = to_etnaviv_priv(obj->dev); + + if (!etnaviv_obj->vram_np) { + drm_err(obj->dev, "No backing vram node, no gpu offset\n"); + return 0; + } + + return priv->vram.gpu_base + etnaviv_obj->vram_np->start; +} + +/* Called with etnaviv_obj->lock held, allocate pages from dedicated VRAM */ +static int etnaviv_gem_vram_get_pages(struct etnaviv_gem_object *etnaviv_obj) +{ + struct drm_gem_object *obj = &etnaviv_obj->base; + struct drm_device *drm = obj->dev; + struct etnaviv_drm_private *priv = to_etnaviv_priv(drm); + unsigned int npages = obj->size >> PAGE_SHIFT; + struct drm_mm_node *vram_np; + struct page **ppages; + u64 gpu_paddr; + unsigned int i; + int ret; + + lockdep_assert_held(&etnaviv_obj->lock); + + vram_np = kzalloc(sizeof(*vram_np), GFP_KERNEL); + if (!vram_np) + return -ENOMEM; + + ret = drm_mm_insert_node(&priv->vram.mm, vram_np, obj->size); + if (ret) { + drm_err(drm, "Failed to insert %zu KiB\n", obj->size >> 10); + goto fail_allocate_range; + } + + ppages = kvmalloc_array(npages, sizeof(*ppages), GFP_KERNEL); + if (!ppages) { + ret = -ENOMEM; + goto fail_allocate_pages; + } + + etnaviv_obj->vram_np = vram_np; + + gpu_paddr = etnaviv_obj_gpu_phys_addr(etnaviv_obj); + for (i = 0; i < npages; ++i) { + ppages[i] = pfn_to_page(__phys_to_pfn(gpu_paddr)); + gpu_paddr += PAGE_SIZE; + } + + etnaviv_obj->pages = ppages; + + return 0; + +fail_allocate_pages: + drm_mm_remove_node(vram_np); +fail_allocate_range: + kfree(vram_np); + + return ret; +} + +static void *etnaviv_gem_vram_vmap(struct etnaviv_gem_object *etnaviv_obj) +{ + struct drm_device *drm = etnaviv_obj->base.dev; + u64 offset; + u64 size; + int ret; + + lockdep_assert_held(&etnaviv_obj->lock); + + /* + * This is equivalent to pin, we need the dedicated vram node + * inserted into the whole drm_mm, to get a valid offset. + */ + if (!etnaviv_obj->pages) { + ret = etnaviv_obj->ops->get_pages(etnaviv_obj); + if (ret) { + drm_err(drm, "Failed to pin %p\n", etnaviv_obj); + return NULL; + } + } + + offset = etnaviv_obj_cpu_phys_addr(etnaviv_obj); + size = etnaviv_obj->base.size; + + drm_dbg(drm, "offset : 0x%llx, %llu\n", offset, size >> 10); + + if (etnaviv_obj->flags & ETNA_BO_WC) + return ioremap_wc(offset, size); + + return ioremap(offset, size); +} + +static void etnaviv_gem_vram_vunmap(struct etnaviv_gem_object *etnaviv_obj) +{ + lockdep_assert_held(&etnaviv_obj->lock); + + iounmap(etnaviv_obj->vaddr); +} + +static void etnaviv_gem_vram_release(struct etnaviv_gem_object *etnaviv_obj) +{ + if (etnaviv_obj->sgt) { + sg_free_table(etnaviv_obj->sgt); + kfree(etnaviv_obj->sgt); + etnaviv_obj->sgt = NULL; + } + + if (etnaviv_obj->pages) { + kvfree(etnaviv_obj->pages); + etnaviv_obj->pages = NULL; + } + + if (etnaviv_obj->vram_np) { + drm_mm_remove_node(etnaviv_obj->vram_np); + kfree(etnaviv_obj->vram_np); + etnaviv_obj->vram_np = NULL; + } +} + +static int etnaviv_gem_vram_mmap(struct etnaviv_gem_object *etnaviv_obj, + struct vm_area_struct *vma) +{ + pgprot_t vm_page_prot; + + vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP); + + vm_page_prot = vm_get_page_prot(vma->vm_flags); + + if (etnaviv_obj->flags & ETNA_BO_WC) + vma->vm_page_prot = pgprot_writecombine(vm_page_prot); + else if (etnaviv_obj->flags & ETNA_BO_UNCACHED) + vma->vm_page_prot = pgprot_noncached(vm_page_prot); + else + vma->vm_page_prot = vm_page_prot; + + return 0; +} + +static const struct etnaviv_gem_ops etnaviv_gem_vram_ops = { + .get_pages = etnaviv_gem_vram_get_pages, + .release = etnaviv_gem_vram_release, + .vmap = etnaviv_gem_vram_vmap, + .vunmap = etnaviv_gem_vram_vunmap, + .mmap = etnaviv_gem_vram_mmap, +}; + +int etnaviv_gem_new_vram(struct drm_device *dev, struct drm_file *file, + u32 size, u32 flags, u32 *handle) +{ + struct etnaviv_gem_object *etnaviv_obj; + struct drm_gem_object *obj; + int ret; + + size = PAGE_ALIGN(size); + + ret = etnaviv_gem_new_private(dev, size, flags, false, + &etnaviv_gem_vram_ops, &etnaviv_obj); + if (ret) + return ret; + + lockdep_set_class(&etnaviv_obj->lock, &etnaviv_vram_lock_class); + + obj = &etnaviv_obj->base; + + ret = drm_gem_handle_create(file, obj, handle); + + /* drop reference from allocate - handle holds it now */ + drm_gem_object_put(obj); + + return ret; +} + +static int etnaviv_pci_init_dedicated_vram(struct device *dev, + struct etnaviv_drm_private *priv) +{ + struct pci_dev *gpu = to_pci_dev(dev); + static const struct etnaviv_pci_gpu_data *pdata; + resource_size_t base, size; + u32 bar; + + pdata = etnaviv_pci_get_match_data(dev); + if (!pdata) + return -ENOENT; + + if (pdata->num_vram <= 0) { + dev_err(dev, "Don't has a dedicated VRAM\n"); + return -ENODEV; + } + + /* Using the first vram bar */ + bar = pdata->vram_bars[0]; + + base = pci_resource_start(gpu, bar); + size = pci_resource_len(gpu, bar); + if (!base || !size) + return -ENOSPC; + + priv->vram.gpu_base = pci_bus_address(gpu, bar); + priv->vram.cpu_base = base; + priv->vram.size = size; + priv->dedicated_vram = true; + + dev_info(dev, "GPU Bar %u contains dedicated VRAM\n", bar); + + return 0; +} + +int etnaviv_init_dedicated_vram(struct device *dev, + struct etnaviv_drm_private *priv) +{ + int ret; + + if (dev->parent && dev_is_pci(dev->parent)) { + ret = etnaviv_pci_init_dedicated_vram(dev->parent, priv); + if (ret) + return ret; + } + + if (!priv->vram.size) + return 0; + + /* CPU physical address */ + drm_mm_init(&priv->vram.mm, 0, priv->vram.size); + + dev_info(dev, "VRAM device address range: %08llx-%08llx\n", + priv->vram.gpu_base, priv->vram.gpu_base + priv->vram.size); + dev_info(dev, "VRAM CPU physical address range: %08llx~%08llx\n", + priv->vram.cpu_base, priv->vram.cpu_base + priv->vram.size); + + return 0; +} diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h b/drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h new file mode 100644 index 000000000000..bbce93f118a2 --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_vram.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ETNAVIV_GEM_VRAM_H__ +#define __ETNAVIV_GEM_VRAM_H__ + +int etnaviv_gem_new_vram(struct drm_device *dev, struct drm_file *file, + u32 size, u32 flags, u32 *handle); + +int etnaviv_init_dedicated_vram(struct device *dev, + struct etnaviv_drm_private *priv); + +#endif diff --git a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c index 9911bfdc41a9..cbb7bc0947a7 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.c @@ -191,6 +191,19 @@ static struct pci_driver etnaviv_pci_driver = { .remove = etnaviv_pci_remove, }; +const struct etnaviv_pci_gpu_data * +etnaviv_pci_get_match_data(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + const struct pci_device_id *entity; + + entity = pci_match_id(etnaviv_pci_id_list, pdev); + if (!entity) + return NULL; + + return etnaviv_pci_get_platform_data(entity); +} + int etnaviv_register_pci_driver(void) { return pci_register_driver(&etnaviv_pci_driver); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h index 39eb2851355a..f5184fb35f50 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_pci_drv.h @@ -42,6 +42,9 @@ struct etnaviv_pci_gpu_data { char market_name[24]; }; +const struct etnaviv_pci_gpu_data * +etnaviv_pci_get_match_data(struct device *dev); + int etnaviv_register_pci_driver(void); void etnaviv_unregister_pci_driver(void); @@ -52,6 +55,9 @@ int jemo_pcie_init(struct pci_dev *pdev); static inline int etnaviv_register_pci_driver(void) { return 0; } static inline void etnaviv_unregister_pci_driver(void) { } +static inline struct etnaviv_pci_gpu_data * +etnaviv_pci_get_match_data(struct device *dev) { return NULL; } + #endif #endif -- 2.43.0