From 9999710bcb1150ffbbf42b50f480683f35d4b2e6 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 16 Aug 2025 15:47:51 +0800 Subject: [PATCH 1/7] drm/etnaviv: add HWDB entry for GC620 r5552 c20b This is the 2D GPU found on the T-Head TH1520 SoC. Feature bits taken from the downstream kernel driver 6.4.6.9.354872. Signed-off-by: Icenowy Zheng Signed-off-by: Linux RISC-V bot --- drivers/gpu/drm/etnaviv/etnaviv_hwdb.c | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c b/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c index 8665f2658d51b3..6a56f1ab444498 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c @@ -69,6 +69,37 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = { .minor_features10 = 0x00000000, .minor_features11 = 0x00000000, }, + { + .model = 0x620, + .revision = 0x5552, + .product_id = 0x6200, + .customer_id = 0x20b, + .eco_id = 0, + .stream_count = 1, + .register_max = 64, + .thread_count = 256, + .shader_core_count = 1, + .vertex_cache_size = 8, + .vertex_output_buffer_size = 512, + .pixel_pipes = 1, + .instruction_count = 256, + .num_constants = 168, + .buffer_size = 0, + .varyings_count = 8, + .features = 0x001b4a40, + .minor_features0 = 0xa0600080, + .minor_features1 = 0x18050000, + .minor_features2 = 0x04f30000, + .minor_features3 = 0x00060005, + .minor_features4 = 0x20629000, + .minor_features5 = 0x0003380c, + .minor_features6 = 0x00000000, + .minor_features7 = 0x00001000, + .minor_features8 = 0x00000000, + .minor_features9 = 0x00000180, + .minor_features10 = 0x00004000, + .minor_features11 = 0x00000000, + }, { .model = 0x7000, .revision = 0x6202, From 8625f2e037f629d6589447ac48e8f9d9d0b739d9 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 16 Aug 2025 15:47:52 +0800 Subject: [PATCH 2/7] drm/etnaviv: add handle for GPUs with only SECURITY_AHB flag In the GC620 on T-Head TH1520 SoC, the SECURITY feature flag isn't set but the SECURITY_AHB feature flag is set. In this situation, the VIVS_MMUv2_AHB_CONTROL register isn't available, but the GPU otherwise behave like secure ones and require commands to load PTA. The 6.4.6.9.354872 driver from T-Head asserts SECURITY_AHB feature flag is set when SECURITY one is set, so it could be assumed that the situation that only SECURITY is set do not exist. Signed-off-by: Icenowy Zheng Signed-off-by: Linux RISC-V bot --- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index cf0d9049bcf1e3..7431e180b3ae4d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -559,7 +559,7 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu) control |= VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU; gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); - if (gpu->sec_mode == ETNA_SEC_KERNEL) { + if (gpu->identity.minor_features7 & chipMinorFeatures7_BIT_SECURITY) { gpu_write(gpu, VIVS_MMUv2_AHB_CONTROL, VIVS_MMUv2_AHB_CONTROL_RESET); } else { @@ -797,7 +797,7 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) gpu_write(gpu, VIVS_MC_BUS_CONFIG, bus_config); } - if (gpu->sec_mode == ETNA_SEC_KERNEL) { + if (gpu->identity.minor_features7 & chipMinorFeatures7_BIT_SECURITY) { u32 val = gpu_read(gpu, VIVS_MMUv2_AHB_CONTROL); val |= VIVS_MMUv2_AHB_CONTROL_NONSEC_ACCESS; gpu_write(gpu, VIVS_MMUv2_AHB_CONTROL, val); @@ -853,7 +853,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) * On cores with security features supported, we claim control over the * security states. */ - if ((gpu->identity.minor_features7 & chipMinorFeatures7_BIT_SECURITY) && + if ((gpu->identity.minor_features7 & chipMinorFeatures7_BIT_SECURITY) || (gpu->identity.minor_features10 & chipMinorFeatures10_SECURITY_AHB)) gpu->sec_mode = ETNA_SEC_KERNEL; From 4d85830a0de5fd9787c1d09add7a57239c929381 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 16 Aug 2025 15:47:53 +0800 Subject: [PATCH 3/7] drm/etnaviv: setup DEC400EX on GC620 r5552 The GC620 r5552 GPU found on T-Head TH1520 features (and requires) a DEC400EX buffer compressor that needs to be set up. In addition, some quirk exist for the DEC400 part that needs to be handled during GPU reset, otherwise the reset will not happen. Set the DEC400EX up and add the quirk code to the GPU reset codepath. Currently the DEC400EX setup is gated by this specific GPU identity, however in future we should add a feature flag for it. Signed-off-by: Icenowy Zheng Signed-off-by: Linux RISC-V bot --- drivers/gpu/drm/etnaviv/etnaviv_gpu.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 7431e180b3ae4d..a8d4394c8f6375 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -559,6 +559,10 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu) control |= VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU; gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control); + if (etnaviv_is_model_rev(gpu, 0x620, 0x5552)) { + gpu_write(gpu, VIVS_DEC400EX_UNK00800, 0x10); + } + if (gpu->identity.minor_features7 & chipMinorFeatures7_BIT_SECURITY) { gpu_write(gpu, VIVS_MMUv2_AHB_CONTROL, VIVS_MMUv2_AHB_CONTROL_RESET); @@ -797,6 +801,15 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu) gpu_write(gpu, VIVS_MC_BUS_CONFIG, bus_config); } + /* + * FIXME: Required by GC620 r5552 as a bug workaround, but might be + * useful on other GPUs with G2D_DEC400EX feature too. + */ + if (etnaviv_is_model_rev(gpu, 0x620, 0x5552)) { + gpu_write(gpu, VIVS_DEC400EX_UNK00800, 0x2010188); + gpu_write(gpu, VIVS_DEC400EX_UNK00808, 0x3fc104); + } + if (gpu->identity.minor_features7 & chipMinorFeatures7_BIT_SECURITY) { u32 val = gpu_read(gpu, VIVS_MMUv2_AHB_CONTROL); val |= VIVS_MMUv2_AHB_CONTROL_NONSEC_ACCESS; From a847009c727d6a5643cbabd093c1655a31556c36 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 16 Aug 2025 15:47:54 +0800 Subject: [PATCH 4/7] drm/etnaviv: protect whole iommuv2 ctx alloc func under global mutex As we are forced to use a global shared context on some PTA-equipped-but-broken GPUs, the fine-grained mutex locking in the current implemtnation of etnaviv_iommuv2_context_alloc() won't be meaningful any more. Make the whole function to be protected by the global lock, in order to prevent reentrance when allocating global shared context. Signed-off-by: Icenowy Zheng Signed-off-by: Linux RISC-V bot --- drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c index d664ae29ae2096..5654a604c70cfd 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c @@ -272,20 +272,18 @@ etnaviv_iommuv2_context_alloc(struct etnaviv_iommu_global *global) struct etnaviv_iommuv2_context *v2_context; struct etnaviv_iommu_context *context; + mutex_lock(&global->lock); + v2_context = vzalloc(sizeof(*v2_context)); if (!v2_context) - return NULL; + goto out_mutex_unlock; - mutex_lock(&global->lock); v2_context->id = find_first_zero_bit(global->v2.pta_alloc, ETNAVIV_PTA_ENTRIES); - if (v2_context->id < ETNAVIV_PTA_ENTRIES) { + if (v2_context->id < ETNAVIV_PTA_ENTRIES) set_bit(v2_context->id, global->v2.pta_alloc); - } else { - mutex_unlock(&global->lock); + else goto out_free; - } - mutex_unlock(&global->lock); v2_context->mtlb_cpu = dma_alloc_wc(global->dev, SZ_4K, &v2_context->mtlb_dma, GFP_KERNEL); @@ -304,11 +302,14 @@ etnaviv_iommuv2_context_alloc(struct etnaviv_iommu_global *global) INIT_LIST_HEAD(&context->mappings); drm_mm_init(&context->mm, SZ_4K, (u64)SZ_1G * 4 - SZ_4K); + mutex_unlock(&global->lock); return context; out_free_id: clear_bit(v2_context->id, global->v2.pta_alloc); out_free: vfree(v2_context); +out_mutex_unlock: + mutex_unlock(&global->lock); return NULL; } From 822728a746c2a3a5176303a2e74b8f3837409012 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 16 Aug 2025 15:47:55 +0800 Subject: [PATCH 5/7] drm/etnaviv: prepare for shared_context support for iommuv2 As we have some unfortunate GPUs with IOMMUv2 but broken PTA (reloading a different page table at runtime always fails), shared_context is now not a v1-only thing. Move it out of the v1 struct in the union. Signed-off-by: Icenowy Zheng Signed-off-by: Linux RISC-V bot --- drivers/gpu/drm/etnaviv/etnaviv_iommu.c | 8 ++++---- drivers/gpu/drm/etnaviv/etnaviv_mmu.h | 22 +++++++--------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c index afe5dd6a9925bf..6fdce63b9971a2 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu.c @@ -39,7 +39,7 @@ static void etnaviv_iommuv1_free(struct etnaviv_iommu_context *context) dma_free_wc(context->global->dev, PT_SIZE, v1_context->pgtable_cpu, v1_context->pgtable_dma); - context->global->v1.shared_context = NULL; + context->global->shared_context = NULL; kfree(v1_context); } @@ -136,8 +136,8 @@ etnaviv_iommuv1_context_alloc(struct etnaviv_iommu_global *global) * a stop the world operation, so we only support a single shared * context with this version. */ - if (global->v1.shared_context) { - context = global->v1.shared_context; + if (global->shared_context) { + context = global->shared_context; etnaviv_iommu_context_get(context); mutex_unlock(&global->lock); return context; @@ -163,7 +163,7 @@ etnaviv_iommuv1_context_alloc(struct etnaviv_iommu_global *global) mutex_init(&context->lock); INIT_LIST_HEAD(&context->mappings); drm_mm_init(&context->mm, GPU_MEM_START, PT_ENTRIES * SZ_4K); - context->global->v1.shared_context = context; + context->global->shared_context = context; mutex_unlock(&global->lock); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h index 7f8ac017854748..2ec4acda02bc60 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h @@ -49,21 +49,13 @@ struct etnaviv_iommu_global { u32 memory_base; - /* - * This union holds members needed by either MMUv1 or MMUv2, which - * can not exist at the same time. - */ - union { - struct { - struct etnaviv_iommu_context *shared_context; - } v1; - struct { - /* P(age) T(able) A(rray) */ - u64 *pta_cpu; - dma_addr_t pta_dma; - DECLARE_BITMAP(pta_alloc, ETNAVIV_PTA_ENTRIES); - } v2; - }; + struct etnaviv_iommu_context *shared_context; + struct { + /* P(age) T(able) A(rray) */ + u64 *pta_cpu; + dma_addr_t pta_dma; + DECLARE_BITMAP(pta_alloc, ETNAVIV_PTA_ENTRIES); + } v2; }; struct etnaviv_iommu_context { From a5f9990a2531715b0ca7617edd18ef8ccd91e201 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 16 Aug 2025 15:47:56 +0800 Subject: [PATCH 6/7] drm/etnaviv: add shared context support for iommuv2 Unfortunately the GC620 GPU seems to have broken PTA capibility, and switching page table ID in command stream after it's running won't work. As directly switching mtlb isn't working either, there will be no reliable way to switch page table in the command stream, and a shared context, like iommuv1, is needed. Add support for this shared context situation. Shared context is set when the broken PTA is known, and the context allocation code will be made short circuit when a shared context is set. Signed-off-by: Icenowy Zheng Signed-off-by: Linux RISC-V bot --- drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c | 8 ++++++++ drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 1 + drivers/gpu/drm/etnaviv/etnaviv_mmu.h | 2 ++ 3 files changed, 11 insertions(+) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c index 5654a604c70cfd..960ba3d659dc50 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c @@ -273,6 +273,12 @@ etnaviv_iommuv2_context_alloc(struct etnaviv_iommu_global *global) struct etnaviv_iommu_context *context; mutex_lock(&global->lock); + if (global->shared_context) { + context = global->shared_context; + etnaviv_iommu_context_get(context); + mutex_unlock(&global->lock); + return context; + } v2_context = vzalloc(sizeof(*v2_context)); if (!v2_context) @@ -301,6 +307,8 @@ etnaviv_iommuv2_context_alloc(struct etnaviv_iommu_global *global) mutex_init(&context->lock); INIT_LIST_HEAD(&context->mappings); drm_mm_init(&context->mm, SZ_4K, (u64)SZ_1G * 4 - SZ_4K); + if (global->v2.broken_pta) + global->shared_context = context; mutex_unlock(&global->lock); return context; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c index df5192083b2019..a0f9c950504e0b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c @@ -504,6 +504,7 @@ int etnaviv_iommu_global_init(struct etnaviv_gpu *gpu) memset32(global->bad_page_cpu, 0xdead55aa, SZ_4K / sizeof(u32)); if (version == ETNAVIV_IOMMU_V2) { + global->v2.broken_pta = gpu->identity.model == chipModel_GC620; global->v2.pta_cpu = dma_alloc_wc(dev, ETNAVIV_PTA_SIZE, &global->v2.pta_dma, GFP_KERNEL); if (!global->v2.pta_cpu) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h index 2ec4acda02bc60..5627d2a0d02377 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.h @@ -55,6 +55,8 @@ struct etnaviv_iommu_global { u64 *pta_cpu; dma_addr_t pta_dma; DECLARE_BITMAP(pta_alloc, ETNAVIV_PTA_ENTRIES); + /* Whether runtime switching page table ID will fail */ + bool broken_pta; } v2; }; From a333170f9a0052eb248f220e6df071f49a38a495 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sat, 16 Aug 2025 15:47:57 +0800 Subject: [PATCH 7/7] riscv: dts: thead: enable GC620 G2D on TH1520 The T-Head TH1520 SoC contains a GC620 2D graphics accelerator. Enable it in the devicetree to allow using etnaviv driver with it. This patch is currently very dirty because it relies on the bootloader leaving the clocks enabled, and the core clock is a fake one. Signed-off-by: Icenowy Zheng Signed-off-by: Linux RISC-V bot --- arch/riscv/boot/dts/thead/th1520.dtsi | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index 42724bf7e90e08..f33601dbf3d92a 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -225,6 +225,13 @@ #clock-cells = <0>; }; + gc620_cclk: clk-gc620-fake { + compatible = "fixed-clock"; + clock-frequency = <264000000>; + clock-output-names = "gc620_cclk"; + #clock-cells = <0>; + }; + stmmac_axi_config: stmmac-axi-config { snps,wr_osr_lmt = <15>; snps,rd_osr_lmt = <15>; @@ -493,6 +500,18 @@ status = "disabled"; }; + /* Vivante GC620, 2D only */ + g2d: gpu@ffecc80000 { + compatible = "vivante,gc"; + reg = <0xff 0xecc80000 0x0 0x40000>; + interrupt-parent = <&plic>; + interrupts = <101 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&gc620_cclk>; + clock-names = "core"; + status = "okay"; + }; + clk: clock-controller@ffef010000 { compatible = "thead,th1520-clk-ap"; reg = <0xff 0xef010000 0x0 0x1000>;