1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2021, NVIDIA Corporation.
4 */
5
6#include <linux/device.h>
7#include <linux/kref.h>
8#include <linux/of.h>
9#include <linux/of_device.h>
10#include <linux/pid.h>
11#include <linux/slab.h>
12
13#include "context.h"
14#include "dev.h"
15
16static void host1x_memory_context_release(struct device *dev)
17{
18	/* context device is freed in host1x_memory_context_list_free() */
19}
20
21int host1x_memory_context_list_init(struct host1x *host1x)
22{
23	struct host1x_memory_context_list *cdl = &host1x->context_list;
24	struct device_node *node = host1x->dev->of_node;
25	struct host1x_memory_context *ctx;
26	unsigned int i;
27	int err;
28
29	cdl->devs = NULL;
30	cdl->len = 0;
31	mutex_init(&cdl->lock);
32
33	err = of_property_count_u32_elems(node, "iommu-map");
34	if (err < 0)
35		return 0;
36
37	cdl->len = err / 4;
38	cdl->devs = kcalloc(cdl->len, sizeof(*cdl->devs), GFP_KERNEL);
39	if (!cdl->devs)
40		return -ENOMEM;
41
42	for (i = 0; i < cdl->len; i++) {
43		ctx = &cdl->devs[i];
44
45		ctx->host = host1x;
46
47		device_initialize(&ctx->dev);
48
49		/*
50		 * Due to an issue with T194 NVENC, only 38 bits can be used.
51		 * Anyway, 256GiB of IOVA ought to be enough for anyone.
52		 */
53		ctx->dma_mask = DMA_BIT_MASK(38);
54		ctx->dev.dma_mask = &ctx->dma_mask;
55		ctx->dev.coherent_dma_mask = ctx->dma_mask;
56		dev_set_name(&ctx->dev, "host1x-ctx.%d", i);
57		ctx->dev.bus = &host1x_context_device_bus_type;
58		ctx->dev.parent = host1x->dev;
59		ctx->dev.release = host1x_memory_context_release;
60
61		dma_set_max_seg_size(&ctx->dev, UINT_MAX);
62
63		err = device_add(&ctx->dev);
64		if (err) {
65			dev_err(host1x->dev, "could not add context device %d: %d\n", i, err);
66			put_device(&ctx->dev);
67			goto unreg_devices;
68		}
69
70		err = of_dma_configure_id(&ctx->dev, node, true, &i);
71		if (err) {
72			dev_err(host1x->dev, "IOMMU configuration failed for context device %d: %d\n",
73				i, err);
74			device_unregister(&ctx->dev);
75			goto unreg_devices;
76		}
77
78		if (!tegra_dev_iommu_get_stream_id(&ctx->dev, &ctx->stream_id) ||
79		    !device_iommu_mapped(&ctx->dev)) {
80			dev_err(host1x->dev, "Context device %d has no IOMMU!\n", i);
81			device_unregister(&ctx->dev);
82
83			/*
84			 * This means that if IOMMU is disabled but context devices
85			 * are defined in the device tree, Host1x will fail to probe.
86			 * That's probably OK in this time and age.
87			 */
88			err = -EINVAL;
89
90			goto unreg_devices;
91		}
92	}
93
94	return 0;
95
96unreg_devices:
97	while (i--)
98		device_unregister(&cdl->devs[i].dev);
99
100	kfree(cdl->devs);
101	cdl->devs = NULL;
102	cdl->len = 0;
103
104	return err;
105}
106
107void host1x_memory_context_list_free(struct host1x_memory_context_list *cdl)
108{
109	unsigned int i;
110
111	for (i = 0; i < cdl->len; i++)
112		device_unregister(&cdl->devs[i].dev);
113
114	kfree(cdl->devs);
115	cdl->len = 0;
116}
117
118struct host1x_memory_context *host1x_memory_context_alloc(struct host1x *host1x,
119							  struct device *dev,
120							  struct pid *pid)
121{
122	struct host1x_memory_context_list *cdl = &host1x->context_list;
123	struct host1x_memory_context *free = NULL;
124	int i;
125
126	if (!cdl->len)
127		return ERR_PTR(-EOPNOTSUPP);
128
129	mutex_lock(&cdl->lock);
130
131	for (i = 0; i < cdl->len; i++) {
132		struct host1x_memory_context *cd = &cdl->devs[i];
133
134		if (cd->dev.iommu->iommu_dev != dev->iommu->iommu_dev)
135			continue;
136
137		if (cd->owner == pid) {
138			refcount_inc(&cd->ref);
139			mutex_unlock(&cdl->lock);
140			return cd;
141		} else if (!cd->owner && !free) {
142			free = cd;
143		}
144	}
145
146	if (!free) {
147		mutex_unlock(&cdl->lock);
148		return ERR_PTR(-EBUSY);
149	}
150
151	refcount_set(&free->ref, 1);
152	free->owner = get_pid(pid);
153
154	mutex_unlock(&cdl->lock);
155
156	return free;
157}
158EXPORT_SYMBOL_GPL(host1x_memory_context_alloc);
159
160void host1x_memory_context_get(struct host1x_memory_context *cd)
161{
162	refcount_inc(&cd->ref);
163}
164EXPORT_SYMBOL_GPL(host1x_memory_context_get);
165
166void host1x_memory_context_put(struct host1x_memory_context *cd)
167{
168	struct host1x_memory_context_list *cdl = &cd->host->context_list;
169
170	if (refcount_dec_and_mutex_lock(&cd->ref, &cdl->lock)) {
171		put_pid(cd->owner);
172		cd->owner = NULL;
173		mutex_unlock(&cdl->lock);
174	}
175}
176EXPORT_SYMBOL_GPL(host1x_memory_context_put);
177