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 | |
16 | static void host1x_memory_context_release(struct device *dev) |
17 | { |
18 | /* context device is freed in host1x_memory_context_list_free() */ |
19 | } |
20 | |
21 | int 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 | |
96 | unreg_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 | |
107 | void 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 | |
118 | struct 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 | } |
158 | EXPORT_SYMBOL_GPL(host1x_memory_context_alloc); |
159 | |
160 | void host1x_memory_context_get(struct host1x_memory_context *cd) |
161 | { |
162 | refcount_inc(r: &cd->ref); |
163 | } |
164 | EXPORT_SYMBOL_GPL(host1x_memory_context_get); |
165 | |
166 | void 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 | } |
176 | EXPORT_SYMBOL_GPL(host1x_memory_context_put); |
177 | |