diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-05 20:18:44 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-05 20:18:44 -0400 |
commit | 1062ae4982cabbf60f89b4e069fbb7def7edc8f7 (patch) | |
tree | 01f6944b55d5b69615234f8c1f52b4e721f3d8e6 | |
parent | 1a5fb64fee203f9f5a9274c67ddbb821a29f723f (diff) | |
parent | 644b4930bf7e2adeffbe842e1097f7933c6a9158 (diff) |
Merge tag 'drm-forgot-about-tegra-for-v4.12-rc1' of git://people.freedesktop.org/~airlied/linux
Pull drm tegra updates from Dave Airlie:
"I missed a pull request from Thierry, this stuff has been in
linux-next for a while anyways.
It does contain a branch from the iommu tree, but Thierry said it
should be fine"
* tag 'drm-forgot-about-tegra-for-v4.12-rc1' of git://people.freedesktop.org/~airlied/linux:
gpu: host1x: Fix host1x driver shutdown
gpu: host1x: Support module reset
gpu: host1x: Sort includes alphabetically
drm/tegra: Add VIC support
dt-bindings: Add bindings for the Tegra VIC
drm/tegra: Add falcon helper library
drm/tegra: Add Tegra DRM allocation API
drm/tegra: Add tiling FB modifiers
drm/tegra: Don't leak kernel pointer to userspace
drm/tegra: Protect IOMMU operations by mutex
drm/tegra: Enable IOVA API when IOMMU support is enabled
gpu: host1x: Add IOMMU support
gpu: host1x: Fix potential out-of-bounds access
iommu/iova: Fix compile error with CONFIG_IOMMU_IOVA=m
iommu: Add dummy implementations for !IOMMU_IOVA
MAINTAINERS: Add related headers to IOMMU section
iommu/iova: Consolidate code for adding new node to iovad domain rbtree
25 files changed, 1517 insertions, 202 deletions
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt index 0fad7ed2ea19..74e1e8add5a1 100644 --- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt | |||
@@ -249,6 +249,19 @@ of the following host1x client modules: | |||
249 | See ../pinctrl/nvidia,tegra124-dpaux-padctl.txt for information | 249 | See ../pinctrl/nvidia,tegra124-dpaux-padctl.txt for information |
250 | regarding the DPAUX pad controller bindings. | 250 | regarding the DPAUX pad controller bindings. |
251 | 251 | ||
252 | - vic: Video Image Compositor | ||
253 | - compatible : "nvidia,tegra<chip>-vic" | ||
254 | - reg: Physical base address and length of the controller's registers. | ||
255 | - interrupts: The interrupt outputs from the controller. | ||
256 | - clocks: Must contain an entry for each entry in clock-names. | ||
257 | See ../clocks/clock-bindings.txt for details. | ||
258 | - clock-names: Must include the following entries: | ||
259 | - vic: clock input for the VIC hardware | ||
260 | - resets: Must contain an entry for each entry in reset-names. | ||
261 | See ../reset/reset.txt for details. | ||
262 | - reset-names: Must include the following entries: | ||
263 | - vic | ||
264 | |||
252 | Example: | 265 | Example: |
253 | 266 | ||
254 | / { | 267 | / { |
diff --git a/MAINTAINERS b/MAINTAINERS index ac0ce262150c..8944b472b90f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -6843,6 +6843,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git | |||
6843 | S: Maintained | 6843 | S: Maintained |
6844 | F: Documentation/devicetree/bindings/iommu/ | 6844 | F: Documentation/devicetree/bindings/iommu/ |
6845 | F: drivers/iommu/ | 6845 | F: drivers/iommu/ |
6846 | F: include/linux/iommu.h | ||
6847 | F: include/linux/iova.h | ||
6846 | 6848 | ||
6847 | IP MASQUERADING | 6849 | IP MASQUERADING |
6848 | M: Juanjo Ciarlante <jjciarla@raiz.uncu.edu.ar> | 6850 | M: Juanjo Ciarlante <jjciarla@raiz.uncu.edu.ar> |
diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig index bbf5a4b7e0b6..2db29d67193d 100644 --- a/drivers/gpu/drm/tegra/Kconfig +++ b/drivers/gpu/drm/tegra/Kconfig | |||
@@ -7,6 +7,7 @@ config DRM_TEGRA | |||
7 | select DRM_MIPI_DSI | 7 | select DRM_MIPI_DSI |
8 | select DRM_PANEL | 8 | select DRM_PANEL |
9 | select TEGRA_HOST1X | 9 | select TEGRA_HOST1X |
10 | select IOMMU_IOVA if IOMMU_SUPPORT | ||
10 | help | 11 | help |
11 | Choose this option if you have an NVIDIA Tegra SoC. | 12 | Choose this option if you have an NVIDIA Tegra SoC. |
12 | 13 | ||
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index 2c66a8db9da4..6af3a9ad6565 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile | |||
@@ -13,6 +13,8 @@ tegra-drm-y := \ | |||
13 | sor.o \ | 13 | sor.o \ |
14 | dpaux.o \ | 14 | dpaux.o \ |
15 | gr2d.o \ | 15 | gr2d.o \ |
16 | gr3d.o | 16 | gr3d.o \ |
17 | falcon.o \ | ||
18 | vic.o | ||
17 | 19 | ||
18 | obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o | 20 | obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o |
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index dba4e090d3df..9a1e34e48f64 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
@@ -1,13 +1,15 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2012 Avionic Design GmbH | 2 | * Copyright (C) 2012 Avionic Design GmbH |
3 | * Copyright (C) 2012-2013 NVIDIA CORPORATION. All rights reserved. | 3 | * Copyright (C) 2012-2016 NVIDIA CORPORATION. All rights reserved. |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 6 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 7 | * published by the Free Software Foundation. |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <linux/bitops.h> | ||
10 | #include <linux/host1x.h> | 11 | #include <linux/host1x.h> |
12 | #include <linux/idr.h> | ||
11 | #include <linux/iommu.h> | 13 | #include <linux/iommu.h> |
12 | 14 | ||
13 | #include <drm/drm_atomic.h> | 15 | #include <drm/drm_atomic.h> |
@@ -23,8 +25,11 @@ | |||
23 | #define DRIVER_MINOR 0 | 25 | #define DRIVER_MINOR 0 |
24 | #define DRIVER_PATCHLEVEL 0 | 26 | #define DRIVER_PATCHLEVEL 0 |
25 | 27 | ||
28 | #define CARVEOUT_SZ SZ_64M | ||
29 | |||
26 | struct tegra_drm_file { | 30 | struct tegra_drm_file { |
27 | struct list_head contexts; | 31 | struct idr contexts; |
32 | struct mutex lock; | ||
28 | }; | 33 | }; |
29 | 34 | ||
30 | static void tegra_atomic_schedule(struct tegra_drm *tegra, | 35 | static void tegra_atomic_schedule(struct tegra_drm *tegra, |
@@ -126,8 +131,9 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | |||
126 | return -ENOMEM; | 131 | return -ENOMEM; |
127 | 132 | ||
128 | if (iommu_present(&platform_bus_type)) { | 133 | if (iommu_present(&platform_bus_type)) { |
134 | u64 carveout_start, carveout_end, gem_start, gem_end; | ||
129 | struct iommu_domain_geometry *geometry; | 135 | struct iommu_domain_geometry *geometry; |
130 | u64 start, end; | 136 | unsigned long order; |
131 | 137 | ||
132 | tegra->domain = iommu_domain_alloc(&platform_bus_type); | 138 | tegra->domain = iommu_domain_alloc(&platform_bus_type); |
133 | if (!tegra->domain) { | 139 | if (!tegra->domain) { |
@@ -136,12 +142,26 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | |||
136 | } | 142 | } |
137 | 143 | ||
138 | geometry = &tegra->domain->geometry; | 144 | geometry = &tegra->domain->geometry; |
139 | start = geometry->aperture_start; | 145 | gem_start = geometry->aperture_start; |
140 | end = geometry->aperture_end; | 146 | gem_end = geometry->aperture_end - CARVEOUT_SZ; |
141 | 147 | carveout_start = gem_end + 1; | |
142 | DRM_DEBUG_DRIVER("IOMMU aperture initialized (%#llx-%#llx)\n", | 148 | carveout_end = geometry->aperture_end; |
143 | start, end); | 149 | |
144 | drm_mm_init(&tegra->mm, start, end - start + 1); | 150 | order = __ffs(tegra->domain->pgsize_bitmap); |
151 | init_iova_domain(&tegra->carveout.domain, 1UL << order, | ||
152 | carveout_start >> order, | ||
153 | carveout_end >> order); | ||
154 | |||
155 | tegra->carveout.shift = iova_shift(&tegra->carveout.domain); | ||
156 | tegra->carveout.limit = carveout_end >> tegra->carveout.shift; | ||
157 | |||
158 | drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1); | ||
159 | mutex_init(&tegra->mm_lock); | ||
160 | |||
161 | DRM_DEBUG("IOMMU apertures:\n"); | ||
162 | DRM_DEBUG(" GEM: %#llx-%#llx\n", gem_start, gem_end); | ||
163 | DRM_DEBUG(" Carveout: %#llx-%#llx\n", carveout_start, | ||
164 | carveout_end); | ||
145 | } | 165 | } |
146 | 166 | ||
147 | mutex_init(&tegra->clients_lock); | 167 | mutex_init(&tegra->clients_lock); |
@@ -161,6 +181,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) | |||
161 | drm->mode_config.max_width = 4096; | 181 | drm->mode_config.max_width = 4096; |
162 | drm->mode_config.max_height = 4096; | 182 | drm->mode_config.max_height = 4096; |
163 | 183 | ||
184 | drm->mode_config.allow_fb_modifiers = true; | ||
185 | |||
164 | drm->mode_config.funcs = &tegra_drm_mode_funcs; | 186 | drm->mode_config.funcs = &tegra_drm_mode_funcs; |
165 | 187 | ||
166 | err = tegra_drm_fb_prepare(drm); | 188 | err = tegra_drm_fb_prepare(drm); |
@@ -208,6 +230,8 @@ config: | |||
208 | if (tegra->domain) { | 230 | if (tegra->domain) { |
209 | iommu_domain_free(tegra->domain); | 231 | iommu_domain_free(tegra->domain); |
210 | drm_mm_takedown(&tegra->mm); | 232 | drm_mm_takedown(&tegra->mm); |
233 | mutex_destroy(&tegra->mm_lock); | ||
234 | put_iova_domain(&tegra->carveout.domain); | ||
211 | } | 235 | } |
212 | free: | 236 | free: |
213 | kfree(tegra); | 237 | kfree(tegra); |
@@ -232,6 +256,8 @@ static void tegra_drm_unload(struct drm_device *drm) | |||
232 | if (tegra->domain) { | 256 | if (tegra->domain) { |
233 | iommu_domain_free(tegra->domain); | 257 | iommu_domain_free(tegra->domain); |
234 | drm_mm_takedown(&tegra->mm); | 258 | drm_mm_takedown(&tegra->mm); |
259 | mutex_destroy(&tegra->mm_lock); | ||
260 | put_iova_domain(&tegra->carveout.domain); | ||
235 | } | 261 | } |
236 | 262 | ||
237 | kfree(tegra); | 263 | kfree(tegra); |
@@ -245,7 +271,8 @@ static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) | |||
245 | if (!fpriv) | 271 | if (!fpriv) |
246 | return -ENOMEM; | 272 | return -ENOMEM; |
247 | 273 | ||
248 | INIT_LIST_HEAD(&fpriv->contexts); | 274 | idr_init(&fpriv->contexts); |
275 | mutex_init(&fpriv->lock); | ||
249 | filp->driver_priv = fpriv; | 276 | filp->driver_priv = fpriv; |
250 | 277 | ||
251 | return 0; | 278 | return 0; |
@@ -424,21 +451,16 @@ fail: | |||
424 | 451 | ||
425 | 452 | ||
426 | #ifdef CONFIG_DRM_TEGRA_STAGING | 453 | #ifdef CONFIG_DRM_TEGRA_STAGING |
427 | static struct tegra_drm_context *tegra_drm_get_context(__u64 context) | 454 | static struct tegra_drm_context * |
428 | { | 455 | tegra_drm_file_get_context(struct tegra_drm_file *file, u32 id) |
429 | return (struct tegra_drm_context *)(uintptr_t)context; | ||
430 | } | ||
431 | |||
432 | static bool tegra_drm_file_owns_context(struct tegra_drm_file *file, | ||
433 | struct tegra_drm_context *context) | ||
434 | { | 456 | { |
435 | struct tegra_drm_context *ctx; | 457 | struct tegra_drm_context *context; |
436 | 458 | ||
437 | list_for_each_entry(ctx, &file->contexts, list) | 459 | mutex_lock(&file->lock); |
438 | if (ctx == context) | 460 | context = idr_find(&file->contexts, id); |
439 | return true; | 461 | mutex_unlock(&file->lock); |
440 | 462 | ||
441 | return false; | 463 | return context; |
442 | } | 464 | } |
443 | 465 | ||
444 | static int tegra_gem_create(struct drm_device *drm, void *data, | 466 | static int tegra_gem_create(struct drm_device *drm, void *data, |
@@ -519,6 +541,28 @@ static int tegra_syncpt_wait(struct drm_device *drm, void *data, | |||
519 | &args->value); | 541 | &args->value); |
520 | } | 542 | } |
521 | 543 | ||
544 | static int tegra_client_open(struct tegra_drm_file *fpriv, | ||
545 | struct tegra_drm_client *client, | ||
546 | struct tegra_drm_context *context) | ||
547 | { | ||
548 | int err; | ||
549 | |||
550 | err = client->ops->open_channel(client, context); | ||
551 | if (err < 0) | ||
552 | return err; | ||
553 | |||
554 | err = idr_alloc(&fpriv->contexts, context, 0, 0, GFP_KERNEL); | ||
555 | if (err < 0) { | ||
556 | client->ops->close_channel(context); | ||
557 | return err; | ||
558 | } | ||
559 | |||
560 | context->client = client; | ||
561 | context->id = err; | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
522 | static int tegra_open_channel(struct drm_device *drm, void *data, | 566 | static int tegra_open_channel(struct drm_device *drm, void *data, |
523 | struct drm_file *file) | 567 | struct drm_file *file) |
524 | { | 568 | { |
@@ -533,19 +577,22 @@ static int tegra_open_channel(struct drm_device *drm, void *data, | |||
533 | if (!context) | 577 | if (!context) |
534 | return -ENOMEM; | 578 | return -ENOMEM; |
535 | 579 | ||
580 | mutex_lock(&fpriv->lock); | ||
581 | |||
536 | list_for_each_entry(client, &tegra->clients, list) | 582 | list_for_each_entry(client, &tegra->clients, list) |
537 | if (client->base.class == args->client) { | 583 | if (client->base.class == args->client) { |
538 | err = client->ops->open_channel(client, context); | 584 | err = tegra_client_open(fpriv, client, context); |
539 | if (err) | 585 | if (err < 0) |
540 | break; | 586 | break; |
541 | 587 | ||
542 | list_add(&context->list, &fpriv->contexts); | 588 | args->context = context->id; |
543 | args->context = (uintptr_t)context; | 589 | break; |
544 | context->client = client; | ||
545 | return 0; | ||
546 | } | 590 | } |
547 | 591 | ||
548 | kfree(context); | 592 | if (err < 0) |
593 | kfree(context); | ||
594 | |||
595 | mutex_unlock(&fpriv->lock); | ||
549 | return err; | 596 | return err; |
550 | } | 597 | } |
551 | 598 | ||
@@ -555,16 +602,22 @@ static int tegra_close_channel(struct drm_device *drm, void *data, | |||
555 | struct tegra_drm_file *fpriv = file->driver_priv; | 602 | struct tegra_drm_file *fpriv = file->driver_priv; |
556 | struct drm_tegra_close_channel *args = data; | 603 | struct drm_tegra_close_channel *args = data; |
557 | struct tegra_drm_context *context; | 604 | struct tegra_drm_context *context; |
605 | int err = 0; | ||
558 | 606 | ||
559 | context = tegra_drm_get_context(args->context); | 607 | mutex_lock(&fpriv->lock); |
560 | 608 | ||
561 | if (!tegra_drm_file_owns_context(fpriv, context)) | 609 | context = tegra_drm_file_get_context(fpriv, args->context); |
562 | return -EINVAL; | 610 | if (!context) { |
611 | err = -EINVAL; | ||
612 | goto unlock; | ||
613 | } | ||
563 | 614 | ||
564 | list_del(&context->list); | 615 | idr_remove(&fpriv->contexts, context->id); |
565 | tegra_drm_context_free(context); | 616 | tegra_drm_context_free(context); |
566 | 617 | ||
567 | return 0; | 618 | unlock: |
619 | mutex_unlock(&fpriv->lock); | ||
620 | return err; | ||
568 | } | 621 | } |
569 | 622 | ||
570 | static int tegra_get_syncpt(struct drm_device *drm, void *data, | 623 | static int tegra_get_syncpt(struct drm_device *drm, void *data, |
@@ -574,19 +627,27 @@ static int tegra_get_syncpt(struct drm_device *drm, void *data, | |||
574 | struct drm_tegra_get_syncpt *args = data; | 627 | struct drm_tegra_get_syncpt *args = data; |
575 | struct tegra_drm_context *context; | 628 | struct tegra_drm_context *context; |
576 | struct host1x_syncpt *syncpt; | 629 | struct host1x_syncpt *syncpt; |
630 | int err = 0; | ||
577 | 631 | ||
578 | context = tegra_drm_get_context(args->context); | 632 | mutex_lock(&fpriv->lock); |
579 | 633 | ||
580 | if (!tegra_drm_file_owns_context(fpriv, context)) | 634 | context = tegra_drm_file_get_context(fpriv, args->context); |
581 | return -ENODEV; | 635 | if (!context) { |
636 | err = -ENODEV; | ||
637 | goto unlock; | ||
638 | } | ||
582 | 639 | ||
583 | if (args->index >= context->client->base.num_syncpts) | 640 | if (args->index >= context->client->base.num_syncpts) { |
584 | return -EINVAL; | 641 | err = -EINVAL; |
642 | goto unlock; | ||
643 | } | ||
585 | 644 | ||
586 | syncpt = context->client->base.syncpts[args->index]; | 645 | syncpt = context->client->base.syncpts[args->index]; |
587 | args->id = host1x_syncpt_id(syncpt); | 646 | args->id = host1x_syncpt_id(syncpt); |
588 | 647 | ||
589 | return 0; | 648 | unlock: |
649 | mutex_unlock(&fpriv->lock); | ||
650 | return err; | ||
590 | } | 651 | } |
591 | 652 | ||
592 | static int tegra_submit(struct drm_device *drm, void *data, | 653 | static int tegra_submit(struct drm_device *drm, void *data, |
@@ -595,13 +656,21 @@ static int tegra_submit(struct drm_device *drm, void *data, | |||
595 | struct tegra_drm_file *fpriv = file->driver_priv; | 656 | struct tegra_drm_file *fpriv = file->driver_priv; |
596 | struct drm_tegra_submit *args = data; | 657 | struct drm_tegra_submit *args = data; |
597 | struct tegra_drm_context *context; | 658 | struct tegra_drm_context *context; |
659 | int err; | ||
598 | 660 | ||
599 | context = tegra_drm_get_context(args->context); | 661 | mutex_lock(&fpriv->lock); |
662 | |||
663 | context = tegra_drm_file_get_context(fpriv, args->context); | ||
664 | if (!context) { | ||
665 | err = -ENODEV; | ||
666 | goto unlock; | ||
667 | } | ||
600 | 668 | ||
601 | if (!tegra_drm_file_owns_context(fpriv, context)) | 669 | err = context->client->ops->submit(context, args, drm, file); |
602 | return -ENODEV; | ||
603 | 670 | ||
604 | return context->client->ops->submit(context, args, drm, file); | 671 | unlock: |
672 | mutex_unlock(&fpriv->lock); | ||
673 | return err; | ||
605 | } | 674 | } |
606 | 675 | ||
607 | static int tegra_get_syncpt_base(struct drm_device *drm, void *data, | 676 | static int tegra_get_syncpt_base(struct drm_device *drm, void *data, |
@@ -612,24 +681,34 @@ static int tegra_get_syncpt_base(struct drm_device *drm, void *data, | |||
612 | struct tegra_drm_context *context; | 681 | struct tegra_drm_context *context; |
613 | struct host1x_syncpt_base *base; | 682 | struct host1x_syncpt_base *base; |
614 | struct host1x_syncpt *syncpt; | 683 | struct host1x_syncpt *syncpt; |
684 | int err = 0; | ||
615 | 685 | ||
616 | context = tegra_drm_get_context(args->context); | 686 | mutex_lock(&fpriv->lock); |
617 | 687 | ||
618 | if (!tegra_drm_file_owns_context(fpriv, context)) | 688 | context = tegra_drm_file_get_context(fpriv, args->context); |
619 | return -ENODEV; | 689 | if (!context) { |
690 | err = -ENODEV; | ||
691 | goto unlock; | ||
692 | } | ||
620 | 693 | ||
621 | if (args->syncpt >= context->client->base.num_syncpts) | 694 | if (args->syncpt >= context->client->base.num_syncpts) { |
622 | return -EINVAL; | 695 | err = -EINVAL; |
696 | goto unlock; | ||
697 | } | ||
623 | 698 | ||
624 | syncpt = context->client->base.syncpts[args->syncpt]; | 699 | syncpt = context->client->base.syncpts[args->syncpt]; |
625 | 700 | ||
626 | base = host1x_syncpt_get_base(syncpt); | 701 | base = host1x_syncpt_get_base(syncpt); |
627 | if (!base) | 702 | if (!base) { |
628 | return -ENXIO; | 703 | err = -ENXIO; |
704 | goto unlock; | ||
705 | } | ||
629 | 706 | ||
630 | args->id = host1x_syncpt_base_id(base); | 707 | args->id = host1x_syncpt_base_id(base); |
631 | 708 | ||
632 | return 0; | 709 | unlock: |
710 | mutex_unlock(&fpriv->lock); | ||
711 | return err; | ||
633 | } | 712 | } |
634 | 713 | ||
635 | static int tegra_gem_set_tiling(struct drm_device *drm, void *data, | 714 | static int tegra_gem_set_tiling(struct drm_device *drm, void *data, |
@@ -804,14 +883,25 @@ static const struct file_operations tegra_drm_fops = { | |||
804 | .llseek = noop_llseek, | 883 | .llseek = noop_llseek, |
805 | }; | 884 | }; |
806 | 885 | ||
886 | static int tegra_drm_context_cleanup(int id, void *p, void *data) | ||
887 | { | ||
888 | struct tegra_drm_context *context = p; | ||
889 | |||
890 | tegra_drm_context_free(context); | ||
891 | |||
892 | return 0; | ||
893 | } | ||
894 | |||
807 | static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) | 895 | static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) |
808 | { | 896 | { |
809 | struct tegra_drm_file *fpriv = file->driver_priv; | 897 | struct tegra_drm_file *fpriv = file->driver_priv; |
810 | struct tegra_drm_context *context, *tmp; | ||
811 | 898 | ||
812 | list_for_each_entry_safe(context, tmp, &fpriv->contexts, list) | 899 | mutex_lock(&fpriv->lock); |
813 | tegra_drm_context_free(context); | 900 | idr_for_each(&fpriv->contexts, tegra_drm_context_cleanup, NULL); |
901 | mutex_unlock(&fpriv->lock); | ||
814 | 902 | ||
903 | idr_destroy(&fpriv->contexts); | ||
904 | mutex_destroy(&fpriv->lock); | ||
815 | kfree(fpriv); | 905 | kfree(fpriv); |
816 | } | 906 | } |
817 | 907 | ||
@@ -844,7 +934,9 @@ static int tegra_debugfs_iova(struct seq_file *s, void *data) | |||
844 | struct tegra_drm *tegra = drm->dev_private; | 934 | struct tegra_drm *tegra = drm->dev_private; |
845 | struct drm_printer p = drm_seq_file_printer(s); | 935 | struct drm_printer p = drm_seq_file_printer(s); |
846 | 936 | ||
937 | mutex_lock(&tegra->mm_lock); | ||
847 | drm_mm_print(&tegra->mm, &p); | 938 | drm_mm_print(&tegra->mm, &p); |
939 | mutex_unlock(&tegra->mm_lock); | ||
848 | 940 | ||
849 | return 0; | 941 | return 0; |
850 | } | 942 | } |
@@ -919,6 +1011,84 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, | |||
919 | return 0; | 1011 | return 0; |
920 | } | 1012 | } |
921 | 1013 | ||
1014 | void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, | ||
1015 | dma_addr_t *dma) | ||
1016 | { | ||
1017 | struct iova *alloc; | ||
1018 | void *virt; | ||
1019 | gfp_t gfp; | ||
1020 | int err; | ||
1021 | |||
1022 | if (tegra->domain) | ||
1023 | size = iova_align(&tegra->carveout.domain, size); | ||
1024 | else | ||
1025 | size = PAGE_ALIGN(size); | ||
1026 | |||
1027 | gfp = GFP_KERNEL | __GFP_ZERO; | ||
1028 | if (!tegra->domain) { | ||
1029 | /* | ||
1030 | * Many units only support 32-bit addresses, even on 64-bit | ||
1031 | * SoCs. If there is no IOMMU to translate into a 32-bit IO | ||
1032 | * virtual address space, force allocations to be in the | ||
1033 | * lower 32-bit range. | ||
1034 | */ | ||
1035 | gfp |= GFP_DMA; | ||
1036 | } | ||
1037 | |||
1038 | virt = (void *)__get_free_pages(gfp, get_order(size)); | ||
1039 | if (!virt) | ||
1040 | return ERR_PTR(-ENOMEM); | ||
1041 | |||
1042 | if (!tegra->domain) { | ||
1043 | /* | ||
1044 | * If IOMMU is disabled, devices address physical memory | ||
1045 | * directly. | ||
1046 | */ | ||
1047 | *dma = virt_to_phys(virt); | ||
1048 | return virt; | ||
1049 | } | ||
1050 | |||
1051 | alloc = alloc_iova(&tegra->carveout.domain, | ||
1052 | size >> tegra->carveout.shift, | ||
1053 | tegra->carveout.limit, true); | ||
1054 | if (!alloc) { | ||
1055 | err = -EBUSY; | ||
1056 | goto free_pages; | ||
1057 | } | ||
1058 | |||
1059 | *dma = iova_dma_addr(&tegra->carveout.domain, alloc); | ||
1060 | err = iommu_map(tegra->domain, *dma, virt_to_phys(virt), | ||
1061 | size, IOMMU_READ | IOMMU_WRITE); | ||
1062 | if (err < 0) | ||
1063 | goto free_iova; | ||
1064 | |||
1065 | return virt; | ||
1066 | |||
1067 | free_iova: | ||
1068 | __free_iova(&tegra->carveout.domain, alloc); | ||
1069 | free_pages: | ||
1070 | free_pages((unsigned long)virt, get_order(size)); | ||
1071 | |||
1072 | return ERR_PTR(err); | ||
1073 | } | ||
1074 | |||
1075 | void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt, | ||
1076 | dma_addr_t dma) | ||
1077 | { | ||
1078 | if (tegra->domain) | ||
1079 | size = iova_align(&tegra->carveout.domain, size); | ||
1080 | else | ||
1081 | size = PAGE_ALIGN(size); | ||
1082 | |||
1083 | if (tegra->domain) { | ||
1084 | iommu_unmap(tegra->domain, dma, size); | ||
1085 | free_iova(&tegra->carveout.domain, | ||
1086 | iova_pfn(&tegra->carveout.domain, dma)); | ||
1087 | } | ||
1088 | |||
1089 | free_pages((unsigned long)virt, get_order(size)); | ||
1090 | } | ||
1091 | |||
922 | static int host1x_drm_probe(struct host1x_device *dev) | 1092 | static int host1x_drm_probe(struct host1x_device *dev) |
923 | { | 1093 | { |
924 | struct drm_driver *driver = &tegra_drm_driver; | 1094 | struct drm_driver *driver = &tegra_drm_driver; |
@@ -1003,11 +1173,13 @@ static const struct of_device_id host1x_drm_subdevs[] = { | |||
1003 | { .compatible = "nvidia,tegra124-sor", }, | 1173 | { .compatible = "nvidia,tegra124-sor", }, |
1004 | { .compatible = "nvidia,tegra124-hdmi", }, | 1174 | { .compatible = "nvidia,tegra124-hdmi", }, |
1005 | { .compatible = "nvidia,tegra124-dsi", }, | 1175 | { .compatible = "nvidia,tegra124-dsi", }, |
1176 | { .compatible = "nvidia,tegra124-vic", }, | ||
1006 | { .compatible = "nvidia,tegra132-dsi", }, | 1177 | { .compatible = "nvidia,tegra132-dsi", }, |
1007 | { .compatible = "nvidia,tegra210-dc", }, | 1178 | { .compatible = "nvidia,tegra210-dc", }, |
1008 | { .compatible = "nvidia,tegra210-dsi", }, | 1179 | { .compatible = "nvidia,tegra210-dsi", }, |
1009 | { .compatible = "nvidia,tegra210-sor", }, | 1180 | { .compatible = "nvidia,tegra210-sor", }, |
1010 | { .compatible = "nvidia,tegra210-sor1", }, | 1181 | { .compatible = "nvidia,tegra210-sor1", }, |
1182 | { .compatible = "nvidia,tegra210-vic", }, | ||
1011 | { /* sentinel */ } | 1183 | { /* sentinel */ } |
1012 | }; | 1184 | }; |
1013 | 1185 | ||
@@ -1029,6 +1201,7 @@ static struct platform_driver * const drivers[] = { | |||
1029 | &tegra_sor_driver, | 1201 | &tegra_sor_driver, |
1030 | &tegra_gr2d_driver, | 1202 | &tegra_gr2d_driver, |
1031 | &tegra_gr3d_driver, | 1203 | &tegra_gr3d_driver, |
1204 | &tegra_vic_driver, | ||
1032 | }; | 1205 | }; |
1033 | 1206 | ||
1034 | static int __init host1x_drm_init(void) | 1207 | static int __init host1x_drm_init(void) |
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 5747accb2271..85aa2e3d9d4e 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h | |||
@@ -12,6 +12,7 @@ | |||
12 | 12 | ||
13 | #include <uapi/drm/tegra_drm.h> | 13 | #include <uapi/drm/tegra_drm.h> |
14 | #include <linux/host1x.h> | 14 | #include <linux/host1x.h> |
15 | #include <linux/iova.h> | ||
15 | #include <linux/of_gpio.h> | 16 | #include <linux/of_gpio.h> |
16 | 17 | ||
17 | #include <drm/drmP.h> | 18 | #include <drm/drmP.h> |
@@ -42,8 +43,15 @@ struct tegra_drm { | |||
42 | struct drm_device *drm; | 43 | struct drm_device *drm; |
43 | 44 | ||
44 | struct iommu_domain *domain; | 45 | struct iommu_domain *domain; |
46 | struct mutex mm_lock; | ||
45 | struct drm_mm mm; | 47 | struct drm_mm mm; |
46 | 48 | ||
49 | struct { | ||
50 | struct iova_domain domain; | ||
51 | unsigned long shift; | ||
52 | unsigned long limit; | ||
53 | } carveout; | ||
54 | |||
47 | struct mutex clients_lock; | 55 | struct mutex clients_lock; |
48 | struct list_head clients; | 56 | struct list_head clients; |
49 | 57 | ||
@@ -67,7 +75,7 @@ struct tegra_drm_client; | |||
67 | struct tegra_drm_context { | 75 | struct tegra_drm_context { |
68 | struct tegra_drm_client *client; | 76 | struct tegra_drm_client *client; |
69 | struct host1x_channel *channel; | 77 | struct host1x_channel *channel; |
70 | struct list_head list; | 78 | unsigned int id; |
71 | }; | 79 | }; |
72 | 80 | ||
73 | struct tegra_drm_client_ops { | 81 | struct tegra_drm_client_ops { |
@@ -105,6 +113,10 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, | |||
105 | int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); | 113 | int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); |
106 | int tegra_drm_exit(struct tegra_drm *tegra); | 114 | int tegra_drm_exit(struct tegra_drm *tegra); |
107 | 115 | ||
116 | void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *iova); | ||
117 | void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt, | ||
118 | dma_addr_t iova); | ||
119 | |||
108 | struct tegra_dc_soc_info; | 120 | struct tegra_dc_soc_info; |
109 | struct tegra_output; | 121 | struct tegra_output; |
110 | 122 | ||
@@ -283,5 +295,6 @@ extern struct platform_driver tegra_dpaux_driver; | |||
283 | extern struct platform_driver tegra_sor_driver; | 295 | extern struct platform_driver tegra_sor_driver; |
284 | extern struct platform_driver tegra_gr2d_driver; | 296 | extern struct platform_driver tegra_gr2d_driver; |
285 | extern struct platform_driver tegra_gr3d_driver; | 297 | extern struct platform_driver tegra_gr3d_driver; |
298 | extern struct platform_driver tegra_vic_driver; | ||
286 | 299 | ||
287 | #endif /* HOST1X_DRM_H */ | 300 | #endif /* HOST1X_DRM_H */ |
diff --git a/drivers/gpu/drm/tegra/falcon.c b/drivers/gpu/drm/tegra/falcon.c new file mode 100644 index 000000000000..f685e72949d1 --- /dev/null +++ b/drivers/gpu/drm/tegra/falcon.c | |||
@@ -0,0 +1,259 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015, NVIDIA Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/platform_device.h> | ||
10 | #include <linux/dma-mapping.h> | ||
11 | #include <linux/firmware.h> | ||
12 | #include <linux/pci_ids.h> | ||
13 | #include <linux/iopoll.h> | ||
14 | |||
15 | #include "falcon.h" | ||
16 | #include "drm.h" | ||
17 | |||
18 | enum falcon_memory { | ||
19 | FALCON_MEMORY_IMEM, | ||
20 | FALCON_MEMORY_DATA, | ||
21 | }; | ||
22 | |||
23 | static void falcon_writel(struct falcon *falcon, u32 value, u32 offset) | ||
24 | { | ||
25 | writel(value, falcon->regs + offset); | ||
26 | } | ||
27 | |||
28 | int falcon_wait_idle(struct falcon *falcon) | ||
29 | { | ||
30 | u32 value; | ||
31 | |||
32 | return readl_poll_timeout(falcon->regs + FALCON_IDLESTATE, value, | ||
33 | (value == 0), 10, 100000); | ||
34 | } | ||
35 | |||
36 | static int falcon_dma_wait_idle(struct falcon *falcon) | ||
37 | { | ||
38 | u32 value; | ||
39 | |||
40 | return readl_poll_timeout(falcon->regs + FALCON_DMATRFCMD, value, | ||
41 | (value & FALCON_DMATRFCMD_IDLE), 10, 100000); | ||
42 | } | ||
43 | |||
44 | static int falcon_copy_chunk(struct falcon *falcon, | ||
45 | phys_addr_t base, | ||
46 | unsigned long offset, | ||
47 | enum falcon_memory target) | ||
48 | { | ||
49 | u32 cmd = FALCON_DMATRFCMD_SIZE_256B; | ||
50 | |||
51 | if (target == FALCON_MEMORY_IMEM) | ||
52 | cmd |= FALCON_DMATRFCMD_IMEM; | ||
53 | |||
54 | falcon_writel(falcon, offset, FALCON_DMATRFMOFFS); | ||
55 | falcon_writel(falcon, base, FALCON_DMATRFFBOFFS); | ||
56 | falcon_writel(falcon, cmd, FALCON_DMATRFCMD); | ||
57 | |||
58 | return falcon_dma_wait_idle(falcon); | ||
59 | } | ||
60 | |||
61 | static void falcon_copy_firmware_image(struct falcon *falcon, | ||
62 | const struct firmware *firmware) | ||
63 | { | ||
64 | u32 *firmware_vaddr = falcon->firmware.vaddr; | ||
65 | dma_addr_t daddr; | ||
66 | size_t i; | ||
67 | int err; | ||
68 | |||
69 | /* copy the whole thing taking into account endianness */ | ||
70 | for (i = 0; i < firmware->size / sizeof(u32); i++) | ||
71 | firmware_vaddr[i] = le32_to_cpu(((u32 *)firmware->data)[i]); | ||
72 | |||
73 | /* ensure that caches are flushed and falcon can see the firmware */ | ||
74 | daddr = dma_map_single(falcon->dev, firmware_vaddr, | ||
75 | falcon->firmware.size, DMA_TO_DEVICE); | ||
76 | err = dma_mapping_error(falcon->dev, daddr); | ||
77 | if (err) { | ||
78 | dev_err(falcon->dev, "failed to map firmware: %d\n", err); | ||
79 | return; | ||
80 | } | ||
81 | dma_sync_single_for_device(falcon->dev, daddr, | ||
82 | falcon->firmware.size, DMA_TO_DEVICE); | ||
83 | dma_unmap_single(falcon->dev, daddr, falcon->firmware.size, | ||
84 | DMA_TO_DEVICE); | ||
85 | } | ||
86 | |||
87 | static int falcon_parse_firmware_image(struct falcon *falcon) | ||
88 | { | ||
89 | struct falcon_fw_bin_header_v1 *bin = (void *)falcon->firmware.vaddr; | ||
90 | struct falcon_fw_os_header_v1 *os; | ||
91 | |||
92 | /* endian problems would show up right here */ | ||
93 | if (bin->magic != PCI_VENDOR_ID_NVIDIA) { | ||
94 | dev_err(falcon->dev, "incorrect firmware magic\n"); | ||
95 | return -EINVAL; | ||
96 | } | ||
97 | |||
98 | /* currently only version 1 is supported */ | ||
99 | if (bin->version != 1) { | ||
100 | dev_err(falcon->dev, "unsupported firmware version\n"); | ||
101 | return -EINVAL; | ||
102 | } | ||
103 | |||
104 | /* check that the firmware size is consistent */ | ||
105 | if (bin->size > falcon->firmware.size) { | ||
106 | dev_err(falcon->dev, "firmware image size inconsistency\n"); | ||
107 | return -EINVAL; | ||
108 | } | ||
109 | |||
110 | os = falcon->firmware.vaddr + bin->os_header_offset; | ||
111 | |||
112 | falcon->firmware.bin_data.size = bin->os_size; | ||
113 | falcon->firmware.bin_data.offset = bin->os_data_offset; | ||
114 | falcon->firmware.code.offset = os->code_offset; | ||
115 | falcon->firmware.code.size = os->code_size; | ||
116 | falcon->firmware.data.offset = os->data_offset; | ||
117 | falcon->firmware.data.size = os->data_size; | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | int falcon_read_firmware(struct falcon *falcon, const char *name) | ||
123 | { | ||
124 | int err; | ||
125 | |||
126 | /* request_firmware prints error if it fails */ | ||
127 | err = request_firmware(&falcon->firmware.firmware, name, falcon->dev); | ||
128 | if (err < 0) | ||
129 | return err; | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | int falcon_load_firmware(struct falcon *falcon) | ||
135 | { | ||
136 | const struct firmware *firmware = falcon->firmware.firmware; | ||
137 | int err; | ||
138 | |||
139 | falcon->firmware.size = firmware->size; | ||
140 | |||
141 | /* allocate iova space for the firmware */ | ||
142 | falcon->firmware.vaddr = falcon->ops->alloc(falcon, firmware->size, | ||
143 | &falcon->firmware.paddr); | ||
144 | if (!falcon->firmware.vaddr) { | ||
145 | dev_err(falcon->dev, "dma memory mapping failed\n"); | ||
146 | return -ENOMEM; | ||
147 | } | ||
148 | |||
149 | /* copy firmware image into local area. this also ensures endianness */ | ||
150 | falcon_copy_firmware_image(falcon, firmware); | ||
151 | |||
152 | /* parse the image data */ | ||
153 | err = falcon_parse_firmware_image(falcon); | ||
154 | if (err < 0) { | ||
155 | dev_err(falcon->dev, "failed to parse firmware image\n"); | ||
156 | goto err_setup_firmware_image; | ||
157 | } | ||
158 | |||
159 | release_firmware(firmware); | ||
160 | falcon->firmware.firmware = NULL; | ||
161 | |||
162 | return 0; | ||
163 | |||
164 | err_setup_firmware_image: | ||
165 | falcon->ops->free(falcon, falcon->firmware.size, | ||
166 | falcon->firmware.paddr, falcon->firmware.vaddr); | ||
167 | |||
168 | return err; | ||
169 | } | ||
170 | |||
171 | int falcon_init(struct falcon *falcon) | ||
172 | { | ||
173 | /* check mandatory ops */ | ||
174 | if (!falcon->ops || !falcon->ops->alloc || !falcon->ops->free) | ||
175 | return -EINVAL; | ||
176 | |||
177 | falcon->firmware.vaddr = NULL; | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | void falcon_exit(struct falcon *falcon) | ||
183 | { | ||
184 | if (falcon->firmware.firmware) { | ||
185 | release_firmware(falcon->firmware.firmware); | ||
186 | falcon->firmware.firmware = NULL; | ||
187 | } | ||
188 | |||
189 | if (falcon->firmware.vaddr) { | ||
190 | falcon->ops->free(falcon, falcon->firmware.size, | ||
191 | falcon->firmware.paddr, | ||
192 | falcon->firmware.vaddr); | ||
193 | falcon->firmware.vaddr = NULL; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | int falcon_boot(struct falcon *falcon) | ||
198 | { | ||
199 | unsigned long offset; | ||
200 | int err; | ||
201 | |||
202 | if (!falcon->firmware.vaddr) | ||
203 | return -EINVAL; | ||
204 | |||
205 | falcon_writel(falcon, 0, FALCON_DMACTL); | ||
206 | |||
207 | /* setup the address of the binary data so Falcon can access it later */ | ||
208 | falcon_writel(falcon, (falcon->firmware.paddr + | ||
209 | falcon->firmware.bin_data.offset) >> 8, | ||
210 | FALCON_DMATRFBASE); | ||
211 | |||
212 | /* copy the data segment into Falcon internal memory */ | ||
213 | for (offset = 0; offset < falcon->firmware.data.size; offset += 256) | ||
214 | falcon_copy_chunk(falcon, | ||
215 | falcon->firmware.data.offset + offset, | ||
216 | offset, FALCON_MEMORY_DATA); | ||
217 | |||
218 | /* copy the first code segment into Falcon internal memory */ | ||
219 | falcon_copy_chunk(falcon, falcon->firmware.code.offset, | ||
220 | 0, FALCON_MEMORY_IMEM); | ||
221 | |||
222 | /* setup falcon interrupts */ | ||
223 | falcon_writel(falcon, FALCON_IRQMSET_EXT(0xff) | | ||
224 | FALCON_IRQMSET_SWGEN1 | | ||
225 | FALCON_IRQMSET_SWGEN0 | | ||
226 | FALCON_IRQMSET_EXTERR | | ||
227 | FALCON_IRQMSET_HALT | | ||
228 | FALCON_IRQMSET_WDTMR, | ||
229 | FALCON_IRQMSET); | ||
230 | falcon_writel(falcon, FALCON_IRQDEST_EXT(0xff) | | ||
231 | FALCON_IRQDEST_SWGEN1 | | ||
232 | FALCON_IRQDEST_SWGEN0 | | ||
233 | FALCON_IRQDEST_EXTERR | | ||
234 | FALCON_IRQDEST_HALT, | ||
235 | FALCON_IRQDEST); | ||
236 | |||
237 | /* enable interface */ | ||
238 | falcon_writel(falcon, FALCON_ITFEN_MTHDEN | | ||
239 | FALCON_ITFEN_CTXEN, | ||
240 | FALCON_ITFEN); | ||
241 | |||
242 | /* boot falcon */ | ||
243 | falcon_writel(falcon, 0x00000000, FALCON_BOOTVEC); | ||
244 | falcon_writel(falcon, FALCON_CPUCTL_STARTCPU, FALCON_CPUCTL); | ||
245 | |||
246 | err = falcon_wait_idle(falcon); | ||
247 | if (err < 0) { | ||
248 | dev_err(falcon->dev, "Falcon boot failed due to timeout\n"); | ||
249 | return err; | ||
250 | } | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | void falcon_execute_method(struct falcon *falcon, u32 method, u32 data) | ||
256 | { | ||
257 | falcon_writel(falcon, method >> 2, FALCON_UCLASS_METHOD_OFFSET); | ||
258 | falcon_writel(falcon, data, FALCON_UCLASS_METHOD_DATA); | ||
259 | } | ||
diff --git a/drivers/gpu/drm/tegra/falcon.h b/drivers/gpu/drm/tegra/falcon.h new file mode 100644 index 000000000000..4504ed5a199e --- /dev/null +++ b/drivers/gpu/drm/tegra/falcon.h | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015, NVIDIA Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _FALCON_H_ | ||
10 | #define _FALCON_H_ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | |||
14 | #define FALCON_UCLASS_METHOD_OFFSET 0x00000040 | ||
15 | |||
16 | #define FALCON_UCLASS_METHOD_DATA 0x00000044 | ||
17 | |||
18 | #define FALCON_IRQMSET 0x00001010 | ||
19 | #define FALCON_IRQMSET_WDTMR (1 << 1) | ||
20 | #define FALCON_IRQMSET_HALT (1 << 4) | ||
21 | #define FALCON_IRQMSET_EXTERR (1 << 5) | ||
22 | #define FALCON_IRQMSET_SWGEN0 (1 << 6) | ||
23 | #define FALCON_IRQMSET_SWGEN1 (1 << 7) | ||
24 | #define FALCON_IRQMSET_EXT(v) (((v) & 0xff) << 8) | ||
25 | |||
26 | #define FALCON_IRQDEST 0x0000101c | ||
27 | #define FALCON_IRQDEST_HALT (1 << 4) | ||
28 | #define FALCON_IRQDEST_EXTERR (1 << 5) | ||
29 | #define FALCON_IRQDEST_SWGEN0 (1 << 6) | ||
30 | #define FALCON_IRQDEST_SWGEN1 (1 << 7) | ||
31 | #define FALCON_IRQDEST_EXT(v) (((v) & 0xff) << 8) | ||
32 | |||
33 | #define FALCON_ITFEN 0x00001048 | ||
34 | #define FALCON_ITFEN_CTXEN (1 << 0) | ||
35 | #define FALCON_ITFEN_MTHDEN (1 << 1) | ||
36 | |||
37 | #define FALCON_IDLESTATE 0x0000104c | ||
38 | |||
39 | #define FALCON_CPUCTL 0x00001100 | ||
40 | #define FALCON_CPUCTL_STARTCPU (1 << 1) | ||
41 | |||
42 | #define FALCON_BOOTVEC 0x00001104 | ||
43 | |||
44 | #define FALCON_DMACTL 0x0000110c | ||
45 | #define FALCON_DMACTL_DMEM_SCRUBBING (1 << 1) | ||
46 | #define FALCON_DMACTL_IMEM_SCRUBBING (1 << 2) | ||
47 | |||
48 | #define FALCON_DMATRFBASE 0x00001110 | ||
49 | |||
50 | #define FALCON_DMATRFMOFFS 0x00001114 | ||
51 | |||
52 | #define FALCON_DMATRFCMD 0x00001118 | ||
53 | #define FALCON_DMATRFCMD_IDLE (1 << 1) | ||
54 | #define FALCON_DMATRFCMD_IMEM (1 << 4) | ||
55 | #define FALCON_DMATRFCMD_SIZE_256B (6 << 8) | ||
56 | |||
57 | #define FALCON_DMATRFFBOFFS 0x0000111c | ||
58 | |||
59 | struct falcon_fw_bin_header_v1 { | ||
60 | u32 magic; /* 0x10de */ | ||
61 | u32 version; /* version of bin format (1) */ | ||
62 | u32 size; /* entire image size including this header */ | ||
63 | u32 os_header_offset; | ||
64 | u32 os_data_offset; | ||
65 | u32 os_size; | ||
66 | }; | ||
67 | |||
68 | struct falcon_fw_os_app_v1 { | ||
69 | u32 offset; | ||
70 | u32 size; | ||
71 | }; | ||
72 | |||
73 | struct falcon_fw_os_header_v1 { | ||
74 | u32 code_offset; | ||
75 | u32 code_size; | ||
76 | u32 data_offset; | ||
77 | u32 data_size; | ||
78 | }; | ||
79 | |||
80 | struct falcon; | ||
81 | |||
82 | struct falcon_ops { | ||
83 | void *(*alloc)(struct falcon *falcon, size_t size, | ||
84 | dma_addr_t *paddr); | ||
85 | void (*free)(struct falcon *falcon, size_t size, | ||
86 | dma_addr_t paddr, void *vaddr); | ||
87 | }; | ||
88 | |||
89 | struct falcon_firmware_section { | ||
90 | unsigned long offset; | ||
91 | size_t size; | ||
92 | }; | ||
93 | |||
94 | struct falcon_firmware { | ||
95 | /* Firmware after it is read but not loaded */ | ||
96 | const struct firmware *firmware; | ||
97 | |||
98 | /* Raw firmware data */ | ||
99 | dma_addr_t paddr; | ||
100 | void *vaddr; | ||
101 | size_t size; | ||
102 | |||
103 | /* Parsed firmware information */ | ||
104 | struct falcon_firmware_section bin_data; | ||
105 | struct falcon_firmware_section data; | ||
106 | struct falcon_firmware_section code; | ||
107 | }; | ||
108 | |||
109 | struct falcon { | ||
110 | /* Set by falcon client */ | ||
111 | struct device *dev; | ||
112 | void __iomem *regs; | ||
113 | const struct falcon_ops *ops; | ||
114 | void *data; | ||
115 | |||
116 | struct falcon_firmware firmware; | ||
117 | }; | ||
118 | |||
119 | int falcon_init(struct falcon *falcon); | ||
120 | void falcon_exit(struct falcon *falcon); | ||
121 | int falcon_read_firmware(struct falcon *falcon, const char *firmware_name); | ||
122 | int falcon_load_firmware(struct falcon *falcon); | ||
123 | int falcon_boot(struct falcon *falcon); | ||
124 | void falcon_execute_method(struct falcon *falcon, u32 method, u32 data); | ||
125 | int falcon_wait_idle(struct falcon *falcon); | ||
126 | |||
127 | #endif /* _FALCON_H_ */ | ||
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index c61d67d16ce3..25acb73ee728 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c | |||
@@ -52,9 +52,26 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, | |||
52 | struct tegra_bo_tiling *tiling) | 52 | struct tegra_bo_tiling *tiling) |
53 | { | 53 | { |
54 | struct tegra_fb *fb = to_tegra_fb(framebuffer); | 54 | struct tegra_fb *fb = to_tegra_fb(framebuffer); |
55 | 55 | uint64_t modifier = fb->base.modifier; | |
56 | /* TODO: handle YUV formats? */ | 56 | |
57 | *tiling = fb->planes[0]->tiling; | 57 | switch (fourcc_mod_tegra_mod(modifier)) { |
58 | case NV_FORMAT_MOD_TEGRA_TILED: | ||
59 | tiling->mode = TEGRA_BO_TILING_MODE_TILED; | ||
60 | tiling->value = 0; | ||
61 | break; | ||
62 | |||
63 | case NV_FORMAT_MOD_TEGRA_16BX2_BLOCK(0): | ||
64 | tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; | ||
65 | tiling->value = fourcc_mod_tegra_param(modifier); | ||
66 | if (tiling->value > 5) | ||
67 | return -EINVAL; | ||
68 | break; | ||
69 | |||
70 | default: | ||
71 | /* TODO: handle YUV formats? */ | ||
72 | *tiling = fb->planes[0]->tiling; | ||
73 | break; | ||
74 | } | ||
58 | 75 | ||
59 | return 0; | 76 | return 0; |
60 | } | 77 | } |
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 8672f5d2f237..424569b53e57 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c | |||
@@ -128,12 +128,14 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) | |||
128 | if (!bo->mm) | 128 | if (!bo->mm) |
129 | return -ENOMEM; | 129 | return -ENOMEM; |
130 | 130 | ||
131 | mutex_lock(&tegra->mm_lock); | ||
132 | |||
131 | err = drm_mm_insert_node_generic(&tegra->mm, | 133 | err = drm_mm_insert_node_generic(&tegra->mm, |
132 | bo->mm, bo->gem.size, PAGE_SIZE, 0, 0); | 134 | bo->mm, bo->gem.size, PAGE_SIZE, 0, 0); |
133 | if (err < 0) { | 135 | if (err < 0) { |
134 | dev_err(tegra->drm->dev, "out of I/O virtual memory: %zd\n", | 136 | dev_err(tegra->drm->dev, "out of I/O virtual memory: %zd\n", |
135 | err); | 137 | err); |
136 | goto free; | 138 | goto unlock; |
137 | } | 139 | } |
138 | 140 | ||
139 | bo->paddr = bo->mm->start; | 141 | bo->paddr = bo->mm->start; |
@@ -147,11 +149,14 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) | |||
147 | 149 | ||
148 | bo->size = err; | 150 | bo->size = err; |
149 | 151 | ||
152 | mutex_unlock(&tegra->mm_lock); | ||
153 | |||
150 | return 0; | 154 | return 0; |
151 | 155 | ||
152 | remove: | 156 | remove: |
153 | drm_mm_remove_node(bo->mm); | 157 | drm_mm_remove_node(bo->mm); |
154 | free: | 158 | unlock: |
159 | mutex_unlock(&tegra->mm_lock); | ||
155 | kfree(bo->mm); | 160 | kfree(bo->mm); |
156 | return err; | 161 | return err; |
157 | } | 162 | } |
@@ -161,8 +166,11 @@ static int tegra_bo_iommu_unmap(struct tegra_drm *tegra, struct tegra_bo *bo) | |||
161 | if (!bo->mm) | 166 | if (!bo->mm) |
162 | return 0; | 167 | return 0; |
163 | 168 | ||
169 | mutex_lock(&tegra->mm_lock); | ||
164 | iommu_unmap(tegra->domain, bo->paddr, bo->size); | 170 | iommu_unmap(tegra->domain, bo->paddr, bo->size); |
165 | drm_mm_remove_node(bo->mm); | 171 | drm_mm_remove_node(bo->mm); |
172 | mutex_unlock(&tegra->mm_lock); | ||
173 | |||
166 | kfree(bo->mm); | 174 | kfree(bo->mm); |
167 | 175 | ||
168 | return 0; | 176 | return 0; |
diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c new file mode 100644 index 000000000000..cd804e404a11 --- /dev/null +++ b/drivers/gpu/drm/tegra/vic.c | |||
@@ -0,0 +1,396 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015, NVIDIA Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/clk.h> | ||
10 | #include <linux/host1x.h> | ||
11 | #include <linux/iommu.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/of.h> | ||
14 | #include <linux/of_device.h> | ||
15 | #include <linux/of_platform.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/pm_runtime.h> | ||
18 | #include <linux/reset.h> | ||
19 | |||
20 | #include <soc/tegra/pmc.h> | ||
21 | |||
22 | #include "drm.h" | ||
23 | #include "falcon.h" | ||
24 | #include "vic.h" | ||
25 | |||
26 | struct vic_config { | ||
27 | const char *firmware; | ||
28 | }; | ||
29 | |||
30 | struct vic { | ||
31 | struct falcon falcon; | ||
32 | bool booted; | ||
33 | |||
34 | void __iomem *regs; | ||
35 | struct tegra_drm_client client; | ||
36 | struct host1x_channel *channel; | ||
37 | struct iommu_domain *domain; | ||
38 | struct device *dev; | ||
39 | struct clk *clk; | ||
40 | |||
41 | /* Platform configuration */ | ||
42 | const struct vic_config *config; | ||
43 | }; | ||
44 | |||
45 | static inline struct vic *to_vic(struct tegra_drm_client *client) | ||
46 | { | ||
47 | return container_of(client, struct vic, client); | ||
48 | } | ||
49 | |||
50 | static void vic_writel(struct vic *vic, u32 value, unsigned int offset) | ||
51 | { | ||
52 | writel(value, vic->regs + offset); | ||
53 | } | ||
54 | |||
55 | static int vic_runtime_resume(struct device *dev) | ||
56 | { | ||
57 | struct vic *vic = dev_get_drvdata(dev); | ||
58 | |||
59 | return clk_prepare_enable(vic->clk); | ||
60 | } | ||
61 | |||
62 | static int vic_runtime_suspend(struct device *dev) | ||
63 | { | ||
64 | struct vic *vic = dev_get_drvdata(dev); | ||
65 | |||
66 | clk_disable_unprepare(vic->clk); | ||
67 | |||
68 | vic->booted = false; | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static int vic_boot(struct vic *vic) | ||
74 | { | ||
75 | u32 fce_ucode_size, fce_bin_data_offset; | ||
76 | void *hdr; | ||
77 | int err = 0; | ||
78 | |||
79 | if (vic->booted) | ||
80 | return 0; | ||
81 | |||
82 | /* setup clockgating registers */ | ||
83 | vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) | | ||
84 | CG_IDLE_CG_EN | | ||
85 | CG_WAKEUP_DLY_CNT(4), | ||
86 | NV_PVIC_MISC_PRI_VIC_CG); | ||
87 | |||
88 | err = falcon_boot(&vic->falcon); | ||
89 | if (err < 0) | ||
90 | return err; | ||
91 | |||
92 | hdr = vic->falcon.firmware.vaddr; | ||
93 | fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET); | ||
94 | hdr = vic->falcon.firmware.vaddr + | ||
95 | *(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET); | ||
96 | fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET); | ||
97 | |||
98 | falcon_execute_method(&vic->falcon, VIC_SET_APPLICATION_ID, 1); | ||
99 | falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE, | ||
100 | fce_ucode_size); | ||
101 | falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_OFFSET, | ||
102 | (vic->falcon.firmware.paddr + fce_bin_data_offset) | ||
103 | >> 8); | ||
104 | |||
105 | err = falcon_wait_idle(&vic->falcon); | ||
106 | if (err < 0) { | ||
107 | dev_err(vic->dev, | ||
108 | "failed to set application ID and FCE base\n"); | ||
109 | return err; | ||
110 | } | ||
111 | |||
112 | vic->booted = true; | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static void *vic_falcon_alloc(struct falcon *falcon, size_t size, | ||
118 | dma_addr_t *iova) | ||
119 | { | ||
120 | struct tegra_drm *tegra = falcon->data; | ||
121 | |||
122 | return tegra_drm_alloc(tegra, size, iova); | ||
123 | } | ||
124 | |||
125 | static void vic_falcon_free(struct falcon *falcon, size_t size, | ||
126 | dma_addr_t iova, void *va) | ||
127 | { | ||
128 | struct tegra_drm *tegra = falcon->data; | ||
129 | |||
130 | return tegra_drm_free(tegra, size, va, iova); | ||
131 | } | ||
132 | |||
133 | static const struct falcon_ops vic_falcon_ops = { | ||
134 | .alloc = vic_falcon_alloc, | ||
135 | .free = vic_falcon_free | ||
136 | }; | ||
137 | |||
138 | static int vic_init(struct host1x_client *client) | ||
139 | { | ||
140 | struct tegra_drm_client *drm = host1x_to_drm_client(client); | ||
141 | struct drm_device *dev = dev_get_drvdata(client->parent); | ||
142 | struct tegra_drm *tegra = dev->dev_private; | ||
143 | struct vic *vic = to_vic(drm); | ||
144 | int err; | ||
145 | |||
146 | if (tegra->domain) { | ||
147 | err = iommu_attach_device(tegra->domain, vic->dev); | ||
148 | if (err < 0) { | ||
149 | dev_err(vic->dev, "failed to attach to domain: %d\n", | ||
150 | err); | ||
151 | return err; | ||
152 | } | ||
153 | |||
154 | vic->domain = tegra->domain; | ||
155 | } | ||
156 | |||
157 | if (!vic->falcon.data) { | ||
158 | vic->falcon.data = tegra; | ||
159 | err = falcon_load_firmware(&vic->falcon); | ||
160 | if (err < 0) | ||
161 | goto detach_device; | ||
162 | } | ||
163 | |||
164 | vic->channel = host1x_channel_request(client->dev); | ||
165 | if (!vic->channel) { | ||
166 | err = -ENOMEM; | ||
167 | goto detach_device; | ||
168 | } | ||
169 | |||
170 | client->syncpts[0] = host1x_syncpt_request(client->dev, 0); | ||
171 | if (!client->syncpts[0]) { | ||
172 | err = -ENOMEM; | ||
173 | goto free_channel; | ||
174 | } | ||
175 | |||
176 | err = tegra_drm_register_client(tegra, drm); | ||
177 | if (err < 0) | ||
178 | goto free_syncpt; | ||
179 | |||
180 | return 0; | ||
181 | |||
182 | free_syncpt: | ||
183 | host1x_syncpt_free(client->syncpts[0]); | ||
184 | free_channel: | ||
185 | host1x_channel_free(vic->channel); | ||
186 | detach_device: | ||
187 | if (tegra->domain) | ||
188 | iommu_detach_device(tegra->domain, vic->dev); | ||
189 | |||
190 | return err; | ||
191 | } | ||
192 | |||
193 | static int vic_exit(struct host1x_client *client) | ||
194 | { | ||
195 | struct tegra_drm_client *drm = host1x_to_drm_client(client); | ||
196 | struct drm_device *dev = dev_get_drvdata(client->parent); | ||
197 | struct tegra_drm *tegra = dev->dev_private; | ||
198 | struct vic *vic = to_vic(drm); | ||
199 | int err; | ||
200 | |||
201 | err = tegra_drm_unregister_client(tegra, drm); | ||
202 | if (err < 0) | ||
203 | return err; | ||
204 | |||
205 | host1x_syncpt_free(client->syncpts[0]); | ||
206 | host1x_channel_free(vic->channel); | ||
207 | |||
208 | if (vic->domain) { | ||
209 | iommu_detach_device(vic->domain, vic->dev); | ||
210 | vic->domain = NULL; | ||
211 | } | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static const struct host1x_client_ops vic_client_ops = { | ||
217 | .init = vic_init, | ||
218 | .exit = vic_exit, | ||
219 | }; | ||
220 | |||
221 | static int vic_open_channel(struct tegra_drm_client *client, | ||
222 | struct tegra_drm_context *context) | ||
223 | { | ||
224 | struct vic *vic = to_vic(client); | ||
225 | int err; | ||
226 | |||
227 | err = pm_runtime_get_sync(vic->dev); | ||
228 | if (err < 0) | ||
229 | return err; | ||
230 | |||
231 | err = vic_boot(vic); | ||
232 | if (err < 0) { | ||
233 | pm_runtime_put(vic->dev); | ||
234 | return err; | ||
235 | } | ||
236 | |||
237 | context->channel = host1x_channel_get(vic->channel); | ||
238 | if (!context->channel) { | ||
239 | pm_runtime_put(vic->dev); | ||
240 | return -ENOMEM; | ||
241 | } | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static void vic_close_channel(struct tegra_drm_context *context) | ||
247 | { | ||
248 | struct vic *vic = to_vic(context->client); | ||
249 | |||
250 | host1x_channel_put(context->channel); | ||
251 | |||
252 | pm_runtime_put(vic->dev); | ||
253 | } | ||
254 | |||
255 | static const struct tegra_drm_client_ops vic_ops = { | ||
256 | .open_channel = vic_open_channel, | ||
257 | .close_channel = vic_close_channel, | ||
258 | .submit = tegra_drm_submit, | ||
259 | }; | ||
260 | |||
261 | static const struct vic_config vic_t124_config = { | ||
262 | .firmware = "nvidia/tegra124/vic03_ucode.bin", | ||
263 | }; | ||
264 | |||
265 | static const struct vic_config vic_t210_config = { | ||
266 | .firmware = "nvidia/tegra210/vic04_ucode.bin", | ||
267 | }; | ||
268 | |||
269 | static const struct of_device_id vic_match[] = { | ||
270 | { .compatible = "nvidia,tegra124-vic", .data = &vic_t124_config }, | ||
271 | { .compatible = "nvidia,tegra210-vic", .data = &vic_t210_config }, | ||
272 | { }, | ||
273 | }; | ||
274 | |||
275 | static int vic_probe(struct platform_device *pdev) | ||
276 | { | ||
277 | struct vic_config *vic_config = NULL; | ||
278 | struct device *dev = &pdev->dev; | ||
279 | struct host1x_syncpt **syncpts; | ||
280 | struct resource *regs; | ||
281 | const struct of_device_id *match; | ||
282 | struct vic *vic; | ||
283 | int err; | ||
284 | |||
285 | match = of_match_device(vic_match, dev); | ||
286 | vic_config = (struct vic_config *)match->data; | ||
287 | |||
288 | vic = devm_kzalloc(dev, sizeof(*vic), GFP_KERNEL); | ||
289 | if (!vic) | ||
290 | return -ENOMEM; | ||
291 | |||
292 | syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL); | ||
293 | if (!syncpts) | ||
294 | return -ENOMEM; | ||
295 | |||
296 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
297 | if (!regs) { | ||
298 | dev_err(&pdev->dev, "failed to get registers\n"); | ||
299 | return -ENXIO; | ||
300 | } | ||
301 | |||
302 | vic->regs = devm_ioremap_resource(dev, regs); | ||
303 | if (IS_ERR(vic->regs)) | ||
304 | return PTR_ERR(vic->regs); | ||
305 | |||
306 | vic->clk = devm_clk_get(dev, NULL); | ||
307 | if (IS_ERR(vic->clk)) { | ||
308 | dev_err(&pdev->dev, "failed to get clock\n"); | ||
309 | return PTR_ERR(vic->clk); | ||
310 | } | ||
311 | |||
312 | vic->falcon.dev = dev; | ||
313 | vic->falcon.regs = vic->regs; | ||
314 | vic->falcon.ops = &vic_falcon_ops; | ||
315 | |||
316 | err = falcon_init(&vic->falcon); | ||
317 | if (err < 0) | ||
318 | return err; | ||
319 | |||
320 | err = falcon_read_firmware(&vic->falcon, vic_config->firmware); | ||
321 | if (err < 0) | ||
322 | goto exit_falcon; | ||
323 | |||
324 | platform_set_drvdata(pdev, vic); | ||
325 | |||
326 | INIT_LIST_HEAD(&vic->client.base.list); | ||
327 | vic->client.base.ops = &vic_client_ops; | ||
328 | vic->client.base.dev = dev; | ||
329 | vic->client.base.class = HOST1X_CLASS_VIC; | ||
330 | vic->client.base.syncpts = syncpts; | ||
331 | vic->client.base.num_syncpts = 1; | ||
332 | vic->dev = dev; | ||
333 | vic->config = vic_config; | ||
334 | |||
335 | INIT_LIST_HEAD(&vic->client.list); | ||
336 | vic->client.ops = &vic_ops; | ||
337 | |||
338 | err = host1x_client_register(&vic->client.base); | ||
339 | if (err < 0) { | ||
340 | dev_err(dev, "failed to register host1x client: %d\n", err); | ||
341 | platform_set_drvdata(pdev, NULL); | ||
342 | goto exit_falcon; | ||
343 | } | ||
344 | |||
345 | pm_runtime_enable(&pdev->dev); | ||
346 | if (!pm_runtime_enabled(&pdev->dev)) { | ||
347 | err = vic_runtime_resume(&pdev->dev); | ||
348 | if (err < 0) | ||
349 | goto unregister_client; | ||
350 | } | ||
351 | |||
352 | return 0; | ||
353 | |||
354 | unregister_client: | ||
355 | host1x_client_unregister(&vic->client.base); | ||
356 | exit_falcon: | ||
357 | falcon_exit(&vic->falcon); | ||
358 | |||
359 | return err; | ||
360 | } | ||
361 | |||
362 | static int vic_remove(struct platform_device *pdev) | ||
363 | { | ||
364 | struct vic *vic = platform_get_drvdata(pdev); | ||
365 | int err; | ||
366 | |||
367 | err = host1x_client_unregister(&vic->client.base); | ||
368 | if (err < 0) { | ||
369 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | ||
370 | err); | ||
371 | return err; | ||
372 | } | ||
373 | |||
374 | if (pm_runtime_enabled(&pdev->dev)) | ||
375 | pm_runtime_disable(&pdev->dev); | ||
376 | else | ||
377 | vic_runtime_suspend(&pdev->dev); | ||
378 | |||
379 | falcon_exit(&vic->falcon); | ||
380 | |||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | static const struct dev_pm_ops vic_pm_ops = { | ||
385 | SET_RUNTIME_PM_OPS(vic_runtime_suspend, vic_runtime_resume, NULL) | ||
386 | }; | ||
387 | |||
388 | struct platform_driver tegra_vic_driver = { | ||
389 | .driver = { | ||
390 | .name = "tegra-vic", | ||
391 | .of_match_table = vic_match, | ||
392 | .pm = &vic_pm_ops | ||
393 | }, | ||
394 | .probe = vic_probe, | ||
395 | .remove = vic_remove, | ||
396 | }; | ||
diff --git a/drivers/gpu/drm/tegra/vic.h b/drivers/gpu/drm/tegra/vic.h new file mode 100644 index 000000000000..21844817a7e1 --- /dev/null +++ b/drivers/gpu/drm/tegra/vic.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015, NVIDIA Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef TEGRA_VIC_H | ||
10 | #define TEGRA_VIC_H | ||
11 | |||
12 | /* VIC methods */ | ||
13 | |||
14 | #define VIC_SET_APPLICATION_ID 0x00000200 | ||
15 | #define VIC_SET_FCE_UCODE_SIZE 0x0000071C | ||
16 | #define VIC_SET_FCE_UCODE_OFFSET 0x0000072C | ||
17 | |||
18 | /* VIC registers */ | ||
19 | |||
20 | #define NV_PVIC_MISC_PRI_VIC_CG 0x000016d0 | ||
21 | #define CG_IDLE_CG_DLY_CNT(val) ((val & 0x3f) << 0) | ||
22 | #define CG_IDLE_CG_EN (1 << 6) | ||
23 | #define CG_WAKEUP_DLY_CNT(val) ((val & 0xf) << 16) | ||
24 | |||
25 | /* Firmware offsets */ | ||
26 | |||
27 | #define VIC_UCODE_FCE_HEADER_OFFSET (6*4) | ||
28 | #define VIC_UCODE_FCE_DATA_OFFSET (7*4) | ||
29 | #define FCE_UCODE_SIZE_OFFSET (2*4) | ||
30 | |||
31 | #endif /* TEGRA_VIC_H */ | ||
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index eeb021fe6410..561831e1ae2c 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c | |||
@@ -267,37 +267,6 @@ static int host1x_device_match(struct device *dev, struct device_driver *drv) | |||
267 | return strcmp(dev_name(dev), drv->name) == 0; | 267 | return strcmp(dev_name(dev), drv->name) == 0; |
268 | } | 268 | } |
269 | 269 | ||
270 | static int host1x_device_probe(struct device *dev) | ||
271 | { | ||
272 | struct host1x_driver *driver = to_host1x_driver(dev->driver); | ||
273 | struct host1x_device *device = to_host1x_device(dev); | ||
274 | |||
275 | if (driver->probe) | ||
276 | return driver->probe(device); | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | static int host1x_device_remove(struct device *dev) | ||
282 | { | ||
283 | struct host1x_driver *driver = to_host1x_driver(dev->driver); | ||
284 | struct host1x_device *device = to_host1x_device(dev); | ||
285 | |||
286 | if (driver->remove) | ||
287 | return driver->remove(device); | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static void host1x_device_shutdown(struct device *dev) | ||
293 | { | ||
294 | struct host1x_driver *driver = to_host1x_driver(dev->driver); | ||
295 | struct host1x_device *device = to_host1x_device(dev); | ||
296 | |||
297 | if (driver->shutdown) | ||
298 | driver->shutdown(device); | ||
299 | } | ||
300 | |||
301 | static const struct dev_pm_ops host1x_device_pm_ops = { | 270 | static const struct dev_pm_ops host1x_device_pm_ops = { |
302 | .suspend = pm_generic_suspend, | 271 | .suspend = pm_generic_suspend, |
303 | .resume = pm_generic_resume, | 272 | .resume = pm_generic_resume, |
@@ -310,9 +279,6 @@ static const struct dev_pm_ops host1x_device_pm_ops = { | |||
310 | struct bus_type host1x_bus_type = { | 279 | struct bus_type host1x_bus_type = { |
311 | .name = "host1x", | 280 | .name = "host1x", |
312 | .match = host1x_device_match, | 281 | .match = host1x_device_match, |
313 | .probe = host1x_device_probe, | ||
314 | .remove = host1x_device_remove, | ||
315 | .shutdown = host1x_device_shutdown, | ||
316 | .pm = &host1x_device_pm_ops, | 282 | .pm = &host1x_device_pm_ops, |
317 | }; | 283 | }; |
318 | 284 | ||
@@ -516,6 +482,37 @@ int host1x_unregister(struct host1x *host1x) | |||
516 | return 0; | 482 | return 0; |
517 | } | 483 | } |
518 | 484 | ||
485 | static int host1x_device_probe(struct device *dev) | ||
486 | { | ||
487 | struct host1x_driver *driver = to_host1x_driver(dev->driver); | ||
488 | struct host1x_device *device = to_host1x_device(dev); | ||
489 | |||
490 | if (driver->probe) | ||
491 | return driver->probe(device); | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | static int host1x_device_remove(struct device *dev) | ||
497 | { | ||
498 | struct host1x_driver *driver = to_host1x_driver(dev->driver); | ||
499 | struct host1x_device *device = to_host1x_device(dev); | ||
500 | |||
501 | if (driver->remove) | ||
502 | return driver->remove(device); | ||
503 | |||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | static void host1x_device_shutdown(struct device *dev) | ||
508 | { | ||
509 | struct host1x_driver *driver = to_host1x_driver(dev->driver); | ||
510 | struct host1x_device *device = to_host1x_device(dev); | ||
511 | |||
512 | if (driver->shutdown) | ||
513 | driver->shutdown(device); | ||
514 | } | ||
515 | |||
519 | int host1x_driver_register_full(struct host1x_driver *driver, | 516 | int host1x_driver_register_full(struct host1x_driver *driver, |
520 | struct module *owner) | 517 | struct module *owner) |
521 | { | 518 | { |
@@ -536,6 +533,9 @@ int host1x_driver_register_full(struct host1x_driver *driver, | |||
536 | 533 | ||
537 | driver->driver.bus = &host1x_bus_type; | 534 | driver->driver.bus = &host1x_bus_type; |
538 | driver->driver.owner = owner; | 535 | driver->driver.owner = owner; |
536 | driver->driver.probe = host1x_device_probe; | ||
537 | driver->driver.remove = host1x_device_remove; | ||
538 | driver->driver.shutdown = host1x_device_shutdown; | ||
539 | 539 | ||
540 | return driver_register(&driver->driver); | 540 | return driver_register(&driver->driver); |
541 | } | 541 | } |
diff --git a/drivers/gpu/host1x/cdma.c b/drivers/gpu/host1x/cdma.c index c5d82a8a2ec9..28541b280739 100644 --- a/drivers/gpu/host1x/cdma.c +++ b/drivers/gpu/host1x/cdma.c | |||
@@ -51,9 +51,15 @@ static void host1x_pushbuffer_destroy(struct push_buffer *pb) | |||
51 | struct host1x_cdma *cdma = pb_to_cdma(pb); | 51 | struct host1x_cdma *cdma = pb_to_cdma(pb); |
52 | struct host1x *host1x = cdma_to_host1x(cdma); | 52 | struct host1x *host1x = cdma_to_host1x(cdma); |
53 | 53 | ||
54 | if (pb->phys != 0) | 54 | if (!pb->phys) |
55 | dma_free_wc(host1x->dev, pb->size_bytes + 4, pb->mapped, | 55 | return; |
56 | pb->phys); | 56 | |
57 | if (host1x->domain) { | ||
58 | iommu_unmap(host1x->domain, pb->dma, pb->alloc_size); | ||
59 | free_iova(&host1x->iova, iova_pfn(&host1x->iova, pb->dma)); | ||
60 | } | ||
61 | |||
62 | dma_free_wc(host1x->dev, pb->alloc_size, pb->mapped, pb->phys); | ||
57 | 63 | ||
58 | pb->mapped = NULL; | 64 | pb->mapped = NULL; |
59 | pb->phys = 0; | 65 | pb->phys = 0; |
@@ -66,28 +72,64 @@ static int host1x_pushbuffer_init(struct push_buffer *pb) | |||
66 | { | 72 | { |
67 | struct host1x_cdma *cdma = pb_to_cdma(pb); | 73 | struct host1x_cdma *cdma = pb_to_cdma(pb); |
68 | struct host1x *host1x = cdma_to_host1x(cdma); | 74 | struct host1x *host1x = cdma_to_host1x(cdma); |
75 | struct iova *alloc; | ||
76 | u32 size; | ||
77 | int err; | ||
69 | 78 | ||
70 | pb->mapped = NULL; | 79 | pb->mapped = NULL; |
71 | pb->phys = 0; | 80 | pb->phys = 0; |
72 | pb->size_bytes = HOST1X_PUSHBUFFER_SLOTS * 8; | 81 | pb->size = HOST1X_PUSHBUFFER_SLOTS * 8; |
82 | |||
83 | size = pb->size + 4; | ||
73 | 84 | ||
74 | /* initialize buffer pointers */ | 85 | /* initialize buffer pointers */ |
75 | pb->fence = pb->size_bytes - 8; | 86 | pb->fence = pb->size - 8; |
76 | pb->pos = 0; | 87 | pb->pos = 0; |
77 | 88 | ||
78 | /* allocate and map pushbuffer memory */ | 89 | if (host1x->domain) { |
79 | pb->mapped = dma_alloc_wc(host1x->dev, pb->size_bytes + 4, &pb->phys, | 90 | unsigned long shift; |
80 | GFP_KERNEL); | 91 | |
81 | if (!pb->mapped) | 92 | size = iova_align(&host1x->iova, size); |
82 | goto fail; | 93 | |
94 | pb->mapped = dma_alloc_wc(host1x->dev, size, &pb->phys, | ||
95 | GFP_KERNEL); | ||
96 | if (!pb->mapped) | ||
97 | return -ENOMEM; | ||
98 | |||
99 | shift = iova_shift(&host1x->iova); | ||
100 | alloc = alloc_iova(&host1x->iova, size >> shift, | ||
101 | host1x->iova_end >> shift, true); | ||
102 | if (!alloc) { | ||
103 | err = -ENOMEM; | ||
104 | goto iommu_free_mem; | ||
105 | } | ||
106 | |||
107 | pb->dma = iova_dma_addr(&host1x->iova, alloc); | ||
108 | err = iommu_map(host1x->domain, pb->dma, pb->phys, size, | ||
109 | IOMMU_READ); | ||
110 | if (err) | ||
111 | goto iommu_free_iova; | ||
112 | } else { | ||
113 | pb->mapped = dma_alloc_wc(host1x->dev, size, &pb->phys, | ||
114 | GFP_KERNEL); | ||
115 | if (!pb->mapped) | ||
116 | return -ENOMEM; | ||
117 | |||
118 | pb->dma = pb->phys; | ||
119 | } | ||
120 | |||
121 | pb->alloc_size = size; | ||
83 | 122 | ||
84 | host1x_hw_pushbuffer_init(host1x, pb); | 123 | host1x_hw_pushbuffer_init(host1x, pb); |
85 | 124 | ||
86 | return 0; | 125 | return 0; |
87 | 126 | ||
88 | fail: | 127 | iommu_free_iova: |
89 | host1x_pushbuffer_destroy(pb); | 128 | __free_iova(&host1x->iova, alloc); |
90 | return -ENOMEM; | 129 | iommu_free_mem: |
130 | dma_free_wc(host1x->dev, pb->alloc_size, pb->mapped, pb->phys); | ||
131 | |||
132 | return err; | ||
91 | } | 133 | } |
92 | 134 | ||
93 | /* | 135 | /* |
@@ -101,7 +143,7 @@ static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2) | |||
101 | WARN_ON(pb->pos == pb->fence); | 143 | WARN_ON(pb->pos == pb->fence); |
102 | *(p++) = op1; | 144 | *(p++) = op1; |
103 | *(p++) = op2; | 145 | *(p++) = op2; |
104 | pb->pos = (pb->pos + 8) & (pb->size_bytes - 1); | 146 | pb->pos = (pb->pos + 8) & (pb->size - 1); |
105 | } | 147 | } |
106 | 148 | ||
107 | /* | 149 | /* |
@@ -111,7 +153,7 @@ static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2) | |||
111 | static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots) | 153 | static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots) |
112 | { | 154 | { |
113 | /* Advance the next write position */ | 155 | /* Advance the next write position */ |
114 | pb->fence = (pb->fence + slots * 8) & (pb->size_bytes - 1); | 156 | pb->fence = (pb->fence + slots * 8) & (pb->size - 1); |
115 | } | 157 | } |
116 | 158 | ||
117 | /* | 159 | /* |
@@ -119,7 +161,7 @@ static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots) | |||
119 | */ | 161 | */ |
120 | static u32 host1x_pushbuffer_space(struct push_buffer *pb) | 162 | static u32 host1x_pushbuffer_space(struct push_buffer *pb) |
121 | { | 163 | { |
122 | return ((pb->fence - pb->pos) & (pb->size_bytes - 1)) / 8; | 164 | return ((pb->fence - pb->pos) & (pb->size - 1)) / 8; |
123 | } | 165 | } |
124 | 166 | ||
125 | /* | 167 | /* |
diff --git a/drivers/gpu/host1x/cdma.h b/drivers/gpu/host1x/cdma.h index 470087af8fe5..ec170a78f4e1 100644 --- a/drivers/gpu/host1x/cdma.h +++ b/drivers/gpu/host1x/cdma.h | |||
@@ -43,10 +43,12 @@ struct host1x_job; | |||
43 | 43 | ||
44 | struct push_buffer { | 44 | struct push_buffer { |
45 | void *mapped; /* mapped pushbuffer memory */ | 45 | void *mapped; /* mapped pushbuffer memory */ |
46 | dma_addr_t phys; /* physical address of pushbuffer */ | 46 | dma_addr_t dma; /* device address of pushbuffer */ |
47 | phys_addr_t phys; /* physical address of pushbuffer */ | ||
47 | u32 fence; /* index we've written */ | 48 | u32 fence; /* index we've written */ |
48 | u32 pos; /* index to write to */ | 49 | u32 pos; /* index to write to */ |
49 | u32 size_bytes; | 50 | u32 size; |
51 | u32 alloc_size; | ||
50 | }; | 52 | }; |
51 | 53 | ||
52 | struct buffer_timeout { | 54 | struct buffer_timeout { |
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index a62317af76ad..f05ebb14fa63 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c | |||
@@ -16,23 +16,25 @@ | |||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/list.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/of.h> | ||
23 | #include <linux/of_device.h> | ||
24 | #include <linux/clk.h> | 19 | #include <linux/clk.h> |
25 | #include <linux/io.h> | ||
26 | #include <linux/dma-mapping.h> | 20 | #include <linux/dma-mapping.h> |
21 | #include <linux/io.h> | ||
22 | #include <linux/list.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/of_device.h> | ||
25 | #include <linux/of.h> | ||
26 | #include <linux/slab.h> | ||
27 | 27 | ||
28 | #define CREATE_TRACE_POINTS | 28 | #define CREATE_TRACE_POINTS |
29 | #include <trace/events/host1x.h> | 29 | #include <trace/events/host1x.h> |
30 | #undef CREATE_TRACE_POINTS | ||
30 | 31 | ||
31 | #include "bus.h" | 32 | #include "bus.h" |
32 | #include "dev.h" | ||
33 | #include "intr.h" | ||
34 | #include "channel.h" | 33 | #include "channel.h" |
35 | #include "debug.h" | 34 | #include "debug.h" |
35 | #include "dev.h" | ||
36 | #include "intr.h" | ||
37 | |||
36 | #include "hw/host1x01.h" | 38 | #include "hw/host1x01.h" |
37 | #include "hw/host1x02.h" | 39 | #include "hw/host1x02.h" |
38 | #include "hw/host1x04.h" | 40 | #include "hw/host1x04.h" |
@@ -168,22 +170,56 @@ static int host1x_probe(struct platform_device *pdev) | |||
168 | return err; | 170 | return err; |
169 | } | 171 | } |
170 | 172 | ||
173 | host->rst = devm_reset_control_get(&pdev->dev, "host1x"); | ||
174 | if (IS_ERR(host->rst)) { | ||
175 | err = PTR_ERR(host->clk); | ||
176 | dev_err(&pdev->dev, "failed to get reset: %d\n", err); | ||
177 | return err; | ||
178 | } | ||
179 | |||
180 | if (iommu_present(&platform_bus_type)) { | ||
181 | struct iommu_domain_geometry *geometry; | ||
182 | unsigned long order; | ||
183 | |||
184 | host->domain = iommu_domain_alloc(&platform_bus_type); | ||
185 | if (!host->domain) | ||
186 | return -ENOMEM; | ||
187 | |||
188 | err = iommu_attach_device(host->domain, &pdev->dev); | ||
189 | if (err) | ||
190 | goto fail_free_domain; | ||
191 | |||
192 | geometry = &host->domain->geometry; | ||
193 | |||
194 | order = __ffs(host->domain->pgsize_bitmap); | ||
195 | init_iova_domain(&host->iova, 1UL << order, | ||
196 | geometry->aperture_start >> order, | ||
197 | geometry->aperture_end >> order); | ||
198 | host->iova_end = geometry->aperture_end; | ||
199 | } | ||
200 | |||
171 | err = host1x_channel_list_init(host); | 201 | err = host1x_channel_list_init(host); |
172 | if (err) { | 202 | if (err) { |
173 | dev_err(&pdev->dev, "failed to initialize channel list\n"); | 203 | dev_err(&pdev->dev, "failed to initialize channel list\n"); |
174 | return err; | 204 | goto fail_detach_device; |
175 | } | 205 | } |
176 | 206 | ||
177 | err = clk_prepare_enable(host->clk); | 207 | err = clk_prepare_enable(host->clk); |
178 | if (err < 0) { | 208 | if (err < 0) { |
179 | dev_err(&pdev->dev, "failed to enable clock\n"); | 209 | dev_err(&pdev->dev, "failed to enable clock\n"); |
180 | return err; | 210 | goto fail_detach_device; |
211 | } | ||
212 | |||
213 | err = reset_control_deassert(host->rst); | ||
214 | if (err < 0) { | ||
215 | dev_err(&pdev->dev, "failed to deassert reset: %d\n", err); | ||
216 | goto fail_unprepare_disable; | ||
181 | } | 217 | } |
182 | 218 | ||
183 | err = host1x_syncpt_init(host); | 219 | err = host1x_syncpt_init(host); |
184 | if (err) { | 220 | if (err) { |
185 | dev_err(&pdev->dev, "failed to initialize syncpts\n"); | 221 | dev_err(&pdev->dev, "failed to initialize syncpts\n"); |
186 | goto fail_unprepare_disable; | 222 | goto fail_reset_assert; |
187 | } | 223 | } |
188 | 224 | ||
189 | err = host1x_intr_init(host, syncpt_irq); | 225 | err = host1x_intr_init(host, syncpt_irq); |
@@ -204,8 +240,19 @@ fail_deinit_intr: | |||
204 | host1x_intr_deinit(host); | 240 | host1x_intr_deinit(host); |
205 | fail_deinit_syncpt: | 241 | fail_deinit_syncpt: |
206 | host1x_syncpt_deinit(host); | 242 | host1x_syncpt_deinit(host); |
243 | fail_reset_assert: | ||
244 | reset_control_assert(host->rst); | ||
207 | fail_unprepare_disable: | 245 | fail_unprepare_disable: |
208 | clk_disable_unprepare(host->clk); | 246 | clk_disable_unprepare(host->clk); |
247 | fail_detach_device: | ||
248 | if (host->domain) { | ||
249 | put_iova_domain(&host->iova); | ||
250 | iommu_detach_device(host->domain, &pdev->dev); | ||
251 | } | ||
252 | fail_free_domain: | ||
253 | if (host->domain) | ||
254 | iommu_domain_free(host->domain); | ||
255 | |||
209 | return err; | 256 | return err; |
210 | } | 257 | } |
211 | 258 | ||
@@ -216,8 +263,15 @@ static int host1x_remove(struct platform_device *pdev) | |||
216 | host1x_unregister(host); | 263 | host1x_unregister(host); |
217 | host1x_intr_deinit(host); | 264 | host1x_intr_deinit(host); |
218 | host1x_syncpt_deinit(host); | 265 | host1x_syncpt_deinit(host); |
266 | reset_control_assert(host->rst); | ||
219 | clk_disable_unprepare(host->clk); | 267 | clk_disable_unprepare(host->clk); |
220 | 268 | ||
269 | if (host->domain) { | ||
270 | put_iova_domain(&host->iova); | ||
271 | iommu_detach_device(host->domain, &pdev->dev); | ||
272 | iommu_domain_free(host->domain); | ||
273 | } | ||
274 | |||
221 | return 0; | 275 | return 0; |
222 | } | 276 | } |
223 | 277 | ||
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index 06dd4f85125f..229d08b6a45e 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h | |||
@@ -17,14 +17,17 @@ | |||
17 | #ifndef HOST1X_DEV_H | 17 | #ifndef HOST1X_DEV_H |
18 | #define HOST1X_DEV_H | 18 | #define HOST1X_DEV_H |
19 | 19 | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/device.h> | 20 | #include <linux/device.h> |
21 | #include <linux/iommu.h> | ||
22 | #include <linux/iova.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/reset.h> | ||
22 | 25 | ||
26 | #include "cdma.h" | ||
23 | #include "channel.h" | 27 | #include "channel.h" |
24 | #include "syncpt.h" | ||
25 | #include "intr.h" | 28 | #include "intr.h" |
26 | #include "cdma.h" | ||
27 | #include "job.h" | 29 | #include "job.h" |
30 | #include "syncpt.h" | ||
28 | 31 | ||
29 | struct host1x_syncpt; | 32 | struct host1x_syncpt; |
30 | struct host1x_syncpt_base; | 33 | struct host1x_syncpt_base; |
@@ -107,6 +110,11 @@ struct host1x { | |||
107 | struct host1x_syncpt_base *bases; | 110 | struct host1x_syncpt_base *bases; |
108 | struct device *dev; | 111 | struct device *dev; |
109 | struct clk *clk; | 112 | struct clk *clk; |
113 | struct reset_control *rst; | ||
114 | |||
115 | struct iommu_domain *domain; | ||
116 | struct iova_domain iova; | ||
117 | dma_addr_t iova_end; | ||
110 | 118 | ||
111 | struct mutex intr_mutex; | 119 | struct mutex intr_mutex; |
112 | int intr_syncpt_irq; | 120 | int intr_syncpt_irq; |
diff --git a/drivers/gpu/host1x/hw/cdma_hw.c b/drivers/gpu/host1x/hw/cdma_hw.c index 659c1bbfeeba..6b231119193e 100644 --- a/drivers/gpu/host1x/hw/cdma_hw.c +++ b/drivers/gpu/host1x/hw/cdma_hw.c | |||
@@ -30,7 +30,7 @@ | |||
30 | */ | 30 | */ |
31 | static void push_buffer_init(struct push_buffer *pb) | 31 | static void push_buffer_init(struct push_buffer *pb) |
32 | { | 32 | { |
33 | *(u32 *)(pb->mapped + pb->size_bytes) = host1x_opcode_restart(0); | 33 | *(u32 *)(pb->mapped + pb->size) = host1x_opcode_restart(0); |
34 | } | 34 | } |
35 | 35 | ||
36 | /* | 36 | /* |
@@ -55,8 +55,8 @@ static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr, | |||
55 | *(p++) = HOST1X_OPCODE_NOP; | 55 | *(p++) = HOST1X_OPCODE_NOP; |
56 | *(p++) = HOST1X_OPCODE_NOP; | 56 | *(p++) = HOST1X_OPCODE_NOP; |
57 | dev_dbg(host1x->dev, "%s: NOP at %pad+%#x\n", __func__, | 57 | dev_dbg(host1x->dev, "%s: NOP at %pad+%#x\n", __func__, |
58 | &pb->phys, getptr); | 58 | &pb->dma, getptr); |
59 | getptr = (getptr + 8) & (pb->size_bytes - 1); | 59 | getptr = (getptr + 8) & (pb->size - 1); |
60 | } | 60 | } |
61 | 61 | ||
62 | wmb(); | 62 | wmb(); |
@@ -78,10 +78,9 @@ static void cdma_start(struct host1x_cdma *cdma) | |||
78 | HOST1X_CHANNEL_DMACTRL); | 78 | HOST1X_CHANNEL_DMACTRL); |
79 | 79 | ||
80 | /* set base, put and end pointer */ | 80 | /* set base, put and end pointer */ |
81 | host1x_ch_writel(ch, cdma->push_buffer.phys, HOST1X_CHANNEL_DMASTART); | 81 | host1x_ch_writel(ch, cdma->push_buffer.dma, HOST1X_CHANNEL_DMASTART); |
82 | host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT); | 82 | host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT); |
83 | host1x_ch_writel(ch, cdma->push_buffer.phys + | 83 | host1x_ch_writel(ch, cdma->push_buffer.dma + cdma->push_buffer.size + 4, |
84 | cdma->push_buffer.size_bytes + 4, | ||
85 | HOST1X_CHANNEL_DMAEND); | 84 | HOST1X_CHANNEL_DMAEND); |
86 | 85 | ||
87 | /* reset GET */ | 86 | /* reset GET */ |
@@ -115,9 +114,8 @@ static void cdma_timeout_restart(struct host1x_cdma *cdma, u32 getptr) | |||
115 | HOST1X_CHANNEL_DMACTRL); | 114 | HOST1X_CHANNEL_DMACTRL); |
116 | 115 | ||
117 | /* set base, end pointer (all of memory) */ | 116 | /* set base, end pointer (all of memory) */ |
118 | host1x_ch_writel(ch, cdma->push_buffer.phys, HOST1X_CHANNEL_DMASTART); | 117 | host1x_ch_writel(ch, cdma->push_buffer.dma, HOST1X_CHANNEL_DMASTART); |
119 | host1x_ch_writel(ch, cdma->push_buffer.phys + | 118 | host1x_ch_writel(ch, cdma->push_buffer.dma + cdma->push_buffer.size, |
120 | cdma->push_buffer.size_bytes, | ||
121 | HOST1X_CHANNEL_DMAEND); | 119 | HOST1X_CHANNEL_DMAEND); |
122 | 120 | ||
123 | /* set GET, by loading the value in PUT (then reset GET) */ | 121 | /* set GET, by loading the value in PUT (then reset GET) */ |
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index 92c3df933303..5f5f8ee6143d 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c | |||
@@ -174,9 +174,10 @@ static int do_waitchks(struct host1x_job *job, struct host1x *host, | |||
174 | return 0; | 174 | return 0; |
175 | } | 175 | } |
176 | 176 | ||
177 | static unsigned int pin_job(struct host1x_job *job) | 177 | static unsigned int pin_job(struct host1x *host, struct host1x_job *job) |
178 | { | 178 | { |
179 | unsigned int i; | 179 | unsigned int i; |
180 | int err; | ||
180 | 181 | ||
181 | job->num_unpins = 0; | 182 | job->num_unpins = 0; |
182 | 183 | ||
@@ -186,12 +187,16 @@ static unsigned int pin_job(struct host1x_job *job) | |||
186 | dma_addr_t phys_addr; | 187 | dma_addr_t phys_addr; |
187 | 188 | ||
188 | reloc->target.bo = host1x_bo_get(reloc->target.bo); | 189 | reloc->target.bo = host1x_bo_get(reloc->target.bo); |
189 | if (!reloc->target.bo) | 190 | if (!reloc->target.bo) { |
191 | err = -EINVAL; | ||
190 | goto unpin; | 192 | goto unpin; |
193 | } | ||
191 | 194 | ||
192 | phys_addr = host1x_bo_pin(reloc->target.bo, &sgt); | 195 | phys_addr = host1x_bo_pin(reloc->target.bo, &sgt); |
193 | if (!phys_addr) | 196 | if (!phys_addr) { |
197 | err = -EINVAL; | ||
194 | goto unpin; | 198 | goto unpin; |
199 | } | ||
195 | 200 | ||
196 | job->addr_phys[job->num_unpins] = phys_addr; | 201 | job->addr_phys[job->num_unpins] = phys_addr; |
197 | job->unpins[job->num_unpins].bo = reloc->target.bo; | 202 | job->unpins[job->num_unpins].bo = reloc->target.bo; |
@@ -201,28 +206,67 @@ static unsigned int pin_job(struct host1x_job *job) | |||
201 | 206 | ||
202 | for (i = 0; i < job->num_gathers; i++) { | 207 | for (i = 0; i < job->num_gathers; i++) { |
203 | struct host1x_job_gather *g = &job->gathers[i]; | 208 | struct host1x_job_gather *g = &job->gathers[i]; |
209 | size_t gather_size = 0; | ||
210 | struct scatterlist *sg; | ||
204 | struct sg_table *sgt; | 211 | struct sg_table *sgt; |
205 | dma_addr_t phys_addr; | 212 | dma_addr_t phys_addr; |
213 | unsigned long shift; | ||
214 | struct iova *alloc; | ||
215 | unsigned int j; | ||
206 | 216 | ||
207 | g->bo = host1x_bo_get(g->bo); | 217 | g->bo = host1x_bo_get(g->bo); |
208 | if (!g->bo) | 218 | if (!g->bo) { |
219 | err = -EINVAL; | ||
209 | goto unpin; | 220 | goto unpin; |
221 | } | ||
210 | 222 | ||
211 | phys_addr = host1x_bo_pin(g->bo, &sgt); | 223 | phys_addr = host1x_bo_pin(g->bo, &sgt); |
212 | if (!phys_addr) | 224 | if (!phys_addr) { |
225 | err = -EINVAL; | ||
213 | goto unpin; | 226 | goto unpin; |
227 | } | ||
228 | |||
229 | if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && host->domain) { | ||
230 | for_each_sg(sgt->sgl, sg, sgt->nents, j) | ||
231 | gather_size += sg->length; | ||
232 | gather_size = iova_align(&host->iova, gather_size); | ||
233 | |||
234 | shift = iova_shift(&host->iova); | ||
235 | alloc = alloc_iova(&host->iova, gather_size >> shift, | ||
236 | host->iova_end >> shift, true); | ||
237 | if (!alloc) { | ||
238 | err = -ENOMEM; | ||
239 | goto unpin; | ||
240 | } | ||
241 | |||
242 | err = iommu_map_sg(host->domain, | ||
243 | iova_dma_addr(&host->iova, alloc), | ||
244 | sgt->sgl, sgt->nents, IOMMU_READ); | ||
245 | if (err == 0) { | ||
246 | __free_iova(&host->iova, alloc); | ||
247 | err = -EINVAL; | ||
248 | goto unpin; | ||
249 | } | ||
250 | |||
251 | job->addr_phys[job->num_unpins] = | ||
252 | iova_dma_addr(&host->iova, alloc); | ||
253 | job->unpins[job->num_unpins].size = gather_size; | ||
254 | } else { | ||
255 | job->addr_phys[job->num_unpins] = phys_addr; | ||
256 | } | ||
257 | |||
258 | job->gather_addr_phys[i] = job->addr_phys[job->num_unpins]; | ||
214 | 259 | ||
215 | job->addr_phys[job->num_unpins] = phys_addr; | ||
216 | job->unpins[job->num_unpins].bo = g->bo; | 260 | job->unpins[job->num_unpins].bo = g->bo; |
217 | job->unpins[job->num_unpins].sgt = sgt; | 261 | job->unpins[job->num_unpins].sgt = sgt; |
218 | job->num_unpins++; | 262 | job->num_unpins++; |
219 | } | 263 | } |
220 | 264 | ||
221 | return job->num_unpins; | 265 | return 0; |
222 | 266 | ||
223 | unpin: | 267 | unpin: |
224 | host1x_job_unpin(job); | 268 | host1x_job_unpin(job); |
225 | return 0; | 269 | return err; |
226 | } | 270 | } |
227 | 271 | ||
228 | static int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) | 272 | static int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) |
@@ -525,8 +569,8 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev) | |||
525 | host1x_syncpt_load(host->syncpt + i); | 569 | host1x_syncpt_load(host->syncpt + i); |
526 | 570 | ||
527 | /* pin memory */ | 571 | /* pin memory */ |
528 | err = pin_job(job); | 572 | err = pin_job(host, job); |
529 | if (!err) | 573 | if (err) |
530 | goto out; | 574 | goto out; |
531 | 575 | ||
532 | /* patch gathers */ | 576 | /* patch gathers */ |
@@ -572,11 +616,19 @@ EXPORT_SYMBOL(host1x_job_pin); | |||
572 | 616 | ||
573 | void host1x_job_unpin(struct host1x_job *job) | 617 | void host1x_job_unpin(struct host1x_job *job) |
574 | { | 618 | { |
619 | struct host1x *host = dev_get_drvdata(job->channel->dev->parent); | ||
575 | unsigned int i; | 620 | unsigned int i; |
576 | 621 | ||
577 | for (i = 0; i < job->num_unpins; i++) { | 622 | for (i = 0; i < job->num_unpins; i++) { |
578 | struct host1x_job_unpin_data *unpin = &job->unpins[i]; | 623 | struct host1x_job_unpin_data *unpin = &job->unpins[i]; |
579 | 624 | ||
625 | if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && host->domain) { | ||
626 | iommu_unmap(host->domain, job->addr_phys[i], | ||
627 | unpin->size); | ||
628 | free_iova(&host->iova, | ||
629 | iova_pfn(&host->iova, job->addr_phys[i])); | ||
630 | } | ||
631 | |||
580 | host1x_bo_unpin(unpin->bo, unpin->sgt); | 632 | host1x_bo_unpin(unpin->bo, unpin->sgt); |
581 | host1x_bo_put(unpin->bo); | 633 | host1x_bo_put(unpin->bo); |
582 | } | 634 | } |
diff --git a/drivers/gpu/host1x/job.h b/drivers/gpu/host1x/job.h index 8b3c15df0660..878239c476d2 100644 --- a/drivers/gpu/host1x/job.h +++ b/drivers/gpu/host1x/job.h | |||
@@ -44,6 +44,7 @@ struct host1x_waitchk { | |||
44 | struct host1x_job_unpin_data { | 44 | struct host1x_job_unpin_data { |
45 | struct host1x_bo *bo; | 45 | struct host1x_bo *bo; |
46 | struct sg_table *sgt; | 46 | struct sg_table *sgt; |
47 | size_t size; | ||
47 | }; | 48 | }; |
48 | 49 | ||
49 | /* | 50 | /* |
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index 25c11a85050b..0ac026cdc30c 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c | |||
@@ -484,7 +484,7 @@ unsigned int host1x_syncpt_nb_mlocks(struct host1x *host) | |||
484 | 484 | ||
485 | struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, unsigned int id) | 485 | struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, unsigned int id) |
486 | { | 486 | { |
487 | if (host->info->nb_pts < id) | 487 | if (id >= host->info->nb_pts) |
488 | return NULL; | 488 | return NULL; |
489 | 489 | ||
490 | return host->syncpt + id; | 490 | return host->syncpt + id; |
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index b7268a14184f..e80a4105ac2a 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c | |||
@@ -100,6 +100,34 @@ __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free) | |||
100 | } | 100 | } |
101 | } | 101 | } |
102 | 102 | ||
103 | /* Insert the iova into domain rbtree by holding writer lock */ | ||
104 | static void | ||
105 | iova_insert_rbtree(struct rb_root *root, struct iova *iova, | ||
106 | struct rb_node *start) | ||
107 | { | ||
108 | struct rb_node **new, *parent = NULL; | ||
109 | |||
110 | new = (start) ? &start : &(root->rb_node); | ||
111 | /* Figure out where to put new node */ | ||
112 | while (*new) { | ||
113 | struct iova *this = rb_entry(*new, struct iova, node); | ||
114 | |||
115 | parent = *new; | ||
116 | |||
117 | if (iova->pfn_lo < this->pfn_lo) | ||
118 | new = &((*new)->rb_left); | ||
119 | else if (iova->pfn_lo > this->pfn_lo) | ||
120 | new = &((*new)->rb_right); | ||
121 | else { | ||
122 | WARN_ON(1); /* this should not happen */ | ||
123 | return; | ||
124 | } | ||
125 | } | ||
126 | /* Add new node and rebalance tree. */ | ||
127 | rb_link_node(&iova->node, parent, new); | ||
128 | rb_insert_color(&iova->node, root); | ||
129 | } | ||
130 | |||
103 | /* | 131 | /* |
104 | * Computes the padding size required, to make the start address | 132 | * Computes the padding size required, to make the start address |
105 | * naturally aligned on the power-of-two order of its size | 133 | * naturally aligned on the power-of-two order of its size |
@@ -157,35 +185,8 @@ move_left: | |||
157 | new->pfn_lo = limit_pfn - (size + pad_size) + 1; | 185 | new->pfn_lo = limit_pfn - (size + pad_size) + 1; |
158 | new->pfn_hi = new->pfn_lo + size - 1; | 186 | new->pfn_hi = new->pfn_lo + size - 1; |
159 | 187 | ||
160 | /* Insert the new_iova into domain rbtree by holding writer lock */ | 188 | /* If we have 'prev', it's a valid place to start the insertion. */ |
161 | /* Add new node and rebalance tree. */ | 189 | iova_insert_rbtree(&iovad->rbroot, new, prev); |
162 | { | ||
163 | struct rb_node **entry, *parent = NULL; | ||
164 | |||
165 | /* If we have 'prev', it's a valid place to start the | ||
166 | insertion. Otherwise, start from the root. */ | ||
167 | if (prev) | ||
168 | entry = &prev; | ||
169 | else | ||
170 | entry = &iovad->rbroot.rb_node; | ||
171 | |||
172 | /* Figure out where to put new node */ | ||
173 | while (*entry) { | ||
174 | struct iova *this = rb_entry(*entry, struct iova, node); | ||
175 | parent = *entry; | ||
176 | |||
177 | if (new->pfn_lo < this->pfn_lo) | ||
178 | entry = &((*entry)->rb_left); | ||
179 | else if (new->pfn_lo > this->pfn_lo) | ||
180 | entry = &((*entry)->rb_right); | ||
181 | else | ||
182 | BUG(); /* this should not happen */ | ||
183 | } | ||
184 | |||
185 | /* Add new node and rebalance tree. */ | ||
186 | rb_link_node(&new->node, parent, entry); | ||
187 | rb_insert_color(&new->node, &iovad->rbroot); | ||
188 | } | ||
189 | __cached_rbnode_insert_update(iovad, saved_pfn, new); | 190 | __cached_rbnode_insert_update(iovad, saved_pfn, new); |
190 | 191 | ||
191 | spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); | 192 | spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); |
@@ -194,28 +195,6 @@ move_left: | |||
194 | return 0; | 195 | return 0; |
195 | } | 196 | } |
196 | 197 | ||
197 | static void | ||
198 | iova_insert_rbtree(struct rb_root *root, struct iova *iova) | ||
199 | { | ||
200 | struct rb_node **new = &(root->rb_node), *parent = NULL; | ||
201 | /* Figure out where to put new node */ | ||
202 | while (*new) { | ||
203 | struct iova *this = rb_entry(*new, struct iova, node); | ||
204 | |||
205 | parent = *new; | ||
206 | |||
207 | if (iova->pfn_lo < this->pfn_lo) | ||
208 | new = &((*new)->rb_left); | ||
209 | else if (iova->pfn_lo > this->pfn_lo) | ||
210 | new = &((*new)->rb_right); | ||
211 | else | ||
212 | BUG(); /* this should not happen */ | ||
213 | } | ||
214 | /* Add new node and rebalance tree. */ | ||
215 | rb_link_node(&iova->node, parent, new); | ||
216 | rb_insert_color(&iova->node, root); | ||
217 | } | ||
218 | |||
219 | static struct kmem_cache *iova_cache; | 198 | static struct kmem_cache *iova_cache; |
220 | static unsigned int iova_cache_users; | 199 | static unsigned int iova_cache_users; |
221 | static DEFINE_MUTEX(iova_cache_mutex); | 200 | static DEFINE_MUTEX(iova_cache_mutex); |
@@ -505,7 +484,7 @@ __insert_new_range(struct iova_domain *iovad, | |||
505 | 484 | ||
506 | iova = alloc_and_init_iova(pfn_lo, pfn_hi); | 485 | iova = alloc_and_init_iova(pfn_lo, pfn_hi); |
507 | if (iova) | 486 | if (iova) |
508 | iova_insert_rbtree(&iovad->rbroot, iova); | 487 | iova_insert_rbtree(&iovad->rbroot, iova, NULL); |
509 | 488 | ||
510 | return iova; | 489 | return iova; |
511 | } | 490 | } |
@@ -612,11 +591,11 @@ split_and_remove_iova(struct iova_domain *iovad, struct iova *iova, | |||
612 | rb_erase(&iova->node, &iovad->rbroot); | 591 | rb_erase(&iova->node, &iovad->rbroot); |
613 | 592 | ||
614 | if (prev) { | 593 | if (prev) { |
615 | iova_insert_rbtree(&iovad->rbroot, prev); | 594 | iova_insert_rbtree(&iovad->rbroot, prev, NULL); |
616 | iova->pfn_lo = pfn_lo; | 595 | iova->pfn_lo = pfn_lo; |
617 | } | 596 | } |
618 | if (next) { | 597 | if (next) { |
619 | iova_insert_rbtree(&iovad->rbroot, next); | 598 | iova_insert_rbtree(&iovad->rbroot, next, NULL); |
620 | iova->pfn_hi = pfn_hi; | 599 | iova->pfn_hi = pfn_hi; |
621 | } | 600 | } |
622 | spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); | 601 | spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); |
diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 1ffbf2a8cb99..3d04aa1dc83e 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h | |||
@@ -26,6 +26,7 @@ enum host1x_class { | |||
26 | HOST1X_CLASS_HOST1X = 0x1, | 26 | HOST1X_CLASS_HOST1X = 0x1, |
27 | HOST1X_CLASS_GR2D = 0x51, | 27 | HOST1X_CLASS_GR2D = 0x51, |
28 | HOST1X_CLASS_GR2D_SB = 0x52, | 28 | HOST1X_CLASS_GR2D_SB = 0x52, |
29 | HOST1X_CLASS_VIC = 0x5D, | ||
29 | HOST1X_CLASS_GR3D = 0x60, | 30 | HOST1X_CLASS_GR3D = 0x60, |
30 | }; | 31 | }; |
31 | 32 | ||
diff --git a/include/linux/iova.h b/include/linux/iova.h index f27bb2c62fca..e0a892ae45c0 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h | |||
@@ -82,6 +82,7 @@ static inline unsigned long iova_pfn(struct iova_domain *iovad, dma_addr_t iova) | |||
82 | return iova >> iova_shift(iovad); | 82 | return iova >> iova_shift(iovad); |
83 | } | 83 | } |
84 | 84 | ||
85 | #if IS_ENABLED(CONFIG_IOMMU_IOVA) | ||
85 | int iova_cache_get(void); | 86 | int iova_cache_get(void); |
86 | void iova_cache_put(void); | 87 | void iova_cache_put(void); |
87 | 88 | ||
@@ -106,5 +107,95 @@ void put_iova_domain(struct iova_domain *iovad); | |||
106 | struct iova *split_and_remove_iova(struct iova_domain *iovad, | 107 | struct iova *split_and_remove_iova(struct iova_domain *iovad, |
107 | struct iova *iova, unsigned long pfn_lo, unsigned long pfn_hi); | 108 | struct iova *iova, unsigned long pfn_lo, unsigned long pfn_hi); |
108 | void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad); | 109 | void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad); |
110 | #else | ||
111 | static inline int iova_cache_get(void) | ||
112 | { | ||
113 | return -ENOTSUPP; | ||
114 | } | ||
115 | |||
116 | static inline void iova_cache_put(void) | ||
117 | { | ||
118 | } | ||
119 | |||
120 | static inline struct iova *alloc_iova_mem(void) | ||
121 | { | ||
122 | return NULL; | ||
123 | } | ||
124 | |||
125 | static inline void free_iova_mem(struct iova *iova) | ||
126 | { | ||
127 | } | ||
128 | |||
129 | static inline void free_iova(struct iova_domain *iovad, unsigned long pfn) | ||
130 | { | ||
131 | } | ||
132 | |||
133 | static inline void __free_iova(struct iova_domain *iovad, struct iova *iova) | ||
134 | { | ||
135 | } | ||
136 | |||
137 | static inline struct iova *alloc_iova(struct iova_domain *iovad, | ||
138 | unsigned long size, | ||
139 | unsigned long limit_pfn, | ||
140 | bool size_aligned) | ||
141 | { | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | static inline void free_iova_fast(struct iova_domain *iovad, | ||
146 | unsigned long pfn, | ||
147 | unsigned long size) | ||
148 | { | ||
149 | } | ||
150 | |||
151 | static inline unsigned long alloc_iova_fast(struct iova_domain *iovad, | ||
152 | unsigned long size, | ||
153 | unsigned long limit_pfn) | ||
154 | { | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static inline struct iova *reserve_iova(struct iova_domain *iovad, | ||
159 | unsigned long pfn_lo, | ||
160 | unsigned long pfn_hi) | ||
161 | { | ||
162 | return NULL; | ||
163 | } | ||
164 | |||
165 | static inline void copy_reserved_iova(struct iova_domain *from, | ||
166 | struct iova_domain *to) | ||
167 | { | ||
168 | } | ||
169 | |||
170 | static inline void init_iova_domain(struct iova_domain *iovad, | ||
171 | unsigned long granule, | ||
172 | unsigned long start_pfn, | ||
173 | unsigned long pfn_32bit) | ||
174 | { | ||
175 | } | ||
176 | |||
177 | static inline struct iova *find_iova(struct iova_domain *iovad, | ||
178 | unsigned long pfn) | ||
179 | { | ||
180 | return NULL; | ||
181 | } | ||
182 | |||
183 | static inline void put_iova_domain(struct iova_domain *iovad) | ||
184 | { | ||
185 | } | ||
186 | |||
187 | static inline struct iova *split_and_remove_iova(struct iova_domain *iovad, | ||
188 | struct iova *iova, | ||
189 | unsigned long pfn_lo, | ||
190 | unsigned long pfn_hi) | ||
191 | { | ||
192 | return NULL; | ||
193 | } | ||
194 | |||
195 | static inline void free_cpu_cached_iovas(unsigned int cpu, | ||
196 | struct iova_domain *iovad) | ||
197 | { | ||
198 | } | ||
199 | #endif | ||
109 | 200 | ||
110 | #endif | 201 | #endif |
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 995c8f9c692f..55e301047b3e 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h | |||
@@ -306,6 +306,51 @@ extern "C" { | |||
306 | */ | 306 | */ |
307 | #define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) | 307 | #define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) |
308 | 308 | ||
309 | |||
310 | /* NVIDIA Tegra frame buffer modifiers */ | ||
311 | |||
312 | /* | ||
313 | * Some modifiers take parameters, for example the number of vertical GOBs in | ||
314 | * a block. Reserve the lower 32 bits for parameters | ||
315 | */ | ||
316 | #define __fourcc_mod_tegra_mode_shift 32 | ||
317 | #define fourcc_mod_tegra_code(val, params) \ | ||
318 | fourcc_mod_code(NV, ((((__u64)val) << __fourcc_mod_tegra_mode_shift) | params)) | ||
319 | #define fourcc_mod_tegra_mod(m) \ | ||
320 | (m & ~((1ULL << __fourcc_mod_tegra_mode_shift) - 1)) | ||
321 | #define fourcc_mod_tegra_param(m) \ | ||
322 | (m & ((1ULL << __fourcc_mod_tegra_mode_shift) - 1)) | ||
323 | |||
324 | /* | ||
325 | * Tegra Tiled Layout, used by Tegra 2, 3 and 4. | ||
326 | * | ||
327 | * Pixels are arranged in simple tiles of 16 x 16 bytes. | ||
328 | */ | ||
329 | #define NV_FORMAT_MOD_TEGRA_TILED fourcc_mod_tegra_code(1, 0) | ||
330 | |||
331 | /* | ||
332 | * Tegra 16Bx2 Block Linear layout, used by TK1/TX1 | ||
333 | * | ||
334 | * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked | ||
335 | * vertically by a power of 2 (1 to 32 GOBs) to form a block. | ||
336 | * | ||
337 | * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape. | ||
338 | * | ||
339 | * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically. | ||
340 | * Valid values are: | ||
341 | * | ||
342 | * 0 == ONE_GOB | ||
343 | * 1 == TWO_GOBS | ||
344 | * 2 == FOUR_GOBS | ||
345 | * 3 == EIGHT_GOBS | ||
346 | * 4 == SIXTEEN_GOBS | ||
347 | * 5 == THIRTYTWO_GOBS | ||
348 | * | ||
349 | * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format | ||
350 | * in full detail. | ||
351 | */ | ||
352 | #define NV_FORMAT_MOD_TEGRA_16BX2_BLOCK(v) fourcc_mod_tegra_code(2, v) | ||
353 | |||
309 | #if defined(__cplusplus) | 354 | #if defined(__cplusplus) |
310 | } | 355 | } |
311 | #endif | 356 | #endif |