1ea1f5729SLucas Stach /* 2ea1f5729SLucas Stach * Copyright (C) 2017 Etnaviv Project 3ea1f5729SLucas Stach * 4ea1f5729SLucas Stach * This program is free software; you can redistribute it and/or modify it 5ea1f5729SLucas Stach * under the terms of the GNU General Public License version 2 as published by 6ea1f5729SLucas Stach * the Free Software Foundation. 7ea1f5729SLucas Stach * 8ea1f5729SLucas Stach * This program is distributed in the hope that it will be useful, but WITHOUT 9ea1f5729SLucas Stach * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10ea1f5729SLucas Stach * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11ea1f5729SLucas Stach * more details. 12ea1f5729SLucas Stach * 13ea1f5729SLucas Stach * You should have received a copy of the GNU General Public License along with 14ea1f5729SLucas Stach * this program. If not, see <http://www.gnu.org/licenses/>. 15ea1f5729SLucas Stach */ 16ea1f5729SLucas Stach 17e66774ddSLucas Stach #include <drm/drm_mm.h> 18e66774ddSLucas Stach 19ea1f5729SLucas Stach #include "etnaviv_cmdbuf.h" 20ea1f5729SLucas Stach #include "etnaviv_gpu.h" 21ea1f5729SLucas Stach #include "etnaviv_mmu.h" 22*4fc3e66aSChristian Gmeiner #include "etnaviv_perfmon.h" 23ea1f5729SLucas Stach 24e66774ddSLucas Stach #define SUBALLOC_SIZE SZ_256K 25e66774ddSLucas Stach #define SUBALLOC_GRANULE SZ_4K 26e66774ddSLucas Stach #define SUBALLOC_GRANULES (SUBALLOC_SIZE / SUBALLOC_GRANULE) 27e66774ddSLucas Stach 28e66774ddSLucas Stach struct etnaviv_cmdbuf_suballoc { 29e66774ddSLucas Stach /* suballocated dma buffer properties */ 30e66774ddSLucas Stach struct etnaviv_gpu *gpu; 31e66774ddSLucas Stach void *vaddr; 32e66774ddSLucas Stach dma_addr_t paddr; 33e66774ddSLucas Stach 34e66774ddSLucas Stach /* GPU mapping */ 35e66774ddSLucas Stach u32 iova; 36e66774ddSLucas Stach struct drm_mm_node vram_node; /* only used on MMUv2 */ 37e66774ddSLucas Stach 38e66774ddSLucas Stach /* allocation management */ 39e66774ddSLucas Stach struct mutex lock; 40e66774ddSLucas Stach DECLARE_BITMAP(granule_map, SUBALLOC_GRANULES); 41e66774ddSLucas Stach int free_space; 42e66774ddSLucas Stach wait_queue_head_t free_event; 43e66774ddSLucas Stach }; 44e66774ddSLucas Stach 45e66774ddSLucas Stach struct etnaviv_cmdbuf_suballoc * 46e66774ddSLucas Stach etnaviv_cmdbuf_suballoc_new(struct etnaviv_gpu * gpu) 47e66774ddSLucas Stach { 48e66774ddSLucas Stach struct etnaviv_cmdbuf_suballoc *suballoc; 49e66774ddSLucas Stach int ret; 50e66774ddSLucas Stach 51e66774ddSLucas Stach suballoc = kzalloc(sizeof(*suballoc), GFP_KERNEL); 52e66774ddSLucas Stach if (!suballoc) 53e66774ddSLucas Stach return ERR_PTR(-ENOMEM); 54e66774ddSLucas Stach 55e66774ddSLucas Stach suballoc->gpu = gpu; 56e66774ddSLucas Stach mutex_init(&suballoc->lock); 57e66774ddSLucas Stach init_waitqueue_head(&suballoc->free_event); 58e66774ddSLucas Stach 59e66774ddSLucas Stach suballoc->vaddr = dma_alloc_wc(gpu->dev, SUBALLOC_SIZE, 60e66774ddSLucas Stach &suballoc->paddr, GFP_KERNEL); 61e66774ddSLucas Stach if (!suballoc->vaddr) 62e66774ddSLucas Stach goto free_suballoc; 63e66774ddSLucas Stach 64e66774ddSLucas Stach ret = etnaviv_iommu_get_suballoc_va(gpu, suballoc->paddr, 65e66774ddSLucas Stach &suballoc->vram_node, SUBALLOC_SIZE, 66e66774ddSLucas Stach &suballoc->iova); 67e66774ddSLucas Stach if (ret) 68e66774ddSLucas Stach goto free_dma; 69e66774ddSLucas Stach 70e66774ddSLucas Stach return suballoc; 71e66774ddSLucas Stach 72e66774ddSLucas Stach free_dma: 73e66774ddSLucas Stach dma_free_wc(gpu->dev, SUBALLOC_SIZE, suballoc->vaddr, suballoc->paddr); 74e66774ddSLucas Stach free_suballoc: 75e66774ddSLucas Stach kfree(suballoc); 76e66774ddSLucas Stach 77e66774ddSLucas Stach return NULL; 78e66774ddSLucas Stach } 79e66774ddSLucas Stach 80e66774ddSLucas Stach void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc) 81e66774ddSLucas Stach { 82e66774ddSLucas Stach etnaviv_iommu_put_suballoc_va(suballoc->gpu, &suballoc->vram_node, 83e66774ddSLucas Stach SUBALLOC_SIZE, suballoc->iova); 84e66774ddSLucas Stach dma_free_wc(suballoc->gpu->dev, SUBALLOC_SIZE, suballoc->vaddr, 85e66774ddSLucas Stach suballoc->paddr); 86e66774ddSLucas Stach kfree(suballoc); 87e66774ddSLucas Stach } 88e66774ddSLucas Stach 89e66774ddSLucas Stach struct etnaviv_cmdbuf * 90e66774ddSLucas Stach etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size, 91*4fc3e66aSChristian Gmeiner size_t nr_bos, size_t nr_pmrs) 92ea1f5729SLucas Stach { 93ea1f5729SLucas Stach struct etnaviv_cmdbuf *cmdbuf; 94*4fc3e66aSChristian Gmeiner struct etnaviv_perfmon_request *pmrs; 95ea1f5729SLucas Stach size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo_map[0]), 96ea1f5729SLucas Stach sizeof(*cmdbuf)); 97e66774ddSLucas Stach int granule_offs, order, ret; 98ea1f5729SLucas Stach 99ea1f5729SLucas Stach cmdbuf = kzalloc(sz, GFP_KERNEL); 100ea1f5729SLucas Stach if (!cmdbuf) 101ea1f5729SLucas Stach return NULL; 102ea1f5729SLucas Stach 103*4fc3e66aSChristian Gmeiner sz = sizeof(*pmrs) * nr_pmrs; 104*4fc3e66aSChristian Gmeiner pmrs = kzalloc(sz, GFP_KERNEL); 105*4fc3e66aSChristian Gmeiner if (!pmrs) 106*4fc3e66aSChristian Gmeiner goto out_free_cmdbuf; 107*4fc3e66aSChristian Gmeiner 108*4fc3e66aSChristian Gmeiner cmdbuf->pmrs = pmrs; 109e66774ddSLucas Stach cmdbuf->suballoc = suballoc; 110e66774ddSLucas Stach cmdbuf->size = size; 111ea1f5729SLucas Stach 112e66774ddSLucas Stach order = order_base_2(ALIGN(size, SUBALLOC_GRANULE) / SUBALLOC_GRANULE); 113e66774ddSLucas Stach retry: 114e66774ddSLucas Stach mutex_lock(&suballoc->lock); 115e66774ddSLucas Stach granule_offs = bitmap_find_free_region(suballoc->granule_map, 116e66774ddSLucas Stach SUBALLOC_GRANULES, order); 117e66774ddSLucas Stach if (granule_offs < 0) { 118e66774ddSLucas Stach suballoc->free_space = 0; 119e66774ddSLucas Stach mutex_unlock(&suballoc->lock); 120e66774ddSLucas Stach ret = wait_event_interruptible_timeout(suballoc->free_event, 121e66774ddSLucas Stach suballoc->free_space, 122e66774ddSLucas Stach msecs_to_jiffies(10 * 1000)); 123e66774ddSLucas Stach if (!ret) { 124e66774ddSLucas Stach dev_err(suballoc->gpu->dev, 125e66774ddSLucas Stach "Timeout waiting for cmdbuf space\n"); 126ea1f5729SLucas Stach return NULL; 127ea1f5729SLucas Stach } 128e66774ddSLucas Stach goto retry; 129e66774ddSLucas Stach } 130e66774ddSLucas Stach mutex_unlock(&suballoc->lock); 131e66774ddSLucas Stach cmdbuf->suballoc_offset = granule_offs * SUBALLOC_GRANULE; 132e66774ddSLucas Stach cmdbuf->vaddr = suballoc->vaddr + cmdbuf->suballoc_offset; 133ea1f5729SLucas Stach 134ea1f5729SLucas Stach return cmdbuf; 135*4fc3e66aSChristian Gmeiner 136*4fc3e66aSChristian Gmeiner out_free_cmdbuf: 137*4fc3e66aSChristian Gmeiner kfree(cmdbuf); 138*4fc3e66aSChristian Gmeiner return NULL; 139ea1f5729SLucas Stach } 140ea1f5729SLucas Stach 141ea1f5729SLucas Stach void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) 142ea1f5729SLucas Stach { 143e66774ddSLucas Stach struct etnaviv_cmdbuf_suballoc *suballoc = cmdbuf->suballoc; 144e66774ddSLucas Stach int order = order_base_2(ALIGN(cmdbuf->size, SUBALLOC_GRANULE) / 145e66774ddSLucas Stach SUBALLOC_GRANULE); 146e66774ddSLucas Stach 147e66774ddSLucas Stach mutex_lock(&suballoc->lock); 148e66774ddSLucas Stach bitmap_release_region(suballoc->granule_map, 149e66774ddSLucas Stach cmdbuf->suballoc_offset / SUBALLOC_GRANULE, 150e66774ddSLucas Stach order); 151e66774ddSLucas Stach suballoc->free_space = 1; 152e66774ddSLucas Stach mutex_unlock(&suballoc->lock); 153e66774ddSLucas Stach wake_up_all(&suballoc->free_event); 154*4fc3e66aSChristian Gmeiner kfree(cmdbuf->pmrs); 155ea1f5729SLucas Stach kfree(cmdbuf); 156ea1f5729SLucas Stach } 157c3ef4b8cSLucas Stach 158c3ef4b8cSLucas Stach u32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf) 159c3ef4b8cSLucas Stach { 160e66774ddSLucas Stach return buf->suballoc->iova + buf->suballoc_offset; 161c3ef4b8cSLucas Stach } 1629912b4dbSLucas Stach 1639912b4dbSLucas Stach dma_addr_t etnaviv_cmdbuf_get_pa(struct etnaviv_cmdbuf *buf) 1649912b4dbSLucas Stach { 165e66774ddSLucas Stach return buf->suballoc->paddr + buf->suballoc_offset; 1669912b4dbSLucas Stach } 167