Merge tag 'drm-misc-next-fixes-2021-09-09' of git://anongit.freedesktop.org/drm/drm...
[linux-2.6-microblaze.git] / drivers / gpu / drm / tegra / uapi.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2020 NVIDIA Corporation */
3
4 #include <linux/host1x.h>
5 #include <linux/iommu.h>
6 #include <linux/list.h>
7
8 #include <drm/drm_drv.h>
9 #include <drm/drm_file.h>
10 #include <drm/drm_utils.h>
11
12 #include "drm.h"
13 #include "uapi.h"
14
15 static void tegra_drm_mapping_release(struct kref *ref)
16 {
17         struct tegra_drm_mapping *mapping =
18                 container_of(ref, struct tegra_drm_mapping, ref);
19
20         if (mapping->sgt)
21                 dma_unmap_sgtable(mapping->dev, mapping->sgt, mapping->direction,
22                                   DMA_ATTR_SKIP_CPU_SYNC);
23
24         host1x_bo_unpin(mapping->dev, mapping->bo, mapping->sgt);
25         host1x_bo_put(mapping->bo);
26
27         kfree(mapping);
28 }
29
30 void tegra_drm_mapping_put(struct tegra_drm_mapping *mapping)
31 {
32         kref_put(&mapping->ref, tegra_drm_mapping_release);
33 }
34
35 static void tegra_drm_channel_context_close(struct tegra_drm_context *context)
36 {
37         struct tegra_drm_mapping *mapping;
38         unsigned long id;
39
40         xa_for_each(&context->mappings, id, mapping)
41                 tegra_drm_mapping_put(mapping);
42
43         xa_destroy(&context->mappings);
44
45         host1x_channel_put(context->channel);
46
47         kfree(context);
48 }
49
50 void tegra_drm_uapi_close_file(struct tegra_drm_file *file)
51 {
52         struct tegra_drm_context *context;
53         struct host1x_syncpt *sp;
54         unsigned long id;
55
56         xa_for_each(&file->contexts, id, context)
57                 tegra_drm_channel_context_close(context);
58
59         xa_for_each(&file->syncpoints, id, sp)
60                 host1x_syncpt_put(sp);
61
62         xa_destroy(&file->contexts);
63         xa_destroy(&file->syncpoints);
64 }
65
66 static struct tegra_drm_client *tegra_drm_find_client(struct tegra_drm *tegra, u32 class)
67 {
68         struct tegra_drm_client *client;
69
70         list_for_each_entry(client, &tegra->clients, list)
71                 if (client->base.class == class)
72                         return client;
73
74         return NULL;
75 }
76
77 int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_file *file)
78 {
79         struct tegra_drm_file *fpriv = file->driver_priv;
80         struct tegra_drm *tegra = drm->dev_private;
81         struct drm_tegra_channel_open *args = data;
82         struct tegra_drm_client *client = NULL;
83         struct tegra_drm_context *context;
84         int err;
85
86         if (args->flags)
87                 return -EINVAL;
88
89         context = kzalloc(sizeof(*context), GFP_KERNEL);
90         if (!context)
91                 return -ENOMEM;
92
93         client = tegra_drm_find_client(tegra, args->host1x_class);
94         if (!client) {
95                 err = -ENODEV;
96                 goto free;
97         }
98
99         if (client->shared_channel) {
100                 context->channel = host1x_channel_get(client->shared_channel);
101         } else {
102                 context->channel = host1x_channel_request(&client->base);
103                 if (!context->channel) {
104                         err = -EBUSY;
105                         goto free;
106                 }
107         }
108
109         err = xa_alloc(&fpriv->contexts, &args->context, context, XA_LIMIT(1, U32_MAX),
110                        GFP_KERNEL);
111         if (err < 0)
112                 goto put_channel;
113
114         context->client = client;
115         xa_init_flags(&context->mappings, XA_FLAGS_ALLOC1);
116
117         args->version = client->version;
118         args->capabilities = 0;
119
120         if (device_get_dma_attr(client->base.dev) == DEV_DMA_COHERENT)
121                 args->capabilities |= DRM_TEGRA_CHANNEL_CAP_CACHE_COHERENT;
122
123         return 0;
124
125 put_channel:
126         host1x_channel_put(context->channel);
127 free:
128         kfree(context);
129
130         return err;
131 }
132
133 int tegra_drm_ioctl_channel_close(struct drm_device *drm, void *data, struct drm_file *file)
134 {
135         struct tegra_drm_file *fpriv = file->driver_priv;
136         struct drm_tegra_channel_close *args = data;
137         struct tegra_drm_context *context;
138
139         mutex_lock(&fpriv->lock);
140
141         context = xa_load(&fpriv->contexts, args->context);
142         if (!context) {
143                 mutex_unlock(&fpriv->lock);
144                 return -EINVAL;
145         }
146
147         xa_erase(&fpriv->contexts, args->context);
148
149         mutex_unlock(&fpriv->lock);
150
151         tegra_drm_channel_context_close(context);
152
153         return 0;
154 }
155
156 int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_file *file)
157 {
158         struct tegra_drm_file *fpriv = file->driver_priv;
159         struct drm_tegra_channel_map *args = data;
160         struct tegra_drm_mapping *mapping;
161         struct tegra_drm_context *context;
162         int err = 0;
163
164         if (args->flags & ~DRM_TEGRA_CHANNEL_MAP_READ_WRITE)
165                 return -EINVAL;
166
167         mutex_lock(&fpriv->lock);
168
169         context = xa_load(&fpriv->contexts, args->context);
170         if (!context) {
171                 mutex_unlock(&fpriv->lock);
172                 return -EINVAL;
173         }
174
175         mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
176         if (!mapping) {
177                 err = -ENOMEM;
178                 goto unlock;
179         }
180
181         kref_init(&mapping->ref);
182
183         mapping->dev = context->client->base.dev;
184         mapping->bo = tegra_gem_lookup(file, args->handle);
185         if (!mapping->bo) {
186                 err = -EINVAL;
187                 goto unlock;
188         }
189
190         if (context->client->base.group) {
191                 /* IOMMU domain managed directly using IOMMU API */
192                 host1x_bo_pin(mapping->dev, mapping->bo, &mapping->iova);
193         } else {
194                 switch (args->flags & DRM_TEGRA_CHANNEL_MAP_READ_WRITE) {
195                 case DRM_TEGRA_CHANNEL_MAP_READ_WRITE:
196                         mapping->direction = DMA_BIDIRECTIONAL;
197                         break;
198
199                 case DRM_TEGRA_CHANNEL_MAP_WRITE:
200                         mapping->direction = DMA_FROM_DEVICE;
201                         break;
202
203                 case DRM_TEGRA_CHANNEL_MAP_READ:
204                         mapping->direction = DMA_TO_DEVICE;
205                         break;
206
207                 default:
208                         return -EINVAL;
209                 }
210
211                 mapping->sgt = host1x_bo_pin(mapping->dev, mapping->bo, NULL);
212                 if (IS_ERR(mapping->sgt)) {
213                         err = PTR_ERR(mapping->sgt);
214                         goto put_gem;
215                 }
216
217                 err = dma_map_sgtable(mapping->dev, mapping->sgt, mapping->direction,
218                                       DMA_ATTR_SKIP_CPU_SYNC);
219                 if (err)
220                         goto unpin;
221
222                 mapping->iova = sg_dma_address(mapping->sgt->sgl);
223         }
224
225         mapping->iova_end = mapping->iova + host1x_to_tegra_bo(mapping->bo)->size;
226
227         err = xa_alloc(&context->mappings, &args->mapping, mapping, XA_LIMIT(1, U32_MAX),
228                        GFP_KERNEL);
229         if (err < 0)
230                 goto unmap;
231
232         mutex_unlock(&fpriv->lock);
233
234         return 0;
235
236 unmap:
237         if (mapping->sgt) {
238                 dma_unmap_sgtable(mapping->dev, mapping->sgt, mapping->direction,
239                                   DMA_ATTR_SKIP_CPU_SYNC);
240         }
241 unpin:
242         host1x_bo_unpin(mapping->dev, mapping->bo, mapping->sgt);
243 put_gem:
244         host1x_bo_put(mapping->bo);
245         kfree(mapping);
246 unlock:
247         mutex_unlock(&fpriv->lock);
248         return err;
249 }
250
251 int tegra_drm_ioctl_channel_unmap(struct drm_device *drm, void *data, struct drm_file *file)
252 {
253         struct tegra_drm_file *fpriv = file->driver_priv;
254         struct drm_tegra_channel_unmap *args = data;
255         struct tegra_drm_mapping *mapping;
256         struct tegra_drm_context *context;
257
258         mutex_lock(&fpriv->lock);
259
260         context = xa_load(&fpriv->contexts, args->context);
261         if (!context) {
262                 mutex_unlock(&fpriv->lock);
263                 return -EINVAL;
264         }
265
266         mapping = xa_erase(&context->mappings, args->mapping);
267
268         mutex_unlock(&fpriv->lock);
269
270         if (!mapping)
271                 return -EINVAL;
272
273         tegra_drm_mapping_put(mapping);
274         return 0;
275 }
276
277 int tegra_drm_ioctl_syncpoint_allocate(struct drm_device *drm, void *data, struct drm_file *file)
278 {
279         struct host1x *host1x = tegra_drm_to_host1x(drm->dev_private);
280         struct tegra_drm_file *fpriv = file->driver_priv;
281         struct drm_tegra_syncpoint_allocate *args = data;
282         struct host1x_syncpt *sp;
283         int err;
284
285         if (args->id)
286                 return -EINVAL;
287
288         sp = host1x_syncpt_alloc(host1x, HOST1X_SYNCPT_CLIENT_MANAGED, current->comm);
289         if (!sp)
290                 return -EBUSY;
291
292         args->id = host1x_syncpt_id(sp);
293
294         err = xa_insert(&fpriv->syncpoints, args->id, sp, GFP_KERNEL);
295         if (err) {
296                 host1x_syncpt_put(sp);
297                 return err;
298         }
299
300         return 0;
301 }
302
303 int tegra_drm_ioctl_syncpoint_free(struct drm_device *drm, void *data, struct drm_file *file)
304 {
305         struct tegra_drm_file *fpriv = file->driver_priv;
306         struct drm_tegra_syncpoint_allocate *args = data;
307         struct host1x_syncpt *sp;
308
309         mutex_lock(&fpriv->lock);
310         sp = xa_erase(&fpriv->syncpoints, args->id);
311         mutex_unlock(&fpriv->lock);
312
313         if (!sp)
314                 return -EINVAL;
315
316         host1x_syncpt_put(sp);
317
318         return 0;
319 }
320
321 int tegra_drm_ioctl_syncpoint_wait(struct drm_device *drm, void *data, struct drm_file *file)
322 {
323         struct host1x *host1x = tegra_drm_to_host1x(drm->dev_private);
324         struct drm_tegra_syncpoint_wait *args = data;
325         signed long timeout_jiffies;
326         struct host1x_syncpt *sp;
327
328         if (args->padding != 0)
329                 return -EINVAL;
330
331         sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
332         if (!sp)
333                 return -EINVAL;
334
335         timeout_jiffies = drm_timeout_abs_to_jiffies(args->timeout_ns);
336
337         return host1x_syncpt_wait(sp, args->threshold, timeout_jiffies, &args->value);
338 }