]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
d8f4a9ed TR |
2 | /* |
3 | * Copyright (C) 2012 Avionic Design GmbH | |
ad926015 | 4 | * Copyright (C) 2012-2016 NVIDIA CORPORATION. All rights reserved. |
d8f4a9ed TR |
5 | */ |
6 | ||
ad926015 | 7 | #include <linux/bitops.h> |
776dc384 | 8 | #include <linux/host1x.h> |
bdd2f9cd | 9 | #include <linux/idr.h> |
df06b759 | 10 | #include <linux/iommu.h> |
eb1df694 SR |
11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> | |
776dc384 | 13 | |
1503ca47 | 14 | #include <drm/drm_atomic.h> |
07866963 | 15 | #include <drm/drm_atomic_helper.h> |
eb1df694 SR |
16 | #include <drm/drm_debugfs.h> |
17 | #include <drm/drm_drv.h> | |
18 | #include <drm/drm_fourcc.h> | |
19 | #include <drm/drm_ioctl.h> | |
20 | #include <drm/drm_prime.h> | |
21 | #include <drm/drm_vblank.h> | |
07866963 | 22 | |
d8f4a9ed | 23 | #include "drm.h" |
de2ba664 | 24 | #include "gem.h" |
d8f4a9ed TR |
25 | |
26 | #define DRIVER_NAME "tegra" | |
27 | #define DRIVER_DESC "NVIDIA Tegra graphics" | |
28 | #define DRIVER_DATE "20120330" | |
29 | #define DRIVER_MAJOR 0 | |
30 | #define DRIVER_MINOR 0 | |
31 | #define DRIVER_PATCHLEVEL 0 | |
32 | ||
ad926015 | 33 | #define CARVEOUT_SZ SZ_64M |
368f622c | 34 | #define CDMA_GATHER_FETCHES_MAX_NB 16383 |
ad926015 | 35 | |
08943e6c | 36 | struct tegra_drm_file { |
bdd2f9cd TR |
37 | struct idr contexts; |
38 | struct mutex lock; | |
08943e6c TR |
39 | }; |
40 | ||
ab7d3f58 TR |
41 | static int tegra_atomic_check(struct drm_device *drm, |
42 | struct drm_atomic_state *state) | |
1503ca47 | 43 | { |
ab7d3f58 | 44 | int err; |
1503ca47 | 45 | |
a18301b9 | 46 | err = drm_atomic_helper_check(drm, state); |
ab7d3f58 TR |
47 | if (err < 0) |
48 | return err; | |
1503ca47 | 49 | |
a18301b9 | 50 | return tegra_display_hub_atomic_check(drm, state); |
1503ca47 TR |
51 | } |
52 | ||
31b02cae | 53 | static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { |
f9914214 | 54 | .fb_create = tegra_fb_create, |
b110ef37 | 55 | #ifdef CONFIG_DRM_FBDEV_EMULATION |
c94bedab | 56 | .output_poll_changed = drm_fb_helper_output_poll_changed, |
f9914214 | 57 | #endif |
ab7d3f58 | 58 | .atomic_check = tegra_atomic_check, |
31b02cae TR |
59 | .atomic_commit = drm_atomic_helper_commit, |
60 | }; | |
61 | ||
c4755fb9 TR |
62 | static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) |
63 | { | |
64 | struct drm_device *drm = old_state->dev; | |
65 | struct tegra_drm *tegra = drm->dev_private; | |
66 | ||
67 | if (tegra->hub) { | |
68 | drm_atomic_helper_commit_modeset_disables(drm, old_state); | |
69 | tegra_display_hub_atomic_commit(drm, old_state); | |
70 | drm_atomic_helper_commit_planes(drm, old_state, 0); | |
71 | drm_atomic_helper_commit_modeset_enables(drm, old_state); | |
72 | drm_atomic_helper_commit_hw_done(old_state); | |
73 | drm_atomic_helper_wait_for_vblanks(drm, old_state); | |
74 | drm_atomic_helper_cleanup_planes(drm, old_state); | |
75 | } else { | |
76 | drm_atomic_helper_commit_tail_rpm(old_state); | |
77 | } | |
78 | } | |
79 | ||
31b02cae TR |
80 | static const struct drm_mode_config_helper_funcs |
81 | tegra_drm_mode_config_helpers = { | |
c4755fb9 | 82 | .atomic_commit_tail = tegra_atomic_commit_tail, |
f9914214 TR |
83 | }; |
84 | ||
d8f4a9ed TR |
85 | static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) |
86 | { | |
08943e6c | 87 | struct tegra_drm_file *fpriv; |
d43f81cb TB |
88 | |
89 | fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL); | |
90 | if (!fpriv) | |
91 | return -ENOMEM; | |
92 | ||
bdd2f9cd TR |
93 | idr_init(&fpriv->contexts); |
94 | mutex_init(&fpriv->lock); | |
d43f81cb TB |
95 | filp->driver_priv = fpriv; |
96 | ||
d8f4a9ed TR |
97 | return 0; |
98 | } | |
99 | ||
c88c3630 | 100 | static void tegra_drm_context_free(struct tegra_drm_context *context) |
d43f81cb TB |
101 | { |
102 | context->client->ops->close_channel(context); | |
103 | kfree(context); | |
104 | } | |
105 | ||
c40f0f1a | 106 | static struct host1x_bo * |
a8ad0bd8 | 107 | host1x_bo_lookup(struct drm_file *file, u32 handle) |
c40f0f1a TR |
108 | { |
109 | struct drm_gem_object *gem; | |
110 | struct tegra_bo *bo; | |
111 | ||
a8ad0bd8 | 112 | gem = drm_gem_object_lookup(file, handle); |
c40f0f1a TR |
113 | if (!gem) |
114 | return NULL; | |
115 | ||
c40f0f1a TR |
116 | bo = to_tegra_bo(gem); |
117 | return &bo->base; | |
118 | } | |
119 | ||
961e3bea TR |
120 | static int host1x_reloc_copy_from_user(struct host1x_reloc *dest, |
121 | struct drm_tegra_reloc __user *src, | |
122 | struct drm_device *drm, | |
123 | struct drm_file *file) | |
124 | { | |
125 | u32 cmdbuf, target; | |
126 | int err; | |
127 | ||
128 | err = get_user(cmdbuf, &src->cmdbuf.handle); | |
129 | if (err < 0) | |
130 | return err; | |
131 | ||
132 | err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset); | |
133 | if (err < 0) | |
134 | return err; | |
135 | ||
136 | err = get_user(target, &src->target.handle); | |
137 | if (err < 0) | |
138 | return err; | |
139 | ||
31f40f86 | 140 | err = get_user(dest->target.offset, &src->target.offset); |
961e3bea TR |
141 | if (err < 0) |
142 | return err; | |
143 | ||
144 | err = get_user(dest->shift, &src->shift); | |
145 | if (err < 0) | |
146 | return err; | |
147 | ||
ab4f81bf TR |
148 | dest->flags = HOST1X_RELOC_READ | HOST1X_RELOC_WRITE; |
149 | ||
a8ad0bd8 | 150 | dest->cmdbuf.bo = host1x_bo_lookup(file, cmdbuf); |
961e3bea TR |
151 | if (!dest->cmdbuf.bo) |
152 | return -ENOENT; | |
153 | ||
a8ad0bd8 | 154 | dest->target.bo = host1x_bo_lookup(file, target); |
961e3bea TR |
155 | if (!dest->target.bo) |
156 | return -ENOENT; | |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
c40f0f1a TR |
161 | int tegra_drm_submit(struct tegra_drm_context *context, |
162 | struct drm_tegra_submit *args, struct drm_device *drm, | |
163 | struct drm_file *file) | |
164 | { | |
bf3d41cc | 165 | struct host1x_client *client = &context->client->base; |
c40f0f1a TR |
166 | unsigned int num_cmdbufs = args->num_cmdbufs; |
167 | unsigned int num_relocs = args->num_relocs; | |
a176c67d MP |
168 | struct drm_tegra_cmdbuf __user *user_cmdbufs; |
169 | struct drm_tegra_reloc __user *user_relocs; | |
a176c67d | 170 | struct drm_tegra_syncpt __user *user_syncpt; |
c40f0f1a | 171 | struct drm_tegra_syncpt syncpt; |
e0b2ce02 | 172 | struct host1x *host1x = dev_get_drvdata(drm->dev->parent); |
ec73c4cf | 173 | struct drm_gem_object **refs; |
e0b2ce02 | 174 | struct host1x_syncpt *sp; |
c40f0f1a | 175 | struct host1x_job *job; |
ec73c4cf | 176 | unsigned int num_refs; |
c40f0f1a TR |
177 | int err; |
178 | ||
a176c67d MP |
179 | user_cmdbufs = u64_to_user_ptr(args->cmdbufs); |
180 | user_relocs = u64_to_user_ptr(args->relocs); | |
a176c67d MP |
181 | user_syncpt = u64_to_user_ptr(args->syncpts); |
182 | ||
c40f0f1a TR |
183 | /* We don't yet support other than one syncpt_incr struct per submit */ |
184 | if (args->num_syncpts != 1) | |
185 | return -EINVAL; | |
186 | ||
d0fbbdff DO |
187 | /* We don't yet support waitchks */ |
188 | if (args->num_waitchks != 0) | |
189 | return -EINVAL; | |
190 | ||
c40f0f1a | 191 | job = host1x_job_alloc(context->channel, args->num_cmdbufs, |
24c94e16 | 192 | args->num_relocs); |
c40f0f1a TR |
193 | if (!job) |
194 | return -ENOMEM; | |
195 | ||
196 | job->num_relocs = args->num_relocs; | |
bf3d41cc TR |
197 | job->client = client; |
198 | job->class = client->class; | |
c40f0f1a TR |
199 | job->serialize = true; |
200 | ||
ec73c4cf DO |
201 | /* |
202 | * Track referenced BOs so that they can be unreferenced after the | |
203 | * submission is complete. | |
204 | */ | |
24c94e16 | 205 | num_refs = num_cmdbufs + num_relocs * 2; |
ec73c4cf DO |
206 | |
207 | refs = kmalloc_array(num_refs, sizeof(*refs), GFP_KERNEL); | |
208 | if (!refs) { | |
209 | err = -ENOMEM; | |
210 | goto put; | |
211 | } | |
212 | ||
213 | /* reuse as an iterator later */ | |
214 | num_refs = 0; | |
215 | ||
c40f0f1a TR |
216 | while (num_cmdbufs) { |
217 | struct drm_tegra_cmdbuf cmdbuf; | |
218 | struct host1x_bo *bo; | |
368f622c DO |
219 | struct tegra_bo *obj; |
220 | u64 offset; | |
c40f0f1a | 221 | |
a176c67d | 222 | if (copy_from_user(&cmdbuf, user_cmdbufs, sizeof(cmdbuf))) { |
9a991600 | 223 | err = -EFAULT; |
c40f0f1a | 224 | goto fail; |
9a991600 | 225 | } |
c40f0f1a | 226 | |
368f622c DO |
227 | /* |
228 | * The maximum number of CDMA gather fetches is 16383, a higher | |
229 | * value means the words count is malformed. | |
230 | */ | |
231 | if (cmdbuf.words > CDMA_GATHER_FETCHES_MAX_NB) { | |
232 | err = -EINVAL; | |
233 | goto fail; | |
234 | } | |
235 | ||
a8ad0bd8 | 236 | bo = host1x_bo_lookup(file, cmdbuf.handle); |
c40f0f1a TR |
237 | if (!bo) { |
238 | err = -ENOENT; | |
239 | goto fail; | |
240 | } | |
241 | ||
368f622c DO |
242 | offset = (u64)cmdbuf.offset + (u64)cmdbuf.words * sizeof(u32); |
243 | obj = host1x_to_tegra_bo(bo); | |
ec73c4cf | 244 | refs[num_refs++] = &obj->gem; |
368f622c DO |
245 | |
246 | /* | |
247 | * Gather buffer base address must be 4-bytes aligned, | |
248 | * unaligned offset is malformed and cause commands stream | |
249 | * corruption on the buffer address relocation. | |
250 | */ | |
5265f033 | 251 | if (offset & 3 || offset > obj->gem.size) { |
368f622c DO |
252 | err = -EINVAL; |
253 | goto fail; | |
254 | } | |
255 | ||
c40f0f1a TR |
256 | host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset); |
257 | num_cmdbufs--; | |
a176c67d | 258 | user_cmdbufs++; |
c40f0f1a TR |
259 | } |
260 | ||
961e3bea | 261 | /* copy and resolve relocations from submit */ |
c40f0f1a | 262 | while (num_relocs--) { |
368f622c DO |
263 | struct host1x_reloc *reloc; |
264 | struct tegra_bo *obj; | |
265 | ||
06490bb9 | 266 | err = host1x_reloc_copy_from_user(&job->relocs[num_relocs], |
a176c67d | 267 | &user_relocs[num_relocs], drm, |
961e3bea TR |
268 | file); |
269 | if (err < 0) | |
c40f0f1a | 270 | goto fail; |
368f622c | 271 | |
06490bb9 | 272 | reloc = &job->relocs[num_relocs]; |
368f622c | 273 | obj = host1x_to_tegra_bo(reloc->cmdbuf.bo); |
ec73c4cf | 274 | refs[num_refs++] = &obj->gem; |
368f622c DO |
275 | |
276 | /* | |
277 | * The unaligned cmdbuf offset will cause an unaligned write | |
278 | * during of the relocations patching, corrupting the commands | |
279 | * stream. | |
280 | */ | |
281 | if (reloc->cmdbuf.offset & 3 || | |
282 | reloc->cmdbuf.offset >= obj->gem.size) { | |
283 | err = -EINVAL; | |
284 | goto fail; | |
285 | } | |
286 | ||
287 | obj = host1x_to_tegra_bo(reloc->target.bo); | |
ec73c4cf | 288 | refs[num_refs++] = &obj->gem; |
368f622c DO |
289 | |
290 | if (reloc->target.offset >= obj->gem.size) { | |
291 | err = -EINVAL; | |
292 | goto fail; | |
293 | } | |
c40f0f1a TR |
294 | } |
295 | ||
a176c67d | 296 | if (copy_from_user(&syncpt, user_syncpt, sizeof(syncpt))) { |
9a991600 | 297 | err = -EFAULT; |
c40f0f1a | 298 | goto fail; |
9a991600 | 299 | } |
c40f0f1a | 300 | |
e0b2ce02 DO |
301 | /* check whether syncpoint ID is valid */ |
302 | sp = host1x_syncpt_get(host1x, syncpt.id); | |
303 | if (!sp) { | |
304 | err = -ENOENT; | |
305 | goto fail; | |
306 | } | |
307 | ||
c40f0f1a | 308 | job->is_addr_reg = context->client->ops->is_addr_reg; |
0f563a4b | 309 | job->is_valid_class = context->client->ops->is_valid_class; |
c40f0f1a TR |
310 | job->syncpt_incrs = syncpt.incrs; |
311 | job->syncpt_id = syncpt.id; | |
312 | job->timeout = 10000; | |
313 | ||
314 | if (args->timeout && args->timeout < 10000) | |
315 | job->timeout = args->timeout; | |
316 | ||
317 | err = host1x_job_pin(job, context->client->base.dev); | |
318 | if (err) | |
319 | goto fail; | |
320 | ||
321 | err = host1x_job_submit(job); | |
ec73c4cf DO |
322 | if (err) { |
323 | host1x_job_unpin(job); | |
324 | goto fail; | |
325 | } | |
c40f0f1a TR |
326 | |
327 | args->fence = job->syncpt_end; | |
328 | ||
c40f0f1a | 329 | fail: |
ec73c4cf DO |
330 | while (num_refs--) |
331 | drm_gem_object_put_unlocked(refs[num_refs]); | |
332 | ||
333 | kfree(refs); | |
334 | ||
335 | put: | |
c40f0f1a TR |
336 | host1x_job_put(job); |
337 | return err; | |
338 | } | |
339 | ||
340 | ||
d43f81cb | 341 | #ifdef CONFIG_DRM_TEGRA_STAGING |
d43f81cb TB |
342 | static int tegra_gem_create(struct drm_device *drm, void *data, |
343 | struct drm_file *file) | |
344 | { | |
345 | struct drm_tegra_gem_create *args = data; | |
346 | struct tegra_bo *bo; | |
347 | ||
773af77f | 348 | bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags, |
d43f81cb TB |
349 | &args->handle); |
350 | if (IS_ERR(bo)) | |
351 | return PTR_ERR(bo); | |
352 | ||
353 | return 0; | |
354 | } | |
355 | ||
356 | static int tegra_gem_mmap(struct drm_device *drm, void *data, | |
357 | struct drm_file *file) | |
358 | { | |
359 | struct drm_tegra_gem_mmap *args = data; | |
360 | struct drm_gem_object *gem; | |
361 | struct tegra_bo *bo; | |
362 | ||
a8ad0bd8 | 363 | gem = drm_gem_object_lookup(file, args->handle); |
d43f81cb TB |
364 | if (!gem) |
365 | return -EINVAL; | |
366 | ||
367 | bo = to_tegra_bo(gem); | |
368 | ||
2bc7b0ca | 369 | args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node); |
d43f81cb | 370 | |
7664b2fa | 371 | drm_gem_object_put_unlocked(gem); |
d43f81cb TB |
372 | |
373 | return 0; | |
374 | } | |
375 | ||
376 | static int tegra_syncpt_read(struct drm_device *drm, void *data, | |
377 | struct drm_file *file) | |
378 | { | |
776dc384 | 379 | struct host1x *host = dev_get_drvdata(drm->dev->parent); |
d43f81cb | 380 | struct drm_tegra_syncpt_read *args = data; |
776dc384 | 381 | struct host1x_syncpt *sp; |
d43f81cb | 382 | |
776dc384 | 383 | sp = host1x_syncpt_get(host, args->id); |
d43f81cb TB |
384 | if (!sp) |
385 | return -EINVAL; | |
386 | ||
387 | args->value = host1x_syncpt_read_min(sp); | |
388 | return 0; | |
389 | } | |
390 | ||
391 | static int tegra_syncpt_incr(struct drm_device *drm, void *data, | |
392 | struct drm_file *file) | |
393 | { | |
776dc384 | 394 | struct host1x *host1x = dev_get_drvdata(drm->dev->parent); |
d43f81cb | 395 | struct drm_tegra_syncpt_incr *args = data; |
776dc384 | 396 | struct host1x_syncpt *sp; |
d43f81cb | 397 | |
776dc384 | 398 | sp = host1x_syncpt_get(host1x, args->id); |
d43f81cb TB |
399 | if (!sp) |
400 | return -EINVAL; | |
401 | ||
ebae30b1 | 402 | return host1x_syncpt_incr(sp); |
d43f81cb TB |
403 | } |
404 | ||
405 | static int tegra_syncpt_wait(struct drm_device *drm, void *data, | |
406 | struct drm_file *file) | |
407 | { | |
776dc384 | 408 | struct host1x *host1x = dev_get_drvdata(drm->dev->parent); |
d43f81cb | 409 | struct drm_tegra_syncpt_wait *args = data; |
776dc384 | 410 | struct host1x_syncpt *sp; |
d43f81cb | 411 | |
776dc384 | 412 | sp = host1x_syncpt_get(host1x, args->id); |
d43f81cb TB |
413 | if (!sp) |
414 | return -EINVAL; | |
415 | ||
4c69ac12 DO |
416 | return host1x_syncpt_wait(sp, args->thresh, |
417 | msecs_to_jiffies(args->timeout), | |
d43f81cb TB |
418 | &args->value); |
419 | } | |
420 | ||
bdd2f9cd TR |
421 | static int tegra_client_open(struct tegra_drm_file *fpriv, |
422 | struct tegra_drm_client *client, | |
423 | struct tegra_drm_context *context) | |
424 | { | |
425 | int err; | |
426 | ||
427 | err = client->ops->open_channel(client, context); | |
428 | if (err < 0) | |
429 | return err; | |
430 | ||
d6c153ec | 431 | err = idr_alloc(&fpriv->contexts, context, 1, 0, GFP_KERNEL); |
bdd2f9cd TR |
432 | if (err < 0) { |
433 | client->ops->close_channel(context); | |
434 | return err; | |
435 | } | |
436 | ||
437 | context->client = client; | |
438 | context->id = err; | |
439 | ||
440 | return 0; | |
441 | } | |
442 | ||
d43f81cb TB |
443 | static int tegra_open_channel(struct drm_device *drm, void *data, |
444 | struct drm_file *file) | |
445 | { | |
08943e6c | 446 | struct tegra_drm_file *fpriv = file->driver_priv; |
386a2a71 | 447 | struct tegra_drm *tegra = drm->dev_private; |
d43f81cb | 448 | struct drm_tegra_open_channel *args = data; |
c88c3630 | 449 | struct tegra_drm_context *context; |
53fa7f72 | 450 | struct tegra_drm_client *client; |
d43f81cb TB |
451 | int err = -ENODEV; |
452 | ||
453 | context = kzalloc(sizeof(*context), GFP_KERNEL); | |
454 | if (!context) | |
455 | return -ENOMEM; | |
456 | ||
bdd2f9cd TR |
457 | mutex_lock(&fpriv->lock); |
458 | ||
776dc384 | 459 | list_for_each_entry(client, &tegra->clients, list) |
53fa7f72 | 460 | if (client->base.class == args->client) { |
bdd2f9cd TR |
461 | err = tegra_client_open(fpriv, client, context); |
462 | if (err < 0) | |
d43f81cb TB |
463 | break; |
464 | ||
bdd2f9cd TR |
465 | args->context = context->id; |
466 | break; | |
d43f81cb TB |
467 | } |
468 | ||
bdd2f9cd TR |
469 | if (err < 0) |
470 | kfree(context); | |
471 | ||
472 | mutex_unlock(&fpriv->lock); | |
d43f81cb TB |
473 | return err; |
474 | } | |
475 | ||
476 | static int tegra_close_channel(struct drm_device *drm, void *data, | |
477 | struct drm_file *file) | |
478 | { | |
08943e6c | 479 | struct tegra_drm_file *fpriv = file->driver_priv; |
776dc384 | 480 | struct drm_tegra_close_channel *args = data; |
c88c3630 | 481 | struct tegra_drm_context *context; |
bdd2f9cd | 482 | int err = 0; |
c88c3630 | 483 | |
bdd2f9cd | 484 | mutex_lock(&fpriv->lock); |
d43f81cb | 485 | |
1066a895 | 486 | context = idr_find(&fpriv->contexts, args->context); |
bdd2f9cd TR |
487 | if (!context) { |
488 | err = -EINVAL; | |
489 | goto unlock; | |
490 | } | |
d43f81cb | 491 | |
bdd2f9cd | 492 | idr_remove(&fpriv->contexts, context->id); |
c88c3630 | 493 | tegra_drm_context_free(context); |
d43f81cb | 494 | |
bdd2f9cd TR |
495 | unlock: |
496 | mutex_unlock(&fpriv->lock); | |
497 | return err; | |
d43f81cb TB |
498 | } |
499 | ||
500 | static int tegra_get_syncpt(struct drm_device *drm, void *data, | |
501 | struct drm_file *file) | |
502 | { | |
08943e6c | 503 | struct tegra_drm_file *fpriv = file->driver_priv; |
d43f81cb | 504 | struct drm_tegra_get_syncpt *args = data; |
c88c3630 | 505 | struct tegra_drm_context *context; |
d43f81cb | 506 | struct host1x_syncpt *syncpt; |
bdd2f9cd | 507 | int err = 0; |
d43f81cb | 508 | |
bdd2f9cd | 509 | mutex_lock(&fpriv->lock); |
c88c3630 | 510 | |
1066a895 | 511 | context = idr_find(&fpriv->contexts, args->context); |
bdd2f9cd TR |
512 | if (!context) { |
513 | err = -ENODEV; | |
514 | goto unlock; | |
515 | } | |
d43f81cb | 516 | |
bdd2f9cd TR |
517 | if (args->index >= context->client->base.num_syncpts) { |
518 | err = -EINVAL; | |
519 | goto unlock; | |
520 | } | |
d43f81cb | 521 | |
53fa7f72 | 522 | syncpt = context->client->base.syncpts[args->index]; |
d43f81cb TB |
523 | args->id = host1x_syncpt_id(syncpt); |
524 | ||
bdd2f9cd TR |
525 | unlock: |
526 | mutex_unlock(&fpriv->lock); | |
527 | return err; | |
d43f81cb TB |
528 | } |
529 | ||
530 | static int tegra_submit(struct drm_device *drm, void *data, | |
531 | struct drm_file *file) | |
532 | { | |
08943e6c | 533 | struct tegra_drm_file *fpriv = file->driver_priv; |
d43f81cb | 534 | struct drm_tegra_submit *args = data; |
c88c3630 | 535 | struct tegra_drm_context *context; |
bdd2f9cd | 536 | int err; |
c88c3630 | 537 | |
bdd2f9cd | 538 | mutex_lock(&fpriv->lock); |
d43f81cb | 539 | |
1066a895 | 540 | context = idr_find(&fpriv->contexts, args->context); |
bdd2f9cd TR |
541 | if (!context) { |
542 | err = -ENODEV; | |
543 | goto unlock; | |
544 | } | |
d43f81cb | 545 | |
bdd2f9cd | 546 | err = context->client->ops->submit(context, args, drm, file); |
d43f81cb | 547 | |
bdd2f9cd TR |
548 | unlock: |
549 | mutex_unlock(&fpriv->lock); | |
550 | return err; | |
d43f81cb | 551 | } |
c54a169b AM |
552 | |
553 | static int tegra_get_syncpt_base(struct drm_device *drm, void *data, | |
554 | struct drm_file *file) | |
555 | { | |
556 | struct tegra_drm_file *fpriv = file->driver_priv; | |
557 | struct drm_tegra_get_syncpt_base *args = data; | |
558 | struct tegra_drm_context *context; | |
559 | struct host1x_syncpt_base *base; | |
560 | struct host1x_syncpt *syncpt; | |
bdd2f9cd | 561 | int err = 0; |
c54a169b | 562 | |
bdd2f9cd | 563 | mutex_lock(&fpriv->lock); |
c54a169b | 564 | |
1066a895 | 565 | context = idr_find(&fpriv->contexts, args->context); |
bdd2f9cd TR |
566 | if (!context) { |
567 | err = -ENODEV; | |
568 | goto unlock; | |
569 | } | |
c54a169b | 570 | |
bdd2f9cd TR |
571 | if (args->syncpt >= context->client->base.num_syncpts) { |
572 | err = -EINVAL; | |
573 | goto unlock; | |
574 | } | |
c54a169b AM |
575 | |
576 | syncpt = context->client->base.syncpts[args->syncpt]; | |
577 | ||
578 | base = host1x_syncpt_get_base(syncpt); | |
bdd2f9cd TR |
579 | if (!base) { |
580 | err = -ENXIO; | |
581 | goto unlock; | |
582 | } | |
c54a169b AM |
583 | |
584 | args->id = host1x_syncpt_base_id(base); | |
585 | ||
bdd2f9cd TR |
586 | unlock: |
587 | mutex_unlock(&fpriv->lock); | |
588 | return err; | |
c54a169b | 589 | } |
7678d71f TR |
590 | |
591 | static int tegra_gem_set_tiling(struct drm_device *drm, void *data, | |
592 | struct drm_file *file) | |
593 | { | |
594 | struct drm_tegra_gem_set_tiling *args = data; | |
595 | enum tegra_bo_tiling_mode mode; | |
596 | struct drm_gem_object *gem; | |
597 | unsigned long value = 0; | |
598 | struct tegra_bo *bo; | |
599 | ||
600 | switch (args->mode) { | |
601 | case DRM_TEGRA_GEM_TILING_MODE_PITCH: | |
602 | mode = TEGRA_BO_TILING_MODE_PITCH; | |
603 | ||
604 | if (args->value != 0) | |
605 | return -EINVAL; | |
606 | ||
607 | break; | |
608 | ||
609 | case DRM_TEGRA_GEM_TILING_MODE_TILED: | |
610 | mode = TEGRA_BO_TILING_MODE_TILED; | |
611 | ||
612 | if (args->value != 0) | |
613 | return -EINVAL; | |
614 | ||
615 | break; | |
616 | ||
617 | case DRM_TEGRA_GEM_TILING_MODE_BLOCK: | |
618 | mode = TEGRA_BO_TILING_MODE_BLOCK; | |
619 | ||
620 | if (args->value > 5) | |
621 | return -EINVAL; | |
622 | ||
623 | value = args->value; | |
624 | break; | |
625 | ||
626 | default: | |
627 | return -EINVAL; | |
628 | } | |
629 | ||
a8ad0bd8 | 630 | gem = drm_gem_object_lookup(file, args->handle); |
7678d71f TR |
631 | if (!gem) |
632 | return -ENOENT; | |
633 | ||
634 | bo = to_tegra_bo(gem); | |
635 | ||
636 | bo->tiling.mode = mode; | |
637 | bo->tiling.value = value; | |
638 | ||
7664b2fa | 639 | drm_gem_object_put_unlocked(gem); |
7678d71f TR |
640 | |
641 | return 0; | |
642 | } | |
643 | ||
644 | static int tegra_gem_get_tiling(struct drm_device *drm, void *data, | |
645 | struct drm_file *file) | |
646 | { | |
647 | struct drm_tegra_gem_get_tiling *args = data; | |
648 | struct drm_gem_object *gem; | |
649 | struct tegra_bo *bo; | |
650 | int err = 0; | |
651 | ||
a8ad0bd8 | 652 | gem = drm_gem_object_lookup(file, args->handle); |
7678d71f TR |
653 | if (!gem) |
654 | return -ENOENT; | |
655 | ||
656 | bo = to_tegra_bo(gem); | |
657 | ||
658 | switch (bo->tiling.mode) { | |
659 | case TEGRA_BO_TILING_MODE_PITCH: | |
660 | args->mode = DRM_TEGRA_GEM_TILING_MODE_PITCH; | |
661 | args->value = 0; | |
662 | break; | |
663 | ||
664 | case TEGRA_BO_TILING_MODE_TILED: | |
665 | args->mode = DRM_TEGRA_GEM_TILING_MODE_TILED; | |
666 | args->value = 0; | |
667 | break; | |
668 | ||
669 | case TEGRA_BO_TILING_MODE_BLOCK: | |
670 | args->mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK; | |
671 | args->value = bo->tiling.value; | |
672 | break; | |
673 | ||
674 | default: | |
675 | err = -EINVAL; | |
676 | break; | |
677 | } | |
678 | ||
7664b2fa | 679 | drm_gem_object_put_unlocked(gem); |
7678d71f TR |
680 | |
681 | return err; | |
682 | } | |
7b129087 TR |
683 | |
684 | static int tegra_gem_set_flags(struct drm_device *drm, void *data, | |
685 | struct drm_file *file) | |
686 | { | |
687 | struct drm_tegra_gem_set_flags *args = data; | |
688 | struct drm_gem_object *gem; | |
689 | struct tegra_bo *bo; | |
690 | ||
691 | if (args->flags & ~DRM_TEGRA_GEM_FLAGS) | |
692 | return -EINVAL; | |
693 | ||
a8ad0bd8 | 694 | gem = drm_gem_object_lookup(file, args->handle); |
7b129087 TR |
695 | if (!gem) |
696 | return -ENOENT; | |
697 | ||
698 | bo = to_tegra_bo(gem); | |
699 | bo->flags = 0; | |
700 | ||
701 | if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP) | |
702 | bo->flags |= TEGRA_BO_BOTTOM_UP; | |
703 | ||
7664b2fa | 704 | drm_gem_object_put_unlocked(gem); |
7b129087 TR |
705 | |
706 | return 0; | |
707 | } | |
708 | ||
709 | static int tegra_gem_get_flags(struct drm_device *drm, void *data, | |
710 | struct drm_file *file) | |
711 | { | |
712 | struct drm_tegra_gem_get_flags *args = data; | |
713 | struct drm_gem_object *gem; | |
714 | struct tegra_bo *bo; | |
715 | ||
a8ad0bd8 | 716 | gem = drm_gem_object_lookup(file, args->handle); |
7b129087 TR |
717 | if (!gem) |
718 | return -ENOENT; | |
719 | ||
720 | bo = to_tegra_bo(gem); | |
721 | args->flags = 0; | |
722 | ||
723 | if (bo->flags & TEGRA_BO_BOTTOM_UP) | |
724 | args->flags |= DRM_TEGRA_GEM_BOTTOM_UP; | |
725 | ||
7664b2fa | 726 | drm_gem_object_put_unlocked(gem); |
7b129087 TR |
727 | |
728 | return 0; | |
729 | } | |
d43f81cb TB |
730 | #endif |
731 | ||
baa70943 | 732 | static const struct drm_ioctl_desc tegra_drm_ioctls[] = { |
d43f81cb | 733 | #ifdef CONFIG_DRM_TEGRA_STAGING |
6c68b717 | 734 | DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, |
d6891db2 | 735 | DRM_RENDER_ALLOW), |
6c68b717 | 736 | DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, |
d6891db2 | 737 | DRM_RENDER_ALLOW), |
6c68b717 | 738 | DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, |
d6891db2 | 739 | DRM_RENDER_ALLOW), |
6c68b717 | 740 | DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, |
d6891db2 | 741 | DRM_RENDER_ALLOW), |
6c68b717 | 742 | DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, |
d6891db2 | 743 | DRM_RENDER_ALLOW), |
6c68b717 | 744 | DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, |
d6891db2 | 745 | DRM_RENDER_ALLOW), |
6c68b717 | 746 | DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, |
d6891db2 | 747 | DRM_RENDER_ALLOW), |
6c68b717 | 748 | DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, |
d6891db2 | 749 | DRM_RENDER_ALLOW), |
6c68b717 | 750 | DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, |
d6891db2 | 751 | DRM_RENDER_ALLOW), |
6c68b717 | 752 | DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, |
d6891db2 | 753 | DRM_RENDER_ALLOW), |
6c68b717 | 754 | DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_TILING, tegra_gem_set_tiling, |
d6891db2 | 755 | DRM_RENDER_ALLOW), |
6c68b717 | 756 | DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling, |
d6891db2 | 757 | DRM_RENDER_ALLOW), |
6c68b717 | 758 | DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags, |
d6891db2 | 759 | DRM_RENDER_ALLOW), |
6c68b717 | 760 | DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags, |
d6891db2 | 761 | DRM_RENDER_ALLOW), |
d43f81cb | 762 | #endif |
d8f4a9ed TR |
763 | }; |
764 | ||
765 | static const struct file_operations tegra_drm_fops = { | |
766 | .owner = THIS_MODULE, | |
767 | .open = drm_open, | |
768 | .release = drm_release, | |
769 | .unlocked_ioctl = drm_ioctl, | |
de2ba664 | 770 | .mmap = tegra_drm_mmap, |
d8f4a9ed | 771 | .poll = drm_poll, |
d8f4a9ed | 772 | .read = drm_read, |
d8f4a9ed | 773 | .compat_ioctl = drm_compat_ioctl, |
d8f4a9ed TR |
774 | .llseek = noop_llseek, |
775 | }; | |
776 | ||
bdd2f9cd TR |
777 | static int tegra_drm_context_cleanup(int id, void *p, void *data) |
778 | { | |
779 | struct tegra_drm_context *context = p; | |
780 | ||
781 | tegra_drm_context_free(context); | |
782 | ||
783 | return 0; | |
784 | } | |
785 | ||
bda0ecc4 | 786 | static void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file) |
3c03c46a | 787 | { |
08943e6c | 788 | struct tegra_drm_file *fpriv = file->driver_priv; |
3c03c46a | 789 | |
bdd2f9cd TR |
790 | mutex_lock(&fpriv->lock); |
791 | idr_for_each(&fpriv->contexts, tegra_drm_context_cleanup, NULL); | |
792 | mutex_unlock(&fpriv->lock); | |
d43f81cb | 793 | |
bdd2f9cd TR |
794 | idr_destroy(&fpriv->contexts); |
795 | mutex_destroy(&fpriv->lock); | |
d43f81cb | 796 | kfree(fpriv); |
3c03c46a TR |
797 | } |
798 | ||
e450fcc6 TR |
799 | #ifdef CONFIG_DEBUG_FS |
800 | static int tegra_debugfs_framebuffers(struct seq_file *s, void *data) | |
801 | { | |
802 | struct drm_info_node *node = (struct drm_info_node *)s->private; | |
803 | struct drm_device *drm = node->minor->dev; | |
804 | struct drm_framebuffer *fb; | |
805 | ||
806 | mutex_lock(&drm->mode_config.fb_lock); | |
807 | ||
808 | list_for_each_entry(fb, &drm->mode_config.fb_list, head) { | |
809 | seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n", | |
b00c600e VS |
810 | fb->base.id, fb->width, fb->height, |
811 | fb->format->depth, | |
272725c7 | 812 | fb->format->cpp[0] * 8, |
747a598f | 813 | drm_framebuffer_read_refcount(fb)); |
e450fcc6 TR |
814 | } |
815 | ||
816 | mutex_unlock(&drm->mode_config.fb_lock); | |
817 | ||
818 | return 0; | |
819 | } | |
820 | ||
28c23373 TR |
821 | static int tegra_debugfs_iova(struct seq_file *s, void *data) |
822 | { | |
823 | struct drm_info_node *node = (struct drm_info_node *)s->private; | |
824 | struct drm_device *drm = node->minor->dev; | |
825 | struct tegra_drm *tegra = drm->dev_private; | |
b5c3714f | 826 | struct drm_printer p = drm_seq_file_printer(s); |
28c23373 | 827 | |
68d890a3 MM |
828 | if (tegra->domain) { |
829 | mutex_lock(&tegra->mm_lock); | |
830 | drm_mm_print(&tegra->mm, &p); | |
831 | mutex_unlock(&tegra->mm_lock); | |
832 | } | |
b5c3714f DV |
833 | |
834 | return 0; | |
28c23373 TR |
835 | } |
836 | ||
e450fcc6 TR |
837 | static struct drm_info_list tegra_debugfs_list[] = { |
838 | { "framebuffers", tegra_debugfs_framebuffers, 0 }, | |
28c23373 | 839 | { "iova", tegra_debugfs_iova, 0 }, |
e450fcc6 TR |
840 | }; |
841 | ||
842 | static int tegra_debugfs_init(struct drm_minor *minor) | |
843 | { | |
844 | return drm_debugfs_create_files(tegra_debugfs_list, | |
845 | ARRAY_SIZE(tegra_debugfs_list), | |
846 | minor->debugfs_root, minor); | |
847 | } | |
e450fcc6 TR |
848 | #endif |
849 | ||
9b57f5f2 | 850 | static struct drm_driver tegra_drm_driver = { |
0424fdaf | 851 | .driver_features = DRIVER_MODESET | DRIVER_GEM | |
6c68b717 | 852 | DRIVER_ATOMIC | DRIVER_RENDER, |
d8f4a9ed | 853 | .open = tegra_drm_open, |
bda0ecc4 | 854 | .postclose = tegra_drm_postclose, |
c94bedab | 855 | .lastclose = drm_fb_helper_lastclose, |
d8f4a9ed | 856 | |
e450fcc6 TR |
857 | #if defined(CONFIG_DEBUG_FS) |
858 | .debugfs_init = tegra_debugfs_init, | |
e450fcc6 TR |
859 | #endif |
860 | ||
1ddbdbd6 | 861 | .gem_free_object_unlocked = tegra_bo_free_object, |
de2ba664 | 862 | .gem_vm_ops = &tegra_bo_vm_ops, |
3800391d TR |
863 | |
864 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | |
865 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
866 | .gem_prime_export = tegra_gem_prime_export, | |
867 | .gem_prime_import = tegra_gem_prime_import, | |
868 | ||
de2ba664 | 869 | .dumb_create = tegra_bo_dumb_create, |
d8f4a9ed TR |
870 | |
871 | .ioctls = tegra_drm_ioctls, | |
872 | .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls), | |
873 | .fops = &tegra_drm_fops, | |
874 | ||
875 | .name = DRIVER_NAME, | |
876 | .desc = DRIVER_DESC, | |
877 | .date = DRIVER_DATE, | |
878 | .major = DRIVER_MAJOR, | |
879 | .minor = DRIVER_MINOR, | |
880 | .patchlevel = DRIVER_PATCHLEVEL, | |
881 | }; | |
776dc384 TR |
882 | |
883 | int tegra_drm_register_client(struct tegra_drm *tegra, | |
884 | struct tegra_drm_client *client) | |
885 | { | |
886 | mutex_lock(&tegra->clients_lock); | |
887 | list_add_tail(&client->list, &tegra->clients); | |
8e5d19c6 | 888 | client->drm = tegra; |
776dc384 TR |
889 | mutex_unlock(&tegra->clients_lock); |
890 | ||
891 | return 0; | |
892 | } | |
893 | ||
894 | int tegra_drm_unregister_client(struct tegra_drm *tegra, | |
895 | struct tegra_drm_client *client) | |
896 | { | |
897 | mutex_lock(&tegra->clients_lock); | |
898 | list_del_init(&client->list); | |
8e5d19c6 | 899 | client->drm = NULL; |
776dc384 TR |
900 | mutex_unlock(&tegra->clients_lock); |
901 | ||
902 | return 0; | |
903 | } | |
904 | ||
7edd7961 | 905 | int host1x_client_iommu_attach(struct host1x_client *client) |
0c407de5 | 906 | { |
fa6661b7 | 907 | struct iommu_domain *domain = iommu_get_domain_for_dev(client->dev); |
608f43ad | 908 | struct drm_device *drm = dev_get_drvdata(client->host); |
0c407de5 TR |
909 | struct tegra_drm *tegra = drm->dev_private; |
910 | struct iommu_group *group = NULL; | |
911 | int err; | |
912 | ||
fa6661b7 TR |
913 | /* |
914 | * If the host1x client is already attached to an IOMMU domain that is | |
915 | * not the shared IOMMU domain, don't try to attach it to a different | |
916 | * domain. This allows using the IOMMU-backed DMA API. | |
917 | */ | |
918 | if (domain && domain != tegra->domain) | |
919 | return 0; | |
7edd7961 | 920 | |
fa6661b7 | 921 | if (tegra->domain) { |
0c407de5 | 922 | group = iommu_group_get(client->dev); |
a8817489 | 923 | if (!group) |
aacdf198 | 924 | return -ENODEV; |
0c407de5 | 925 | |
7edd7961 | 926 | if (domain != tegra->domain) { |
0c407de5 TR |
927 | err = iommu_attach_group(tegra->domain, group); |
928 | if (err < 0) { | |
929 | iommu_group_put(group); | |
aacdf198 | 930 | return err; |
0c407de5 | 931 | } |
0c407de5 | 932 | } |
fa6661b7 TR |
933 | |
934 | tegra->use_explicit_iommu = true; | |
0c407de5 TR |
935 | } |
936 | ||
aacdf198 TR |
937 | client->group = group; |
938 | ||
939 | return 0; | |
0c407de5 TR |
940 | } |
941 | ||
aacdf198 | 942 | void host1x_client_iommu_detach(struct host1x_client *client) |
0c407de5 | 943 | { |
608f43ad | 944 | struct drm_device *drm = dev_get_drvdata(client->host); |
0c407de5 | 945 | struct tegra_drm *tegra = drm->dev_private; |
7edd7961 | 946 | struct iommu_domain *domain; |
0c407de5 | 947 | |
aacdf198 | 948 | if (client->group) { |
7edd7961 TR |
949 | /* |
950 | * Devices that are part of the same group may no longer be | |
951 | * attached to a domain at this point because their group may | |
952 | * have been detached by an earlier client. | |
953 | */ | |
954 | domain = iommu_get_domain_for_dev(client->dev); | |
955 | if (domain) | |
aacdf198 | 956 | iommu_detach_group(tegra->domain, client->group); |
0c407de5 | 957 | |
aacdf198 | 958 | iommu_group_put(client->group); |
fa6661b7 | 959 | client->group = NULL; |
0c407de5 TR |
960 | } |
961 | } | |
962 | ||
67485fb8 | 963 | void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *dma) |
ad926015 MP |
964 | { |
965 | struct iova *alloc; | |
966 | void *virt; | |
967 | gfp_t gfp; | |
968 | int err; | |
969 | ||
970 | if (tegra->domain) | |
971 | size = iova_align(&tegra->carveout.domain, size); | |
972 | else | |
973 | size = PAGE_ALIGN(size); | |
974 | ||
975 | gfp = GFP_KERNEL | __GFP_ZERO; | |
976 | if (!tegra->domain) { | |
977 | /* | |
978 | * Many units only support 32-bit addresses, even on 64-bit | |
979 | * SoCs. If there is no IOMMU to translate into a 32-bit IO | |
980 | * virtual address space, force allocations to be in the | |
981 | * lower 32-bit range. | |
982 | */ | |
983 | gfp |= GFP_DMA; | |
984 | } | |
985 | ||
986 | virt = (void *)__get_free_pages(gfp, get_order(size)); | |
987 | if (!virt) | |
988 | return ERR_PTR(-ENOMEM); | |
989 | ||
990 | if (!tegra->domain) { | |
991 | /* | |
992 | * If IOMMU is disabled, devices address physical memory | |
993 | * directly. | |
994 | */ | |
995 | *dma = virt_to_phys(virt); | |
996 | return virt; | |
997 | } | |
998 | ||
999 | alloc = alloc_iova(&tegra->carveout.domain, | |
1000 | size >> tegra->carveout.shift, | |
1001 | tegra->carveout.limit, true); | |
1002 | if (!alloc) { | |
1003 | err = -EBUSY; | |
1004 | goto free_pages; | |
1005 | } | |
1006 | ||
1007 | *dma = iova_dma_addr(&tegra->carveout.domain, alloc); | |
1008 | err = iommu_map(tegra->domain, *dma, virt_to_phys(virt), | |
1009 | size, IOMMU_READ | IOMMU_WRITE); | |
1010 | if (err < 0) | |
1011 | goto free_iova; | |
1012 | ||
1013 | return virt; | |
1014 | ||
1015 | free_iova: | |
1016 | __free_iova(&tegra->carveout.domain, alloc); | |
1017 | free_pages: | |
1018 | free_pages((unsigned long)virt, get_order(size)); | |
1019 | ||
1020 | return ERR_PTR(err); | |
1021 | } | |
1022 | ||
1023 | void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt, | |
1024 | dma_addr_t dma) | |
1025 | { | |
1026 | if (tegra->domain) | |
1027 | size = iova_align(&tegra->carveout.domain, size); | |
1028 | else | |
1029 | size = PAGE_ALIGN(size); | |
1030 | ||
1031 | if (tegra->domain) { | |
1032 | iommu_unmap(tegra->domain, dma, size); | |
1033 | free_iova(&tegra->carveout.domain, | |
1034 | iova_pfn(&tegra->carveout.domain, dma)); | |
1035 | } | |
1036 | ||
1037 | free_pages((unsigned long)virt, get_order(size)); | |
1038 | } | |
1039 | ||
2d9384ff | 1040 | static bool host1x_drm_wants_iommu(struct host1x_device *dev) |
776dc384 | 1041 | { |
501be6c1 | 1042 | struct host1x *host1x = dev_get_drvdata(dev->dev.parent); |
fa6661b7 | 1043 | struct iommu_domain *domain; |
a7303f77 | 1044 | |
fa6661b7 TR |
1045 | /* |
1046 | * If the Tegra DRM clients are backed by an IOMMU, push buffers are | |
1047 | * likely to be allocated beyond the 32-bit boundary if sufficient | |
1048 | * system memory is available. This is problematic on earlier Tegra | |
1049 | * generations where host1x supports a maximum of 32 address bits in | |
1050 | * the GATHER opcode. In this case, unless host1x is behind an IOMMU | |
1051 | * as well it won't be able to process buffers allocated beyond the | |
1052 | * 32-bit boundary. | |
1053 | * | |
1054 | * The DMA API will use bounce buffers in this case, so that could | |
1055 | * perhaps still be made to work, even if less efficient, but there | |
1056 | * is another catch: in order to perform cache maintenance on pages | |
1057 | * allocated for discontiguous buffers we need to map and unmap the | |
1058 | * SG table representing these buffers. This is fine for something | |
1059 | * small like a push buffer, but it exhausts the bounce buffer pool | |
1060 | * (typically on the order of a few MiB) for framebuffers (many MiB | |
1061 | * for any modern resolution). | |
1062 | * | |
1063 | * Work around this by making sure that Tegra DRM clients only use | |
1064 | * an IOMMU if the parent host1x also uses an IOMMU. | |
1065 | * | |
1066 | * Note that there's still a small gap here that we don't cover: if | |
1067 | * the DMA API is backed by an IOMMU there's no way to control which | |
1068 | * device is attached to an IOMMU and which isn't, except via wiring | |
1069 | * up the device tree appropriately. This is considered an problem | |
1070 | * of integration, so care must be taken for the DT to be consistent. | |
1071 | */ | |
2d9384ff TR |
1072 | domain = iommu_get_domain_for_dev(dev->dev.parent); |
1073 | ||
1074 | /* | |
1075 | * Tegra20 and Tegra30 don't support addressing memory beyond the | |
1076 | * 32-bit boundary, so the regular GATHER opcodes will always be | |
1077 | * sufficient and whether or not the host1x is attached to an IOMMU | |
1078 | * doesn't matter. | |
1079 | */ | |
501be6c1 | 1080 | if (!domain && host1x_get_dma_mask(host1x) <= DMA_BIT_MASK(32)) |
2d9384ff TR |
1081 | return true; |
1082 | ||
1083 | return domain != NULL; | |
1084 | } | |
1085 | ||
1086 | static int host1x_drm_probe(struct host1x_device *dev) | |
1087 | { | |
1088 | struct drm_driver *driver = &tegra_drm_driver; | |
1089 | struct tegra_drm *tegra; | |
1090 | struct drm_device *drm; | |
1091 | int err; | |
1092 | ||
1093 | drm = drm_dev_alloc(driver, &dev->dev); | |
1094 | if (IS_ERR(drm)) | |
1095 | return PTR_ERR(drm); | |
1096 | ||
1097 | tegra = kzalloc(sizeof(*tegra), GFP_KERNEL); | |
1098 | if (!tegra) { | |
1099 | err = -ENOMEM; | |
1100 | goto put; | |
1101 | } | |
fa6661b7 | 1102 | |
2d9384ff | 1103 | if (host1x_drm_wants_iommu(dev) && iommu_present(&platform_bus_type)) { |
a7303f77 TR |
1104 | tegra->domain = iommu_domain_alloc(&platform_bus_type); |
1105 | if (!tegra->domain) { | |
1106 | err = -ENOMEM; | |
1107 | goto free; | |
1108 | } | |
1109 | ||
1110 | err = iova_cache_get(); | |
1111 | if (err < 0) | |
1112 | goto domain; | |
1113 | } | |
1114 | ||
1115 | mutex_init(&tegra->clients_lock); | |
1116 | INIT_LIST_HEAD(&tegra->clients); | |
1117 | ||
9910f5c4 | 1118 | dev_set_drvdata(&dev->dev, drm); |
a7303f77 TR |
1119 | drm->dev_private = tegra; |
1120 | tegra->drm = drm; | |
1121 | ||
1122 | drm_mode_config_init(drm); | |
1123 | ||
1124 | drm->mode_config.min_width = 0; | |
1125 | drm->mode_config.min_height = 0; | |
1126 | ||
1127 | drm->mode_config.max_width = 4096; | |
1128 | drm->mode_config.max_height = 4096; | |
1129 | ||
1130 | drm->mode_config.allow_fb_modifiers = true; | |
1131 | ||
1132 | drm->mode_config.normalize_zpos = true; | |
9910f5c4 | 1133 | |
a7303f77 TR |
1134 | drm->mode_config.funcs = &tegra_drm_mode_config_funcs; |
1135 | drm->mode_config.helper_private = &tegra_drm_mode_config_helpers; | |
1136 | ||
1137 | err = tegra_drm_fb_prepare(drm); | |
6e4228fb | 1138 | if (err < 0) |
a7303f77 TR |
1139 | goto config; |
1140 | ||
1141 | drm_kms_helper_poll_init(drm); | |
1142 | ||
1143 | err = host1x_device_init(dev); | |
1144 | if (err < 0) | |
1145 | goto fbdev; | |
1146 | ||
fa6661b7 | 1147 | if (tegra->use_explicit_iommu) { |
a7303f77 TR |
1148 | u64 carveout_start, carveout_end, gem_start, gem_end; |
1149 | u64 dma_mask = dma_get_mask(&dev->dev); | |
1150 | dma_addr_t start, end; | |
1151 | unsigned long order; | |
1152 | ||
1153 | start = tegra->domain->geometry.aperture_start & dma_mask; | |
1154 | end = tegra->domain->geometry.aperture_end & dma_mask; | |
1155 | ||
1156 | gem_start = start; | |
1157 | gem_end = end - CARVEOUT_SZ; | |
1158 | carveout_start = gem_end + 1; | |
1159 | carveout_end = end; | |
1160 | ||
1161 | order = __ffs(tegra->domain->pgsize_bitmap); | |
1162 | init_iova_domain(&tegra->carveout.domain, 1UL << order, | |
1163 | carveout_start >> order); | |
1164 | ||
1165 | tegra->carveout.shift = iova_shift(&tegra->carveout.domain); | |
1166 | tegra->carveout.limit = carveout_end >> tegra->carveout.shift; | |
1167 | ||
1168 | drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1); | |
1169 | mutex_init(&tegra->mm_lock); | |
1170 | ||
1171 | DRM_DEBUG_DRIVER("IOMMU apertures:\n"); | |
1172 | DRM_DEBUG_DRIVER(" GEM: %#llx-%#llx\n", gem_start, gem_end); | |
1173 | DRM_DEBUG_DRIVER(" Carveout: %#llx-%#llx\n", carveout_start, | |
1174 | carveout_end); | |
fa6661b7 TR |
1175 | } else if (tegra->domain) { |
1176 | iommu_domain_free(tegra->domain); | |
1177 | tegra->domain = NULL; | |
1178 | iova_cache_put(); | |
a7303f77 TR |
1179 | } |
1180 | ||
1181 | if (tegra->hub) { | |
1182 | err = tegra_display_hub_prepare(tegra->hub); | |
1183 | if (err < 0) | |
1184 | goto device; | |
1185 | } | |
1186 | ||
1187 | /* | |
1188 | * We don't use the drm_irq_install() helpers provided by the DRM | |
1189 | * core, so we need to set this manually in order to allow the | |
1190 | * DRM_IOCTL_WAIT_VBLANK to operate correctly. | |
1191 | */ | |
1192 | drm->irq_enabled = true; | |
1193 | ||
1194 | /* syncpoints are used for full 32-bit hardware VBLANK counters */ | |
1195 | drm->max_vblank_count = 0xffffffff; | |
1196 | ||
1197 | err = drm_vblank_init(drm, drm->mode_config.num_crtc); | |
1198 | if (err < 0) | |
1199 | goto hub; | |
1200 | ||
1201 | drm_mode_config_reset(drm); | |
1202 | ||
1203 | err = drm_fb_helper_remove_conflicting_framebuffers(NULL, "tegradrmfb", | |
1204 | false); | |
1205 | if (err < 0) | |
1206 | goto hub; | |
1207 | ||
1208 | err = tegra_drm_fb_init(drm); | |
1209 | if (err < 0) | |
1210 | goto hub; | |
6e4228fb | 1211 | |
9910f5c4 TR |
1212 | err = drm_dev_register(drm, 0); |
1213 | if (err < 0) | |
a7303f77 | 1214 | goto fb; |
9910f5c4 | 1215 | |
9910f5c4 TR |
1216 | return 0; |
1217 | ||
a7303f77 TR |
1218 | fb: |
1219 | tegra_drm_fb_exit(drm); | |
1220 | hub: | |
1221 | if (tegra->hub) | |
1222 | tegra_display_hub_cleanup(tegra->hub); | |
1223 | device: | |
1224 | if (tegra->domain) { | |
1225 | mutex_destroy(&tegra->mm_lock); | |
1226 | drm_mm_takedown(&tegra->mm); | |
1227 | put_iova_domain(&tegra->carveout.domain); | |
1228 | iova_cache_put(); | |
1229 | } | |
1230 | ||
1231 | host1x_device_exit(dev); | |
1232 | fbdev: | |
1233 | drm_kms_helper_poll_fini(drm); | |
1234 | tegra_drm_fb_free(drm); | |
1235 | config: | |
1236 | drm_mode_config_cleanup(drm); | |
1237 | domain: | |
1238 | if (tegra->domain) | |
1239 | iommu_domain_free(tegra->domain); | |
1240 | free: | |
1241 | kfree(tegra); | |
9c942096 TZ |
1242 | put: |
1243 | drm_dev_put(drm); | |
9910f5c4 | 1244 | return err; |
776dc384 TR |
1245 | } |
1246 | ||
9910f5c4 | 1247 | static int host1x_drm_remove(struct host1x_device *dev) |
776dc384 | 1248 | { |
9910f5c4 | 1249 | struct drm_device *drm = dev_get_drvdata(&dev->dev); |
a7303f77 TR |
1250 | struct tegra_drm *tegra = drm->dev_private; |
1251 | int err; | |
9910f5c4 TR |
1252 | |
1253 | drm_dev_unregister(drm); | |
a7303f77 TR |
1254 | |
1255 | drm_kms_helper_poll_fini(drm); | |
1256 | tegra_drm_fb_exit(drm); | |
1257 | drm_atomic_helper_shutdown(drm); | |
1258 | drm_mode_config_cleanup(drm); | |
1259 | ||
d66dfcf8 TR |
1260 | if (tegra->hub) |
1261 | tegra_display_hub_cleanup(tegra->hub); | |
1262 | ||
a7303f77 TR |
1263 | err = host1x_device_exit(dev); |
1264 | if (err < 0) | |
1265 | dev_err(&dev->dev, "host1x device cleanup failed: %d\n", err); | |
1266 | ||
1267 | if (tegra->domain) { | |
1268 | mutex_destroy(&tegra->mm_lock); | |
1269 | drm_mm_takedown(&tegra->mm); | |
1270 | put_iova_domain(&tegra->carveout.domain); | |
1271 | iova_cache_put(); | |
1272 | iommu_domain_free(tegra->domain); | |
1273 | } | |
1274 | ||
1275 | kfree(tegra); | |
9c942096 | 1276 | drm_dev_put(drm); |
776dc384 TR |
1277 | |
1278 | return 0; | |
1279 | } | |
1280 | ||
359ae687 TR |
1281 | #ifdef CONFIG_PM_SLEEP |
1282 | static int host1x_drm_suspend(struct device *dev) | |
1283 | { | |
1284 | struct drm_device *drm = dev_get_drvdata(dev); | |
986c58d1 | 1285 | |
53f1e062 | 1286 | return drm_mode_config_helper_suspend(drm); |
359ae687 TR |
1287 | } |
1288 | ||
1289 | static int host1x_drm_resume(struct device *dev) | |
1290 | { | |
1291 | struct drm_device *drm = dev_get_drvdata(dev); | |
359ae687 | 1292 | |
53f1e062 | 1293 | return drm_mode_config_helper_resume(drm); |
359ae687 TR |
1294 | } |
1295 | #endif | |
1296 | ||
a13f1dc4 TR |
1297 | static SIMPLE_DEV_PM_OPS(host1x_drm_pm_ops, host1x_drm_suspend, |
1298 | host1x_drm_resume); | |
359ae687 | 1299 | |
776dc384 TR |
1300 | static const struct of_device_id host1x_drm_subdevs[] = { |
1301 | { .compatible = "nvidia,tegra20-dc", }, | |
1302 | { .compatible = "nvidia,tegra20-hdmi", }, | |
1303 | { .compatible = "nvidia,tegra20-gr2d", }, | |
5f60ed0d | 1304 | { .compatible = "nvidia,tegra20-gr3d", }, |
776dc384 TR |
1305 | { .compatible = "nvidia,tegra30-dc", }, |
1306 | { .compatible = "nvidia,tegra30-hdmi", }, | |
1307 | { .compatible = "nvidia,tegra30-gr2d", }, | |
5f60ed0d | 1308 | { .compatible = "nvidia,tegra30-gr3d", }, |
dec72739 | 1309 | { .compatible = "nvidia,tegra114-dsi", }, |
7d1d28ac | 1310 | { .compatible = "nvidia,tegra114-hdmi", }, |
5f60ed0d | 1311 | { .compatible = "nvidia,tegra114-gr3d", }, |
8620fc62 | 1312 | { .compatible = "nvidia,tegra124-dc", }, |
6b6b6042 | 1313 | { .compatible = "nvidia,tegra124-sor", }, |
fb7be70e | 1314 | { .compatible = "nvidia,tegra124-hdmi", }, |
7d338587 | 1315 | { .compatible = "nvidia,tegra124-dsi", }, |
0ae797a8 | 1316 | { .compatible = "nvidia,tegra124-vic", }, |
c06c7930 | 1317 | { .compatible = "nvidia,tegra132-dsi", }, |
5b4f516f | 1318 | { .compatible = "nvidia,tegra210-dc", }, |
ddfb406b | 1319 | { .compatible = "nvidia,tegra210-dsi", }, |
3309ac83 | 1320 | { .compatible = "nvidia,tegra210-sor", }, |
459cc2c6 | 1321 | { .compatible = "nvidia,tegra210-sor1", }, |
0ae797a8 | 1322 | { .compatible = "nvidia,tegra210-vic", }, |
c4755fb9 | 1323 | { .compatible = "nvidia,tegra186-display", }, |
47307954 | 1324 | { .compatible = "nvidia,tegra186-dc", }, |
c57997bc TR |
1325 | { .compatible = "nvidia,tegra186-sor", }, |
1326 | { .compatible = "nvidia,tegra186-sor1", }, | |
6e44b9ad | 1327 | { .compatible = "nvidia,tegra186-vic", }, |
5725daaa | 1328 | { .compatible = "nvidia,tegra194-display", }, |
47443196 | 1329 | { .compatible = "nvidia,tegra194-dc", }, |
9b6c14b8 | 1330 | { .compatible = "nvidia,tegra194-sor", }, |
d6b9bc02 | 1331 | { .compatible = "nvidia,tegra194-vic", }, |
776dc384 TR |
1332 | { /* sentinel */ } |
1333 | }; | |
1334 | ||
1335 | static struct host1x_driver host1x_drm_driver = { | |
f4c5cf88 TR |
1336 | .driver = { |
1337 | .name = "drm", | |
359ae687 | 1338 | .pm = &host1x_drm_pm_ops, |
f4c5cf88 | 1339 | }, |
776dc384 TR |
1340 | .probe = host1x_drm_probe, |
1341 | .remove = host1x_drm_remove, | |
1342 | .subdevs = host1x_drm_subdevs, | |
1343 | }; | |
1344 | ||
473112e4 | 1345 | static struct platform_driver * const drivers[] = { |
c4755fb9 | 1346 | &tegra_display_hub_driver, |
473112e4 TR |
1347 | &tegra_dc_driver, |
1348 | &tegra_hdmi_driver, | |
1349 | &tegra_dsi_driver, | |
1350 | &tegra_dpaux_driver, | |
1351 | &tegra_sor_driver, | |
1352 | &tegra_gr2d_driver, | |
1353 | &tegra_gr3d_driver, | |
0ae797a8 | 1354 | &tegra_vic_driver, |
473112e4 TR |
1355 | }; |
1356 | ||
776dc384 TR |
1357 | static int __init host1x_drm_init(void) |
1358 | { | |
1359 | int err; | |
1360 | ||
1361 | err = host1x_driver_register(&host1x_drm_driver); | |
1362 | if (err < 0) | |
1363 | return err; | |
1364 | ||
473112e4 | 1365 | err = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); |
776dc384 TR |
1366 | if (err < 0) |
1367 | goto unregister_host1x; | |
1368 | ||
776dc384 TR |
1369 | return 0; |
1370 | ||
776dc384 TR |
1371 | unregister_host1x: |
1372 | host1x_driver_unregister(&host1x_drm_driver); | |
1373 | return err; | |
1374 | } | |
1375 | module_init(host1x_drm_init); | |
1376 | ||
1377 | static void __exit host1x_drm_exit(void) | |
1378 | { | |
473112e4 | 1379 | platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); |
776dc384 TR |
1380 | host1x_driver_unregister(&host1x_drm_driver); |
1381 | } | |
1382 | module_exit(host1x_drm_exit); | |
1383 | ||
1384 | MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); | |
1385 | MODULE_DESCRIPTION("NVIDIA Tegra DRM driver"); | |
1386 | MODULE_LICENSE("GPL v2"); |