162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2012 Avionic Design GmbH
462306a36Sopenharmony_ci * Copyright (C) 2012-2016 NVIDIA CORPORATION.  All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/bitops.h>
862306a36Sopenharmony_ci#include <linux/host1x.h>
962306a36Sopenharmony_ci#include <linux/idr.h>
1062306a36Sopenharmony_ci#include <linux/iommu.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <drm/drm_aperture.h>
1662306a36Sopenharmony_ci#include <drm/drm_atomic.h>
1762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
1862306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
1962306a36Sopenharmony_ci#include <drm/drm_drv.h>
2062306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2162306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
2262306a36Sopenharmony_ci#include <drm/drm_ioctl.h>
2362306a36Sopenharmony_ci#include <drm/drm_prime.h>
2462306a36Sopenharmony_ci#include <drm/drm_vblank.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
2762306a36Sopenharmony_ci#include <asm/dma-iommu.h>
2862306a36Sopenharmony_ci#endif
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "dc.h"
3162306a36Sopenharmony_ci#include "drm.h"
3262306a36Sopenharmony_ci#include "gem.h"
3362306a36Sopenharmony_ci#include "uapi.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define DRIVER_NAME "tegra"
3662306a36Sopenharmony_ci#define DRIVER_DESC "NVIDIA Tegra graphics"
3762306a36Sopenharmony_ci#define DRIVER_DATE "20120330"
3862306a36Sopenharmony_ci#define DRIVER_MAJOR 1
3962306a36Sopenharmony_ci#define DRIVER_MINOR 0
4062306a36Sopenharmony_ci#define DRIVER_PATCHLEVEL 0
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define CARVEOUT_SZ SZ_64M
4362306a36Sopenharmony_ci#define CDMA_GATHER_FETCHES_MAX_NB 16383
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int tegra_atomic_check(struct drm_device *drm,
4662306a36Sopenharmony_ci			      struct drm_atomic_state *state)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int err;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	err = drm_atomic_helper_check(drm, state);
5162306a36Sopenharmony_ci	if (err < 0)
5262306a36Sopenharmony_ci		return err;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return tegra_display_hub_atomic_check(drm, state);
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = {
5862306a36Sopenharmony_ci	.fb_create = tegra_fb_create,
5962306a36Sopenharmony_ci	.atomic_check = tegra_atomic_check,
6062306a36Sopenharmony_ci	.atomic_commit = drm_atomic_helper_commit,
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void tegra_atomic_post_commit(struct drm_device *drm,
6462306a36Sopenharmony_ci				     struct drm_atomic_state *old_state)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct drm_crtc_state *old_crtc_state __maybe_unused;
6762306a36Sopenharmony_ci	struct drm_crtc *crtc;
6862306a36Sopenharmony_ci	unsigned int i;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i)
7162306a36Sopenharmony_ci		tegra_crtc_atomic_post_commit(crtc, old_state);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void tegra_atomic_commit_tail(struct drm_atomic_state *old_state)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct drm_device *drm = old_state->dev;
7762306a36Sopenharmony_ci	struct tegra_drm *tegra = drm->dev_private;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (tegra->hub) {
8062306a36Sopenharmony_ci		bool fence_cookie = dma_fence_begin_signalling();
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		drm_atomic_helper_commit_modeset_disables(drm, old_state);
8362306a36Sopenharmony_ci		tegra_display_hub_atomic_commit(drm, old_state);
8462306a36Sopenharmony_ci		drm_atomic_helper_commit_planes(drm, old_state, 0);
8562306a36Sopenharmony_ci		drm_atomic_helper_commit_modeset_enables(drm, old_state);
8662306a36Sopenharmony_ci		drm_atomic_helper_commit_hw_done(old_state);
8762306a36Sopenharmony_ci		dma_fence_end_signalling(fence_cookie);
8862306a36Sopenharmony_ci		drm_atomic_helper_wait_for_vblanks(drm, old_state);
8962306a36Sopenharmony_ci		drm_atomic_helper_cleanup_planes(drm, old_state);
9062306a36Sopenharmony_ci	} else {
9162306a36Sopenharmony_ci		drm_atomic_helper_commit_tail_rpm(old_state);
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	tegra_atomic_post_commit(drm, old_state);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic const struct drm_mode_config_helper_funcs
9862306a36Sopenharmony_citegra_drm_mode_config_helpers = {
9962306a36Sopenharmony_ci	.atomic_commit_tail = tegra_atomic_commit_tail,
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct tegra_drm_file *fpriv;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
10762306a36Sopenharmony_ci	if (!fpriv)
10862306a36Sopenharmony_ci		return -ENOMEM;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	idr_init_base(&fpriv->legacy_contexts, 1);
11162306a36Sopenharmony_ci	xa_init_flags(&fpriv->contexts, XA_FLAGS_ALLOC1);
11262306a36Sopenharmony_ci	xa_init(&fpriv->syncpoints);
11362306a36Sopenharmony_ci	mutex_init(&fpriv->lock);
11462306a36Sopenharmony_ci	filp->driver_priv = fpriv;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void tegra_drm_context_free(struct tegra_drm_context *context)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	context->client->ops->close_channel(context);
12262306a36Sopenharmony_ci	pm_runtime_put(context->client->base.dev);
12362306a36Sopenharmony_ci	kfree(context);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
12762306a36Sopenharmony_ci				       struct drm_tegra_reloc __user *src,
12862306a36Sopenharmony_ci				       struct drm_device *drm,
12962306a36Sopenharmony_ci				       struct drm_file *file)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	u32 cmdbuf, target;
13262306a36Sopenharmony_ci	int err;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	err = get_user(cmdbuf, &src->cmdbuf.handle);
13562306a36Sopenharmony_ci	if (err < 0)
13662306a36Sopenharmony_ci		return err;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset);
13962306a36Sopenharmony_ci	if (err < 0)
14062306a36Sopenharmony_ci		return err;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	err = get_user(target, &src->target.handle);
14362306a36Sopenharmony_ci	if (err < 0)
14462306a36Sopenharmony_ci		return err;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	err = get_user(dest->target.offset, &src->target.offset);
14762306a36Sopenharmony_ci	if (err < 0)
14862306a36Sopenharmony_ci		return err;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	err = get_user(dest->shift, &src->shift);
15162306a36Sopenharmony_ci	if (err < 0)
15262306a36Sopenharmony_ci		return err;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	dest->flags = HOST1X_RELOC_READ | HOST1X_RELOC_WRITE;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	dest->cmdbuf.bo = tegra_gem_lookup(file, cmdbuf);
15762306a36Sopenharmony_ci	if (!dest->cmdbuf.bo)
15862306a36Sopenharmony_ci		return -ENOENT;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	dest->target.bo = tegra_gem_lookup(file, target);
16162306a36Sopenharmony_ci	if (!dest->target.bo)
16262306a36Sopenharmony_ci		return -ENOENT;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return 0;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ciint tegra_drm_submit(struct tegra_drm_context *context,
16862306a36Sopenharmony_ci		     struct drm_tegra_submit *args, struct drm_device *drm,
16962306a36Sopenharmony_ci		     struct drm_file *file)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct host1x_client *client = &context->client->base;
17262306a36Sopenharmony_ci	unsigned int num_cmdbufs = args->num_cmdbufs;
17362306a36Sopenharmony_ci	unsigned int num_relocs = args->num_relocs;
17462306a36Sopenharmony_ci	struct drm_tegra_cmdbuf __user *user_cmdbufs;
17562306a36Sopenharmony_ci	struct drm_tegra_reloc __user *user_relocs;
17662306a36Sopenharmony_ci	struct drm_tegra_syncpt __user *user_syncpt;
17762306a36Sopenharmony_ci	struct drm_tegra_syncpt syncpt;
17862306a36Sopenharmony_ci	struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
17962306a36Sopenharmony_ci	struct drm_gem_object **refs;
18062306a36Sopenharmony_ci	struct host1x_syncpt *sp = NULL;
18162306a36Sopenharmony_ci	struct host1x_job *job;
18262306a36Sopenharmony_ci	unsigned int num_refs;
18362306a36Sopenharmony_ci	int err;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	user_cmdbufs = u64_to_user_ptr(args->cmdbufs);
18662306a36Sopenharmony_ci	user_relocs = u64_to_user_ptr(args->relocs);
18762306a36Sopenharmony_ci	user_syncpt = u64_to_user_ptr(args->syncpts);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* We don't yet support other than one syncpt_incr struct per submit */
19062306a36Sopenharmony_ci	if (args->num_syncpts != 1)
19162306a36Sopenharmony_ci		return -EINVAL;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* We don't yet support waitchks */
19462306a36Sopenharmony_ci	if (args->num_waitchks != 0)
19562306a36Sopenharmony_ci		return -EINVAL;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	job = host1x_job_alloc(context->channel, args->num_cmdbufs,
19862306a36Sopenharmony_ci			       args->num_relocs, false);
19962306a36Sopenharmony_ci	if (!job)
20062306a36Sopenharmony_ci		return -ENOMEM;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	job->num_relocs = args->num_relocs;
20362306a36Sopenharmony_ci	job->client = client;
20462306a36Sopenharmony_ci	job->class = client->class;
20562306a36Sopenharmony_ci	job->serialize = true;
20662306a36Sopenharmony_ci	job->syncpt_recovery = true;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/*
20962306a36Sopenharmony_ci	 * Track referenced BOs so that they can be unreferenced after the
21062306a36Sopenharmony_ci	 * submission is complete.
21162306a36Sopenharmony_ci	 */
21262306a36Sopenharmony_ci	num_refs = num_cmdbufs + num_relocs * 2;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	refs = kmalloc_array(num_refs, sizeof(*refs), GFP_KERNEL);
21562306a36Sopenharmony_ci	if (!refs) {
21662306a36Sopenharmony_ci		err = -ENOMEM;
21762306a36Sopenharmony_ci		goto put;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* reuse as an iterator later */
22162306a36Sopenharmony_ci	num_refs = 0;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	while (num_cmdbufs) {
22462306a36Sopenharmony_ci		struct drm_tegra_cmdbuf cmdbuf;
22562306a36Sopenharmony_ci		struct host1x_bo *bo;
22662306a36Sopenharmony_ci		struct tegra_bo *obj;
22762306a36Sopenharmony_ci		u64 offset;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		if (copy_from_user(&cmdbuf, user_cmdbufs, sizeof(cmdbuf))) {
23062306a36Sopenharmony_ci			err = -EFAULT;
23162306a36Sopenharmony_ci			goto fail;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		/*
23562306a36Sopenharmony_ci		 * The maximum number of CDMA gather fetches is 16383, a higher
23662306a36Sopenharmony_ci		 * value means the words count is malformed.
23762306a36Sopenharmony_ci		 */
23862306a36Sopenharmony_ci		if (cmdbuf.words > CDMA_GATHER_FETCHES_MAX_NB) {
23962306a36Sopenharmony_ci			err = -EINVAL;
24062306a36Sopenharmony_ci			goto fail;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		bo = tegra_gem_lookup(file, cmdbuf.handle);
24462306a36Sopenharmony_ci		if (!bo) {
24562306a36Sopenharmony_ci			err = -ENOENT;
24662306a36Sopenharmony_ci			goto fail;
24762306a36Sopenharmony_ci		}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		offset = (u64)cmdbuf.offset + (u64)cmdbuf.words * sizeof(u32);
25062306a36Sopenharmony_ci		obj = host1x_to_tegra_bo(bo);
25162306a36Sopenharmony_ci		refs[num_refs++] = &obj->gem;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		/*
25462306a36Sopenharmony_ci		 * Gather buffer base address must be 4-bytes aligned,
25562306a36Sopenharmony_ci		 * unaligned offset is malformed and cause commands stream
25662306a36Sopenharmony_ci		 * corruption on the buffer address relocation.
25762306a36Sopenharmony_ci		 */
25862306a36Sopenharmony_ci		if (offset & 3 || offset > obj->gem.size) {
25962306a36Sopenharmony_ci			err = -EINVAL;
26062306a36Sopenharmony_ci			goto fail;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
26462306a36Sopenharmony_ci		num_cmdbufs--;
26562306a36Sopenharmony_ci		user_cmdbufs++;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* copy and resolve relocations from submit */
26962306a36Sopenharmony_ci	while (num_relocs--) {
27062306a36Sopenharmony_ci		struct host1x_reloc *reloc;
27162306a36Sopenharmony_ci		struct tegra_bo *obj;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		err = host1x_reloc_copy_from_user(&job->relocs[num_relocs],
27462306a36Sopenharmony_ci						  &user_relocs[num_relocs], drm,
27562306a36Sopenharmony_ci						  file);
27662306a36Sopenharmony_ci		if (err < 0)
27762306a36Sopenharmony_ci			goto fail;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		reloc = &job->relocs[num_relocs];
28062306a36Sopenharmony_ci		obj = host1x_to_tegra_bo(reloc->cmdbuf.bo);
28162306a36Sopenharmony_ci		refs[num_refs++] = &obj->gem;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/*
28462306a36Sopenharmony_ci		 * The unaligned cmdbuf offset will cause an unaligned write
28562306a36Sopenharmony_ci		 * during of the relocations patching, corrupting the commands
28662306a36Sopenharmony_ci		 * stream.
28762306a36Sopenharmony_ci		 */
28862306a36Sopenharmony_ci		if (reloc->cmdbuf.offset & 3 ||
28962306a36Sopenharmony_ci		    reloc->cmdbuf.offset >= obj->gem.size) {
29062306a36Sopenharmony_ci			err = -EINVAL;
29162306a36Sopenharmony_ci			goto fail;
29262306a36Sopenharmony_ci		}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		obj = host1x_to_tegra_bo(reloc->target.bo);
29562306a36Sopenharmony_ci		refs[num_refs++] = &obj->gem;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		if (reloc->target.offset >= obj->gem.size) {
29862306a36Sopenharmony_ci			err = -EINVAL;
29962306a36Sopenharmony_ci			goto fail;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (copy_from_user(&syncpt, user_syncpt, sizeof(syncpt))) {
30462306a36Sopenharmony_ci		err = -EFAULT;
30562306a36Sopenharmony_ci		goto fail;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* Syncpoint ref will be dropped on job release. */
30962306a36Sopenharmony_ci	sp = host1x_syncpt_get_by_id(host1x, syncpt.id);
31062306a36Sopenharmony_ci	if (!sp) {
31162306a36Sopenharmony_ci		err = -ENOENT;
31262306a36Sopenharmony_ci		goto fail;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	job->is_addr_reg = context->client->ops->is_addr_reg;
31662306a36Sopenharmony_ci	job->is_valid_class = context->client->ops->is_valid_class;
31762306a36Sopenharmony_ci	job->syncpt_incrs = syncpt.incrs;
31862306a36Sopenharmony_ci	job->syncpt = sp;
31962306a36Sopenharmony_ci	job->timeout = 10000;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (args->timeout && args->timeout < 10000)
32262306a36Sopenharmony_ci		job->timeout = args->timeout;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	err = host1x_job_pin(job, context->client->base.dev);
32562306a36Sopenharmony_ci	if (err)
32662306a36Sopenharmony_ci		goto fail;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	err = host1x_job_submit(job);
32962306a36Sopenharmony_ci	if (err) {
33062306a36Sopenharmony_ci		host1x_job_unpin(job);
33162306a36Sopenharmony_ci		goto fail;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	args->fence = job->syncpt_end;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cifail:
33762306a36Sopenharmony_ci	while (num_refs--)
33862306a36Sopenharmony_ci		drm_gem_object_put(refs[num_refs]);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	kfree(refs);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ciput:
34362306a36Sopenharmony_ci	host1x_job_put(job);
34462306a36Sopenharmony_ci	return err;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci#ifdef CONFIG_DRM_TEGRA_STAGING
34962306a36Sopenharmony_cistatic int tegra_gem_create(struct drm_device *drm, void *data,
35062306a36Sopenharmony_ci			    struct drm_file *file)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct drm_tegra_gem_create *args = data;
35362306a36Sopenharmony_ci	struct tegra_bo *bo;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags,
35662306a36Sopenharmony_ci					 &args->handle);
35762306a36Sopenharmony_ci	if (IS_ERR(bo))
35862306a36Sopenharmony_ci		return PTR_ERR(bo);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int tegra_gem_mmap(struct drm_device *drm, void *data,
36462306a36Sopenharmony_ci			  struct drm_file *file)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct drm_tegra_gem_mmap *args = data;
36762306a36Sopenharmony_ci	struct drm_gem_object *gem;
36862306a36Sopenharmony_ci	struct tegra_bo *bo;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	gem = drm_gem_object_lookup(file, args->handle);
37162306a36Sopenharmony_ci	if (!gem)
37262306a36Sopenharmony_ci		return -EINVAL;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	bo = to_tegra_bo(gem);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	drm_gem_object_put(gem);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int tegra_syncpt_read(struct drm_device *drm, void *data,
38462306a36Sopenharmony_ci			     struct drm_file *file)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct host1x *host = dev_get_drvdata(drm->dev->parent);
38762306a36Sopenharmony_ci	struct drm_tegra_syncpt_read *args = data;
38862306a36Sopenharmony_ci	struct host1x_syncpt *sp;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	sp = host1x_syncpt_get_by_id_noref(host, args->id);
39162306a36Sopenharmony_ci	if (!sp)
39262306a36Sopenharmony_ci		return -EINVAL;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	args->value = host1x_syncpt_read_min(sp);
39562306a36Sopenharmony_ci	return 0;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int tegra_syncpt_incr(struct drm_device *drm, void *data,
39962306a36Sopenharmony_ci			     struct drm_file *file)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
40262306a36Sopenharmony_ci	struct drm_tegra_syncpt_incr *args = data;
40362306a36Sopenharmony_ci	struct host1x_syncpt *sp;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
40662306a36Sopenharmony_ci	if (!sp)
40762306a36Sopenharmony_ci		return -EINVAL;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	return host1x_syncpt_incr(sp);
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic int tegra_syncpt_wait(struct drm_device *drm, void *data,
41362306a36Sopenharmony_ci			     struct drm_file *file)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
41662306a36Sopenharmony_ci	struct drm_tegra_syncpt_wait *args = data;
41762306a36Sopenharmony_ci	struct host1x_syncpt *sp;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
42062306a36Sopenharmony_ci	if (!sp)
42162306a36Sopenharmony_ci		return -EINVAL;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return host1x_syncpt_wait(sp, args->thresh,
42462306a36Sopenharmony_ci				  msecs_to_jiffies(args->timeout),
42562306a36Sopenharmony_ci				  &args->value);
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int tegra_client_open(struct tegra_drm_file *fpriv,
42962306a36Sopenharmony_ci			     struct tegra_drm_client *client,
43062306a36Sopenharmony_ci			     struct tegra_drm_context *context)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	int err;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	err = pm_runtime_resume_and_get(client->base.dev);
43562306a36Sopenharmony_ci	if (err)
43662306a36Sopenharmony_ci		return err;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	err = client->ops->open_channel(client, context);
43962306a36Sopenharmony_ci	if (err < 0) {
44062306a36Sopenharmony_ci		pm_runtime_put(client->base.dev);
44162306a36Sopenharmony_ci		return err;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	err = idr_alloc(&fpriv->legacy_contexts, context, 1, 0, GFP_KERNEL);
44562306a36Sopenharmony_ci	if (err < 0) {
44662306a36Sopenharmony_ci		client->ops->close_channel(context);
44762306a36Sopenharmony_ci		pm_runtime_put(client->base.dev);
44862306a36Sopenharmony_ci		return err;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	context->client = client;
45262306a36Sopenharmony_ci	context->id = err;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int tegra_open_channel(struct drm_device *drm, void *data,
45862306a36Sopenharmony_ci			      struct drm_file *file)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct tegra_drm_file *fpriv = file->driver_priv;
46162306a36Sopenharmony_ci	struct tegra_drm *tegra = drm->dev_private;
46262306a36Sopenharmony_ci	struct drm_tegra_open_channel *args = data;
46362306a36Sopenharmony_ci	struct tegra_drm_context *context;
46462306a36Sopenharmony_ci	struct tegra_drm_client *client;
46562306a36Sopenharmony_ci	int err = -ENODEV;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	context = kzalloc(sizeof(*context), GFP_KERNEL);
46862306a36Sopenharmony_ci	if (!context)
46962306a36Sopenharmony_ci		return -ENOMEM;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	mutex_lock(&fpriv->lock);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	list_for_each_entry(client, &tegra->clients, list)
47462306a36Sopenharmony_ci		if (client->base.class == args->client) {
47562306a36Sopenharmony_ci			err = tegra_client_open(fpriv, client, context);
47662306a36Sopenharmony_ci			if (err < 0)
47762306a36Sopenharmony_ci				break;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci			args->context = context->id;
48062306a36Sopenharmony_ci			break;
48162306a36Sopenharmony_ci		}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (err < 0)
48462306a36Sopenharmony_ci		kfree(context);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	mutex_unlock(&fpriv->lock);
48762306a36Sopenharmony_ci	return err;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int tegra_close_channel(struct drm_device *drm, void *data,
49162306a36Sopenharmony_ci			       struct drm_file *file)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	struct tegra_drm_file *fpriv = file->driver_priv;
49462306a36Sopenharmony_ci	struct drm_tegra_close_channel *args = data;
49562306a36Sopenharmony_ci	struct tegra_drm_context *context;
49662306a36Sopenharmony_ci	int err = 0;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	mutex_lock(&fpriv->lock);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	context = idr_find(&fpriv->legacy_contexts, args->context);
50162306a36Sopenharmony_ci	if (!context) {
50262306a36Sopenharmony_ci		err = -EINVAL;
50362306a36Sopenharmony_ci		goto unlock;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	idr_remove(&fpriv->legacy_contexts, context->id);
50762306a36Sopenharmony_ci	tegra_drm_context_free(context);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ciunlock:
51062306a36Sopenharmony_ci	mutex_unlock(&fpriv->lock);
51162306a36Sopenharmony_ci	return err;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int tegra_get_syncpt(struct drm_device *drm, void *data,
51562306a36Sopenharmony_ci			    struct drm_file *file)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct tegra_drm_file *fpriv = file->driver_priv;
51862306a36Sopenharmony_ci	struct drm_tegra_get_syncpt *args = data;
51962306a36Sopenharmony_ci	struct tegra_drm_context *context;
52062306a36Sopenharmony_ci	struct host1x_syncpt *syncpt;
52162306a36Sopenharmony_ci	int err = 0;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	mutex_lock(&fpriv->lock);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	context = idr_find(&fpriv->legacy_contexts, args->context);
52662306a36Sopenharmony_ci	if (!context) {
52762306a36Sopenharmony_ci		err = -ENODEV;
52862306a36Sopenharmony_ci		goto unlock;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (args->index >= context->client->base.num_syncpts) {
53262306a36Sopenharmony_ci		err = -EINVAL;
53362306a36Sopenharmony_ci		goto unlock;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	syncpt = context->client->base.syncpts[args->index];
53762306a36Sopenharmony_ci	args->id = host1x_syncpt_id(syncpt);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ciunlock:
54062306a36Sopenharmony_ci	mutex_unlock(&fpriv->lock);
54162306a36Sopenharmony_ci	return err;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic int tegra_submit(struct drm_device *drm, void *data,
54562306a36Sopenharmony_ci			struct drm_file *file)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct tegra_drm_file *fpriv = file->driver_priv;
54862306a36Sopenharmony_ci	struct drm_tegra_submit *args = data;
54962306a36Sopenharmony_ci	struct tegra_drm_context *context;
55062306a36Sopenharmony_ci	int err;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	mutex_lock(&fpriv->lock);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	context = idr_find(&fpriv->legacy_contexts, args->context);
55562306a36Sopenharmony_ci	if (!context) {
55662306a36Sopenharmony_ci		err = -ENODEV;
55762306a36Sopenharmony_ci		goto unlock;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	err = context->client->ops->submit(context, args, drm, file);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ciunlock:
56362306a36Sopenharmony_ci	mutex_unlock(&fpriv->lock);
56462306a36Sopenharmony_ci	return err;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic int tegra_get_syncpt_base(struct drm_device *drm, void *data,
56862306a36Sopenharmony_ci				 struct drm_file *file)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct tegra_drm_file *fpriv = file->driver_priv;
57162306a36Sopenharmony_ci	struct drm_tegra_get_syncpt_base *args = data;
57262306a36Sopenharmony_ci	struct tegra_drm_context *context;
57362306a36Sopenharmony_ci	struct host1x_syncpt_base *base;
57462306a36Sopenharmony_ci	struct host1x_syncpt *syncpt;
57562306a36Sopenharmony_ci	int err = 0;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	mutex_lock(&fpriv->lock);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	context = idr_find(&fpriv->legacy_contexts, args->context);
58062306a36Sopenharmony_ci	if (!context) {
58162306a36Sopenharmony_ci		err = -ENODEV;
58262306a36Sopenharmony_ci		goto unlock;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (args->syncpt >= context->client->base.num_syncpts) {
58662306a36Sopenharmony_ci		err = -EINVAL;
58762306a36Sopenharmony_ci		goto unlock;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	syncpt = context->client->base.syncpts[args->syncpt];
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	base = host1x_syncpt_get_base(syncpt);
59362306a36Sopenharmony_ci	if (!base) {
59462306a36Sopenharmony_ci		err = -ENXIO;
59562306a36Sopenharmony_ci		goto unlock;
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	args->id = host1x_syncpt_base_id(base);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ciunlock:
60162306a36Sopenharmony_ci	mutex_unlock(&fpriv->lock);
60262306a36Sopenharmony_ci	return err;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic int tegra_gem_set_tiling(struct drm_device *drm, void *data,
60662306a36Sopenharmony_ci				struct drm_file *file)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct drm_tegra_gem_set_tiling *args = data;
60962306a36Sopenharmony_ci	enum tegra_bo_tiling_mode mode;
61062306a36Sopenharmony_ci	struct drm_gem_object *gem;
61162306a36Sopenharmony_ci	unsigned long value = 0;
61262306a36Sopenharmony_ci	struct tegra_bo *bo;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	switch (args->mode) {
61562306a36Sopenharmony_ci	case DRM_TEGRA_GEM_TILING_MODE_PITCH:
61662306a36Sopenharmony_ci		mode = TEGRA_BO_TILING_MODE_PITCH;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		if (args->value != 0)
61962306a36Sopenharmony_ci			return -EINVAL;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		break;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	case DRM_TEGRA_GEM_TILING_MODE_TILED:
62462306a36Sopenharmony_ci		mode = TEGRA_BO_TILING_MODE_TILED;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		if (args->value != 0)
62762306a36Sopenharmony_ci			return -EINVAL;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		break;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	case DRM_TEGRA_GEM_TILING_MODE_BLOCK:
63262306a36Sopenharmony_ci		mode = TEGRA_BO_TILING_MODE_BLOCK;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		if (args->value > 5)
63562306a36Sopenharmony_ci			return -EINVAL;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		value = args->value;
63862306a36Sopenharmony_ci		break;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	default:
64162306a36Sopenharmony_ci		return -EINVAL;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	gem = drm_gem_object_lookup(file, args->handle);
64562306a36Sopenharmony_ci	if (!gem)
64662306a36Sopenharmony_ci		return -ENOENT;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	bo = to_tegra_bo(gem);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	bo->tiling.mode = mode;
65162306a36Sopenharmony_ci	bo->tiling.value = value;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	drm_gem_object_put(gem);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return 0;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic int tegra_gem_get_tiling(struct drm_device *drm, void *data,
65962306a36Sopenharmony_ci				struct drm_file *file)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	struct drm_tegra_gem_get_tiling *args = data;
66262306a36Sopenharmony_ci	struct drm_gem_object *gem;
66362306a36Sopenharmony_ci	struct tegra_bo *bo;
66462306a36Sopenharmony_ci	int err = 0;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	gem = drm_gem_object_lookup(file, args->handle);
66762306a36Sopenharmony_ci	if (!gem)
66862306a36Sopenharmony_ci		return -ENOENT;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	bo = to_tegra_bo(gem);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	switch (bo->tiling.mode) {
67362306a36Sopenharmony_ci	case TEGRA_BO_TILING_MODE_PITCH:
67462306a36Sopenharmony_ci		args->mode = DRM_TEGRA_GEM_TILING_MODE_PITCH;
67562306a36Sopenharmony_ci		args->value = 0;
67662306a36Sopenharmony_ci		break;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	case TEGRA_BO_TILING_MODE_TILED:
67962306a36Sopenharmony_ci		args->mode = DRM_TEGRA_GEM_TILING_MODE_TILED;
68062306a36Sopenharmony_ci		args->value = 0;
68162306a36Sopenharmony_ci		break;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	case TEGRA_BO_TILING_MODE_BLOCK:
68462306a36Sopenharmony_ci		args->mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
68562306a36Sopenharmony_ci		args->value = bo->tiling.value;
68662306a36Sopenharmony_ci		break;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	default:
68962306a36Sopenharmony_ci		err = -EINVAL;
69062306a36Sopenharmony_ci		break;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	drm_gem_object_put(gem);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return err;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic int tegra_gem_set_flags(struct drm_device *drm, void *data,
69962306a36Sopenharmony_ci			       struct drm_file *file)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct drm_tegra_gem_set_flags *args = data;
70262306a36Sopenharmony_ci	struct drm_gem_object *gem;
70362306a36Sopenharmony_ci	struct tegra_bo *bo;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (args->flags & ~DRM_TEGRA_GEM_FLAGS)
70662306a36Sopenharmony_ci		return -EINVAL;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	gem = drm_gem_object_lookup(file, args->handle);
70962306a36Sopenharmony_ci	if (!gem)
71062306a36Sopenharmony_ci		return -ENOENT;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	bo = to_tegra_bo(gem);
71362306a36Sopenharmony_ci	bo->flags = 0;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP)
71662306a36Sopenharmony_ci		bo->flags |= TEGRA_BO_BOTTOM_UP;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	drm_gem_object_put(gem);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	return 0;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic int tegra_gem_get_flags(struct drm_device *drm, void *data,
72462306a36Sopenharmony_ci			       struct drm_file *file)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	struct drm_tegra_gem_get_flags *args = data;
72762306a36Sopenharmony_ci	struct drm_gem_object *gem;
72862306a36Sopenharmony_ci	struct tegra_bo *bo;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	gem = drm_gem_object_lookup(file, args->handle);
73162306a36Sopenharmony_ci	if (!gem)
73262306a36Sopenharmony_ci		return -ENOENT;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	bo = to_tegra_bo(gem);
73562306a36Sopenharmony_ci	args->flags = 0;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (bo->flags & TEGRA_BO_BOTTOM_UP)
73862306a36Sopenharmony_ci		args->flags |= DRM_TEGRA_GEM_BOTTOM_UP;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	drm_gem_object_put(gem);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci#endif
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic const struct drm_ioctl_desc tegra_drm_ioctls[] = {
74762306a36Sopenharmony_ci#ifdef CONFIG_DRM_TEGRA_STAGING
74862306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_OPEN, tegra_drm_ioctl_channel_open,
74962306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
75062306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_CLOSE, tegra_drm_ioctl_channel_close,
75162306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
75262306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_MAP, tegra_drm_ioctl_channel_map,
75362306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
75462306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_UNMAP, tegra_drm_ioctl_channel_unmap,
75562306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
75662306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_SUBMIT, tegra_drm_ioctl_channel_submit,
75762306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
75862306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_ALLOCATE, tegra_drm_ioctl_syncpoint_allocate,
75962306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
76062306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_FREE, tegra_drm_ioctl_syncpoint_free,
76162306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
76262306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_WAIT, tegra_drm_ioctl_syncpoint_wait,
76362306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_RENDER_ALLOW),
76662306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_RENDER_ALLOW),
76762306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read,
76862306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
76962306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr,
77062306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
77162306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait,
77262306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
77362306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel,
77462306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
77562306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel,
77662306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
77762306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt,
77862306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
77962306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit,
78062306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
78162306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base,
78262306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
78362306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_TILING, tegra_gem_set_tiling,
78462306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
78562306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling,
78662306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
78762306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags,
78862306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
78962306a36Sopenharmony_ci	DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags,
79062306a36Sopenharmony_ci			  DRM_RENDER_ALLOW),
79162306a36Sopenharmony_ci#endif
79262306a36Sopenharmony_ci};
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic const struct file_operations tegra_drm_fops = {
79562306a36Sopenharmony_ci	.owner = THIS_MODULE,
79662306a36Sopenharmony_ci	.open = drm_open,
79762306a36Sopenharmony_ci	.release = drm_release,
79862306a36Sopenharmony_ci	.unlocked_ioctl = drm_ioctl,
79962306a36Sopenharmony_ci	.mmap = tegra_drm_mmap,
80062306a36Sopenharmony_ci	.poll = drm_poll,
80162306a36Sopenharmony_ci	.read = drm_read,
80262306a36Sopenharmony_ci	.compat_ioctl = drm_compat_ioctl,
80362306a36Sopenharmony_ci	.llseek = noop_llseek,
80462306a36Sopenharmony_ci};
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic int tegra_drm_context_cleanup(int id, void *p, void *data)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct tegra_drm_context *context = p;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	tegra_drm_context_free(context);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return 0;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct tegra_drm_file *fpriv = file->driver_priv;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	mutex_lock(&fpriv->lock);
82062306a36Sopenharmony_ci	idr_for_each(&fpriv->legacy_contexts, tegra_drm_context_cleanup, NULL);
82162306a36Sopenharmony_ci	tegra_drm_uapi_close_file(fpriv);
82262306a36Sopenharmony_ci	mutex_unlock(&fpriv->lock);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	idr_destroy(&fpriv->legacy_contexts);
82562306a36Sopenharmony_ci	mutex_destroy(&fpriv->lock);
82662306a36Sopenharmony_ci	kfree(fpriv);
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
83062306a36Sopenharmony_cistatic int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	struct drm_info_node *node = (struct drm_info_node *)s->private;
83362306a36Sopenharmony_ci	struct drm_device *drm = node->minor->dev;
83462306a36Sopenharmony_ci	struct drm_framebuffer *fb;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	mutex_lock(&drm->mode_config.fb_lock);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
83962306a36Sopenharmony_ci		seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
84062306a36Sopenharmony_ci			   fb->base.id, fb->width, fb->height,
84162306a36Sopenharmony_ci			   fb->format->depth,
84262306a36Sopenharmony_ci			   fb->format->cpp[0] * 8,
84362306a36Sopenharmony_ci			   drm_framebuffer_read_refcount(fb));
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	mutex_unlock(&drm->mode_config.fb_lock);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	return 0;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic int tegra_debugfs_iova(struct seq_file *s, void *data)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct drm_info_node *node = (struct drm_info_node *)s->private;
85462306a36Sopenharmony_ci	struct drm_device *drm = node->minor->dev;
85562306a36Sopenharmony_ci	struct tegra_drm *tegra = drm->dev_private;
85662306a36Sopenharmony_ci	struct drm_printer p = drm_seq_file_printer(s);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	if (tegra->domain) {
85962306a36Sopenharmony_ci		mutex_lock(&tegra->mm_lock);
86062306a36Sopenharmony_ci		drm_mm_print(&tegra->mm, &p);
86162306a36Sopenharmony_ci		mutex_unlock(&tegra->mm_lock);
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	return 0;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_cistatic struct drm_info_list tegra_debugfs_list[] = {
86862306a36Sopenharmony_ci	{ "framebuffers", tegra_debugfs_framebuffers, 0 },
86962306a36Sopenharmony_ci	{ "iova", tegra_debugfs_iova, 0 },
87062306a36Sopenharmony_ci};
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic void tegra_debugfs_init(struct drm_minor *minor)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	drm_debugfs_create_files(tegra_debugfs_list,
87562306a36Sopenharmony_ci				 ARRAY_SIZE(tegra_debugfs_list),
87662306a36Sopenharmony_ci				 minor->debugfs_root, minor);
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci#endif
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic const struct drm_driver tegra_drm_driver = {
88162306a36Sopenharmony_ci	.driver_features = DRIVER_MODESET | DRIVER_GEM |
88262306a36Sopenharmony_ci			   DRIVER_ATOMIC | DRIVER_RENDER | DRIVER_SYNCOBJ,
88362306a36Sopenharmony_ci	.open = tegra_drm_open,
88462306a36Sopenharmony_ci	.postclose = tegra_drm_postclose,
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS)
88762306a36Sopenharmony_ci	.debugfs_init = tegra_debugfs_init,
88862306a36Sopenharmony_ci#endif
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	.gem_prime_import = tegra_gem_prime_import,
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	.dumb_create = tegra_bo_dumb_create,
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	.ioctls = tegra_drm_ioctls,
89562306a36Sopenharmony_ci	.num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
89662306a36Sopenharmony_ci	.fops = &tegra_drm_fops,
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	.name = DRIVER_NAME,
89962306a36Sopenharmony_ci	.desc = DRIVER_DESC,
90062306a36Sopenharmony_ci	.date = DRIVER_DATE,
90162306a36Sopenharmony_ci	.major = DRIVER_MAJOR,
90262306a36Sopenharmony_ci	.minor = DRIVER_MINOR,
90362306a36Sopenharmony_ci	.patchlevel = DRIVER_PATCHLEVEL,
90462306a36Sopenharmony_ci};
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ciint tegra_drm_register_client(struct tegra_drm *tegra,
90762306a36Sopenharmony_ci			      struct tegra_drm_client *client)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	/*
91062306a36Sopenharmony_ci	 * When MLOCKs are implemented, change to allocate a shared channel
91162306a36Sopenharmony_ci	 * only when MLOCKs are disabled.
91262306a36Sopenharmony_ci	 */
91362306a36Sopenharmony_ci	client->shared_channel = host1x_channel_request(&client->base);
91462306a36Sopenharmony_ci	if (!client->shared_channel)
91562306a36Sopenharmony_ci		return -EBUSY;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	mutex_lock(&tegra->clients_lock);
91862306a36Sopenharmony_ci	list_add_tail(&client->list, &tegra->clients);
91962306a36Sopenharmony_ci	client->drm = tegra;
92062306a36Sopenharmony_ci	mutex_unlock(&tegra->clients_lock);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	return 0;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ciint tegra_drm_unregister_client(struct tegra_drm *tegra,
92662306a36Sopenharmony_ci				struct tegra_drm_client *client)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	mutex_lock(&tegra->clients_lock);
92962306a36Sopenharmony_ci	list_del_init(&client->list);
93062306a36Sopenharmony_ci	client->drm = NULL;
93162306a36Sopenharmony_ci	mutex_unlock(&tegra->clients_lock);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	if (client->shared_channel)
93462306a36Sopenharmony_ci		host1x_channel_put(client->shared_channel);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	return 0;
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ciint host1x_client_iommu_attach(struct host1x_client *client)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	struct iommu_domain *domain = iommu_get_domain_for_dev(client->dev);
94262306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(client->host);
94362306a36Sopenharmony_ci	struct tegra_drm *tegra = drm->dev_private;
94462306a36Sopenharmony_ci	struct iommu_group *group = NULL;
94562306a36Sopenharmony_ci	int err;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
94862306a36Sopenharmony_ci	if (client->dev->archdata.mapping) {
94962306a36Sopenharmony_ci		struct dma_iommu_mapping *mapping =
95062306a36Sopenharmony_ci				to_dma_iommu_mapping(client->dev);
95162306a36Sopenharmony_ci		arm_iommu_detach_device(client->dev);
95262306a36Sopenharmony_ci		arm_iommu_release_mapping(mapping);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		domain = iommu_get_domain_for_dev(client->dev);
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci#endif
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	/*
95962306a36Sopenharmony_ci	 * If the host1x client is already attached to an IOMMU domain that is
96062306a36Sopenharmony_ci	 * not the shared IOMMU domain, don't try to attach it to a different
96162306a36Sopenharmony_ci	 * domain. This allows using the IOMMU-backed DMA API.
96262306a36Sopenharmony_ci	 */
96362306a36Sopenharmony_ci	if (domain && domain != tegra->domain)
96462306a36Sopenharmony_ci		return 0;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (tegra->domain) {
96762306a36Sopenharmony_ci		group = iommu_group_get(client->dev);
96862306a36Sopenharmony_ci		if (!group)
96962306a36Sopenharmony_ci			return -ENODEV;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci		if (domain != tegra->domain) {
97262306a36Sopenharmony_ci			err = iommu_attach_group(tegra->domain, group);
97362306a36Sopenharmony_ci			if (err < 0) {
97462306a36Sopenharmony_ci				iommu_group_put(group);
97562306a36Sopenharmony_ci				return err;
97662306a36Sopenharmony_ci			}
97762306a36Sopenharmony_ci		}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci		tegra->use_explicit_iommu = true;
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	client->group = group;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	return 0;
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_civoid host1x_client_iommu_detach(struct host1x_client *client)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(client->host);
99062306a36Sopenharmony_ci	struct tegra_drm *tegra = drm->dev_private;
99162306a36Sopenharmony_ci	struct iommu_domain *domain;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	if (client->group) {
99462306a36Sopenharmony_ci		/*
99562306a36Sopenharmony_ci		 * Devices that are part of the same group may no longer be
99662306a36Sopenharmony_ci		 * attached to a domain at this point because their group may
99762306a36Sopenharmony_ci		 * have been detached by an earlier client.
99862306a36Sopenharmony_ci		 */
99962306a36Sopenharmony_ci		domain = iommu_get_domain_for_dev(client->dev);
100062306a36Sopenharmony_ci		if (domain)
100162306a36Sopenharmony_ci			iommu_detach_group(tegra->domain, client->group);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci		iommu_group_put(client->group);
100462306a36Sopenharmony_ci		client->group = NULL;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_civoid *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *dma)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct iova *alloc;
101162306a36Sopenharmony_ci	void *virt;
101262306a36Sopenharmony_ci	gfp_t gfp;
101362306a36Sopenharmony_ci	int err;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (tegra->domain)
101662306a36Sopenharmony_ci		size = iova_align(&tegra->carveout.domain, size);
101762306a36Sopenharmony_ci	else
101862306a36Sopenharmony_ci		size = PAGE_ALIGN(size);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	gfp = GFP_KERNEL | __GFP_ZERO;
102162306a36Sopenharmony_ci	if (!tegra->domain) {
102262306a36Sopenharmony_ci		/*
102362306a36Sopenharmony_ci		 * Many units only support 32-bit addresses, even on 64-bit
102462306a36Sopenharmony_ci		 * SoCs. If there is no IOMMU to translate into a 32-bit IO
102562306a36Sopenharmony_ci		 * virtual address space, force allocations to be in the
102662306a36Sopenharmony_ci		 * lower 32-bit range.
102762306a36Sopenharmony_ci		 */
102862306a36Sopenharmony_ci		gfp |= GFP_DMA;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	virt = (void *)__get_free_pages(gfp, get_order(size));
103262306a36Sopenharmony_ci	if (!virt)
103362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	if (!tegra->domain) {
103662306a36Sopenharmony_ci		/*
103762306a36Sopenharmony_ci		 * If IOMMU is disabled, devices address physical memory
103862306a36Sopenharmony_ci		 * directly.
103962306a36Sopenharmony_ci		 */
104062306a36Sopenharmony_ci		*dma = virt_to_phys(virt);
104162306a36Sopenharmony_ci		return virt;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	alloc = alloc_iova(&tegra->carveout.domain,
104562306a36Sopenharmony_ci			   size >> tegra->carveout.shift,
104662306a36Sopenharmony_ci			   tegra->carveout.limit, true);
104762306a36Sopenharmony_ci	if (!alloc) {
104862306a36Sopenharmony_ci		err = -EBUSY;
104962306a36Sopenharmony_ci		goto free_pages;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	*dma = iova_dma_addr(&tegra->carveout.domain, alloc);
105362306a36Sopenharmony_ci	err = iommu_map(tegra->domain, *dma, virt_to_phys(virt),
105462306a36Sopenharmony_ci			size, IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
105562306a36Sopenharmony_ci	if (err < 0)
105662306a36Sopenharmony_ci		goto free_iova;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	return virt;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cifree_iova:
106162306a36Sopenharmony_ci	__free_iova(&tegra->carveout.domain, alloc);
106262306a36Sopenharmony_cifree_pages:
106362306a36Sopenharmony_ci	free_pages((unsigned long)virt, get_order(size));
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	return ERR_PTR(err);
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_civoid tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt,
106962306a36Sopenharmony_ci		    dma_addr_t dma)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	if (tegra->domain)
107262306a36Sopenharmony_ci		size = iova_align(&tegra->carveout.domain, size);
107362306a36Sopenharmony_ci	else
107462306a36Sopenharmony_ci		size = PAGE_ALIGN(size);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (tegra->domain) {
107762306a36Sopenharmony_ci		iommu_unmap(tegra->domain, dma, size);
107862306a36Sopenharmony_ci		free_iova(&tegra->carveout.domain,
107962306a36Sopenharmony_ci			  iova_pfn(&tegra->carveout.domain, dma));
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	free_pages((unsigned long)virt, get_order(size));
108362306a36Sopenharmony_ci}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_cistatic bool host1x_drm_wants_iommu(struct host1x_device *dev)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	struct host1x *host1x = dev_get_drvdata(dev->dev.parent);
108862306a36Sopenharmony_ci	struct iommu_domain *domain;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	/* Our IOMMU usage policy doesn't currently play well with GART */
109162306a36Sopenharmony_ci	if (of_machine_is_compatible("nvidia,tegra20"))
109262306a36Sopenharmony_ci		return false;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	/*
109562306a36Sopenharmony_ci	 * If the Tegra DRM clients are backed by an IOMMU, push buffers are
109662306a36Sopenharmony_ci	 * likely to be allocated beyond the 32-bit boundary if sufficient
109762306a36Sopenharmony_ci	 * system memory is available. This is problematic on earlier Tegra
109862306a36Sopenharmony_ci	 * generations where host1x supports a maximum of 32 address bits in
109962306a36Sopenharmony_ci	 * the GATHER opcode. In this case, unless host1x is behind an IOMMU
110062306a36Sopenharmony_ci	 * as well it won't be able to process buffers allocated beyond the
110162306a36Sopenharmony_ci	 * 32-bit boundary.
110262306a36Sopenharmony_ci	 *
110362306a36Sopenharmony_ci	 * The DMA API will use bounce buffers in this case, so that could
110462306a36Sopenharmony_ci	 * perhaps still be made to work, even if less efficient, but there
110562306a36Sopenharmony_ci	 * is another catch: in order to perform cache maintenance on pages
110662306a36Sopenharmony_ci	 * allocated for discontiguous buffers we need to map and unmap the
110762306a36Sopenharmony_ci	 * SG table representing these buffers. This is fine for something
110862306a36Sopenharmony_ci	 * small like a push buffer, but it exhausts the bounce buffer pool
110962306a36Sopenharmony_ci	 * (typically on the order of a few MiB) for framebuffers (many MiB
111062306a36Sopenharmony_ci	 * for any modern resolution).
111162306a36Sopenharmony_ci	 *
111262306a36Sopenharmony_ci	 * Work around this by making sure that Tegra DRM clients only use
111362306a36Sopenharmony_ci	 * an IOMMU if the parent host1x also uses an IOMMU.
111462306a36Sopenharmony_ci	 *
111562306a36Sopenharmony_ci	 * Note that there's still a small gap here that we don't cover: if
111662306a36Sopenharmony_ci	 * the DMA API is backed by an IOMMU there's no way to control which
111762306a36Sopenharmony_ci	 * device is attached to an IOMMU and which isn't, except via wiring
111862306a36Sopenharmony_ci	 * up the device tree appropriately. This is considered an problem
111962306a36Sopenharmony_ci	 * of integration, so care must be taken for the DT to be consistent.
112062306a36Sopenharmony_ci	 */
112162306a36Sopenharmony_ci	domain = iommu_get_domain_for_dev(dev->dev.parent);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	/*
112462306a36Sopenharmony_ci	 * Tegra20 and Tegra30 don't support addressing memory beyond the
112562306a36Sopenharmony_ci	 * 32-bit boundary, so the regular GATHER opcodes will always be
112662306a36Sopenharmony_ci	 * sufficient and whether or not the host1x is attached to an IOMMU
112762306a36Sopenharmony_ci	 * doesn't matter.
112862306a36Sopenharmony_ci	 */
112962306a36Sopenharmony_ci	if (!domain && host1x_get_dma_mask(host1x) <= DMA_BIT_MASK(32))
113062306a36Sopenharmony_ci		return true;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	return domain != NULL;
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_cistatic int host1x_drm_probe(struct host1x_device *dev)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	struct tegra_drm *tegra;
113862306a36Sopenharmony_ci	struct drm_device *drm;
113962306a36Sopenharmony_ci	int err;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	drm = drm_dev_alloc(&tegra_drm_driver, &dev->dev);
114262306a36Sopenharmony_ci	if (IS_ERR(drm))
114362306a36Sopenharmony_ci		return PTR_ERR(drm);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
114662306a36Sopenharmony_ci	if (!tegra) {
114762306a36Sopenharmony_ci		err = -ENOMEM;
114862306a36Sopenharmony_ci		goto put;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	if (host1x_drm_wants_iommu(dev) && iommu_present(&platform_bus_type)) {
115262306a36Sopenharmony_ci		tegra->domain = iommu_domain_alloc(&platform_bus_type);
115362306a36Sopenharmony_ci		if (!tegra->domain) {
115462306a36Sopenharmony_ci			err = -ENOMEM;
115562306a36Sopenharmony_ci			goto free;
115662306a36Sopenharmony_ci		}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci		err = iova_cache_get();
115962306a36Sopenharmony_ci		if (err < 0)
116062306a36Sopenharmony_ci			goto domain;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	mutex_init(&tegra->clients_lock);
116462306a36Sopenharmony_ci	INIT_LIST_HEAD(&tegra->clients);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, drm);
116762306a36Sopenharmony_ci	drm->dev_private = tegra;
116862306a36Sopenharmony_ci	tegra->drm = drm;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	drm_mode_config_init(drm);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	drm->mode_config.min_width = 0;
117362306a36Sopenharmony_ci	drm->mode_config.min_height = 0;
117462306a36Sopenharmony_ci	drm->mode_config.max_width = 0;
117562306a36Sopenharmony_ci	drm->mode_config.max_height = 0;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	drm->mode_config.normalize_zpos = true;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	drm->mode_config.funcs = &tegra_drm_mode_config_funcs;
118062306a36Sopenharmony_ci	drm->mode_config.helper_private = &tegra_drm_mode_config_helpers;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	drm_kms_helper_poll_init(drm);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	err = host1x_device_init(dev);
118562306a36Sopenharmony_ci	if (err < 0)
118662306a36Sopenharmony_ci		goto poll;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/*
118962306a36Sopenharmony_ci	 * Now that all display controller have been initialized, the maximum
119062306a36Sopenharmony_ci	 * supported resolution is known and the bitmask for horizontal and
119162306a36Sopenharmony_ci	 * vertical bitfields can be computed.
119262306a36Sopenharmony_ci	 */
119362306a36Sopenharmony_ci	tegra->hmask = drm->mode_config.max_width - 1;
119462306a36Sopenharmony_ci	tegra->vmask = drm->mode_config.max_height - 1;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	if (tegra->use_explicit_iommu) {
119762306a36Sopenharmony_ci		u64 carveout_start, carveout_end, gem_start, gem_end;
119862306a36Sopenharmony_ci		u64 dma_mask = dma_get_mask(&dev->dev);
119962306a36Sopenharmony_ci		dma_addr_t start, end;
120062306a36Sopenharmony_ci		unsigned long order;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci		start = tegra->domain->geometry.aperture_start & dma_mask;
120362306a36Sopenharmony_ci		end = tegra->domain->geometry.aperture_end & dma_mask;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		gem_start = start;
120662306a36Sopenharmony_ci		gem_end = end - CARVEOUT_SZ;
120762306a36Sopenharmony_ci		carveout_start = gem_end + 1;
120862306a36Sopenharmony_ci		carveout_end = end;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci		order = __ffs(tegra->domain->pgsize_bitmap);
121162306a36Sopenharmony_ci		init_iova_domain(&tegra->carveout.domain, 1UL << order,
121262306a36Sopenharmony_ci				 carveout_start >> order);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci		tegra->carveout.shift = iova_shift(&tegra->carveout.domain);
121562306a36Sopenharmony_ci		tegra->carveout.limit = carveout_end >> tegra->carveout.shift;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci		drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1);
121862306a36Sopenharmony_ci		mutex_init(&tegra->mm_lock);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("IOMMU apertures:\n");
122162306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("  GEM: %#llx-%#llx\n", gem_start, gem_end);
122262306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("  Carveout: %#llx-%#llx\n", carveout_start,
122362306a36Sopenharmony_ci				 carveout_end);
122462306a36Sopenharmony_ci	} else if (tegra->domain) {
122562306a36Sopenharmony_ci		iommu_domain_free(tegra->domain);
122662306a36Sopenharmony_ci		tegra->domain = NULL;
122762306a36Sopenharmony_ci		iova_cache_put();
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if (tegra->hub) {
123162306a36Sopenharmony_ci		err = tegra_display_hub_prepare(tegra->hub);
123262306a36Sopenharmony_ci		if (err < 0)
123362306a36Sopenharmony_ci			goto device;
123462306a36Sopenharmony_ci	}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	/* syncpoints are used for full 32-bit hardware VBLANK counters */
123762306a36Sopenharmony_ci	drm->max_vblank_count = 0xffffffff;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	err = drm_vblank_init(drm, drm->mode_config.num_crtc);
124062306a36Sopenharmony_ci	if (err < 0)
124162306a36Sopenharmony_ci		goto hub;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	drm_mode_config_reset(drm);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/*
124662306a36Sopenharmony_ci	 * Only take over from a potential firmware framebuffer if any CRTCs
124762306a36Sopenharmony_ci	 * have been registered. This must not be a fatal error because there
124862306a36Sopenharmony_ci	 * are other accelerators that are exposed via this driver.
124962306a36Sopenharmony_ci	 *
125062306a36Sopenharmony_ci	 * Another case where this happens is on Tegra234 where the display
125162306a36Sopenharmony_ci	 * hardware is no longer part of the host1x complex, so this driver
125262306a36Sopenharmony_ci	 * will not expose any modesetting features.
125362306a36Sopenharmony_ci	 */
125462306a36Sopenharmony_ci	if (drm->mode_config.num_crtc > 0) {
125562306a36Sopenharmony_ci		err = drm_aperture_remove_framebuffers(&tegra_drm_driver);
125662306a36Sopenharmony_ci		if (err < 0)
125762306a36Sopenharmony_ci			goto hub;
125862306a36Sopenharmony_ci	} else {
125962306a36Sopenharmony_ci		/*
126062306a36Sopenharmony_ci		 * Indicate to userspace that this doesn't expose any display
126162306a36Sopenharmony_ci		 * capabilities.
126262306a36Sopenharmony_ci		 */
126362306a36Sopenharmony_ci		drm->driver_features &= ~(DRIVER_MODESET | DRIVER_ATOMIC);
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	err = drm_dev_register(drm, 0);
126762306a36Sopenharmony_ci	if (err < 0)
126862306a36Sopenharmony_ci		goto hub;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	tegra_fbdev_setup(drm);
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	return 0;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cihub:
127562306a36Sopenharmony_ci	if (tegra->hub)
127662306a36Sopenharmony_ci		tegra_display_hub_cleanup(tegra->hub);
127762306a36Sopenharmony_cidevice:
127862306a36Sopenharmony_ci	if (tegra->domain) {
127962306a36Sopenharmony_ci		mutex_destroy(&tegra->mm_lock);
128062306a36Sopenharmony_ci		drm_mm_takedown(&tegra->mm);
128162306a36Sopenharmony_ci		put_iova_domain(&tegra->carveout.domain);
128262306a36Sopenharmony_ci		iova_cache_put();
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	host1x_device_exit(dev);
128662306a36Sopenharmony_cipoll:
128762306a36Sopenharmony_ci	drm_kms_helper_poll_fini(drm);
128862306a36Sopenharmony_ci	drm_mode_config_cleanup(drm);
128962306a36Sopenharmony_cidomain:
129062306a36Sopenharmony_ci	if (tegra->domain)
129162306a36Sopenharmony_ci		iommu_domain_free(tegra->domain);
129262306a36Sopenharmony_cifree:
129362306a36Sopenharmony_ci	kfree(tegra);
129462306a36Sopenharmony_ciput:
129562306a36Sopenharmony_ci	drm_dev_put(drm);
129662306a36Sopenharmony_ci	return err;
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_cistatic int host1x_drm_remove(struct host1x_device *dev)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(&dev->dev);
130262306a36Sopenharmony_ci	struct tegra_drm *tegra = drm->dev_private;
130362306a36Sopenharmony_ci	int err;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	drm_dev_unregister(drm);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	drm_kms_helper_poll_fini(drm);
130862306a36Sopenharmony_ci	drm_atomic_helper_shutdown(drm);
130962306a36Sopenharmony_ci	drm_mode_config_cleanup(drm);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (tegra->hub)
131262306a36Sopenharmony_ci		tegra_display_hub_cleanup(tegra->hub);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	err = host1x_device_exit(dev);
131562306a36Sopenharmony_ci	if (err < 0)
131662306a36Sopenharmony_ci		dev_err(&dev->dev, "host1x device cleanup failed: %d\n", err);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	if (tegra->domain) {
131962306a36Sopenharmony_ci		mutex_destroy(&tegra->mm_lock);
132062306a36Sopenharmony_ci		drm_mm_takedown(&tegra->mm);
132162306a36Sopenharmony_ci		put_iova_domain(&tegra->carveout.domain);
132262306a36Sopenharmony_ci		iova_cache_put();
132362306a36Sopenharmony_ci		iommu_domain_free(tegra->domain);
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	kfree(tegra);
132762306a36Sopenharmony_ci	drm_dev_put(drm);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	return 0;
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
133362306a36Sopenharmony_cistatic int host1x_drm_suspend(struct device *dev)
133462306a36Sopenharmony_ci{
133562306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(dev);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	return drm_mode_config_helper_suspend(drm);
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_cistatic int host1x_drm_resume(struct device *dev)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(dev);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	return drm_mode_config_helper_resume(drm);
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci#endif
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(host1x_drm_pm_ops, host1x_drm_suspend,
134962306a36Sopenharmony_ci			 host1x_drm_resume);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cistatic const struct of_device_id host1x_drm_subdevs[] = {
135262306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra20-dc", },
135362306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra20-hdmi", },
135462306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra20-gr2d", },
135562306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra20-gr3d", },
135662306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra30-dc", },
135762306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra30-hdmi", },
135862306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra30-gr2d", },
135962306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra30-gr3d", },
136062306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra114-dc", },
136162306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra114-dsi", },
136262306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra114-hdmi", },
136362306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra114-gr2d", },
136462306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra114-gr3d", },
136562306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra124-dc", },
136662306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra124-sor", },
136762306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra124-hdmi", },
136862306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra124-dsi", },
136962306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra124-vic", },
137062306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra132-dsi", },
137162306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-dc", },
137262306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-dsi", },
137362306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-sor", },
137462306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-sor1", },
137562306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-vic", },
137662306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-nvdec", },
137762306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra186-display", },
137862306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra186-dc", },
137962306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra186-sor", },
138062306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra186-sor1", },
138162306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra186-vic", },
138262306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra186-nvdec", },
138362306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra194-display", },
138462306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra194-dc", },
138562306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra194-sor", },
138662306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra194-vic", },
138762306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra194-nvdec", },
138862306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra234-vic", },
138962306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra234-nvdec", },
139062306a36Sopenharmony_ci	{ /* sentinel */ }
139162306a36Sopenharmony_ci};
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_cistatic struct host1x_driver host1x_drm_driver = {
139462306a36Sopenharmony_ci	.driver = {
139562306a36Sopenharmony_ci		.name = "drm",
139662306a36Sopenharmony_ci		.pm = &host1x_drm_pm_ops,
139762306a36Sopenharmony_ci	},
139862306a36Sopenharmony_ci	.probe = host1x_drm_probe,
139962306a36Sopenharmony_ci	.remove = host1x_drm_remove,
140062306a36Sopenharmony_ci	.subdevs = host1x_drm_subdevs,
140162306a36Sopenharmony_ci};
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_cistatic struct platform_driver * const drivers[] = {
140462306a36Sopenharmony_ci	&tegra_display_hub_driver,
140562306a36Sopenharmony_ci	&tegra_dc_driver,
140662306a36Sopenharmony_ci	&tegra_hdmi_driver,
140762306a36Sopenharmony_ci	&tegra_dsi_driver,
140862306a36Sopenharmony_ci	&tegra_dpaux_driver,
140962306a36Sopenharmony_ci	&tegra_sor_driver,
141062306a36Sopenharmony_ci	&tegra_gr2d_driver,
141162306a36Sopenharmony_ci	&tegra_gr3d_driver,
141262306a36Sopenharmony_ci	&tegra_vic_driver,
141362306a36Sopenharmony_ci	&tegra_nvdec_driver,
141462306a36Sopenharmony_ci};
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic int __init host1x_drm_init(void)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	int err;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	if (drm_firmware_drivers_only())
142162306a36Sopenharmony_ci		return -ENODEV;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	err = host1x_driver_register(&host1x_drm_driver);
142462306a36Sopenharmony_ci	if (err < 0)
142562306a36Sopenharmony_ci		return err;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	err = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
142862306a36Sopenharmony_ci	if (err < 0)
142962306a36Sopenharmony_ci		goto unregister_host1x;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	return 0;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ciunregister_host1x:
143462306a36Sopenharmony_ci	host1x_driver_unregister(&host1x_drm_driver);
143562306a36Sopenharmony_ci	return err;
143662306a36Sopenharmony_ci}
143762306a36Sopenharmony_cimodule_init(host1x_drm_init);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_cistatic void __exit host1x_drm_exit(void)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
144262306a36Sopenharmony_ci	host1x_driver_unregister(&host1x_drm_driver);
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_cimodule_exit(host1x_drm_exit);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
144762306a36Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
144862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1449