[PATCH 6/7] drm/tegra: vic: Add Tegra264 support

From: Mikko Perttunen

Date: Fri Jun 12 2026 - 02:34:15 EST


Add support for VIC on Tegra264. The Tegra264 VIC uses a RISC-V based
Falcon microcontroller instead of the traditional Falcon previously,
and has the TRANSCFG register in a different place.

The .version field is set to 0x264 rather than 0x26 to allow
distinguishing between different VIC capabilities between minor version
variations of some chips.

Signed-off-by: Mikko Perttunen <mperttunen@xxxxxxxxxx>
---
drivers/gpu/drm/tegra/drm.c | 1 +
drivers/gpu/drm/tegra/vic.c | 95 +++++++++++++++++++++++++++++++++------------
drivers/gpu/drm/tegra/vic.h | 9 ++---
3 files changed, 76 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 1dcef4e7d104..28245bf5ba5f 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -1396,6 +1396,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra194-nvdec", },
{ .compatible = "nvidia,tegra234-vic", },
{ .compatible = "nvidia,tegra234-nvdec", },
+ { .compatible = "nvidia,tegra264-vic", },
{ /* sentinel */ }
};

diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c
index 332c9b563d3f..46c7e00de347 100644
--- a/drivers/gpu/drm/tegra/vic.c
+++ b/drivers/gpu/drm/tegra/vic.c
@@ -8,6 +8,7 @@
#include <linux/dma-mapping.h>
#include <linux/host1x.h>
#include <linux/iommu.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -20,10 +21,16 @@
#include "falcon.h"
#include "vic.h"

+#define VIC_FALCON_DEBUGINFO 0x1094
+#define VIC_DEBUGINFO_DUMMY 0xabcd1234
+#define VIC_DEBUGINFO_CLEAR 0x0
+
struct vic_config {
const char *firmware;
unsigned int version;
bool supports_sid;
+ bool has_riscv;
+ unsigned int transcfg_offset;
};

struct vic {
@@ -54,8 +61,8 @@ static void vic_writel(struct vic *vic, u32 value, unsigned int offset)

static int vic_boot(struct vic *vic)
{
- u32 fce_ucode_size, fce_bin_data_offset, stream_id;
- void *hdr;
+ u32 stream_id;
+ u32 val;
int err = 0;

if (vic->config->supports_sid && tegra_dev_iommu_get_stream_id(vic->dev, &stream_id)) {
@@ -63,7 +70,7 @@ static int vic_boot(struct vic *vic)

value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) |
TRANSCFG_ATT(0, TRANSCFG_SID_HW);
- vic_writel(vic, value, VIC_TFBIF_TRANSCFG);
+ vic_writel(vic, value, vic->config->transcfg_offset);

/*
* STREAMID0 is used for input/output buffers. Initialize it to SID_VIC in case
@@ -85,31 +92,50 @@ static int vic_boot(struct vic *vic)
CG_WAKEUP_DLY_CNT(4),
NV_PVIC_MISC_PRI_VIC_CG);

+ if (vic->config->has_riscv) {
+ /* Write a known pattern into DEBUGINFO register */
+ vic_writel(vic, VIC_DEBUGINFO_DUMMY, VIC_FALCON_DEBUGINFO);
+ }
+
err = falcon_boot(&vic->falcon);
if (err < 0)
return err;

- hdr = vic->falcon.firmware.virt;
- fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET);
-
- /* Old VIC firmware needs kernel help with setting up FCE microcode. */
- if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
- hdr = vic->falcon.firmware.virt +
- *(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET);
- fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET);
-
- falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE,
- fce_ucode_size);
- falcon_execute_method(
- &vic->falcon, VIC_SET_FCE_UCODE_OFFSET,
- (vic->falcon.firmware.iova + fce_bin_data_offset) >> 8);
- }
+ if (vic->config->has_riscv) {
+ /* Check VIC has reached a proper initialized state */
+ err = readl_poll_timeout(vic->regs + VIC_FALCON_DEBUGINFO, val,
+ val == VIC_DEBUGINFO_CLEAR,
+ 1000, 2000000);
+ if (err) {
+ dev_err(vic->dev, "VIC not initialized, timeout, val=0x%x\n", val);
+ return err;
+ }
+ } else {
+ u32 fce_ucode_size, fce_bin_data_offset;
+ void *hdr;
+
+ hdr = vic->falcon.firmware.virt;
+ fce_bin_data_offset = *(u32 *)(hdr + VIC_UCODE_FCE_DATA_OFFSET);
+
+ /* Old VIC firmware needs kernel help with setting up FCE microcode. */
+ if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
+ hdr = vic->falcon.firmware.virt +
+ *(u32 *)(hdr + VIC_UCODE_FCE_HEADER_OFFSET);
+ fce_ucode_size = *(u32 *)(hdr + FCE_UCODE_SIZE_OFFSET);
+
+ falcon_execute_method(&vic->falcon, VIC_SET_FCE_UCODE_SIZE,
+ fce_ucode_size);
+ falcon_execute_method(
+ &vic->falcon, VIC_SET_FCE_UCODE_OFFSET,
+ (vic->falcon.firmware.iova + fce_bin_data_offset) >> 8);
+ }

- err = falcon_wait_idle(&vic->falcon);
- if (err < 0) {
- dev_err(vic->dev,
- "failed to set application ID and FCE base\n");
- return err;
+ err = falcon_wait_idle(&vic->falcon);
+ if (err < 0) {
+ dev_err(vic->dev,
+ "failed to set application ID and FCE base\n");
+ return err;
+ }
}

return 0;
@@ -277,6 +303,8 @@ static int vic_load_firmware(struct vic *vic)

if (!vic->config->supports_sid) {
vic->can_use_context = false;
+ } else if (vic->config->has_riscv) {
+ vic->can_use_context = true;
} else if (fce_bin_data_offset != 0x0 && fce_bin_data_offset != 0xa5a5a5a5) {
/*
* Firmware will access FCE through STREAMID0, so context
@@ -302,7 +330,6 @@ static int vic_load_firmware(struct vic *vic)
return err;
}

-
static int __maybe_unused vic_runtime_resume(struct device *dev)
{
struct vic *vic = dev_get_drvdata(dev);
@@ -417,6 +444,7 @@ static const struct vic_config vic_t186_config = {
.firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE,
.version = 0x18,
.supports_sid = true,
+ .transcfg_offset = 0x2044,
};

#define NVIDIA_TEGRA_194_VIC_FIRMWARE "nvidia/tegra194/vic.bin"
@@ -425,6 +453,7 @@ static const struct vic_config vic_t194_config = {
.firmware = NVIDIA_TEGRA_194_VIC_FIRMWARE,
.version = 0x19,
.supports_sid = true,
+ .transcfg_offset = 0x2044,
};

#define NVIDIA_TEGRA_234_VIC_FIRMWARE "nvidia/tegra234/vic.bin"
@@ -433,6 +462,18 @@ static const struct vic_config vic_t234_config = {
.firmware = NVIDIA_TEGRA_234_VIC_FIRMWARE,
.version = 0x23,
.supports_sid = true,
+ .transcfg_offset = 0x2044,
+};
+
+#define NVIDIA_TEGRA_264_VIC_FIRMWARE "nvidia/tegra264/vic.bin"
+#define NVIDIA_TEGRA_264_VIC_DESC "nvidia/tegra264/vic.bin.desc"
+
+static const struct vic_config vic_t264_config = {
+ .firmware = NVIDIA_TEGRA_264_VIC_FIRMWARE,
+ .version = 0x264,
+ .supports_sid = true,
+ .has_riscv = true,
+ .transcfg_offset = 0x2244,
};

static const struct of_device_id tegra_vic_of_match[] = {
@@ -441,6 +482,7 @@ static const struct of_device_id tegra_vic_of_match[] = {
{ .compatible = "nvidia,tegra186-vic", .data = &vic_t186_config },
{ .compatible = "nvidia,tegra194-vic", .data = &vic_t194_config },
{ .compatible = "nvidia,tegra234-vic", .data = &vic_t234_config },
+ { .compatible = "nvidia,tegra264-vic", .data = &vic_t264_config },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_vic_of_match);
@@ -495,6 +537,7 @@ static int vic_probe(struct platform_device *pdev)

vic->falcon.dev = dev;
vic->falcon.regs = vic->regs;
+ vic->falcon.riscv = vic->config->has_riscv;

err = falcon_init(&vic->falcon);
if (err < 0)
@@ -571,3 +614,7 @@ MODULE_FIRMWARE(NVIDIA_TEGRA_194_VIC_FIRMWARE);
#if IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
MODULE_FIRMWARE(NVIDIA_TEGRA_234_VIC_FIRMWARE);
#endif
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_264_SOC)
+MODULE_FIRMWARE(NVIDIA_TEGRA_264_VIC_FIRMWARE);
+MODULE_FIRMWARE(NVIDIA_TEGRA_264_VIC_DESC);
+#endif
diff --git a/drivers/gpu/drm/tegra/vic.h b/drivers/gpu/drm/tegra/vic.h
index acf35aac948b..e525a06daaba 100644
--- a/drivers/gpu/drm/tegra/vic.h
+++ b/drivers/gpu/drm/tegra/vic.h
@@ -21,11 +21,10 @@
#define CG_IDLE_CG_EN (1 << 6)
#define CG_WAKEUP_DLY_CNT(val) ((val & 0xf) << 16)

-#define VIC_TFBIF_TRANSCFG 0x00002044
-#define TRANSCFG_ATT(i, v) (((v) & 0x3) << (i * 4))
-#define TRANSCFG_SID_HW 0
-#define TRANSCFG_SID_PHY 1
-#define TRANSCFG_SID_FALCON 2
+#define TRANSCFG_ATT(i, v) (((v) & 0x3) << (i * 4))
+#define TRANSCFG_SID_HW 0
+#define TRANSCFG_SID_PHY 1
+#define TRANSCFG_SID_FALCON 2

/* Firmware offsets */


--
2.53.0