From a9a13406ddcb740a72f29506c3194be6e026a7d3 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Mon, 3 Apr 2023 14:06:21 +0000 Subject: [PATCH 01/26] host1x-nvhost: Remove Makefile to prepare integration The host1x-nvhost drivers are available in kernel/nvidia and to integrate the drivers to the kernel/nvidia-oot, remove the dummy Makefile. Bug 4038415 Change-Id: I2179a9e4cc096bf9b8534b3415b595d185785e06 Signed-off-by: Laxman Dewangan --- drivers/gpu/host1x-nvhost/Makefile | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 drivers/gpu/host1x-nvhost/Makefile diff --git a/drivers/gpu/host1x-nvhost/Makefile b/drivers/gpu/host1x-nvhost/Makefile deleted file mode 100644 index 2a76c04d..00000000 --- a/drivers/gpu/host1x-nvhost/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. - -# NOTE: Do not change or add anything in this makefile. -# The source code and makefile rules are copied from the -# kernel/nvidia/drivers/gpu/host1x-nvhost. This file is -# just place-holder for empty makefile to avoid any build -# issue when copy is not done from command line and building -# the tree independent of source copy. - From fd1ad24e14216e421c0814ad9aa76da1a70f8d24 Mon Sep 17 00:00:00 2001 From: Deepak Nibade Date: Fri, 12 May 2017 12:35:40 +0530 Subject: [PATCH 02/26] video: tegra: host: move exported APIs to public header Exported APIs like below are defined in private header file nvhost_syncpt_unit_interface.h nvhost_syncpt_unit_interface_get_aperture() nvhost_syncpt_unit_interface_get_byte_offset() This causes linking problems if these functions are needed by some other driver outside nvhost Hence move these exported APIs into a new public header in include/linux/nvhost_t194.h Change-Id: Ib70177da71c2cc6aee1c6da2668b0a92ea01ccf7 Signed-off-by: Deepak Nibade Reviewed-on: http://git-master/r/1480588 Reviewed-by: Arto Merilainen GVS: Gerrit_Virtual_Submit Reviewed-by: Seshendra Gadagottu Tested-by: Seshendra Gadagottu Reviewed-by: Terje Bergstrom --- include/linux/nvhost_t194.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 include/linux/nvhost_t194.h diff --git a/include/linux/nvhost_t194.h b/include/linux/nvhost_t194.h new file mode 100644 index 00000000..f7b7dbde --- /dev/null +++ b/include/linux/nvhost_t194.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017, 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __LINUX_NVHOST_T194_H__ +#define __LINUX_NVHOST_T194_H__ + +int nvhost_syncpt_unit_interface_get_aperture( + struct platform_device *host_pdev, + phys_addr_t *base, + size_t *size); + +u32 nvhost_syncpt_unit_interface_get_byte_offset(u32 syncpt_id); + +#endif /* __LINUX_NVHOST_T194_H__ */ From a03ddacc0d1c2a899e6ec42ad7170434b16139dd Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 14 Aug 2018 14:17:05 +0300 Subject: [PATCH 03/26] eventlib: unify NVDLA/PVA fence types and add fence recording Bug 2170736 Change-Id: If4fdeda140bf2474a08beb2a0d7c3fc1737a1a66 Signed-off-by: Dmitry Antipov Reviewed-on: https://git-master.nvidia.com/r/1750906 Reviewed-by: Saleh Dindar GVS: Gerrit_Virtual_Submit Reviewed-by: Colin Tracey Reviewed-by: Prashant Gaikwad Reviewed-by: mobile promotions Tested-by: mobile promotions --- include/uapi/linux/nvdev_fence.h | 58 ++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 include/uapi/linux/nvdev_fence.h diff --git a/include/uapi/linux/nvdev_fence.h b/include/uapi/linux/nvdev_fence.h new file mode 100644 index 00000000..37d81be4 --- /dev/null +++ b/include/uapi/linux/nvdev_fence.h @@ -0,0 +1,58 @@ +/* + * include/uapi/linux/nvdev_fence.h + * + * Tegra PVA/DLA fence support + * + * Copyright (c) 2018, 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef LINUX_NVDEV_FENCE_H +#define LINUX_NVDEV_FENCE_H + +#include + +/* used for the recording with keventlib */ +enum nvdev_fence_kind { + NVDEV_FENCE_KIND_PRE = 0, + NVDEV_FENCE_KIND_POST +}; + +/** + * struct nvdev_fence structure for passing fence information + * + * @type: Type of the fence (syncpoint, sync fd or semaphore) + * @syncpoint_index: Syncpoint id + * @syncpoint_value: Value of syncpoint id + * @sync_fd: Linux sync FD handle + * @semaphore_handle: File handle to the semaphore memory buffer + * @semaphore_offset: Offset to the semaphore within the buffer + * @semaphore_value: Value of the semaphore + */ +struct nvdev_fence { + __u32 type; +#define NVDEV_FENCE_TYPE_SYNCPT 0 +#define NVDEV_FENCE_TYPE_SYNC_FD 1 +#define NVDEV_FENCE_TYPE_SEMAPHORE 2 +#define NVDEV_FENCE_TYPE_SEMAPHORE_TS 3 + __u32 syncpoint_index; + __u32 syncpoint_value; + __u32 sync_fd; + __u32 semaphore_handle; + __u32 semaphore_offset; + __u32 semaphore_value; +}; + +#endif /* LINUX_NVDEV_FENCE_H */ From c165791a7b92372d85fa209e414cde8904b94ffc Mon Sep 17 00:00:00 2001 From: Arvind M Date: Thu, 16 May 2019 17:28:22 +0530 Subject: [PATCH 04/26] video: tegra: host: add prefence signal support [1] Adds support for prefence signal support [2] Facilitates handling of SOF feature request from higher levels [3] Update ioctl structure for emu_submit to allow prefence with signal action Jira DLA-1992 Jira DLA-1993 Change-Id: Id0fe8a7a3ff46227121ec5342197b1da2a3f346d Signed-off-by: Arvind M Reviewed-on: https://git-master.nvidia.com/r/2121530 Reviewed-by: Shridhar Rasal Reviewed-by: Bharat Nihalani GVS: Gerrit_Virtual_Submit Reviewed-by: mobile promotions Tested-by: mobile promotions --- include/uapi/linux/nvdev_fence.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/nvdev_fence.h b/include/uapi/linux/nvdev_fence.h index 37d81be4..5877a388 100644 --- a/include/uapi/linux/nvdev_fence.h +++ b/include/uapi/linux/nvdev_fence.h @@ -3,7 +3,7 @@ * * Tegra PVA/DLA fence support * - * Copyright (c) 2018, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2018-2019, 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 as published by @@ -34,6 +34,7 @@ enum nvdev_fence_kind { * struct nvdev_fence structure for passing fence information * * @type: Type of the fence (syncpoint, sync fd or semaphore) + * @type: fence action (wait or signal) * @syncpoint_index: Syncpoint id * @syncpoint_value: Value of syncpoint id * @sync_fd: Linux sync FD handle @@ -47,6 +48,9 @@ struct nvdev_fence { #define NVDEV_FENCE_TYPE_SYNC_FD 1 #define NVDEV_FENCE_TYPE_SEMAPHORE 2 #define NVDEV_FENCE_TYPE_SEMAPHORE_TS 3 + __u32 action; +#define NVDEV_FENCE_WAIT 0 +#define NVDEV_FENCE_SIGNAL 1 __u32 syncpoint_index; __u32 syncpoint_value; __u32 sync_fd; From 8a803afc52be086aaea9a9058f7fbc193ae5f118 Mon Sep 17 00:00:00 2001 From: Sachin Jadhav Date: Mon, 10 Jun 2019 02:53:59 -0700 Subject: [PATCH 05/26] Revert "video: tegra: host: add prefence signal support" This reverts commit 109f8e8c53e5296ea8c782086500c2e78d99cfbd. Change-Id: I4fd6f37e271c52f4a1c1b6870c0b1355118a301d Signed-off-by: Sachin Jadhav Reviewed-on: https://git-master.nvidia.com/r/2133502 --- include/uapi/linux/nvdev_fence.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/include/uapi/linux/nvdev_fence.h b/include/uapi/linux/nvdev_fence.h index 5877a388..37d81be4 100644 --- a/include/uapi/linux/nvdev_fence.h +++ b/include/uapi/linux/nvdev_fence.h @@ -3,7 +3,7 @@ * * Tegra PVA/DLA fence support * - * Copyright (c) 2018-2019, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2018, 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 as published by @@ -34,7 +34,6 @@ enum nvdev_fence_kind { * struct nvdev_fence structure for passing fence information * * @type: Type of the fence (syncpoint, sync fd or semaphore) - * @type: fence action (wait or signal) * @syncpoint_index: Syncpoint id * @syncpoint_value: Value of syncpoint id * @sync_fd: Linux sync FD handle @@ -48,9 +47,6 @@ struct nvdev_fence { #define NVDEV_FENCE_TYPE_SYNC_FD 1 #define NVDEV_FENCE_TYPE_SEMAPHORE 2 #define NVDEV_FENCE_TYPE_SEMAPHORE_TS 3 - __u32 action; -#define NVDEV_FENCE_WAIT 0 -#define NVDEV_FENCE_SIGNAL 1 __u32 syncpoint_index; __u32 syncpoint_value; __u32 sync_fd; From 831cb47c60d9b4a8dbe637729312cf71ef5687eb Mon Sep 17 00:00:00 2001 From: Arvind M Date: Wed, 12 Jun 2019 16:31:24 +0530 Subject: [PATCH 06/26] video: tegra: host: restores sof/eof, TS feature This commit restores the following, [1] 890be46 video: tegra: host: dla: refactor and cleanup code [2] 109f8e8 video: tegra: host: add prefence signal support [3] 6f683c8 video: tegra: host: add sof/eof taskstatus support [4] a9cb0c9 video: tegra: host: add sof/eof timestamp support [5] dcdb0d6 video: tegra: host: increase fw minor version Jira DLA-1992 Jira DLA-1993 Change-Id: I9844b141b72fc553ba7d886e706e340160586129 Signed-off-by: Arvind M Reviewed-on: https://git-master.nvidia.com/r/2135092 Tested-by: Mitch Harwell GVS: Gerrit_Virtual_Submit Reviewed-by: Prashant Gaikwad Reviewed-by: mobile promotions Tested-by: mobile promotions --- include/uapi/linux/nvdev_fence.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/nvdev_fence.h b/include/uapi/linux/nvdev_fence.h index 37d81be4..5877a388 100644 --- a/include/uapi/linux/nvdev_fence.h +++ b/include/uapi/linux/nvdev_fence.h @@ -3,7 +3,7 @@ * * Tegra PVA/DLA fence support * - * Copyright (c) 2018, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2018-2019, 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 as published by @@ -34,6 +34,7 @@ enum nvdev_fence_kind { * struct nvdev_fence structure for passing fence information * * @type: Type of the fence (syncpoint, sync fd or semaphore) + * @type: fence action (wait or signal) * @syncpoint_index: Syncpoint id * @syncpoint_value: Value of syncpoint id * @sync_fd: Linux sync FD handle @@ -47,6 +48,9 @@ struct nvdev_fence { #define NVDEV_FENCE_TYPE_SYNC_FD 1 #define NVDEV_FENCE_TYPE_SEMAPHORE 2 #define NVDEV_FENCE_TYPE_SEMAPHORE_TS 3 + __u32 action; +#define NVDEV_FENCE_WAIT 0 +#define NVDEV_FENCE_SIGNAL 1 __u32 syncpoint_index; __u32 syncpoint_value; __u32 sync_fd; From f16d677563ad2766a89e8651153a0708338cd8d5 Mon Sep 17 00:00:00 2001 From: Arvind M Date: Tue, 22 Jun 2021 17:19:44 +0530 Subject: [PATCH 07/26] nvdla: kmd: add support for stride signal action NVDEV_FENCE_SIGNAL_STRIDE fence action translates to ACTION_INCREMENT_SEM firmware action. Jira DLA-4445 Change-Id: Idb537ea784614d031f66c570359dbc7dd74374af Signed-off-by: Arvind M Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2548309 Reviewed-by: Amit Sharma (SW-TEGRA) Reviewed-by: Anup Mahindre Reviewed-by: svc_kernel_abi Reviewed-by: Bharat Nihalani Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- include/uapi/linux/nvdev_fence.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/nvdev_fence.h b/include/uapi/linux/nvdev_fence.h index 5877a388..bfa8bed7 100644 --- a/include/uapi/linux/nvdev_fence.h +++ b/include/uapi/linux/nvdev_fence.h @@ -3,7 +3,7 @@ * * Tegra PVA/DLA fence support * - * Copyright (c) 2018-2019, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2018-2021, 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 as published by @@ -51,6 +51,7 @@ struct nvdev_fence { __u32 action; #define NVDEV_FENCE_WAIT 0 #define NVDEV_FENCE_SIGNAL 1 +#define NVDEV_FENCE_SIGNAL_STRIDE 2 __u32 syncpoint_index; __u32 syncpoint_value; __u32 sync_fd; From 7343e4379b29f28a4b5b94af21b908a85f50c943 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 10 Jan 2022 15:48:57 +0000 Subject: [PATCH 08/26] gpu: host1x-nvhost: Add host1x-nvhost driver Add a new host1x-nvhost driver that provides an nvhost interface for the upstream Linux host1x driver so that downstream drivers that use this interface can be supported on upstream. Note that some of the nvhost function prototypes are moved from their default header file to the include/linux/nvhost.h header file so simplify building with upstream and downstream kernels. JIRA LS-410 Change-Id: Icdbb34e879dcd91f6e3dd093b7fb400d1be4d561 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2653097 Reviewed-by: Mikko Perttunen Reviewed-by: svc_kernel_abi Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/Makefile | 16 + drivers/gpu/host1x-nvhost/falcon.c | 222 ++++++++ drivers/gpu/host1x-nvhost/falcon.h | 114 ++++ drivers/gpu/host1x-nvhost/nvhost.c | 826 +++++++++++++++++++++++++++++ 4 files changed, 1178 insertions(+) create mode 100644 drivers/gpu/host1x-nvhost/Makefile create mode 100644 drivers/gpu/host1x-nvhost/falcon.c create mode 100644 drivers/gpu/host1x-nvhost/falcon.h create mode 100644 drivers/gpu/host1x-nvhost/nvhost.c diff --git a/drivers/gpu/host1x-nvhost/Makefile b/drivers/gpu/host1x-nvhost/Makefile new file mode 100644 index 00000000..65fbd82d --- /dev/null +++ b/drivers/gpu/host1x-nvhost/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Tegra Host1x-Nvhost Driver. +# +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# + +ccflags-y += -I$(srctree.nvidia)/include +ccflags-y += -I$(srctree.nvidia)/include/uapi/linux +ccflags-y += -I$(srctree.host1x)/include +ccflags-y += -DCONFIG_TEGRA_HOST1X +ccflags-y += -Werror + +host1x-nvhost-objs = nvhost.o falcon.o + +obj-m += host1x-nvhost.o diff --git a/drivers/gpu/host1x-nvhost/falcon.c b/drivers/gpu/host1x-nvhost/falcon.c new file mode 100644 index 00000000..f6901546 --- /dev/null +++ b/drivers/gpu/host1x-nvhost/falcon.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2022, NVIDIA Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include "falcon.h" + +enum falcon_memory { + FALCON_MEMORY_IMEM, + FALCON_MEMORY_DATA, +}; + +static void falcon_writel(struct falcon *falcon, u32 value, u32 offset) +{ + writel(value, falcon->regs + offset); +} + +int falcon_wait_idle(struct falcon *falcon) +{ + u32 value; + + return readl_poll_timeout(falcon->regs + FALCON_IDLESTATE, value, + (value == 0), 10, 100000); +} + +static int falcon_dma_wait_idle(struct falcon *falcon) +{ + u32 value; + + return readl_poll_timeout(falcon->regs + FALCON_DMATRFCMD, value, + (value & FALCON_DMATRFCMD_IDLE), 10, 100000); +} + +static int falcon_copy_chunk(struct falcon *falcon, + phys_addr_t base, + unsigned long offset, + enum falcon_memory target) +{ + u32 cmd = FALCON_DMATRFCMD_SIZE_256B; + + if (target == FALCON_MEMORY_IMEM) + cmd |= FALCON_DMATRFCMD_IMEM; + + falcon_writel(falcon, offset, FALCON_DMATRFMOFFS); + falcon_writel(falcon, base, FALCON_DMATRFFBOFFS); + falcon_writel(falcon, cmd, FALCON_DMATRFCMD); + + return falcon_dma_wait_idle(falcon); +} + +static void falcon_copy_firmware_image(struct falcon *falcon, + const struct firmware *firmware) +{ + u32 *virt = falcon->firmware.virt; + size_t i; + + /* copy the whole thing taking into account endianness */ + for (i = 0; i < firmware->size / sizeof(u32); i++) + virt[i] = le32_to_cpu(((u32 *)firmware->data)[i]); +} + +static int falcon_parse_firmware_image(struct falcon *falcon) +{ + struct falcon_fw_bin_header_v1 *bin = (void *)falcon->firmware.virt; + struct falcon_fw_os_header_v1 *os; + + /* endian problems would show up right here */ + if (bin->magic != PCI_VENDOR_ID_NVIDIA && bin->magic != 0x10fe) { + dev_err(falcon->dev, "incorrect firmware magic\n"); + return -EINVAL; + } + + /* currently only version 1 is supported */ + if (bin->version != 1) { + dev_err(falcon->dev, "unsupported firmware version\n"); + return -EINVAL; + } + + /* check that the firmware size is consistent */ + if (bin->size > falcon->firmware.size) { + dev_err(falcon->dev, "firmware image size inconsistency\n"); + return -EINVAL; + } + + os = falcon->firmware.virt + bin->os_header_offset; + + falcon->firmware.bin_data.size = bin->os_size; + falcon->firmware.bin_data.offset = bin->os_data_offset; + falcon->firmware.code.offset = os->code_offset; + falcon->firmware.code.size = os->code_size; + falcon->firmware.data.offset = os->data_offset; + falcon->firmware.data.size = os->data_size; + + return 0; +} + +int falcon_read_firmware(struct falcon *falcon, const char *name) +{ + int err; + + /* request_firmware prints error if it fails */ + err = request_firmware(&falcon->firmware.firmware, name, falcon->dev); + if (err < 0) + return err; + + falcon->firmware.size = falcon->firmware.firmware->size; + + return 0; +} + +int falcon_load_firmware(struct falcon *falcon) +{ + const struct firmware *firmware = falcon->firmware.firmware; + int err; + + /* copy firmware image into local area. this also ensures endianness */ + falcon_copy_firmware_image(falcon, firmware); + + /* parse the image data */ + err = falcon_parse_firmware_image(falcon); + if (err < 0) { + dev_err(falcon->dev, "failed to parse firmware image\n"); + return err; + } + + release_firmware(firmware); + falcon->firmware.firmware = NULL; + + return 0; +} + +int falcon_init(struct falcon *falcon) +{ + falcon->firmware.virt = NULL; + + return 0; +} + +void falcon_exit(struct falcon *falcon) +{ + if (falcon->firmware.firmware) + release_firmware(falcon->firmware.firmware); +} + +int falcon_boot(struct falcon *falcon) +{ + unsigned long offset; + u32 value; + int err; + + if (!falcon->firmware.virt) + return -EINVAL; + + err = readl_poll_timeout(falcon->regs + FALCON_DMACTL, value, + (value & (FALCON_DMACTL_IMEM_SCRUBBING | + FALCON_DMACTL_DMEM_SCRUBBING)) == 0, + 10, 10000); + if (err < 0) + return err; + + falcon_writel(falcon, 0, FALCON_DMACTL); + + /* setup the address of the binary data so Falcon can access it later */ + falcon_writel(falcon, (falcon->firmware.iova + + falcon->firmware.bin_data.offset) >> 8, + FALCON_DMATRFBASE); + + /* copy the data segment into Falcon internal memory */ + for (offset = 0; offset < falcon->firmware.data.size; offset += 256) + falcon_copy_chunk(falcon, + falcon->firmware.data.offset + offset, + offset, FALCON_MEMORY_DATA); + + /* copy the code segment into Falcon internal memory */ + for (offset = 0; offset < falcon->firmware.code.size; offset += 256) + falcon_copy_chunk(falcon, falcon->firmware.code.offset + offset, + offset, FALCON_MEMORY_IMEM); + + /* setup falcon interrupts */ + falcon_writel(falcon, FALCON_IRQMSET_EXT(0xff) | + FALCON_IRQMSET_SWGEN1 | + FALCON_IRQMSET_SWGEN0 | + FALCON_IRQMSET_EXTERR | + FALCON_IRQMSET_HALT | + FALCON_IRQMSET_WDTMR, + FALCON_IRQMSET); + falcon_writel(falcon, FALCON_IRQDEST_EXT(0xff) | + FALCON_IRQDEST_SWGEN1 | + FALCON_IRQDEST_SWGEN0 | + FALCON_IRQDEST_EXTERR | + FALCON_IRQDEST_HALT, + FALCON_IRQDEST); + + /* enable interface */ + falcon_writel(falcon, FALCON_ITFEN_MTHDEN | + FALCON_ITFEN_CTXEN, + FALCON_ITFEN); + + /* boot falcon */ + falcon_writel(falcon, 0x00000000, FALCON_BOOTVEC); + falcon_writel(falcon, FALCON_CPUCTL_STARTCPU, FALCON_CPUCTL); + + err = falcon_wait_idle(falcon); + if (err < 0) { + dev_err(falcon->dev, "Falcon boot failed due to timeout\n"); + return err; + } + + return 0; +} + +void falcon_execute_method(struct falcon *falcon, u32 method, u32 data) +{ + falcon_writel(falcon, method >> 2, FALCON_UCLASS_METHOD_OFFSET); + falcon_writel(falcon, data, FALCON_UCLASS_METHOD_DATA); +} diff --git a/drivers/gpu/host1x-nvhost/falcon.h b/drivers/gpu/host1x-nvhost/falcon.h new file mode 100644 index 00000000..3c4a039a --- /dev/null +++ b/drivers/gpu/host1x-nvhost/falcon.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2015-2022, NVIDIA Corporation. All rights reserved. + */ + +#ifndef _FALCON_H_ +#define _FALCON_H_ + +#include + +#define FALCON_UCLASS_METHOD_OFFSET 0x00000040 + +#define FALCON_UCLASS_METHOD_DATA 0x00000044 + +#define FALCON_IRQMSET 0x00001010 +#define FALCON_IRQMSET_WDTMR (1 << 1) +#define FALCON_IRQMSET_HALT (1 << 4) +#define FALCON_IRQMSET_EXTERR (1 << 5) +#define FALCON_IRQMSET_SWGEN0 (1 << 6) +#define FALCON_IRQMSET_SWGEN1 (1 << 7) +#define FALCON_IRQMSET_EXT(v) (((v) & 0xff) << 8) + +#define FALCON_IRQDEST 0x0000101c +#define FALCON_IRQDEST_HALT (1 << 4) +#define FALCON_IRQDEST_EXTERR (1 << 5) +#define FALCON_IRQDEST_SWGEN0 (1 << 6) +#define FALCON_IRQDEST_SWGEN1 (1 << 7) +#define FALCON_IRQDEST_EXT(v) (((v) & 0xff) << 8) + +#define FALCON_ITFEN 0x00001048 +#define FALCON_ITFEN_CTXEN (1 << 0) +#define FALCON_ITFEN_MTHDEN (1 << 1) + +#define FALCON_IDLESTATE 0x0000104c + +#define FALCON_CPUCTL 0x00001100 +#define FALCON_CPUCTL_STARTCPU (1 << 1) + +#define FALCON_BOOTVEC 0x00001104 + +#define FALCON_DMACTL 0x0000110c +#define FALCON_DMACTL_DMEM_SCRUBBING (1 << 1) +#define FALCON_DMACTL_IMEM_SCRUBBING (1 << 2) + +#define FALCON_DMATRFBASE 0x00001110 + +#define FALCON_DMATRFMOFFS 0x00001114 + +#define FALCON_DMATRFCMD 0x00001118 +#define FALCON_DMATRFCMD_IDLE (1 << 1) +#define FALCON_DMATRFCMD_IMEM (1 << 4) +#define FALCON_DMATRFCMD_SIZE_256B (6 << 8) + +#define FALCON_DMATRFFBOFFS 0x0000111c + +struct falcon_fw_bin_header_v1 { + u32 magic; /* 0x10de */ + u32 version; /* version of bin format (1) */ + u32 size; /* entire image size including this header */ + u32 os_header_offset; + u32 os_data_offset; + u32 os_size; +}; + +struct falcon_fw_os_app_v1 { + u32 offset; + u32 size; +}; + +struct falcon_fw_os_header_v1 { + u32 code_offset; + u32 code_size; + u32 data_offset; + u32 data_size; +}; + +struct falcon_firmware_section { + unsigned long offset; + size_t size; +}; + +struct falcon_firmware { + /* Firmware after it is read but not loaded */ + const struct firmware *firmware; + + /* Raw firmware data */ + dma_addr_t iova; + dma_addr_t phys; + void *virt; + size_t size; + + /* Parsed firmware information */ + struct falcon_firmware_section bin_data; + struct falcon_firmware_section data; + struct falcon_firmware_section code; +}; + +struct falcon { + /* Set by falcon client */ + struct device *dev; + void __iomem *regs; + + struct falcon_firmware firmware; +}; + +int falcon_init(struct falcon *falcon); +void falcon_exit(struct falcon *falcon); +int falcon_read_firmware(struct falcon *falcon, const char *firmware_name); +int falcon_load_firmware(struct falcon *falcon); +int falcon_boot(struct falcon *falcon); +void falcon_execute_method(struct falcon *falcon, u32 method, u32 data); +int falcon_wait_idle(struct falcon *falcon); + +#endif /* _FALCON_H_ */ diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c new file mode 100644 index 00000000..cd5f6436 --- /dev/null +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -0,0 +1,826 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, NVIDIA Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "falcon.h" + +#define TEGRA194_SYNCPT_PAGE_SIZE 0x1000 +#define TEGRA194_SYNCPT_SHIM_BASE 0x60000000 +#define TEGRA194_SYNCPT_SHIM_SIZE 0x00400000 + +#define THI_STREAMID0 0x00000030 +#define THI_STREAMID1 0x00000034 + +#define NVHOST_NUM_CDEV 1 + +struct nvhost_syncpt_interface { + dma_addr_t base; + uint32_t page_size; +}; + +u32 host1x_readl(struct platform_device *pdev, u32 r) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + void __iomem *addr = pdata->aperture[0] + r; + + return readl(addr); +} +EXPORT_SYMBOL(host1x_readl); + +void host1x_writel(struct platform_device *pdev, u32 r, u32 v) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + void __iomem *addr = pdata->aperture[0] + r; + + writel(v, addr); +} +EXPORT_SYMBOL(host1x_writel); + +static const struct of_device_id host1x_match[] = { + { .compatible = "nvidia,tegra194-host1x", }, + {}, +}; + +static int nvhost_get_host1x_dev(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct platform_device *host1x_pdev; + struct device_node *np; + + np = of_find_matching_node(NULL, host1x_match); + if (!np) { + dev_err(&pdev->dev, "Failed to find host1x!\n"); + return -ENODEV; + } + + host1x_pdev = of_find_device_by_node(np); + if (!host1x_pdev) { + dev_dbg(&pdev->dev, "host1x device not available\n"); + return -EPROBE_DEFER; + } + + pdata->host1x = platform_get_drvdata(host1x_pdev); + if (!pdata->host1x) { + dev_warn(&pdev->dev, "No platform data for host1x!\n"); + return -ENODEV; + } + + return 0; +} + +static struct device *nvhost_client_device_create(struct platform_device *pdev, + struct cdev *cdev, + const char *cdev_name, + dev_t devno, + const struct file_operations *ops) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct device *dev; + int err; + + pdata->nvhost_class = class_create(THIS_MODULE, pdev->dev.of_node->name); + if (IS_ERR(pdata->nvhost_class)) { + dev_err(&pdev->dev, "failed to create class\n"); + return ERR_CAST(pdata->nvhost_class); + } + + cdev_init(cdev, ops); + cdev->owner = THIS_MODULE; + + err = cdev_add(cdev, devno, 1); + if (err < 0) { + dev_err(&pdev->dev, "failed to add cdev\n"); + return ERR_PTR(err); + } + + dev = device_create(pdata->nvhost_class, &pdev->dev, devno, NULL, + (pdev->id <= 0) ? "nvhost-%s%s" : "nvhost-%s%s.%d", + cdev_name, pdev->dev.of_node->name, pdev->id); + + if (IS_ERR(dev)) { + dev_err(&pdev->dev, "failed to create %s device\n", cdev_name); + cdev_del(cdev); + } + + return dev; +} + +int nvhost_client_device_get_resources(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct resource *res; + int err; + + err = nvhost_get_host1x_dev(pdev); + if (err) + return err; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + pdata->aperture[0] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pdata->aperture[0])) + return PTR_ERR(pdata->aperture); + + return 0; +} +EXPORT_SYMBOL(nvhost_client_device_get_resources); + +int nvhost_client_device_init(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + dev_t devno; + int err; + + err = alloc_chrdev_region(&devno, 0, NVHOST_NUM_CDEV, "nvhost"); + if (err < 0) { + dev_err(&pdev->dev, "failed to reserve chrdev region\n"); + return err; + } + + pdata->ctrl_node = nvhost_client_device_create(pdev, &pdata->ctrl_cdev, + "ctrl-", devno, + pdata->ctrl_ops); + if (IS_ERR(pdata->ctrl_node)) { + err = PTR_ERR(pdata->ctrl_node); + goto destroy; + } + + pdata->cdev_region = devno; + + return 0; + +destroy: + device_destroy(pdata->nvhost_class, pdata->ctrl_cdev.dev); + + return err; +} +EXPORT_SYMBOL(nvhost_client_device_init); + +int nvhost_client_device_release(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + + if (!IS_ERR_OR_NULL(pdata->ctrl_node)) { + device_destroy(pdata->nvhost_class, pdata->ctrl_cdev.dev); + cdev_del(&pdata->ctrl_cdev); + } + + unregister_chrdev_region(pdata->cdev_region, NVHOST_NUM_CDEV); + + return 0; +} +EXPORT_SYMBOL(nvhost_client_device_release); + +u32 nvhost_get_syncpt_host_managed(struct platform_device *pdev, + u32 param, const char *syncpt_name) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + + sp = host1x_syncpt_alloc(pdata->host1x, 0, syncpt_name ? syncpt_name : + dev_name(&pdev->dev)); + if (!sp) + return 0; + + return host1x_syncpt_id(sp); +} +EXPORT_SYMBOL(nvhost_get_syncpt_host_managed); + +struct host1x_syncpt *nvhost_syncpt_get_by_id(struct platform_device *pdev, + u32 id) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + + return host1x_syncpt_get_by_id(pdata->host1x, id); +} +EXPORT_SYMBOL(nvhost_syncpt_get_by_id); + +void nvhost_syncpt_put_ref_ext(struct platform_device *pdev, u32 id) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + + sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id); + if (WARN_ON(!sp)) + return; + + host1x_syncpt_put(sp); +} +EXPORT_SYMBOL(nvhost_syncpt_put_ref_ext); + +bool nvhost_syncpt_is_valid_pt_ext(struct platform_device *pdev, u32 id) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + + if (!pdata || pdata->host1x) + return -ENODEV; + + sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id); + + return sp ? true : false; +} +EXPORT_SYMBOL(nvhost_syncpt_is_valid_pt_ext); + +int nvhost_syncpt_is_expired_ext(struct platform_device *pdev, u32 id, + u32 thresh) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + + sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id); + if (WARN_ON(!sp)) + return true; + + if (host1x_syncpt_wait(sp, thresh, 0, NULL)) + return false; + + return true; +} +EXPORT_SYMBOL(nvhost_syncpt_is_expired_ext); + +void nvhost_syncpt_set_minval(struct platform_device *pdev, u32 id, u32 val) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + u32 cur; + + sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id); + if (WARN_ON(!sp)) + return; + + cur = host1x_syncpt_read(sp); + + while (cur++ != val) + host1x_syncpt_incr(sp); +} +EXPORT_SYMBOL(nvhost_syncpt_set_minval); + +void nvhost_syncpt_set_min_update(struct platform_device *pdev, u32 id, u32 val) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + u32 cur; + + sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id); + if (WARN_ON(!sp)) + return; + + cur = host1x_syncpt_read(sp); + + while (cur++ != val) + host1x_syncpt_incr(sp); + + host1x_syncpt_read(sp); +} +EXPORT_SYMBOL(nvhost_syncpt_set_min_update); + +u32 nvhost_syncpt_read_maxval(struct platform_device *pdev, u32 id) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + + sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id); + if (WARN_ON(!sp)) + return 0; + + return host1x_syncpt_read_max(sp); +} +EXPORT_SYMBOL(nvhost_syncpt_read_maxval); + +u32 nvhost_syncpt_incr_max_ext(struct platform_device *pdev, u32 id, u32 incrs) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + + sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id); + if (WARN_ON(!sp)) + return 0; + + return host1x_syncpt_incr_max(sp, incrs); +} +EXPORT_SYMBOL(nvhost_syncpt_incr_max_ext); + +static int nvhost_syncpt_get_aperture(struct device_node *np, u64 *base, + size_t *size) +{ + if (of_device_is_compatible(np, "nvidia,tegra194-host1x")) { + *base = TEGRA194_SYNCPT_SHIM_BASE; + *size = TEGRA194_SYNCPT_SHIM_SIZE; + return 0; + } + + return -ENODEV; +} + +static int nvhost_syncpt_get_page_size(struct device_node *np, uint32_t *size) +{ + if (of_device_is_compatible(np, "nvidia,tegra194-host1x")) { + *size = TEGRA194_SYNCPT_PAGE_SIZE; + return 0; + } + + return -ENODEV; +} + +int nvhost_syncpt_unit_interface_init(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct nvhost_syncpt_interface *syncpt_if; + size_t size; + u64 base; + int err; + + syncpt_if = devm_kzalloc(&pdev->dev, sizeof(*syncpt_if), GFP_KERNEL); + if (!syncpt_if) + return -ENOMEM; + + err = nvhost_syncpt_get_aperture(pdev->dev.parent->of_node, &base, + &size); + if (err < 0) { + dev_err(&pdev->dev, "failed to get syncpt aperture\n"); + return err; + } + + err = nvhost_syncpt_get_page_size(pdev->dev.parent->of_node, + &syncpt_if->page_size); + if (err < 0) { + dev_err(&pdev->dev, "failed to get syncpt page size\n"); + return err; + } + + /* If IOMMU is enabled, map it into the device memory */ + if (iommu_get_domain_for_dev(&pdev->dev)) { + struct scatterlist sg; + + sg_init_table(&sg, 1); + sg_set_page(&sg, phys_to_page(base), size, 0); + + err = dma_map_sg_attrs(&pdev->dev, &sg, 1, + DMA_BIDIRECTIONAL, + DMA_ATTR_SKIP_CPU_SYNC); + if (err == 0) { + err = -ENOMEM; + return err; + } + + syncpt_if->base = sg_dma_address(&sg); + } else { + syncpt_if->base = base; + } + + pdata->syncpt_unit_interface = syncpt_if; + + dev_info(&pdev->dev, + "syncpt_unit_base %llx syncpt_unit_size %zx size %x\n", + base, size, syncpt_if->page_size); + + return 0; +} +EXPORT_SYMBOL(nvhost_syncpt_unit_interface_init); + +dma_addr_t nvhost_syncpt_address(struct platform_device *pdev, u32 id) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct nvhost_syncpt_interface *syncpt_if = pdata->syncpt_unit_interface; + + return syncpt_if->base + syncpt_if->page_size * id; +} +EXPORT_SYMBOL(nvhost_syncpt_address); + +static irqreturn_t flcn_isr(int irq, void *dev_id) +{ + struct platform_device *pdev = (struct platform_device *)(dev_id); + struct nvhost_device_data *pdata = nvhost_get_devdata(pdev); + + if (pdata->flcn_isr) + pdata->flcn_isr(pdev); + + return IRQ_HANDLED; +} + +int flcn_intr_init(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = nvhost_get_devdata(pdev); + int ret = 0; + + pdata->irq = platform_get_irq(pdev, 0); + if (pdata->irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ\n"); + return -ENXIO; + } + + spin_lock_init(&pdata->mirq_lock); + ret = request_irq(pdata->irq, flcn_isr, 0, dev_name(&pdev->dev), pdev); + if (ret) { + dev_err(&pdev->dev, "failed to request irq. err %d\n", ret); + return ret; + } + + /* keep irq disabled */ + disable_irq(pdata->irq); + + return 0; +} +EXPORT_SYMBOL(flcn_intr_init); + +int flcn_reload_fw(struct platform_device *pdev) +{ + /* TODO: Used by debugfs */ + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(flcn_reload_fw); + +int nvhost_flcn_prepare_poweroff(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + + if (pdata->flcn_isr) + disable_irq(pdata->irq); + + return 0; +} +EXPORT_SYMBOL(nvhost_flcn_prepare_poweroff); + +static int nvhost_flcn_load_firmware(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct falcon *falcon = pdata->falcon_data; + dma_addr_t iova; + size_t size; + void *virt; + int err; + + if (falcon->firmware.virt) + return 0; + + err = falcon_read_firmware(falcon, pdata->firmware_name); + if (err < 0) + return err; + + size = falcon->firmware.size; + virt = dma_alloc_coherent(&pdev->dev, size, &iova, GFP_KERNEL); + if (!virt) + return -ENOMEM; + + falcon->firmware.virt = virt; + falcon->firmware.iova = iova; + + err = falcon_load_firmware(falcon); + if (err < 0) + goto cleanup; + + return 0; + +cleanup: + dma_free_coherent(&pdev->dev, size, virt, iova); + + return err; +} + +int nvhost_flcn_finalize_poweron(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct falcon *falcon = pdata->falcon_data; +#ifdef CONFIG_IOMMU_API + struct iommu_fwspec *spec = dev_iommu_fwspec_get(&pdev->dev); +#endif + int err; + u32 value; + +#ifdef CONFIG_IOMMU_API + if (spec) { + host1x_writel(pdev, pdata->transcfg_addr, pdata->transcfg_val); + + if (spec->num_ids > 0) { + value = spec->ids[0] & 0xffff; + host1x_writel(pdev, THI_STREAMID0, value); + host1x_writel(pdev, THI_STREAMID1, value); + } + } +#endif + + err = falcon_boot(falcon); + if (err < 0) + return err; + + err = falcon_wait_idle(falcon); + if (err < 0) { + dev_err(&pdev->dev, "falcon boot timed out\n"); + return err; + } + + return 0; +} +EXPORT_SYMBOL(nvhost_flcn_finalize_poweron); + +struct nvhost_host1x_cb { + struct dma_fence_cb cb; + struct work_struct work; + void (*notifier)(void *data, int unused); + void *notifier_data; +}; + +static void nvhost_host1x_cb_func(struct dma_fence *f, struct dma_fence_cb *cb) +{ + struct nvhost_host1x_cb *host1x_cb; + + host1x_cb = container_of(cb, struct nvhost_host1x_cb, cb); + schedule_work(&host1x_cb->work); + dma_fence_put(f); +} + +static void nvhost_intr_do_work(struct work_struct *work) +{ + struct nvhost_host1x_cb *host1x_cb; + + host1x_cb = container_of(work, struct nvhost_host1x_cb, work); + host1x_cb->notifier(host1x_cb->notifier_data, 0); + kfree(host1x_cb); +} + +int nvhost_intr_register_notifier(struct platform_device *pdev, + u32 id, u32 thresh, + void (*callback)(void *data, int unused), + void *private_data) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct dma_fence *fence; + struct nvhost_host1x_cb *cb; + struct host1x_syncpt *sp; + int err; + + sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id); + if (!sp) + return -EINVAL; + + fence = host1x_fence_create(sp, thresh); + if (IS_ERR(fence)) { + pr_err("error %d during construction of fence!", + (int)PTR_ERR(fence)); + return PTR_ERR(fence); + } + + cb = kzalloc(sizeof(*cb), GFP_KERNEL); + if (!cb) { + dma_fence_put(fence); + return -ENOMEM; + } + + INIT_WORK(&cb->work, nvhost_intr_do_work); + cb->notifier = callback; + cb->notifier_data = private_data; + + err = dma_fence_add_callback(fence, &cb->cb, nvhost_host1x_cb_func); + if (err < 0) { + dma_fence_put(fence); + kfree(cb); + } + + return err; +} +EXPORT_SYMBOL(nvhost_intr_register_notifier); + +void nvhost_module_deinit(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct falcon *falcon = pdata->falcon_data; + + pm_runtime_disable(&pdev->dev); + dma_free_coherent(&pdev->dev, falcon->firmware.size, + falcon->firmware.virt, falcon->firmware.iova); + falcon_exit(falcon); + debugfs_remove(pdata->debugfs); +} +EXPORT_SYMBOL(nvhost_module_deinit); + +int nvhost_module_init(struct platform_device *pdev) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct falcon *falcon; + unsigned int i; + int err; + + falcon = devm_kzalloc(&pdev->dev, sizeof(*falcon), GFP_KERNEL); + if (!falcon) + return -ENOMEM; + + falcon->dev = &pdev->dev; + falcon->regs = pdata->aperture[0]; + + err = devm_clk_bulk_get_all(&pdev->dev, &pdata->clks); + if (err < 0) { + dev_err(&pdev->dev, "failed to get clocks %d\n", err); + return err; + } + pdata->num_clks = err; + + for (i = 0; i < pdata->num_clks; i++) { + err = clk_set_rate(pdata->clks[0].clk, ULONG_MAX); + if (err < 0) { + dev_err(&pdev->dev, "failed to set clock rate!\n"); + return err; + } + } + + pdata->reset_control = devm_reset_control_get_exclusive_released( + &pdev->dev, NULL); + if (IS_ERR(pdata->reset_control)) { + dev_err(&pdev->dev, "failed to get reset\n"); + return PTR_ERR(pdata->reset_control); + } + + reset_control_acquire(pdata->reset_control); + if (err < 0) { + dev_err(&pdev->dev, "failed to acquire reset: %d\n", err); + return err; + } + + err = clk_bulk_prepare_enable(pdata->num_clks, pdata->clks); + if (err < 0) { + reset_control_release(pdata->reset_control); + dev_err(&pdev->dev, "failed to enabled clocks: %d\n", err); + return err; + } + + reset_control_reset(pdata->reset_control); + clk_bulk_disable_unprepare(pdata->num_clks, pdata->clks); + reset_control_release(pdata->reset_control); + + if (pdata->autosuspend_delay) { + pm_runtime_set_autosuspend_delay(&pdev->dev, + pdata->autosuspend_delay); + pm_runtime_use_autosuspend(&pdev->dev); + } + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) + return -EOPNOTSUPP; + + pdata->debugfs = debugfs_create_dir(pdev->dev.of_node->name, + NULL); + + falcon_init(falcon); + + pdata->falcon_data = falcon; + + return nvhost_flcn_load_firmware(pdev); +} +EXPORT_SYMBOL(nvhost_module_init); + +static void nvhost_module_load_regs(struct platform_device *pdev, bool prod) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct nvhost_gating_register *regs = pdata->engine_cg_regs; + + if (!regs) + return; + + while (regs->addr) { + if (prod) + host1x_writel(pdev, regs->addr, regs->prod); + else + host1x_writel(pdev, regs->addr, regs->disable); + regs++; + } +} + +void nvhost_module_reset(struct platform_device *pdev, bool reboot) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + int err; + + if (reboot) + if (pdata->prepare_poweroff) + pdata->prepare_poweroff(pdev); + + mutex_lock(&pdata->lock); + err = reset_control_acquire(pdata->reset_control); + if (err < 0) { + dev_err(&pdev->dev, "failed to acquire reset: %d\n", err); + } else { + reset_control_reset(pdata->reset_control); + reset_control_release(pdata->reset_control); + } + mutex_unlock(&pdata->lock); + + if (reboot) { + /* Load clockgating registers */ + nvhost_module_load_regs(pdev, pdata->engine_can_cg); + + /* ..and execute engine specific operations (i.e. boot) */ + if (pdata->finalize_poweron) + pdata->finalize_poweron(pdev); + } +} +EXPORT_SYMBOL(nvhost_module_reset); + +int nvhost_module_busy(struct platform_device *dev) +{ + int err; + + err = pm_runtime_get_sync(&dev->dev); + if (err < 0) { + pm_runtime_put_noidle(&dev->dev); + return err; + } + + return 0; +} +EXPORT_SYMBOL(nvhost_module_busy); + +void nvhost_module_idle_mult(struct platform_device *pdev, int refs) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + + while (refs--) { + pm_runtime_mark_last_busy(&pdev->dev); + if (pdata->autosuspend_delay) + pm_runtime_put_autosuspend(&pdev->dev); + else + pm_runtime_put(&pdev->dev); + } +} +EXPORT_SYMBOL(nvhost_module_idle_mult); + +inline void nvhost_module_idle(struct platform_device *pdev) +{ + nvhost_module_idle_mult(pdev, 1); +} +EXPORT_SYMBOL(nvhost_module_idle); + +int nvhost_module_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct nvhost_device_data *pdata = dev_get_drvdata(dev); + int err; + + err = clk_bulk_prepare_enable(pdata->num_clks, pdata->clks); + if (err < 0) { + dev_err(&pdev->dev, "failed to enabled clocks: %d\n", err); + return err; + } + + if (pdata->poweron_reset) + nvhost_module_reset(pdev, false); + + /* Load clockgating registers */ + nvhost_module_load_regs(pdev, pdata->engine_can_cg); + + if (pdata->flcn_isr) + enable_irq(pdata->irq); + + if (pdata->finalize_poweron) + err = pdata->finalize_poweron(pdev); + + return err; +} +EXPORT_SYMBOL(nvhost_module_runtime_resume); + +int nvhost_module_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct nvhost_device_data *pdata = dev_get_drvdata(dev); + int err; + + if (pdata->prepare_poweroff) { + err = pdata->prepare_poweroff(pdev); + if (err) + return err; + } + + clk_bulk_disable_unprepare(pdata->num_clks, pdata->clks); + + return 0; +} +EXPORT_SYMBOL(nvhost_module_runtime_suspend); + +const struct dev_pm_ops nvhost_module_pm_ops = { + SET_RUNTIME_PM_OPS(nvhost_module_runtime_suspend, + nvhost_module_runtime_resume, NULL) +}; +EXPORT_SYMBOL(nvhost_module_pm_ops); + +static struct platform_driver nvhost_driver = { + .driver = { + .name = "host1x-nvhost", + }, +}; + +module_platform_driver(nvhost_driver); +MODULE_LICENSE("GPL v2"); From 9f0425f9c1434f582ea4e725dc48c2d843cf81b2 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 21 Jan 2022 10:33:44 +0000 Subject: [PATCH 09/26] gpu: host1x-nvhost: Fix clock rate setup The host1x-nvhost driver by default configures the clock rates for the host1x client devices to the maximum supported rate because there is runtime clock scaling support yet. However, currently the host1x-nvhost driver is only setting the rate for the client's first clock and not the others. Fix this by correcting the index used when iterating through the list of clocks. JIRA LS-410 Change-Id: If410ee25548c0dddecc5b0de9d8b6f4f687fd73c Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2657897 Tested-by: mobile promotions Reviewed-by: svcacv Reviewed-by: Mikko Perttunen Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index cd5f6436..97a52266 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -631,7 +631,7 @@ int nvhost_module_init(struct platform_device *pdev) pdata->num_clks = err; for (i = 0; i < pdata->num_clks; i++) { - err = clk_set_rate(pdata->clks[0].clk, ULONG_MAX); + err = clk_set_rate(pdata->clks[i].clk, ULONG_MAX); if (err < 0) { dev_err(&pdev->dev, "failed to set clock rate!\n"); return err; From 9ecd6942b9890a790a4fdf22f392bd07465b9d1f Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 10 Feb 2022 08:12:56 +0000 Subject: [PATCH 10/26] gpu: host1x-nvhost: Fix build for Linux v5.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling the host1x-nvhost driver for Linux v5.10 it is currently failing and the following errors are seen ... host1x-nvhost/nvhost.c: In function ‘nvhost_intr_do_work’: nvhost.c:554:2: error: implicit declaration of function ‘kfree’ [-Werror=implicit-function-declaration] kfree(host1x_cb); ^~~~~ host1x-nvhost/nvhost.c: In function ‘nvhost_intr_register_notifier’: nvhost.c:579:7: error: implicit declaration of function ‘kzalloc’ [-Werror=implicit-function-declaration] cb = kzalloc(sizeof(*cb), GFP_KERNEL); ^~~~~~~ Fix this by including slab.h. JIRA LS-410 Change-Id: I8a56d33933d3e57c04b011e2c6da03d5f2bbc707 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2667882 Reviewed-by: svcacv Reviewed-by: Sachin Nikam Reviewed-by: Mikko Perttunen GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 97a52266..1473b7f7 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "falcon.h" From c103925414cc67ad475dcf4e6c5b43667147c2eb Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 15 Feb 2022 10:37:34 +0000 Subject: [PATCH 11/26] gpu: host1x-nvhost: Fix casting and functions declarations Sparse reports the following warnings for the host1x-nvhost driver ... drivers/gpu/host1x-nvhost/falcon.c:65:27: warning: cast to restricted __le32 drivers/gpu/host1x-nvhost/nvhost.c:205:22: warning: symbol 'nvhost_syncpt_get_by_id' was not declared. Should it be static? drivers/gpu/host1x-nvhost/nvhost.c:768:5: warning: symbol 'nvhost_module_runtime_resume' was not declared. Should it be static? drivers/gpu/host1x-nvhost/nvhost.c:796:5: warning: symbol 'nvhost_module_runtime_suspend' was not declared. Should it be static? Fix the above by correcting the cast in falcon_copy_firmware_image(), remove the function nvhost_syncpt_get_by_id() because it is not used, and make the nvhost_module_runtime_resume/suspend functions static. JIRA LS-410 Change-Id: I2c923fccd0b6e53d17198617b90905c6cd06eb5c Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2670180 Reviewed-by: Sachin Nikam Reviewed-by: Mikko Perttunen GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/falcon.c | 2 +- drivers/gpu/host1x-nvhost/nvhost.c | 15 ++------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/falcon.c b/drivers/gpu/host1x-nvhost/falcon.c index f6901546..9f66549b 100644 --- a/drivers/gpu/host1x-nvhost/falcon.c +++ b/drivers/gpu/host1x-nvhost/falcon.c @@ -62,7 +62,7 @@ static void falcon_copy_firmware_image(struct falcon *falcon, /* copy the whole thing taking into account endianness */ for (i = 0; i < firmware->size / sizeof(u32); i++) - virt[i] = le32_to_cpu(((u32 *)firmware->data)[i]); + virt[i] = le32_to_cpu(((__le32 *)firmware->data)[i]); } static int falcon_parse_firmware_image(struct falcon *falcon) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 1473b7f7..609124a4 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -202,15 +202,6 @@ u32 nvhost_get_syncpt_host_managed(struct platform_device *pdev, } EXPORT_SYMBOL(nvhost_get_syncpt_host_managed); -struct host1x_syncpt *nvhost_syncpt_get_by_id(struct platform_device *pdev, - u32 id) -{ - struct nvhost_device_data *pdata = platform_get_drvdata(pdev); - - return host1x_syncpt_get_by_id(pdata->host1x, id); -} -EXPORT_SYMBOL(nvhost_syncpt_get_by_id); - void nvhost_syncpt_put_ref_ext(struct platform_device *pdev, u32 id) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); @@ -765,7 +756,7 @@ inline void nvhost_module_idle(struct platform_device *pdev) } EXPORT_SYMBOL(nvhost_module_idle); -int nvhost_module_runtime_resume(struct device *dev) +static int nvhost_module_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct nvhost_device_data *pdata = dev_get_drvdata(dev); @@ -791,9 +782,8 @@ int nvhost_module_runtime_resume(struct device *dev) return err; } -EXPORT_SYMBOL(nvhost_module_runtime_resume); -int nvhost_module_runtime_suspend(struct device *dev) +static int nvhost_module_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct nvhost_device_data *pdata = dev_get_drvdata(dev); @@ -809,7 +799,6 @@ int nvhost_module_runtime_suspend(struct device *dev) return 0; } -EXPORT_SYMBOL(nvhost_module_runtime_suspend); const struct dev_pm_ops nvhost_module_pm_ops = { SET_RUNTIME_PM_OPS(nvhost_module_runtime_suspend, From ad8058fce9c787d2de01e3830f43864fda82a5a0 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2022 16:11:19 +0100 Subject: [PATCH 12/26] gpu: host1x-nvhost: Fix client unloading After unloading the NVDLA driver and then reloading the NVDLA, the kernel is crashing. The host1x-nvhost driver is missing a call to class_destroy() in nvhost_client_device_release() which gets called when removing the DLA driver and this is preventing the DLA driver from creating the class again when reloading. The crash then occurs because when the driver is reloaded, creating the class for the DLA driver fails and then the host1x-nvhost driver incorrectly calls device_destroy() even though the device has not been created yet. Fix this by ensuring the class_destroy() is called by nvhost_client_device_release() and in the necessary error paths and then remove the call to device_destroy(). Finally, replace request_irq() with devm_request_irq() to ensure that the interrupts are also released as necessary on removal. Bug 3641820 Change-Id: Ia328bf63d528e8c31bff1d7b3ac0d5dddc22f1f5 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2710546 Reviewed-by: svcacv Reviewed-by: Mikko Perttunen GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 609124a4..44c2a289 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -106,6 +106,7 @@ static struct device *nvhost_client_device_create(struct platform_device *pdev, err = cdev_add(cdev, devno, 1); if (err < 0) { dev_err(&pdev->dev, "failed to add cdev\n"); + class_destroy(pdata->nvhost_class); return ERR_PTR(err); } @@ -115,6 +116,7 @@ static struct device *nvhost_client_device_create(struct platform_device *pdev, if (IS_ERR(dev)) { dev_err(&pdev->dev, "failed to create %s device\n", cdev_name); + class_destroy(pdata->nvhost_class); cdev_del(cdev); } @@ -156,19 +158,12 @@ int nvhost_client_device_init(struct platform_device *pdev) pdata->ctrl_node = nvhost_client_device_create(pdev, &pdata->ctrl_cdev, "ctrl-", devno, pdata->ctrl_ops); - if (IS_ERR(pdata->ctrl_node)) { - err = PTR_ERR(pdata->ctrl_node); - goto destroy; - } + if (IS_ERR(pdata->ctrl_node)) + return PTR_ERR(pdata->ctrl_node); pdata->cdev_region = devno; return 0; - -destroy: - device_destroy(pdata->nvhost_class, pdata->ctrl_cdev.dev); - - return err; } EXPORT_SYMBOL(nvhost_client_device_init); @@ -179,6 +174,7 @@ int nvhost_client_device_release(struct platform_device *pdev) if (!IS_ERR_OR_NULL(pdata->ctrl_node)) { device_destroy(pdata->nvhost_class, pdata->ctrl_cdev.dev); cdev_del(&pdata->ctrl_cdev); + class_destroy(pdata->nvhost_class); } unregister_chrdev_region(pdata->cdev_region, NVHOST_NUM_CDEV); @@ -418,7 +414,8 @@ int flcn_intr_init(struct platform_device *pdev) } spin_lock_init(&pdata->mirq_lock); - ret = request_irq(pdata->irq, flcn_isr, 0, dev_name(&pdev->dev), pdev); + ret = devm_request_irq(&pdev->dev, pdata->irq, flcn_isr, 0, + dev_name(&pdev->dev), pdev); if (ret) { dev_err(&pdev->dev, "failed to request irq. err %d\n", ret); return ret; From 58159f3b83a0099ee6b40e58594e2a0942285b24 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 10 May 2022 18:14:19 +0100 Subject: [PATCH 13/26] gpu: host1x-nvhost: Move falcon initialisation Some host1x clients, such as PVA, do not have a falcon microprocessor and currently the host1x-nvhost shim driver assumes that all clients do. Fix this by moving the falcon initialisation code from the nvhost_module_init() function and into the nvhost_flcn_finalize_poweron() function. This aligns with the implementation in the downstream nvhost driver. JIRA LS-493 Change-Id: Ic94c3489cefa6fd256f111d8e6c7afb539b5bc8a Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2710548 Reviewed-by: Nan Wang Reviewed-by: svcacv Reviewed-by: Mikko Perttunen GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 63 ++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 44c2a289..65a9a6a7 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -435,6 +435,25 @@ int flcn_reload_fw(struct platform_device *pdev) } EXPORT_SYMBOL(flcn_reload_fw); +static int nvhost_flcn_init(struct platform_device *pdev, + struct nvhost_device_data *pdata) +{ + struct falcon *falcon; + + falcon = devm_kzalloc(&pdev->dev, sizeof(*falcon), GFP_KERNEL); + if (!falcon) + return -ENOMEM; + + falcon->dev = &pdev->dev; + falcon->regs = pdata->aperture[0]; + + falcon_init(falcon); + + pdata->falcon_data = falcon; + + return 0; +} + int nvhost_flcn_prepare_poweroff(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); @@ -446,10 +465,10 @@ int nvhost_flcn_prepare_poweroff(struct platform_device *pdev) } EXPORT_SYMBOL(nvhost_flcn_prepare_poweroff); -static int nvhost_flcn_load_firmware(struct platform_device *pdev) +static int nvhost_flcn_load_firmware(struct platform_device *pdev, + struct falcon *falcon, + char *firmware_name) { - struct nvhost_device_data *pdata = platform_get_drvdata(pdev); - struct falcon *falcon = pdata->falcon_data; dma_addr_t iova; size_t size; void *virt; @@ -458,7 +477,7 @@ static int nvhost_flcn_load_firmware(struct platform_device *pdev) if (falcon->firmware.virt) return 0; - err = falcon_read_firmware(falcon, pdata->firmware_name); + err = falcon_read_firmware(falcon, firmware_name); if (err < 0) return err; @@ -485,13 +504,25 @@ cleanup: int nvhost_flcn_finalize_poweron(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); - struct falcon *falcon = pdata->falcon_data; #ifdef CONFIG_IOMMU_API struct iommu_fwspec *spec = dev_iommu_fwspec_get(&pdev->dev); #endif + struct falcon *falcon; int err; u32 value; + if (!pdata->falcon_data) { + err = nvhost_flcn_init(pdev, pdata); + if (err < 0) + return -ENOMEM; + } + + falcon = pdata->falcon_data; + + err = nvhost_flcn_load_firmware(pdev, falcon, pdata->firmware_name); + if (err < 0) + return err; + #ifdef CONFIG_IOMMU_API if (spec) { host1x_writel(pdev, pdata->transcfg_addr, pdata->transcfg_val); @@ -591,9 +622,13 @@ void nvhost_module_deinit(struct platform_device *pdev) struct falcon *falcon = pdata->falcon_data; pm_runtime_disable(&pdev->dev); - dma_free_coherent(&pdev->dev, falcon->firmware.size, + + if (falcon) { + dma_free_coherent(&pdev->dev, falcon->firmware.size, falcon->firmware.virt, falcon->firmware.iova); - falcon_exit(falcon); + falcon_exit(falcon); + } + debugfs_remove(pdata->debugfs); } EXPORT_SYMBOL(nvhost_module_deinit); @@ -601,17 +636,9 @@ EXPORT_SYMBOL(nvhost_module_deinit); int nvhost_module_init(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); - struct falcon *falcon; unsigned int i; int err; - falcon = devm_kzalloc(&pdev->dev, sizeof(*falcon), GFP_KERNEL); - if (!falcon) - return -ENOMEM; - - falcon->dev = &pdev->dev; - falcon->regs = pdata->aperture[0]; - err = devm_clk_bulk_get_all(&pdev->dev, &pdata->clks); if (err < 0) { dev_err(&pdev->dev, "failed to get clocks %d\n", err); @@ -664,11 +691,7 @@ int nvhost_module_init(struct platform_device *pdev) pdata->debugfs = debugfs_create_dir(pdev->dev.of_node->name, NULL); - falcon_init(falcon); - - pdata->falcon_data = falcon; - - return nvhost_flcn_load_firmware(pdev); + return 0; } EXPORT_SYMBOL(nvhost_module_init); From c87eb8b8d9adb4d1f31dd213ee058bd8afe52b49 Mon Sep 17 00:00:00 2001 From: Abhinaya Agrawal Date: Tue, 17 May 2022 14:26:31 -0700 Subject: [PATCH 14/26] drivers: Fix host1x and nvhost debugfs cleanup Both host1x and nvhost expose APIs that deinitialize the debugfs and remove debugfs files/directories that were created. At present, debugfs_remove() is inovked to do this. However, debugfs_remove_recursive() should instead be used to ensure that all files within a debugfs are recursively removed before the directory is removed. Bug 3662478 Change-Id: Idb7d1458c02f57f6d656a7bfe1df3adf5e0497aa Signed-off-by: Abhinaya Agrawal Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2714042 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Omar Nemri GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 65a9a6a7..502d6c6a 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -629,7 +629,7 @@ void nvhost_module_deinit(struct platform_device *pdev) falcon_exit(falcon); } - debugfs_remove(pdata->debugfs); + debugfs_remove_recursive(pdata->debugfs); } EXPORT_SYMBOL(nvhost_module_deinit); From 96d0bbd3877707b6675ce6bc5ae24b08d92b0e5e Mon Sep 17 00:00:00 2001 From: Nan Wang Date: Mon, 23 May 2022 09:28:07 -0700 Subject: [PATCH 15/26] pva: support both upstream and downstream kernel 1. Replace nvhost print functions with PVA driver's private implementation with similar functionalities. 2. Change syncpoint from host managed to client managed so that syncpoint max value is tracked inside PVA driver. This change is needed because upstream host1x driver doesn't allow decrement of max value. 3. Various minor changes for adapting to upstream kernel APIs. Bug 3662478 Change-Id: I16eff6c06c51afe0f274598e1273404924d18684 Signed-off-by: Nan Wang Signed-off-by: Abhinaya Agrawal Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2710684 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/gpu/host1x-nvhost/nvhost.c | 58 +++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 502d6c6a..cf2d964f 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -126,20 +126,38 @@ static struct device *nvhost_client_device_create(struct platform_device *pdev, int nvhost_client_device_get_resources(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); - struct resource *res; int err; + u32 i; err = nvhost_get_host1x_dev(pdev); if (err) return err; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + for (i = 0; i < pdev->num_resources; i++) { + void __iomem *regs = NULL; + struct resource *r; - pdata->aperture[0] = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pdata->aperture[0])) - return PTR_ERR(pdata->aperture); + r = platform_get_resource(pdev, IORESOURCE_MEM, i); + /* We've run out of mem resources */ + if (!r) + break; + + regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(regs)) { + err = PTR_ERR(regs); + goto fail; + } + + pdata->aperture[i] = regs; + } return 0; + +fail: + dev_err(&pdev->dev, "failed to get register memory\n"); + + return err; + } EXPORT_SYMBOL(nvhost_client_device_get_resources); @@ -198,6 +216,22 @@ u32 nvhost_get_syncpt_host_managed(struct platform_device *pdev, } EXPORT_SYMBOL(nvhost_get_syncpt_host_managed); +u32 nvhost_get_syncpt_client_managed(struct platform_device *pdev, + const char *syncpt_name) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + + sp = host1x_syncpt_alloc(pdata->host1x, HOST1X_SYNCPT_CLIENT_MANAGED, + syncpt_name ? syncpt_name : + dev_name(&pdev->dev)); + if (!sp) + return 0; + + return host1x_syncpt_id(sp); +} +EXPORT_SYMBOL_GPL(nvhost_get_syncpt_client_managed); + void nvhost_syncpt_put_ref_ext(struct platform_device *pdev, u32 id) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); @@ -278,6 +312,20 @@ void nvhost_syncpt_set_min_update(struct platform_device *pdev, u32 id, u32 val) } EXPORT_SYMBOL(nvhost_syncpt_set_min_update); +int nvhost_syncpt_read_ext_check(struct platform_device *pdev, u32 id, u32 *val) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct host1x_syncpt *sp; + + sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id); + if (!sp) + return -EINVAL; + + *val = host1x_syncpt_read(sp); + return 0; +} +EXPORT_SYMBOL(nvhost_syncpt_read_ext_check); + u32 nvhost_syncpt_read_maxval(struct platform_device *pdev, u32 id) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); From b5c5a485da71ca9229d9d884f21ca4749e0d0d05 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 10 Aug 2022 19:43:14 +0100 Subject: [PATCH 16/26] gpu: host1x-nvhost: Add support for Tegra234 Add support for Tegra234 in the host1x-nvhost driver. Bug 3724727 Change-Id: I3e42d066ce22a461d71f26c120f08aed96f30430 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2759200 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Mikko Perttunen --- drivers/gpu/host1x-nvhost/nvhost.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index cf2d964f..6e1e6250 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -23,6 +23,9 @@ #define TEGRA194_SYNCPT_PAGE_SIZE 0x1000 #define TEGRA194_SYNCPT_SHIM_BASE 0x60000000 #define TEGRA194_SYNCPT_SHIM_SIZE 0x00400000 +#define TEGRA234_SYNCPT_PAGE_SIZE 0x10000 +#define TEGRA234_SYNCPT_SHIM_BASE 0x60000000 +#define TEGRA234_SYNCPT_SHIM_SIZE 0x04000000 #define THI_STREAMID0 0x00000030 #define THI_STREAMID1 0x00000034 @@ -54,6 +57,7 @@ EXPORT_SYMBOL(host1x_writel); static const struct of_device_id host1x_match[] = { { .compatible = "nvidia,tegra194-host1x", }, + { .compatible = "nvidia,tegra234-host1x", }, {}, }; @@ -361,6 +365,12 @@ static int nvhost_syncpt_get_aperture(struct device_node *np, u64 *base, return 0; } + if (of_device_is_compatible(np, "nvidia,tegra234-host1x")) { + *base = TEGRA234_SYNCPT_SHIM_BASE; + *size = TEGRA234_SYNCPT_SHIM_SIZE; + return 0; + } + return -ENODEV; } @@ -371,6 +381,11 @@ static int nvhost_syncpt_get_page_size(struct device_node *np, uint32_t *size) return 0; } + if (of_device_is_compatible(np, "nvidia,tegra234-host1x")) { + *size = TEGRA234_SYNCPT_PAGE_SIZE; + return 0; + } + return -ENODEV; } From 2e94a90e1d7760f05e99f159d4ff99064aecff67 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 26 Sep 2022 15:40:21 +0100 Subject: [PATCH 17/26] gpu: host1x-nvhost: Update syncpt helper functions - add get aperture API - add get syncpt offset api Bug 3768126 Change-Id: I95ae6a149e8a362317f4f1c89e7a1eac3c596fe4 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2782259 Tested-by: Omar Nemri Reviewed-by: Omar Nemri GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 22 ++++++++++++++++++++++ include/linux/nvhost_t194.h | 13 +++++++++++++ 2 files changed, 35 insertions(+) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 6e1e6250..d9043f84 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -389,6 +390,27 @@ static int nvhost_syncpt_get_page_size(struct device_node *np, uint32_t *size) return -ENODEV; } +u32 nvhost_syncpt_unit_interface_get_byte_offset_ext(struct platform_device *pdev, + u32 syncpt_id) +{ + struct nvhost_device_data *pdata = platform_get_drvdata(pdev); + struct nvhost_syncpt_interface *syncpt_if = pdata->syncpt_unit_interface; + + if (WARN_ON(!syncpt_if)) + return 0; + + return syncpt_id * syncpt_if->page_size; +} +EXPORT_SYMBOL(nvhost_syncpt_unit_interface_get_byte_offset_ext); + +int nvhost_syncpt_unit_interface_get_aperture(struct platform_device *pdev, + u64 *base, size_t *size) +{ + return nvhost_syncpt_get_aperture(pdev->dev.parent->of_node, base, + size); +} +EXPORT_SYMBOL(nvhost_syncpt_unit_interface_get_aperture); + int nvhost_syncpt_unit_interface_init(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); diff --git a/include/linux/nvhost_t194.h b/include/linux/nvhost_t194.h index f7b7dbde..29ac8617 100644 --- a/include/linux/nvhost_t194.h +++ b/include/linux/nvhost_t194.h @@ -24,6 +24,19 @@ int nvhost_syncpt_unit_interface_get_aperture( phys_addr_t *base, size_t *size); +#ifdef CONFIG_TEGRA_HOST1X +u32 nvhost_syncpt_unit_interface_get_byte_offset_ext( + struct platform_device *host_pdev, + u32 syncpt_id); +#else u32 nvhost_syncpt_unit_interface_get_byte_offset(u32 syncpt_id); +static inline u32 nvhost_syncpt_unit_interface_get_byte_offset_ext( + struct platform_device *host_pdev, + u32 syncpt_id) +{ + return nvhost_syncpt_unit_interface_get_byte_offset(syncpt_id); +} +#endif + #endif /* __LINUX_NVHOST_T194_H__ */ From 9d0dda14ae666dd8bef1d59dcf3de8f66b24cc6f Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 3 Oct 2022 11:41:57 +0100 Subject: [PATCH 18/26] gpu: host1x-nvhost: Update include path When building the host1x-nvhost driver as an external module, the driver source is now copied into a common location with all the other external modules. Therefore, we can now use the 'srctree.nvidia' path for finding the necessary header files for Host1x. Update the include search paths to use 'srctree.nvidia' when building the driver. Bug 3817518 Change-Id: I443dd4cb6d01c4e9be8f3c02550b1050d0bcfd28 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2785956 Reviewed-by: svcacv Reviewed-by: Mikko Perttunen GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/host1x-nvhost/Makefile b/drivers/gpu/host1x-nvhost/Makefile index 65fbd82d..48bd9441 100644 --- a/drivers/gpu/host1x-nvhost/Makefile +++ b/drivers/gpu/host1x-nvhost/Makefile @@ -7,7 +7,7 @@ ccflags-y += -I$(srctree.nvidia)/include ccflags-y += -I$(srctree.nvidia)/include/uapi/linux -ccflags-y += -I$(srctree.host1x)/include +ccflags-y += -I$(srctree.nvidia)/drivers/gpu/host1x/include ccflags-y += -DCONFIG_TEGRA_HOST1X ccflags-y += -Werror From 968b6f758fca0e29245f909bb23b8d4b803345a1 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Tue, 4 Oct 2022 13:11:25 +0300 Subject: [PATCH 19/26] gpu: host1x-nvhost: Adapt for change in host1x_fence_create Add 'true' parameter to enable internal timeout since there is no external cancellation mechanism. Also change kfree to kfree_rcu to ensure workqueue is not freed during callback. Signed-off-by: Mikko Perttunen Change-Id: Ib0ef9a211bafbdd911dc90175a18680880e3e2f8 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2786638 Reviewed-by: Jonathan Hunter GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index d9043f84..933aedd5 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -656,7 +656,7 @@ static void nvhost_intr_do_work(struct work_struct *work) host1x_cb = container_of(work, struct nvhost_host1x_cb, work); host1x_cb->notifier(host1x_cb->notifier_data, 0); - kfree(host1x_cb); + kfree_rcu(host1x_cb); } int nvhost_intr_register_notifier(struct platform_device *pdev, @@ -674,7 +674,7 @@ int nvhost_intr_register_notifier(struct platform_device *pdev, if (!sp) return -EINVAL; - fence = host1x_fence_create(sp, thresh); + fence = host1x_fence_create(sp, thresh, true); if (IS_ERR(fence)) { pr_err("error %d during construction of fence!", (int)PTR_ERR(fence)); From 6cd2f00c4637640efeff4e6a0a2bef3e63992be4 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 31 Oct 2022 15:34:20 +0000 Subject: [PATCH 20/26] nvhost: Add clean up function for syncpt interface Drivers, such as the PVA and DLA driver, that call nvhost_syncpt_unit_interface_init() during probe are missing a call to clean-up the DMA mappings this function may create if the driver probe fails or if the driver is removed. The function nvhost_syncpt_unit_interface_init() may make a call to dma_map_sg_attrs() and we need to ensure that dma_unmap_sg_attrs() is called if the probe of the driver fails or if the driver is unloaded. Add a new function, nvhost_syncpt_unit_interface_deinit(), that calls dma_unmap_sg_attrs() if needed for drivers to call. Bug 3800349 Change-Id: I62a4e19cd42878dac54fa623509440596ffdf17f Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2801280 (cherry picked from commit cee12fb989186611aa9deb5ada45831ab5f783aa) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2801934 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Mikko Perttunen GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 933aedd5..bd1fc6cd 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -34,6 +34,7 @@ #define NVHOST_NUM_CDEV 1 struct nvhost_syncpt_interface { + struct scatterlist sg; dma_addr_t base; uint32_t page_size; }; @@ -439,12 +440,10 @@ int nvhost_syncpt_unit_interface_init(struct platform_device *pdev) /* If IOMMU is enabled, map it into the device memory */ if (iommu_get_domain_for_dev(&pdev->dev)) { - struct scatterlist sg; + sg_init_table(&syncpt_if->sg, 1); + sg_set_page(&syncpt_if->sg, phys_to_page(base), size, 0); - sg_init_table(&sg, 1); - sg_set_page(&sg, phys_to_page(base), size, 0); - - err = dma_map_sg_attrs(&pdev->dev, &sg, 1, + err = dma_map_sg_attrs(&pdev->dev, &syncpt_if->sg, 1, DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC); if (err == 0) { @@ -452,7 +451,7 @@ int nvhost_syncpt_unit_interface_init(struct platform_device *pdev) return err; } - syncpt_if->base = sg_dma_address(&sg); + syncpt_if->base = sg_dma_address(&syncpt_if->sg); } else { syncpt_if->base = base; } @@ -467,6 +466,21 @@ int nvhost_syncpt_unit_interface_init(struct platform_device *pdev) } EXPORT_SYMBOL(nvhost_syncpt_unit_interface_init); +void nvhost_syncpt_unit_interface_deinit(struct platform_device *pdev) +{ + struct nvhost_syncpt_interface *syncpt_if; + struct nvhost_device_data *pdata; + + if (iommu_get_domain_for_dev(&pdev->dev)) { + pdata = platform_get_drvdata(pdev); + syncpt_if = pdata->syncpt_unit_interface; + + dma_unmap_sg_attrs(&pdev->dev, &syncpt_if->sg, 1, + DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC); + } +} +EXPORT_SYMBOL(nvhost_syncpt_unit_interface_deinit); + dma_addr_t nvhost_syncpt_address(struct platform_device *pdev, u32 id) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); From 7d40c3953e52c4d4eed5c31607ad5574634f86bf Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 29 Nov 2022 10:53:20 +0000 Subject: [PATCH 21/26] gpu: host1x-nvhost: Fix mapping of syncpt shim With Linux v6.1, if CONFIG_PCI_P2PDMA is enabled, then mapping the syncpt shim causes a kernel paging request fault. The fault occurs when dma_map_sg_attrs() is called in by nvhost_syncpt_unit_interface_init(), which is attempting to map the syncpt shim MMIO space. The function dma_map_sg_attrs() calls iomm_dma_map_sg() and since upstream Linux commit 30280eee2db1 ("iommu/dma: support PCI P2PDMA pages in dma-iommu map_sg") was added this now calls is_pci_p2pdma_page(). The function is_pci_p2pdma_page() requires that there is a 'struct page' associated with the sg, but this is not the case and so deferencing the 'struct page' pointer returned by sg_page() results in a paging fault. Using dma_map_sg_attrs() to map MMIO that is not associated with valid page structures is not guaranteed to work. Therefore, fix this by using dma_map_resource() to map the syncpt shim instead. Bug 3871422 Change-Id: I2b08a3b78be0850bff44e62bfc93c8d095eb82a2 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2818417 (cherry picked from commit 97f1282f2959c208effebf93cc5061d6e7fa6c55) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2819086 Reviewed-by: Mikko Perttunen GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index bd1fc6cd..972f4b34 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -34,8 +34,8 @@ #define NVHOST_NUM_CDEV 1 struct nvhost_syncpt_interface { - struct scatterlist sg; dma_addr_t base; + size_t size; uint32_t page_size; }; @@ -416,7 +416,6 @@ int nvhost_syncpt_unit_interface_init(struct platform_device *pdev) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct nvhost_syncpt_interface *syncpt_if; - size_t size; u64 base; int err; @@ -425,7 +424,7 @@ int nvhost_syncpt_unit_interface_init(struct platform_device *pdev) return -ENOMEM; err = nvhost_syncpt_get_aperture(pdev->dev.parent->of_node, &base, - &size); + &syncpt_if->size); if (err < 0) { dev_err(&pdev->dev, "failed to get syncpt aperture\n"); return err; @@ -440,18 +439,12 @@ int nvhost_syncpt_unit_interface_init(struct platform_device *pdev) /* If IOMMU is enabled, map it into the device memory */ if (iommu_get_domain_for_dev(&pdev->dev)) { - sg_init_table(&syncpt_if->sg, 1); - sg_set_page(&syncpt_if->sg, phys_to_page(base), size, 0); - - err = dma_map_sg_attrs(&pdev->dev, &syncpt_if->sg, 1, - DMA_BIDIRECTIONAL, - DMA_ATTR_SKIP_CPU_SYNC); - if (err == 0) { - err = -ENOMEM; - return err; - } - - syncpt_if->base = sg_dma_address(&syncpt_if->sg); + syncpt_if->base = dma_map_resource(&pdev->dev, base, + syncpt_if->size, + DMA_BIDIRECTIONAL, + DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(&pdev->dev, syncpt_if->base)) + return -ENOMEM; } else { syncpt_if->base = base; } @@ -460,7 +453,7 @@ int nvhost_syncpt_unit_interface_init(struct platform_device *pdev) dev_info(&pdev->dev, "syncpt_unit_base %llx syncpt_unit_size %zx size %x\n", - base, size, syncpt_if->page_size); + base, syncpt_if->size, syncpt_if->page_size); return 0; } @@ -475,7 +468,7 @@ void nvhost_syncpt_unit_interface_deinit(struct platform_device *pdev) pdata = platform_get_drvdata(pdev); syncpt_if = pdata->syncpt_unit_interface; - dma_unmap_sg_attrs(&pdev->dev, &syncpt_if->sg, 1, + dma_unmap_resource(&pdev->dev, syncpt_if->base, syncpt_if->size, DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC); } } From 88028eda4be5e84fa6a2ed2f533cbaefc5d601c0 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Mon, 21 Nov 2022 20:18:36 +0530 Subject: [PATCH 22/26] gpu: host1x-nvhost: Align syncpt interface APIs The syncpt interface APIs in the host1x-nvhost driver slightly differ from those in the legacy nvhost driver because instead of passing the platform device structure for the host1x device, the platform device structure for the host1x client device is passed. By aligning the APIs so that we pass the platform device structure for the host1x in all implementations, we can simplify the PVA driver and directly use the APIs in the NVIDIA display driver. The NVIDIA display driver requires some additional syncpt interface APIs and so implement these as well. Bug 3713048 Change-Id: I507e6fd066e6e22c0c47c20ba0dd3be5fa033c59 Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2813827 Reviewed-by: Bibek Basu Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 66 +++++++++++++++++++++--------- include/linux/nvhost_t194.h | 4 +- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 972f4b34..32209630 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -63,32 +63,43 @@ static const struct of_device_id host1x_match[] = { {}, }; -static int nvhost_get_host1x_dev(struct platform_device *pdev) +struct platform_device *nvhost_get_default_device(void) { - struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct platform_device *host1x_pdev; struct device_node *np; np = of_find_matching_node(NULL, host1x_match); - if (!np) { - dev_err(&pdev->dev, "Failed to find host1x!\n"); - return -ENODEV; - } + if (!np) + return NULL; host1x_pdev = of_find_device_by_node(np); + if (!host1x_pdev) + return NULL; + + return host1x_pdev; +} +EXPORT_SYMBOL(nvhost_get_default_device); + +struct host1x *nvhost_get_host1x(struct platform_device *pdev) +{ + struct platform_device *host1x_pdev; + struct host1x *host1x; + + host1x_pdev = nvhost_get_default_device(); if (!host1x_pdev) { dev_dbg(&pdev->dev, "host1x device not available\n"); - return -EPROBE_DEFER; + return NULL; } - pdata->host1x = platform_get_drvdata(host1x_pdev); - if (!pdata->host1x) { + host1x = platform_get_drvdata(host1x_pdev); + if (!host1x) { dev_warn(&pdev->dev, "No platform data for host1x!\n"); - return -ENODEV; + return NULL; } - return 0; + return host1x; } +EXPORT_SYMBOL(nvhost_get_host1x); static struct device *nvhost_client_device_create(struct platform_device *pdev, struct cdev *cdev, @@ -135,9 +146,11 @@ int nvhost_client_device_get_resources(struct platform_device *pdev) int err; u32 i; - err = nvhost_get_host1x_dev(pdev); - if (err) - return err; + pdata->host1x = nvhost_get_host1x(pdev); + if (!pdata->host1x) { + dev_warn(&pdev->dev, "No platform data for host1x!\n"); + return -ENODEV; + } for (i = 0; i < pdev->num_resources; i++) { void __iomem *regs = NULL; @@ -394,21 +407,34 @@ static int nvhost_syncpt_get_page_size(struct device_node *np, uint32_t *size) u32 nvhost_syncpt_unit_interface_get_byte_offset_ext(struct platform_device *pdev, u32 syncpt_id) { - struct nvhost_device_data *pdata = platform_get_drvdata(pdev); - struct nvhost_syncpt_interface *syncpt_if = pdata->syncpt_unit_interface; + uint32_t size; + int err; - if (WARN_ON(!syncpt_if)) + err = nvhost_syncpt_get_page_size(pdev->dev.of_node, &size); + if (WARN_ON(err < 0)) return 0; - return syncpt_id * syncpt_if->page_size; + return syncpt_id * size; } EXPORT_SYMBOL(nvhost_syncpt_unit_interface_get_byte_offset_ext); +u32 nvhost_syncpt_unit_interface_get_byte_offset(u32 syncpt_id) +{ + struct platform_device *host1x_pdev; + + host1x_pdev = nvhost_get_default_device(); + if (WARN_ON(!host1x_pdev)) + return 0; + + return nvhost_syncpt_unit_interface_get_byte_offset_ext(host1x_pdev, + syncpt_id); +} +EXPORT_SYMBOL(nvhost_syncpt_unit_interface_get_byte_offset); + int nvhost_syncpt_unit_interface_get_aperture(struct platform_device *pdev, u64 *base, size_t *size) { - return nvhost_syncpt_get_aperture(pdev->dev.parent->of_node, base, - size); + return nvhost_syncpt_get_aperture(pdev->dev.of_node, base, size); } EXPORT_SYMBOL(nvhost_syncpt_unit_interface_get_aperture); diff --git a/include/linux/nvhost_t194.h b/include/linux/nvhost_t194.h index 29ac8617..0f1e4c25 100644 --- a/include/linux/nvhost_t194.h +++ b/include/linux/nvhost_t194.h @@ -24,13 +24,13 @@ int nvhost_syncpt_unit_interface_get_aperture( phys_addr_t *base, size_t *size); +u32 nvhost_syncpt_unit_interface_get_byte_offset(u32 syncpt_id); + #ifdef CONFIG_TEGRA_HOST1X u32 nvhost_syncpt_unit_interface_get_byte_offset_ext( struct platform_device *host_pdev, u32 syncpt_id); #else -u32 nvhost_syncpt_unit_interface_get_byte_offset(u32 syncpt_id); - static inline u32 nvhost_syncpt_unit_interface_get_byte_offset_ext( struct platform_device *host_pdev, u32 syncpt_id) From b394e81a9e79532a24a33d455417fe6684e29afd Mon Sep 17 00:00:00 2001 From: Sanif Veeras Date: Fri, 2 Dec 2022 11:39:53 +0530 Subject: [PATCH 23/26] video:tegra:host: add gpu specific syncpt pool - Support allocation from the GPU specific syncpoint pool after reading from the Device Tree - Expose an interface for the NvGPU driver to allocate the syncpoint from the GPU pool Jira HOSTX-4515 Change-Id: Ie5cc9ec22c039b958b0c0a1750f959ebbf6c6bee Signed-off-by: Sanif Veeras Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2820453 Reviewed-by: svc-mobile-coverity Reviewed-by: svc_kernel_abi Reviewed-by: Arto Merilainen Reviewed-by: Raghavendra Vishnu Kumar GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 32209630..477d3a4f 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -251,6 +251,13 @@ u32 nvhost_get_syncpt_client_managed(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(nvhost_get_syncpt_client_managed); +u32 nvhost_get_syncpt_gpu_managed(struct platform_device *pdev, + const char *syncpt_name) +{ + return nvhost_get_syncpt_client_managed(pdev, syncpt_name); +} +EXPORT_SYMBOL_GPL(nvhost_get_syncpt_gpu_managed); + void nvhost_syncpt_put_ref_ext(struct platform_device *pdev, u32 id) { struct nvhost_device_data *pdata = platform_get_drvdata(pdev); From bc86b17fa2860fa4df204668e423dadb31ef32a5 Mon Sep 17 00:00:00 2001 From: Arvind M Date: Tue, 14 Feb 2023 15:41:21 +0000 Subject: [PATCH 24/26] host1x: kmd: enable irq on poweron [1] Since the irq is not enabled during the power-on, the firmware is not communicable state upon module reset. [2] This commit fixes the issue by enabling irq on power-on. Bug 3979666 Bug 3975703 Change-Id: I746a9236b390ab2d429df1f4a3f6243745147b65 Signed-off-by: Arvind M Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2858111 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Amit Sharma (SW-TEGRA) Reviewed-by: Ayush Kumar Reviewed-by: Jonathan Hunter Reviewed-by: Mikko Perttunen GVS: Gerrit_Virtual_Submit Tested-by: Amit Sharma (SW-TEGRA) --- drivers/gpu/host1x-nvhost/nvhost.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 477d3a4f..40469e36 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2022, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2022-2023, NVIDIA Corporation. All rights reserved. */ #include @@ -670,6 +670,9 @@ int nvhost_flcn_finalize_poweron(struct platform_device *pdev) return err; } + if (pdata->flcn_isr) + enable_irq(pdata->irq); + return 0; } EXPORT_SYMBOL(nvhost_flcn_finalize_poweron); @@ -919,9 +922,6 @@ static int nvhost_module_runtime_resume(struct device *dev) /* Load clockgating registers */ nvhost_module_load_regs(pdev, pdata->engine_can_cg); - if (pdata->flcn_isr) - enable_irq(pdata->irq); - if (pdata->finalize_poweron) err = pdata->finalize_poweron(pdev); From 8942623b15569a25b756fdeba727b82e52abb782 Mon Sep 17 00:00:00 2001 From: Arvind M Date: Tue, 21 Feb 2023 11:23:37 +0000 Subject: [PATCH 25/26] gpu: host1x-nvhost: avoid setting flcn irq at boot [1] The DLA firmware configures the DEST and MASK registers during the initialization. [2] In OOT, the DLA KMD is accidentally setting the interrupts. This will result in enabling additional interrupts that the firmware does not handle (like NVDLA_GENERIC_INTR_FAULT_CRITICAL). [3] This commit fixes the issue by avoiding the interrupt settings at the DLA KMD. Bug 3960841 Change-Id: Ied77ac7564ae3b3202b9cceaf08ad3c7da9f947c Signed-off-by: Arvind M Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2861253 Reviewed-by: Mikko Perttunen Reviewed-by: Amit Sharma (SW-TEGRA) Reviewed-by: Jonathan Hunter Reviewed-by: svcacv GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/falcon.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/gpu/host1x-nvhost/falcon.c b/drivers/gpu/host1x-nvhost/falcon.c index 9f66549b..53f52550 100644 --- a/drivers/gpu/host1x-nvhost/falcon.c +++ b/drivers/gpu/host1x-nvhost/falcon.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2022, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2015-2023, NVIDIA Corporation. All rights reserved. */ #include @@ -182,21 +182,6 @@ int falcon_boot(struct falcon *falcon) falcon_copy_chunk(falcon, falcon->firmware.code.offset + offset, offset, FALCON_MEMORY_IMEM); - /* setup falcon interrupts */ - falcon_writel(falcon, FALCON_IRQMSET_EXT(0xff) | - FALCON_IRQMSET_SWGEN1 | - FALCON_IRQMSET_SWGEN0 | - FALCON_IRQMSET_EXTERR | - FALCON_IRQMSET_HALT | - FALCON_IRQMSET_WDTMR, - FALCON_IRQMSET); - falcon_writel(falcon, FALCON_IRQDEST_EXT(0xff) | - FALCON_IRQDEST_SWGEN1 | - FALCON_IRQDEST_SWGEN0 | - FALCON_IRQDEST_EXTERR | - FALCON_IRQDEST_HALT, - FALCON_IRQDEST); - /* enable interface */ falcon_writel(falcon, FALCON_ITFEN_MTHDEN | FALCON_ITFEN_CTXEN, From 10c73be4bfd325e66d41376441d4e1e37495b2f2 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 22 Feb 2023 15:10:51 +0200 Subject: [PATCH 26/26] gpu: host1x-nvhost: Fix invalid NULL check NULL check in is_valid_pt_ext was negated, making this function useless. Fix the check. Signed-off-by: Mikko Perttunen Change-Id: I8764bcfceb457bf26ef79ebffdf17526d4174849 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2861958 Reviewed-by: svcacv Reviewed-by: Jonathan Hunter Reviewed-by: Santosh BS GVS: Gerrit_Virtual_Submit --- drivers/gpu/host1x-nvhost/nvhost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/host1x-nvhost/nvhost.c b/drivers/gpu/host1x-nvhost/nvhost.c index 40469e36..2a4988fb 100644 --- a/drivers/gpu/host1x-nvhost/nvhost.c +++ b/drivers/gpu/host1x-nvhost/nvhost.c @@ -276,7 +276,7 @@ bool nvhost_syncpt_is_valid_pt_ext(struct platform_device *pdev, u32 id) struct nvhost_device_data *pdata = platform_get_drvdata(pdev); struct host1x_syncpt *sp; - if (!pdata || pdata->host1x) + if (!pdata || !pdata->host1x) return -ENODEV; sp = host1x_syncpt_get_by_id_noref(pdata->host1x, id);