1f6ffbd4fSLucas Stach // SPDX-License-Identifier: GPL-2.0 2ea1f5729SLucas Stach /* 3f6ffbd4fSLucas Stach * Copyright (C) 2017-2018 Etnaviv Project 4ea1f5729SLucas Stach */ 5ea1f5729SLucas Stach 6*6eae41feSSam Ravnborg #include <linux/dma-mapping.h> 7*6eae41feSSam Ravnborg 8e66774ddSLucas Stach #include <drm/drm_mm.h> 9e66774ddSLucas Stach 10ea1f5729SLucas Stach #include "etnaviv_cmdbuf.h" 11ea1f5729SLucas Stach #include "etnaviv_gpu.h" 12ea1f5729SLucas Stach #include "etnaviv_mmu.h" 134fc3e66aSChristian Gmeiner #include "etnaviv_perfmon.h" 14ea1f5729SLucas Stach 15e66774ddSLucas Stach #define SUBALLOC_SIZE SZ_256K 16e66774ddSLucas Stach #define SUBALLOC_GRANULE SZ_4K 17e66774ddSLucas Stach #define SUBALLOC_GRANULES (SUBALLOC_SIZE / SUBALLOC_GRANULE) 18e66774ddSLucas Stach 19e66774ddSLucas Stach struct etnaviv_cmdbuf_suballoc { 20e66774ddSLucas Stach /* suballocated dma buffer properties */ 21e66774ddSLucas Stach struct etnaviv_gpu *gpu; 22e66774ddSLucas Stach void *vaddr; 23e66774ddSLucas Stach dma_addr_t paddr; 24e66774ddSLucas Stach 25e66774ddSLucas Stach /* GPU mapping */ 26e66774ddSLucas Stach u32 iova; 27e66774ddSLucas Stach struct drm_mm_node vram_node; /* only used on MMUv2 */ 28e66774ddSLucas Stach 29e66774ddSLucas Stach /* allocation management */ 30e66774ddSLucas Stach struct mutex lock; 31e66774ddSLucas Stach DECLARE_BITMAP(granule_map, SUBALLOC_GRANULES); 32e66774ddSLucas Stach int free_space; 33e66774ddSLucas Stach wait_queue_head_t free_event; 34e66774ddSLucas Stach }; 35e66774ddSLucas Stach 36e66774ddSLucas Stach struct etnaviv_cmdbuf_suballoc * 37e66774ddSLucas Stach etnaviv_cmdbuf_suballoc_new(struct etnaviv_gpu * gpu) 38e66774ddSLucas Stach { 39e66774ddSLucas Stach struct etnaviv_cmdbuf_suballoc *suballoc; 40e66774ddSLucas Stach int ret; 41e66774ddSLucas Stach 42e66774ddSLucas Stach suballoc = kzalloc(sizeof(*suballoc), GFP_KERNEL); 43e66774ddSLucas Stach if (!suballoc) 44e66774ddSLucas Stach return ERR_PTR(-ENOMEM); 45e66774ddSLucas Stach 46e66774ddSLucas Stach suballoc->gpu = gpu; 47e66774ddSLucas Stach mutex_init(&suballoc->lock); 48e66774ddSLucas Stach init_waitqueue_head(&suballoc->free_event); 49e66774ddSLucas Stach 50e66774ddSLucas Stach suballoc->vaddr = dma_alloc_wc(gpu->dev, SUBALLOC_SIZE, 51e66774ddSLucas Stach &suballoc->paddr, GFP_KERNEL); 52e66774ddSLucas Stach if (!suballoc->vaddr) 53e66774ddSLucas Stach goto free_suballoc; 54e66774ddSLucas Stach 55e66774ddSLucas Stach ret = etnaviv_iommu_get_suballoc_va(gpu, suballoc->paddr, 56e66774ddSLucas Stach &suballoc->vram_node, SUBALLOC_SIZE, 57e66774ddSLucas Stach &suballoc->iova); 58e66774ddSLucas Stach if (ret) 59e66774ddSLucas Stach goto free_dma; 60e66774ddSLucas Stach 61e66774ddSLucas Stach return suballoc; 62e66774ddSLucas Stach 63e66774ddSLucas Stach free_dma: 64e66774ddSLucas Stach dma_free_wc(gpu->dev, SUBALLOC_SIZE, suballoc->vaddr, suballoc->paddr); 65e66774ddSLucas Stach free_suballoc: 66e66774ddSLucas Stach kfree(suballoc); 67e66774ddSLucas Stach 68e66774ddSLucas Stach return NULL; 69e66774ddSLucas Stach } 70e66774ddSLucas Stach 71e66774ddSLucas Stach void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc) 72e66774ddSLucas Stach { 73e66774ddSLucas Stach etnaviv_iommu_put_suballoc_va(suballoc->gpu, &suballoc->vram_node, 74e66774ddSLucas Stach SUBALLOC_SIZE, suballoc->iova); 75e66774ddSLucas Stach dma_free_wc(suballoc->gpu->dev, SUBALLOC_SIZE, suballoc->vaddr, 76e66774ddSLucas Stach suballoc->paddr); 77e66774ddSLucas Stach kfree(suballoc); 78e66774ddSLucas Stach } 79e66774ddSLucas Stach 802f9225dbSLucas Stach int etnaviv_cmdbuf_init(struct etnaviv_cmdbuf_suballoc *suballoc, 812f9225dbSLucas Stach struct etnaviv_cmdbuf *cmdbuf, u32 size) 82ea1f5729SLucas Stach { 83e66774ddSLucas Stach int granule_offs, order, ret; 84ea1f5729SLucas Stach 85e66774ddSLucas Stach cmdbuf->suballoc = suballoc; 86e66774ddSLucas Stach cmdbuf->size = size; 87ea1f5729SLucas Stach 88e66774ddSLucas Stach order = order_base_2(ALIGN(size, SUBALLOC_GRANULE) / SUBALLOC_GRANULE); 89e66774ddSLucas Stach retry: 90e66774ddSLucas Stach mutex_lock(&suballoc->lock); 91e66774ddSLucas Stach granule_offs = bitmap_find_free_region(suballoc->granule_map, 92e66774ddSLucas Stach SUBALLOC_GRANULES, order); 93e66774ddSLucas Stach if (granule_offs < 0) { 94e66774ddSLucas Stach suballoc->free_space = 0; 95e66774ddSLucas Stach mutex_unlock(&suballoc->lock); 96e66774ddSLucas Stach ret = wait_event_interruptible_timeout(suballoc->free_event, 97e66774ddSLucas Stach suballoc->free_space, 98e66774ddSLucas Stach msecs_to_jiffies(10 * 1000)); 99e66774ddSLucas Stach if (!ret) { 100e66774ddSLucas Stach dev_err(suballoc->gpu->dev, 101e66774ddSLucas Stach "Timeout waiting for cmdbuf space\n"); 1022f9225dbSLucas Stach return -ETIMEDOUT; 103ea1f5729SLucas Stach } 104e66774ddSLucas Stach goto retry; 105e66774ddSLucas Stach } 106e66774ddSLucas Stach mutex_unlock(&suballoc->lock); 107e66774ddSLucas Stach cmdbuf->suballoc_offset = granule_offs * SUBALLOC_GRANULE; 108e66774ddSLucas Stach cmdbuf->vaddr = suballoc->vaddr + cmdbuf->suballoc_offset; 109ea1f5729SLucas Stach 1102f9225dbSLucas Stach return 0; 111ea1f5729SLucas Stach } 112ea1f5729SLucas Stach 113ea1f5729SLucas Stach void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) 114ea1f5729SLucas Stach { 115e66774ddSLucas Stach struct etnaviv_cmdbuf_suballoc *suballoc = cmdbuf->suballoc; 116e66774ddSLucas Stach int order = order_base_2(ALIGN(cmdbuf->size, SUBALLOC_GRANULE) / 117e66774ddSLucas Stach SUBALLOC_GRANULE); 118e66774ddSLucas Stach 119e66774ddSLucas Stach mutex_lock(&suballoc->lock); 120e66774ddSLucas Stach bitmap_release_region(suballoc->granule_map, 121e66774ddSLucas Stach cmdbuf->suballoc_offset / SUBALLOC_GRANULE, 122e66774ddSLucas Stach order); 123e66774ddSLucas Stach suballoc->free_space = 1; 124e66774ddSLucas Stach mutex_unlock(&suballoc->lock); 125e66774ddSLucas Stach wake_up_all(&suballoc->free_event); 126ea1f5729SLucas Stach } 127c3ef4b8cSLucas Stach 128c3ef4b8cSLucas Stach u32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf) 129c3ef4b8cSLucas Stach { 130e66774ddSLucas Stach return buf->suballoc->iova + buf->suballoc_offset; 131c3ef4b8cSLucas Stach } 1329912b4dbSLucas Stach 1339912b4dbSLucas Stach dma_addr_t etnaviv_cmdbuf_get_pa(struct etnaviv_cmdbuf *buf) 1349912b4dbSLucas Stach { 135e66774ddSLucas Stach return buf->suballoc->paddr + buf->suballoc_offset; 1369912b4dbSLucas Stach } 137