From patchwork Thu Nov 10 18:23:38 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mikko Perttunen X-Patchwork-Id: 9421951 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id D89A960484 for ; Thu, 10 Nov 2016 18:27:46 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E8A44297D6 for ; Thu, 10 Nov 2016 18:27:46 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DD4C0297F3; Thu, 10 Nov 2016 18:27:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-3.7 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RCVD_IN_SORBS_SPAM autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 5FD83297D6 for ; Thu, 10 Nov 2016 18:27:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 21E0F6E7EC; Thu, 10 Nov 2016 18:27:44 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail.kapsi.fi (mx1.kapsi.fi [IPv6:2001:1bc8:1004::1:25]) by gabe.freedesktop.org (Postfix) with ESMTPS id 01E2C6E809 for ; Thu, 10 Nov 2016 18:27:42 +0000 (UTC) Received: from dsl-espbrasgw1-54f9c1-183.dhcp.inet.fi ([84.249.193.183] helo=toshino.dhcp.inet.fi) by mail.kapsi.fi with esmtpsa (TLS1.2:DHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.80) (envelope-from ) id 1c4u4Y-0001Qc-Cz; Thu, 10 Nov 2016 20:27:38 +0200 From: Mikko Perttunen To: thierry.reding@gmail.com Subject: [PATCH 1/8] drm/tegra: Add Tegra DRM allocation API Date: Thu, 10 Nov 2016 20:23:38 +0200 Message-Id: <20161110182345.31777-2-mperttunen@nvidia.com> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20161110182345.31777-1-mperttunen@nvidia.com> References: <20161110182345.31777-1-mperttunen@nvidia.com> X-SA-Exim-Connect-IP: 84.249.193.183 X-SA-Exim-Mail-From: mperttunen@nvidia.com X-SA-Exim-Scanned: No (on mail.kapsi.fi); SAEximRunCond expanded to false Cc: linux-tegra@vger.kernel.org, dri-devel@lists.freedesktop.org, Mikko Perttunen X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP Add a new IO virtual memory allocation API to allow clients to allocate non-GEM memory in the Tegra DRM IOMMU domain. This is required e.g. for loading client firmware when clients are attached to the IOMMU domain. The allocator allocates contiguous physical pages that are then mapped contiguously to the IOMMU domain using the iova_domain library provided by the kernel. Contiguous physical pages are used so that the same allocator works also when IOMMU support is disabled and therefore devices access physical memory directly. Signed-off-by: Mikko Perttunen --- drivers/gpu/drm/tegra/drm.c | 98 ++++++++++++++++++++++++++++++++++++++++++--- drivers/gpu/drm/tegra/drm.h | 7 ++++ 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index b8be3ee..ecfe7ba 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1,12 +1,13 @@ /* * Copyright (C) 2012 Avionic Design GmbH - * Copyright (C) 2012-2013 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2012-2015 NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include #include #include @@ -23,6 +24,8 @@ #define DRIVER_MINOR 0 #define DRIVER_PATCHLEVEL 0 +#define IOVA_AREA_SZ (1024 * 1024 * 64) /* 64 MiB */ + struct tegra_drm_file { struct list_head contexts; }; @@ -127,7 +130,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (iommu_present(&platform_bus_type)) { struct iommu_domain_geometry *geometry; - u64 start, end; + unsigned long order; + u64 iova_start, start, end; tegra->domain = iommu_domain_alloc(&platform_bus_type); if (!tegra->domain) { @@ -138,10 +142,17 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) geometry = &tegra->domain->geometry; start = geometry->aperture_start; end = geometry->aperture_end; + iova_start = end - IOVA_AREA_SZ + 1; + + order = __ffs(tegra->domain->pgsize_bitmap); + init_iova_domain(&tegra->iova, 1UL << order, + iova_start >> order, + end >> order); - DRM_DEBUG_DRIVER("IOMMU aperture initialized (%#llx-%#llx)\n", - start, end); - drm_mm_init(&tegra->mm, start, end - start + 1); + drm_mm_init(&tegra->mm, start, iova_start - start); + + DRM_DEBUG("IOMMU apertures initialized (GEM: %#llx-%#llx, IOVA: %#llx-%#llx)\n", + start, iova_start-1, iova_start, end); } mutex_init(&tegra->clients_lock); @@ -208,6 +219,7 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (tegra->domain) { iommu_domain_free(tegra->domain); drm_mm_takedown(&tegra->mm); + put_iova_domain(&tegra->iova); } free: kfree(tegra); @@ -232,6 +244,7 @@ static int tegra_drm_unload(struct drm_device *drm) if (tegra->domain) { iommu_domain_free(tegra->domain); drm_mm_takedown(&tegra->mm); + put_iova_domain(&tegra->iova); } kfree(tegra); @@ -975,6 +988,81 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, return 0; } +void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, + dma_addr_t *iova) +{ + struct iova *alloc; + unsigned long shift; + void *virt; + gfp_t gfp; + int err; + + if (tegra->domain) + size = iova_align(&tegra->iova, size); + else + size = PAGE_ALIGN(size); + + gfp = GFP_KERNEL | __GFP_ZERO; + if (!tegra->domain) { + /* + * Tegra210 has 64-bit physical addresses but units only support + * 32-bit addresses, so if we don't have an IOMMU to do + * translation, force allocations to be in the 32-bit range. + */ + gfp |= GFP_DMA; + } + + virt = (void *)__get_free_pages(gfp, get_order(size)); + if (!virt) + return NULL; + + if (!tegra->domain) { + /* + * If IOMMU is disabled, devices address physical memory + * directly. + */ + *iova = virt_to_phys(virt); + return virt; + } + + shift = iova_shift(&tegra->iova); + alloc = alloc_iova(&tegra->iova, size >> shift, + tegra->domain->geometry.aperture_end >> shift, true); + if (!alloc) + goto free_pages; + + *iova = iova_dma_addr(&tegra->iova, alloc); + err = iommu_map(tegra->domain, *iova, virt_to_phys(virt), + size, IOMMU_READ | IOMMU_WRITE); + if (err < 0) + goto free_iova; + + return virt; + +free_iova: + __free_iova(&tegra->iova, alloc); +free_pages: + free_pages((unsigned long)virt, get_order(size)); + + return NULL; +} + +void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt, + dma_addr_t iova) +{ + if (tegra->domain) + size = iova_align(&tegra->iova, size); + else + size = PAGE_ALIGN(size); + + if (tegra->domain) { + iommu_unmap(tegra->domain, iova, size); + free_iova(&tegra->iova, iova_pfn(&tegra->iova, iova)); + } + + free_pages((unsigned long)virt, get_order(size)); +} + static int host1x_drm_probe(struct host1x_device *dev) { struct drm_driver *driver = &tegra_drm_driver; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 0ddcce1..f75b505 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -43,6 +44,8 @@ struct tegra_drm { struct iommu_domain *domain; struct drm_mm mm; + struct iova_domain iova; + struct mutex clients_lock; struct list_head clients; @@ -104,6 +107,10 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm); int tegra_drm_exit(struct tegra_drm *tegra); +void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *iova); +void tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt, + dma_addr_t iova); + struct tegra_dc_soc_info; struct tegra_output;