18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017-2018 Etnaviv Project 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <drm/drm_mm.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "etnaviv_cmdbuf.h" 118c2ecf20Sopenharmony_ci#include "etnaviv_gem.h" 128c2ecf20Sopenharmony_ci#include "etnaviv_gpu.h" 138c2ecf20Sopenharmony_ci#include "etnaviv_mmu.h" 148c2ecf20Sopenharmony_ci#include "etnaviv_perfmon.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define SUBALLOC_SIZE SZ_512K 178c2ecf20Sopenharmony_ci#define SUBALLOC_GRANULE SZ_4K 188c2ecf20Sopenharmony_ci#define SUBALLOC_GRANULES (SUBALLOC_SIZE / SUBALLOC_GRANULE) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct etnaviv_cmdbuf_suballoc { 218c2ecf20Sopenharmony_ci /* suballocated dma buffer properties */ 228c2ecf20Sopenharmony_ci struct device *dev; 238c2ecf20Sopenharmony_ci void *vaddr; 248c2ecf20Sopenharmony_ci dma_addr_t paddr; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci /* allocation management */ 278c2ecf20Sopenharmony_ci struct mutex lock; 288c2ecf20Sopenharmony_ci DECLARE_BITMAP(granule_map, SUBALLOC_GRANULES); 298c2ecf20Sopenharmony_ci int free_space; 308c2ecf20Sopenharmony_ci wait_queue_head_t free_event; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct etnaviv_cmdbuf_suballoc * 348c2ecf20Sopenharmony_cietnaviv_cmdbuf_suballoc_new(struct device *dev) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct etnaviv_cmdbuf_suballoc *suballoc; 378c2ecf20Sopenharmony_ci int ret; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci suballoc = kzalloc(sizeof(*suballoc), GFP_KERNEL); 408c2ecf20Sopenharmony_ci if (!suballoc) 418c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci suballoc->dev = dev; 448c2ecf20Sopenharmony_ci mutex_init(&suballoc->lock); 458c2ecf20Sopenharmony_ci init_waitqueue_head(&suballoc->free_event); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci BUILD_BUG_ON(ETNAVIV_SOFTPIN_START_ADDRESS < SUBALLOC_SIZE); 488c2ecf20Sopenharmony_ci suballoc->vaddr = dma_alloc_wc(dev, SUBALLOC_SIZE, 498c2ecf20Sopenharmony_ci &suballoc->paddr, GFP_KERNEL); 508c2ecf20Sopenharmony_ci if (!suballoc->vaddr) { 518c2ecf20Sopenharmony_ci ret = -ENOMEM; 528c2ecf20Sopenharmony_ci goto free_suballoc; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return suballoc; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cifree_suballoc: 588c2ecf20Sopenharmony_ci kfree(suballoc); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return ERR_PTR(ret); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciint etnaviv_cmdbuf_suballoc_map(struct etnaviv_cmdbuf_suballoc *suballoc, 648c2ecf20Sopenharmony_ci struct etnaviv_iommu_context *context, 658c2ecf20Sopenharmony_ci struct etnaviv_vram_mapping *mapping, 668c2ecf20Sopenharmony_ci u32 memory_base) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci return etnaviv_iommu_get_suballoc_va(context, mapping, memory_base, 698c2ecf20Sopenharmony_ci suballoc->paddr, SUBALLOC_SIZE); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_civoid etnaviv_cmdbuf_suballoc_unmap(struct etnaviv_iommu_context *context, 738c2ecf20Sopenharmony_ci struct etnaviv_vram_mapping *mapping) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci etnaviv_iommu_put_suballoc_va(context, mapping); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_civoid etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci dma_free_wc(suballoc->dev, SUBALLOC_SIZE, suballoc->vaddr, 818c2ecf20Sopenharmony_ci suballoc->paddr); 828c2ecf20Sopenharmony_ci kfree(suballoc); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciint etnaviv_cmdbuf_init(struct etnaviv_cmdbuf_suballoc *suballoc, 868c2ecf20Sopenharmony_ci struct etnaviv_cmdbuf *cmdbuf, u32 size) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int granule_offs, order, ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci cmdbuf->suballoc = suballoc; 918c2ecf20Sopenharmony_ci cmdbuf->size = size; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci order = order_base_2(ALIGN(size, SUBALLOC_GRANULE) / SUBALLOC_GRANULE); 948c2ecf20Sopenharmony_ciretry: 958c2ecf20Sopenharmony_ci mutex_lock(&suballoc->lock); 968c2ecf20Sopenharmony_ci granule_offs = bitmap_find_free_region(suballoc->granule_map, 978c2ecf20Sopenharmony_ci SUBALLOC_GRANULES, order); 988c2ecf20Sopenharmony_ci if (granule_offs < 0) { 998c2ecf20Sopenharmony_ci suballoc->free_space = 0; 1008c2ecf20Sopenharmony_ci mutex_unlock(&suballoc->lock); 1018c2ecf20Sopenharmony_ci ret = wait_event_interruptible_timeout(suballoc->free_event, 1028c2ecf20Sopenharmony_ci suballoc->free_space, 1038c2ecf20Sopenharmony_ci msecs_to_jiffies(10 * 1000)); 1048c2ecf20Sopenharmony_ci if (!ret) { 1058c2ecf20Sopenharmony_ci dev_err(suballoc->dev, 1068c2ecf20Sopenharmony_ci "Timeout waiting for cmdbuf space\n"); 1078c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci goto retry; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci mutex_unlock(&suballoc->lock); 1128c2ecf20Sopenharmony_ci cmdbuf->suballoc_offset = granule_offs * SUBALLOC_GRANULE; 1138c2ecf20Sopenharmony_ci cmdbuf->vaddr = suballoc->vaddr + cmdbuf->suballoc_offset; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_civoid etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct etnaviv_cmdbuf_suballoc *suballoc = cmdbuf->suballoc; 1218c2ecf20Sopenharmony_ci int order = order_base_2(ALIGN(cmdbuf->size, SUBALLOC_GRANULE) / 1228c2ecf20Sopenharmony_ci SUBALLOC_GRANULE); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mutex_lock(&suballoc->lock); 1258c2ecf20Sopenharmony_ci bitmap_release_region(suballoc->granule_map, 1268c2ecf20Sopenharmony_ci cmdbuf->suballoc_offset / SUBALLOC_GRANULE, 1278c2ecf20Sopenharmony_ci order); 1288c2ecf20Sopenharmony_ci suballoc->free_space = 1; 1298c2ecf20Sopenharmony_ci mutex_unlock(&suballoc->lock); 1308c2ecf20Sopenharmony_ci wake_up_all(&suballoc->free_event); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ciu32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf, 1348c2ecf20Sopenharmony_ci struct etnaviv_vram_mapping *mapping) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return mapping->iova + buf->suballoc_offset; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cidma_addr_t etnaviv_cmdbuf_get_pa(struct etnaviv_cmdbuf *buf) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return buf->suballoc->paddr + buf->suballoc_offset; 1428c2ecf20Sopenharmony_ci} 143