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