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
Unified · Split
| @@ -249,6 +249,19 @@ of the following host1x client modules: · 248 unmodified lines | ||
| 249 | 249 | See ../pinctrl/nvidia,tegra124-dpaux-padctl.txt for information |
| 250 | 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 | 265 | Example: |
| 253 | 266 | |
| 254 | 267 | / { |
| @@ -6843,6 +6843,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git · 6842 unmodified lines | ||
| 6843 | 6843 | S: Maintained |
| 6844 | 6844 | F: Documentation/devicetree/bindings/iommu/ |
| 6845 | 6845 | F: drivers/iommu/ |
| 6846 | +F: include/linux/iommu.h | |
| 6847 | +F: include/linux/iova.h | |
| 6846 | 6848 | |
| 6847 | 6849 | IP MASQUERADING |
| 6848 | 6850 | M: Juanjo Ciarlante <jjciarla@raiz.uncu.edu.ar> |
| @@ -7,6 +7,7 @@ config DRM_TEGRA · 6 unmodified lines | ||
| 7 | 7 | select DRM_MIPI_DSI |
| 8 | 8 | select DRM_PANEL |
| 9 | 9 | select TEGRA_HOST1X |
| 10 | + select IOMMU_IOVA if IOMMU_SUPPORT | |
| 10 | 11 | help |
| 11 | 12 | Choose this option if you have an NVIDIA Tegra SoC. |
| 12 | 13 | |
| @@ -13,6 +13,8 @@ tegra-drm-y := \ · 12 unmodified lines | ||
| 13 | 13 | sor.o \ |
| 14 | 14 | dpaux.o \ |
| 15 | 15 | gr2d.o \ |
| 16 | − gr3d.o | |
| 16 | + gr3d.o \ | |
| 17 | + falcon.o \ | |
| 18 | + vic.o | |
| 17 | 19 | |
| 18 | 20 | obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o |
| @@ -1,13 +1,15 @@ | ||
| 1 | 1 | /* |
| 2 | 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 | 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | 6 | * it under the terms of the GNU General Public License version 2 as |
| 7 | 7 | * published by the Free Software Foundation. |
| 8 | 8 | */ |
| 9 | 9 | |
| 10 | +#include <linux/bitops.h> | |
| 10 | 11 | #include <linux/host1x.h> |
| 12 | +#include <linux/idr.h> | |
| 11 | 13 | #include <linux/iommu.h> |
| 12 | 14 | |
| 13 | 15 | #include <drm/drm_atomic.h> |
| @@ -23,8 +25,11 @@ · 9 unmodified lines | ||
| 23 | 25 | #define DRIVER_MINOR 0 |
| 24 | 26 | #define DRIVER_PATCHLEVEL 0 |
| 25 | 27 | |
| 28 | +#define CARVEOUT_SZ SZ_64M | |
| 29 | + | |
| 26 | 30 | struct tegra_drm_file { |
| 27 | − struct list_head contexts; | |
| 31 | + struct idr contexts; | |
| 32 | + struct mutex lock; | |
| 28 | 33 | }; |
| 29 | 34 | |
| 30 | 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) · 95 unmodified lines | ||
| 126 | 131 | return -ENOMEM; |
| 127 | 132 | |
| 128 | 133 | if (iommu_present(&platform_bus_type)) { |
| 134 | + u64 carveout_start, carveout_end, gem_start, gem_end; | |
| 129 | 135 | struct iommu_domain_geometry *geometry; |
| 130 | − u64 start, end; | |
| 136 | + unsigned long order; | |
| 131 | 137 | |
| 132 | 138 | tegra->domain = iommu_domain_alloc(&platform_bus_type); |
| 133 | 139 | if (!tegra->domain) { |
| @@ -136,12 +142,26 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) · 2 unmodified lines | ||
| 136 | 142 | } |
| 137 | 143 | |
| 138 | 144 | geometry = &tegra->domain->geometry; |
| 139 | − start = geometry->aperture_start; | |
| 140 | − end = geometry->aperture_end; | |
| 141 | − | |
| 142 | − DRM_DEBUG_DRIVER("IOMMU aperture initialized (%#llx-%#llx)\n", | |
| 143 | − start, end); | |
| 144 | − drm_mm_init(&tegra->mm, start, end - start + 1); | |
| 145 | + gem_start = geometry->aperture_start; | |
| 146 | + gem_end = geometry->aperture_end - CARVEOUT_SZ; | |
| 147 | + carveout_start = gem_end + 1; | |
| 148 | + carveout_end = geometry->aperture_end; | |
| 149 | + | |
| 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 | 167 | mutex_init(&tegra->clients_lock); |
| @@ -161,6 +181,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) · 13 unmodified lines | ||
| 161 | 181 | drm->mode_config.max_width = 4096; |
| 162 | 182 | drm->mode_config.max_height = 4096; |
| 163 | 183 | |
| 184 | + drm->mode_config.allow_fb_modifiers = true; | |
| 185 | + | |
| 164 | 186 | drm->mode_config.funcs = &tegra_drm_mode_funcs; |
| 165 | 187 | |
| 166 | 188 | err = tegra_drm_fb_prepare(drm); |
| @@ -208,6 +230,8 @@ config: · 41 unmodified lines | ||
| 208 | 230 | if (tegra->domain) { |
| 209 | 231 | iommu_domain_free(tegra->domain); |
| 210 | 232 | drm_mm_takedown(&tegra->mm); |
| 233 | + mutex_destroy(&tegra->mm_lock); | |
| 234 | + put_iova_domain(&tegra->carveout.domain); | |
| 211 | 235 | } |
| 212 | 236 | free: |
| 213 | 237 | kfree(tegra); |
| @@ -232,6 +256,8 @@ static void tegra_drm_unload(struct drm_device *drm) · 18 unmodified lines | ||
| 232 | 256 | if (tegra->domain) { |
| 233 | 257 | iommu_domain_free(tegra->domain); |
| 234 | 258 | drm_mm_takedown(&tegra->mm); |
| 259 | + mutex_destroy(&tegra->mm_lock); | |
| 260 | + put_iova_domain(&tegra->carveout.domain); | |
| 235 | 261 | } |
| 236 | 262 | |
| 237 | 263 | kfree(tegra); |
| @@ -245,7 +271,8 @@ static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) · 7 unmodified lines | ||
| 245 | 271 | if (!fpriv) |
| 246 | 272 | return -ENOMEM; |
| 247 | 273 | |
| 248 | − INIT_LIST_HEAD(&fpriv->contexts); | |
| 274 | + idr_init(&fpriv->contexts); | |
| 275 | + mutex_init(&fpriv->lock); | |
| 249 | 276 | filp->driver_priv = fpriv; |
| 250 | 277 | |
| 251 | 278 | return 0; |
| @@ -424,21 +451,16 @@ fail: · 172 unmodified lines | ||
| 424 | 451 | |
| 425 | 452 | |
| 426 | 453 | #ifdef CONFIG_DRM_TEGRA_STAGING |
| 427 | −static struct tegra_drm_context *tegra_drm_get_context(__u64 context) | |
| 428 | −{ | |
| 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) | |
| 454 | +static struct tegra_drm_context * | |
| 455 | +tegra_drm_file_get_context(struct tegra_drm_file *file, u32 id) | |
| 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) | |
| 438 | − if (ctx == context) | |
| 439 | − return true; | |
| 459 | + mutex_lock(&file->lock); | |
| 460 | + context = idr_find(&file->contexts, id); | |
| 461 | + mutex_unlock(&file->lock); | |
| 440 | 462 | |
| 441 | − return false; | |
| 463 | + return context; | |
| 442 | 464 | } |
| 443 | 465 | |
| 444 | 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, · 74 unmodified lines | ||
| 519 | 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 | 566 | static int tegra_open_channel(struct drm_device *drm, void *data, |
| 523 | 567 | struct drm_file *file) |
| 524 | 568 | { |
| @@ -533,19 +577,22 @@ static int tegra_open_channel(struct drm_device *drm, void *data, · 8 unmodified lines | ||
| 533 | 577 | if (!context) |
| 534 | 578 | return -ENOMEM; |
| 535 | 579 | |
| 580 | + mutex_lock(&fpriv->lock); | |
| 581 | + | |
| 536 | 582 | list_for_each_entry(client, &tegra->clients, list) |
| 537 | 583 | if (client->base.class == args->client) { |
| 538 | − err = client->ops->open_channel(client, context); | |
| 539 | − if (err) | |
| 584 | + err = tegra_client_open(fpriv, client, context); | |
| 585 | + if (err < 0) | |
| 540 | 586 | break; |
| 541 | 587 | |
| 542 | − list_add(&context->list, &fpriv->contexts); | |
| 543 | − args->context = (uintptr_t)context; | |
| 544 | − context->client = client; | |
| 545 | − return 0; | |
| 588 | + args->context = context->id; | |
| 589 | + break; | |
| 546 | 590 | } |
| 547 | 591 | |
| 548 | − kfree(context); | |
| 592 | + if (err < 0) | |
| 593 | + kfree(context); | |
| 594 | + | |
| 595 | + mutex_unlock(&fpriv->lock); | |
| 549 | 596 | return err; |
| 550 | 597 | } |
| 551 | 598 | |
| @@ -555,16 +602,22 @@ static int tegra_close_channel(struct drm_device *drm, void *data, · 3 unmodified lines | ||
| 555 | 602 | struct tegra_drm_file *fpriv = file->driver_priv; |
| 556 | 603 | struct drm_tegra_close_channel *args = data; |
| 557 | 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)) | |
| 562 | − return -EINVAL; | |
| 609 | + context = tegra_drm_file_get_context(fpriv, args->context); | |
| 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 | 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 | 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, · 3 unmodified lines | ||
| 574 | 627 | struct drm_tegra_get_syncpt *args = data; |
| 575 | 628 | struct tegra_drm_context *context; |
| 576 | 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)) | |
| 581 | − return -ENODEV; | |
| 634 | + context = tegra_drm_file_get_context(fpriv, args->context); | |
| 635 | + if (!context) { | |
| 636 | + err = -ENODEV; | |
| 637 | + goto unlock; | |
| 638 | + } | |
| 582 | 639 | |
| 583 | − if (args->index >= context->client->base.num_syncpts) | |
| 584 | − return -EINVAL; | |
| 640 | + if (args->index >= context->client->base.num_syncpts) { | |
| 641 | + err = -EINVAL; | |
| 642 | + goto unlock; | |
| 643 | + } | |
| 585 | 644 | |
| 586 | 645 | syncpt = context->client->base.syncpts[args->index]; |
| 587 | 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 | 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, · 2 unmodified lines | ||
| 595 | 656 | struct tegra_drm_file *fpriv = file->driver_priv; |
| 596 | 657 | struct drm_tegra_submit *args = data; |
| 597 | 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)) | |
| 602 | − return -ENODEV; | |
| 669 | + err = context->client->ops->submit(context, args, drm, file); | |
| 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 | 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, · 4 unmodified lines | ||
| 612 | 681 | struct tegra_drm_context *context; |
| 613 | 682 | struct host1x_syncpt_base *base; |
| 614 | 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)) | |
| 619 | − return -ENODEV; | |
| 688 | + context = tegra_drm_file_get_context(fpriv, args->context); | |
| 689 | + if (!context) { | |
| 690 | + err = -ENODEV; | |
| 691 | + goto unlock; | |
| 692 | + } | |
| 620 | 693 | |
| 621 | − if (args->syncpt >= context->client->base.num_syncpts) | |
| 622 | − return -EINVAL; | |
| 694 | + if (args->syncpt >= context->client->base.num_syncpts) { | |
| 695 | + err = -EINVAL; | |
| 696 | + goto unlock; | |
| 697 | + } | |
| 623 | 698 | |
| 624 | 699 | syncpt = context->client->base.syncpts[args->syncpt]; |
| 625 | 700 | |
| 626 | 701 | base = host1x_syncpt_get_base(syncpt); |
| 627 | − if (!base) | |
| 628 | − return -ENXIO; | |
| 702 | + if (!base) { | |
| 703 | + err = -ENXIO; | |
| 704 | + goto unlock; | |
| 705 | + } | |
| 629 | 706 | |
| 630 | 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 | 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 = { · 168 unmodified lines | ||
| 804 | 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 | 895 | static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) |
| 808 | 896 | { |
| 809 | 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) | |
| 813 | − tegra_drm_context_free(context); | |
| 899 | + mutex_lock(&fpriv->lock); | |
| 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 | 905 | kfree(fpriv); |
| 816 | 906 | } |
| 817 | 907 | |
| @@ -844,7 +934,9 @@ static int tegra_debugfs_iova(struct seq_file *s, void *data) · 26 unmodified lines | ||
| 844 | 934 | struct tegra_drm *tegra = drm->dev_private; |
| 845 | 935 | struct drm_printer p = drm_seq_file_printer(s); |
| 846 | 936 | |
| 937 | + mutex_lock(&tegra->mm_lock); | |
| 847 | 938 | drm_mm_print(&tegra->mm, &p); |
| 939 | + mutex_unlock(&tegra->mm_lock); | |
| 848 | 940 | |
| 849 | 941 | return 0; |
| 850 | 942 | } |
| @@ -919,6 +1011,84 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, · 68 unmodified lines | ||
| 919 | 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 | 1092 | static int host1x_drm_probe(struct host1x_device *dev) |
| 923 | 1093 | { |
| 924 | 1094 | struct drm_driver *driver = &tegra_drm_driver; |
| @@ -1003,11 +1173,13 @@ static const struct of_device_id host1x_drm_subdevs[] = { · 78 unmodified lines | ||
| 1003 | 1173 | { .compatible = "nvidia,tegra124-sor", }, |
| 1004 | 1174 | { .compatible = "nvidia,tegra124-hdmi", }, |
| 1005 | 1175 | { .compatible = "nvidia,tegra124-dsi", }, |
| 1176 | + { .compatible = "nvidia,tegra124-vic", }, | |
| 1006 | 1177 | { .compatible = "nvidia,tegra132-dsi", }, |
| 1007 | 1178 | { .compatible = "nvidia,tegra210-dc", }, |
| 1008 | 1179 | { .compatible = "nvidia,tegra210-dsi", }, |
| 1009 | 1180 | { .compatible = "nvidia,tegra210-sor", }, |
| 1010 | 1181 | { .compatible = "nvidia,tegra210-sor1", }, |
| 1182 | + { .compatible = "nvidia,tegra210-vic", }, | |
| 1011 | 1183 | { /* sentinel */ } |
| 1012 | 1184 | }; |
| 1013 | 1185 | |
| @@ -1029,6 +1201,7 @@ static struct platform_driver * const drivers[] = { · 15 unmodified lines | ||
| 1029 | 1201 | &tegra_sor_driver, |
| 1030 | 1202 | &tegra_gr2d_driver, |
| 1031 | 1203 | &tegra_gr3d_driver, |
| 1204 | + &tegra_vic_driver, | |
| 1032 | 1205 | }; |
| 1033 | 1206 | |
| 1034 | 1207 | static int __init host1x_drm_init(void) |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | |
| 13 | 13 | #include <uapi/drm/tegra_drm.h> |
| 14 | 14 | #include <linux/host1x.h> |
| 15 | +#include <linux/iova.h> | |
| 15 | 16 | #include <linux/of_gpio.h> |
| 16 | 17 | |
| 17 | 18 | #include <drm/drmP.h> |
| @@ -42,8 +43,15 @@ struct tegra_drm { · 24 unmodified lines | ||
| 42 | 43 | struct drm_device *drm; |
| 43 | 44 | |
| 44 | 45 | struct iommu_domain *domain; |
| 46 | + struct mutex mm_lock; | |
| 45 | 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 | 55 | struct mutex clients_lock; |
| 48 | 56 | struct list_head clients; |
| 49 | 57 | |
| @@ -67,7 +75,7 @@ struct tegra_drm_client; · 17 unmodified lines | ||
| 67 | 75 | struct tegra_drm_context { |
| 68 | 76 | struct tegra_drm_client *client; |
| 69 | 77 | struct host1x_channel *channel; |
| 70 | − struct list_head list; | |
| 78 | + unsigned int id; | |
| 71 | 79 | }; |
| 72 | 80 | |
| 73 | 81 | struct tegra_drm_client_ops { |
| @@ -105,6 +113,10 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, · 31 unmodified lines | ||
| 105 | 113 | int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); |
| 106 | 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 | 120 | struct tegra_dc_soc_info; |
| 109 | 121 | struct tegra_output; |
| 110 | 122 | |
| @@ -283,5 +295,6 @@ extern struct platform_driver tegra_dpaux_driver; · 172 unmodified lines | ||
| 283 | 295 | extern struct platform_driver tegra_sor_driver; |
| 284 | 296 | extern struct platform_driver tegra_gr2d_driver; |
| 285 | 297 | extern struct platform_driver tegra_gr3d_driver; |
| 298 | +extern struct platform_driver tegra_vic_driver; | |
| 286 | 299 | |
| 287 | 300 | #endif /* HOST1X_DRM_H */ |
| @@ -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 | +} | |
| @@ -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_ */ | |
| @@ -52,9 +52,26 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, · 51 unmodified lines | ||
| 52 | 52 | struct tegra_bo_tiling *tiling) |
| 53 | 53 | { |
| 54 | 54 | struct tegra_fb *fb = to_tegra_fb(framebuffer); |
| 55 | − | |
| 56 | − /* TODO: handle YUV formats? */ | |
| 57 | − *tiling = fb->planes[0]->tiling; | |
| 55 | + uint64_t modifier = fb->base.modifier; | |
| 56 | + | |
| 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 | 76 | return 0; |
| 60 | 77 | } |
| @@ -128,12 +128,14 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) · 127 unmodified lines | ||
| 128 | 128 | if (!bo->mm) |
| 129 | 129 | return -ENOMEM; |
| 130 | 130 | |
| 131 | + mutex_lock(&tegra->mm_lock); | |
| 132 | + | |
| 131 | 133 | err = drm_mm_insert_node_generic(&tegra->mm, |
| 132 | 134 | bo->mm, bo->gem.size, PAGE_SIZE, 0, 0); |
| 133 | 135 | if (err < 0) { |
| 134 | 136 | dev_err(tegra->drm->dev, "out of I/O virtual memory: %zd\n", |
| 135 | 137 | err); |
| 136 | − goto free; | |
| 138 | + goto unlock; | |
| 137 | 139 | } |
| 138 | 140 | |
| 139 | 141 | bo->paddr = bo->mm->start; |
| @@ -147,11 +149,14 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) · 7 unmodified lines | ||
| 147 | 149 | |
| 148 | 150 | bo->size = err; |
| 149 | 151 | |
| 152 | + mutex_unlock(&tegra->mm_lock); | |
| 153 | + | |
| 150 | 154 | return 0; |
| 151 | 155 | |
| 152 | 156 | remove: |
| 153 | 157 | drm_mm_remove_node(bo->mm); |
| 154 | −free: | |
| 158 | +unlock: | |
| 159 | + mutex_unlock(&tegra->mm_lock); | |
| 155 | 160 | kfree(bo->mm); |
| 156 | 161 | return err; |
| 157 | 162 | } |
| @@ -161,8 +166,11 @@ static int tegra_bo_iommu_unmap(struct tegra_drm *tegra, struct tegra_bo *bo) · 3 unmodified lines | ||
| 161 | 166 | if (!bo->mm) |
| 162 | 167 | return 0; |
| 163 | 168 | |
| 169 | + mutex_lock(&tegra->mm_lock); | |
| 164 | 170 | iommu_unmap(tegra->domain, bo->paddr, bo->size); |
| 165 | 171 | drm_mm_remove_node(bo->mm); |
| 172 | + mutex_unlock(&tegra->mm_lock); | |
| 173 | + | |
| 166 | 174 | kfree(bo->mm); |
| 167 | 175 | |
| 168 | 176 | return 0; |
| @@ -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 | +}; | |
| @@ -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 */ | |
| @@ -267,37 +267,6 @@ static int host1x_device_match(struct device *dev, struct device_driver *drv) · 266 unmodified lines | ||
| 267 | 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 | 270 | static const struct dev_pm_ops host1x_device_pm_ops = { |
| 302 | 271 | .suspend = pm_generic_suspend, |
| 303 | 272 | .resume = pm_generic_resume, |
| @@ -310,9 +279,6 @@ static const struct dev_pm_ops host1x_device_pm_ops = { · 6 unmodified lines | ||
| 310 | 279 | struct bus_type host1x_bus_type = { |
| 311 | 280 | .name = "host1x", |
| 312 | 281 | .match = host1x_device_match, |
| 313 | − .probe = host1x_device_probe, | |
| 314 | − .remove = host1x_device_remove, | |
| 315 | − .shutdown = host1x_device_shutdown, | |
| 316 | 282 | .pm = &host1x_device_pm_ops, |
| 317 | 283 | }; |
| 318 | 284 | |
| @@ -516,6 +482,37 @@ int host1x_unregister(struct host1x *host1x) · 197 unmodified lines | ||
| 516 | 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 | 516 | int host1x_driver_register_full(struct host1x_driver *driver, |
| 520 | 517 | struct module *owner) |
| 521 | 518 | { |
| @@ -536,6 +533,9 @@ int host1x_driver_register_full(struct host1x_driver *driver, · 14 unmodified lines | ||
| 536 | 533 | |
| 537 | 534 | driver->driver.bus = &host1x_bus_type; |
| 538 | 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 | 540 | return driver_register(&driver->driver); |
| 541 | 541 | } |
| @@ -51,9 +51,15 @@ static void host1x_pushbuffer_destroy(struct push_buffer *pb) · 50 unmodified lines | ||
| 51 | 51 | struct host1x_cdma *cdma = pb_to_cdma(pb); |
| 52 | 52 | struct host1x *host1x = cdma_to_host1x(cdma); |
| 53 | 53 | |
| 54 | − if (pb->phys != 0) | |
| 55 | − dma_free_wc(host1x->dev, pb->size_bytes + 4, pb->mapped, | |
| 56 | − pb->phys); | |
| 54 | + if (!pb->phys) | |
| 55 | + return; | |
| 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 | 64 | pb->mapped = NULL; |
| 59 | 65 | pb->phys = 0; |
| @@ -66,28 +72,64 @@ static int host1x_pushbuffer_init(struct push_buffer *pb) · 6 unmodified lines | ||
| 66 | 72 | { |
| 67 | 73 | struct host1x_cdma *cdma = pb_to_cdma(pb); |
| 68 | 74 | struct host1x *host1x = cdma_to_host1x(cdma); |
| 75 | + struct iova *alloc; | |
| 76 | + u32 size; | |
| 77 | + int err; | |
| 69 | 78 | |
| 70 | 79 | pb->mapped = NULL; |
| 71 | 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 | 85 | /* initialize buffer pointers */ |
| 75 | − pb->fence = pb->size_bytes - 8; | |
| 86 | + pb->fence = pb->size - 8; | |
| 76 | 87 | pb->pos = 0; |
| 77 | 88 | |
| 78 | − /* allocate and map pushbuffer memory */ | |
| 79 | − pb->mapped = dma_alloc_wc(host1x->dev, pb->size_bytes + 4, &pb->phys, | |
| 80 | − GFP_KERNEL); | |
| 81 | − if (!pb->mapped) | |
| 82 | − goto fail; | |
| 89 | + if (host1x->domain) { | |
| 90 | + unsigned long shift; | |
| 91 | + | |
| 92 | + size = iova_align(&host1x->iova, size); | |
| 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 | 123 | host1x_hw_pushbuffer_init(host1x, pb); |
| 85 | 124 | |
| 86 | 125 | return 0; |
| 87 | 126 | |
| 88 | −fail: | |
| 89 | − host1x_pushbuffer_destroy(pb); | |
| 90 | − return -ENOMEM; | |
| 127 | +iommu_free_iova: | |
| 128 | + __free_iova(&host1x->iova, alloc); | |
| 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) · 7 unmodified lines | ||
| 101 | 143 | WARN_ON(pb->pos == pb->fence); |
| 102 | 144 | *(p++) = op1; |
| 103 | 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) · 3 unmodified lines | ||
| 111 | 153 | static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots) |
| 112 | 154 | { |
| 113 | 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) · 1 unmodified line | ||
| 119 | 161 | */ |
| 120 | 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 | /* |
| @@ -43,10 +43,12 @@ struct host1x_job; · 42 unmodified lines | ||
| 43 | 43 | |
| 44 | 44 | struct push_buffer { |
| 45 | 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 | 48 | u32 fence; /* index we've written */ |
| 48 | 49 | u32 pos; /* index to write to */ |
| 49 | − u32 size_bytes; | |
| 50 | + u32 size; | |
| 51 | + u32 alloc_size; | |
| 50 | 52 | }; |
| 51 | 53 | |
| 52 | 54 | struct buffer_timeout { |
| @@ -16,23 +16,25 @@ · 15 unmodified lines | ||
| 16 | 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 | 19 | #include <linux/clk.h> |
| 25 | −#include <linux/io.h> | |
| 26 | 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 | 28 | #define CREATE_TRACE_POINTS |
| 29 | 29 | #include <trace/events/host1x.h> |
| 30 | +#undef CREATE_TRACE_POINTS | |
| 30 | 31 | |
| 31 | 32 | #include "bus.h" |
| 32 | −#include "dev.h" | |
| 33 | −#include "intr.h" | |
| 34 | 33 | #include "channel.h" |
| 35 | 34 | #include "debug.h" |
| 35 | +#include "dev.h" | |
| 36 | +#include "intr.h" | |
| 37 | + | |
| 36 | 38 | #include "hw/host1x01.h" |
| 37 | 39 | #include "hw/host1x02.h" |
| 38 | 40 | #include "hw/host1x04.h" |
| @@ -168,22 +170,56 @@ static int host1x_probe(struct platform_device *pdev) · 129 unmodified lines | ||
| 168 | 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 | 201 | err = host1x_channel_list_init(host); |
| 172 | 202 | if (err) { |
| 173 | 203 | dev_err(&pdev->dev, "failed to initialize channel list\n"); |
| 174 | − return err; | |
| 204 | + goto fail_detach_device; | |
| 175 | 205 | } |
| 176 | 206 | |
| 177 | 207 | err = clk_prepare_enable(host->clk); |
| 178 | 208 | if (err < 0) { |
| 179 | 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 | 219 | err = host1x_syncpt_init(host); |
| 184 | 220 | if (err) { |
| 185 | 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 | 225 | err = host1x_intr_init(host, syncpt_irq); |
| @@ -204,8 +240,19 @@ fail_deinit_intr: · 14 unmodified lines | ||
| 204 | 240 | host1x_intr_deinit(host); |
| 205 | 241 | fail_deinit_syncpt: |
| 206 | 242 | host1x_syncpt_deinit(host); |
| 243 | +fail_reset_assert: | |
| 244 | + reset_control_assert(host->rst); | |
| 207 | 245 | fail_unprepare_disable: |
| 208 | 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 | 256 | return err; |
| 210 | 257 | } |
| 211 | 258 | |
| @@ -216,8 +263,15 @@ static int host1x_remove(struct platform_device *pdev) · 4 unmodified lines | ||
| 216 | 263 | host1x_unregister(host); |
| 217 | 264 | host1x_intr_deinit(host); |
| 218 | 265 | host1x_syncpt_deinit(host); |
| 266 | + reset_control_assert(host->rst); | |
| 219 | 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 | 275 | return 0; |
| 222 | 276 | } |
| 223 | 277 | |
| @@ -17,14 +17,17 @@ · 16 unmodified lines | ||
| 17 | 17 | #ifndef HOST1X_DEV_H |
| 18 | 18 | #define HOST1X_DEV_H |
| 19 | 19 | |
| 20 | −#include <linux/platform_device.h> | |
| 21 | 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 | 27 | #include "channel.h" |
| 24 | −#include "syncpt.h" | |
| 25 | 28 | #include "intr.h" |
| 26 | −#include "cdma.h" | |
| 27 | 29 | #include "job.h" |
| 30 | +#include "syncpt.h" | |
| 28 | 31 | |
| 29 | 32 | struct host1x_syncpt; |
| 30 | 33 | struct host1x_syncpt_base; |
| @@ -107,6 +110,11 @@ struct host1x { · 76 unmodified lines | ||
| 107 | 110 | struct host1x_syncpt_base *bases; |
| 108 | 111 | struct device *dev; |
| 109 | 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 | 119 | struct mutex intr_mutex; |
| 112 | 120 | int intr_syncpt_irq; |
| @@ -30,7 +30,7 @@ · 29 unmodified lines | ||
| 30 | 30 | */ |
| 31 | 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, · 18 unmodified lines | ||
| 55 | 55 | *(p++) = HOST1X_OPCODE_NOP; |
| 56 | 56 | *(p++) = HOST1X_OPCODE_NOP; |
| 57 | 57 | dev_dbg(host1x->dev, "%s: NOP at %pad+%#x\n", __func__, |
| 58 | − &pb->phys, getptr); | |
| 59 | − getptr = (getptr + 8) & (pb->size_bytes - 1); | |
| 58 | + &pb->dma, getptr); | |
| 59 | + getptr = (getptr + 8) & (pb->size - 1); | |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | 62 | wmb(); |
| @@ -78,10 +78,9 @@ static void cdma_start(struct host1x_cdma *cdma) · 15 unmodified lines | ||
| 78 | 78 | HOST1X_CHANNEL_DMACTRL); |
| 79 | 79 | |
| 80 | 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 | 82 | host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT); |
| 83 | − host1x_ch_writel(ch, cdma->push_buffer.phys + | |
| 84 | − cdma->push_buffer.size_bytes + 4, | |
| 83 | + host1x_ch_writel(ch, cdma->push_buffer.dma + cdma->push_buffer.size + 4, | |
| 85 | 84 | HOST1X_CHANNEL_DMAEND); |
| 86 | 85 | |
| 87 | 86 | /* reset GET */ |
| @@ -115,9 +114,8 @@ static void cdma_timeout_restart(struct host1x_cdma *cdma, u32 getptr) · 27 unmodified lines | ||
| 115 | 114 | HOST1X_CHANNEL_DMACTRL); |
| 116 | 115 | |
| 117 | 116 | /* set base, end pointer (all of memory) */ |
| 118 | − host1x_ch_writel(ch, cdma->push_buffer.phys, HOST1X_CHANNEL_DMASTART); | |
| 119 | − host1x_ch_writel(ch, cdma->push_buffer.phys + | |
| 120 | − cdma->push_buffer.size_bytes, | |
| 117 | + host1x_ch_writel(ch, cdma->push_buffer.dma, HOST1X_CHANNEL_DMASTART); | |
| 118 | + host1x_ch_writel(ch, cdma->push_buffer.dma + cdma->push_buffer.size, | |
| 121 | 119 | HOST1X_CHANNEL_DMAEND); |
| 122 | 120 | |
| 123 | 121 | /* set GET, by loading the value in PUT (then reset GET) */ |
| @@ -174,9 +174,10 @@ static int do_waitchks(struct host1x_job *job, struct host1x *host, · 173 unmodified lines | ||
| 174 | 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 | 179 | unsigned int i; |
| 180 | + int err; | |
| 180 | 181 | |
| 181 | 182 | job->num_unpins = 0; |
| 182 | 183 | |
| @@ -186,12 +187,16 @@ static unsigned int pin_job(struct host1x_job *job) · 3 unmodified lines | ||
| 186 | 187 | dma_addr_t phys_addr; |
| 187 | 188 | |
| 188 | 189 | reloc->target.bo = host1x_bo_get(reloc->target.bo); |
| 189 | − if (!reloc->target.bo) | |
| 190 | + if (!reloc->target.bo) { | |
| 191 | + err = -EINVAL; | |
| 190 | 192 | goto unpin; |
| 193 | + } | |
| 191 | 194 | |
| 192 | 195 | phys_addr = host1x_bo_pin(reloc->target.bo, &sgt); |
| 193 | − if (!phys_addr) | |
| 196 | + if (!phys_addr) { | |
| 197 | + err = -EINVAL; | |
| 194 | 198 | goto unpin; |
| 199 | + } | |
| 195 | 200 | |
| 196 | 201 | job->addr_phys[job->num_unpins] = phys_addr; |
| 197 | 202 | job->unpins[job->num_unpins].bo = reloc->target.bo; |
| @@ -201,28 +206,67 @@ static unsigned int pin_job(struct host1x_job *job) · 3 unmodified lines | ||
| 201 | 206 | |
| 202 | 207 | for (i = 0; i < job->num_gathers; i++) { |
| 203 | 208 | struct host1x_job_gather *g = &job->gathers[i]; |
| 209 | + size_t gather_size = 0; | |
| 210 | + struct scatterlist *sg; | |
| 204 | 211 | struct sg_table *sgt; |
| 205 | 212 | dma_addr_t phys_addr; |
| 213 | + unsigned long shift; | |
| 214 | + struct iova *alloc; | |
| 215 | + unsigned int j; | |
| 206 | 216 | |
| 207 | 217 | g->bo = host1x_bo_get(g->bo); |
| 208 | − if (!g->bo) | |
| 218 | + if (!g->bo) { | |
| 219 | + err = -EINVAL; | |
| 209 | 220 | goto unpin; |
| 221 | + } | |
| 210 | 222 | |
| 211 | 223 | phys_addr = host1x_bo_pin(g->bo, &sgt); |
| 212 | − if (!phys_addr) | |
| 224 | + if (!phys_addr) { | |
| 225 | + err = -EINVAL; | |
| 213 | 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 | 260 | job->unpins[job->num_unpins].bo = g->bo; |
| 217 | 261 | job->unpins[job->num_unpins].sgt = sgt; |
| 218 | 262 | job->num_unpins++; |
| 219 | 263 | } |
| 220 | 264 | |
| 221 | − return job->num_unpins; | |
| 265 | + return 0; | |
| 222 | 266 | |
| 223 | 267 | unpin: |
| 224 | 268 | host1x_job_unpin(job); |
| 225 | − return 0; | |
| 269 | + return err; | |
| 226 | 270 | } |
| 227 | 271 | |
| 228 | 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) · 296 unmodified lines | ||
| 525 | 569 | host1x_syncpt_load(host->syncpt + i); |
| 526 | 570 | |
| 527 | 571 | /* pin memory */ |
| 528 | − err = pin_job(job); | |
| 529 | − if (!err) | |
| 572 | + err = pin_job(host, job); | |
| 573 | + if (err) | |
| 530 | 574 | goto out; |
| 531 | 575 | |
| 532 | 576 | /* patch gathers */ |
| @@ -572,11 +616,19 @@ EXPORT_SYMBOL(host1x_job_pin); · 39 unmodified lines | ||
| 572 | 616 | |
| 573 | 617 | void host1x_job_unpin(struct host1x_job *job) |
| 574 | 618 | { |
| 619 | + struct host1x *host = dev_get_drvdata(job->channel->dev->parent); | |
| 575 | 620 | unsigned int i; |
| 576 | 621 | |
| 577 | 622 | for (i = 0; i < job->num_unpins; i++) { |
| 578 | 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 | 632 | host1x_bo_unpin(unpin->bo, unpin->sgt); |
| 581 | 633 | host1x_bo_put(unpin->bo); |
| 582 | 634 | } |
| @@ -44,6 +44,7 @@ struct host1x_waitchk { · 43 unmodified lines | ||
| 44 | 44 | struct host1x_job_unpin_data { |
| 45 | 45 | struct host1x_bo *bo; |
| 46 | 46 | struct sg_table *sgt; |
| 47 | + size_t size; | |
| 47 | 48 | }; |
| 48 | 49 | |
| 49 | 50 | /* |
| @@ -484,7 +484,7 @@ unsigned int host1x_syncpt_nb_mlocks(struct host1x *host) · 483 unmodified lines | ||
| 484 | 484 | |
| 485 | 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 | 488 | return NULL; |
| 489 | 489 | |
| 490 | 490 | return host->syncpt + id; |
| @@ -100,6 +100,34 @@ __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free) · 99 unmodified lines | ||
| 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 | 132 | * Computes the padding size required, to make the start address |
| 105 | 133 | * naturally aligned on the power-of-two order of its size |
| @@ -157,35 +185,8 @@ move_left: · 51 unmodified lines | ||
| 157 | 185 | new->pfn_lo = limit_pfn - (size + pad_size) + 1; |
| 158 | 186 | new->pfn_hi = new->pfn_lo + size - 1; |
| 159 | 187 | |
| 160 | − /* Insert the new_iova into domain rbtree by holding writer lock */ | |
| 161 | − /* Add new node and rebalance tree. */ | |
| 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 | − } | |
| 188 | + /* If we have 'prev', it's a valid place to start the insertion. */ | |
| 189 | + iova_insert_rbtree(&iovad->rbroot, new, prev); | |
| 189 | 190 | __cached_rbnode_insert_update(iovad, saved_pfn, new); |
| 190 | 191 | |
| 191 | 192 | spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); |
| @@ -194,28 +195,6 @@ move_left: · 2 unmodified lines | ||
| 194 | 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 | 198 | static struct kmem_cache *iova_cache; |
| 220 | 199 | static unsigned int iova_cache_users; |
| 221 | 200 | static DEFINE_MUTEX(iova_cache_mutex); |
| @@ -505,7 +484,7 @@ __insert_new_range(struct iova_domain *iovad, · 283 unmodified lines | ||
| 505 | 484 | |
| 506 | 485 | iova = alloc_and_init_iova(pfn_lo, pfn_hi); |
| 507 | 486 | if (iova) |
| 508 | − iova_insert_rbtree(&iovad->rbroot, iova); | |
| 487 | + iova_insert_rbtree(&iovad->rbroot, iova, NULL); | |
| 509 | 488 | |
| 510 | 489 | return iova; |
| 511 | 490 | } |
| @@ -612,11 +591,11 @@ split_and_remove_iova(struct iova_domain *iovad, struct iova *iova, · 100 unmodified lines | ||
| 612 | 591 | rb_erase(&iova->node, &iovad->rbroot); |
| 613 | 592 | |
| 614 | 593 | if (prev) { |
| 615 | − iova_insert_rbtree(&iovad->rbroot, prev); | |
| 594 | + iova_insert_rbtree(&iovad->rbroot, prev, NULL); | |
| 616 | 595 | iova->pfn_lo = pfn_lo; |
| 617 | 596 | } |
| 618 | 597 | if (next) { |
| 619 | − iova_insert_rbtree(&iovad->rbroot, next); | |
| 598 | + iova_insert_rbtree(&iovad->rbroot, next, NULL); | |
| 620 | 599 | iova->pfn_hi = pfn_hi; |
| 621 | 600 | } |
| 622 | 601 | spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); |
| @@ -26,6 +26,7 @@ enum host1x_class { · 25 unmodified lines | ||
| 26 | 26 | HOST1X_CLASS_HOST1X = 0x1, |
| 27 | 27 | HOST1X_CLASS_GR2D = 0x51, |
| 28 | 28 | HOST1X_CLASS_GR2D_SB = 0x52, |
| 29 | + HOST1X_CLASS_VIC = 0x5D, | |
| 29 | 30 | HOST1X_CLASS_GR3D = 0x60, |
| 30 | 31 | }; |
| 31 | 32 | |
| @@ -82,6 +82,7 @@ static inline unsigned long iova_pfn(struct iova_domain *iovad, dma_addr_t iova) · 81 unmodified lines | ||
| 82 | 82 | return iova >> iova_shift(iovad); |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | +#if IS_ENABLED(CONFIG_IOMMU_IOVA) | |
| 85 | 86 | int iova_cache_get(void); |
| 86 | 87 | void iova_cache_put(void); |
| 87 | 88 | |
| @@ -106,5 +107,95 @@ void put_iova_domain(struct iova_domain *iovad); · 18 unmodified lines | ||
| 106 | 107 | struct iova *split_and_remove_iova(struct iova_domain *iovad, |
| 107 | 108 | struct iova *iova, unsigned long pfn_lo, unsigned long pfn_hi); |
| 108 | 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 | 201 | #endif |
| @@ -306,6 +306,51 @@ extern "C" { · 305 unmodified lines | ||
| 306 | 306 | */ |
| 307 | 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 | 354 | #if defined(__cplusplus) |
| 310 | 355 | } |
| 311 | 356 | #endif |