From nobody Sat Oct 11 08:21:18 2025 Received: from smtp1.tecnico.ulisboa.pt (smtp1.tecnico.ulisboa.pt [193.136.128.21]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CF0E91DA3D; Wed, 11 Jun 2025 12:18:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.136.128.21 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749644330; cv=none; b=l9MwVRVSM/Go0CrUe2TwgO1V1fgjjRpAtHLaPpcj+aN0eXdPC1hHN6wWVW9RFPCY4p+ox+HoP8nJeUW58l5yXCF4dsPkwmPVJAZ0PWZl28FvLHiv0ifc+IuLaOhxDtQDGOZJQ9mbsChyOlJbFWAILJpVib2Yw0rQlirVNB049uw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749644330; c=relaxed/simple; bh=a1Kwhc9xVpU1siL0iMkW/cHqeeI29i0pfMRqY4k+tnI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=KZ4h/Yxvki2Ge5I+1I9JwGLVGNAZCUf3eaIepoTUK3LZrHBG6PoI4N3TYyfvDnkJANS/pRxaxxjlxvFDzOePJJGx/Z/9a30x2NxwZjcZqmNFc9JXf+eR5IFWQdvssAR/MHxnA/x+JGQjDZT+f7O2/06AvUhxpL1F5zU3QPOLhmA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=tecnico.ulisboa.pt; spf=pass smtp.mailfrom=tecnico.ulisboa.pt; dkim=pass (1024-bit key) header.d=tecnico.ulisboa.pt header.i=@tecnico.ulisboa.pt header.b=mhfipmsc; arc=none smtp.client-ip=193.136.128.21 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=tecnico.ulisboa.pt Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=tecnico.ulisboa.pt Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=tecnico.ulisboa.pt header.i=@tecnico.ulisboa.pt header.b="mhfipmsc" Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp1.tecnico.ulisboa.pt (Postfix) with ESMTP id 00A7B6002405; Wed, 11 Jun 2025 13:18:46 +0100 (WEST) X-Virus-Scanned: by amavis-2.13.0 (20230106) (Debian) at tecnico.ulisboa.pt Received: from smtp1.tecnico.ulisboa.pt ([127.0.0.1]) by localhost (smtp1.tecnico.ulisboa.pt [127.0.0.1]) (amavis, port 10025) with LMTP id ui_YMVBi7nnt; Wed, 11 Jun 2025 13:18:43 +0100 (WEST) Received: from mail1.tecnico.ulisboa.pt (mail1.ist.utl.pt [IPv6:2001:690:2100:1::b3dd:b9ac]) by smtp1.tecnico.ulisboa.pt (Postfix) with ESMTPS id 7C5276002309; Wed, 11 Jun 2025 13:18:43 +0100 (WEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tecnico.ulisboa.pt; s=mail; t=1749644323; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=GJLWztSF70GRD4MSuWKA6hqX2AfeS+giESxmLBVQWoE=; b=mhfipmscz+1yAilBz/doY2AjUCZztX4GW8GA1TBoyyYh2oTfQLAsK7bzTec3UhNw2ynHqN j2/lH4QKnaT0PejKfuZy/44cZRsN+HQhc1PuEh5kkfku7wh1TacJnkHR7UrCcfW+EWf/iI eatWekcGOXKObA6w7aoH51novujIe+I= Received: from [192.168.1.151] (unknown [IPv6:2001:8a0:57db:f00:3ee2:38aa:e2c9:7dde]) (Authenticated sender: ist187313) by mail1.tecnico.ulisboa.pt (Postfix) with ESMTPSA id 58B7E36008F; Wed, 11 Jun 2025 13:18:43 +0100 (WEST) From: Diogo Ivo Date: Wed, 11 Jun 2025 13:18:01 +0100 Subject: [PATCH v2 1/3] drm/tegra: Add NVJPG driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250611-diogo-nvjpg-v2-1-01f8c76ea90f@tecnico.ulisboa.pt> References: <20250611-diogo-nvjpg-v2-0-01f8c76ea90f@tecnico.ulisboa.pt> In-Reply-To: <20250611-diogo-nvjpg-v2-0-01f8c76ea90f@tecnico.ulisboa.pt> To: Thierry Reding , Mikko Perttunen , David Airlie , Simona Vetter , Jonathan Hunter , Philipp Zabel , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-tegra@vger.kernel.org, devicetree@vger.kernel.org, Diogo Ivo X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1749644321; l=10931; i=diogo.ivo@tecnico.ulisboa.pt; s=20240529; h=from:subject:message-id; bh=a1Kwhc9xVpU1siL0iMkW/cHqeeI29i0pfMRqY4k+tnI=; b=L0PcWNPavpdukrcLRaUmJu9hbJorN/oAM8v71Zx8MHBL+ZGgj4V73SmgYX+EiodDpRiUEEDeT JIF9L/4lFMuDMVSb3T2Ibf6ntdSMp2yP1yNe5SyMNBAr7Ah3h7cLOV3 X-Developer-Key: i=diogo.ivo@tecnico.ulisboa.pt; a=ed25519; pk=BRGXhMh1q5KDlZ9y2B8SodFFY8FGupal+NMtJPwRpUQ= Add support for booting and using NVJPG on Tegra210 to the Host1x and TegraDRM drivers. This driver only supports the new TegraDRM uAPI. Signed-off-by: Diogo Ivo Acked-by: Mikko Perttunen --- v1->v2: - Removed explicit reset handling, leaving it to power-domain code - Set clock rate to maximum upon getting clock - Use modern variants to set PM ops - Simplify getting io resource --- drivers/gpu/drm/tegra/Makefile | 1 + drivers/gpu/drm/tegra/drm.c | 2 + drivers/gpu/drm/tegra/drm.h | 1 + drivers/gpu/drm/tegra/nvjpg.c | 331 +++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 335 insertions(+) diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index 6fc4b504e7861df8802f3f0f5d6faf9eb167b27a..e399b40d64a1d2070b27c2219a3= 6693a8e3edc61 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile @@ -25,6 +25,7 @@ tegra-drm-y :=3D \ falcon.o \ vic.o \ nvdec.o \ + nvjpg.o \ riscv.o =20 tegra-drm-y +=3D trace.o diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 4596073fe28fa1beb6689da305775f4468714548..f44fb145920ae77dda82780a659= a98c88ea25a5a 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1383,6 +1383,7 @@ static const struct of_device_id host1x_drm_subdevs[]= =3D { { .compatible =3D "nvidia,tegra210-sor1", }, { .compatible =3D "nvidia,tegra210-vic", }, { .compatible =3D "nvidia,tegra210-nvdec", }, + { .compatible =3D "nvidia,tegra210-nvjpg", }, { .compatible =3D "nvidia,tegra186-display", }, { .compatible =3D "nvidia,tegra186-dc", }, { .compatible =3D "nvidia,tegra186-sor", }, @@ -1421,6 +1422,7 @@ static struct platform_driver * const drivers[] =3D { &tegra_gr3d_driver, &tegra_vic_driver, &tegra_nvdec_driver, + &tegra_nvjpg_driver, }; =20 static int __init host1x_drm_init(void) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 0b65e69f3a8add906e48b471804ad45bb3241455..64c8577720245564c421b85057a= 02419e235a6b6 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -212,5 +212,6 @@ extern struct platform_driver tegra_gr2d_driver; extern struct platform_driver tegra_gr3d_driver; extern struct platform_driver tegra_vic_driver; extern struct platform_driver tegra_nvdec_driver; +extern struct platform_driver tegra_nvjpg_driver; =20 #endif /* HOST1X_DRM_H */ diff --git a/drivers/gpu/drm/tegra/nvjpg.c b/drivers/gpu/drm/tegra/nvjpg.c new file mode 100644 index 0000000000000000000000000000000000000000..933f45806ba955da5dc6545e743= f5af65ff7282f --- /dev/null +++ b/drivers/gpu/drm/tegra/nvjpg.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drm.h" +#include "falcon.h" + +struct nvjpg_config { + const char *firmware; + unsigned int version; +}; + +struct nvjpg { + struct falcon falcon; + + void __iomem *regs; + struct tegra_drm_client client; + struct device *dev; + struct clk *clk; + + /* Platform configuration */ + const struct nvjpg_config *config; +}; + +static inline struct nvjpg *to_nvjpg(struct tegra_drm_client *client) +{ + return container_of(client, struct nvjpg, client); +} + +static int nvjpg_init(struct host1x_client *client) +{ + struct tegra_drm_client *drm =3D host1x_to_drm_client(client); + struct drm_device *dev =3D dev_get_drvdata(client->host); + struct tegra_drm *tegra =3D dev->dev_private; + struct nvjpg *nvjpg =3D to_nvjpg(drm); + int err; + + err =3D host1x_client_iommu_attach(client); + if (err < 0 && err !=3D -ENODEV) { + dev_err(nvjpg->dev, "failed to attach to domain: %d\n", err); + return err; + } + + err =3D tegra_drm_register_client(tegra, drm); + if (err < 0) + goto detach; + + /* + * Inherit the DMA parameters (such as maximum segment size) from the + * parent host1x device. + */ + client->dev->dma_parms =3D client->host->dma_parms; + + return 0; + +detach: + host1x_client_iommu_detach(client); + + return err; +} + +static int nvjpg_exit(struct host1x_client *client) +{ + struct tegra_drm_client *drm =3D host1x_to_drm_client(client); + struct drm_device *dev =3D dev_get_drvdata(client->host); + struct tegra_drm *tegra =3D dev->dev_private; + struct nvjpg *nvjpg =3D to_nvjpg(drm); + int err; + + /* avoid a dangling pointer just in case this disappears */ + client->dev->dma_parms =3D NULL; + + err =3D tegra_drm_unregister_client(tegra, drm); + if (err < 0) + return err; + + pm_runtime_dont_use_autosuspend(client->dev); + pm_runtime_force_suspend(client->dev); + + host1x_client_iommu_detach(client); + + if (client->group) { + dma_unmap_single(nvjpg->dev, nvjpg->falcon.firmware.phys, + nvjpg->falcon.firmware.size, DMA_TO_DEVICE); + tegra_drm_free(tegra, nvjpg->falcon.firmware.size, + nvjpg->falcon.firmware.virt, + nvjpg->falcon.firmware.iova); + } else { + dma_free_coherent(nvjpg->dev, nvjpg->falcon.firmware.size, + nvjpg->falcon.firmware.virt, + nvjpg->falcon.firmware.iova); + } + + return 0; +} + +static const struct host1x_client_ops nvjpg_client_ops =3D { + .init =3D nvjpg_init, + .exit =3D nvjpg_exit, +}; + +static int nvjpg_load_falcon_firmware(struct nvjpg *nvjpg) +{ + struct host1x_client *client =3D &nvjpg->client.base; + struct tegra_drm *tegra =3D nvjpg->client.drm; + dma_addr_t iova; + size_t size; + void *virt; + int err; + + if (nvjpg->falcon.firmware.virt) + return 0; + + err =3D falcon_read_firmware(&nvjpg->falcon, nvjpg->config->firmware); + if (err < 0) + return err; + + size =3D nvjpg->falcon.firmware.size; + + if (!client->group) { + virt =3D dma_alloc_coherent(nvjpg->dev, size, &iova, GFP_KERNEL); + + err =3D dma_mapping_error(nvjpg->dev, iova); + if (err < 0) + return err; + } else { + virt =3D tegra_drm_alloc(tegra, size, &iova); + if (IS_ERR(virt)) + return PTR_ERR(virt); + } + + nvjpg->falcon.firmware.virt =3D virt; + nvjpg->falcon.firmware.iova =3D iova; + + err =3D falcon_load_firmware(&nvjpg->falcon); + if (err < 0) + goto cleanup; + + /* + * In this case we have received an IOVA from the shared domain, so we + * need to make sure to get the physical address so that the DMA API + * knows what memory pages to flush the cache for. + */ + if (client->group) { + dma_addr_t phys; + + phys =3D dma_map_single(nvjpg->dev, virt, size, DMA_TO_DEVICE); + + err =3D dma_mapping_error(nvjpg->dev, phys); + if (err < 0) + goto cleanup; + + nvjpg->falcon.firmware.phys =3D phys; + } + + return 0; + +cleanup: + if (!client->group) + dma_free_coherent(nvjpg->dev, size, virt, iova); + else + tegra_drm_free(tegra, size, virt, iova); + + return err; +} + +static __maybe_unused int nvjpg_runtime_resume(struct device *dev) +{ + struct nvjpg *nvjpg =3D dev_get_drvdata(dev); + int err; + + err =3D clk_prepare_enable(nvjpg->clk); + if (err < 0) + return err; + + usleep_range(20, 30); + + err =3D nvjpg_load_falcon_firmware(nvjpg); + if (err < 0) + goto disable_clk; + + err =3D falcon_boot(&nvjpg->falcon); + if (err < 0) + goto disable_clk; + + return 0; + +disable_clk: + clk_disable_unprepare(nvjpg->clk); + return err; +} + +static __maybe_unused int nvjpg_runtime_suspend(struct device *dev) +{ + struct nvjpg *nvjpg =3D dev_get_drvdata(dev); + + clk_disable_unprepare(nvjpg->clk); + + return 0; +} + +static int nvjpg_can_use_memory_ctx(struct tegra_drm_client *client, bool = *supported) +{ + *supported =3D false; + + return 0; +} + +static const struct tegra_drm_client_ops nvjpg_ops =3D { + .get_streamid_offset =3D NULL, + .can_use_memory_ctx =3D nvjpg_can_use_memory_ctx, +}; +#define NVIDIA_TEGRA_210_NVJPG_FIRMWARE "nvidia/tegra210/nvjpg.bin" + +static const struct nvjpg_config nvjpg_t210_config =3D { + .firmware =3D NVIDIA_TEGRA_210_NVJPG_FIRMWARE, + .version =3D 0x21, +}; + +static const struct of_device_id tegra_nvjpg_of_match[] =3D { + { .compatible =3D "nvidia,tegra210-nvjpg", .data =3D &nvjpg_t210_config }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_nvjpg_of_match); + +static int nvjpg_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct nvjpg *nvjpg; + int err; + + /* inherit DMA mask from host1x parent */ + err =3D dma_coerce_mask_and_coherent(dev, *dev->parent->dma_mask); + if (err < 0) { + dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); + return err; + } + + nvjpg =3D devm_kzalloc(dev, sizeof(*nvjpg), GFP_KERNEL); + if (!nvjpg) + return -ENOMEM; + + nvjpg->config =3D of_device_get_match_data(dev); + + nvjpg->regs =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(nvjpg->regs)) + return PTR_ERR(nvjpg->regs); + + nvjpg->clk =3D devm_clk_get(dev, "nvjpg"); + if (IS_ERR(nvjpg->clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(nvjpg->clk); + } + + err =3D clk_set_rate(nvjpg->clk, ULONG_MAX); + if (err < 0) { + dev_err(&pdev->dev, "failed to set clock rate\n"); + return err; + } + + nvjpg->falcon.dev =3D dev; + nvjpg->falcon.regs =3D nvjpg->regs; + + err =3D falcon_init(&nvjpg->falcon); + if (err < 0) + return err; + + platform_set_drvdata(pdev, nvjpg); + + INIT_LIST_HEAD(&nvjpg->client.base.list); + nvjpg->client.base.ops =3D &nvjpg_client_ops; + nvjpg->client.base.dev =3D dev; + nvjpg->client.base.class =3D HOST1X_CLASS_NVJPG; + nvjpg->dev =3D dev; + + INIT_LIST_HEAD(&nvjpg->client.list); + nvjpg->client.version =3D nvjpg->config->version; + nvjpg->client.ops =3D &nvjpg_ops; + + err =3D host1x_client_register(&nvjpg->client.base); + if (err < 0) { + dev_err(dev, "failed to register host1x client: %d\n", err); + goto exit_falcon; + } + + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 500); + devm_pm_runtime_enable(dev); + + return 0; + +exit_falcon: + falcon_exit(&nvjpg->falcon); + + return err; +} + +static void nvjpg_remove(struct platform_device *pdev) +{ + struct nvjpg *nvjpg =3D platform_get_drvdata(pdev); + + host1x_client_unregister(&nvjpg->client.base); + falcon_exit(&nvjpg->falcon); +} + +static const struct dev_pm_ops nvjpg_pm_ops =3D { + RUNTIME_PM_OPS(nvjpg_runtime_suspend, nvjpg_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + +struct platform_driver tegra_nvjpg_driver =3D { + .driver =3D { + .name =3D "tegra-nvjpg", + .of_match_table =3D tegra_nvjpg_of_match, + .pm =3D &nvjpg_pm_ops + }, + .probe =3D nvjpg_probe, + .remove =3D nvjpg_remove, +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +MODULE_FIRMWARE(NVIDIA_TEGRA_210_NVJPG_FIRMWARE); +#endif --=20 2.49.0