xref: /openbmc/linux/drivers/gpu/drm/tegra/drm.c (revision 816ffd28002651a469e86d1118a225862e392ecd)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2dee8268fSThierry Reding /*
3dee8268fSThierry Reding  * Copyright (C) 2012 Avionic Design GmbH
4ad926015SMikko Perttunen  * Copyright (C) 2012-2016 NVIDIA CORPORATION.  All rights reserved.
5dee8268fSThierry Reding  */
6dee8268fSThierry Reding 
7ad926015SMikko Perttunen #include <linux/bitops.h>
8dee8268fSThierry Reding #include <linux/host1x.h>
9bdd2f9cdSThierry Reding #include <linux/idr.h>
10df06b759SThierry Reding #include <linux/iommu.h>
11eb1df694SSam Ravnborg #include <linux/module.h>
12eb1df694SSam Ravnborg #include <linux/platform_device.h>
1358ed47adSDmitry Osipenko #include <linux/pm_runtime.h>
14dee8268fSThierry Reding 
156848c291SThomas Zimmermann #include <drm/drm_aperture.h>
161503ca47SThierry Reding #include <drm/drm_atomic.h>
1707866963SThierry Reding #include <drm/drm_atomic_helper.h>
18eb1df694SSam Ravnborg #include <drm/drm_debugfs.h>
19eb1df694SSam Ravnborg #include <drm/drm_drv.h>
20eb1df694SSam Ravnborg #include <drm/drm_fourcc.h>
21720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
22eb1df694SSam Ravnborg #include <drm/drm_ioctl.h>
23eb1df694SSam Ravnborg #include <drm/drm_prime.h>
24eb1df694SSam Ravnborg #include <drm/drm_vblank.h>
2507866963SThierry Reding 
26d210919dSDmitry Osipenko #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
27d210919dSDmitry Osipenko #include <asm/dma-iommu.h>
28d210919dSDmitry Osipenko #endif
29d210919dSDmitry Osipenko 
3004d5d5dfSDmitry Osipenko #include "dc.h"
31dee8268fSThierry Reding #include "drm.h"
32dee8268fSThierry Reding #include "gem.h"
3304d5d5dfSDmitry Osipenko #include "uapi.h"
34dee8268fSThierry Reding 
35dee8268fSThierry Reding #define DRIVER_NAME "tegra"
36dee8268fSThierry Reding #define DRIVER_DESC "NVIDIA Tegra graphics"
37dee8268fSThierry Reding #define DRIVER_DATE "20120330"
38ef531d01SMikko Perttunen #define DRIVER_MAJOR 1
39dee8268fSThierry Reding #define DRIVER_MINOR 0
40dee8268fSThierry Reding #define DRIVER_PATCHLEVEL 0
41dee8268fSThierry Reding 
42ad926015SMikko Perttunen #define CARVEOUT_SZ SZ_64M
43368f622cSDmitry Osipenko #define CDMA_GATHER_FETCHES_MAX_NB 16383
44ad926015SMikko Perttunen 
tegra_atomic_check(struct drm_device * drm,struct drm_atomic_state * state)45ab7d3f58SThierry Reding static int tegra_atomic_check(struct drm_device *drm,
461503ca47SThierry Reding 			      struct drm_atomic_state *state)
471503ca47SThierry Reding {
481503ca47SThierry Reding 	int err;
491503ca47SThierry Reding 
50a18301b9SPeter Ujfalusi 	err = drm_atomic_helper_check(drm, state);
51ab7d3f58SThierry Reding 	if (err < 0)
521503ca47SThierry Reding 		return err;
531503ca47SThierry Reding 
54a18301b9SPeter Ujfalusi 	return tegra_display_hub_atomic_check(drm, state);
551503ca47SThierry Reding }
561503ca47SThierry Reding 
5731b02caeSThierry Reding static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = {
58f9914214SThierry Reding 	.fb_create = tegra_fb_create,
59ab7d3f58SThierry Reding 	.atomic_check = tegra_atomic_check,
6031b02caeSThierry Reding 	.atomic_commit = drm_atomic_helper_commit,
6131b02caeSThierry Reding };
6231b02caeSThierry Reding 
tegra_atomic_post_commit(struct drm_device * drm,struct drm_atomic_state * old_state)6304d5d5dfSDmitry Osipenko static void tegra_atomic_post_commit(struct drm_device *drm,
6404d5d5dfSDmitry Osipenko 				     struct drm_atomic_state *old_state)
6504d5d5dfSDmitry Osipenko {
6604d5d5dfSDmitry Osipenko 	struct drm_crtc_state *old_crtc_state __maybe_unused;
6704d5d5dfSDmitry Osipenko 	struct drm_crtc *crtc;
6804d5d5dfSDmitry Osipenko 	unsigned int i;
6904d5d5dfSDmitry Osipenko 
7004d5d5dfSDmitry Osipenko 	for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i)
7104d5d5dfSDmitry Osipenko 		tegra_crtc_atomic_post_commit(crtc, old_state);
7204d5d5dfSDmitry Osipenko }
7304d5d5dfSDmitry Osipenko 
tegra_atomic_commit_tail(struct drm_atomic_state * old_state)74c4755fb9SThierry Reding static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state)
75c4755fb9SThierry Reding {
76c4755fb9SThierry Reding 	struct drm_device *drm = old_state->dev;
77c4755fb9SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
78c4755fb9SThierry Reding 
79c4755fb9SThierry Reding 	if (tegra->hub) {
80a1891b91SDaniel Vetter 		bool fence_cookie = dma_fence_begin_signalling();
81a1891b91SDaniel Vetter 
82c4755fb9SThierry Reding 		drm_atomic_helper_commit_modeset_disables(drm, old_state);
83c4755fb9SThierry Reding 		tegra_display_hub_atomic_commit(drm, old_state);
84c4755fb9SThierry Reding 		drm_atomic_helper_commit_planes(drm, old_state, 0);
85c4755fb9SThierry Reding 		drm_atomic_helper_commit_modeset_enables(drm, old_state);
86c4755fb9SThierry Reding 		drm_atomic_helper_commit_hw_done(old_state);
87a1891b91SDaniel Vetter 		dma_fence_end_signalling(fence_cookie);
88c4755fb9SThierry Reding 		drm_atomic_helper_wait_for_vblanks(drm, old_state);
89c4755fb9SThierry Reding 		drm_atomic_helper_cleanup_planes(drm, old_state);
90c4755fb9SThierry Reding 	} else {
91c4755fb9SThierry Reding 		drm_atomic_helper_commit_tail_rpm(old_state);
92c4755fb9SThierry Reding 	}
9304d5d5dfSDmitry Osipenko 
9404d5d5dfSDmitry Osipenko 	tegra_atomic_post_commit(drm, old_state);
95c4755fb9SThierry Reding }
96c4755fb9SThierry Reding 
9731b02caeSThierry Reding static const struct drm_mode_config_helper_funcs
9831b02caeSThierry Reding tegra_drm_mode_config_helpers = {
99c4755fb9SThierry Reding 	.atomic_commit_tail = tegra_atomic_commit_tail,
100f9914214SThierry Reding };
101f9914214SThierry Reding 
tegra_drm_open(struct drm_device * drm,struct drm_file * filp)102dee8268fSThierry Reding static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
103dee8268fSThierry Reding {
104dee8268fSThierry Reding 	struct tegra_drm_file *fpriv;
105dee8268fSThierry Reding 
106dee8268fSThierry Reding 	fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
107dee8268fSThierry Reding 	if (!fpriv)
108dee8268fSThierry Reding 		return -ENOMEM;
109dee8268fSThierry Reding 
110d7c591bcSMikko Perttunen 	idr_init_base(&fpriv->legacy_contexts, 1);
111d7c591bcSMikko Perttunen 	xa_init_flags(&fpriv->contexts, XA_FLAGS_ALLOC1);
112fc348336SMikko Perttunen 	xa_init(&fpriv->syncpoints);
113bdd2f9cdSThierry Reding 	mutex_init(&fpriv->lock);
114dee8268fSThierry Reding 	filp->driver_priv = fpriv;
115dee8268fSThierry Reding 
116dee8268fSThierry Reding 	return 0;
117dee8268fSThierry Reding }
118dee8268fSThierry Reding 
tegra_drm_context_free(struct tegra_drm_context * context)119dee8268fSThierry Reding static void tegra_drm_context_free(struct tegra_drm_context *context)
120dee8268fSThierry Reding {
121dee8268fSThierry Reding 	context->client->ops->close_channel(context);
12258ed47adSDmitry Osipenko 	pm_runtime_put(context->client->base.dev);
123dee8268fSThierry Reding 	kfree(context);
124dee8268fSThierry Reding }
125dee8268fSThierry Reding 
host1x_reloc_copy_from_user(struct host1x_reloc * dest,struct drm_tegra_reloc __user * src,struct drm_device * drm,struct drm_file * file)126961e3beaSThierry Reding static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
127961e3beaSThierry Reding 				       struct drm_tegra_reloc __user *src,
128961e3beaSThierry Reding 				       struct drm_device *drm,
129961e3beaSThierry Reding 				       struct drm_file *file)
130961e3beaSThierry Reding {
131961e3beaSThierry Reding 	u32 cmdbuf, target;
132961e3beaSThierry Reding 	int err;
133961e3beaSThierry Reding 
134961e3beaSThierry Reding 	err = get_user(cmdbuf, &src->cmdbuf.handle);
135961e3beaSThierry Reding 	if (err < 0)
136961e3beaSThierry Reding 		return err;
137961e3beaSThierry Reding 
138961e3beaSThierry Reding 	err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset);
139961e3beaSThierry Reding 	if (err < 0)
140961e3beaSThierry Reding 		return err;
141961e3beaSThierry Reding 
142961e3beaSThierry Reding 	err = get_user(target, &src->target.handle);
143961e3beaSThierry Reding 	if (err < 0)
144961e3beaSThierry Reding 		return err;
145961e3beaSThierry Reding 
14631f40f86SDavid Ung 	err = get_user(dest->target.offset, &src->target.offset);
147961e3beaSThierry Reding 	if (err < 0)
148961e3beaSThierry Reding 		return err;
149961e3beaSThierry Reding 
150961e3beaSThierry Reding 	err = get_user(dest->shift, &src->shift);
151961e3beaSThierry Reding 	if (err < 0)
152961e3beaSThierry Reding 		return err;
153961e3beaSThierry Reding 
154ab4f81bfSThierry Reding 	dest->flags = HOST1X_RELOC_READ | HOST1X_RELOC_WRITE;
155ab4f81bfSThierry Reding 
156f51632ccSMikko Perttunen 	dest->cmdbuf.bo = tegra_gem_lookup(file, cmdbuf);
157961e3beaSThierry Reding 	if (!dest->cmdbuf.bo)
158961e3beaSThierry Reding 		return -ENOENT;
159961e3beaSThierry Reding 
160f51632ccSMikko Perttunen 	dest->target.bo = tegra_gem_lookup(file, target);
161961e3beaSThierry Reding 	if (!dest->target.bo)
162961e3beaSThierry Reding 		return -ENOENT;
163961e3beaSThierry Reding 
164961e3beaSThierry Reding 	return 0;
165961e3beaSThierry Reding }
166961e3beaSThierry Reding 
tegra_drm_submit(struct tegra_drm_context * context,struct drm_tegra_submit * args,struct drm_device * drm,struct drm_file * file)167c40f0f1aSThierry Reding int tegra_drm_submit(struct tegra_drm_context *context,
168c40f0f1aSThierry Reding 		     struct drm_tegra_submit *args, struct drm_device *drm,
169c40f0f1aSThierry Reding 		     struct drm_file *file)
170c40f0f1aSThierry Reding {
171bf3d41ccSThierry Reding 	struct host1x_client *client = &context->client->base;
172c40f0f1aSThierry Reding 	unsigned int num_cmdbufs = args->num_cmdbufs;
173c40f0f1aSThierry Reding 	unsigned int num_relocs = args->num_relocs;
174a176c67dSMikko Perttunen 	struct drm_tegra_cmdbuf __user *user_cmdbufs;
175a176c67dSMikko Perttunen 	struct drm_tegra_reloc __user *user_relocs;
176a176c67dSMikko Perttunen 	struct drm_tegra_syncpt __user *user_syncpt;
177c40f0f1aSThierry Reding 	struct drm_tegra_syncpt syncpt;
178e0b2ce02SDmitry Osipenko 	struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
179ec73c4cfSDmitry Osipenko 	struct drm_gem_object **refs;
1802aed4f5aSMikko Perttunen 	struct host1x_syncpt *sp = NULL;
181c40f0f1aSThierry Reding 	struct host1x_job *job;
182ec73c4cfSDmitry Osipenko 	unsigned int num_refs;
183c40f0f1aSThierry Reding 	int err;
184c40f0f1aSThierry Reding 
185a176c67dSMikko Perttunen 	user_cmdbufs = u64_to_user_ptr(args->cmdbufs);
186a176c67dSMikko Perttunen 	user_relocs = u64_to_user_ptr(args->relocs);
187a176c67dSMikko Perttunen 	user_syncpt = u64_to_user_ptr(args->syncpts);
188a176c67dSMikko Perttunen 
189c40f0f1aSThierry Reding 	/* We don't yet support other than one syncpt_incr struct per submit */
190c40f0f1aSThierry Reding 	if (args->num_syncpts != 1)
191c40f0f1aSThierry Reding 		return -EINVAL;
192c40f0f1aSThierry Reding 
193d0fbbdffSDmitry Osipenko 	/* We don't yet support waitchks */
194d0fbbdffSDmitry Osipenko 	if (args->num_waitchks != 0)
195d0fbbdffSDmitry Osipenko 		return -EINVAL;
196d0fbbdffSDmitry Osipenko 
197c40f0f1aSThierry Reding 	job = host1x_job_alloc(context->channel, args->num_cmdbufs,
1980fddaa85SMikko Perttunen 			       args->num_relocs, false);
199c40f0f1aSThierry Reding 	if (!job)
200c40f0f1aSThierry Reding 		return -ENOMEM;
201c40f0f1aSThierry Reding 
202c40f0f1aSThierry Reding 	job->num_relocs = args->num_relocs;
203bf3d41ccSThierry Reding 	job->client = client;
204bf3d41ccSThierry Reding 	job->class = client->class;
205c40f0f1aSThierry Reding 	job->serialize = true;
206c78f837aSMikko Perttunen 	job->syncpt_recovery = true;
207c40f0f1aSThierry Reding 
208ec73c4cfSDmitry Osipenko 	/*
209ec73c4cfSDmitry Osipenko 	 * Track referenced BOs so that they can be unreferenced after the
210ec73c4cfSDmitry Osipenko 	 * submission is complete.
211ec73c4cfSDmitry Osipenko 	 */
21224c94e16SThierry Reding 	num_refs = num_cmdbufs + num_relocs * 2;
213ec73c4cfSDmitry Osipenko 
214ec73c4cfSDmitry Osipenko 	refs = kmalloc_array(num_refs, sizeof(*refs), GFP_KERNEL);
215ec73c4cfSDmitry Osipenko 	if (!refs) {
216ec73c4cfSDmitry Osipenko 		err = -ENOMEM;
217ec73c4cfSDmitry Osipenko 		goto put;
218ec73c4cfSDmitry Osipenko 	}
219ec73c4cfSDmitry Osipenko 
220ec73c4cfSDmitry Osipenko 	/* reuse as an iterator later */
221ec73c4cfSDmitry Osipenko 	num_refs = 0;
222ec73c4cfSDmitry Osipenko 
223c40f0f1aSThierry Reding 	while (num_cmdbufs) {
224c40f0f1aSThierry Reding 		struct drm_tegra_cmdbuf cmdbuf;
225c40f0f1aSThierry Reding 		struct host1x_bo *bo;
226368f622cSDmitry Osipenko 		struct tegra_bo *obj;
227368f622cSDmitry Osipenko 		u64 offset;
228c40f0f1aSThierry Reding 
229a176c67dSMikko Perttunen 		if (copy_from_user(&cmdbuf, user_cmdbufs, sizeof(cmdbuf))) {
2309a991600SDan Carpenter 			err = -EFAULT;
231c40f0f1aSThierry Reding 			goto fail;
2329a991600SDan Carpenter 		}
233c40f0f1aSThierry Reding 
234368f622cSDmitry Osipenko 		/*
235368f622cSDmitry Osipenko 		 * The maximum number of CDMA gather fetches is 16383, a higher
236368f622cSDmitry Osipenko 		 * value means the words count is malformed.
237368f622cSDmitry Osipenko 		 */
238368f622cSDmitry Osipenko 		if (cmdbuf.words > CDMA_GATHER_FETCHES_MAX_NB) {
239368f622cSDmitry Osipenko 			err = -EINVAL;
240368f622cSDmitry Osipenko 			goto fail;
241368f622cSDmitry Osipenko 		}
242368f622cSDmitry Osipenko 
243f51632ccSMikko Perttunen 		bo = tegra_gem_lookup(file, cmdbuf.handle);
244c40f0f1aSThierry Reding 		if (!bo) {
245c40f0f1aSThierry Reding 			err = -ENOENT;
246c40f0f1aSThierry Reding 			goto fail;
247c40f0f1aSThierry Reding 		}
248c40f0f1aSThierry Reding 
249368f622cSDmitry Osipenko 		offset = (u64)cmdbuf.offset + (u64)cmdbuf.words * sizeof(u32);
250368f622cSDmitry Osipenko 		obj = host1x_to_tegra_bo(bo);
251ec73c4cfSDmitry Osipenko 		refs[num_refs++] = &obj->gem;
252368f622cSDmitry Osipenko 
253368f622cSDmitry Osipenko 		/*
254368f622cSDmitry Osipenko 		 * Gather buffer base address must be 4-bytes aligned,
255368f622cSDmitry Osipenko 		 * unaligned offset is malformed and cause commands stream
256368f622cSDmitry Osipenko 		 * corruption on the buffer address relocation.
257368f622cSDmitry Osipenko 		 */
2585265f033SMikko Perttunen 		if (offset & 3 || offset > obj->gem.size) {
259368f622cSDmitry Osipenko 			err = -EINVAL;
260368f622cSDmitry Osipenko 			goto fail;
261368f622cSDmitry Osipenko 		}
262368f622cSDmitry Osipenko 
263c40f0f1aSThierry Reding 		host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
264c40f0f1aSThierry Reding 		num_cmdbufs--;
265a176c67dSMikko Perttunen 		user_cmdbufs++;
266c40f0f1aSThierry Reding 	}
267c40f0f1aSThierry Reding 
268961e3beaSThierry Reding 	/* copy and resolve relocations from submit */
269c40f0f1aSThierry Reding 	while (num_relocs--) {
270368f622cSDmitry Osipenko 		struct host1x_reloc *reloc;
271368f622cSDmitry Osipenko 		struct tegra_bo *obj;
272368f622cSDmitry Osipenko 
27306490bb9SThierry Reding 		err = host1x_reloc_copy_from_user(&job->relocs[num_relocs],
274a176c67dSMikko Perttunen 						  &user_relocs[num_relocs], drm,
275961e3beaSThierry Reding 						  file);
276961e3beaSThierry Reding 		if (err < 0)
277c40f0f1aSThierry Reding 			goto fail;
278368f622cSDmitry Osipenko 
27906490bb9SThierry Reding 		reloc = &job->relocs[num_relocs];
280368f622cSDmitry Osipenko 		obj = host1x_to_tegra_bo(reloc->cmdbuf.bo);
281ec73c4cfSDmitry Osipenko 		refs[num_refs++] = &obj->gem;
282368f622cSDmitry Osipenko 
283368f622cSDmitry Osipenko 		/*
284368f622cSDmitry Osipenko 		 * The unaligned cmdbuf offset will cause an unaligned write
285368f622cSDmitry Osipenko 		 * during of the relocations patching, corrupting the commands
286368f622cSDmitry Osipenko 		 * stream.
287368f622cSDmitry Osipenko 		 */
288368f622cSDmitry Osipenko 		if (reloc->cmdbuf.offset & 3 ||
289368f622cSDmitry Osipenko 		    reloc->cmdbuf.offset >= obj->gem.size) {
290368f622cSDmitry Osipenko 			err = -EINVAL;
291368f622cSDmitry Osipenko 			goto fail;
292c40f0f1aSThierry Reding 		}
293c40f0f1aSThierry Reding 
294368f622cSDmitry Osipenko 		obj = host1x_to_tegra_bo(reloc->target.bo);
295ec73c4cfSDmitry Osipenko 		refs[num_refs++] = &obj->gem;
296368f622cSDmitry Osipenko 
297368f622cSDmitry Osipenko 		if (reloc->target.offset >= obj->gem.size) {
298368f622cSDmitry Osipenko 			err = -EINVAL;
299c40f0f1aSThierry Reding 			goto fail;
3009a991600SDan Carpenter 		}
301c40f0f1aSThierry Reding 	}
302c40f0f1aSThierry Reding 
303a176c67dSMikko Perttunen 	if (copy_from_user(&syncpt, user_syncpt, sizeof(syncpt))) {
304c40f0f1aSThierry Reding 		err = -EFAULT;
305c40f0f1aSThierry Reding 		goto fail;
306c40f0f1aSThierry Reding 	}
307c40f0f1aSThierry Reding 
3082aed4f5aSMikko Perttunen 	/* Syncpoint ref will be dropped on job release. */
3092aed4f5aSMikko Perttunen 	sp = host1x_syncpt_get_by_id(host1x, syncpt.id);
310e0b2ce02SDmitry Osipenko 	if (!sp) {
311e0b2ce02SDmitry Osipenko 		err = -ENOENT;
312e0b2ce02SDmitry Osipenko 		goto fail;
313e0b2ce02SDmitry Osipenko 	}
314e0b2ce02SDmitry Osipenko 
315c40f0f1aSThierry Reding 	job->is_addr_reg = context->client->ops->is_addr_reg;
3160f563a4bSDmitry Osipenko 	job->is_valid_class = context->client->ops->is_valid_class;
317c40f0f1aSThierry Reding 	job->syncpt_incrs = syncpt.incrs;
3182aed4f5aSMikko Perttunen 	job->syncpt = sp;
319c40f0f1aSThierry Reding 	job->timeout = 10000;
320c40f0f1aSThierry Reding 
321c40f0f1aSThierry Reding 	if (args->timeout && args->timeout < 10000)
322c40f0f1aSThierry Reding 		job->timeout = args->timeout;
323c40f0f1aSThierry Reding 
324c40f0f1aSThierry Reding 	err = host1x_job_pin(job, context->client->base.dev);
325c40f0f1aSThierry Reding 	if (err)
326c40f0f1aSThierry Reding 		goto fail;
327c40f0f1aSThierry Reding 
328c40f0f1aSThierry Reding 	err = host1x_job_submit(job);
329ec73c4cfSDmitry Osipenko 	if (err) {
330ec73c4cfSDmitry Osipenko 		host1x_job_unpin(job);
331ec73c4cfSDmitry Osipenko 		goto fail;
332ec73c4cfSDmitry Osipenko 	}
333c40f0f1aSThierry Reding 
334c40f0f1aSThierry Reding 	args->fence = job->syncpt_end;
335c40f0f1aSThierry Reding 
336c40f0f1aSThierry Reding fail:
337ec73c4cfSDmitry Osipenko 	while (num_refs--)
338b8912e29SEmil Velikov 		drm_gem_object_put(refs[num_refs]);
339ec73c4cfSDmitry Osipenko 
340ec73c4cfSDmitry Osipenko 	kfree(refs);
341ec73c4cfSDmitry Osipenko 
342ec73c4cfSDmitry Osipenko put:
343c40f0f1aSThierry Reding 	host1x_job_put(job);
344c40f0f1aSThierry Reding 	return err;
345c40f0f1aSThierry Reding }
346c40f0f1aSThierry Reding 
347c40f0f1aSThierry Reding 
348dee8268fSThierry Reding #ifdef CONFIG_DRM_TEGRA_STAGING
tegra_gem_create(struct drm_device * drm,void * data,struct drm_file * file)349dee8268fSThierry Reding static int tegra_gem_create(struct drm_device *drm, void *data,
350dee8268fSThierry Reding 			    struct drm_file *file)
351dee8268fSThierry Reding {
352dee8268fSThierry Reding 	struct drm_tegra_gem_create *args = data;
353dee8268fSThierry Reding 	struct tegra_bo *bo;
354dee8268fSThierry Reding 
355773af77fSThierry Reding 	bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags,
356dee8268fSThierry Reding 					 &args->handle);
357dee8268fSThierry Reding 	if (IS_ERR(bo))
358dee8268fSThierry Reding 		return PTR_ERR(bo);
359dee8268fSThierry Reding 
360dee8268fSThierry Reding 	return 0;
361dee8268fSThierry Reding }
362dee8268fSThierry Reding 
tegra_gem_mmap(struct drm_device * drm,void * data,struct drm_file * file)363dee8268fSThierry Reding static int tegra_gem_mmap(struct drm_device *drm, void *data,
364dee8268fSThierry Reding 			  struct drm_file *file)
365dee8268fSThierry Reding {
366dee8268fSThierry Reding 	struct drm_tegra_gem_mmap *args = data;
367dee8268fSThierry Reding 	struct drm_gem_object *gem;
368dee8268fSThierry Reding 	struct tegra_bo *bo;
369dee8268fSThierry Reding 
370a8ad0bd8SChris Wilson 	gem = drm_gem_object_lookup(file, args->handle);
371dee8268fSThierry Reding 	if (!gem)
372dee8268fSThierry Reding 		return -EINVAL;
373dee8268fSThierry Reding 
374dee8268fSThierry Reding 	bo = to_tegra_bo(gem);
375dee8268fSThierry Reding 
376dee8268fSThierry Reding 	args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
377dee8268fSThierry Reding 
378b8912e29SEmil Velikov 	drm_gem_object_put(gem);
379dee8268fSThierry Reding 
380dee8268fSThierry Reding 	return 0;
381dee8268fSThierry Reding }
382dee8268fSThierry Reding 
tegra_syncpt_read(struct drm_device * drm,void * data,struct drm_file * file)383dee8268fSThierry Reding static int tegra_syncpt_read(struct drm_device *drm, void *data,
384dee8268fSThierry Reding 			     struct drm_file *file)
385dee8268fSThierry Reding {
386dee8268fSThierry Reding 	struct host1x *host = dev_get_drvdata(drm->dev->parent);
387dee8268fSThierry Reding 	struct drm_tegra_syncpt_read *args = data;
388dee8268fSThierry Reding 	struct host1x_syncpt *sp;
389dee8268fSThierry Reding 
3902aed4f5aSMikko Perttunen 	sp = host1x_syncpt_get_by_id_noref(host, args->id);
391dee8268fSThierry Reding 	if (!sp)
392dee8268fSThierry Reding 		return -EINVAL;
393dee8268fSThierry Reding 
394dee8268fSThierry Reding 	args->value = host1x_syncpt_read_min(sp);
395dee8268fSThierry Reding 	return 0;
396dee8268fSThierry Reding }
397dee8268fSThierry Reding 
tegra_syncpt_incr(struct drm_device * drm,void * data,struct drm_file * file)398dee8268fSThierry Reding static int tegra_syncpt_incr(struct drm_device *drm, void *data,
399dee8268fSThierry Reding 			     struct drm_file *file)
400dee8268fSThierry Reding {
401dee8268fSThierry Reding 	struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
402dee8268fSThierry Reding 	struct drm_tegra_syncpt_incr *args = data;
403dee8268fSThierry Reding 	struct host1x_syncpt *sp;
404dee8268fSThierry Reding 
4052aed4f5aSMikko Perttunen 	sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
406dee8268fSThierry Reding 	if (!sp)
407dee8268fSThierry Reding 		return -EINVAL;
408dee8268fSThierry Reding 
409dee8268fSThierry Reding 	return host1x_syncpt_incr(sp);
410dee8268fSThierry Reding }
411dee8268fSThierry Reding 
tegra_syncpt_wait(struct drm_device * drm,void * data,struct drm_file * file)412dee8268fSThierry Reding static int tegra_syncpt_wait(struct drm_device *drm, void *data,
413dee8268fSThierry Reding 			     struct drm_file *file)
414dee8268fSThierry Reding {
415dee8268fSThierry Reding 	struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
416dee8268fSThierry Reding 	struct drm_tegra_syncpt_wait *args = data;
417dee8268fSThierry Reding 	struct host1x_syncpt *sp;
418dee8268fSThierry Reding 
4192aed4f5aSMikko Perttunen 	sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
420dee8268fSThierry Reding 	if (!sp)
421dee8268fSThierry Reding 		return -EINVAL;
422dee8268fSThierry Reding 
4234c69ac12SDmitry Osipenko 	return host1x_syncpt_wait(sp, args->thresh,
4244c69ac12SDmitry Osipenko 				  msecs_to_jiffies(args->timeout),
425dee8268fSThierry Reding 				  &args->value);
426dee8268fSThierry Reding }
427dee8268fSThierry Reding 
tegra_client_open(struct tegra_drm_file * fpriv,struct tegra_drm_client * client,struct tegra_drm_context * context)428bdd2f9cdSThierry Reding static int tegra_client_open(struct tegra_drm_file *fpriv,
429bdd2f9cdSThierry Reding 			     struct tegra_drm_client *client,
430bdd2f9cdSThierry Reding 			     struct tegra_drm_context *context)
431bdd2f9cdSThierry Reding {
432bdd2f9cdSThierry Reding 	int err;
433bdd2f9cdSThierry Reding 
43458ed47adSDmitry Osipenko 	err = pm_runtime_resume_and_get(client->base.dev);
43558ed47adSDmitry Osipenko 	if (err)
436bdd2f9cdSThierry Reding 		return err;
437bdd2f9cdSThierry Reding 
43858ed47adSDmitry Osipenko 	err = client->ops->open_channel(client, context);
43958ed47adSDmitry Osipenko 	if (err < 0) {
44058ed47adSDmitry Osipenko 		pm_runtime_put(client->base.dev);
44158ed47adSDmitry Osipenko 		return err;
44258ed47adSDmitry Osipenko 	}
44358ed47adSDmitry Osipenko 
444d7c591bcSMikko Perttunen 	err = idr_alloc(&fpriv->legacy_contexts, context, 1, 0, GFP_KERNEL);
445bdd2f9cdSThierry Reding 	if (err < 0) {
446bdd2f9cdSThierry Reding 		client->ops->close_channel(context);
44758ed47adSDmitry Osipenko 		pm_runtime_put(client->base.dev);
448bdd2f9cdSThierry Reding 		return err;
449bdd2f9cdSThierry Reding 	}
450bdd2f9cdSThierry Reding 
451bdd2f9cdSThierry Reding 	context->client = client;
452bdd2f9cdSThierry Reding 	context->id = err;
453bdd2f9cdSThierry Reding 
454bdd2f9cdSThierry Reding 	return 0;
455bdd2f9cdSThierry Reding }
456bdd2f9cdSThierry Reding 
tegra_open_channel(struct drm_device * drm,void * data,struct drm_file * file)457dee8268fSThierry Reding static int tegra_open_channel(struct drm_device *drm, void *data,
458dee8268fSThierry Reding 			      struct drm_file *file)
459dee8268fSThierry Reding {
460dee8268fSThierry Reding 	struct tegra_drm_file *fpriv = file->driver_priv;
461dee8268fSThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
462dee8268fSThierry Reding 	struct drm_tegra_open_channel *args = data;
463dee8268fSThierry Reding 	struct tegra_drm_context *context;
464dee8268fSThierry Reding 	struct tegra_drm_client *client;
465dee8268fSThierry Reding 	int err = -ENODEV;
466dee8268fSThierry Reding 
467dee8268fSThierry Reding 	context = kzalloc(sizeof(*context), GFP_KERNEL);
468dee8268fSThierry Reding 	if (!context)
469dee8268fSThierry Reding 		return -ENOMEM;
470dee8268fSThierry Reding 
471bdd2f9cdSThierry Reding 	mutex_lock(&fpriv->lock);
472bdd2f9cdSThierry Reding 
473dee8268fSThierry Reding 	list_for_each_entry(client, &tegra->clients, list)
474dee8268fSThierry Reding 		if (client->base.class == args->client) {
475bdd2f9cdSThierry Reding 			err = tegra_client_open(fpriv, client, context);
476bdd2f9cdSThierry Reding 			if (err < 0)
477dee8268fSThierry Reding 				break;
478dee8268fSThierry Reding 
479bdd2f9cdSThierry Reding 			args->context = context->id;
480bdd2f9cdSThierry Reding 			break;
481dee8268fSThierry Reding 		}
482dee8268fSThierry Reding 
483bdd2f9cdSThierry Reding 	if (err < 0)
484dee8268fSThierry Reding 		kfree(context);
485bdd2f9cdSThierry Reding 
486bdd2f9cdSThierry Reding 	mutex_unlock(&fpriv->lock);
487dee8268fSThierry Reding 	return err;
488dee8268fSThierry Reding }
489dee8268fSThierry Reding 
tegra_close_channel(struct drm_device * drm,void * data,struct drm_file * file)490dee8268fSThierry Reding static int tegra_close_channel(struct drm_device *drm, void *data,
491dee8268fSThierry Reding 			       struct drm_file *file)
492dee8268fSThierry Reding {
493dee8268fSThierry Reding 	struct tegra_drm_file *fpriv = file->driver_priv;
494dee8268fSThierry Reding 	struct drm_tegra_close_channel *args = data;
495dee8268fSThierry Reding 	struct tegra_drm_context *context;
496bdd2f9cdSThierry Reding 	int err = 0;
497dee8268fSThierry Reding 
498bdd2f9cdSThierry Reding 	mutex_lock(&fpriv->lock);
499dee8268fSThierry Reding 
500d7c591bcSMikko Perttunen 	context = idr_find(&fpriv->legacy_contexts, args->context);
501bdd2f9cdSThierry Reding 	if (!context) {
502bdd2f9cdSThierry Reding 		err = -EINVAL;
503bdd2f9cdSThierry Reding 		goto unlock;
504bdd2f9cdSThierry Reding 	}
505dee8268fSThierry Reding 
506d7c591bcSMikko Perttunen 	idr_remove(&fpriv->legacy_contexts, context->id);
507dee8268fSThierry Reding 	tegra_drm_context_free(context);
508dee8268fSThierry Reding 
509bdd2f9cdSThierry Reding unlock:
510bdd2f9cdSThierry Reding 	mutex_unlock(&fpriv->lock);
511bdd2f9cdSThierry Reding 	return err;
512dee8268fSThierry Reding }
513dee8268fSThierry Reding 
tegra_get_syncpt(struct drm_device * drm,void * data,struct drm_file * file)514dee8268fSThierry Reding static int tegra_get_syncpt(struct drm_device *drm, void *data,
515dee8268fSThierry Reding 			    struct drm_file *file)
516dee8268fSThierry Reding {
517dee8268fSThierry Reding 	struct tegra_drm_file *fpriv = file->driver_priv;
518dee8268fSThierry Reding 	struct drm_tegra_get_syncpt *args = data;
519dee8268fSThierry Reding 	struct tegra_drm_context *context;
520dee8268fSThierry Reding 	struct host1x_syncpt *syncpt;
521bdd2f9cdSThierry Reding 	int err = 0;
522dee8268fSThierry Reding 
523bdd2f9cdSThierry Reding 	mutex_lock(&fpriv->lock);
524dee8268fSThierry Reding 
525d7c591bcSMikko Perttunen 	context = idr_find(&fpriv->legacy_contexts, args->context);
526bdd2f9cdSThierry Reding 	if (!context) {
527bdd2f9cdSThierry Reding 		err = -ENODEV;
528bdd2f9cdSThierry Reding 		goto unlock;
529bdd2f9cdSThierry Reding 	}
530dee8268fSThierry Reding 
531bdd2f9cdSThierry Reding 	if (args->index >= context->client->base.num_syncpts) {
532bdd2f9cdSThierry Reding 		err = -EINVAL;
533bdd2f9cdSThierry Reding 		goto unlock;
534bdd2f9cdSThierry Reding 	}
535dee8268fSThierry Reding 
536dee8268fSThierry Reding 	syncpt = context->client->base.syncpts[args->index];
537dee8268fSThierry Reding 	args->id = host1x_syncpt_id(syncpt);
538dee8268fSThierry Reding 
539bdd2f9cdSThierry Reding unlock:
540bdd2f9cdSThierry Reding 	mutex_unlock(&fpriv->lock);
541bdd2f9cdSThierry Reding 	return err;
542dee8268fSThierry Reding }
543dee8268fSThierry Reding 
tegra_submit(struct drm_device * drm,void * data,struct drm_file * file)544dee8268fSThierry Reding static int tegra_submit(struct drm_device *drm, void *data,
545dee8268fSThierry Reding 			struct drm_file *file)
546dee8268fSThierry Reding {
547dee8268fSThierry Reding 	struct tegra_drm_file *fpriv = file->driver_priv;
548dee8268fSThierry Reding 	struct drm_tegra_submit *args = data;
549dee8268fSThierry Reding 	struct tegra_drm_context *context;
550bdd2f9cdSThierry Reding 	int err;
551dee8268fSThierry Reding 
552bdd2f9cdSThierry Reding 	mutex_lock(&fpriv->lock);
553dee8268fSThierry Reding 
554d7c591bcSMikko Perttunen 	context = idr_find(&fpriv->legacy_contexts, args->context);
555bdd2f9cdSThierry Reding 	if (!context) {
556bdd2f9cdSThierry Reding 		err = -ENODEV;
557bdd2f9cdSThierry Reding 		goto unlock;
558bdd2f9cdSThierry Reding 	}
559dee8268fSThierry Reding 
560bdd2f9cdSThierry Reding 	err = context->client->ops->submit(context, args, drm, file);
561bdd2f9cdSThierry Reding 
562bdd2f9cdSThierry Reding unlock:
563bdd2f9cdSThierry Reding 	mutex_unlock(&fpriv->lock);
564bdd2f9cdSThierry Reding 	return err;
565dee8268fSThierry Reding }
566c54a169bSArto Merilainen 
tegra_get_syncpt_base(struct drm_device * drm,void * data,struct drm_file * file)567c54a169bSArto Merilainen static int tegra_get_syncpt_base(struct drm_device *drm, void *data,
568c54a169bSArto Merilainen 				 struct drm_file *file)
569c54a169bSArto Merilainen {
570c54a169bSArto Merilainen 	struct tegra_drm_file *fpriv = file->driver_priv;
571c54a169bSArto Merilainen 	struct drm_tegra_get_syncpt_base *args = data;
572c54a169bSArto Merilainen 	struct tegra_drm_context *context;
573c54a169bSArto Merilainen 	struct host1x_syncpt_base *base;
574c54a169bSArto Merilainen 	struct host1x_syncpt *syncpt;
575bdd2f9cdSThierry Reding 	int err = 0;
576c54a169bSArto Merilainen 
577bdd2f9cdSThierry Reding 	mutex_lock(&fpriv->lock);
578c54a169bSArto Merilainen 
579d7c591bcSMikko Perttunen 	context = idr_find(&fpriv->legacy_contexts, args->context);
580bdd2f9cdSThierry Reding 	if (!context) {
581bdd2f9cdSThierry Reding 		err = -ENODEV;
582bdd2f9cdSThierry Reding 		goto unlock;
583bdd2f9cdSThierry Reding 	}
584c54a169bSArto Merilainen 
585bdd2f9cdSThierry Reding 	if (args->syncpt >= context->client->base.num_syncpts) {
586bdd2f9cdSThierry Reding 		err = -EINVAL;
587bdd2f9cdSThierry Reding 		goto unlock;
588bdd2f9cdSThierry Reding 	}
589c54a169bSArto Merilainen 
590c54a169bSArto Merilainen 	syncpt = context->client->base.syncpts[args->syncpt];
591c54a169bSArto Merilainen 
592c54a169bSArto Merilainen 	base = host1x_syncpt_get_base(syncpt);
593bdd2f9cdSThierry Reding 	if (!base) {
594bdd2f9cdSThierry Reding 		err = -ENXIO;
595bdd2f9cdSThierry Reding 		goto unlock;
596bdd2f9cdSThierry Reding 	}
597c54a169bSArto Merilainen 
598c54a169bSArto Merilainen 	args->id = host1x_syncpt_base_id(base);
599c54a169bSArto Merilainen 
600bdd2f9cdSThierry Reding unlock:
601bdd2f9cdSThierry Reding 	mutex_unlock(&fpriv->lock);
602bdd2f9cdSThierry Reding 	return err;
603c54a169bSArto Merilainen }
6047678d71fSThierry Reding 
tegra_gem_set_tiling(struct drm_device * drm,void * data,struct drm_file * file)6057678d71fSThierry Reding static int tegra_gem_set_tiling(struct drm_device *drm, void *data,
6067678d71fSThierry Reding 				struct drm_file *file)
6077678d71fSThierry Reding {
6087678d71fSThierry Reding 	struct drm_tegra_gem_set_tiling *args = data;
6097678d71fSThierry Reding 	enum tegra_bo_tiling_mode mode;
6107678d71fSThierry Reding 	struct drm_gem_object *gem;
6117678d71fSThierry Reding 	unsigned long value = 0;
6127678d71fSThierry Reding 	struct tegra_bo *bo;
6137678d71fSThierry Reding 
6147678d71fSThierry Reding 	switch (args->mode) {
6157678d71fSThierry Reding 	case DRM_TEGRA_GEM_TILING_MODE_PITCH:
6167678d71fSThierry Reding 		mode = TEGRA_BO_TILING_MODE_PITCH;
6177678d71fSThierry Reding 
6187678d71fSThierry Reding 		if (args->value != 0)
6197678d71fSThierry Reding 			return -EINVAL;
6207678d71fSThierry Reding 
6217678d71fSThierry Reding 		break;
6227678d71fSThierry Reding 
6237678d71fSThierry Reding 	case DRM_TEGRA_GEM_TILING_MODE_TILED:
6247678d71fSThierry Reding 		mode = TEGRA_BO_TILING_MODE_TILED;
6257678d71fSThierry Reding 
6267678d71fSThierry Reding 		if (args->value != 0)
6277678d71fSThierry Reding 			return -EINVAL;
6287678d71fSThierry Reding 
6297678d71fSThierry Reding 		break;
6307678d71fSThierry Reding 
6317678d71fSThierry Reding 	case DRM_TEGRA_GEM_TILING_MODE_BLOCK:
6327678d71fSThierry Reding 		mode = TEGRA_BO_TILING_MODE_BLOCK;
6337678d71fSThierry Reding 
6347678d71fSThierry Reding 		if (args->value > 5)
6357678d71fSThierry Reding 			return -EINVAL;
6367678d71fSThierry Reding 
6377678d71fSThierry Reding 		value = args->value;
6387678d71fSThierry Reding 		break;
6397678d71fSThierry Reding 
6407678d71fSThierry Reding 	default:
6417678d71fSThierry Reding 		return -EINVAL;
6427678d71fSThierry Reding 	}
6437678d71fSThierry Reding 
644a8ad0bd8SChris Wilson 	gem = drm_gem_object_lookup(file, args->handle);
6457678d71fSThierry Reding 	if (!gem)
6467678d71fSThierry Reding 		return -ENOENT;
6477678d71fSThierry Reding 
6487678d71fSThierry Reding 	bo = to_tegra_bo(gem);
6497678d71fSThierry Reding 
6507678d71fSThierry Reding 	bo->tiling.mode = mode;
6517678d71fSThierry Reding 	bo->tiling.value = value;
6527678d71fSThierry Reding 
653b8912e29SEmil Velikov 	drm_gem_object_put(gem);
6547678d71fSThierry Reding 
6557678d71fSThierry Reding 	return 0;
6567678d71fSThierry Reding }
6577678d71fSThierry Reding 
tegra_gem_get_tiling(struct drm_device * drm,void * data,struct drm_file * file)6587678d71fSThierry Reding static int tegra_gem_get_tiling(struct drm_device *drm, void *data,
6597678d71fSThierry Reding 				struct drm_file *file)
6607678d71fSThierry Reding {
6617678d71fSThierry Reding 	struct drm_tegra_gem_get_tiling *args = data;
6627678d71fSThierry Reding 	struct drm_gem_object *gem;
6637678d71fSThierry Reding 	struct tegra_bo *bo;
6647678d71fSThierry Reding 	int err = 0;
6657678d71fSThierry Reding 
666a8ad0bd8SChris Wilson 	gem = drm_gem_object_lookup(file, args->handle);
6677678d71fSThierry Reding 	if (!gem)
6687678d71fSThierry Reding 		return -ENOENT;
6697678d71fSThierry Reding 
6707678d71fSThierry Reding 	bo = to_tegra_bo(gem);
6717678d71fSThierry Reding 
6727678d71fSThierry Reding 	switch (bo->tiling.mode) {
6737678d71fSThierry Reding 	case TEGRA_BO_TILING_MODE_PITCH:
6747678d71fSThierry Reding 		args->mode = DRM_TEGRA_GEM_TILING_MODE_PITCH;
6757678d71fSThierry Reding 		args->value = 0;
6767678d71fSThierry Reding 		break;
6777678d71fSThierry Reding 
6787678d71fSThierry Reding 	case TEGRA_BO_TILING_MODE_TILED:
6797678d71fSThierry Reding 		args->mode = DRM_TEGRA_GEM_TILING_MODE_TILED;
6807678d71fSThierry Reding 		args->value = 0;
6817678d71fSThierry Reding 		break;
6827678d71fSThierry Reding 
6837678d71fSThierry Reding 	case TEGRA_BO_TILING_MODE_BLOCK:
6847678d71fSThierry Reding 		args->mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
6857678d71fSThierry Reding 		args->value = bo->tiling.value;
6867678d71fSThierry Reding 		break;
6877678d71fSThierry Reding 
6887678d71fSThierry Reding 	default:
6897678d71fSThierry Reding 		err = -EINVAL;
6907678d71fSThierry Reding 		break;
6917678d71fSThierry Reding 	}
6927678d71fSThierry Reding 
693b8912e29SEmil Velikov 	drm_gem_object_put(gem);
6947678d71fSThierry Reding 
6957678d71fSThierry Reding 	return err;
6967678d71fSThierry Reding }
6977b129087SThierry Reding 
tegra_gem_set_flags(struct drm_device * drm,void * data,struct drm_file * file)6987b129087SThierry Reding static int tegra_gem_set_flags(struct drm_device *drm, void *data,
6997b129087SThierry Reding 			       struct drm_file *file)
7007b129087SThierry Reding {
7017b129087SThierry Reding 	struct drm_tegra_gem_set_flags *args = data;
7027b129087SThierry Reding 	struct drm_gem_object *gem;
7037b129087SThierry Reding 	struct tegra_bo *bo;
7047b129087SThierry Reding 
7057b129087SThierry Reding 	if (args->flags & ~DRM_TEGRA_GEM_FLAGS)
7067b129087SThierry Reding 		return -EINVAL;
7077b129087SThierry Reding 
708a8ad0bd8SChris Wilson 	gem = drm_gem_object_lookup(file, args->handle);
7097b129087SThierry Reding 	if (!gem)
7107b129087SThierry Reding 		return -ENOENT;
7117b129087SThierry Reding 
7127b129087SThierry Reding 	bo = to_tegra_bo(gem);
7137b129087SThierry Reding 	bo->flags = 0;
7147b129087SThierry Reding 
7157b129087SThierry Reding 	if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP)
7167b129087SThierry Reding 		bo->flags |= TEGRA_BO_BOTTOM_UP;
7177b129087SThierry Reding 
718b8912e29SEmil Velikov 	drm_gem_object_put(gem);
7197b129087SThierry Reding 
7207b129087SThierry Reding 	return 0;
7217b129087SThierry Reding }
7227b129087SThierry Reding 
tegra_gem_get_flags(struct drm_device * drm,void * data,struct drm_file * file)7237b129087SThierry Reding static int tegra_gem_get_flags(struct drm_device *drm, void *data,
7247b129087SThierry Reding 			       struct drm_file *file)
7257b129087SThierry Reding {
7267b129087SThierry Reding 	struct drm_tegra_gem_get_flags *args = data;
7277b129087SThierry Reding 	struct drm_gem_object *gem;
7287b129087SThierry Reding 	struct tegra_bo *bo;
7297b129087SThierry Reding 
730a8ad0bd8SChris Wilson 	gem = drm_gem_object_lookup(file, args->handle);
7317b129087SThierry Reding 	if (!gem)
7327b129087SThierry Reding 		return -ENOENT;
7337b129087SThierry Reding 
7347b129087SThierry Reding 	bo = to_tegra_bo(gem);
7357b129087SThierry Reding 	args->flags = 0;
7367b129087SThierry Reding 
7377b129087SThierry Reding 	if (bo->flags & TEGRA_BO_BOTTOM_UP)
7387b129087SThierry Reding 		args->flags |= DRM_TEGRA_GEM_BOTTOM_UP;
7397b129087SThierry Reding 
740b8912e29SEmil Velikov 	drm_gem_object_put(gem);
7417b129087SThierry Reding 
7427b129087SThierry Reding 	return 0;
7437b129087SThierry Reding }
744dee8268fSThierry Reding #endif
745dee8268fSThierry Reding 
746dee8268fSThierry Reding static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
747dee8268fSThierry Reding #ifdef CONFIG_DRM_TEGRA_STAGING
748d7c591bcSMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_OPEN, tegra_drm_ioctl_channel_open,
749d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
750d7c591bcSMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_CLOSE, tegra_drm_ioctl_channel_close,
751d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
752d7c591bcSMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_MAP, tegra_drm_ioctl_channel_map,
753d7c591bcSMikko Perttunen 			  DRM_RENDER_ALLOW),
754d7c591bcSMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_UNMAP, tegra_drm_ioctl_channel_unmap,
755d7c591bcSMikko Perttunen 			  DRM_RENDER_ALLOW),
75613abe0bbSMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_SUBMIT, tegra_drm_ioctl_channel_submit,
75713abe0bbSMikko Perttunen 			  DRM_RENDER_ALLOW),
758fc348336SMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_ALLOCATE, tegra_drm_ioctl_syncpoint_allocate,
759fc348336SMikko Perttunen 			  DRM_RENDER_ALLOW),
760fc348336SMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_FREE, tegra_drm_ioctl_syncpoint_free,
761fc348336SMikko Perttunen 			  DRM_RENDER_ALLOW),
76244e96138SMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_WAIT, tegra_drm_ioctl_syncpoint_wait,
76344e96138SMikko Perttunen 			  DRM_RENDER_ALLOW),
764d7c591bcSMikko Perttunen 
765d7c591bcSMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_RENDER_ALLOW),
766d7c591bcSMikko Perttunen 	DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_RENDER_ALLOW),
7676c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read,
768d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7696c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr,
770d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7716c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait,
772d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7736c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel,
774d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7756c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel,
776d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7776c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt,
778d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7796c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit,
780d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7816c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base,
782d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7836c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_TILING, tegra_gem_set_tiling,
784d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7856c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling,
786d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7876c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags,
788d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
7896c68b717SThierry Reding 	DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags,
790d6891db2SEmil Velikov 			  DRM_RENDER_ALLOW),
791dee8268fSThierry Reding #endif
792dee8268fSThierry Reding };
793dee8268fSThierry Reding 
794dee8268fSThierry Reding static const struct file_operations tegra_drm_fops = {
795dee8268fSThierry Reding 	.owner = THIS_MODULE,
796dee8268fSThierry Reding 	.open = drm_open,
797dee8268fSThierry Reding 	.release = drm_release,
798dee8268fSThierry Reding 	.unlocked_ioctl = drm_ioctl,
799dee8268fSThierry Reding 	.mmap = tegra_drm_mmap,
800dee8268fSThierry Reding 	.poll = drm_poll,
801dee8268fSThierry Reding 	.read = drm_read,
802dee8268fSThierry Reding 	.compat_ioctl = drm_compat_ioctl,
803dee8268fSThierry Reding 	.llseek = noop_llseek,
804dee8268fSThierry Reding };
805dee8268fSThierry Reding 
tegra_drm_context_cleanup(int id,void * p,void * data)806bdd2f9cdSThierry Reding static int tegra_drm_context_cleanup(int id, void *p, void *data)
807bdd2f9cdSThierry Reding {
808bdd2f9cdSThierry Reding 	struct tegra_drm_context *context = p;
809bdd2f9cdSThierry Reding 
810bdd2f9cdSThierry Reding 	tegra_drm_context_free(context);
811bdd2f9cdSThierry Reding 
812bdd2f9cdSThierry Reding 	return 0;
813bdd2f9cdSThierry Reding }
814bdd2f9cdSThierry Reding 
tegra_drm_postclose(struct drm_device * drm,struct drm_file * file)815bda0ecc4SDaniel Vetter static void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file)
816dee8268fSThierry Reding {
817dee8268fSThierry Reding 	struct tegra_drm_file *fpriv = file->driver_priv;
818dee8268fSThierry Reding 
819bdd2f9cdSThierry Reding 	mutex_lock(&fpriv->lock);
820d7c591bcSMikko Perttunen 	idr_for_each(&fpriv->legacy_contexts, tegra_drm_context_cleanup, NULL);
821d7c591bcSMikko Perttunen 	tegra_drm_uapi_close_file(fpriv);
822bdd2f9cdSThierry Reding 	mutex_unlock(&fpriv->lock);
823dee8268fSThierry Reding 
824d7c591bcSMikko Perttunen 	idr_destroy(&fpriv->legacy_contexts);
825bdd2f9cdSThierry Reding 	mutex_destroy(&fpriv->lock);
826dee8268fSThierry Reding 	kfree(fpriv);
827dee8268fSThierry Reding }
828dee8268fSThierry Reding 
829dee8268fSThierry Reding #ifdef CONFIG_DEBUG_FS
tegra_debugfs_framebuffers(struct seq_file * s,void * data)830dee8268fSThierry Reding static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
831dee8268fSThierry Reding {
832dee8268fSThierry Reding 	struct drm_info_node *node = (struct drm_info_node *)s->private;
833dee8268fSThierry Reding 	struct drm_device *drm = node->minor->dev;
834dee8268fSThierry Reding 	struct drm_framebuffer *fb;
835dee8268fSThierry Reding 
836dee8268fSThierry Reding 	mutex_lock(&drm->mode_config.fb_lock);
837dee8268fSThierry Reding 
838dee8268fSThierry Reding 	list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
839dee8268fSThierry Reding 		seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
840b00c600eSVille Syrjälä 			   fb->base.id, fb->width, fb->height,
841b00c600eSVille Syrjälä 			   fb->format->depth,
842272725c7SVille Syrjälä 			   fb->format->cpp[0] * 8,
843747a598fSDave Airlie 			   drm_framebuffer_read_refcount(fb));
844dee8268fSThierry Reding 	}
845dee8268fSThierry Reding 
846dee8268fSThierry Reding 	mutex_unlock(&drm->mode_config.fb_lock);
847dee8268fSThierry Reding 
848dee8268fSThierry Reding 	return 0;
849dee8268fSThierry Reding }
850dee8268fSThierry Reding 
tegra_debugfs_iova(struct seq_file * s,void * data)85128c23373SThierry Reding static int tegra_debugfs_iova(struct seq_file *s, void *data)
85228c23373SThierry Reding {
85328c23373SThierry Reding 	struct drm_info_node *node = (struct drm_info_node *)s->private;
85428c23373SThierry Reding 	struct drm_device *drm = node->minor->dev;
85528c23373SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
856b5c3714fSDaniel Vetter 	struct drm_printer p = drm_seq_file_printer(s);
85728c23373SThierry Reding 
85868d890a3SMichał Mirosław 	if (tegra->domain) {
859347ad49dSThierry Reding 		mutex_lock(&tegra->mm_lock);
860b5c3714fSDaniel Vetter 		drm_mm_print(&tegra->mm, &p);
861347ad49dSThierry Reding 		mutex_unlock(&tegra->mm_lock);
86268d890a3SMichał Mirosław 	}
863b5c3714fSDaniel Vetter 
864b5c3714fSDaniel Vetter 	return 0;
86528c23373SThierry Reding }
86628c23373SThierry Reding 
867dee8268fSThierry Reding static struct drm_info_list tegra_debugfs_list[] = {
868dee8268fSThierry Reding 	{ "framebuffers", tegra_debugfs_framebuffers, 0 },
86928c23373SThierry Reding 	{ "iova", tegra_debugfs_iova, 0 },
870dee8268fSThierry Reding };
871dee8268fSThierry Reding 
tegra_debugfs_init(struct drm_minor * minor)8727ce84471SWambui Karuga static void tegra_debugfs_init(struct drm_minor *minor)
873dee8268fSThierry Reding {
874ad6d94f2SWambui Karuga 	drm_debugfs_create_files(tegra_debugfs_list,
875dee8268fSThierry Reding 				 ARRAY_SIZE(tegra_debugfs_list),
876dee8268fSThierry Reding 				 minor->debugfs_root, minor);
877dee8268fSThierry Reding }
878dee8268fSThierry Reding #endif
879dee8268fSThierry Reding 
88070a59dd8SDaniel Vetter static const struct drm_driver tegra_drm_driver = {
8810424fdafSDaniel Vetter 	.driver_features = DRIVER_MODESET | DRIVER_GEM |
88213abe0bbSMikko Perttunen 			   DRIVER_ATOMIC | DRIVER_RENDER | DRIVER_SYNCOBJ,
883dee8268fSThierry Reding 	.open = tegra_drm_open,
884bda0ecc4SDaniel Vetter 	.postclose = tegra_drm_postclose,
885dee8268fSThierry Reding 
886dee8268fSThierry Reding #if defined(CONFIG_DEBUG_FS)
887dee8268fSThierry Reding 	.debugfs_init = tegra_debugfs_init,
888dee8268fSThierry Reding #endif
889dee8268fSThierry Reding 
8903800391dSThierry Reding 	.gem_prime_import = tegra_gem_prime_import,
8913800391dSThierry Reding 
892dee8268fSThierry Reding 	.dumb_create = tegra_bo_dumb_create,
893dee8268fSThierry Reding 
894dee8268fSThierry Reding 	.ioctls = tegra_drm_ioctls,
895dee8268fSThierry Reding 	.num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
896dee8268fSThierry Reding 	.fops = &tegra_drm_fops,
897dee8268fSThierry Reding 
898dee8268fSThierry Reding 	.name = DRIVER_NAME,
899dee8268fSThierry Reding 	.desc = DRIVER_DESC,
900dee8268fSThierry Reding 	.date = DRIVER_DATE,
901dee8268fSThierry Reding 	.major = DRIVER_MAJOR,
902dee8268fSThierry Reding 	.minor = DRIVER_MINOR,
903dee8268fSThierry Reding 	.patchlevel = DRIVER_PATCHLEVEL,
904dee8268fSThierry Reding };
905dee8268fSThierry Reding 
tegra_drm_register_client(struct tegra_drm * tegra,struct tegra_drm_client * client)906dee8268fSThierry Reding int tegra_drm_register_client(struct tegra_drm *tegra,
907dee8268fSThierry Reding 			      struct tegra_drm_client *client)
908dee8268fSThierry Reding {
909e0f2977cSMikko Perttunen 	/*
910e0f2977cSMikko Perttunen 	 * When MLOCKs are implemented, change to allocate a shared channel
911e0f2977cSMikko Perttunen 	 * only when MLOCKs are disabled.
912e0f2977cSMikko Perttunen 	 */
913e0f2977cSMikko Perttunen 	client->shared_channel = host1x_channel_request(&client->base);
914e0f2977cSMikko Perttunen 	if (!client->shared_channel)
915e0f2977cSMikko Perttunen 		return -EBUSY;
916e0f2977cSMikko Perttunen 
917dee8268fSThierry Reding 	mutex_lock(&tegra->clients_lock);
918dee8268fSThierry Reding 	list_add_tail(&client->list, &tegra->clients);
9198e5d19c6SThierry Reding 	client->drm = tegra;
920dee8268fSThierry Reding 	mutex_unlock(&tegra->clients_lock);
921dee8268fSThierry Reding 
922dee8268fSThierry Reding 	return 0;
923dee8268fSThierry Reding }
924dee8268fSThierry Reding 
tegra_drm_unregister_client(struct tegra_drm * tegra,struct tegra_drm_client * client)925dee8268fSThierry Reding int tegra_drm_unregister_client(struct tegra_drm *tegra,
926dee8268fSThierry Reding 				struct tegra_drm_client *client)
927dee8268fSThierry Reding {
928dee8268fSThierry Reding 	mutex_lock(&tegra->clients_lock);
929dee8268fSThierry Reding 	list_del_init(&client->list);
9308e5d19c6SThierry Reding 	client->drm = NULL;
931dee8268fSThierry Reding 	mutex_unlock(&tegra->clients_lock);
932dee8268fSThierry Reding 
933e0f2977cSMikko Perttunen 	if (client->shared_channel)
934e0f2977cSMikko Perttunen 		host1x_channel_put(client->shared_channel);
935e0f2977cSMikko Perttunen 
936dee8268fSThierry Reding 	return 0;
937dee8268fSThierry Reding }
938dee8268fSThierry Reding 
host1x_client_iommu_attach(struct host1x_client * client)9397edd7961SThierry Reding int host1x_client_iommu_attach(struct host1x_client *client)
9400c407de5SThierry Reding {
941fa6661b7SThierry Reding 	struct iommu_domain *domain = iommu_get_domain_for_dev(client->dev);
942608f43adSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
9430c407de5SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
9440c407de5SThierry Reding 	struct iommu_group *group = NULL;
9450c407de5SThierry Reding 	int err;
9460c407de5SThierry Reding 
947d210919dSDmitry Osipenko #if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
948d210919dSDmitry Osipenko 	if (client->dev->archdata.mapping) {
949d210919dSDmitry Osipenko 		struct dma_iommu_mapping *mapping =
950d210919dSDmitry Osipenko 				to_dma_iommu_mapping(client->dev);
951d210919dSDmitry Osipenko 		arm_iommu_detach_device(client->dev);
952d210919dSDmitry Osipenko 		arm_iommu_release_mapping(mapping);
953d210919dSDmitry Osipenko 
954d210919dSDmitry Osipenko 		domain = iommu_get_domain_for_dev(client->dev);
955d210919dSDmitry Osipenko 	}
956d210919dSDmitry Osipenko #endif
957d210919dSDmitry Osipenko 
958fa6661b7SThierry Reding 	/*
959fa6661b7SThierry Reding 	 * If the host1x client is already attached to an IOMMU domain that is
960fa6661b7SThierry Reding 	 * not the shared IOMMU domain, don't try to attach it to a different
961fa6661b7SThierry Reding 	 * domain. This allows using the IOMMU-backed DMA API.
962fa6661b7SThierry Reding 	 */
963fa6661b7SThierry Reding 	if (domain && domain != tegra->domain)
964fa6661b7SThierry Reding 		return 0;
9657edd7961SThierry Reding 
966fa6661b7SThierry Reding 	if (tegra->domain) {
9670c407de5SThierry Reding 		group = iommu_group_get(client->dev);
968a8817489SThierry Reding 		if (!group)
969aacdf198SThierry Reding 			return -ENODEV;
9700c407de5SThierry Reding 
9717edd7961SThierry Reding 		if (domain != tegra->domain) {
9720c407de5SThierry Reding 			err = iommu_attach_group(tegra->domain, group);
9730c407de5SThierry Reding 			if (err < 0) {
9740c407de5SThierry Reding 				iommu_group_put(group);
975aacdf198SThierry Reding 				return err;
9760c407de5SThierry Reding 			}
9770c407de5SThierry Reding 		}
978fa6661b7SThierry Reding 
979fa6661b7SThierry Reding 		tegra->use_explicit_iommu = true;
9800c407de5SThierry Reding 	}
9810c407de5SThierry Reding 
982aacdf198SThierry Reding 	client->group = group;
983aacdf198SThierry Reding 
984aacdf198SThierry Reding 	return 0;
9850c407de5SThierry Reding }
9860c407de5SThierry Reding 
host1x_client_iommu_detach(struct host1x_client * client)987aacdf198SThierry Reding void host1x_client_iommu_detach(struct host1x_client *client)
9880c407de5SThierry Reding {
989608f43adSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
9900c407de5SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
9917edd7961SThierry Reding 	struct iommu_domain *domain;
9920c407de5SThierry Reding 
993aacdf198SThierry Reding 	if (client->group) {
9947edd7961SThierry Reding 		/*
9957edd7961SThierry Reding 		 * Devices that are part of the same group may no longer be
9967edd7961SThierry Reding 		 * attached to a domain at this point because their group may
9977edd7961SThierry Reding 		 * have been detached by an earlier client.
9987edd7961SThierry Reding 		 */
9997edd7961SThierry Reding 		domain = iommu_get_domain_for_dev(client->dev);
10007edd7961SThierry Reding 		if (domain)
1001aacdf198SThierry Reding 			iommu_detach_group(tegra->domain, client->group);
10020c407de5SThierry Reding 
1003aacdf198SThierry Reding 		iommu_group_put(client->group);
1004fa6661b7SThierry Reding 		client->group = NULL;
10050c407de5SThierry Reding 	}
10060c407de5SThierry Reding }
10070c407de5SThierry Reding 
tegra_drm_alloc(struct tegra_drm * tegra,size_t size,dma_addr_t * dma)100867485fb8SThierry Reding void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *dma)
1009ad926015SMikko Perttunen {
1010ad926015SMikko Perttunen 	struct iova *alloc;
1011ad926015SMikko Perttunen 	void *virt;
1012ad926015SMikko Perttunen 	gfp_t gfp;
1013ad926015SMikko Perttunen 	int err;
1014ad926015SMikko Perttunen 
1015ad926015SMikko Perttunen 	if (tegra->domain)
1016ad926015SMikko Perttunen 		size = iova_align(&tegra->carveout.domain, size);
1017ad926015SMikko Perttunen 	else
1018ad926015SMikko Perttunen 		size = PAGE_ALIGN(size);
1019ad926015SMikko Perttunen 
1020ad926015SMikko Perttunen 	gfp = GFP_KERNEL | __GFP_ZERO;
1021ad926015SMikko Perttunen 	if (!tegra->domain) {
1022ad926015SMikko Perttunen 		/*
1023ad926015SMikko Perttunen 		 * Many units only support 32-bit addresses, even on 64-bit
1024ad926015SMikko Perttunen 		 * SoCs. If there is no IOMMU to translate into a 32-bit IO
1025ad926015SMikko Perttunen 		 * virtual address space, force allocations to be in the
1026ad926015SMikko Perttunen 		 * lower 32-bit range.
1027ad926015SMikko Perttunen 		 */
1028ad926015SMikko Perttunen 		gfp |= GFP_DMA;
1029ad926015SMikko Perttunen 	}
1030ad926015SMikko Perttunen 
1031ad926015SMikko Perttunen 	virt = (void *)__get_free_pages(gfp, get_order(size));
1032ad926015SMikko Perttunen 	if (!virt)
1033ad926015SMikko Perttunen 		return ERR_PTR(-ENOMEM);
1034ad926015SMikko Perttunen 
1035ad926015SMikko Perttunen 	if (!tegra->domain) {
1036ad926015SMikko Perttunen 		/*
1037ad926015SMikko Perttunen 		 * If IOMMU is disabled, devices address physical memory
1038ad926015SMikko Perttunen 		 * directly.
1039ad926015SMikko Perttunen 		 */
1040ad926015SMikko Perttunen 		*dma = virt_to_phys(virt);
1041ad926015SMikko Perttunen 		return virt;
1042ad926015SMikko Perttunen 	}
1043ad926015SMikko Perttunen 
1044ad926015SMikko Perttunen 	alloc = alloc_iova(&tegra->carveout.domain,
1045ad926015SMikko Perttunen 			   size >> tegra->carveout.shift,
1046ad926015SMikko Perttunen 			   tegra->carveout.limit, true);
1047ad926015SMikko Perttunen 	if (!alloc) {
1048ad926015SMikko Perttunen 		err = -EBUSY;
1049ad926015SMikko Perttunen 		goto free_pages;
1050ad926015SMikko Perttunen 	}
1051ad926015SMikko Perttunen 
1052ad926015SMikko Perttunen 	*dma = iova_dma_addr(&tegra->carveout.domain, alloc);
1053ad926015SMikko Perttunen 	err = iommu_map(tegra->domain, *dma, virt_to_phys(virt),
10541369459bSJason Gunthorpe 			size, IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
1055ad926015SMikko Perttunen 	if (err < 0)
1056ad926015SMikko Perttunen 		goto free_iova;
1057ad926015SMikko Perttunen 
1058ad926015SMikko Perttunen 	return virt;
1059ad926015SMikko Perttunen 
1060ad926015SMikko Perttunen free_iova:
1061ad926015SMikko Perttunen 	__free_iova(&tegra->carveout.domain, alloc);
1062ad926015SMikko Perttunen free_pages:
1063ad926015SMikko Perttunen 	free_pages((unsigned long)virt, get_order(size));
1064ad926015SMikko Perttunen 
1065ad926015SMikko Perttunen 	return ERR_PTR(err);
1066ad926015SMikko Perttunen }
1067ad926015SMikko Perttunen 
tegra_drm_free(struct tegra_drm * tegra,size_t size,void * virt,dma_addr_t dma)1068ad926015SMikko Perttunen void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt,
1069ad926015SMikko Perttunen 		    dma_addr_t dma)
1070ad926015SMikko Perttunen {
1071ad926015SMikko Perttunen 	if (tegra->domain)
1072ad926015SMikko Perttunen 		size = iova_align(&tegra->carveout.domain, size);
1073ad926015SMikko Perttunen 	else
1074ad926015SMikko Perttunen 		size = PAGE_ALIGN(size);
1075ad926015SMikko Perttunen 
1076ad926015SMikko Perttunen 	if (tegra->domain) {
1077ad926015SMikko Perttunen 		iommu_unmap(tegra->domain, dma, size);
1078ad926015SMikko Perttunen 		free_iova(&tegra->carveout.domain,
1079ad926015SMikko Perttunen 			  iova_pfn(&tegra->carveout.domain, dma));
1080ad926015SMikko Perttunen 	}
1081ad926015SMikko Perttunen 
1082ad926015SMikko Perttunen 	free_pages((unsigned long)virt, get_order(size));
1083ad926015SMikko Perttunen }
1084ad926015SMikko Perttunen 
host1x_drm_wants_iommu(struct host1x_device * dev)10852d9384ffSThierry Reding static bool host1x_drm_wants_iommu(struct host1x_device *dev)
1086dee8268fSThierry Reding {
1087501be6c1SThierry Reding 	struct host1x *host1x = dev_get_drvdata(dev->dev.parent);
1088fa6661b7SThierry Reding 	struct iommu_domain *domain;
1089a7303f77SThierry Reding 
1090c2418f91SRobin Murphy 	/* Our IOMMU usage policy doesn't currently play well with GART */
1091c2418f91SRobin Murphy 	if (of_machine_is_compatible("nvidia,tegra20"))
1092c2418f91SRobin Murphy 		return false;
1093c2418f91SRobin Murphy 
1094fa6661b7SThierry Reding 	/*
1095fa6661b7SThierry Reding 	 * If the Tegra DRM clients are backed by an IOMMU, push buffers are
1096fa6661b7SThierry Reding 	 * likely to be allocated beyond the 32-bit boundary if sufficient
1097fa6661b7SThierry Reding 	 * system memory is available. This is problematic on earlier Tegra
1098fa6661b7SThierry Reding 	 * generations where host1x supports a maximum of 32 address bits in
1099fa6661b7SThierry Reding 	 * the GATHER opcode. In this case, unless host1x is behind an IOMMU
1100fa6661b7SThierry Reding 	 * as well it won't be able to process buffers allocated beyond the
1101fa6661b7SThierry Reding 	 * 32-bit boundary.
1102fa6661b7SThierry Reding 	 *
1103fa6661b7SThierry Reding 	 * The DMA API will use bounce buffers in this case, so that could
1104fa6661b7SThierry Reding 	 * perhaps still be made to work, even if less efficient, but there
1105fa6661b7SThierry Reding 	 * is another catch: in order to perform cache maintenance on pages
1106fa6661b7SThierry Reding 	 * allocated for discontiguous buffers we need to map and unmap the
1107fa6661b7SThierry Reding 	 * SG table representing these buffers. This is fine for something
1108fa6661b7SThierry Reding 	 * small like a push buffer, but it exhausts the bounce buffer pool
1109fa6661b7SThierry Reding 	 * (typically on the order of a few MiB) for framebuffers (many MiB
1110fa6661b7SThierry Reding 	 * for any modern resolution).
1111fa6661b7SThierry Reding 	 *
1112fa6661b7SThierry Reding 	 * Work around this by making sure that Tegra DRM clients only use
1113fa6661b7SThierry Reding 	 * an IOMMU if the parent host1x also uses an IOMMU.
1114fa6661b7SThierry Reding 	 *
1115fa6661b7SThierry Reding 	 * Note that there's still a small gap here that we don't cover: if
1116fa6661b7SThierry Reding 	 * the DMA API is backed by an IOMMU there's no way to control which
1117fa6661b7SThierry Reding 	 * device is attached to an IOMMU and which isn't, except via wiring
1118fa6661b7SThierry Reding 	 * up the device tree appropriately. This is considered an problem
1119fa6661b7SThierry Reding 	 * of integration, so care must be taken for the DT to be consistent.
1120fa6661b7SThierry Reding 	 */
11212d9384ffSThierry Reding 	domain = iommu_get_domain_for_dev(dev->dev.parent);
1122fa6661b7SThierry Reding 
11232d9384ffSThierry Reding 	/*
11242d9384ffSThierry Reding 	 * Tegra20 and Tegra30 don't support addressing memory beyond the
11252d9384ffSThierry Reding 	 * 32-bit boundary, so the regular GATHER opcodes will always be
11262d9384ffSThierry Reding 	 * sufficient and whether or not the host1x is attached to an IOMMU
11272d9384ffSThierry Reding 	 * doesn't matter.
11282d9384ffSThierry Reding 	 */
1129501be6c1SThierry Reding 	if (!domain && host1x_get_dma_mask(host1x) <= DMA_BIT_MASK(32))
11302d9384ffSThierry Reding 		return true;
11312d9384ffSThierry Reding 
11322d9384ffSThierry Reding 	return domain != NULL;
11332d9384ffSThierry Reding }
11342d9384ffSThierry Reding 
host1x_drm_probe(struct host1x_device * dev)11352d9384ffSThierry Reding static int host1x_drm_probe(struct host1x_device *dev)
11362d9384ffSThierry Reding {
11372d9384ffSThierry Reding 	struct tegra_drm *tegra;
11382d9384ffSThierry Reding 	struct drm_device *drm;
11392d9384ffSThierry Reding 	int err;
11402d9384ffSThierry Reding 
114170a59dd8SDaniel Vetter 	drm = drm_dev_alloc(&tegra_drm_driver, &dev->dev);
11422d9384ffSThierry Reding 	if (IS_ERR(drm))
11432d9384ffSThierry Reding 		return PTR_ERR(drm);
11442d9384ffSThierry Reding 
11452d9384ffSThierry Reding 	tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
11462d9384ffSThierry Reding 	if (!tegra) {
11472d9384ffSThierry Reding 		err = -ENOMEM;
11482d9384ffSThierry Reding 		goto put;
11492d9384ffSThierry Reding 	}
11502d9384ffSThierry Reding 
11512d9384ffSThierry Reding 	if (host1x_drm_wants_iommu(dev) && iommu_present(&platform_bus_type)) {
1152a7303f77SThierry Reding 		tegra->domain = iommu_domain_alloc(&platform_bus_type);
1153a7303f77SThierry Reding 		if (!tegra->domain) {
1154a7303f77SThierry Reding 			err = -ENOMEM;
1155a7303f77SThierry Reding 			goto free;
1156a7303f77SThierry Reding 		}
1157a7303f77SThierry Reding 
1158a7303f77SThierry Reding 		err = iova_cache_get();
1159a7303f77SThierry Reding 		if (err < 0)
1160a7303f77SThierry Reding 			goto domain;
1161a7303f77SThierry Reding 	}
1162a7303f77SThierry Reding 
1163a7303f77SThierry Reding 	mutex_init(&tegra->clients_lock);
1164a7303f77SThierry Reding 	INIT_LIST_HEAD(&tegra->clients);
1165a7303f77SThierry Reding 
1166a7303f77SThierry Reding 	dev_set_drvdata(&dev->dev, drm);
1167a7303f77SThierry Reding 	drm->dev_private = tegra;
1168a7303f77SThierry Reding 	tegra->drm = drm;
1169a7303f77SThierry Reding 
1170a7303f77SThierry Reding 	drm_mode_config_init(drm);
1171a7303f77SThierry Reding 
1172a7303f77SThierry Reding 	drm->mode_config.min_width = 0;
1173a7303f77SThierry Reding 	drm->mode_config.min_height = 0;
1174042c0bd7SThierry Reding 	drm->mode_config.max_width = 0;
1175042c0bd7SThierry Reding 	drm->mode_config.max_height = 0;
1176a7303f77SThierry Reding 
1177a7303f77SThierry Reding 	drm->mode_config.normalize_zpos = true;
1178a7303f77SThierry Reding 
1179a7303f77SThierry Reding 	drm->mode_config.funcs = &tegra_drm_mode_config_funcs;
1180a7303f77SThierry Reding 	drm->mode_config.helper_private = &tegra_drm_mode_config_helpers;
1181a7303f77SThierry Reding 
1182a7303f77SThierry Reding 	drm_kms_helper_poll_init(drm);
1183a7303f77SThierry Reding 
1184a7303f77SThierry Reding 	err = host1x_device_init(dev);
1185a7303f77SThierry Reding 	if (err < 0)
118671ec16f4SThomas Zimmermann 		goto poll;
1187a7303f77SThierry Reding 
1188042c0bd7SThierry Reding 	/*
1189042c0bd7SThierry Reding 	 * Now that all display controller have been initialized, the maximum
1190042c0bd7SThierry Reding 	 * supported resolution is known and the bitmask for horizontal and
1191042c0bd7SThierry Reding 	 * vertical bitfields can be computed.
1192042c0bd7SThierry Reding 	 */
1193042c0bd7SThierry Reding 	tegra->hmask = drm->mode_config.max_width - 1;
1194042c0bd7SThierry Reding 	tegra->vmask = drm->mode_config.max_height - 1;
1195042c0bd7SThierry Reding 
1196fa6661b7SThierry Reding 	if (tegra->use_explicit_iommu) {
1197a7303f77SThierry Reding 		u64 carveout_start, carveout_end, gem_start, gem_end;
1198a7303f77SThierry Reding 		u64 dma_mask = dma_get_mask(&dev->dev);
1199a7303f77SThierry Reding 		dma_addr_t start, end;
1200a7303f77SThierry Reding 		unsigned long order;
1201a7303f77SThierry Reding 
1202a7303f77SThierry Reding 		start = tegra->domain->geometry.aperture_start & dma_mask;
1203a7303f77SThierry Reding 		end = tegra->domain->geometry.aperture_end & dma_mask;
1204a7303f77SThierry Reding 
1205a7303f77SThierry Reding 		gem_start = start;
1206a7303f77SThierry Reding 		gem_end = end - CARVEOUT_SZ;
1207a7303f77SThierry Reding 		carveout_start = gem_end + 1;
1208a7303f77SThierry Reding 		carveout_end = end;
1209a7303f77SThierry Reding 
1210a7303f77SThierry Reding 		order = __ffs(tegra->domain->pgsize_bitmap);
1211a7303f77SThierry Reding 		init_iova_domain(&tegra->carveout.domain, 1UL << order,
1212a7303f77SThierry Reding 				 carveout_start >> order);
1213a7303f77SThierry Reding 
1214a7303f77SThierry Reding 		tegra->carveout.shift = iova_shift(&tegra->carveout.domain);
1215a7303f77SThierry Reding 		tegra->carveout.limit = carveout_end >> tegra->carveout.shift;
1216a7303f77SThierry Reding 
1217a7303f77SThierry Reding 		drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1);
1218a7303f77SThierry Reding 		mutex_init(&tegra->mm_lock);
1219a7303f77SThierry Reding 
1220a7303f77SThierry Reding 		DRM_DEBUG_DRIVER("IOMMU apertures:\n");
1221a7303f77SThierry Reding 		DRM_DEBUG_DRIVER("  GEM: %#llx-%#llx\n", gem_start, gem_end);
1222a7303f77SThierry Reding 		DRM_DEBUG_DRIVER("  Carveout: %#llx-%#llx\n", carveout_start,
1223a7303f77SThierry Reding 				 carveout_end);
1224fa6661b7SThierry Reding 	} else if (tegra->domain) {
1225fa6661b7SThierry Reding 		iommu_domain_free(tegra->domain);
1226fa6661b7SThierry Reding 		tegra->domain = NULL;
1227fa6661b7SThierry Reding 		iova_cache_put();
1228a7303f77SThierry Reding 	}
1229a7303f77SThierry Reding 
1230a7303f77SThierry Reding 	if (tegra->hub) {
1231a7303f77SThierry Reding 		err = tegra_display_hub_prepare(tegra->hub);
1232a7303f77SThierry Reding 		if (err < 0)
1233a7303f77SThierry Reding 			goto device;
1234a7303f77SThierry Reding 	}
1235a7303f77SThierry Reding 
1236a7303f77SThierry Reding 	/* syncpoints are used for full 32-bit hardware VBLANK counters */
1237a7303f77SThierry Reding 	drm->max_vblank_count = 0xffffffff;
1238a7303f77SThierry Reding 
1239a7303f77SThierry Reding 	err = drm_vblank_init(drm, drm->mode_config.num_crtc);
1240a7303f77SThierry Reding 	if (err < 0)
1241a7303f77SThierry Reding 		goto hub;
1242a7303f77SThierry Reding 
1243a7303f77SThierry Reding 	drm_mode_config_reset(drm);
1244a7303f77SThierry Reding 
1245*8e8c66afSThierry Reding 	/*
1246*8e8c66afSThierry Reding 	 * Only take over from a potential firmware framebuffer if any CRTCs
1247*8e8c66afSThierry Reding 	 * have been registered. This must not be a fatal error because there
1248*8e8c66afSThierry Reding 	 * are other accelerators that are exposed via this driver.
1249*8e8c66afSThierry Reding 	 *
1250*8e8c66afSThierry Reding 	 * Another case where this happens is on Tegra234 where the display
1251*8e8c66afSThierry Reding 	 * hardware is no longer part of the host1x complex, so this driver
1252*8e8c66afSThierry Reding 	 * will not expose any modesetting features.
1253*8e8c66afSThierry Reding 	 */
1254*8e8c66afSThierry Reding 	if (drm->mode_config.num_crtc > 0) {
125562aeaeaaSDaniel Vetter 		err = drm_aperture_remove_framebuffers(&tegra_drm_driver);
1256a7303f77SThierry Reding 		if (err < 0)
1257a7303f77SThierry Reding 			goto hub;
1258*8e8c66afSThierry Reding 	} else {
1259*8e8c66afSThierry Reding 		/*
1260*8e8c66afSThierry Reding 		 * Indicate to userspace that this doesn't expose any display
1261*8e8c66afSThierry Reding 		 * capabilities.
1262*8e8c66afSThierry Reding 		 */
1263*8e8c66afSThierry Reding 		drm->driver_features &= ~(DRIVER_MODESET | DRIVER_ATOMIC);
1264*8e8c66afSThierry Reding 	}
1265a7303f77SThierry Reding 
126671ec16f4SThomas Zimmermann 	err = drm_dev_register(drm, 0);
1267a7303f77SThierry Reding 	if (err < 0)
1268a7303f77SThierry Reding 		goto hub;
12696e4228fbSMichał Mirosław 
127071ec16f4SThomas Zimmermann 	tegra_fbdev_setup(drm);
12719910f5c4SThierry Reding 
12729910f5c4SThierry Reding 	return 0;
12739910f5c4SThierry Reding 
1274a7303f77SThierry Reding hub:
1275a7303f77SThierry Reding 	if (tegra->hub)
1276a7303f77SThierry Reding 		tegra_display_hub_cleanup(tegra->hub);
1277a7303f77SThierry Reding device:
1278a7303f77SThierry Reding 	if (tegra->domain) {
1279a7303f77SThierry Reding 		mutex_destroy(&tegra->mm_lock);
1280a7303f77SThierry Reding 		drm_mm_takedown(&tegra->mm);
1281a7303f77SThierry Reding 		put_iova_domain(&tegra->carveout.domain);
1282a7303f77SThierry Reding 		iova_cache_put();
1283a7303f77SThierry Reding 	}
1284a7303f77SThierry Reding 
1285a7303f77SThierry Reding 	host1x_device_exit(dev);
128671ec16f4SThomas Zimmermann poll:
1287a7303f77SThierry Reding 	drm_kms_helper_poll_fini(drm);
1288a7303f77SThierry Reding 	drm_mode_config_cleanup(drm);
1289a7303f77SThierry Reding domain:
1290a7303f77SThierry Reding 	if (tegra->domain)
1291a7303f77SThierry Reding 		iommu_domain_free(tegra->domain);
1292a7303f77SThierry Reding free:
1293a7303f77SThierry Reding 	kfree(tegra);
12949c942096SThomas Zimmermann put:
12959c942096SThomas Zimmermann 	drm_dev_put(drm);
12969910f5c4SThierry Reding 	return err;
1297dee8268fSThierry Reding }
1298dee8268fSThierry Reding 
host1x_drm_remove(struct host1x_device * dev)12999910f5c4SThierry Reding static int host1x_drm_remove(struct host1x_device *dev)
1300dee8268fSThierry Reding {
13019910f5c4SThierry Reding 	struct drm_device *drm = dev_get_drvdata(&dev->dev);
1302a7303f77SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
1303a7303f77SThierry Reding 	int err;
13049910f5c4SThierry Reding 
13059910f5c4SThierry Reding 	drm_dev_unregister(drm);
1306a7303f77SThierry Reding 
1307a7303f77SThierry Reding 	drm_kms_helper_poll_fini(drm);
1308a7303f77SThierry Reding 	drm_atomic_helper_shutdown(drm);
1309a7303f77SThierry Reding 	drm_mode_config_cleanup(drm);
1310a7303f77SThierry Reding 
1311d66dfcf8SThierry Reding 	if (tegra->hub)
1312d66dfcf8SThierry Reding 		tegra_display_hub_cleanup(tegra->hub);
1313d66dfcf8SThierry Reding 
1314a7303f77SThierry Reding 	err = host1x_device_exit(dev);
1315a7303f77SThierry Reding 	if (err < 0)
1316a7303f77SThierry Reding 		dev_err(&dev->dev, "host1x device cleanup failed: %d\n", err);
1317a7303f77SThierry Reding 
1318a7303f77SThierry Reding 	if (tegra->domain) {
1319a7303f77SThierry Reding 		mutex_destroy(&tegra->mm_lock);
1320a7303f77SThierry Reding 		drm_mm_takedown(&tegra->mm);
1321a7303f77SThierry Reding 		put_iova_domain(&tegra->carveout.domain);
1322a7303f77SThierry Reding 		iova_cache_put();
1323a7303f77SThierry Reding 		iommu_domain_free(tegra->domain);
1324a7303f77SThierry Reding 	}
1325a7303f77SThierry Reding 
1326a7303f77SThierry Reding 	kfree(tegra);
13279c942096SThomas Zimmermann 	drm_dev_put(drm);
1328dee8268fSThierry Reding 
1329dee8268fSThierry Reding 	return 0;
1330dee8268fSThierry Reding }
1331dee8268fSThierry Reding 
1332359ae687SThierry Reding #ifdef CONFIG_PM_SLEEP
host1x_drm_suspend(struct device * dev)1333359ae687SThierry Reding static int host1x_drm_suspend(struct device *dev)
1334359ae687SThierry Reding {
1335359ae687SThierry Reding 	struct drm_device *drm = dev_get_drvdata(dev);
1336359ae687SThierry Reding 
133753f1e062SSouptick Joarder 	return drm_mode_config_helper_suspend(drm);
1338359ae687SThierry Reding }
1339359ae687SThierry Reding 
host1x_drm_resume(struct device * dev)1340359ae687SThierry Reding static int host1x_drm_resume(struct device *dev)
1341359ae687SThierry Reding {
1342359ae687SThierry Reding 	struct drm_device *drm = dev_get_drvdata(dev);
1343359ae687SThierry Reding 
134453f1e062SSouptick Joarder 	return drm_mode_config_helper_resume(drm);
1345359ae687SThierry Reding }
1346359ae687SThierry Reding #endif
1347359ae687SThierry Reding 
1348a13f1dc4SThierry Reding static SIMPLE_DEV_PM_OPS(host1x_drm_pm_ops, host1x_drm_suspend,
1349a13f1dc4SThierry Reding 			 host1x_drm_resume);
1350359ae687SThierry Reding 
1351dee8268fSThierry Reding static const struct of_device_id host1x_drm_subdevs[] = {
1352dee8268fSThierry Reding 	{ .compatible = "nvidia,tegra20-dc", },
1353dee8268fSThierry Reding 	{ .compatible = "nvidia,tegra20-hdmi", },
1354dee8268fSThierry Reding 	{ .compatible = "nvidia,tegra20-gr2d", },
13555f60ed0dSThierry Reding 	{ .compatible = "nvidia,tegra20-gr3d", },
1356dee8268fSThierry Reding 	{ .compatible = "nvidia,tegra30-dc", },
1357dee8268fSThierry Reding 	{ .compatible = "nvidia,tegra30-hdmi", },
1358dee8268fSThierry Reding 	{ .compatible = "nvidia,tegra30-gr2d", },
13595f60ed0dSThierry Reding 	{ .compatible = "nvidia,tegra30-gr3d", },
1360e87ba0feSDmitry Osipenko 	{ .compatible = "nvidia,tegra114-dc", },
1361dec72739SThierry Reding 	{ .compatible = "nvidia,tegra114-dsi", },
13627d1d28acSMikko Perttunen 	{ .compatible = "nvidia,tegra114-hdmi", },
13633ef170c2SDmitry Osipenko 	{ .compatible = "nvidia,tegra114-gr2d", },
13645f60ed0dSThierry Reding 	{ .compatible = "nvidia,tegra114-gr3d", },
13658620fc62SThierry Reding 	{ .compatible = "nvidia,tegra124-dc", },
13666b6b6042SThierry Reding 	{ .compatible = "nvidia,tegra124-sor", },
1367fb7be70eSThierry Reding 	{ .compatible = "nvidia,tegra124-hdmi", },
13687d338587SThierry Reding 	{ .compatible = "nvidia,tegra124-dsi", },
13690ae797a8SArto Merilainen 	{ .compatible = "nvidia,tegra124-vic", },
1370c06c7930SThierry Reding 	{ .compatible = "nvidia,tegra132-dsi", },
13715b4f516fSThierry Reding 	{ .compatible = "nvidia,tegra210-dc", },
1372ddfb406bSThierry Reding 	{ .compatible = "nvidia,tegra210-dsi", },
13733309ac83SThierry Reding 	{ .compatible = "nvidia,tegra210-sor", },
1374459cc2c6SThierry Reding 	{ .compatible = "nvidia,tegra210-sor1", },
13750ae797a8SArto Merilainen 	{ .compatible = "nvidia,tegra210-vic", },
137646f226c9SMikko Perttunen 	{ .compatible = "nvidia,tegra210-nvdec", },
1377c4755fb9SThierry Reding 	{ .compatible = "nvidia,tegra186-display", },
137847307954SThierry Reding 	{ .compatible = "nvidia,tegra186-dc", },
1379c57997bcSThierry Reding 	{ .compatible = "nvidia,tegra186-sor", },
1380c57997bcSThierry Reding 	{ .compatible = "nvidia,tegra186-sor1", },
13816e44b9adSMikko Perttunen 	{ .compatible = "nvidia,tegra186-vic", },
138246f226c9SMikko Perttunen 	{ .compatible = "nvidia,tegra186-nvdec", },
13835725daaaSThierry Reding 	{ .compatible = "nvidia,tegra194-display", },
138447443196SThierry Reding 	{ .compatible = "nvidia,tegra194-dc", },
13859b6c14b8SThierry Reding 	{ .compatible = "nvidia,tegra194-sor", },
1386d6b9bc02SThierry Reding 	{ .compatible = "nvidia,tegra194-vic", },
138746f226c9SMikko Perttunen 	{ .compatible = "nvidia,tegra194-nvdec", },
13889550669cSMikko Perttunen 	{ .compatible = "nvidia,tegra234-vic", },
1389fbc82b9bSMikko Perttunen 	{ .compatible = "nvidia,tegra234-nvdec", },
1390dee8268fSThierry Reding 	{ /* sentinel */ }
1391dee8268fSThierry Reding };
1392dee8268fSThierry Reding 
1393dee8268fSThierry Reding static struct host1x_driver host1x_drm_driver = {
1394f4c5cf88SThierry Reding 	.driver = {
1395dee8268fSThierry Reding 		.name = "drm",
1396359ae687SThierry Reding 		.pm = &host1x_drm_pm_ops,
1397f4c5cf88SThierry Reding 	},
1398dee8268fSThierry Reding 	.probe = host1x_drm_probe,
1399dee8268fSThierry Reding 	.remove = host1x_drm_remove,
1400dee8268fSThierry Reding 	.subdevs = host1x_drm_subdevs,
1401dee8268fSThierry Reding };
1402dee8268fSThierry Reding 
1403473112e4SThierry Reding static struct platform_driver * const drivers[] = {
1404c4755fb9SThierry Reding 	&tegra_display_hub_driver,
1405473112e4SThierry Reding 	&tegra_dc_driver,
1406473112e4SThierry Reding 	&tegra_hdmi_driver,
1407473112e4SThierry Reding 	&tegra_dsi_driver,
1408473112e4SThierry Reding 	&tegra_dpaux_driver,
1409473112e4SThierry Reding 	&tegra_sor_driver,
1410473112e4SThierry Reding 	&tegra_gr2d_driver,
1411473112e4SThierry Reding 	&tegra_gr3d_driver,
14120ae797a8SArto Merilainen 	&tegra_vic_driver,
141346f226c9SMikko Perttunen 	&tegra_nvdec_driver,
1414473112e4SThierry Reding };
1415473112e4SThierry Reding 
host1x_drm_init(void)1416dee8268fSThierry Reding static int __init host1x_drm_init(void)
1417dee8268fSThierry Reding {
1418dee8268fSThierry Reding 	int err;
1419dee8268fSThierry Reding 
142093804f5dSJavier Martinez Canillas 	if (drm_firmware_drivers_only())
142193804f5dSJavier Martinez Canillas 		return -ENODEV;
142293804f5dSJavier Martinez Canillas 
1423dee8268fSThierry Reding 	err = host1x_driver_register(&host1x_drm_driver);
1424dee8268fSThierry Reding 	if (err < 0)
1425dee8268fSThierry Reding 		return err;
1426dee8268fSThierry Reding 
1427473112e4SThierry Reding 	err = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
1428dee8268fSThierry Reding 	if (err < 0)
1429dee8268fSThierry Reding 		goto unregister_host1x;
1430dee8268fSThierry Reding 
1431dee8268fSThierry Reding 	return 0;
1432dee8268fSThierry Reding 
1433dee8268fSThierry Reding unregister_host1x:
1434dee8268fSThierry Reding 	host1x_driver_unregister(&host1x_drm_driver);
1435dee8268fSThierry Reding 	return err;
1436dee8268fSThierry Reding }
1437dee8268fSThierry Reding module_init(host1x_drm_init);
1438dee8268fSThierry Reding 
host1x_drm_exit(void)1439dee8268fSThierry Reding static void __exit host1x_drm_exit(void)
1440dee8268fSThierry Reding {
1441473112e4SThierry Reding 	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
1442dee8268fSThierry Reding 	host1x_driver_unregister(&host1x_drm_driver);
1443dee8268fSThierry Reding }
1444dee8268fSThierry Reding module_exit(host1x_drm_exit);
1445dee8268fSThierry Reding 
1446dee8268fSThierry Reding MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
1447dee8268fSThierry Reding MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
1448dee8268fSThierry Reding MODULE_LICENSE("GPL v2");
1449