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(np: node, propname: "iommu-map");
34 if (err < 0)
35 return 0;
36
37 cdl->len = err / 4;
38 cdl->devs = kcalloc(n: cdl->len, size: 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(dev: &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(dev: &ctx->dev, name: "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(dev: &ctx->dev, UINT_MAX);
62
63 err = device_add(dev: &ctx->dev);
64 if (err) {
65 dev_err(host1x->dev, "could not add context device %d: %d\n", i, err);
66 put_device(dev: &ctx->dev);
67 goto unreg_devices;
68 }
69
70 err = of_dma_configure_id(dev: &ctx->dev, np: node, force_dma: true, id: &i);
71 if (err) {
72 dev_err(host1x->dev, "IOMMU configuration failed for context device %d: %d\n",
73 i, err);
74 device_unregister(dev: &ctx->dev);
75 goto unreg_devices;
76 }
77
78 if (!tegra_dev_iommu_get_stream_id(dev: &ctx->dev, stream_id: &ctx->stream_id) ||
79 !device_iommu_mapped(dev: &ctx->dev)) {
80 dev_err(host1x->dev, "Context device %d has no IOMMU!\n", i);
81 device_unregister(dev: &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(dev: &cdl->devs[i].dev);
99
100 kfree(objp: 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(dev: &cdl->devs[i].dev);
113
114 kfree(objp: 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(error: -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(r: &cd->ref);
139 mutex_unlock(lock: &cdl->lock);
140 return cd;
141 } else if (!cd->owner && !free) {
142 free = cd;
143 }
144 }
145
146 if (!free) {
147 mutex_unlock(lock: &cdl->lock);
148 return ERR_PTR(error: -EBUSY);
149 }
150
151 refcount_set(r: &free->ref, n: 1);
152 free->owner = get_pid(pid);
153
154 mutex_unlock(lock: &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(r: &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(r: &cd->ref, lock: &cdl->lock)) {
171 put_pid(pid: cd->owner);
172 cd->owner = NULL;
173 mutex_unlock(lock: &cdl->lock);
174 }
175}
176EXPORT_SYMBOL_GPL(host1x_memory_context_put);
177

source code of linux/drivers/gpu/host1x/context.c