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