xref: /linux/drivers/gpu/drm/tegra/submit.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
113abe0bbSMikko Perttunen // SPDX-License-Identifier: GPL-2.0-only
213abe0bbSMikko Perttunen /* Copyright (c) 2020 NVIDIA Corporation */
313abe0bbSMikko Perttunen 
413abe0bbSMikko Perttunen #include <linux/dma-fence-array.h>
513abe0bbSMikko Perttunen #include <linux/dma-mapping.h>
613abe0bbSMikko Perttunen #include <linux/file.h>
713abe0bbSMikko Perttunen #include <linux/host1x.h>
813abe0bbSMikko Perttunen #include <linux/iommu.h>
913abe0bbSMikko Perttunen #include <linux/kref.h>
1013abe0bbSMikko Perttunen #include <linux/list.h>
1113abe0bbSMikko Perttunen #include <linux/nospec.h>
1213abe0bbSMikko Perttunen #include <linux/pm_runtime.h>
1313abe0bbSMikko Perttunen #include <linux/scatterlist.h>
1413abe0bbSMikko Perttunen #include <linux/slab.h>
1513abe0bbSMikko Perttunen #include <linux/sync_file.h>
1613abe0bbSMikko Perttunen 
1713abe0bbSMikko Perttunen #include <drm/drm_drv.h>
1813abe0bbSMikko Perttunen #include <drm/drm_file.h>
1913abe0bbSMikko Perttunen #include <drm/drm_syncobj.h>
2013abe0bbSMikko Perttunen 
2113abe0bbSMikko Perttunen #include "drm.h"
2213abe0bbSMikko Perttunen #include "gem.h"
2313abe0bbSMikko Perttunen #include "submit.h"
2413abe0bbSMikko Perttunen #include "uapi.h"
2513abe0bbSMikko Perttunen 
2613abe0bbSMikko Perttunen #define SUBMIT_ERR(context, fmt, ...) \
2713abe0bbSMikko Perttunen 	dev_err_ratelimited(context->client->base.dev, \
2813abe0bbSMikko Perttunen 		"%s: job submission failed: " fmt "\n", \
2913abe0bbSMikko Perttunen 		current->comm, ##__VA_ARGS__)
3013abe0bbSMikko Perttunen 
3113abe0bbSMikko Perttunen struct gather_bo {
3213abe0bbSMikko Perttunen 	struct host1x_bo base;
3313abe0bbSMikko Perttunen 
3413abe0bbSMikko Perttunen 	struct kref ref;
3513abe0bbSMikko Perttunen 
3613abe0bbSMikko Perttunen 	struct device *dev;
3713abe0bbSMikko Perttunen 	u32 *gather_data;
3813abe0bbSMikko Perttunen 	dma_addr_t gather_data_dma;
3913abe0bbSMikko Perttunen 	size_t gather_data_words;
4013abe0bbSMikko Perttunen };
4113abe0bbSMikko Perttunen 
gather_bo_get(struct host1x_bo * host_bo)4213abe0bbSMikko Perttunen static struct host1x_bo *gather_bo_get(struct host1x_bo *host_bo)
4313abe0bbSMikko Perttunen {
4413abe0bbSMikko Perttunen 	struct gather_bo *bo = container_of(host_bo, struct gather_bo, base);
4513abe0bbSMikko Perttunen 
4613abe0bbSMikko Perttunen 	kref_get(&bo->ref);
4713abe0bbSMikko Perttunen 
4813abe0bbSMikko Perttunen 	return host_bo;
4913abe0bbSMikko Perttunen }
5013abe0bbSMikko Perttunen 
gather_bo_release(struct kref * ref)5113abe0bbSMikko Perttunen static void gather_bo_release(struct kref *ref)
5213abe0bbSMikko Perttunen {
5313abe0bbSMikko Perttunen 	struct gather_bo *bo = container_of(ref, struct gather_bo, ref);
5413abe0bbSMikko Perttunen 
5513abe0bbSMikko Perttunen 	dma_free_attrs(bo->dev, bo->gather_data_words * 4, bo->gather_data, bo->gather_data_dma,
5613abe0bbSMikko Perttunen 		       0);
5713abe0bbSMikko Perttunen 	kfree(bo);
5813abe0bbSMikko Perttunen }
5913abe0bbSMikko Perttunen 
gather_bo_put(struct host1x_bo * host_bo)6013abe0bbSMikko Perttunen static void gather_bo_put(struct host1x_bo *host_bo)
6113abe0bbSMikko Perttunen {
6213abe0bbSMikko Perttunen 	struct gather_bo *bo = container_of(host_bo, struct gather_bo, base);
6313abe0bbSMikko Perttunen 
6413abe0bbSMikko Perttunen 	kref_put(&bo->ref, gather_bo_release);
6513abe0bbSMikko Perttunen }
6613abe0bbSMikko Perttunen 
67c6aeaf56SThierry Reding static struct host1x_bo_mapping *
gather_bo_pin(struct device * dev,struct host1x_bo * bo,enum dma_data_direction direction)68c6aeaf56SThierry Reding gather_bo_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_direction direction)
6913abe0bbSMikko Perttunen {
70c6aeaf56SThierry Reding 	struct gather_bo *gather = container_of(bo, struct gather_bo, base);
71c6aeaf56SThierry Reding 	struct host1x_bo_mapping *map;
7213abe0bbSMikko Perttunen 	int err;
7313abe0bbSMikko Perttunen 
74c6aeaf56SThierry Reding 	map = kzalloc(sizeof(*map), GFP_KERNEL);
75c6aeaf56SThierry Reding 	if (!map)
7613abe0bbSMikko Perttunen 		return ERR_PTR(-ENOMEM);
7713abe0bbSMikko Perttunen 
781f39b1dfSThierry Reding 	kref_init(&map->ref);
79c6aeaf56SThierry Reding 	map->bo = host1x_bo_get(bo);
80c6aeaf56SThierry Reding 	map->direction = direction;
81c6aeaf56SThierry Reding 	map->dev = dev;
82c6aeaf56SThierry Reding 
83c6aeaf56SThierry Reding 	map->sgt = kzalloc(sizeof(*map->sgt), GFP_KERNEL);
84c6aeaf56SThierry Reding 	if (!map->sgt) {
85c6aeaf56SThierry Reding 		err = -ENOMEM;
86c6aeaf56SThierry Reding 		goto free;
87c6aeaf56SThierry Reding 	}
88c6aeaf56SThierry Reding 
89c6aeaf56SThierry Reding 	err = dma_get_sgtable(gather->dev, map->sgt, gather->gather_data, gather->gather_data_dma,
90c6aeaf56SThierry Reding 			      gather->gather_data_words * 4);
91c6aeaf56SThierry Reding 	if (err)
92c6aeaf56SThierry Reding 		goto free_sgt;
93c6aeaf56SThierry Reding 
94c6aeaf56SThierry Reding 	err = dma_map_sgtable(dev, map->sgt, direction, 0);
95c6aeaf56SThierry Reding 	if (err)
96c6aeaf56SThierry Reding 		goto free_sgt;
97c6aeaf56SThierry Reding 
98c6aeaf56SThierry Reding 	map->phys = sg_dma_address(map->sgt->sgl);
99c6aeaf56SThierry Reding 	map->size = gather->gather_data_words * 4;
100c6aeaf56SThierry Reding 	map->chunks = err;
101c6aeaf56SThierry Reding 
102c6aeaf56SThierry Reding 	return map;
103c6aeaf56SThierry Reding 
104c6aeaf56SThierry Reding free_sgt:
105c6aeaf56SThierry Reding 	sg_free_table(map->sgt);
106c6aeaf56SThierry Reding 	kfree(map->sgt);
107c6aeaf56SThierry Reding free:
108c6aeaf56SThierry Reding 	kfree(map);
10913abe0bbSMikko Perttunen 	return ERR_PTR(err);
11013abe0bbSMikko Perttunen }
11113abe0bbSMikko Perttunen 
gather_bo_unpin(struct host1x_bo_mapping * map)112c6aeaf56SThierry Reding static void gather_bo_unpin(struct host1x_bo_mapping *map)
11313abe0bbSMikko Perttunen {
114c6aeaf56SThierry Reding 	if (!map)
115c6aeaf56SThierry Reding 		return;
116c6aeaf56SThierry Reding 
117c6aeaf56SThierry Reding 	dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0);
118c6aeaf56SThierry Reding 	sg_free_table(map->sgt);
119c6aeaf56SThierry Reding 	kfree(map->sgt);
120c6aeaf56SThierry Reding 	host1x_bo_put(map->bo);
121c6aeaf56SThierry Reding 
122c6aeaf56SThierry Reding 	kfree(map);
12313abe0bbSMikko Perttunen }
12413abe0bbSMikko Perttunen 
gather_bo_mmap(struct host1x_bo * host_bo)12513abe0bbSMikko Perttunen static void *gather_bo_mmap(struct host1x_bo *host_bo)
12613abe0bbSMikko Perttunen {
12713abe0bbSMikko Perttunen 	struct gather_bo *bo = container_of(host_bo, struct gather_bo, base);
12813abe0bbSMikko Perttunen 
12913abe0bbSMikko Perttunen 	return bo->gather_data;
13013abe0bbSMikko Perttunen }
13113abe0bbSMikko Perttunen 
gather_bo_munmap(struct host1x_bo * host_bo,void * addr)13213abe0bbSMikko Perttunen static void gather_bo_munmap(struct host1x_bo *host_bo, void *addr)
13313abe0bbSMikko Perttunen {
13413abe0bbSMikko Perttunen }
13513abe0bbSMikko Perttunen 
1362a1a310cSruanjinjie static const struct host1x_bo_ops gather_bo_ops = {
13713abe0bbSMikko Perttunen 	.get = gather_bo_get,
13813abe0bbSMikko Perttunen 	.put = gather_bo_put,
13913abe0bbSMikko Perttunen 	.pin = gather_bo_pin,
14013abe0bbSMikko Perttunen 	.unpin = gather_bo_unpin,
14113abe0bbSMikko Perttunen 	.mmap = gather_bo_mmap,
14213abe0bbSMikko Perttunen 	.munmap = gather_bo_munmap,
14313abe0bbSMikko Perttunen };
14413abe0bbSMikko Perttunen 
14513abe0bbSMikko Perttunen static struct tegra_drm_mapping *
tegra_drm_mapping_get(struct tegra_drm_context * context,u32 id)14613abe0bbSMikko Perttunen tegra_drm_mapping_get(struct tegra_drm_context *context, u32 id)
14713abe0bbSMikko Perttunen {
14813abe0bbSMikko Perttunen 	struct tegra_drm_mapping *mapping;
14913abe0bbSMikko Perttunen 
15013abe0bbSMikko Perttunen 	xa_lock(&context->mappings);
15113abe0bbSMikko Perttunen 
15213abe0bbSMikko Perttunen 	mapping = xa_load(&context->mappings, id);
15313abe0bbSMikko Perttunen 	if (mapping)
15413abe0bbSMikko Perttunen 		kref_get(&mapping->ref);
15513abe0bbSMikko Perttunen 
15613abe0bbSMikko Perttunen 	xa_unlock(&context->mappings);
15713abe0bbSMikko Perttunen 
15813abe0bbSMikko Perttunen 	return mapping;
15913abe0bbSMikko Perttunen }
16013abe0bbSMikko Perttunen 
alloc_copy_user_array(void __user * from,size_t count,size_t size)16113abe0bbSMikko Perttunen static void *alloc_copy_user_array(void __user *from, size_t count, size_t size)
16213abe0bbSMikko Perttunen {
16313abe0bbSMikko Perttunen 	size_t copy_len;
16413abe0bbSMikko Perttunen 	void *data;
16513abe0bbSMikko Perttunen 
16613abe0bbSMikko Perttunen 	if (check_mul_overflow(count, size, &copy_len))
16713abe0bbSMikko Perttunen 		return ERR_PTR(-EINVAL);
16813abe0bbSMikko Perttunen 
16913abe0bbSMikko Perttunen 	if (copy_len > 0x4000)
17013abe0bbSMikko Perttunen 		return ERR_PTR(-E2BIG);
17113abe0bbSMikko Perttunen 
172bbdca2d4SQing Wang 	data = vmemdup_user(from, copy_len);
173bbdca2d4SQing Wang 	if (IS_ERR(data))
174bbdca2d4SQing Wang 		return ERR_CAST(data);
17513abe0bbSMikko Perttunen 
17613abe0bbSMikko Perttunen 	return data;
17713abe0bbSMikko Perttunen }
17813abe0bbSMikko Perttunen 
submit_copy_gather_data(struct gather_bo ** pbo,struct device * dev,struct tegra_drm_context * context,struct drm_tegra_channel_submit * args)17913abe0bbSMikko Perttunen static int submit_copy_gather_data(struct gather_bo **pbo, struct device *dev,
18013abe0bbSMikko Perttunen 				   struct tegra_drm_context *context,
18113abe0bbSMikko Perttunen 				   struct drm_tegra_channel_submit *args)
18213abe0bbSMikko Perttunen {
18313abe0bbSMikko Perttunen 	struct gather_bo *bo;
18413abe0bbSMikko Perttunen 	size_t copy_len;
18513abe0bbSMikko Perttunen 
18613abe0bbSMikko Perttunen 	if (args->gather_data_words == 0) {
18713abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "gather_data_words cannot be zero");
18813abe0bbSMikko Perttunen 		return -EINVAL;
18913abe0bbSMikko Perttunen 	}
19013abe0bbSMikko Perttunen 
19113abe0bbSMikko Perttunen 	if (check_mul_overflow((size_t)args->gather_data_words, (size_t)4, &copy_len)) {
19213abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "gather_data_words is too large");
19313abe0bbSMikko Perttunen 		return -EINVAL;
19413abe0bbSMikko Perttunen 	}
19513abe0bbSMikko Perttunen 
19613abe0bbSMikko Perttunen 	bo = kzalloc(sizeof(*bo), GFP_KERNEL);
19713abe0bbSMikko Perttunen 	if (!bo) {
19813abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "failed to allocate memory for bo info");
19913abe0bbSMikko Perttunen 		return -ENOMEM;
20013abe0bbSMikko Perttunen 	}
20113abe0bbSMikko Perttunen 
20213abe0bbSMikko Perttunen 	host1x_bo_init(&bo->base, &gather_bo_ops);
20313abe0bbSMikko Perttunen 	kref_init(&bo->ref);
20413abe0bbSMikko Perttunen 	bo->dev = dev;
20513abe0bbSMikko Perttunen 
20613abe0bbSMikko Perttunen 	bo->gather_data = dma_alloc_attrs(dev, copy_len, &bo->gather_data_dma,
20713abe0bbSMikko Perttunen 					  GFP_KERNEL | __GFP_NOWARN, 0);
20813abe0bbSMikko Perttunen 	if (!bo->gather_data) {
20913abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "failed to allocate memory for gather data");
21013abe0bbSMikko Perttunen 		kfree(bo);
21113abe0bbSMikko Perttunen 		return -ENOMEM;
21213abe0bbSMikko Perttunen 	}
21313abe0bbSMikko Perttunen 
21413abe0bbSMikko Perttunen 	if (copy_from_user(bo->gather_data, u64_to_user_ptr(args->gather_data_ptr), copy_len)) {
21513abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "failed to copy gather data from userspace");
21613abe0bbSMikko Perttunen 		dma_free_attrs(dev, copy_len, bo->gather_data, bo->gather_data_dma, 0);
21713abe0bbSMikko Perttunen 		kfree(bo);
21813abe0bbSMikko Perttunen 		return -EFAULT;
21913abe0bbSMikko Perttunen 	}
22013abe0bbSMikko Perttunen 
22113abe0bbSMikko Perttunen 	bo->gather_data_words = args->gather_data_words;
22213abe0bbSMikko Perttunen 
22313abe0bbSMikko Perttunen 	*pbo = bo;
22413abe0bbSMikko Perttunen 
22513abe0bbSMikko Perttunen 	return 0;
22613abe0bbSMikko Perttunen }
22713abe0bbSMikko Perttunen 
submit_write_reloc(struct tegra_drm_context * context,struct gather_bo * bo,struct drm_tegra_submit_buf * buf,struct tegra_drm_mapping * mapping)22813abe0bbSMikko Perttunen static int submit_write_reloc(struct tegra_drm_context *context, struct gather_bo *bo,
22913abe0bbSMikko Perttunen 			      struct drm_tegra_submit_buf *buf, struct tegra_drm_mapping *mapping)
23013abe0bbSMikko Perttunen {
23113abe0bbSMikko Perttunen 	/* TODO check that target_offset is within bounds */
23213abe0bbSMikko Perttunen 	dma_addr_t iova = mapping->iova + buf->reloc.target_offset;
23313abe0bbSMikko Perttunen 	u32 written_ptr;
23413abe0bbSMikko Perttunen 
23513abe0bbSMikko Perttunen #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
23613abe0bbSMikko Perttunen 	if (buf->flags & DRM_TEGRA_SUBMIT_RELOC_SECTOR_LAYOUT)
23713abe0bbSMikko Perttunen 		iova |= BIT_ULL(39);
23813abe0bbSMikko Perttunen #endif
23913abe0bbSMikko Perttunen 
24013abe0bbSMikko Perttunen 	written_ptr = iova >> buf->reloc.shift;
24113abe0bbSMikko Perttunen 
24213abe0bbSMikko Perttunen 	if (buf->reloc.gather_offset_words >= bo->gather_data_words) {
24313abe0bbSMikko Perttunen 		SUBMIT_ERR(context,
24413abe0bbSMikko Perttunen 			   "relocation has too large gather offset (%u vs gather length %zu)",
24513abe0bbSMikko Perttunen 			   buf->reloc.gather_offset_words, bo->gather_data_words);
24613abe0bbSMikko Perttunen 		return -EINVAL;
24713abe0bbSMikko Perttunen 	}
24813abe0bbSMikko Perttunen 
24913abe0bbSMikko Perttunen 	buf->reloc.gather_offset_words = array_index_nospec(buf->reloc.gather_offset_words,
25013abe0bbSMikko Perttunen 							    bo->gather_data_words);
25113abe0bbSMikko Perttunen 
25213abe0bbSMikko Perttunen 	bo->gather_data[buf->reloc.gather_offset_words] = written_ptr;
25313abe0bbSMikko Perttunen 
25413abe0bbSMikko Perttunen 	return 0;
25513abe0bbSMikko Perttunen }
25613abe0bbSMikko Perttunen 
submit_process_bufs(struct tegra_drm_context * context,struct gather_bo * bo,struct drm_tegra_channel_submit * args,struct tegra_drm_submit_data * job_data)25713abe0bbSMikko Perttunen static int submit_process_bufs(struct tegra_drm_context *context, struct gather_bo *bo,
25813abe0bbSMikko Perttunen 			       struct drm_tegra_channel_submit *args,
25913abe0bbSMikko Perttunen 			       struct tegra_drm_submit_data *job_data)
26013abe0bbSMikko Perttunen {
26113abe0bbSMikko Perttunen 	struct tegra_drm_used_mapping *mappings;
26213abe0bbSMikko Perttunen 	struct drm_tegra_submit_buf *bufs;
26313abe0bbSMikko Perttunen 	int err;
26413abe0bbSMikko Perttunen 	u32 i;
26513abe0bbSMikko Perttunen 
26613abe0bbSMikko Perttunen 	bufs = alloc_copy_user_array(u64_to_user_ptr(args->bufs_ptr), args->num_bufs,
26713abe0bbSMikko Perttunen 				     sizeof(*bufs));
26813abe0bbSMikko Perttunen 	if (IS_ERR(bufs)) {
26913abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "failed to copy bufs array from userspace");
27013abe0bbSMikko Perttunen 		return PTR_ERR(bufs);
27113abe0bbSMikko Perttunen 	}
27213abe0bbSMikko Perttunen 
27313abe0bbSMikko Perttunen 	mappings = kcalloc(args->num_bufs, sizeof(*mappings), GFP_KERNEL);
27413abe0bbSMikko Perttunen 	if (!mappings) {
27513abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "failed to allocate memory for mapping info");
27613abe0bbSMikko Perttunen 		err = -ENOMEM;
27713abe0bbSMikko Perttunen 		goto done;
27813abe0bbSMikko Perttunen 	}
27913abe0bbSMikko Perttunen 
28013abe0bbSMikko Perttunen 	for (i = 0; i < args->num_bufs; i++) {
28113abe0bbSMikko Perttunen 		struct drm_tegra_submit_buf *buf = &bufs[i];
28213abe0bbSMikko Perttunen 		struct tegra_drm_mapping *mapping;
28313abe0bbSMikko Perttunen 
28413abe0bbSMikko Perttunen 		if (buf->flags & ~DRM_TEGRA_SUBMIT_RELOC_SECTOR_LAYOUT) {
28513abe0bbSMikko Perttunen 			SUBMIT_ERR(context, "invalid flag specified for buffer");
28613abe0bbSMikko Perttunen 			err = -EINVAL;
28713abe0bbSMikko Perttunen 			goto drop_refs;
28813abe0bbSMikko Perttunen 		}
28913abe0bbSMikko Perttunen 
29013abe0bbSMikko Perttunen 		mapping = tegra_drm_mapping_get(context, buf->mapping);
29113abe0bbSMikko Perttunen 		if (!mapping) {
29213abe0bbSMikko Perttunen 			SUBMIT_ERR(context, "invalid mapping ID '%u' for buffer", buf->mapping);
29313abe0bbSMikko Perttunen 			err = -EINVAL;
29413abe0bbSMikko Perttunen 			goto drop_refs;
29513abe0bbSMikko Perttunen 		}
29613abe0bbSMikko Perttunen 
29713abe0bbSMikko Perttunen 		err = submit_write_reloc(context, bo, buf, mapping);
29813abe0bbSMikko Perttunen 		if (err) {
29913abe0bbSMikko Perttunen 			tegra_drm_mapping_put(mapping);
30013abe0bbSMikko Perttunen 			goto drop_refs;
30113abe0bbSMikko Perttunen 		}
30213abe0bbSMikko Perttunen 
30313abe0bbSMikko Perttunen 		mappings[i].mapping = mapping;
30413abe0bbSMikko Perttunen 		mappings[i].flags = buf->flags;
30513abe0bbSMikko Perttunen 	}
30613abe0bbSMikko Perttunen 
30713abe0bbSMikko Perttunen 	job_data->used_mappings = mappings;
30813abe0bbSMikko Perttunen 	job_data->num_used_mappings = i;
30913abe0bbSMikko Perttunen 
31013abe0bbSMikko Perttunen 	err = 0;
31113abe0bbSMikko Perttunen 
31213abe0bbSMikko Perttunen 	goto done;
31313abe0bbSMikko Perttunen 
31413abe0bbSMikko Perttunen drop_refs:
31513abe0bbSMikko Perttunen 	while (i--)
31613abe0bbSMikko Perttunen 		tegra_drm_mapping_put(mappings[i].mapping);
31713abe0bbSMikko Perttunen 
31813abe0bbSMikko Perttunen 	kfree(mappings);
31913abe0bbSMikko Perttunen 	job_data->used_mappings = NULL;
32013abe0bbSMikko Perttunen 
32113abe0bbSMikko Perttunen done:
32213abe0bbSMikko Perttunen 	kvfree(bufs);
32313abe0bbSMikko Perttunen 
32413abe0bbSMikko Perttunen 	return err;
32513abe0bbSMikko Perttunen }
32613abe0bbSMikko Perttunen 
submit_get_syncpt(struct tegra_drm_context * context,struct host1x_job * job,struct xarray * syncpoints,struct drm_tegra_channel_submit * args)32713abe0bbSMikko Perttunen static int submit_get_syncpt(struct tegra_drm_context *context, struct host1x_job *job,
32813abe0bbSMikko Perttunen 			     struct xarray *syncpoints, struct drm_tegra_channel_submit *args)
32913abe0bbSMikko Perttunen {
33013abe0bbSMikko Perttunen 	struct host1x_syncpt *sp;
33113abe0bbSMikko Perttunen 
33213abe0bbSMikko Perttunen 	if (args->syncpt.flags) {
33313abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "invalid flag specified for syncpt");
33413abe0bbSMikko Perttunen 		return -EINVAL;
33513abe0bbSMikko Perttunen 	}
33613abe0bbSMikko Perttunen 
33713abe0bbSMikko Perttunen 	/* Syncpt ref will be dropped on job release */
33813abe0bbSMikko Perttunen 	sp = xa_load(syncpoints, args->syncpt.id);
33913abe0bbSMikko Perttunen 	if (!sp) {
34013abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "syncpoint specified in syncpt was not allocated");
34113abe0bbSMikko Perttunen 		return -EINVAL;
34213abe0bbSMikko Perttunen 	}
34313abe0bbSMikko Perttunen 
34413abe0bbSMikko Perttunen 	job->syncpt = host1x_syncpt_get(sp);
34513abe0bbSMikko Perttunen 	job->syncpt_incrs = args->syncpt.increments;
34613abe0bbSMikko Perttunen 
34713abe0bbSMikko Perttunen 	return 0;
34813abe0bbSMikko Perttunen }
34913abe0bbSMikko Perttunen 
submit_job_add_gather(struct host1x_job * job,struct tegra_drm_context * context,struct drm_tegra_submit_cmd_gather_uptr * cmd,struct gather_bo * bo,u32 * offset,struct tegra_drm_submit_data * job_data,u32 * class)35013abe0bbSMikko Perttunen static int submit_job_add_gather(struct host1x_job *job, struct tegra_drm_context *context,
35113abe0bbSMikko Perttunen 				 struct drm_tegra_submit_cmd_gather_uptr *cmd,
35213abe0bbSMikko Perttunen 				 struct gather_bo *bo, u32 *offset,
3538cc95f3fSMikko Perttunen 				 struct tegra_drm_submit_data *job_data,
3548cc95f3fSMikko Perttunen 				 u32 *class)
35513abe0bbSMikko Perttunen {
35613abe0bbSMikko Perttunen 	u32 next_offset;
35713abe0bbSMikko Perttunen 
35813abe0bbSMikko Perttunen 	if (cmd->reserved[0] || cmd->reserved[1] || cmd->reserved[2]) {
35913abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "non-zero reserved field in GATHER_UPTR command");
36013abe0bbSMikko Perttunen 		return -EINVAL;
36113abe0bbSMikko Perttunen 	}
36213abe0bbSMikko Perttunen 
36313abe0bbSMikko Perttunen 	/* Check for maximum gather size */
36413abe0bbSMikko Perttunen 	if (cmd->words > 16383) {
36513abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "too many words in GATHER_UPTR command");
36613abe0bbSMikko Perttunen 		return -EINVAL;
36713abe0bbSMikko Perttunen 	}
36813abe0bbSMikko Perttunen 
36913abe0bbSMikko Perttunen 	if (check_add_overflow(*offset, cmd->words, &next_offset)) {
37013abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "too many total words in job");
37113abe0bbSMikko Perttunen 		return -EINVAL;
37213abe0bbSMikko Perttunen 	}
37313abe0bbSMikko Perttunen 
37413abe0bbSMikko Perttunen 	if (next_offset > bo->gather_data_words) {
37513abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "GATHER_UPTR command overflows gather data");
37613abe0bbSMikko Perttunen 		return -EINVAL;
37713abe0bbSMikko Perttunen 	}
37813abe0bbSMikko Perttunen 
3798cc95f3fSMikko Perttunen 	if (tegra_drm_fw_validate(context->client, bo->gather_data, *offset,
3808cc95f3fSMikko Perttunen 				  cmd->words, job_data, class)) {
3818cc95f3fSMikko Perttunen 		SUBMIT_ERR(context, "job was rejected by firewall");
3828cc95f3fSMikko Perttunen 		return -EINVAL;
3838cc95f3fSMikko Perttunen 	}
3848cc95f3fSMikko Perttunen 
38513abe0bbSMikko Perttunen 	host1x_job_add_gather(job, &bo->base, cmd->words, *offset * 4);
38613abe0bbSMikko Perttunen 
38713abe0bbSMikko Perttunen 	*offset = next_offset;
38813abe0bbSMikko Perttunen 
38913abe0bbSMikko Perttunen 	return 0;
39013abe0bbSMikko Perttunen }
39113abe0bbSMikko Perttunen 
39213abe0bbSMikko Perttunen static struct host1x_job *
submit_create_job(struct tegra_drm_context * context,struct gather_bo * bo,struct drm_tegra_channel_submit * args,struct tegra_drm_submit_data * job_data,struct xarray * syncpoints)39313abe0bbSMikko Perttunen submit_create_job(struct tegra_drm_context *context, struct gather_bo *bo,
39413abe0bbSMikko Perttunen 		  struct drm_tegra_channel_submit *args, struct tegra_drm_submit_data *job_data,
39513abe0bbSMikko Perttunen 		  struct xarray *syncpoints)
39613abe0bbSMikko Perttunen {
39713abe0bbSMikko Perttunen 	struct drm_tegra_submit_cmd *cmds;
39813abe0bbSMikko Perttunen 	u32 i, gather_offset = 0, class;
39913abe0bbSMikko Perttunen 	struct host1x_job *job;
40013abe0bbSMikko Perttunen 	int err;
40113abe0bbSMikko Perttunen 
40213abe0bbSMikko Perttunen 	/* Set initial class for firewall. */
40313abe0bbSMikko Perttunen 	class = context->client->base.class;
40413abe0bbSMikko Perttunen 
40513abe0bbSMikko Perttunen 	cmds = alloc_copy_user_array(u64_to_user_ptr(args->cmds_ptr), args->num_cmds,
40613abe0bbSMikko Perttunen 				     sizeof(*cmds));
40713abe0bbSMikko Perttunen 	if (IS_ERR(cmds)) {
40813abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "failed to copy cmds array from userspace");
40913abe0bbSMikko Perttunen 		return ERR_CAST(cmds);
41013abe0bbSMikko Perttunen 	}
41113abe0bbSMikko Perttunen 
41213abe0bbSMikko Perttunen 	job = host1x_job_alloc(context->channel, args->num_cmds, 0, true);
41313abe0bbSMikko Perttunen 	if (!job) {
41413abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "failed to allocate memory for job");
41513abe0bbSMikko Perttunen 		job = ERR_PTR(-ENOMEM);
41613abe0bbSMikko Perttunen 		goto done;
41713abe0bbSMikko Perttunen 	}
41813abe0bbSMikko Perttunen 
41913abe0bbSMikko Perttunen 	err = submit_get_syncpt(context, job, syncpoints, args);
42013abe0bbSMikko Perttunen 	if (err < 0)
42113abe0bbSMikko Perttunen 		goto free_job;
42213abe0bbSMikko Perttunen 
42313abe0bbSMikko Perttunen 	job->client = &context->client->base;
42413abe0bbSMikko Perttunen 	job->class = context->client->base.class;
42513abe0bbSMikko Perttunen 	job->serialize = true;
42613abe0bbSMikko Perttunen 
42713abe0bbSMikko Perttunen 	for (i = 0; i < args->num_cmds; i++) {
42813abe0bbSMikko Perttunen 		struct drm_tegra_submit_cmd *cmd = &cmds[i];
42913abe0bbSMikko Perttunen 
43013abe0bbSMikko Perttunen 		if (cmd->flags) {
43113abe0bbSMikko Perttunen 			SUBMIT_ERR(context, "unknown flags given for cmd");
43213abe0bbSMikko Perttunen 			err = -EINVAL;
43313abe0bbSMikko Perttunen 			goto free_job;
43413abe0bbSMikko Perttunen 		}
43513abe0bbSMikko Perttunen 
43613abe0bbSMikko Perttunen 		if (cmd->type == DRM_TEGRA_SUBMIT_CMD_GATHER_UPTR) {
43713abe0bbSMikko Perttunen 			err = submit_job_add_gather(job, context, &cmd->gather_uptr, bo,
4388cc95f3fSMikko Perttunen 						    &gather_offset, job_data, &class);
43913abe0bbSMikko Perttunen 			if (err)
44013abe0bbSMikko Perttunen 				goto free_job;
44113abe0bbSMikko Perttunen 		} else if (cmd->type == DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT) {
44213abe0bbSMikko Perttunen 			if (cmd->wait_syncpt.reserved[0] || cmd->wait_syncpt.reserved[1]) {
44313abe0bbSMikko Perttunen 				SUBMIT_ERR(context, "non-zero reserved value");
44413abe0bbSMikko Perttunen 				err = -EINVAL;
44513abe0bbSMikko Perttunen 				goto free_job;
44613abe0bbSMikko Perttunen 			}
44713abe0bbSMikko Perttunen 
44813abe0bbSMikko Perttunen 			host1x_job_add_wait(job, cmd->wait_syncpt.id, cmd->wait_syncpt.value,
44913abe0bbSMikko Perttunen 					    false, class);
45013abe0bbSMikko Perttunen 		} else if (cmd->type == DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT_RELATIVE) {
45113abe0bbSMikko Perttunen 			if (cmd->wait_syncpt.reserved[0] || cmd->wait_syncpt.reserved[1]) {
45213abe0bbSMikko Perttunen 				SUBMIT_ERR(context, "non-zero reserved value");
45313abe0bbSMikko Perttunen 				err = -EINVAL;
45413abe0bbSMikko Perttunen 				goto free_job;
45513abe0bbSMikko Perttunen 			}
45613abe0bbSMikko Perttunen 
45713abe0bbSMikko Perttunen 			if (cmd->wait_syncpt.id != args->syncpt.id) {
45813abe0bbSMikko Perttunen 				SUBMIT_ERR(context, "syncpoint ID in CMD_WAIT_SYNCPT_RELATIVE is not used by the job");
45913abe0bbSMikko Perttunen 				err = -EINVAL;
46013abe0bbSMikko Perttunen 				goto free_job;
46113abe0bbSMikko Perttunen 			}
46213abe0bbSMikko Perttunen 
46313abe0bbSMikko Perttunen 			host1x_job_add_wait(job, cmd->wait_syncpt.id, cmd->wait_syncpt.value,
46413abe0bbSMikko Perttunen 					    true, class);
46513abe0bbSMikko Perttunen 		} else {
46613abe0bbSMikko Perttunen 			SUBMIT_ERR(context, "unknown cmd type");
46713abe0bbSMikko Perttunen 			err = -EINVAL;
46813abe0bbSMikko Perttunen 			goto free_job;
46913abe0bbSMikko Perttunen 		}
47013abe0bbSMikko Perttunen 	}
47113abe0bbSMikko Perttunen 
47213abe0bbSMikko Perttunen 	if (gather_offset == 0) {
47313abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "job must have at least one gather");
47413abe0bbSMikko Perttunen 		err = -EINVAL;
47513abe0bbSMikko Perttunen 		goto free_job;
47613abe0bbSMikko Perttunen 	}
47713abe0bbSMikko Perttunen 
47813abe0bbSMikko Perttunen 	goto done;
47913abe0bbSMikko Perttunen 
48013abe0bbSMikko Perttunen free_job:
48113abe0bbSMikko Perttunen 	host1x_job_put(job);
48213abe0bbSMikko Perttunen 	job = ERR_PTR(err);
48313abe0bbSMikko Perttunen 
48413abe0bbSMikko Perttunen done:
48513abe0bbSMikko Perttunen 	kvfree(cmds);
48613abe0bbSMikko Perttunen 
48713abe0bbSMikko Perttunen 	return job;
48813abe0bbSMikko Perttunen }
48913abe0bbSMikko Perttunen 
release_job(struct host1x_job * job)49013abe0bbSMikko Perttunen static void release_job(struct host1x_job *job)
49113abe0bbSMikko Perttunen {
49213abe0bbSMikko Perttunen 	struct tegra_drm_client *client = container_of(job->client, struct tegra_drm_client, base);
49313abe0bbSMikko Perttunen 	struct tegra_drm_submit_data *job_data = job->user_data;
49413abe0bbSMikko Perttunen 	u32 i;
49513abe0bbSMikko Perttunen 
496e09db978SMikko Perttunen 	if (job->memory_context)
497e09db978SMikko Perttunen 		host1x_memory_context_put(job->memory_context);
498e09db978SMikko Perttunen 
49913abe0bbSMikko Perttunen 	for (i = 0; i < job_data->num_used_mappings; i++)
50013abe0bbSMikko Perttunen 		tegra_drm_mapping_put(job_data->used_mappings[i].mapping);
50113abe0bbSMikko Perttunen 
50213abe0bbSMikko Perttunen 	kfree(job_data->used_mappings);
50313abe0bbSMikko Perttunen 	kfree(job_data);
50413abe0bbSMikko Perttunen 
505a21115ddSDmitry Osipenko 	pm_runtime_mark_last_busy(client->base.dev);
50613abe0bbSMikko Perttunen 	pm_runtime_put_autosuspend(client->base.dev);
50713abe0bbSMikko Perttunen }
50813abe0bbSMikko Perttunen 
tegra_drm_ioctl_channel_submit(struct drm_device * drm,void * data,struct drm_file * file)50913abe0bbSMikko Perttunen int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
51013abe0bbSMikko Perttunen 				   struct drm_file *file)
51113abe0bbSMikko Perttunen {
51213abe0bbSMikko Perttunen 	struct tegra_drm_file *fpriv = file->driver_priv;
51313abe0bbSMikko Perttunen 	struct drm_tegra_channel_submit *args = data;
51413abe0bbSMikko Perttunen 	struct tegra_drm_submit_data *job_data;
51513abe0bbSMikko Perttunen 	struct drm_syncobj *syncobj = NULL;
51613abe0bbSMikko Perttunen 	struct tegra_drm_context *context;
51713abe0bbSMikko Perttunen 	struct host1x_job *job;
51813abe0bbSMikko Perttunen 	struct gather_bo *bo;
51913abe0bbSMikko Perttunen 	u32 i;
52013abe0bbSMikko Perttunen 	int err;
52113abe0bbSMikko Perttunen 
52213abe0bbSMikko Perttunen 	mutex_lock(&fpriv->lock);
52313abe0bbSMikko Perttunen 
52413abe0bbSMikko Perttunen 	context = xa_load(&fpriv->contexts, args->context);
52513abe0bbSMikko Perttunen 	if (!context) {
52613abe0bbSMikko Perttunen 		mutex_unlock(&fpriv->lock);
52713abe0bbSMikko Perttunen 		pr_err_ratelimited("%s: %s: invalid channel context '%#x'", __func__,
52813abe0bbSMikko Perttunen 				   current->comm, args->context);
52913abe0bbSMikko Perttunen 		return -EINVAL;
53013abe0bbSMikko Perttunen 	}
53113abe0bbSMikko Perttunen 
53213abe0bbSMikko Perttunen 	if (args->syncobj_in) {
53313abe0bbSMikko Perttunen 		struct dma_fence *fence;
53413abe0bbSMikko Perttunen 
53513abe0bbSMikko Perttunen 		err = drm_syncobj_find_fence(file, args->syncobj_in, 0, 0, &fence);
53613abe0bbSMikko Perttunen 		if (err) {
53713abe0bbSMikko Perttunen 			SUBMIT_ERR(context, "invalid syncobj_in '%#x'", args->syncobj_in);
53813abe0bbSMikko Perttunen 			goto unlock;
53913abe0bbSMikko Perttunen 		}
54013abe0bbSMikko Perttunen 
54113abe0bbSMikko Perttunen 		err = dma_fence_wait_timeout(fence, true, msecs_to_jiffies(10000));
54213abe0bbSMikko Perttunen 		dma_fence_put(fence);
54313abe0bbSMikko Perttunen 		if (err) {
54413abe0bbSMikko Perttunen 			SUBMIT_ERR(context, "wait for syncobj_in timed out");
54513abe0bbSMikko Perttunen 			goto unlock;
54613abe0bbSMikko Perttunen 		}
54713abe0bbSMikko Perttunen 	}
54813abe0bbSMikko Perttunen 
54913abe0bbSMikko Perttunen 	if (args->syncobj_out) {
55013abe0bbSMikko Perttunen 		syncobj = drm_syncobj_find(file, args->syncobj_out);
55113abe0bbSMikko Perttunen 		if (!syncobj) {
55213abe0bbSMikko Perttunen 			SUBMIT_ERR(context, "invalid syncobj_out '%#x'", args->syncobj_out);
55313abe0bbSMikko Perttunen 			err = -ENOENT;
55413abe0bbSMikko Perttunen 			goto unlock;
55513abe0bbSMikko Perttunen 		}
55613abe0bbSMikko Perttunen 	}
55713abe0bbSMikko Perttunen 
55813abe0bbSMikko Perttunen 	/* Allocate gather BO and copy gather words in. */
55913abe0bbSMikko Perttunen 	err = submit_copy_gather_data(&bo, drm->dev, context, args);
56013abe0bbSMikko Perttunen 	if (err)
56113abe0bbSMikko Perttunen 		goto unlock;
56213abe0bbSMikko Perttunen 
56313abe0bbSMikko Perttunen 	job_data = kzalloc(sizeof(*job_data), GFP_KERNEL);
56413abe0bbSMikko Perttunen 	if (!job_data) {
56513abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "failed to allocate memory for job data");
56613abe0bbSMikko Perttunen 		err = -ENOMEM;
56713abe0bbSMikko Perttunen 		goto put_bo;
56813abe0bbSMikko Perttunen 	}
56913abe0bbSMikko Perttunen 
57013abe0bbSMikko Perttunen 	/* Get data buffer mappings and do relocation patching. */
57113abe0bbSMikko Perttunen 	err = submit_process_bufs(context, bo, args, job_data);
57213abe0bbSMikko Perttunen 	if (err)
57313abe0bbSMikko Perttunen 		goto free_job_data;
57413abe0bbSMikko Perttunen 
57513abe0bbSMikko Perttunen 	/* Allocate host1x_job and add gathers and waits to it. */
57613abe0bbSMikko Perttunen 	job = submit_create_job(context, bo, args, job_data, &fpriv->syncpoints);
57713abe0bbSMikko Perttunen 	if (IS_ERR(job)) {
57813abe0bbSMikko Perttunen 		err = PTR_ERR(job);
57913abe0bbSMikko Perttunen 		goto free_job_data;
58013abe0bbSMikko Perttunen 	}
58113abe0bbSMikko Perttunen 
58213abe0bbSMikko Perttunen 	/* Map gather data for Host1x. */
58313abe0bbSMikko Perttunen 	err = host1x_job_pin(job, context->client->base.dev);
58413abe0bbSMikko Perttunen 	if (err) {
58513abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "failed to pin job: %d", err);
58613abe0bbSMikko Perttunen 		goto put_job;
58713abe0bbSMikko Perttunen 	}
58813abe0bbSMikko Perttunen 
589e09db978SMikko Perttunen 	if (context->client->ops->get_streamid_offset) {
590e09db978SMikko Perttunen 		err = context->client->ops->get_streamid_offset(
591e09db978SMikko Perttunen 			context->client, &job->engine_streamid_offset);
592e09db978SMikko Perttunen 		if (err) {
593e09db978SMikko Perttunen 			SUBMIT_ERR(context, "failed to get streamid offset: %d", err);
594e09db978SMikko Perttunen 			goto unpin_job;
595e09db978SMikko Perttunen 		}
596e09db978SMikko Perttunen 	}
597e09db978SMikko Perttunen 
598e09db978SMikko Perttunen 	if (context->memory_context && context->client->ops->can_use_memory_ctx) {
599e09db978SMikko Perttunen 		bool supported;
600e09db978SMikko Perttunen 
601e09db978SMikko Perttunen 		err = context->client->ops->can_use_memory_ctx(context->client, &supported);
602e09db978SMikko Perttunen 		if (err) {
603e09db978SMikko Perttunen 			SUBMIT_ERR(context, "failed to detect if engine can use memory context: %d", err);
604e09db978SMikko Perttunen 			goto unpin_job;
605e09db978SMikko Perttunen 		}
606e09db978SMikko Perttunen 
607e09db978SMikko Perttunen 		if (supported) {
608e09db978SMikko Perttunen 			job->memory_context = context->memory_context;
609e09db978SMikko Perttunen 			host1x_memory_context_get(job->memory_context);
610e09db978SMikko Perttunen 		}
611e09db978SMikko Perttunen 	} else if (context->client->ops->get_streamid_offset) {
612e09db978SMikko Perttunen 		/*
613e09db978SMikko Perttunen 		 * Job submission will need to temporarily change stream ID,
614e09db978SMikko Perttunen 		 * so need to tell it what to change it back to.
615e09db978SMikko Perttunen 		 */
616*b8cbb04fSThierry Reding 		if (!tegra_dev_iommu_get_stream_id(context->client->base.dev,
617*b8cbb04fSThierry Reding 						   &job->engine_fallback_streamid))
618*b8cbb04fSThierry Reding 			job->engine_fallback_streamid = TEGRA_STREAM_ID_BYPASS;
619e09db978SMikko Perttunen 	}
620e09db978SMikko Perttunen 
62113abe0bbSMikko Perttunen 	/* Boot engine. */
62213abe0bbSMikko Perttunen 	err = pm_runtime_resume_and_get(context->client->base.dev);
62313abe0bbSMikko Perttunen 	if (err < 0) {
62413abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "could not power up engine: %d", err);
625e09db978SMikko Perttunen 		goto put_memory_context;
62613abe0bbSMikko Perttunen 	}
62713abe0bbSMikko Perttunen 
62813abe0bbSMikko Perttunen 	job->user_data = job_data;
62913abe0bbSMikko Perttunen 	job->release = release_job;
63013abe0bbSMikko Perttunen 	job->timeout = 10000;
63113abe0bbSMikko Perttunen 
63213abe0bbSMikko Perttunen 	/*
63313abe0bbSMikko Perttunen 	 * job_data is now part of job reference counting, so don't release
63413abe0bbSMikko Perttunen 	 * it from here.
63513abe0bbSMikko Perttunen 	 */
63613abe0bbSMikko Perttunen 	job_data = NULL;
63713abe0bbSMikko Perttunen 
63813abe0bbSMikko Perttunen 	/* Submit job to hardware. */
63913abe0bbSMikko Perttunen 	err = host1x_job_submit(job);
64013abe0bbSMikko Perttunen 	if (err) {
64113abe0bbSMikko Perttunen 		SUBMIT_ERR(context, "host1x job submission failed: %d", err);
64213abe0bbSMikko Perttunen 		goto unpin_job;
64313abe0bbSMikko Perttunen 	}
64413abe0bbSMikko Perttunen 
64513abe0bbSMikko Perttunen 	/* Return postfences to userspace and add fences to DMA reservations. */
64613abe0bbSMikko Perttunen 	args->syncpt.value = job->syncpt_end;
64713abe0bbSMikko Perttunen 
64813abe0bbSMikko Perttunen 	if (syncobj) {
649d5179020SMikko Perttunen 		struct dma_fence *fence = host1x_fence_create(job->syncpt, job->syncpt_end, true);
65013abe0bbSMikko Perttunen 		if (IS_ERR(fence)) {
65113abe0bbSMikko Perttunen 			err = PTR_ERR(fence);
65213abe0bbSMikko Perttunen 			SUBMIT_ERR(context, "failed to create postfence: %d", err);
65313abe0bbSMikko Perttunen 		}
65413abe0bbSMikko Perttunen 
65513abe0bbSMikko Perttunen 		drm_syncobj_replace_fence(syncobj, fence);
65613abe0bbSMikko Perttunen 	}
65713abe0bbSMikko Perttunen 
65813abe0bbSMikko Perttunen 	goto put_job;
65913abe0bbSMikko Perttunen 
660e09db978SMikko Perttunen put_memory_context:
661e09db978SMikko Perttunen 	if (job->memory_context)
662e09db978SMikko Perttunen 		host1x_memory_context_put(job->memory_context);
66313abe0bbSMikko Perttunen unpin_job:
66413abe0bbSMikko Perttunen 	host1x_job_unpin(job);
66513abe0bbSMikko Perttunen put_job:
66613abe0bbSMikko Perttunen 	host1x_job_put(job);
66713abe0bbSMikko Perttunen free_job_data:
66813abe0bbSMikko Perttunen 	if (job_data && job_data->used_mappings) {
66913abe0bbSMikko Perttunen 		for (i = 0; i < job_data->num_used_mappings; i++)
67013abe0bbSMikko Perttunen 			tegra_drm_mapping_put(job_data->used_mappings[i].mapping);
67113abe0bbSMikko Perttunen 
67213abe0bbSMikko Perttunen 		kfree(job_data->used_mappings);
67313abe0bbSMikko Perttunen 	}
67413abe0bbSMikko Perttunen 
67513abe0bbSMikko Perttunen 	kfree(job_data);
67613abe0bbSMikko Perttunen put_bo:
67713abe0bbSMikko Perttunen 	gather_bo_put(&bo->base);
67813abe0bbSMikko Perttunen unlock:
67913abe0bbSMikko Perttunen 	if (syncobj)
68013abe0bbSMikko Perttunen 		drm_syncobj_put(syncobj);
68113abe0bbSMikko Perttunen 
68213abe0bbSMikko Perttunen 	mutex_unlock(&fpriv->lock);
68313abe0bbSMikko Perttunen 	return err;
68413abe0bbSMikko Perttunen }
685