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 | 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 | / { |
| @@ -6843,6 +6843,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git · 6842 unmodified lines | |||
| 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> |
| @@ -7,6 +7,7 @@ config DRM_TEGRA · 6 unmodified lines | |||
| 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 | ||
| @@ -13,6 +13,8 @@ tegra-drm-y := \ · 12 unmodified lines | |||
| 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 |
| @@ -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 @@ · 9 unmodified lines | |||
| 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) · 95 unmodified lines | |||
| 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) · 2 unmodified lines | |||
| 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) · 13 unmodified lines | |||
| 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: · 41 unmodified lines | |||
| 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) · 18 unmodified lines | |||
| 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) · 7 unmodified lines | |||
| 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: · 172 unmodified lines | |||
| 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, · 74 unmodified lines | |||
| 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, · 8 unmodified lines | |||
| 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, · 3 unmodified lines | |||
| 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, · 3 unmodified lines | |||
| 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, · 2 unmodified lines | |||
| 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, · 4 unmodified lines | |||
| 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 = { · 168 unmodified lines | |||
| 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) · 26 unmodified lines | |||
| 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, · 68 unmodified lines | |||
| 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[] = { · 78 unmodified lines | |||
| 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[] = { · 15 unmodified lines | |||
| 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) |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | |||
| 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 { · 24 unmodified lines | |||
| 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; · 17 unmodified lines | |||
| 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, · 31 unmodified lines | |||
| 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; · 172 unmodified lines | |||
| 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 */ |
| @@ -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 | 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 | } |
| @@ -128,12 +128,14 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) · 127 unmodified lines | |||
| 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) · 7 unmodified lines | |||
| 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) · 3 unmodified lines | |||
| 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; |
| @@ -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 | 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 = { · 6 unmodified lines | |||
| 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) · 197 unmodified lines | |||
| 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, · 14 unmodified lines | |||
| 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 | } |
| @@ -51,9 +51,15 @@ static void host1x_pushbuffer_destroy(struct push_buffer *pb) · 50 unmodified lines | |||
| 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) · 6 unmodified lines | |||
| 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) · 7 unmodified lines | |||
| 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) · 3 unmodified lines | |||
| 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) · 1 unmodified line | |||
| 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 | /* |
| @@ -43,10 +43,12 @@ struct host1x_job; · 42 unmodified lines | |||
| 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 { |
| @@ -16,23 +16,25 @@ · 15 unmodified lines | |||
| 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) · 129 unmodified lines | |||
| 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: · 14 unmodified lines | |||
| 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) · 4 unmodified lines | |||
| 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 | ||
| @@ -17,14 +17,17 @@ · 16 unmodified lines | |||
| 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 { · 76 unmodified lines | |||
| 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; |
| @@ -30,7 +30,7 @@ · 29 unmodified lines | |||
| 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, · 18 unmodified lines | |||
| 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) · 15 unmodified lines | |||
| 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) · 27 unmodified lines | |||
| 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) */ |
| @@ -174,9 +174,10 @@ static int do_waitchks(struct host1x_job *job, struct host1x *host, · 173 unmodified lines | |||
| 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) · 3 unmodified lines | |||
| 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) · 3 unmodified lines | |||
| 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) · 296 unmodified lines | |||
| 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); · 39 unmodified lines | |||
| 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 | } |
| @@ -44,6 +44,7 @@ struct host1x_waitchk { · 43 unmodified lines | |||
| 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 | /* |
| @@ -484,7 +484,7 @@ unsigned int host1x_syncpt_nb_mlocks(struct host1x *host) · 483 unmodified lines | |||
| 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; |
| @@ -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 | * 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: · 51 unmodified lines | |||
| 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: · 2 unmodified lines | |||
| 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, · 283 unmodified lines | |||
| 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, · 100 unmodified lines | |||
| 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); |
| @@ -26,6 +26,7 @@ enum host1x_class { · 25 unmodified lines | |||
| 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 | ||
| @@ -82,6 +82,7 @@ static inline unsigned long iova_pfn(struct iova_domain *iovad, dma_addr_t iova) · 81 unmodified lines | |||
| 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); · 18 unmodified lines | |||
| 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 |
| @@ -306,6 +306,51 @@ extern "C" { · 305 unmodified lines | |||
| 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 |