From: Lima Project Developers <dri-devel@xxxxxxxxxxxxxxxxxxxxx> Signed-off-by: Qiang Yu <yuq825@xxxxxxxxx> Signed-off-by: Vasily Khoruzhick <anarsoul@xxxxxxxxx> --- drivers/gpu/drm/lima/lima_vm.c | 312 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/lima/lima_vm.h | 73 ++++++++ 2 files changed, 385 insertions(+) create mode 100644 drivers/gpu/drm/lima/lima_vm.c create mode 100644 drivers/gpu/drm/lima/lima_vm.h diff --git a/drivers/gpu/drm/lima/lima_vm.c b/drivers/gpu/drm/lima/lima_vm.c new file mode 100644 index 000000000000..00a3f6b59a33 --- /dev/null +++ b/drivers/gpu/drm/lima/lima_vm.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2017-2018 Lima Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/interval_tree_generic.h> + +#include "lima_device.h" +#include "lima_vm.h" +#include "lima_object.h" +#include "lima_regs.h" + +struct lima_bo_va_mapping { + struct list_head list; + struct rb_node rb; + uint32_t start; + uint32_t last; + uint32_t __subtree_last; +}; + +struct lima_bo_va { + struct list_head list; + unsigned ref_count; + + struct list_head mapping; + + struct lima_vm *vm; +}; + +#define LIMA_PDE(va) (va >> 22) +#define LIMA_PTE(va) ((va & 0x3FFFFF) >> 12) + +#define START(node) ((node)->start) +#define LAST(node) ((node)->last) + +INTERVAL_TREE_DEFINE(struct lima_bo_va_mapping, rb, uint32_t, __subtree_last, + START, LAST, static, lima_vm_it) + +#undef START +#undef LAST + +static void lima_vm_unmap_page_table(struct lima_vm *vm, u32 start, u32 end) +{ + u32 addr; + + for (addr = start; addr <= end; addr += LIMA_PAGE_SIZE) { + u32 pde = LIMA_PDE(addr); + u32 pte = LIMA_PTE(addr); + u32 *pt; + + pt = lima_bo_kmap(vm->pt[pde]); + pt[pte] = 0; + } +} + +static int lima_vm_map_page_table(struct lima_vm *vm, dma_addr_t *dma, + u32 start, u32 end) +{ + u64 addr; + int err, i = 0; + + for (addr = start; addr <= end; addr += LIMA_PAGE_SIZE) { + u32 pde = LIMA_PDE(addr); + u32 pte = LIMA_PTE(addr); + u32 *pd, *pt; + + if (vm->pt[pde]) + pt = lima_bo_kmap(vm->pt[pde]); + else { + vm->pt[pde] = lima_bo_create( + vm->dev, LIMA_PAGE_SIZE, 0, ttm_bo_type_kernel, + NULL, vm->pd->tbo.resv); + if (IS_ERR(vm->pt[pde])) { + err = PTR_ERR(vm->pt[pde]); + goto err_out; + } + + pt = lima_bo_kmap(vm->pt[pde]); + if (IS_ERR(pt)) { + err = PTR_ERR(pt); + goto err_out; + } + + pd = lima_bo_kmap(vm->pd); + pd[pde] = *lima_bo_get_pages(vm->pt[pde]) | LIMA_VM_FLAG_PRESENT; + } + + pt[pte] = dma[i++] | LIMA_VM_FLAGS_CACHE; + } + + return 0; + +err_out: + if (addr != start) + lima_vm_unmap_page_table(vm, start, addr - 1); + return err; +} + +static struct lima_bo_va * +lima_vm_bo_find(struct lima_vm *vm, struct lima_bo *bo) +{ + struct lima_bo_va *bo_va, *ret = NULL; + + list_for_each_entry(bo_va, &bo->va, list) { + if (bo_va->vm == vm) { + ret = bo_va; + break; + } + } + + return ret; +} + +int lima_vm_bo_map(struct lima_vm *vm, struct lima_bo *bo, u32 start) +{ + int err; + struct lima_bo_va_mapping *it, *mapping; + u32 end = start + bo->gem.size - 1; + dma_addr_t *pages_dma = lima_bo_get_pages(bo); + struct lima_bo_va *bo_va; + + it = lima_vm_it_iter_first(&vm->va, start, end); + if (it) { + dev_dbg(bo->gem.dev->dev, "lima vm map va overlap %x-%x %x-%x\n", + start, end, it->start, it->last); + return -EINVAL; + } + + mapping = kmalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) + return -ENOMEM; + mapping->start = start; + mapping->last = end; + + err = lima_vm_map_page_table(vm, pages_dma, start, end); + if (err) { + kfree(mapping); + return err; + } + + lima_vm_it_insert(mapping, &vm->va); + + bo_va = lima_vm_bo_find(vm, bo); + list_add_tail(&mapping->list, &bo_va->mapping); + + return 0; +} + +static void lima_vm_unmap(struct lima_vm *vm, + struct lima_bo_va_mapping *mapping) +{ + lima_vm_it_remove(mapping, &vm->va); + + lima_vm_unmap_page_table(vm, mapping->start, mapping->last); + + list_del(&mapping->list); + kfree(mapping); +} + +int lima_vm_bo_unmap(struct lima_vm *vm, struct lima_bo *bo, u32 start) +{ + struct lima_bo_va *bo_va; + struct lima_bo_va_mapping *mapping; + + bo_va = lima_vm_bo_find(vm, bo); + list_for_each_entry(mapping, &bo_va->mapping, list) { + if (mapping->start == start) { + lima_vm_unmap(vm, mapping); + break; + } + } + + return 0; +} + +int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo) +{ + struct lima_bo_va *bo_va; + + bo_va = lima_vm_bo_find(vm, bo); + if (bo_va) { + bo_va->ref_count++; + return 0; + } + + bo_va = kmalloc(sizeof(*bo_va), GFP_KERNEL); + if (!bo_va) + return -ENOMEM; + + bo_va->vm = vm; + bo_va->ref_count = 1; + INIT_LIST_HEAD(&bo_va->mapping); + list_add_tail(&bo_va->list, &bo->va); + return 0; +} + +int lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo) +{ + struct lima_bo_va *bo_va; + struct lima_bo_va_mapping *mapping, *tmp; + + bo_va = lima_vm_bo_find(vm, bo); + if (--bo_va->ref_count > 0) + return 0; + + list_for_each_entry_safe(mapping, tmp, &bo_va->mapping, list) { + lima_vm_unmap(vm, mapping); + } + list_del(&bo_va->list); + kfree(bo_va); + return 0; +} + +struct lima_vm *lima_vm_create(struct lima_device *dev) +{ + struct lima_vm *vm; + void *pd; + + vm = kzalloc(sizeof(*vm), GFP_KERNEL); + if (!vm) + return NULL; + + vm->dev = dev; + vm->va = RB_ROOT_CACHED; + kref_init(&vm->refcount); + + vm->pd = lima_bo_create(dev, LIMA_PAGE_SIZE, 0, + ttm_bo_type_kernel, NULL, NULL); + if (IS_ERR(vm->pd)) + goto err_out0; + + pd = lima_bo_kmap(vm->pd); + if (IS_ERR(pd)) + goto err_out1; + + if (dev->dlbu_cpu) { + int err = lima_vm_map_page_table( + vm, &dev->dlbu_dma, LIMA_VA_RESERVE_DLBU, + LIMA_VA_RESERVE_DLBU + LIMA_PAGE_SIZE - 1); + if (err) + goto err_out1; + } + + return vm; + +err_out1: + lima_bo_unref(vm->pd); +err_out0: + kfree(vm); + return NULL; +} + +void lima_vm_release(struct kref *kref) +{ + struct lima_vm *vm = container_of(kref, struct lima_vm, refcount); + struct lima_device *dev = vm->dev; + int i; + + if (!RB_EMPTY_ROOT(&vm->va.rb_root)) { + dev_err(dev->dev, "still active bo inside vm\n"); + } + + for (i = 0; i < LIMA_PAGE_ENT_NUM; i++) { + if (vm->pt[i]) + lima_bo_unref(vm->pt[i]); + } + + if (vm->pd) + lima_bo_unref(vm->pd); + + kfree(vm); +} + +void lima_vm_print(struct lima_vm *vm) +{ + int i, j; + u32 *pd = lima_bo_kmap(vm->pd); + + /* to avoid the defined by not used warning */ + (void)&lima_vm_it_iter_next; + + for (i = 0; i < LIMA_PAGE_ENT_NUM; i++) { + if (pd[i]) { + u32 *pt = lima_bo_kmap(vm->pt[i]); + + printk(KERN_INFO "lima vm pd %03x:%08x\n", i, pd[i]); + for (j = 0; j < LIMA_PAGE_ENT_NUM; j++) { + if (pt[j]) + printk(KERN_INFO " pt %03x:%08x\n", j, pt[j]); + } + } + } +} diff --git a/drivers/gpu/drm/lima/lima_vm.h b/drivers/gpu/drm/lima/lima_vm.h new file mode 100644 index 000000000000..20506459def0 --- /dev/null +++ b/drivers/gpu/drm/lima/lima_vm.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017-2018 Lima Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __LIMA_VM_H__ +#define __LIMA_VM_H__ + +#include <linux/rbtree.h> +#include <linux/kref.h> + +#define LIMA_PAGE_SIZE 4096 +#define LIMA_PAGE_MASK (LIMA_PAGE_SIZE - 1) +#define LIMA_PAGE_ENT_NUM (LIMA_PAGE_SIZE / sizeof(u32)) + +#define LIMA_VA_RESERVE_START 0xFFF00000 +#define LIMA_VA_RESERVE_DLBU LIMA_VA_RESERVE_START +#define LIMA_VA_RESERVE_END 0x100000000 + +struct lima_bo; +struct lima_device; + +struct lima_vm { + struct kref refcount; + + /* tree of virtual addresses mapped */ + struct rb_root_cached va; + + struct lima_device *dev; + + struct lima_bo *pd; + struct lima_bo *pt[LIMA_PAGE_ENT_NUM]; +}; + +int lima_vm_bo_map(struct lima_vm *vm, struct lima_bo *bo, u32 start); +int lima_vm_bo_unmap(struct lima_vm *vm, struct lima_bo *bo, u32 start); + +int lima_vm_bo_add(struct lima_vm *vm, struct lima_bo *bo); +int lima_vm_bo_del(struct lima_vm *vm, struct lima_bo *bo); + +struct lima_vm *lima_vm_create(struct lima_device *dev); +void lima_vm_release(struct kref *kref); + +static inline struct lima_vm *lima_vm_get(struct lima_vm *vm) +{ + kref_get(&vm->refcount); + return vm; +} + +static inline void lima_vm_put(struct lima_vm *vm) +{ + kref_put(&vm->refcount, lima_vm_release); +} + +void lima_vm_print(struct lima_vm *vm); + +#endif -- 2.17.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html