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