xref: /linux/drivers/gpu/host1x/context.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
18aa5bcb6SMikko Perttunen // SPDX-License-Identifier: GPL-2.0-only
28aa5bcb6SMikko Perttunen /*
38aa5bcb6SMikko Perttunen  * Copyright (c) 2021, NVIDIA Corporation.
48aa5bcb6SMikko Perttunen  */
58aa5bcb6SMikko Perttunen 
68aa5bcb6SMikko Perttunen #include <linux/device.h>
78aa5bcb6SMikko Perttunen #include <linux/kref.h>
88aa5bcb6SMikko Perttunen #include <linux/of.h>
9573cbf48SRob Herring #include <linux/of_device.h>
108aa5bcb6SMikko Perttunen #include <linux/pid.h>
118aa5bcb6SMikko Perttunen #include <linux/slab.h>
128aa5bcb6SMikko Perttunen 
138aa5bcb6SMikko Perttunen #include "context.h"
148aa5bcb6SMikko Perttunen #include "dev.h"
158aa5bcb6SMikko Perttunen 
host1x_memory_context_release(struct device * dev)1655879dadSYang Yingliang static void host1x_memory_context_release(struct device *dev)
1755879dadSYang Yingliang {
1855879dadSYang Yingliang 	/* context device is freed in host1x_memory_context_list_free() */
1955879dadSYang Yingliang }
2055879dadSYang Yingliang 
host1x_memory_context_list_init(struct host1x * host1x)218aa5bcb6SMikko Perttunen int host1x_memory_context_list_init(struct host1x *host1x)
228aa5bcb6SMikko Perttunen {
238aa5bcb6SMikko Perttunen 	struct host1x_memory_context_list *cdl = &host1x->context_list;
248aa5bcb6SMikko Perttunen 	struct device_node *node = host1x->dev->of_node;
258aa5bcb6SMikko Perttunen 	struct host1x_memory_context *ctx;
268aa5bcb6SMikko Perttunen 	unsigned int i;
278aa5bcb6SMikko Perttunen 	int err;
288aa5bcb6SMikko Perttunen 
298aa5bcb6SMikko Perttunen 	cdl->devs = NULL;
308aa5bcb6SMikko Perttunen 	cdl->len = 0;
318aa5bcb6SMikko Perttunen 	mutex_init(&cdl->lock);
328aa5bcb6SMikko Perttunen 
338aa5bcb6SMikko Perttunen 	err = of_property_count_u32_elems(node, "iommu-map");
348aa5bcb6SMikko Perttunen 	if (err < 0)
358aa5bcb6SMikko Perttunen 		return 0;
368aa5bcb6SMikko Perttunen 
37*e889a311SJohnny Liu 	cdl->len = err / 4;
38*e889a311SJohnny Liu 	cdl->devs = kcalloc(cdl->len, sizeof(*cdl->devs), GFP_KERNEL);
398aa5bcb6SMikko Perttunen 	if (!cdl->devs)
408aa5bcb6SMikko Perttunen 		return -ENOMEM;
418aa5bcb6SMikko Perttunen 
428aa5bcb6SMikko Perttunen 	for (i = 0; i < cdl->len; i++) {
438aa5bcb6SMikko Perttunen 		ctx = &cdl->devs[i];
448aa5bcb6SMikko Perttunen 
458aa5bcb6SMikko Perttunen 		ctx->host = host1x;
468aa5bcb6SMikko Perttunen 
478aa5bcb6SMikko Perttunen 		device_initialize(&ctx->dev);
488aa5bcb6SMikko Perttunen 
498aa5bcb6SMikko Perttunen 		/*
508aa5bcb6SMikko Perttunen 		 * Due to an issue with T194 NVENC, only 38 bits can be used.
518aa5bcb6SMikko Perttunen 		 * Anyway, 256GiB of IOVA ought to be enough for anyone.
528aa5bcb6SMikko Perttunen 		 */
538aa5bcb6SMikko Perttunen 		ctx->dma_mask = DMA_BIT_MASK(38);
548aa5bcb6SMikko Perttunen 		ctx->dev.dma_mask = &ctx->dma_mask;
558aa5bcb6SMikko Perttunen 		ctx->dev.coherent_dma_mask = ctx->dma_mask;
568aa5bcb6SMikko Perttunen 		dev_set_name(&ctx->dev, "host1x-ctx.%d", i);
578aa5bcb6SMikko Perttunen 		ctx->dev.bus = &host1x_context_device_bus_type;
588aa5bcb6SMikko Perttunen 		ctx->dev.parent = host1x->dev;
5955879dadSYang Yingliang 		ctx->dev.release = host1x_memory_context_release;
608aa5bcb6SMikko Perttunen 
618aa5bcb6SMikko Perttunen 		dma_set_max_seg_size(&ctx->dev, UINT_MAX);
628aa5bcb6SMikko Perttunen 
638aa5bcb6SMikko Perttunen 		err = device_add(&ctx->dev);
648aa5bcb6SMikko Perttunen 		if (err) {
658aa5bcb6SMikko Perttunen 			dev_err(host1x->dev, "could not add context device %d: %d\n", i, err);
6655879dadSYang Yingliang 			put_device(&ctx->dev);
6755879dadSYang Yingliang 			goto unreg_devices;
688aa5bcb6SMikko Perttunen 		}
698aa5bcb6SMikko Perttunen 
708aa5bcb6SMikko Perttunen 		err = of_dma_configure_id(&ctx->dev, node, true, &i);
718aa5bcb6SMikko Perttunen 		if (err) {
728aa5bcb6SMikko Perttunen 			dev_err(host1x->dev, "IOMMU configuration failed for context device %d: %d\n",
738aa5bcb6SMikko Perttunen 				i, err);
7455879dadSYang Yingliang 			device_unregister(&ctx->dev);
7555879dadSYang Yingliang 			goto unreg_devices;
768aa5bcb6SMikko Perttunen 		}
778aa5bcb6SMikko Perttunen 
789026ba72SThierry Reding 		if (!tegra_dev_iommu_get_stream_id(&ctx->dev, &ctx->stream_id) ||
799026ba72SThierry Reding 		    !device_iommu_mapped(&ctx->dev)) {
808aa5bcb6SMikko Perttunen 			dev_err(host1x->dev, "Context device %d has no IOMMU!\n", i);
8155879dadSYang Yingliang 			device_unregister(&ctx->dev);
82b02e6e04SMikko Perttunen 
83b02e6e04SMikko Perttunen 			/*
84b02e6e04SMikko Perttunen 			 * This means that if IOMMU is disabled but context devices
85b02e6e04SMikko Perttunen 			 * are defined in the device tree, Host1x will fail to probe.
86b02e6e04SMikko Perttunen 			 * That's probably OK in this time and age.
87b02e6e04SMikko Perttunen 			 */
88b02e6e04SMikko Perttunen 			err = -EINVAL;
89b02e6e04SMikko Perttunen 
9055879dadSYang Yingliang 			goto unreg_devices;
918aa5bcb6SMikko Perttunen 		}
928aa5bcb6SMikko Perttunen 	}
938aa5bcb6SMikko Perttunen 
948aa5bcb6SMikko Perttunen 	return 0;
958aa5bcb6SMikko Perttunen 
9655879dadSYang Yingliang unreg_devices:
978aa5bcb6SMikko Perttunen 	while (i--)
9855879dadSYang Yingliang 		device_unregister(&cdl->devs[i].dev);
998aa5bcb6SMikko Perttunen 
1008aa5bcb6SMikko Perttunen 	kfree(cdl->devs);
1018466ff24SYang Yingliang 	cdl->devs = NULL;
1028aa5bcb6SMikko Perttunen 	cdl->len = 0;
1038aa5bcb6SMikko Perttunen 
1048aa5bcb6SMikko Perttunen 	return err;
1058aa5bcb6SMikko Perttunen }
1068aa5bcb6SMikko Perttunen 
host1x_memory_context_list_free(struct host1x_memory_context_list * cdl)1078aa5bcb6SMikko Perttunen void host1x_memory_context_list_free(struct host1x_memory_context_list *cdl)
1088aa5bcb6SMikko Perttunen {
1098aa5bcb6SMikko Perttunen 	unsigned int i;
1108aa5bcb6SMikko Perttunen 
1118aa5bcb6SMikko Perttunen 	for (i = 0; i < cdl->len; i++)
11255879dadSYang Yingliang 		device_unregister(&cdl->devs[i].dev);
1138aa5bcb6SMikko Perttunen 
1148aa5bcb6SMikko Perttunen 	kfree(cdl->devs);
1158aa5bcb6SMikko Perttunen 	cdl->len = 0;
1168aa5bcb6SMikko Perttunen }
1178aa5bcb6SMikko Perttunen 
host1x_memory_context_alloc(struct host1x * host1x,struct device * dev,struct pid * pid)1188aa5bcb6SMikko Perttunen struct host1x_memory_context *host1x_memory_context_alloc(struct host1x *host1x,
1198935002fSMikko Perttunen 							  struct device *dev,
1208aa5bcb6SMikko Perttunen 							  struct pid *pid)
1218aa5bcb6SMikko Perttunen {
1228aa5bcb6SMikko Perttunen 	struct host1x_memory_context_list *cdl = &host1x->context_list;
1238aa5bcb6SMikko Perttunen 	struct host1x_memory_context *free = NULL;
1248aa5bcb6SMikko Perttunen 	int i;
1258aa5bcb6SMikko Perttunen 
1268aa5bcb6SMikko Perttunen 	if (!cdl->len)
1278aa5bcb6SMikko Perttunen 		return ERR_PTR(-EOPNOTSUPP);
1288aa5bcb6SMikko Perttunen 
1298aa5bcb6SMikko Perttunen 	mutex_lock(&cdl->lock);
1308aa5bcb6SMikko Perttunen 
1318aa5bcb6SMikko Perttunen 	for (i = 0; i < cdl->len; i++) {
1328aa5bcb6SMikko Perttunen 		struct host1x_memory_context *cd = &cdl->devs[i];
1338aa5bcb6SMikko Perttunen 
1348935002fSMikko Perttunen 		if (cd->dev.iommu->iommu_dev != dev->iommu->iommu_dev)
1358935002fSMikko Perttunen 			continue;
1368935002fSMikko Perttunen 
1378aa5bcb6SMikko Perttunen 		if (cd->owner == pid) {
1388aa5bcb6SMikko Perttunen 			refcount_inc(&cd->ref);
1398aa5bcb6SMikko Perttunen 			mutex_unlock(&cdl->lock);
1408aa5bcb6SMikko Perttunen 			return cd;
1418aa5bcb6SMikko Perttunen 		} else if (!cd->owner && !free) {
1428aa5bcb6SMikko Perttunen 			free = cd;
1438aa5bcb6SMikko Perttunen 		}
1448aa5bcb6SMikko Perttunen 	}
1458aa5bcb6SMikko Perttunen 
1468aa5bcb6SMikko Perttunen 	if (!free) {
1478aa5bcb6SMikko Perttunen 		mutex_unlock(&cdl->lock);
1488aa5bcb6SMikko Perttunen 		return ERR_PTR(-EBUSY);
1498aa5bcb6SMikko Perttunen 	}
1508aa5bcb6SMikko Perttunen 
1518aa5bcb6SMikko Perttunen 	refcount_set(&free->ref, 1);
1528aa5bcb6SMikko Perttunen 	free->owner = get_pid(pid);
1538aa5bcb6SMikko Perttunen 
1548aa5bcb6SMikko Perttunen 	mutex_unlock(&cdl->lock);
1558aa5bcb6SMikko Perttunen 
1568aa5bcb6SMikko Perttunen 	return free;
1578aa5bcb6SMikko Perttunen }
1588aa5bcb6SMikko Perttunen EXPORT_SYMBOL_GPL(host1x_memory_context_alloc);
1598aa5bcb6SMikko Perttunen 
host1x_memory_context_get(struct host1x_memory_context * cd)1608aa5bcb6SMikko Perttunen void host1x_memory_context_get(struct host1x_memory_context *cd)
1618aa5bcb6SMikko Perttunen {
1628aa5bcb6SMikko Perttunen 	refcount_inc(&cd->ref);
1638aa5bcb6SMikko Perttunen }
1648aa5bcb6SMikko Perttunen EXPORT_SYMBOL_GPL(host1x_memory_context_get);
1658aa5bcb6SMikko Perttunen 
host1x_memory_context_put(struct host1x_memory_context * cd)1668aa5bcb6SMikko Perttunen void host1x_memory_context_put(struct host1x_memory_context *cd)
1678aa5bcb6SMikko Perttunen {
1688aa5bcb6SMikko Perttunen 	struct host1x_memory_context_list *cdl = &cd->host->context_list;
1698aa5bcb6SMikko Perttunen 
1708aa5bcb6SMikko Perttunen 	if (refcount_dec_and_mutex_lock(&cd->ref, &cdl->lock)) {
1718aa5bcb6SMikko Perttunen 		put_pid(cd->owner);
1728aa5bcb6SMikko Perttunen 		cd->owner = NULL;
1738aa5bcb6SMikko Perttunen 		mutex_unlock(&cdl->lock);
1748aa5bcb6SMikko Perttunen 	}
1758aa5bcb6SMikko Perttunen }
1768aa5bcb6SMikko Perttunen EXPORT_SYMBOL_GPL(host1x_memory_context_put);
177