From f78a0607c8bf6912943527175db61996436d5792 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 27 Apr 2026 12:04:57 +0200 Subject: [PATCH 01/71] dma-buf: heaps: Export mem_accounting parameter The mem_accounting kernel parameter is used by heaps to know if they should account allocations in their respective cgroup controllers. Since we're going to allow heaps to compile as modules, we need to export that variable. Reviewed-by: T.J. Mercier Acked-by: Andrew Davis Signed-off-by: Maxime Ripard Signed-off-by: Sumit Semwal Link: https://patch.msgid.link/20260427-dma-buf-heaps-as-modules-v5-1-b6f5678feefc@kernel.org --- drivers/dma-buf/dma-heap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c index ac5f8685a649..a76bf3f8b071 100644 --- a/drivers/dma-buf/dma-heap.c +++ b/drivers/dma-buf/dma-heap.c @@ -53,6 +53,7 @@ bool __read_mostly mem_accounting; module_param(mem_accounting, bool, 0444); MODULE_PARM_DESC(mem_accounting, "Enable cgroup-based memory accounting for dma-buf heap allocations (default=false)."); +EXPORT_SYMBOL_NS_GPL(mem_accounting, "DMA_BUF_HEAP"); static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, u32 fd_flags, From 10bb37fc624cfcc1d10e0ebfc1b829401abba91e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 27 Apr 2026 12:04:58 +0200 Subject: [PATCH 02/71] dma-buf: heaps: cma: Turn the heap into a module All the symbols used by the CMA heap are already exported, so turning it into a module is straightforward. We only need to add the usual MODULE_* macros, import the proper namespaces and change the Kconfig symbol to a tristate. This heap won't be able to unload though, since we're missing a lot of infrastructure to make it safe. Reviewed-by: T.J. Mercier Acked-by: Andrew Davis Signed-off-by: Maxime Ripard Signed-off-by: Sumit Semwal Link: https://patch.msgid.link/20260427-dma-buf-heaps-as-modules-v5-2-b6f5678feefc@kernel.org --- drivers/dma-buf/heaps/Kconfig | 2 +- drivers/dma-buf/heaps/cma_heap.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig index a5eef06c4226..aed0b9b4febf 100644 --- a/drivers/dma-buf/heaps/Kconfig +++ b/drivers/dma-buf/heaps/Kconfig @@ -6,7 +6,7 @@ config DMABUF_HEAPS_SYSTEM is backed by pages from the buddy allocator. If in doubt, say Y. config DMABUF_HEAPS_CMA - bool "DMA-BUF CMA Heap" + tristate "DMA-BUF CMA Heap" depends on DMABUF_HEAPS && DMA_CMA help Choose this option to enable dma-buf CMA heap. This heap is backed diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c index a359aac45579..3fb4b946c91a 100644 --- a/drivers/dma-buf/heaps/cma_heap.c +++ b/drivers/dma-buf/heaps/cma_heap.c @@ -420,3 +420,6 @@ static int __init add_cma_heaps(void) } module_init(add_cma_heaps); MODULE_DESCRIPTION("DMA-BUF CMA Heap"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("DMA_BUF"); +MODULE_IMPORT_NS("DMA_BUF_HEAP"); From fd55edff8a0abe1b198af9a4280bfb208b9d2ab7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 27 Apr 2026 12:04:59 +0200 Subject: [PATCH 03/71] dma-buf: heaps: system: Turn the heap into a module The system heap can be easily turned into a module by adding the usual MODULE_* macros, importing the proper namespaces and changing the Kconfig symbol to a tristate. This heap won't be able to unload though, since we're missing a lot of infrastructure to make it safe. Reviewed-by: T.J. Mercier Acked-by: Andrew Davis Signed-off-by: Maxime Ripard Signed-off-by: Sumit Semwal Link: https://patch.msgid.link/20260427-dma-buf-heaps-as-modules-v5-3-b6f5678feefc@kernel.org --- drivers/dma-buf/heaps/Kconfig | 2 +- drivers/dma-buf/heaps/system_heap.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig index aed0b9b4febf..e273fb18feca 100644 --- a/drivers/dma-buf/heaps/Kconfig +++ b/drivers/dma-buf/heaps/Kconfig @@ -1,5 +1,5 @@ config DMABUF_HEAPS_SYSTEM - bool "DMA-BUF System Heap" + tristate "DMA-BUF System Heap" depends on DMABUF_HEAPS help Choose this option to enable the system dmabuf heap. The system heap diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c index 03c2b87cb111..c92bdec356fc 100644 --- a/drivers/dma-buf/heaps/system_heap.c +++ b/drivers/dma-buf/heaps/system_heap.c @@ -537,3 +537,8 @@ static int __init system_heap_create(void) return 0; } module_init(system_heap_create); + +MODULE_DESCRIPTION("DMA-BUF System Heap"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("DMA_BUF"); +MODULE_IMPORT_NS("DMA_BUF_HEAP"); From 4e3099285a644d0ef64f1d50b682c305284d7c40 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 27 Apr 2026 12:05:00 +0200 Subject: [PATCH 04/71] arm64: defconfig: Enable dma-buf heaps Now that the system and CMA heaps can be built as modules, enable both as modules in the arm64 defconfig. Acked-by: Andrew Davis Signed-off-by: Maxime Ripard Signed-off-by: Sumit Semwal Link: https://patch.msgid.link/20260427-dma-buf-heaps-as-modules-v5-4-b6f5678feefc@kernel.org --- arch/arm64/configs/defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index d905a0777f93..f33193b50e6b 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1411,6 +1411,9 @@ CONFIG_RZ_DMAC=y CONFIG_TI_K3_UDMA=y CONFIG_TI_K3_UDMA_GLUE_LAYER=y CONFIG_STM32_DMA3=m +CONFIG_DMABUF_HEAPS=y +CONFIG_DMABUF_HEAPS_SYSTEM=m +CONFIG_DMABUF_HEAPS_CMA=m CONFIG_VFIO=y CONFIG_VFIO_PCI=y CONFIG_VIRTIO_PCI=y From 31ac8899d1a9284fb50bd42c409f224788220e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20de=20Bretagne?= Date: Wed, 20 May 2026 18:40:05 +0200 Subject: [PATCH 05/71] drm/panel-edp: Add LG LP129WT232166 panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an entry for the eDP LG LP129WT232166 panel used in the Microsoft Surface Pro 9 5G. edid-decode (hex): 00 ff ff ff ff ff ff 00 30 e4 b2 06 a1 25 10 00 00 1f 01 04 a5 1b 12 78 01 ef 70 a7 51 4c a8 26 0e 4f 53 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fd 00 18 78 f1 f1 48 01 0a 20 20 20 20 20 20 00 00 00 fe 00 4c 47 44 5f 4d 50 31 2e 30 5f 0a 20 20 00 00 00 fe 00 4c 50 31 32 39 57 54 32 33 32 31 36 36 01 23 70 13 79 00 00 03 01 14 56 16 01 88 3f 0b 4f 00 07 80 1f 00 7f 07 55 00 47 00 07 00 03 01 14 56 16 01 08 3f 0b 4f 00 07 80 1f 00 7f 07 2b 08 47 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2c 90 Signed-off-by: Jérôme de Bretagne Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260520-surface-sp9-5g-for-next-v1-1-9df52552bf87@gmail.com --- drivers/gpu/drm/panel/panel-edp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index cb7d6c43466e..aa27d6cd932e 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -2134,6 +2134,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('L', 'G', 'D', 0x0567, &delay_200_500_e200_d200, "Unknown"), EDP_PANEL_ENTRY('L', 'G', 'D', 0x05af, &delay_200_500_e200_d200, "Unknown"), EDP_PANEL_ENTRY('L', 'G', 'D', 0x05f1, &delay_200_500_e200_d200, "Unknown"), + EDP_PANEL_ENTRY('L', 'G', 'D', 0x06b2, &delay_200_500_e200_d200, "LP129WT232166"), EDP_PANEL_ENTRY('L', 'G', 'D', 0x0778, &delay_200_500_e200_d200, "134WT1"), EDP_PANEL_ENTRY('L', 'G', 'D', 0x07fe, &delay_200_500_e200_d200, "LP116WHA-SPB1"), From 3a2be32a681764f20601a8d9692d4cd5fed5dcbd Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Tue, 19 May 2026 17:08:21 +0100 Subject: [PATCH 06/71] dt-bindings: display: renesas,rzg2l-du: Refuse port@1 for RZ/G2UL The RZ/G2UL DU supports only a single port@0 DPI. Explicitly refuse port@1 in the ports node. Reviewed-by: Laurent Pinchart Acked-by: Rob Herring (Arm) Signed-off-by: Tommaso Merciai Signed-off-by: Lad Prabhakar Link: https://patch.msgid.link/20260519160825.4082566-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Biju Das --- Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml index 2cc66dcef870..5add3b832eab 100644 --- a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml +++ b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml @@ -102,6 +102,7 @@ allOf: properties: port@0: description: DPI + port@1: false required: - port@0 From 4608f1ce7a4cc53973bfc6a7fc7999e53fbb0309 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 19 May 2026 17:08:22 +0100 Subject: [PATCH 07/71] dt-bindings: display: renesas,rzg2l-du: Add RZ/T2H and RZ/N2H support Document the Display Unit (DU) support for the RZ/T2H and RZ/N2H SoCs. The DU block on RZ/T2H is functionally equivalent to the RZ/G2UL DU and supports the DPI interface, but includes SoC-specific register differences and has no reset control. Add a dedicated compatible string to represent this variant and update the allOf constraints accordingly. As the DU implementation on RZ/N2H matches RZ/T2H, describe it using an RZ/N2H specific compatible string with the RZ/T2H compatible as fallback. Signed-off-by: Lad Prabhakar Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260519160825.4082566-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Biju Das --- .../bindings/display/renesas,rzg2l-du.yaml | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml index 5add3b832eab..7c84a9ecc7a7 100644 --- a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml +++ b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml @@ -21,6 +21,7 @@ properties: - renesas,r9a07g043u-du # RZ/G2UL - renesas,r9a07g044-du # RZ/G2{L,LC} - renesas,r9a09g057-du # RZ/V2H(P) + - renesas,r9a09g077-du # RZ/T2H - items: - enum: - renesas,r9a07g054-du # RZ/V2L @@ -28,6 +29,9 @@ properties: - items: - const: renesas,r9a09g056-du # RZ/V2N - const: renesas,r9a09g057-du # RZ/V2H(P) fallback + - items: + - const: renesas,r9a09g087-du # RZ/N2H + - const: renesas,r9a09g077-du # RZ/T2H fallback reg: maxItems: 1 @@ -83,7 +87,6 @@ required: - interrupts - clocks - clock-names - - resets - power-domains - ports - renesas,vsps @@ -95,7 +98,9 @@ allOf: properties: compatible: contains: - const: renesas,r9a07g043u-du + enum: + - renesas,r9a07g043u-du + - renesas,r9a09g077-du then: properties: ports: @@ -138,6 +143,17 @@ allOf: required: - port@0 + - if: + properties: + compatible: + contains: + const: renesas,r9a09g077-du + then: + properties: + resets: false + else: + required: + - resets examples: # RZ/G2L DU From 62ca607fcb1f442e909e6729fa9e1e7a18e6ae5d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 19 May 2026 17:08:23 +0100 Subject: [PATCH 08/71] drm: renesas: rz-du: Make DU reset control optional for RZ/T2H support Update the DU CRTC initialisation to request the reset control using devm_reset_control_get_optional_shared(). On RZ/T2H SoCs the DU block does not expose a reset line, and treating the reset as mandatory prevents the driver from probing on those platforms. Signed-off-by: Lad Prabhakar Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260519160825.4082566-4-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Biju Das --- drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c index 26b95153ce88..48065f4952a3 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c @@ -380,7 +380,7 @@ int rzg2l_du_crtc_create(struct rzg2l_du_device *rcdu) struct drm_plane *primary; int ret; - rcrtc->rstc = devm_reset_control_get_shared(rcdu->dev, NULL); + rcrtc->rstc = devm_reset_control_get_optional_shared(rcdu->dev, NULL); if (IS_ERR(rcrtc->rstc)) { dev_err(rcdu->dev, "can't get cpg reset\n"); return PTR_ERR(rcrtc->rstc); From 332d72b9513cee25682108b5b37fe9fe9468ba73 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 19 May 2026 17:08:24 +0100 Subject: [PATCH 09/71] drm: renesas: rz-du: Move mode_valid logic to per-SoC clock limits Move pixel clock validation from a fixed encoder check to per SoC constraints stored in rzg2l_du_device_info. Pixel clock limits differ across SoCs in the RZ DU family and cannot be expressed by a single shared rule. For example, RZ/G2UL and RZ/G2L limit the DPAD0 pixel clock to a narrow window, while other SoCs such as RZ/T2H require a wider operating range. Add mode_clock_min and mode_clock_max fields to rzg2l_du_device_info to describe the supported pixel clock range for each SoC. Update rzg2l_du_encoder_mode_valid() to check these bounds when evaluating DPAD0 outputs, returning MODE_CLOCK_LOW when the pixel clock falls below mode_clock_min and MODE_CLOCK_HIGH when it exceeds mode_clock_max. Populate the pixel clock limits for both the RZ/G2UL (R9A07G043U) and RZ/G2L (R9A07G044) variants to a minimum of 20875 kHz and a maximum of 83500 kHz. Signed-off-by: Lad Prabhakar Reviewed-by: Biju Das Link: https://patch.msgid.link/20260519160825.4082566-5-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Biju Das --- drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c | 6 +++++- drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h | 4 ++++ drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c | 9 ++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c index 0fef33a5a089..1e4b9f38c55b 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c @@ -35,6 +35,8 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g043u_info = { .port = 0, }, }, + .mode_clock_min = 20875, + .mode_clock_max = 83500, }; static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = { @@ -48,7 +50,9 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = { .possible_outputs = BIT(0), .port = 1, } - } + }, + .mode_clock_min = 20875, + .mode_clock_max = 83500, }; static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = { diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h index 58806c2a8f2b..885558eb9547 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h @@ -44,10 +44,14 @@ struct rzg2l_du_output_routing { * struct rzg2l_du_device_info - DU model-specific information * @channels_mask: bit mask of available DU channels * @routes: array of CRTC to output routes, indexed by output (RZG2L_DU_OUTPUT_*) + * @mode_clock_min: minimum pixel clock in kHz + * @mode_clock_max: maximum pixel clock in kHz */ struct rzg2l_du_device_info { unsigned int channels_mask; struct rzg2l_du_output_routing routes[RZG2L_DU_OUTPUT_MAX]; + u32 mode_clock_min; + u32 mode_clock_max; }; #define RZG2L_DU_MAX_CRTCS 1 diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c index 0e567b57a408..f50d166b764f 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c @@ -50,8 +50,15 @@ rzg2l_du_encoder_mode_valid(struct drm_encoder *encoder, const struct drm_display_mode *mode) { struct rzg2l_du_encoder *renc = to_rzg2l_encoder(encoder); + struct rzg2l_du_device *rcdu = to_rzg2l_du_device(renc->base.dev); + const struct rzg2l_du_device_info *info = rcdu->info; - if (renc->output == RZG2L_DU_OUTPUT_DPAD0 && mode->clock > 83500) + if (renc->output != RZG2L_DU_OUTPUT_DPAD0) + return MODE_OK; + + if (mode->clock < info->mode_clock_min) + return MODE_CLOCK_LOW; + if (mode->clock > info->mode_clock_max) return MODE_CLOCK_HIGH; return MODE_OK; From 4f2e625f3d5d27242f0f18ba106b9442d29a51ad Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 19 May 2026 17:08:25 +0100 Subject: [PATCH 10/71] drm: renesas: rz-du: Add support for RZ/T2H SoC The RZ/T2H (R9A09G077) SoC includes a DU with a DPI interface, supporting resolutions up to WXGA with two RPFs for layer blending. Unlike earlier RZ/G2L SoCs, RZ/T2H requires explicit assertion of a DPI output-enable signal (DU_MCR0_DPI_EN) during CRTC startup. Signed-off-by: Lad Prabhakar Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260519160825.4082566-6-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Biju Das --- drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c | 7 ++++++- drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c | 14 ++++++++++++++ drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h | 10 ++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c index 48065f4952a3..d0f01aa642a7 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c @@ -28,6 +28,7 @@ #include "rzg2l_du_vsp.h" #define DU_MCR0 0x00 +#define DU_MCR0_DPI_EN BIT(0) #define DU_MCR0_DI_EN BIT(8) #define DU_DITR0 0x10 @@ -217,8 +218,12 @@ static void rzg2l_du_crtc_put(struct rzg2l_du_crtc *rcrtc) static void rzg2l_du_start_stop(struct rzg2l_du_crtc *rcrtc, bool start) { struct rzg2l_du_device *rcdu = rcrtc->dev; + u32 val = DU_MCR0_DI_EN; - writel(start ? DU_MCR0_DI_EN : 0, rcdu->mmio + DU_MCR0); + if (start && rzg2l_du_has(rcdu, RZG2L_DU_FEATURE_DPIO_OE)) + val |= DU_MCR0_DPI_EN; + + writel(start ? val : 0, rcdu->mmio + DU_MCR0); } static void rzg2l_du_crtc_start(struct rzg2l_du_crtc *rcrtc) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c index 1e4b9f38c55b..3d13f61d3c97 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c @@ -65,10 +65,24 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = { }, }; +static const struct rzg2l_du_device_info rzg2l_du_r9a09g077_info = { + .channels_mask = BIT(0), + .routes = { + [RZG2L_DU_OUTPUT_DPAD0] = { + .possible_outputs = BIT(0), + .port = 0, + }, + }, + .features = RZG2L_DU_FEATURE_DPIO_OE, + .mode_clock_min = 5000, + .mode_clock_max = 100000, +}; + static const struct of_device_id rzg2l_du_of_table[] = { { .compatible = "renesas,r9a07g043u-du", .data = &rzg2l_du_r9a07g043u_info }, { .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info }, { .compatible = "renesas,r9a09g057-du", .data = &rzg2l_du_r9a09g057_info }, + { .compatible = "renesas,r9a09g077-du", .data = &rzg2l_du_r9a09g077_info }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h index 885558eb9547..baf076d69cda 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h @@ -20,6 +20,8 @@ struct device; struct drm_property; +#define RZG2L_DU_FEATURE_DPIO_OE BIT(0) /* Has DPIO output enable control */ + enum rzg2l_du_output { RZG2L_DU_OUTPUT_DSI0, RZG2L_DU_OUTPUT_DPAD0, @@ -46,12 +48,14 @@ struct rzg2l_du_output_routing { * @routes: array of CRTC to output routes, indexed by output (RZG2L_DU_OUTPUT_*) * @mode_clock_min: minimum pixel clock in kHz * @mode_clock_max: maximum pixel clock in kHz + * @features: device features (RZG2L_DU_FEATURE_*) */ struct rzg2l_du_device_info { unsigned int channels_mask; struct rzg2l_du_output_routing routes[RZG2L_DU_OUTPUT_MAX]; u32 mode_clock_min; u32 mode_clock_max; + unsigned int features; }; #define RZG2L_DU_MAX_CRTCS 1 @@ -77,6 +81,12 @@ static inline struct rzg2l_du_device *to_rzg2l_du_device(struct drm_device *dev) return container_of(dev, struct rzg2l_du_device, ddev); } +static inline bool rzg2l_du_has(struct rzg2l_du_device *rcdu, + unsigned int feature) +{ + return rcdu->info->features & feature; +} + const char *rzg2l_du_output_name(enum rzg2l_du_output output); #endif /* __RZG2L_DU_DRV_H__ */ From 9637867d78a7bc5e76b17fe6cb0be5e054fce973 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 01:56:09 +0800 Subject: [PATCH 11/71] drm: verisilicon: add max cursor size to HWDB Different display controller variants support different maximum cursor size. All known DC8200 variants support both 32x32 and 64x64, but some DC8000 variants support either only 32x32 or up to 256x256. The minimum size is fixed at 32 and only PoT square sizes are supported. Add the max cursor size field to HWDB and fill all entries with 64. Signed-off-by: Icenowy Zheng Reviewed-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260506175610.2542888-2-zhengxingda@iscas.ac.cn --- drivers/gpu/drm/verisilicon/vs_hwdb.c | 4 ++++ drivers/gpu/drm/verisilicon/vs_hwdb.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c b/drivers/gpu/drm/verisilicon/vs_hwdb.c index 09336af0900a..2a0f7c59afa3 100644 --- a/drivers/gpu/drm/verisilicon/vs_hwdb.c +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c @@ -95,6 +95,7 @@ static struct vs_chip_identity vs_chip_identities[] = { .customer_id = ~0U, .display_count = 2, + .max_cursor_size = 64, .formats = &vs_formats_no_yuv444, }, { @@ -103,6 +104,7 @@ static struct vs_chip_identity vs_chip_identities[] = { .customer_id = 0x30B, .display_count = 2, + .max_cursor_size = 64, .formats = &vs_formats_no_yuv444, }, { @@ -111,6 +113,7 @@ static struct vs_chip_identity vs_chip_identities[] = { .customer_id = 0x310, .display_count = 2, + .max_cursor_size = 64, .formats = &vs_formats_with_yuv444, }, { @@ -119,6 +122,7 @@ static struct vs_chip_identity vs_chip_identities[] = { .customer_id = 0x311, .display_count = 2, + .max_cursor_size = 64, .formats = &vs_formats_no_yuv444, }, }; diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h b/drivers/gpu/drm/verisilicon/vs_hwdb.h index 92192e4fa086..2065ecb73043 100644 --- a/drivers/gpu/drm/verisilicon/vs_hwdb.h +++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h @@ -20,6 +20,11 @@ struct vs_chip_identity { u32 customer_id; u32 display_count; + /* + * The hardware only supports square cursor planes, so this field + * is both the maximum width and height in pixels. + */ + int32_t max_cursor_size; const struct vs_formats *formats; }; From 8c4ae218912554ef427440293cdb97319d17ca5d Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 7 May 2026 01:56:10 +0800 Subject: [PATCH 12/71] drm: verisilicon: add support for cursor planes Verisilicon display controllers support hardware cursors per output port. Add support for them as cursor planes. Signed-off-by: Icenowy Zheng Reviewed-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260506175610.2542888-3-zhengxingda@iscas.ac.cn --- drivers/gpu/drm/verisilicon/Makefile | 3 +- drivers/gpu/drm/verisilicon/vs_crtc.c | 11 +- drivers/gpu/drm/verisilicon/vs_cursor_plane.c | 272 ++++++++++++++++++ .../drm/verisilicon/vs_cursor_plane_regs.h | 44 +++ drivers/gpu/drm/verisilicon/vs_plane.h | 1 + 5 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/verisilicon/vs_cursor_plane.c create mode 100644 drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile index fd8d805fbcde..426f4bcaa834 100644 --- a/drivers/gpu/drm/verisilicon/Makefile +++ b/drivers/gpu/drm/verisilicon/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o vs_hwdb.o vs_plane.o vs_primary_plane.o +verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o vs_hwdb.o \ + vs_plane.o vs_primary_plane.o vs_cursor_plane.o obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c index 9080344398ca..0b8a35d09cd2 100644 --- a/drivers/gpu/drm/verisilicon/vs_crtc.c +++ b/drivers/gpu/drm/verisilicon/vs_crtc.c @@ -159,7 +159,7 @@ struct vs_crtc *vs_crtc_init(struct drm_device *drm_dev, struct vs_dc *dc, unsigned int output) { struct vs_crtc *vcrtc; - struct drm_plane *primary; + struct drm_plane *primary, *cursor; int ret; vcrtc = drmm_kzalloc(drm_dev, sizeof(*vcrtc), GFP_KERNEL); @@ -175,9 +175,16 @@ struct vs_crtc *vs_crtc_init(struct drm_device *drm_dev, struct vs_dc *dc, return ERR_PTR(PTR_ERR(primary)); } + /* Create our cursor plane */ + cursor = vs_cursor_plane_init(drm_dev, dc); + if (IS_ERR(cursor)) { + drm_err(drm_dev, "Couldn't create the cursor plane\n"); + return ERR_CAST(cursor); + } + ret = drmm_crtc_init_with_planes(drm_dev, &vcrtc->base, primary, - NULL, + cursor, &vs_crtc_funcs, NULL); if (ret) { diff --git a/drivers/gpu/drm/verisilicon/vs_cursor_plane.c b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c new file mode 100644 index 000000000000..acb9854fa67e --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS) + * + * Authors: + * Icenowy Zheng + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vs_crtc.h" +#include "vs_plane.h" +#include "vs_dc.h" +#include "vs_hwdb.h" +#include "vs_cursor_plane_regs.h" + +#define VSDC_MIN_CURSOR_SIZE 32 +#define VSDC_MAX_CURSOR_SIZE 256 + +#define VSDC_CURSOR_LOCATION_MAX_POSITIVE BIT_MASK(15) +#define VSDC_CURSOR_LOCATION_MAX_NEGATIVE BIT_MASK(5) + +static bool vs_cursor_plane_check_coord(int32_t coord) +{ + if (coord >= 0) + return coord <= VSDC_CURSOR_LOCATION_MAX_POSITIVE; + else + return (-coord) <= VSDC_CURSOR_LOCATION_MAX_NEGATIVE; +} + +static int vs_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_framebuffer *fb = new_plane_state->fb; + struct drm_crtc_state *crtc_state = NULL; + struct vs_crtc *vcrtc; + struct vs_dc *dc; + int ret; + + if (crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + ret = drm_atomic_helper_check_plane_state(new_plane_state, + crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); + if (ret) + return ret; + + if (!new_plane_state->visible) + return 0; /* Skip validity check */ + + vcrtc = drm_crtc_to_vs_crtc(crtc); + dc = vcrtc->dc; + + /* Only certain PoT square sizes is supported. */ + if (!is_power_of_2(new_plane_state->crtc_w) || + new_plane_state->crtc_w < VSDC_MIN_CURSOR_SIZE || + new_plane_state->crtc_w > dc->identity.max_cursor_size) + return -EINVAL; + + if (new_plane_state->crtc_w != new_plane_state->crtc_h) + return -EINVAL; + + /* Check if the cursor is inside the register fields' range */ + if (!vs_cursor_plane_check_coord(new_plane_state->crtc_x) || + !vs_cursor_plane_check_coord(new_plane_state->crtc_y)) + return -EINVAL; + + /* Extra line padding isn't supported */ + if (fb->pitches[0] != + drm_format_info_min_pitch(fb->format, 0, new_plane_state->crtc_w)) + return -EINVAL; + + return 0; +} + +static void vs_cursor_plane_commit(struct vs_dc *dc, unsigned int output) +{ + regmap_set_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_COMMIT | + VSDC_CURSOR_CONFIG_IMG_UPDATE); +} + +static void vs_cursor_plane_atomic_enable(struct drm_plane *plane, + struct drm_atomic_state *atomic_state) +{ + struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state, + plane); + struct drm_crtc *crtc = state->crtc; + struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); + unsigned int output = vcrtc->id; + struct vs_dc *dc = vcrtc->dc; + + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_FMT_MASK, + VSDC_CURSOR_CONFIG_FMT_ARGB8888); + + vs_cursor_plane_commit(dc, output); +} + +static void vs_cursor_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *atomic_state) +{ + struct drm_plane_state *state = drm_atomic_get_old_plane_state(atomic_state, + plane); + struct drm_crtc *crtc = state->crtc; + struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc); + unsigned int output = vcrtc->id; + struct vs_dc *dc = vcrtc->dc; + + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_FMT_MASK, + VSDC_CURSOR_CONFIG_FMT_OFF); + + vs_cursor_plane_commit(dc, output); +} + +static void vs_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *atomic_state) +{ + struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state, + plane); + struct drm_framebuffer *fb = state->fb; + struct drm_crtc *crtc = state->crtc; + struct vs_dc *dc; + struct vs_crtc *vcrtc; + unsigned int output; + dma_addr_t dma_addr; + + if (!state->visible) { + vs_cursor_plane_atomic_disable(plane, atomic_state); + return; + } + + vcrtc = drm_crtc_to_vs_crtc(crtc); + output = vcrtc->id; + dc = vcrtc->dc; + + /* Other sizes should be rejected by atomic_check */ + switch (state->crtc_w) { + case 32: + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_SIZE_MASK, + VSDC_CURSOR_CONFIG_SIZE_32); + break; + case 64: + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_SIZE_MASK, + VSDC_CURSOR_CONFIG_SIZE_64); + break; + case 128: + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_SIZE_MASK, + VSDC_CURSOR_CONFIG_SIZE_128); + break; + case 256: + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_SIZE_MASK, + VSDC_CURSOR_CONFIG_SIZE_256); + break; + } + + dma_addr = vs_fb_get_dma_addr(fb, &state->src); + + regmap_write(dc->regs, VSDC_CURSOR_ADDRESS(output), + lower_32_bits(dma_addr)); + + /* + * The X_OFF and Y_OFF fields define which point does the LOCATION + * register represent in the cursor image, and LOCATION register + * values are unsigned. To for positive left-top coordinates the + * offset is set to 0 and the location is set to the coordinate, for + * negative coordinates the location is set to 0 and the offset + * is set to the opposite number of the coordinate to offset the + * cursor image partly off-screen. + */ + if (state->crtc_x >= 0) { + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_X_OFF_MASK, 0); + regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), + VSDC_CURSOR_LOCATION_X_MASK, + VSDC_CURSOR_LOCATION_X(state->crtc_x)); + } else { + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_X_OFF_MASK, + -state->crtc_x); + regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), + VSDC_CURSOR_LOCATION_X_MASK, 0); + } + + if (state->crtc_y >= 0) { + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_Y_OFF_MASK, 0); + regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), + VSDC_CURSOR_LOCATION_Y_MASK, + VSDC_CURSOR_LOCATION_Y(state->crtc_y)); + } else { + regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output), + VSDC_CURSOR_CONFIG_Y_OFF_MASK, + -state->crtc_y); + regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output), + VSDC_CURSOR_LOCATION_Y_MASK, 0); + } + + vs_cursor_plane_commit(dc, output); +} + +static const struct drm_plane_helper_funcs vs_cursor_plane_helper_funcs = { + .atomic_check = vs_cursor_plane_atomic_check, + .atomic_update = vs_cursor_plane_atomic_update, + .atomic_enable = vs_cursor_plane_atomic_enable, + .atomic_disable = vs_cursor_plane_atomic_disable, +}; + +static const struct drm_plane_funcs vs_cursor_plane_funcs = { + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = drm_atomic_helper_plane_reset, + .update_plane = drm_atomic_helper_update_plane, +}; + +static const u32 vs_cursor_plane_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +static const u64 vs_cursor_plane_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID, /* sentinel */ +}; + +struct drm_plane *vs_cursor_plane_init(struct drm_device *drm_dev, + struct vs_dc *dc) +{ + int32_t max_cursor_size = dc->identity.max_cursor_size; + struct drm_plane *plane; + + if (drm_WARN_ON_ONCE(drm_dev, max_cursor_size < VSDC_MIN_CURSOR_SIZE || + max_cursor_size > VSDC_MAX_CURSOR_SIZE)) + return ERR_PTR(-EINVAL); + + plane = drmm_universal_plane_alloc(drm_dev, struct drm_plane, dev, 0, + &vs_cursor_plane_funcs, + vs_cursor_plane_formats, + ARRAY_SIZE(vs_cursor_plane_formats), + vs_cursor_plane_modifiers, + DRM_PLANE_TYPE_CURSOR, + NULL); + + if (IS_ERR(plane)) + return plane; + + drm_plane_helper_add(plane, &vs_cursor_plane_helper_funcs); + + return plane; +} diff --git a/drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h b/drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h new file mode 100644 index 000000000000..99693f2c95b9 --- /dev/null +++ b/drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2025 Icenowy Zheng + * + * Based on vs_dc_hw.h, which is: + * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd. + */ + +#ifndef _VS_CURSOR_PLANE_REGS_H_ +#define _VS_CURSOR_PLANE_REGS_H_ + +#include + +#define VSDC_CURSOR_CONFIG(n) (0x1468 + 0x1080 * (n)) +#define VSDC_CURSOR_CONFIG_FMT_MASK GENMASK(1, 0) +#define VSDC_CURSOR_CONFIG_FMT_ARGB8888 (0x2 << 0) +#define VSDC_CURSOR_CONFIG_FMT_OFF (0x0 << 0) +#define VSDC_CURSOR_CONFIG_IMG_UPDATE BIT(2) +#define VSDC_CURSOR_CONFIG_COMMIT BIT(3) +#define VSDC_CURSOR_CONFIG_SIZE_MASK GENMASK(7, 5) +#define VSDC_CURSOR_CONFIG_SIZE_32 (0x0 << 5) +#define VSDC_CURSOR_CONFIG_SIZE_64 (0x1 << 5) +#define VSDC_CURSOR_CONFIG_SIZE_128 (0x2 << 5) +#define VSDC_CURSOR_CONFIG_SIZE_256 (0x3 << 5) +#define VSDC_CURSOR_CONFIG_Y_OFF_MASK GENMASK(12, 8) +#define VSDC_CURSOR_CONFIG_Y_OFF(v) ((v) << 8) +#define VSDC_CURSOR_CONFIG_X_OFF_MASK GENMASK(20, 16) +#define VSDC_CURSOR_CONFIG_X_OFF(v) ((v) << 16) + +#define VSDC_CURSOR_ADDRESS(n) (0x146C + 0x1080 * (n)) + +#define VSDC_CURSOR_LOCATION(n) (0x1470 + 0x1080 * (n)) +#define VSDC_CURSOR_LOCATION_X_MASK GENMASK(14, 0) +#define VSDC_CURSOR_LOCATION_X(v) ((v) << 0) +#define VSDC_CURSOR_LOCATION_Y_MASK GENMASK(30, 16) +#define VSDC_CURSOR_LOCATION_Y(v) ((v) << 16) + +#define VSDC_CURSOR_BACKGROUND(n) (0x1474 + 0x1080 * (n)) +#define VSDC_CURSOR_BACKGRUOND_DEFAULT 0x00FFFFFF + +#define VSDC_CURSOR_FOREGROUND(n) (0x1478 + 0x1080 * (n)) +#define VSDC_CURSOR_FOREGRUOND_DEFAULT 0x00AAAAAA + +#endif /* _VS_CURSOR_PLANE_REGS_H_ */ diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h index f18c36ef4cd9..32ed1d70f3b8 100644 --- a/drivers/gpu/drm/verisilicon/vs_plane.h +++ b/drivers/gpu/drm/verisilicon/vs_plane.h @@ -84,5 +84,6 @@ void vs_plane_destroy_state(struct drm_plane *plane, void vs_plane_reset(struct drm_plane *plane); struct drm_plane *vs_primary_plane_init(struct drm_device *dev, struct vs_dc *dc); +struct drm_plane *vs_cursor_plane_init(struct drm_device *dev, struct vs_dc *dc); #endif /* _VS_PLANE_H_ */ From 213c92ac9717e4951f052a499f91c89302889813 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Thu, 21 May 2026 09:29:30 -0700 Subject: [PATCH 13/71] Revert "accel/amdxdna: Support read-only user-pointer BO mappings" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f649e63d4a6423eda8eb208638849fd6396aedd7. The read-only feature requires further consideration. Signed-off-by: Lizhi Hou Acked-by: Christian König Link: https://patch.msgid.link/20260521162930.1451042-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_ubuf.c | 29 ++-------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c index 3769210c55cc..4c0647057759 100644 --- a/drivers/accel/amdxdna/amdxdna_ubuf.c +++ b/drivers/accel/amdxdna/amdxdna_ubuf.c @@ -125,26 +125,6 @@ static const struct dma_buf_ops amdxdna_ubuf_dmabuf_ops = { .vunmap = amdxdna_ubuf_vunmap, }; -static int readonly_va_entry(struct amdxdna_drm_va_entry *va_ent) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - int ret; - - mmap_read_lock(mm); - - vma = find_vma(mm, va_ent->vaddr); - if (!vma || - vma->vm_start > va_ent->vaddr || - vma->vm_end - va_ent->vaddr < va_ent->len) - ret = -ENOENT; - else - ret = vma->vm_flags & VM_WRITE ? 0 : 1; - - mmap_read_unlock(mm); - return ret; -} - struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, u32 num_entries, void __user *va_entries) { @@ -154,7 +134,6 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, struct amdxdna_ubuf_priv *ubuf; u32 npages, start = 0; struct dma_buf *dbuf; - bool readonly = true; int i, ret; DEFINE_DMA_BUF_EXPORT_INFO(exp_info); @@ -193,10 +172,6 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, ret = -EINVAL; goto free_ent; } - - /* Pin pages as writable as long as not all entries are read-only. */ - if (readonly && readonly_va_entry(&va_ent[i]) != 1) - readonly = false; } ubuf->nr_pages = exp_info.size >> PAGE_SHIFT; @@ -219,7 +194,7 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, npages = va_ent[i].len >> PAGE_SHIFT; ret = pin_user_pages_fast(va_ent[i].vaddr, npages, - (readonly ? 0 : FOLL_WRITE) | FOLL_LONGTERM, + FOLL_WRITE | FOLL_LONGTERM, &ubuf->pages[start]); if (ret >= 0) { start += ret; @@ -236,7 +211,7 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, exp_info.ops = &amdxdna_ubuf_dmabuf_ops; exp_info.priv = ubuf; - exp_info.flags = (readonly ? O_RDONLY : O_RDWR) | O_CLOEXEC; + exp_info.flags = O_RDWR | O_CLOEXEC; dbuf = dma_buf_export(&exp_info); if (IS_ERR(dbuf)) { From 0d8e8229f020b5960ee7d959958cb9c9d616351d Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Fri, 22 May 2026 11:32:09 +0200 Subject: [PATCH 14/71] accel/ivpu: Document why full JSM message size is always used Firmware expects IPC messages to always carry the full fixed sizeof(struct vpu_jsm_msg) size. Sending the full struct also ensures unused fields are zeroed, which maintains compatibility when existing commands are extended with new fields in the future. Replace the misleading TODO comment with an explanation of the actual intent. Reviewed-by: Dawid Osuchowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260522093209.1169716-1-karol.wachowski@linux.intel.com --- drivers/accel/ivpu/ivpu_ipc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c index f47df092bb0d..9f1ba0c57442 100644 --- a/drivers/accel/ivpu/ivpu_ipc.c +++ b/drivers/accel/ivpu/ivpu_ipc.c @@ -97,7 +97,11 @@ ivpu_ipc_tx_prepare(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, memset(tx_buf, 0, sizeof(*tx_buf)); tx_buf->ipc.data_addr = jsm_vpu_addr; - /* TODO: Set data_size to actual JSM message size, not union of all messages */ + /* + * Firmware expects full JSM message size regardless of the payload size. + * Unused fields must be zeroed to allow future extensions of existing + * commands without breaking compatibility. + */ tx_buf->ipc.data_size = sizeof(*req); tx_buf->ipc.channel = cons->channel; tx_buf->ipc.src_node = 0; From ce44b78512e9102aea54ff6b6e521d6c8de9f31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Wed, 20 May 2026 12:16:13 +0200 Subject: [PATCH 15/71] drm/exec: Remove the index parameter from drm_exec_for_each_locked_obj[_reverse] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nobody makes any use of it. Possible internal future users can instead use the _index variable. External users shouldn't use it since the array it's pointing into is internal drm_exec state. v2: - Use a unique id for the loop variable (Christian) Assisted-by: GitHub Copilot:claude-sonnet-4.6 Signed-off-by: Thomas Hellström Reviewed-by: Christian König Link: https://patch.msgid.link/20260520101616.41284-2-thomas.hellstrom@linux.intel.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 9 +++------ .../drm/amd/amdgpu/amdgpu_eviction_fence.c | 3 +-- drivers/gpu/drm/drm_exec.c | 6 ++---- drivers/gpu/drm/drm_gpuvm.c | 3 +-- drivers/gpu/drm/xe/xe_vm.c | 3 +-- include/drm/drm_exec.h | 20 +++++++++++-------- 6 files changed, 20 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 32af8cce3df8..97a851ae7bd8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -850,7 +850,6 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_bo_list_entry *e; struct drm_gem_object *obj; - unsigned long index; unsigned int i; int r; @@ -961,7 +960,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, goto out_free_user_pages; } - drm_exec_for_each_locked_object(&p->exec, index, obj) { + drm_exec_for_each_locked_object(&p->exec, obj) { r = amdgpu_cs_bo_validate(p, gem_to_amdgpu_bo(obj)); if (unlikely(r)) goto out_free_user_pages; @@ -1201,7 +1200,6 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) struct drm_gpu_scheduler *sched; struct drm_gem_object *obj; struct dma_fence *fence; - unsigned long index; unsigned int i; int r; @@ -1212,7 +1210,7 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) return r; } - drm_exec_for_each_locked_object(&p->exec, index, obj) { + drm_exec_for_each_locked_object(&p->exec, obj) { struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); struct dma_resv *resv = bo->tbo.base.resv; @@ -1281,7 +1279,6 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, struct amdgpu_job *leader = p->gang_leader; struct amdgpu_bo_list_entry *e; struct drm_gem_object *gobj; - unsigned long index; unsigned int i; uint64_t seq; int r; @@ -1331,7 +1328,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, } p->fence = dma_fence_get(&leader->base.s_fence->finished); - drm_exec_for_each_locked_object(&p->exec, index, gobj) { + drm_exec_for_each_locked_object(&p->exec, gobj) { ttm_bo_move_to_lru_tail_unlocked(&gem_to_amdgpu_bo(gobj)->tbo); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c index 4c5e38dea4c2..f6b7522c3c82 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c @@ -121,7 +121,6 @@ int amdgpu_evf_mgr_rearm(struct amdgpu_eviction_fence_mgr *evf_mgr, { struct amdgpu_eviction_fence *ev_fence; struct drm_gem_object *obj; - unsigned long index; /* Create and initialize a new eviction fence */ ev_fence = kzalloc_obj(*ev_fence); @@ -140,7 +139,7 @@ int amdgpu_evf_mgr_rearm(struct amdgpu_eviction_fence_mgr *evf_mgr, evf_mgr->ev_fence = &ev_fence->base; /* And add it to all existing BOs */ - drm_exec_for_each_locked_object(exec, index, obj) { + drm_exec_for_each_locked_object(exec, obj) { struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj); amdgpu_evf_mgr_attach_fence(evf_mgr, bo); diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index 8d0601400182..746210f3f6c2 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -24,7 +24,6 @@ * * struct drm_gem_object *obj; * struct drm_exec exec; - * unsigned long index; * int ret; * * drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); @@ -40,7 +39,7 @@ * goto error; * } * - * drm_exec_for_each_locked_object(&exec, index, obj) { + * drm_exec_for_each_locked_object(&exec, obj) { * dma_resv_add_fence(obj->resv, fence, DMA_RESV_USAGE_READ); * ... * } @@ -56,9 +55,8 @@ static void drm_exec_unlock_all(struct drm_exec *exec) { struct drm_gem_object *obj; - unsigned long index; - drm_exec_for_each_locked_object_reverse(exec, index, obj) { + drm_exec_for_each_locked_object_reverse(exec, obj) { dma_resv_unlock(obj->resv); drm_gem_object_put(obj); } diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c index f56719e9f435..078cbc0235a4 100644 --- a/drivers/gpu/drm/drm_gpuvm.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -1553,9 +1553,8 @@ drm_gpuvm_resv_add_fence(struct drm_gpuvm *gpuvm, enum dma_resv_usage extobj_usage) { struct drm_gem_object *obj; - unsigned long index; - drm_exec_for_each_locked_object(exec, index, obj) { + drm_exec_for_each_locked_object(exec, obj) { dma_resv_assert_held(obj->resv); dma_resv_add_fence(obj->resv, fence, drm_gpuvm_is_extobj(gpuvm, obj) ? diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 43a578d9c067..250dc4063a07 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -373,7 +373,6 @@ int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec, unsigned int num_fences) { struct drm_gem_object *obj; - unsigned long index; int ret; do { @@ -386,7 +385,7 @@ int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec, return ret; } while (!list_empty(&vm->gpuvm.evict.list)); - drm_exec_for_each_locked_object(exec, index, obj) { + drm_exec_for_each_locked_object(exec, obj) { ret = dma_resv_reserve_fences(obj->resv, num_fences); if (ret) return ret; diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index aa786b828a0a..dee6ebdbe416 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -65,31 +65,35 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) return index < exec->num_objects ? exec->objects[index] : NULL; } +/* Helper for drm_exec_for_each_locked_object(). Internal use only. */ +#define __drm_exec_for_each_locked_object(exec, obj, __index) \ + for (unsigned long __index = 0; ((obj) = drm_exec_obj(exec, __index)); ++__index) /** * drm_exec_for_each_locked_object - iterate over all the locked objects * @exec: drm_exec object - * @index: unsigned long index for the iteration * @obj: the current GEM object * * Iterate over all the locked GEM objects inside the drm_exec object. */ -#define drm_exec_for_each_locked_object(exec, index, obj) \ - for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index)) +#define drm_exec_for_each_locked_object(exec, obj) \ + __drm_exec_for_each_locked_object(exec, obj, __UNIQUE_ID(drm_exec)) +/* Helper for drm_exec_for_each_locked_object_reverse(). Internal use only. */ +#define __drm_exec_for_each_locked_object_reverse(exec, obj, __index) \ + for (unsigned long __index = (exec)->num_objects - 1; \ + ((obj) = drm_exec_obj(exec, __index)); --__index) /** * drm_exec_for_each_locked_object_reverse - iterate over all the locked * objects in reverse locking order * @exec: drm_exec object - * @index: unsigned long index for the iteration * @obj: the current GEM object * * Iterate over all the locked GEM objects inside the drm_exec object in - * reverse locking order. Note that @index may go below zero and wrap, + * reverse locking order. Note that the internal index may wrap around, * but that will be caught by drm_exec_obj(), returning a NULL object. */ -#define drm_exec_for_each_locked_object_reverse(exec, index, obj) \ - for ((index) = (exec)->num_objects - 1; \ - ((obj) = drm_exec_obj(exec, index)); --(index)) +#define drm_exec_for_each_locked_object_reverse(exec, obj) \ + __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec)) /** * drm_exec_until_all_locked - loop until all GEM objects are locked From 25331d9d40d093dfe2805ab558e8ad238bd2a2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Wed, 20 May 2026 12:16:14 +0200 Subject: [PATCH 16/71] drm/exec: Make the drm_exec_until_all_locked() macro more readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use __UNIQUE_ID as done elsewhere in the kernel rather than a hand-rolled __PASTE to craft a unique id. Also use __maybe_unused rather than (void) to signify that a variable, althrough written to, may not actually be used. v2: - Move assignment to declaration (Christian) - Declare the retry pointer as void *const. Signed-off-by: Thomas Hellström Reviewed-by: Christian König Link: https://patch.msgid.link/20260520101616.41284-3-thomas.hellstrom@linux.intel.com --- include/drm/drm_exec.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index dee6ebdbe416..18f84faabbb9 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -95,6 +95,17 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) #define drm_exec_for_each_locked_object_reverse(exec, obj) \ __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec)) +/* + * Helper to drm_exec_until_all_locked(). Don't use directly. + * + * Since labels can't be defined local to the loop's body we use a jump pointer + * to make sure that the retry is only used from within the loop's body. + */ +#define __drm_exec_until_all_locked(exec, _label) \ +_label: \ + for (void *const __maybe_unused __drm_exec_retry_ptr = &&_label; \ + drm_exec_cleanup(exec);) + /** * drm_exec_until_all_locked - loop until all GEM objects are locked * @exec: drm_exec object @@ -102,17 +113,9 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index) * Core functionality of the drm_exec object. Loops until all GEM objects are * locked and no more contention exists. At the beginning of the loop it is * guaranteed that no GEM object is locked. - * - * Since labels can't be defined local to the loops body we use a jump pointer - * to make sure that the retry is only used from within the loops body. */ #define drm_exec_until_all_locked(exec) \ -__PASTE(__drm_exec_, __LINE__): \ - for (void *__drm_exec_retry_ptr; ({ \ - __drm_exec_retry_ptr = &&__PASTE(__drm_exec_, __LINE__);\ - (void)__drm_exec_retry_ptr; \ - drm_exec_cleanup(exec); \ - });) + __drm_exec_until_all_locked(exec, __UNIQUE_ID(drm_exec)) /** * drm_exec_retry_on_contention - restart the loop to grap all locks From bba175a2d4be400312a64bd3d1e0481ef28a1681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Wed, 20 May 2026 12:16:15 +0200 Subject: [PATCH 17/71] drm/exec, drm/xe: Avoid abusing the drm_exec retry pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xe driver was using the drm_exec retry pointer directly to restart the locking loop after out-of-memory errors. This is relying on undocumented behaviour. Instead add a drm_exec_retry() macro that can be used in this situation, and that also warns if the struct drm_exec is not newly (re-)initialized. Use that macro in xe. v2: - Only allow if the drm_exec context is newly initialized. (Christian) Signed-off-by: Thomas Hellström Reviewed-by: Christian König Link: https://patch.msgid.link/20260520101616.41284-4-thomas.hellstrom@linux.intel.com --- drivers/gpu/drm/drm_exec.c | 3 --- drivers/gpu/drm/xe/xe_validation.h | 2 +- include/drm/drm_exec.h | 19 +++++++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index 746210f3f6c2..7988f5e7d56a 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -48,9 +48,6 @@ * See struct dma_exec for more details. */ -/* Dummy value used to initially enter the retry loop */ -#define DRM_EXEC_DUMMY ((void *)~0) - /* Unlock all objects and drop references */ static void drm_exec_unlock_all(struct drm_exec *exec) { diff --git a/drivers/gpu/drm/xe/xe_validation.h b/drivers/gpu/drm/xe/xe_validation.h index a30e732c4d51..4cd955ce6cd2 100644 --- a/drivers/gpu/drm/xe/xe_validation.h +++ b/drivers/gpu/drm/xe/xe_validation.h @@ -146,7 +146,7 @@ bool xe_validation_should_retry(struct xe_validation_ctx *ctx, int *ret); #define xe_validation_retry_on_oom(_ctx, _ret) \ do { \ if (xe_validation_should_retry(_ctx, _ret)) \ - goto *__drm_exec_retry_ptr; \ + drm_exec_retry((_ctx)->exec); \ } while (0) /** diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index 18f84faabbb9..99c7e1bb3c5b 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -9,6 +9,12 @@ #define DRM_EXEC_INTERRUPTIBLE_WAIT BIT(0) #define DRM_EXEC_IGNORE_DUPLICATES BIT(1) +/* + * Dummy value used to initially enter the retry loop. + * internal use only. + */ +#define DRM_EXEC_DUMMY ((void *)~0) + struct drm_gem_object; /** @@ -142,6 +148,19 @@ static inline bool drm_exec_is_contended(struct drm_exec *exec) return !!exec->contended; } +/** + * drm_exec_retry() - Unconditionally restart the loop to grab all locks. + * @exec: drm_exec object + * + * Unconditionally retry the loop to lock all objects. For consistency, + * the exec object needs to be newly initialized. + */ +#define drm_exec_retry(_exec) \ + do { \ + WARN_ON((_exec)->contended != DRM_EXEC_DUMMY); \ + goto *__drm_exec_retry_ptr; \ + } while (0) + void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr); void drm_exec_fini(struct drm_exec *exec); bool drm_exec_cleanup(struct drm_exec *exec); From b34bafc38cd9573694f5049bb308387ba8d8c042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= Date: Wed, 20 May 2026 12:16:16 +0200 Subject: [PATCH 18/71] drm/exec, drm/xe, drm/amdgpu: Add an accessor for struct drm_exec::ticket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers were accessing this drm_exec member directly. While that may seem harmless, it will require action if the drm_exec utility is made a subclass of a dma-resv transaction utility as outlined in the cover-letter. Provide an accessor, drm_exec_ticket() to avoid that. v2: - Fix amdgpu compile error (Intel CI) - Update the commit message. Signed-off-by: Thomas Hellström Reviewed-by: Christian König Link: https://patch.msgid.link/20260520101616.41284-5-thomas.hellstrom@linux.intel.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 4 ++-- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 6 +++--- drivers/gpu/drm/xe/xe_validation.c | 4 ++-- include/drm/drm_exec.h | 11 +++++++++++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 72a5a29e63f6..d54794e5b18b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -2999,7 +2999,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu * /* Validate PDs, PTs and evicted DMABuf imports last. Otherwise BO * validations above would invalidate DMABuf imports again. */ - ret = process_validate_vms(process_info, &exec.ticket); + ret = process_validate_vms(process_info, drm_exec_ticket(&exec)); if (ret) { pr_debug("Validating VMs failed, ret: %d\n", ret); goto validate_map_fail; @@ -3040,7 +3040,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu * goto validate_map_fail; } - ret = amdgpu_vm_handle_moved(adev, peer_vm, &exec.ticket); + ret = amdgpu_vm_handle_moved(adev, peer_vm, drm_exec_ticket(&exec)); if (ret) { dev_dbg(adev->dev, "Memory eviction: handle moved failed, pid %8d. Try again.\n", diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 97a851ae7bd8..6ada57abce9d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1157,7 +1157,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) return r; } - r = amdgpu_vm_handle_moved(adev, vm, &p->exec.ticket); + r = amdgpu_vm_handle_moved(adev, vm, drm_exec_ticket(&p->exec)); if (r) return r; @@ -1359,7 +1359,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, cs->out.handle = seq; leader->uf_sequence = seq; - amdgpu_vm_bo_trace_cs(&fpriv->vm, &p->exec.ticket); + amdgpu_vm_bo_trace_cs(&fpriv->vm, drm_exec_ticket(&p->exec)); for (i = 0; i < p->gang_size; ++i) { amdgpu_job_free_resources(p->jobs[i]); trace_amdgpu_cs_ioctl(p->jobs[i]); @@ -1794,7 +1794,7 @@ int amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser, *map = mapping; /* Double check that the BO is reserved by this CS */ - if (dma_resv_locking_ctx((*bo)->tbo.base.resv) != &parser->exec.ticket) + if (dma_resv_locking_ctx((*bo)->tbo.base.resv) != drm_exec_ticket(&parser->exec)) return -EINVAL; /* Make sure VRAM is allocated contigiously */ diff --git a/drivers/gpu/drm/xe/xe_validation.c b/drivers/gpu/drm/xe/xe_validation.c index a611438eaafe..8dff4d0ec895 100644 --- a/drivers/gpu/drm/xe/xe_validation.c +++ b/drivers/gpu/drm/xe/xe_validation.c @@ -156,7 +156,7 @@ int xe_validation_ctx_init(struct xe_validation_ctx *ctx, struct xe_validation_d #ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH /* - * This abuses both drm_exec and ww_mutex internals and should be + * This abuses ww_mutex internals and should be * replaced by checking for -EDEADLK when we can make TTM * stop converting -EDEADLK to -ENOMEM. * An alternative is to not have exhaustive eviction with @@ -164,7 +164,7 @@ int xe_validation_ctx_init(struct xe_validation_ctx *ctx, struct xe_validation_d */ static bool xe_validation_contention_injected(struct drm_exec *exec) { - return !!exec->ticket.contending_lock; + return !!drm_exec_ticket(exec)->contending_lock; } #else diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index 99c7e1bb3c5b..8725ba92ff91 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -161,6 +161,17 @@ static inline bool drm_exec_is_contended(struct drm_exec *exec) goto *__drm_exec_retry_ptr; \ } while (0) +/** + * drm_exec_ticket - return the ww_acquire_ctx for this exec context + * @exec: drm_exec object + * + * Return: Pointer to the ww_acquire_ctx embedded in @exec. + */ +static inline struct ww_acquire_ctx *drm_exec_ticket(struct drm_exec *exec) +{ + return &exec->ticket; +} + void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr); void drm_exec_fini(struct drm_exec *exec); bool drm_exec_cleanup(struct drm_exec *exec); From e07761b9364f66736de06e159dd88173d175df92 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 25 May 2026 23:36:18 +0800 Subject: [PATCH 19/71] drm: verisilicon: fix build failure of cursor plane code The cursor plane patch was stalled for a too long time that the struct drm_atomic_state parameter of atomic modeset hooks has been changed to struct drm_atomic_commit. Fix this by replacing the parameter's type. All helpers that retrieve information from this struct are also changed so simply replacing the type works. Fixes: 8c4ae2189125 ("drm: verisilicon: add support for cursor planes") Signed-off-by: Icenowy Zheng Reviewed-by: Thomas Zimmermann Link: https://patch.msgid.link/20260525153618.1336239-1-zhengxingda@iscas.ac.cn --- drivers/gpu/drm/verisilicon/vs_cursor_plane.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/verisilicon/vs_cursor_plane.c b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c index acb9854fa67e..fa4f601dd0c8 100644 --- a/drivers/gpu/drm/verisilicon/vs_cursor_plane.c +++ b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c @@ -40,7 +40,7 @@ static bool vs_cursor_plane_check_coord(int32_t coord) } static int vs_cursor_plane_atomic_check(struct drm_plane *plane, - struct drm_atomic_state *state) + struct drm_atomic_commit *state) { struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); @@ -98,7 +98,7 @@ static void vs_cursor_plane_commit(struct vs_dc *dc, unsigned int output) } static void vs_cursor_plane_atomic_enable(struct drm_plane *plane, - struct drm_atomic_state *atomic_state) + struct drm_atomic_commit *atomic_state) { struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state, plane); @@ -115,7 +115,7 @@ static void vs_cursor_plane_atomic_enable(struct drm_plane *plane, } static void vs_cursor_plane_atomic_disable(struct drm_plane *plane, - struct drm_atomic_state *atomic_state) + struct drm_atomic_commit *atomic_state) { struct drm_plane_state *state = drm_atomic_get_old_plane_state(atomic_state, plane); @@ -132,7 +132,7 @@ static void vs_cursor_plane_atomic_disable(struct drm_plane *plane, } static void vs_cursor_plane_atomic_update(struct drm_plane *plane, - struct drm_atomic_state *atomic_state) + struct drm_atomic_commit *atomic_state) { struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state, plane); From 8ee92e75052a93d55539af7042c2c480a0244d35 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 23 May 2026 12:40:58 +0200 Subject: [PATCH 20/71] drm/bridge: ite-it66121: Switch to the HDMI connector helpers Instead of open coding the HDMI AVI Infoframes buffer management, use the helpers provided by the HDMI connector framework. Also, add callbacks to implement HDMI Vendor Specific Infoframe and Audio InfoFrame support. The driver was not sending these before, but they are required when using the HDMI helpers. These were implemented following the IT66121 Programming Guide. Suggested-by: Maxime Ripard Reviewed-by: Maxime Ripard Link: https://patch.msgid.link/20260523-it66121-fix-dvi-mode-v5-v5-1-33b4468162f9@redhat.com Signed-off-by: Javier Martinez Canillas --- drivers/gpu/drm/bridge/Kconfig | 2 + drivers/gpu/drm/bridge/ite-it66121.c | 211 ++++++++++++++++++++------- 2 files changed, 162 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index f81b566c82a1..4a57d49b4c6d 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -205,6 +205,8 @@ config DRM_LONTIUM_LT8713SX config DRM_ITE_IT66121 tristate "ITE IT66121 HDMI bridge" depends on OF + select DRM_DISPLAY_HDMI_STATE_HELPER + select DRM_DISPLAY_HELPER select DRM_KMS_HELPER select REGMAP_I2C help diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 19e188fe6e3b..f41f51f300c9 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include #include @@ -161,6 +163,14 @@ #define IT66121_PKT_GEN_CTRL_ON BIT(0) #define IT66121_PKT_GEN_CTRL_RPT BIT(1) +#define IT66121_PKT_NULL_CTRL_REG 0xC9 +#define IT66121_PKT_NULL_CTRL_ON BIT(0) +#define IT66121_PKT_NULL_CTRL_RPT BIT(1) + +/* Null packet data registers (used for HDMI Vendor Specific InfoFrame) */ +#define IT66121_PKT_NULL_HB(n) (0x138 + (n)) +#define IT66121_PKT_NULL_PB(n) (0x13B + (n)) + #define IT66121_AVIINFO_DB1_REG 0x158 #define IT66121_AVIINFO_DB2_REG 0x159 #define IT66121_AVIINFO_DB3_REG 0x15A @@ -180,6 +190,13 @@ #define IT66121_AVI_INFO_PKT_ON BIT(0) #define IT66121_AVI_INFO_PKT_RPT BIT(1) +#define IT66121_AUD_INFO_PKT_REG 0xCE +#define IT66121_AUD_INFO_PKT_ON BIT(0) +#define IT66121_AUD_INFO_PKT_RPT BIT(1) + +#define IT66121_AUD_INFO_DB1_REG 0x168 +#define IT66121_AUD_INFO_CSUM_REG 0x16D + #define IT66121_HDMI_MODE_REG 0xC0 #define IT66121_HDMI_MODE_HDMI BIT(0) @@ -304,7 +321,6 @@ struct it66121_ctx { struct i2c_client *client; u32 bus_width; struct mutex lock; /* Protects fields below and device registers */ - struct hdmi_avi_infoframe hdmi_avi_infoframe; struct { u8 ch_enable; u8 fs; @@ -726,6 +742,10 @@ static void it66121_bridge_enable(struct drm_bridge *bridge, struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); ctx->connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!ctx->connector)) + return; + + drm_atomic_helper_connector_hdmi_update_infoframes(ctx->connector, state); it66121_set_mute(ctx, false); } @@ -763,40 +783,10 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { - u8 buf[HDMI_INFOFRAME_SIZE(AVI)]; struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); - int ret; mutex_lock(&ctx->lock); - ret = drm_hdmi_avi_infoframe_from_display_mode(&ctx->hdmi_avi_infoframe, ctx->connector, - adjusted_mode); - if (ret) { - DRM_ERROR("Failed to setup AVI infoframe: %d\n", ret); - goto unlock; - } - - ret = hdmi_avi_infoframe_pack(&ctx->hdmi_avi_infoframe, buf, sizeof(buf)); - if (ret < 0) { - DRM_ERROR("Failed to pack infoframe: %d\n", ret); - goto unlock; - } - - /* Write new AVI infoframe packet */ - ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG, - &buf[HDMI_INFOFRAME_HEADER_SIZE], - HDMI_AVI_INFOFRAME_SIZE); - if (ret) - goto unlock; - - if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3])) - goto unlock; - - /* Enable AVI infoframe */ - if (regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG, - IT66121_AVI_INFO_PKT_ON | IT66121_AVI_INFO_PKT_RPT)) - goto unlock; - /* Set TX mode to HDMI */ if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) goto unlock; @@ -824,24 +814,6 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, mutex_unlock(&ctx->lock); } -static enum drm_mode_status it66121_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_info *info, - const struct drm_display_mode *mode) -{ - struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); - unsigned long max_clock; - - max_clock = (ctx->bus_width == 12) ? 74250 : 148500; - - if (mode->clock > max_clock) - return MODE_CLOCK_HIGH; - - if (mode->clock < 25000) - return MODE_CLOCK_LOW; - - return MODE_OK; -} - static enum drm_connector_status it66121_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -872,6 +844,128 @@ static void it66121_bridge_hpd_disable(struct drm_bridge *bridge) dev_err(ctx->dev, "failed to disable HPD IRQ\n"); } +static enum drm_mode_status +it66121_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge, + const struct drm_display_mode *mode, + unsigned long long tmds_rate) +{ + const struct it66121_ctx *ctx = + container_of(bridge, const struct it66121_ctx, bridge); + unsigned long long max_rate; + + max_rate = (ctx->bus_width == 12) ? 74250000ULL : 148500000ULL; + + if (tmds_rate > max_rate) + return MODE_CLOCK_HIGH; + + if (tmds_rate < HDMI_TMDS_CHAR_RATE_MIN_HZ) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +static int it66121_bridge_hdmi_clear_avi_infoframe(struct drm_bridge *bridge) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + + /* Clear both IT66121_AVI_INFO_PKT_ON and IT66121_AVI_INFO_PKT_RPT */ + return regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG, 0); +} + +static int it66121_bridge_hdmi_write_avi_infoframe(struct drm_bridge *bridge, + const u8 *buffer, size_t len) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + int ret; + + mutex_lock(&ctx->lock); + + /* Write new AVI infoframe packet */ + ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG, + &buffer[HDMI_INFOFRAME_HEADER_SIZE], + HDMI_AVI_INFOFRAME_SIZE); + if (ret) + goto unlock; + + ret = regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buffer[3]); + if (ret) + goto unlock; + + /* Enable AVI infoframe */ + ret = regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG, + IT66121_AVI_INFO_PKT_ON | IT66121_AVI_INFO_PKT_RPT); + +unlock: + mutex_unlock(&ctx->lock); + return ret; +} + +static int it66121_bridge_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + + /* Clear both IT66121_PKT_NULL_CTRL_ON and IT66121_PKT_NULL_CTRL_RPT */ + return regmap_write(ctx->regmap, IT66121_PKT_NULL_CTRL_REG, 0); +} + +static int it66121_bridge_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge, + const u8 *buffer, size_t len) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + int ret; + + mutex_lock(&ctx->lock); + + /* Write new HDMI Vendor Specific Infoframe packet */ + ret = regmap_bulk_write(ctx->regmap, IT66121_PKT_NULL_HB(0), buffer, len); + if (ret) + goto unlock; + + /* Enable HDMI Vendor Specific Infoframe */ + ret = regmap_write(ctx->regmap, IT66121_PKT_NULL_CTRL_REG, + IT66121_PKT_NULL_CTRL_ON | IT66121_PKT_NULL_CTRL_RPT); + +unlock: + mutex_unlock(&ctx->lock); + return ret; +} + +static int it66121_bridge_hdmi_clear_audio_infoframe(struct drm_bridge *bridge) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + + /* Clear both IT66121_AUD_INFO_PKT_ON and IT66121_AUD_INFO_PKT_RPT */ + return regmap_write(ctx->regmap, IT66121_AUD_INFO_PKT_REG, 0); +} + +static int it66121_bridge_hdmi_write_audio_infoframe(struct drm_bridge *bridge, + const u8 *buffer, size_t len) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + int ret; + + mutex_lock(&ctx->lock); + + /* Write new Audio infoframe packet */ + ret = regmap_bulk_write(ctx->regmap, IT66121_AUD_INFO_DB1_REG, + &buffer[HDMI_INFOFRAME_HEADER_SIZE], + min_t(size_t, len - HDMI_INFOFRAME_HEADER_SIZE, 5)); + if (ret) + goto unlock; + + ret = regmap_write(ctx->regmap, IT66121_AUD_INFO_CSUM_REG, buffer[3]); + if (ret) + goto unlock; + + /* Enable Audio infoframe */ + ret = regmap_write(ctx->regmap, IT66121_AUD_INFO_PKT_REG, + IT66121_AUD_INFO_PKT_ON | IT66121_AUD_INFO_PKT_RPT); + +unlock: + mutex_unlock(&ctx->lock); + return ret; +} + static const struct drm_edid *it66121_bridge_edid_read(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -1359,6 +1453,10 @@ static int it66121_hdmi_audio_prepare(struct drm_bridge *bridge, out: mutex_unlock(&ctx->lock); + if (!ret) + ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector, + ¶ms->cea); + return ret; } @@ -1384,6 +1482,8 @@ static void it66121_hdmi_audio_shutdown(struct drm_bridge *bridge, int ret; struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector); + mutex_lock(&ctx->lock); ret = it661221_audio_output_enable(ctx, false); if (ret) @@ -1433,11 +1533,17 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = { .atomic_disable = it66121_bridge_disable, .atomic_check = it66121_bridge_check, .mode_set = it66121_bridge_mode_set, - .mode_valid = it66121_bridge_mode_valid, .detect = it66121_bridge_detect, .edid_read = it66121_bridge_edid_read, .hpd_enable = it66121_bridge_hpd_enable, .hpd_disable = it66121_bridge_hpd_disable, + .hdmi_tmds_char_rate_valid = it66121_bridge_hdmi_tmds_char_rate_valid, + .hdmi_clear_avi_infoframe = it66121_bridge_hdmi_clear_avi_infoframe, + .hdmi_write_avi_infoframe = it66121_bridge_hdmi_write_avi_infoframe, + .hdmi_clear_hdmi_infoframe = it66121_bridge_hdmi_clear_hdmi_infoframe, + .hdmi_write_hdmi_infoframe = it66121_bridge_hdmi_write_hdmi_infoframe, + .hdmi_clear_audio_infoframe = it66121_bridge_hdmi_clear_audio_infoframe, + .hdmi_write_audio_infoframe = it66121_bridge_hdmi_write_audio_infoframe, .hdmi_audio_startup = it66121_hdmi_audio_startup, .hdmi_audio_prepare = it66121_hdmi_audio_prepare, .hdmi_audio_shutdown = it66121_hdmi_audio_shutdown, @@ -1538,7 +1644,10 @@ static int it66121_probe(struct i2c_client *client) ctx->bridge.of_node = dev->of_node; ctx->bridge.type = DRM_MODE_CONNECTOR_HDMIA; - ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; + ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | + DRM_BRIDGE_OP_HDMI; + ctx->bridge.vendor = "ITE"; + ctx->bridge.product = "IT66121"; if (client->irq > 0) { ctx->bridge.ops |= DRM_BRIDGE_OP_HPD; From 4730eb08b9d7860611f2ebdaa971fc9eb4020bde Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 23 May 2026 12:40:59 +0200 Subject: [PATCH 21/71] drm/bridge: ite-it66121: Move .mode_set logic to .atomic_enable Move the existing .mode_set logic to the .atomic_enable callback. The former is deprecated and drivers are supposed to use the latter instead. Also, drop the struct it66121_ctx.connector field because the connector can be accessed through the atomic state and there is no need to store it anymore. Suggested-by: Maxime Ripard Reviewed-by: Maxime Ripard Link: https://patch.msgid.link/20260523-it66121-fix-dvi-mode-v5-v5-2-33b4468162f9@redhat.com Signed-off-by: Javier Martinez Canillas --- drivers/gpu/drm/bridge/ite-it66121.c | 100 +++++++++++++++------------ 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index f41f51f300c9..9725e5db5db3 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -315,7 +315,6 @@ struct it66121_chip_info { struct it66121_ctx { struct regmap *regmap; struct drm_bridge bridge; - struct drm_connector *connector; struct device *dev; struct gpio_desc *gpio_reset; struct i2c_client *client; @@ -668,6 +667,58 @@ static int it66121_bridge_attach(struct drm_bridge *bridge, return 0; } +static void it66121_set_mode(struct it66121_ctx *ctx, + struct drm_connector *connector, + struct drm_atomic_commit *state) +{ + const struct drm_connector_state *conn_state; + const struct drm_crtc_state *crtc_state; + const struct drm_display_mode *mode; + struct drm_crtc *crtc; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + + crtc = conn_state->crtc; + if (WARN_ON(!crtc)) + return; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (WARN_ON(!crtc_state)) + return; + + mode = &crtc_state->adjusted_mode; + + mutex_lock(&ctx->lock); + + /* Set TX mode to HDMI */ + if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) + goto unlock; + + if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) && + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, + IT66121_CLK_BANK_PWROFF_TXCLK)) { + goto unlock; + } + + if (it66121_configure_input(ctx)) + goto unlock; + + if (it66121_configure_afe(ctx, mode)) + goto unlock; + + if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) && + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, 0)) { + goto unlock; + } + +unlock: + mutex_unlock(&ctx->lock); +} + static int it66121_set_mute(struct it66121_ctx *ctx, bool mute) { int ret; @@ -740,13 +791,15 @@ static void it66121_bridge_enable(struct drm_bridge *bridge, struct drm_atomic_commit *state) { struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + struct drm_connector *connector; - ctx->connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); - if (WARN_ON(!ctx->connector)) + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!connector)) return; - drm_atomic_helper_connector_hdmi_update_infoframes(ctx->connector, state); + drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); + it66121_set_mode(ctx, connector, state); it66121_set_mute(ctx, false); } @@ -756,8 +809,6 @@ static void it66121_bridge_disable(struct drm_bridge *bridge, struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); it66121_set_mute(ctx, true); - - ctx->connector = NULL; } static int it66121_bridge_check(struct drm_bridge *bridge, @@ -778,42 +829,6 @@ static int it66121_bridge_check(struct drm_bridge *bridge, return 0; } -static -void it66121_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adjusted_mode) -{ - struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); - - mutex_lock(&ctx->lock); - - /* Set TX mode to HDMI */ - if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) - goto unlock; - - if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) && - regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, - IT66121_CLK_BANK_PWROFF_TXCLK, - IT66121_CLK_BANK_PWROFF_TXCLK)) { - goto unlock; - } - - if (it66121_configure_input(ctx)) - goto unlock; - - if (it66121_configure_afe(ctx, adjusted_mode)) - goto unlock; - - if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) && - regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, - IT66121_CLK_BANK_PWROFF_TXCLK, 0)) { - goto unlock; - } - -unlock: - mutex_unlock(&ctx->lock); -} - static enum drm_connector_status it66121_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -1532,7 +1547,6 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = { .atomic_enable = it66121_bridge_enable, .atomic_disable = it66121_bridge_disable, .atomic_check = it66121_bridge_check, - .mode_set = it66121_bridge_mode_set, .detect = it66121_bridge_detect, .edid_read = it66121_bridge_edid_read, .hpd_enable = it66121_bridge_hpd_enable, From fde6fa5a58f30078a06d5a3bc07bb558f48e142b Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 23 May 2026 12:41:00 +0200 Subject: [PATCH 22/71] drm/bridge: ite-it66121: Select HDMI or DVI mode based on sink type The driver unconditionally sets the transmission mode to HDMI, which leads to display output not working with DVI monitors. Check the connector's display information sink type to identify the correct mode to configure the bridge. Reviewed-by: Maxime Ripard Link: https://patch.msgid.link/20260523-it66121-fix-dvi-mode-v5-v5-3-33b4468162f9@redhat.com Signed-off-by: Javier Martinez Canillas --- drivers/gpu/drm/bridge/ite-it66121.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 9725e5db5db3..a469e8d9d2d8 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -199,6 +199,7 @@ #define IT66121_HDMI_MODE_REG 0xC0 #define IT66121_HDMI_MODE_HDMI BIT(0) +#define IT66121_HDMI_MODE_DVI 0 #define IT66121_SYS_STATUS_REG 0x0E #define IT66121_SYS_STATUS_ACTIVE_IRQ BIT(7) @@ -692,8 +693,10 @@ static void it66121_set_mode(struct it66121_ctx *ctx, mutex_lock(&ctx->lock); - /* Set TX mode to HDMI */ - if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) + /* Set TX mode to HDMI or DVI */ + if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, + connector->display_info.is_hdmi ? + IT66121_HDMI_MODE_HDMI : IT66121_HDMI_MODE_DVI)) goto unlock; if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) && From adcea8fcedb67cbd2f8a164393526f26c36c3dbd Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Fri, 15 May 2026 10:22:00 +0200 Subject: [PATCH 23/71] dt-bindings: display: simple: Add AM-1280800W8TZQW-T00H Add dt-bindings for 10.1" TFT LCD module from Ampire Co. Ltd. as part of panel-simple. Signed-off-by: Dario Binacchi Acked-by: Conor Dooley Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260515082232.1766586-1-dario.binacchi@amarulasolutions.com --- .../devicetree/bindings/display/panel/panel-simple.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 44ec7388d851..42e7eaaba37c 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -31,6 +31,8 @@ properties: # Ampire AM-1280800N3TZQW-T00H 10.1" WQVGA TFT LCD panel - ampire,am-1280800n3tzqw-t00h + # Ampire AM-1280800W8TZQW-T00H 10.1" WXGA TFT LCD panel + - ampire,am-1280800w8tzqw-t00h # Ampire AM-480272H3TMQW-T01H 4.3" WQVGA TFT LCD panel - ampire,am-480272h3tmqw-t01h # Ampire AM-800480L1TMQW-T00H 5" WVGA TFT LCD panel From 6acb810ebc5d8dea5c250326c14dc44e32dc8e92 Mon Sep 17 00:00:00 2001 From: Dario Binacchi Date: Fri, 15 May 2026 10:22:01 +0200 Subject: [PATCH 24/71] drm/panel: simple: Add AM-1280800W8TZQW-T00H Add Ampire, AM-1280800W8TZQW-T00H 10.1" TFT LCD panel timings. Co-developed-by: Michael Trimarchi Signed-off-by: Michael Trimarchi Signed-off-by: Dario Binacchi Reviewed-by: Dmitry Baryshkov Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260515082232.1766586-2-dario.binacchi@amarulasolutions.com --- drivers/gpu/drm/panel/panel-simple.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 2fbb9dff0a34..5119e42b203e 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -820,6 +820,31 @@ static const struct panel_desc ampire_am_1280800n3tzqw_t00h = { .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct drm_display_mode ampire_am_1280800w8tzqw_t00h_mode = { + .clock = 72400, + .hdisplay = 1280, + .hsync_start = 1280 + 40, + .hsync_end = 1280 + 40 + 80, + .htotal = 1280 + 40 + 80 + 40, + .vdisplay = 800, + .vsync_start = 800 + 10, + .vsync_end = 800 + 10 + 18, + .vtotal = 800 + 10 + 18 + 10, +}; + +static const struct panel_desc ampire_am_1280800w8tzqw_t00h = { + .modes = &ire_am_1280800w8tzqw_t00h_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 217, + .height = 136, + }, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct drm_display_mode ampire_am_480272h3tmqw_t01h_mode = { .clock = 9000, .hdisplay = 480, @@ -5588,6 +5613,9 @@ static const struct of_device_id platform_of_match[] = { { .compatible = "ampire,am-1280800n3tzqw-t00h", .data = &ire_am_1280800n3tzqw_t00h, + }, { + .compatible = "ampire,am-1280800w8tzqw-t00h", + .data = &ire_am_1280800w8tzqw_t00h, }, { .compatible = "ampire,am-480272h3tmqw-t01h", .data = &ire_am_480272h3tmqw_t01h, From 7a1d12f6f9305578641d3fd0ab01777e511f1785 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Mon, 18 May 2026 09:47:45 +0200 Subject: [PATCH 25/71] dt-bindings: display: simple: Add NEC NL6448BC33-70C panel Add the NEC LCD Technologies,Ltd. NL6448BC33-70C 10.4" 640x480 LCD panel. Signed-off-by: Steffen Trumtrar Acked-by: Conor Dooley Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260518-v7-1-topic-panel-simple-nl6448bc33-v3-1-21ea14a6e835@pengutronix.de --- .../devicetree/bindings/display/panel/panel-simple.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 42e7eaaba37c..21453f0d7ce2 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -236,6 +236,8 @@ properties: - nec,nl12880bc20-05 # NEC LCD Technologies,Ltd. WQVGA TFT LCD panel - nec,nl4827hc19-05b + # NEC LCD Technologies,Ltd. VGA TFT LCD panel + - nec,nl6448bc33-70c # Netron-DY E231732 7.0" WSVGA TFT LCD panel - netron-dy,e231732 # Newhaven Display International 480 x 272 TFT LCD panel From 23c8c2cd3d4a2b65c21d31cf91e81d5e0b81ecd5 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Mon, 18 May 2026 09:47:46 +0200 Subject: [PATCH 26/71] drm/panel: simple: add NEC NL6448BC33-70C Add NEC NL6448BC33-70C 10.4" 640x480 LCD module support. Signed-off-by: Steffen Trumtrar Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260518-v7-1-topic-panel-simple-nl6448bc33-v3-2-21ea14a6e835@pengutronix.de --- drivers/gpu/drm/panel/panel-simple.c | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 5119e42b203e..c09bf3db5e78 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -3717,6 +3717,32 @@ static const struct panel_desc nec_nl4827hc19_05b = { .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, }; +static const struct drm_display_mode nec_nl6448bc33_70c_mode = { + .clock = 25175, + .hdisplay = 640, + .hsync_start = 640 + 16, + .hsync_end = 640 + 16 + 48, + .htotal = 640 + 16 + 48 + 96, + .vdisplay = 480, + .vsync_start = 480 + 2, + .vsync_end = 480 + 2 + 31, + .vtotal = 480 + 2 + 31 + 31, + .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC, +}; + +static const struct panel_desc nec_nl6448bc33_70c = { + .modes = &nec_nl6448bc33_70c_mode, + .num_modes = 1, + .bpc = 6, + .size = { + .width = 211, + .height = 158, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct drm_display_mode netron_dy_e231732_mode = { .clock = 66000, .hdisplay = 1024, @@ -5946,6 +5972,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "nec,nl4827hc19-05b", .data = &nec_nl4827hc19_05b, + }, { + .compatible = "nec,nl6448bc33-70c", + .data = &nec_nl6448bc33_70c, }, { .compatible = "netron-dy,e231732", .data = &netron_dy_e231732, From aeb119dbff2466b8cc9fefa37d4691049ac27e04 Mon Sep 17 00:00:00 2001 From: Dongyang Jin Date: Thu, 21 May 2026 17:45:45 +0800 Subject: [PATCH 27/71] drm/panel: fix kernel-doc warning for devm_drm_panel_add() Use the correct kernel-doc notation for struct members to eliminate kernel-doc warnings: Warning: drivers/gpu/drm/drm_panel.c:119 function parameter 'dev' not described in 'devm_drm_panel_add' Warning: drivers/gpu/drm/drm_panel.c:119 function parameter 'dev' not described in 'devm_drm_panel_add' Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202605210648.RI4ufD66-lkp@intel.com/ Signed-off-by: Dongyang Jin Reviewed-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260521094545.3039207-1-jindongyang@kylinos.cn --- drivers/gpu/drm/drm_panel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 04f4a31ed27a..2c5649e433df 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -108,6 +108,7 @@ static void drm_panel_add_release(void *data) /** * devm_drm_panel_add - add a panel to the global registry using devres + * @dev: device to which the panel is attached * @panel: panel to add * * Add a panel to the global registry so that it can be looked From 3c3c5fb9b36836d279ebe370189d68a0a3387362 Mon Sep 17 00:00:00 2001 From: Vitor Soares Date: Fri, 22 May 2026 17:11:08 +0100 Subject: [PATCH 28/71] dt-bindings: display: panel-lvds: Add Riverdi RVT70HSLNWCA0 and RVT101HVLNWC00 The Riverdi RVT70HSLNWCA0 is a 7.0" WSVGA (1024x600) IPS TFT LCD LVDS panel used in the Riverdi RVT70HSDNWCA0 display module. The Riverdi RVT101HVLNWC00 is a 10.1" WXGA (1280x800) IPS TFT LCD LVDS panel used in the Riverdi RVT101HVDNWC00 display module. Link: https://download.riverdi.com/RVT70HSLNWCA0/DS_RVT70HSLNWCA0_Rev.1.4.pdf Link: https://download.riverdi.com/RVT101HVLNWC00/DS_RVT101HVLNWC00_Rev.1.4.pdf Acked-by: Conor Dooley Signed-off-by: Vitor Soares Reviewed-by: Francesco Dolcini Signed-off-by: Neil Armstrong Link: https://patch.msgid.link/20260522161105.277519-17-ivitro@gmail.com --- .../devicetree/bindings/display/panel/panel-lvds.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml index b31c67babaa8..b89f86bc0683 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml @@ -58,6 +58,10 @@ properties: - hydis,hv070wx2-1e0 # Jenson Display BL-JT60050-01A 7" WSVGA (1024x600) color TFT LCD LVDS panel - jenson,bl-jt60050-01a + # Riverdi RVT101HVLNWC00 10.1" WXGA (1280x800) TFT LCD LVDS panel + - riverdi,rvt101hvlnwc00 + # Riverdi RVT70HSLNWCA0 7.0" WSVGA (1024x600) TFT LCD LVDS panel + - riverdi,rvt70hslnwca0 # Samsung LTN070NL01 7.0" WSVGA (1024x600) TFT LCD LVDS panel - samsung,ltn070nl01 # Samsung LTN101AL03 10.1" WXGA (800x1280) TFT LCD LVDS panel From bff2da7aa0020eb72e42d63868dc5adaa0fb85d3 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 25 May 2026 15:55:16 +0300 Subject: [PATCH 29/71] drm/dp: Add DSC virtual DPCD quirk for Realtek MST branch device The ASUS DC301 USB-C dock containing a Realtek MST branch device supports the DSC decompression functionality on each of the dock's downstream connectors, even though there is no discoverable peer-to-peer virtual device in the MST topology (which the DP Standard requires/suggests to control the DSC functionality on a per-DFP basis). Add the DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD quirk for this branch device as well to enable the DSC decompression functionality on all DFP connectors of the dock, similarly to how this is done for dock's containing older Synaptics branch devices. Cc: Lyude Paul Reported-and-tested-by: Shawn C Lee Reviewed-by: Mika Kahola Reviewed-by: Lyude Paul Signed-off-by: Imre Deak Link: https://patch.msgid.link/20260525125516.2794636-1-imre.deak@intel.com --- drivers/gpu/drm/display/drm_dp_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index a6067471dd56..9c31e14cc413 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -2546,6 +2546,8 @@ static const struct dpcd_quirk dpcd_quirk_list[] = { { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) }, /* Synaptics DP1.4 MST hubs can support DSC without virtual DPCD */ { OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) }, + /* Realtek DP1.4 MST hubs can support DSC without virtual DPCD */ + { OUI(0x00, 0xe0, 0x4c), DEVICE_ID('D', 'p', '1', '.', '4', 0), true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) }, /* Synaptics DP1.4 MST hubs require DSC for some modes on which it applies HBLANK expansion. */ { OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC) }, /* MediaTek panels (at least in U3224KBA) require DSC for modes with a short HBLANK on UHBR links. */ From 448c21201e8d6962ae48b884003f7ea3eec0114d Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Tue, 26 May 2026 14:55:21 +0200 Subject: [PATCH 30/71] accel/ivpu: Remove disable_d0i3_msg workaround All published NPU firmware versions support D0i3 delayed entry flow, making this workaround obsolete. It was originally added as a safety measure for potential firmware bugs. Recent firmware dropped legacy D0i3 entry support, so the workaround can't be used anyway. Hardcode d0i3_delayed_entry boot param to 1 to ensure older firmware works in the correct mode. No functional changes, just dead code cleanup. Signed-off-by: Andrzej Kacprowski Reviewed-by: Karol Wachowski Signed-off-by: Karol Wachowski Link: https://patch.msgid.link/20260526125521.594479-1-andrzej.kacprowski@linux.intel.com --- drivers/accel/ivpu/ivpu_drv.h | 3 --- drivers/accel/ivpu/ivpu_fw.c | 23 ++--------------------- drivers/accel/ivpu/ivpu_jsm_msg.c | 3 --- drivers/accel/ivpu/vpu_boot_api.h | 6 ++++-- 4 files changed, 6 insertions(+), 29 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index c77dde310e81..9eefbbb7ba11 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -111,7 +111,6 @@ struct ivpu_wa_table { bool clear_runtime_mem; bool interrupt_clear_with_0; bool disable_clock_relinquish; - bool disable_d0i3_msg; bool wp0_during_power_up; bool disable_d0i2; }; @@ -220,8 +219,6 @@ extern bool ivpu_force_snoop; #define IVPU_TEST_MODE_FW_TEST BIT(0) #define IVPU_TEST_MODE_NULL_HW BIT(1) #define IVPU_TEST_MODE_NULL_SUBMISSION BIT(2) -#define IVPU_TEST_MODE_D0I3_MSG_DISABLE BIT(4) -#define IVPU_TEST_MODE_D0I3_MSG_ENABLE BIT(5) #define IVPU_TEST_MODE_MIP_DISABLE BIT(6) #define IVPU_TEST_MODE_DISABLE_TIMEOUTS BIT(8) #define IVPU_TEST_MODE_TURBO_ENABLE BIT(9) diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index 107f8ad31050..7db199e04f7c 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2020-2025 Intel Corporation + * Copyright (C) 2020-2026 Intel Corporation */ #include @@ -347,22 +347,6 @@ static void ivpu_fw_release(struct ivpu_device *vdev) release_firmware(vdev->fw->file); } -/* Initialize workarounds that depend on FW version */ -static void -ivpu_fw_init_wa(struct ivpu_device *vdev) -{ - const struct vpu_firmware_header *fw_hdr = (const void *)vdev->fw->file->data; - - if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, BOOT, 3, 17) || - (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_DISABLE)) - vdev->wa.disable_d0i3_msg = true; - - /* Force enable the feature for testing purposes */ - if (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_ENABLE) - vdev->wa.disable_d0i3_msg = false; - - IVPU_PRINT_WA(disable_d0i3_msg); -} static int ivpu_fw_mem_init(struct ivpu_device *vdev) { @@ -480,8 +464,6 @@ int ivpu_fw_init(struct ivpu_device *vdev) if (ret) goto err_fw_release; - ivpu_fw_init_wa(vdev); - ret = ivpu_fw_mem_init(vdev); if (ret) goto err_fw_release; @@ -711,8 +693,7 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) boot_params->vpu_focus_present_timer_ms = IVPU_FOCUS_PRESENT_TIMER_MS; boot_params->dvfs_mode = vdev->fw->dvfs_mode; - if (!IVPU_WA(disable_d0i3_msg)) - boot_params->d0i3_delayed_entry = 1; + boot_params->d0i3_delayed_entry = 1; boot_params->d0i3_residency_time_us = 0; boot_params->d0i3_entry_vpu_ts = 0; if (IVPU_WA(disable_d0i2)) diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.c b/drivers/accel/ivpu/ivpu_jsm_msg.c index 17b42a76aef9..6361fe50596e 100644 --- a/drivers/accel/ivpu/ivpu_jsm_msg.c +++ b/drivers/accel/ivpu/ivpu_jsm_msg.c @@ -275,9 +275,6 @@ int ivpu_jsm_pwr_d0i3_enter(struct ivpu_device *vdev) struct vpu_jsm_msg resp; int ret; - if (IVPU_WA(disable_d0i3_msg)) - return 0; - req.payload.pwr_d0i3_enter.send_response = 1; ret = ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_PWR_D0I3_ENTER_DONE, &resp, diff --git a/drivers/accel/ivpu/vpu_boot_api.h b/drivers/accel/ivpu/vpu_boot_api.h index a41170bbc6b7..06c092d5e675 100644 --- a/drivers/accel/ivpu/vpu_boot_api.h +++ b/drivers/accel/ivpu/vpu_boot_api.h @@ -41,7 +41,7 @@ /** * API header changed (field names, documentation, formatting) but API itself has not been changed */ -#define VPU_BOOT_API_VER_PATCH 4 +#define VPU_BOOT_API_VER_PATCH 5 /** * Index in the API version table @@ -320,9 +320,11 @@ struct vpu_boot_params { u64 dvfs_param; /** * D0i3 delayed entry - * Bit0: Disable CPU state save on D0i2 entry flow. + * Bit 0: Disable CPU state save on D0i2 entry flow. * 0: Every D0i2 entry saves state. Save state IPC message ignored. * 1: IPC message required to save state on D0i3 entry flow. + * NOTE: This parameter is deprecated starting NPU50xx+. Bit 0 is now hardcoded to 1, + * meaning CPU state save always requires IPC message on D0i3 entry flow. */ u32 d0i3_delayed_entry; /** Time spent by VPU in D0i3 state */ From 5715f1f1b9283d49956882d6d3f6d8113e02f609 Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Thu, 21 May 2026 15:51:15 +0200 Subject: [PATCH 31/71] drm/panthor: Implement evicted status for GEM objects For fdinfo to be able to fill its evicted counter with data, panthor needs to keep track of whether a GEM object has ever been reclaimed. Just checking whether the pages are resident isn't enough, as newly allocated objects also won't be resident. Do this with a new atomic_t member on panthor_gem_object. It's increased when an object gets evicted by the shrinker, and saturates at INT_MAX. This means that once an object has been evicted at least once, its reclaim counter will never return to 0. Due to this, it's possible to distinguish evicted non-resident pages from newly allocated non-resident pages by checking whether reclaimed_count is != 0 Also add a new column and status flag to the panthor gems debugfs: the column is the number of times an object has been evicted, whereas the flag indicates whether it currently is evicted. Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Signed-off-by: Nicolas Frattaroli Link: https://patch.msgid.link/20260521-panthor-bo-reclaim-observability-v5-1-49313994da55@collabora.com Signed-off-by: Boris Brezillon --- drivers/gpu/drm/panthor/panthor_gem.c | 16 ++++++++++++---- drivers/gpu/drm/panthor/panthor_gem.h | 10 ++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c index abe0c5bb1bca..ec2abc6ce417 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.c +++ b/drivers/gpu/drm/panthor/panthor_gem.c @@ -687,6 +687,8 @@ static void panthor_gem_evict_locked(struct panthor_gem_object *bo) if (drm_WARN_ON_ONCE(bo->base.dev, !bo->backing.pages)) return; + atomic_add_unless(&bo->reclaimed_count, 1, INT_MAX); + panthor_gem_dev_map_cleanup_locked(bo); panthor_gem_backing_cleanup_locked(bo); panthor_gem_update_reclaim_state_locked(bo, NULL); @@ -1590,6 +1592,7 @@ static void panthor_gem_debugfs_print_flag_names(struct seq_file *m) static const char * const gem_state_flags_names[] = { [PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT] = "imported", [PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT] = "exported", + [PANTHOR_DEBUGFS_GEM_STATE_EVICTED_BIT] = "evicted", }; static const char * const gem_usage_flags_names[] = { @@ -1620,6 +1623,7 @@ static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo, { enum panthor_gem_reclaim_state reclaim_state = bo->reclaim_state; unsigned int refcount = kref_read(&bo->base.refcount); + int reclaimed_count = atomic_read(&bo->reclaimed_count); char creator_info[32] = {}; size_t resident_size; u32 gem_usage_flags = bo->debugfs.flags; @@ -1633,16 +1637,20 @@ static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo, snprintf(creator_info, sizeof(creator_info), "%s/%d", bo->debugfs.creator.process_name, bo->debugfs.creator.tgid); - seq_printf(m, "%-32s%-16d%-16d%-16zd%-16zd0x%-16lx", + seq_printf(m, "%-32s%-16d%-16d%-11d%-16zd%-16zd0x%-16lx", creator_info, bo->base.name, refcount, + reclaimed_count, bo->base.size, resident_size, drm_vma_node_start(&bo->base.vma_node)); if (drm_gem_is_imported(&bo->base)) gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED; + else if (!resident_size && reclaimed_count) + gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_EVICTED; + if (bo->base.dma_buf) gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED; @@ -1666,8 +1674,8 @@ static void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev, panthor_gem_debugfs_print_flag_names(m); - seq_puts(m, "created-by global-name refcount size resident-size file-offset state usage label\n"); - seq_puts(m, "----------------------------------------------------------------------------------------------------------------------------------------------\n"); + seq_puts(m, "created-by global-name refcount evictions size resident-size file-offset state usage label\n"); + seq_puts(m, "---------------------------------------------------------------------------------------------------------------------------------------------------------\n"); scoped_guard(mutex, &ptdev->gems.lock) { list_for_each_entry(bo, &ptdev->gems.node, debugfs.node) { @@ -1675,7 +1683,7 @@ static void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev, } } - seq_puts(m, "==============================================================================================================================================\n"); + seq_puts(m, "=========================================================================================================================================================\n"); seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n", totals.size, totals.resident, totals.reclaimable); } diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h index ae0491d0b121..56d63137b4eb 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.h +++ b/drivers/gpu/drm/panthor/panthor_gem.h @@ -19,12 +19,16 @@ struct panthor_vm; enum panthor_debugfs_gem_state_flags { PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT = 0, PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT = 1, + PANTHOR_DEBUGFS_GEM_STATE_EVICTED_BIT = 2, /** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED: GEM BO is PRIME imported. */ PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT), /** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED: GEM BO is PRIME exported. */ PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT), + + /** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_EVICTED: GEM BO is evicted to swap. */ + PANTHOR_DEBUGFS_GEM_STATE_FLAG_EVICTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_EVICTED_BIT), }; enum panthor_debugfs_gem_usage_flags { @@ -172,6 +176,12 @@ struct panthor_gem_object { /** @reclaim_state: Cached reclaim state */ enum panthor_gem_reclaim_state reclaim_state; + /** + * @reclaimed_count: How many times object has been evicted to swap. + * The count saturates at %INT_MAX and will never wrap around to 0. + */ + atomic_t reclaimed_count; + /** * @exclusive_vm_root_gem: Root GEM of the exclusive VM this GEM object * is attached to. From 9f7c2ccae957296d732e0bf5aeccfc41cc0bb16d Mon Sep 17 00:00:00 2001 From: Nicolas Frattaroli Date: Thu, 21 May 2026 15:51:16 +0200 Subject: [PATCH 32/71] drm/panthor: Reduce padding in gems debugfs for refcount The "gems" debugfs file is getting a little too wide for comfort. While a lot of this is unavoidable due to the theoretical upper limits of numbers here (e.g. size needs to be 16 chars because 2**48-1 in decimal is 15 digits, plus one space for separation), the refcount column has a decent 5 characters to be saved, as it can only ever contain a 10-digit decimal number. Reduce the refcount column's width to 11, which fulfils this requirement with an additional space for separation. Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Signed-off-by: Nicolas Frattaroli Link: https://patch.msgid.link/20260521-panthor-bo-reclaim-observability-v5-2-49313994da55@collabora.com Signed-off-by: Boris Brezillon --- drivers/gpu/drm/panthor/panthor_gem.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c index ec2abc6ce417..a1e2eb1ca7bb 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.c +++ b/drivers/gpu/drm/panthor/panthor_gem.c @@ -1637,7 +1637,7 @@ static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo, snprintf(creator_info, sizeof(creator_info), "%s/%d", bo->debugfs.creator.process_name, bo->debugfs.creator.tgid); - seq_printf(m, "%-32s%-16d%-16d%-11d%-16zd%-16zd0x%-16lx", + seq_printf(m, "%-32s%-16d%-11d%-11d%-16zd%-16zd0x%-16lx", creator_info, bo->base.name, refcount, @@ -1674,8 +1674,8 @@ static void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev, panthor_gem_debugfs_print_flag_names(m); - seq_puts(m, "created-by global-name refcount evictions size resident-size file-offset state usage label\n"); - seq_puts(m, "---------------------------------------------------------------------------------------------------------------------------------------------------------\n"); + seq_puts(m, "created-by global-name refcount evictions size resident-size file-offset state usage label\n"); + seq_puts(m, "----------------------------------------------------------------------------------------------------------------------------------------------------\n"); scoped_guard(mutex, &ptdev->gems.lock) { list_for_each_entry(bo, &ptdev->gems.node, debugfs.node) { @@ -1683,7 +1683,7 @@ static void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev, } } - seq_puts(m, "=========================================================================================================================================================\n"); + seq_puts(m, "====================================================================================================================================================\n"); seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n", totals.size, totals.resident, totals.reclaimable); } From e6fabde34307c2bbc339c0787c7144a3dbf27f28 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 21 May 2026 21:46:59 +0200 Subject: [PATCH 33/71] drm/tegra: dp: use str_plural in drm_dp_link_train_{full,fast} Replace the manual ternary "s" pluralizations with str_plural() to simplify the code. This also corrects the "0 lanes" case. Signed-off-by: Thorsten Blum Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260521194658.366737-3-thorsten.blum@linux.dev --- drivers/gpu/drm/tegra/dp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tegra/dp.c b/drivers/gpu/drm/tegra/dp.c index 990e744b0923..3cb194f5ce36 100644 --- a/drivers/gpu/drm/tegra/dp.c +++ b/drivers/gpu/drm/tegra/dp.c @@ -4,6 +4,8 @@ * Copyright (C) 2015 Rob Clark */ +#include + #include #include #include @@ -668,7 +670,7 @@ static int drm_dp_link_train_full(struct drm_dp_link *link) retry: DRM_DEBUG_KMS("full-training link: %u lane%s at %u MHz\n", - link->lanes, (link->lanes > 1) ? "s" : "", + link->lanes, str_plural(link->lanes), link->rate / 100); err = drm_dp_link_configure(link->aux, link); @@ -724,7 +726,7 @@ static int drm_dp_link_train_fast(struct drm_dp_link *link) int err; DRM_DEBUG_KMS("fast-training link: %u lane%s at %u MHz\n", - link->lanes, (link->lanes > 1) ? "s" : "", + link->lanes, str_plural(link->lanes), link->rate / 100); err = drm_dp_link_configure(link->aux, link); From 285d3055412233f25d9501b348faed11e2d2b0de Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 21 May 2026 21:47:00 +0200 Subject: [PATCH 34/71] drm/tegra: sor: use str_plural in tegra_sor_dp_link_configure Replace the manual ternary "s" pluralization with str_plural() to simplify the code. Signed-off-by: Thorsten Blum Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260521194658.366737-4-thorsten.blum@linux.dev --- drivers/gpu/drm/tegra/sor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index de8b2dfc4984..93e5ffd4f206 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -940,7 +941,7 @@ static int tegra_sor_dp_link_configure(struct drm_dp_link *link) err = tegra_sor_power_up_lanes(sor, lanes); if (err < 0) { dev_err(sor->dev, "failed to power up %u lane%s: %d\n", - lanes, (lanes != 1) ? "s" : "", err); + lanes, str_plural(lanes), err); return err; } From 697d6de37650f9c362e02efcf49df2ec774796d6 Mon Sep 17 00:00:00 2001 From: Artur Kowalski Date: Tue, 19 May 2026 12:16:43 +0200 Subject: [PATCH 35/71] gpu: host1x: trace: fix string fields in host1x traces Use __assign_str and __get_str as required by tracing subsystem. Fixes string fields being rejected by the verifier and unreadable from userspace. Tested on v6.18.21. Signed-off-by: Artur Kowalski Acked-by: Steven Rostedt Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260519-host1x-tracing-v1-1-55afb8cbd186@gmail.com --- include/trace/events/host1x.h | 50 ++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/include/trace/events/host1x.h b/include/trace/events/host1x.h index 1ba84b738e46..1b6aeb7b177b 100644 --- a/include/trace/events/host1x.h +++ b/include/trace/events/host1x.h @@ -21,9 +21,11 @@ struct host1x_bo; DECLARE_EVENT_CLASS(host1x, TP_PROTO(const char *name), TP_ARGS(name), - TP_STRUCT__entry(__field(const char *, name)), - TP_fast_assign(__entry->name = name;), - TP_printk("name=%s", __entry->name) + TP_STRUCT__entry(__string(name, name)), + TP_fast_assign( + __assign_str(name); + ), + TP_printk("name=%s", __get_str(name)) ); DEFINE_EVENT(host1x, host1x_channel_open, @@ -52,19 +54,19 @@ TRACE_EVENT(host1x_cdma_push, TP_ARGS(name, op1, op2), TP_STRUCT__entry( - __field(const char *, name) + __string(name, name) __field(u32, op1) __field(u32, op2) ), TP_fast_assign( - __entry->name = name; + __assign_str(name); __entry->op1 = op1; __entry->op2 = op2; ), TP_printk("name=%s, op1=%08x, op2=%08x", - __entry->name, __entry->op1, __entry->op2) + __get_str(name), __entry->op1, __entry->op2) ); TRACE_EVENT(host1x_cdma_push_wide, @@ -73,7 +75,7 @@ TRACE_EVENT(host1x_cdma_push_wide, TP_ARGS(name, op1, op2, op3, op4), TP_STRUCT__entry( - __field(const char *, name) + __string(name, name) __field(u32, op1) __field(u32, op2) __field(u32, op3) @@ -81,7 +83,7 @@ TRACE_EVENT(host1x_cdma_push_wide, ), TP_fast_assign( - __entry->name = name; + __assign_str(name); __entry->op1 = op1; __entry->op2 = op2; __entry->op3 = op3; @@ -89,7 +91,7 @@ TRACE_EVENT(host1x_cdma_push_wide, ), TP_printk("name=%s, op1=%08x, op2=%08x, op3=%08x op4=%08x", - __entry->name, __entry->op1, __entry->op2, __entry->op3, + __get_str(name), __entry->op1, __entry->op2, __entry->op3, __entry->op4) ); @@ -100,7 +102,7 @@ TRACE_EVENT(host1x_cdma_push_gather, TP_ARGS(name, bo, words, offset, cmdbuf), TP_STRUCT__entry( - __field(const char *, name) + __string(name, name) __field(struct host1x_bo *, bo) __field(u32, words) __field(u32, offset) @@ -114,14 +116,14 @@ TRACE_EVENT(host1x_cdma_push_gather, words * sizeof(u32)); } __entry->cmdbuf = cmdbuf; - __entry->name = name; + __assign_str(name); __entry->bo = bo; __entry->words = words; __entry->offset = offset; ), TP_printk("name=%s, bo=%p, words=%u, offset=%d, contents=[%s]", - __entry->name, __entry->bo, + __get_str(name), __entry->bo, __entry->words, __entry->offset, __print_hex(__get_dynamic_array(cmdbuf), __entry->cmdbuf ? __entry->words * 4 : 0)) @@ -134,7 +136,7 @@ TRACE_EVENT(host1x_channel_submit, TP_ARGS(name, cmdbufs, relocs, syncpt_id, syncpt_incrs), TP_STRUCT__entry( - __field(const char *, name) + __string(name, name) __field(u32, cmdbufs) __field(u32, relocs) __field(u32, syncpt_id) @@ -142,7 +144,7 @@ TRACE_EVENT(host1x_channel_submit, ), TP_fast_assign( - __entry->name = name; + __assign_str(name); __entry->cmdbufs = cmdbufs; __entry->relocs = relocs; __entry->syncpt_id = syncpt_id; @@ -151,7 +153,7 @@ TRACE_EVENT(host1x_channel_submit, TP_printk("name=%s, cmdbufs=%u, relocs=%u, syncpt_id=%u, " "syncpt_incrs=%u", - __entry->name, __entry->cmdbufs, __entry->relocs, + __get_str(name), __entry->cmdbufs, __entry->relocs, __entry->syncpt_id, __entry->syncpt_incrs) ); @@ -161,19 +163,19 @@ TRACE_EVENT(host1x_channel_submitted, TP_ARGS(name, syncpt_base, syncpt_max), TP_STRUCT__entry( - __field(const char *, name) + __string(name, name) __field(u32, syncpt_base) __field(u32, syncpt_max) ), TP_fast_assign( - __entry->name = name; + __assign_str(name); __entry->syncpt_base = syncpt_base; __entry->syncpt_max = syncpt_max; ), TP_printk("name=%s, syncpt_base=%d, syncpt_max=%d", - __entry->name, __entry->syncpt_base, __entry->syncpt_max) + __get_str(name), __entry->syncpt_base, __entry->syncpt_max) ); TRACE_EVENT(host1x_channel_submit_complete, @@ -182,19 +184,19 @@ TRACE_EVENT(host1x_channel_submit_complete, TP_ARGS(name, count, thresh), TP_STRUCT__entry( - __field(const char *, name) + __string(name, name) __field(int, count) __field(u32, thresh) ), TP_fast_assign( - __entry->name = name; + __assign_str(name); __entry->count = count; __entry->thresh = thresh; ), TP_printk("name=%s, count=%d, thresh=%d", - __entry->name, __entry->count, __entry->thresh) + __get_str(name), __entry->count, __entry->thresh) ); TRACE_EVENT(host1x_wait_cdma, @@ -203,16 +205,16 @@ TRACE_EVENT(host1x_wait_cdma, TP_ARGS(name, eventid), TP_STRUCT__entry( - __field(const char *, name) + __string(name, name) __field(u32, eventid) ), TP_fast_assign( - __entry->name = name; + __assign_str(name); __entry->eventid = eventid; ), - TP_printk("name=%s, event=%d", __entry->name, __entry->eventid) + TP_printk("name=%s, event=%d", __get_str(name), __entry->eventid) ); TRACE_EVENT(host1x_syncpt_load_min, From c4ef5ba1131346159e31f4ef858525cf377380a6 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Sun, 17 May 2026 12:14:49 +0300 Subject: [PATCH 36/71] drm/tegra: gr2d/gr3d: Initialize address register map before HOST1X client is registered The host1x_client_register() function is called just prior to register map initialization loop, making the device available to userspace. This may result in userspace attempting to submits a job before the register map is initialized. Address this by moving register initialization before host1x client registration. Acked-by: Mikko Perttunen Signed-off-by: Svyatoslav Ryhel Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260517091450.46728-2-clamor95@gmail.com --- drivers/gpu/drm/tegra/gr2d.c | 8 ++++---- drivers/gpu/drm/tegra/gr3d.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 21f4dd0fa6af..e4148b034af7 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -276,16 +276,16 @@ static int gr2d_probe(struct platform_device *pdev) if (err) return err; + /* initialize address register map */ + for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) + set_bit(gr2d_addr_regs[i], gr2d->addr_regs); + err = host1x_client_register(&gr2d->client.base); if (err < 0) { dev_err(dev, "failed to register host1x client: %d\n", err); return err; } - /* initialize address register map */ - for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) - set_bit(gr2d_addr_regs[i], gr2d->addr_regs); - return 0; } diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 42e9656ab80c..47b0c6c56bfd 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -506,6 +506,10 @@ static int gr3d_probe(struct platform_device *pdev) if (err) return err; + /* initialize address register map */ + for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) + set_bit(gr3d_addr_regs[i], gr3d->addr_regs); + err = host1x_client_register(&gr3d->client.base); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", @@ -513,10 +517,6 @@ static int gr3d_probe(struct platform_device *pdev) return err; } - /* initialize address register map */ - for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) - set_bit(gr3d_addr_regs[i], gr3d->addr_regs); - return 0; } From ace01e2af3871343d700fb60c6f64d8f8e3180e1 Mon Sep 17 00:00:00 2001 From: Ion Agorria Date: Sun, 17 May 2026 12:14:50 +0300 Subject: [PATCH 37/71] drm/tegra: gr2d/gr3d: Contain PM in the gr*d_probe/gr*d_remove The current power management configuration causes GR2G/GR3D to malfunction after resume. Reconfigure all PM actions to be handled within the GR*D probe and remove operations to address this. Fixes: 62fa0a985e2c ("drm/tegra: Enable runtime PM during probe") Acked-by: Mikko Perttunen Signed-off-by: Ion Agorria Signed-off-by: Svyatoslav Ryhel Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260517091450.46728-3-clamor95@gmail.com --- drivers/gpu/drm/tegra/gr2d.c | 13 ++++++------- drivers/gpu/drm/tegra/gr3d.c | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index e4148b034af7..892e3450b281 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -100,9 +100,6 @@ static int gr2d_exit(struct host1x_client *client) if (err < 0) return err; - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - host1x_client_iommu_detach(client); host1x_syncpt_put(client->syncpts[0]); host1x_channel_put(gr2d->channel); @@ -280,12 +277,18 @@ static int gr2d_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) set_bit(gr2d_addr_regs[i], gr2d->addr_regs); + pm_runtime_enable(dev); + err = host1x_client_register(&gr2d->client.base); if (err < 0) { + pm_runtime_disable(dev); dev_err(dev, "failed to register host1x client: %d\n", err); return err; } + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 500); + return 0; } @@ -367,10 +370,6 @@ static int __maybe_unused gr2d_runtime_resume(struct device *dev) goto disable_clk; } - pm_runtime_enable(dev); - pm_runtime_use_autosuspend(dev); - pm_runtime_set_autosuspend_delay(dev, 500); - return 0; disable_clk: diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 47b0c6c56bfd..388e47943d5e 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -109,9 +109,6 @@ static int gr3d_exit(struct host1x_client *client) if (err < 0) return err; - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - host1x_client_iommu_detach(client); host1x_syncpt_put(client->syncpts[0]); host1x_channel_put(gr3d->channel); @@ -510,13 +507,19 @@ static int gr3d_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++) set_bit(gr3d_addr_regs[i], gr3d->addr_regs); + pm_runtime_enable(&pdev->dev); + err = host1x_client_register(&gr3d->client.base); if (err < 0) { + pm_runtime_disable(&pdev->dev); dev_err(&pdev->dev, "failed to register host1x client: %d\n", err); return err; } + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 500); + return 0; } @@ -578,10 +581,6 @@ static int __maybe_unused gr3d_runtime_resume(struct device *dev) goto disable_clk; } - pm_runtime_enable(dev); - pm_runtime_use_autosuspend(dev); - pm_runtime_set_autosuspend_delay(dev, 500); - return 0; disable_clk: From 3cbf5e3c46e66d9b3b6b91099bb720c6cb1be3bc Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Fri, 15 May 2026 11:34:51 +0900 Subject: [PATCH 38/71] gpu: host1x: Allow entries in BO caches to be freed When a buffer object is pinned via host1x_bo_pin() with a cache, the resulting mapping is kept in the cache so it can be reused on subsequent pins. Each mapping held a reference to the underlying host1x_bo (taken in tegra_bo_pin / gather_bo_pin), so as long as a mapping was cached, the bo itself could not be freed. However, the only way to remove the cached mapping was through the free path of the buffer object. This meant that if a bo got cached, it could never get freed again. Resolve the circularity by holding a weak reference to the bo from the cache side. This is done by having the .pin callbacks not bump the bo's refcount -- instead the common Host1x bo code does so, except for the cache reference. Also move the remove-cache-mapping-on-free code into a common function inside Host1x code. This is only called from the TegraDRM GEM buffers since those are the only ones that can be cached at the moment. Reported-by: Aaron Kling Fixes: 1f39b1dfa53c ("drm/tegra: Implement buffer object cache") Signed-off-by: Mikko Perttunen Tested-by: Aaron Kling Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260515-host1x-bocache-leak-v1-1-a0375f68aeab@nvidia.com --- drivers/gpu/drm/tegra/gem.c | 13 ++------ drivers/gpu/drm/tegra/submit.c | 3 +- drivers/gpu/host1x/bus.c | 60 +++++++++++++++++++++++++++++++++- include/linux/host1x.h | 7 ++++ 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index d2bae88ad545..2377e2b76397 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -69,7 +69,7 @@ static struct host1x_bo_mapping *tegra_bo_pin(struct device *dev, struct host1x_ return ERR_PTR(-ENOMEM); kref_init(&map->ref); - map->bo = host1x_bo_get(bo); + map->bo = bo; map->direction = direction; map->dev = dev; @@ -170,7 +170,6 @@ static void tegra_bo_unpin(struct host1x_bo_mapping *map) kfree(map->sgt); } - host1x_bo_put(map->bo); kfree(map); } @@ -509,17 +508,9 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm, void tegra_bo_free_object(struct drm_gem_object *gem) { struct tegra_drm *tegra = gem->dev->dev_private; - struct host1x_bo_mapping *mapping, *tmp; struct tegra_bo *bo = to_tegra_bo(gem); - /* remove all mappings of this buffer object from any caches */ - list_for_each_entry_safe(mapping, tmp, &bo->base.mappings, list) { - if (mapping->cache) - host1x_bo_unpin(mapping); - else - dev_err(gem->dev->dev, "mapping %p stale for device %s\n", mapping, - dev_name(mapping->dev)); - } + host1x_bo_clear_cached_mappings(&bo->base); if (tegra->domain) { tegra_bo_iommu_unmap(tegra, bo); diff --git a/drivers/gpu/drm/tegra/submit.c b/drivers/gpu/drm/tegra/submit.c index 3009b8b9e619..e5841857c937 100644 --- a/drivers/gpu/drm/tegra/submit.c +++ b/drivers/gpu/drm/tegra/submit.c @@ -76,7 +76,7 @@ gather_bo_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_direction return ERR_PTR(-ENOMEM); kref_init(&map->ref); - map->bo = host1x_bo_get(bo); + map->bo = bo; map->direction = direction; map->dev = dev; @@ -117,7 +117,6 @@ static void gather_bo_unpin(struct host1x_bo_mapping *map) dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0); sg_free_table(map->sgt); kfree(map->sgt); - host1x_bo_put(map->bo); kfree(map); } diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index f814eb4941c0..772e05a7b45b 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -887,6 +887,20 @@ int host1x_client_resume(struct host1x_client *client) } EXPORT_SYMBOL(host1x_client_resume); +/** + * host1x_bo_pin() - Create a DMA mapping for the buffer object + * @dev: Device onto which DMA map to + * @bo: Buffer object to map + * @dir: DMA direction + * @cache: Cache in which to store mapping, or NULL + * + * Creates a DMA mapping pointing to @bo for @dev. The refcount of @bo is incremented + * until host1x_bo_unpin is called. + * + * If @cache is specified, the mapping is also stored in the cache and not released + * until @bo is freed (refcount drops to zero). This improves performance when a buffer + * is pinned and unpinned frequently as in the case of display use. + */ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_direction dir, struct host1x_bo_cache *cache) @@ -899,6 +913,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo list_for_each_entry(mapping, &cache->mappings, entry) { if (mapping->bo == bo && mapping->direction == dir) { kref_get(&mapping->ref); + host1x_bo_get(bo); goto unlock; } } @@ -908,6 +923,8 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo if (IS_ERR(mapping)) goto unlock; + host1x_bo_get(bo); + spin_lock(&mapping->bo->lock); list_add_tail(&mapping->list, &bo->mappings); spin_unlock(&mapping->bo->lock); @@ -918,7 +935,12 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo list_add_tail(&mapping->entry, &cache->mappings); - /* bump reference count to track the copy in the cache */ + /* + * Bump the mapping reference count to track the mapping in the cache, + * but do not bump the BO's refcount. This allows the BO to still get freed, + * triggering the release of the cache mapping through + * host1x_bo_clear_cached_mappings. + */ kref_get(&mapping->ref); } @@ -948,9 +970,17 @@ static void __host1x_bo_unpin(struct kref *ref) mapping->bo->ops->unpin(mapping); } +/** + * host1x_bo_unpin() - Release an established DMA mapping of a buffer object + * @mapping: Mapping to release + * + * Unmaps the given @mapping, unless it is cached. Decreases the refcount on + * the underlying buffer object. + */ void host1x_bo_unpin(struct host1x_bo_mapping *mapping) { struct host1x_bo_cache *cache = mapping->cache; + struct host1x_bo *bo = mapping->bo; if (cache) mutex_lock(&cache->lock); @@ -959,5 +989,33 @@ void host1x_bo_unpin(struct host1x_bo_mapping *mapping) if (cache) mutex_unlock(&cache->lock); + + host1x_bo_put(bo); } EXPORT_SYMBOL(host1x_bo_unpin); + +/** + * host1x_bo_clear_cached_mappings() - Remove all cached mappings pointing at a bo + * @bo: Buffer object to release mappings of + * + * Drops references to any mappings pointing to @bo left in any caches. This must + * be called by any host1x_bo implementers that may be pinned with caching enabled + * before freeing the bo. + */ +void host1x_bo_clear_cached_mappings(struct host1x_bo *bo) +{ + struct host1x_bo_mapping *mapping, *tmp; + struct host1x_bo_cache *cache; + + list_for_each_entry_safe(mapping, tmp, &bo->mappings, list) { + cache = mapping->cache; + if (WARN_ON(!cache)) + continue; + + mutex_lock(&mapping->cache->lock); + WARN_ON(kref_read(&mapping->ref) != 1); + __host1x_bo_unpin(&mapping->ref); + mutex_unlock(&mapping->cache->lock); + } +} +EXPORT_SYMBOL(host1x_bo_clear_cached_mappings); diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 1f5f55917d1c..a7a675783136 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -143,6 +143,12 @@ static inline struct host1x_bo_mapping *to_host1x_bo_mapping(struct kref *ref) return container_of(ref, struct host1x_bo_mapping, ref); } +/** + * struct host1x_bo_ops - operations implemented by a host1x_bo provider + * + * @pin: create a DMA mapping. Implementation must not touch the bo's refcount. + * @unpin: destroy a DMA mapping. Implementation must not touch the bo's refcount. + */ struct host1x_bo_ops { struct host1x_bo *(*get)(struct host1x_bo *bo); void (*put)(struct host1x_bo *bo); @@ -181,6 +187,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo enum dma_data_direction dir, struct host1x_bo_cache *cache); void host1x_bo_unpin(struct host1x_bo_mapping *map); +void host1x_bo_clear_cached_mappings(struct host1x_bo *bo); static inline void *host1x_bo_mmap(struct host1x_bo *bo) { From 6fea41ff3642483cd6d968c3a9bcdb47a8c35312 Mon Sep 17 00:00:00 2001 From: Tanmay Patil Date: Thu, 14 May 2026 10:31:52 +0000 Subject: [PATCH 39/71] gpu: host1x: Skip redundant syncpoint loads in host1x_syncpt_wait() In host1x_syncpt_wait(), the hardware syncpoint value was loaded initially for expiry check, and then loaded a second time to populate the caller's value pointer. Reuse a single load for both purposes. After dma_fence_wait_timeout(), the previous code reloaded the syncpoint value for the expiry check, which is only required in the timeout case. On success (i.e., return value > 0, or return value == 0 with zero jiffies remaining), the ISR has already cached the value before signaling the fence. The value pointer can therefore be populated using the cached value using host1x_syncpt_read_min() without MMIO access. Only the timeout path requires a fresh load, move host1x_syncpt_load() under that path. Measured Syncpoint wait latency (50000 samples): Average latency: 12.2 us -> 10.6 us 99.99 pct latency: 62.96 us -> 51.90 us Signed-off-by: Tanmay Patil Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260514103153.766343-2-tanmayp@nvidia.com --- drivers/gpu/host1x/syncpt.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index acc7d82e0585..807c74fc6a0a 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -222,11 +222,12 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, { struct dma_fence *fence; long wait_err; + u32 curr; - host1x_hw_syncpt_load(sp->host, sp); + curr = host1x_syncpt_load(sp); if (value) - *value = host1x_syncpt_load(sp); + *value = curr; if (host1x_syncpt_is_expired(sp, thresh)) return 0; @@ -245,21 +246,25 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, host1x_fence_cancel(fence); dma_fence_put(fence); - if (value) - *value = host1x_syncpt_load(sp); - /* * Don't rely on dma_fence_wait_timeout return value, * since it returns zero both on timeout and if the * wait completed with 0 jiffies left. */ - host1x_hw_syncpt_load(sp->host, sp); - if (wait_err == 0 && !host1x_syncpt_is_expired(sp, thresh)) + if (wait_err == 0 && !host1x_syncpt_is_expired(sp, thresh)) { + if (value) + *value = host1x_syncpt_load(sp); + return -EAGAIN; - else if (wait_err < 0) + } else if (wait_err < 0) { return wait_err; - else + } else { + /* Success, read the value cached by ISR */ + if (value) + *value = host1x_syncpt_read_min(sp); + return 0; + } } EXPORT_SYMBOL(host1x_syncpt_wait); From 132926b2173360a48950a726bbdee464868f740b Mon Sep 17 00:00:00 2001 From: Tanmay Patil Date: Thu, 14 May 2026 10:31:53 +0000 Subject: [PATCH 40/71] gpu: host1x: Skip redundant HW state update When the fence list is empty, host1x_intr_update_hw_state() falls through to host1x_intr_disable_syncpt_intr() which does two MMIO writes to disable the syncpoint interrupt and clear its status. The ISR has already disabled and acked the interrupt before calling host1x_intr_handle_interrupt(), making these two writes redundant. Skip the update_hw_state() call if no fences remain. Measured Syncpoint wait latency (50000 samples): Average latency: 10.6 us -> 9.4 us 99.99 pct latency: 51.90 us -> 36.58 us Signed-off-by: Tanmay Patil Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260514103153.766343-3-tanmayp@nvidia.com --- drivers/gpu/host1x/intr.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x/intr.c b/drivers/gpu/host1x/intr.c index f77a678949e9..723297250768 100644 --- a/drivers/gpu/host1x/intr.c +++ b/drivers/gpu/host1x/intr.c @@ -92,8 +92,12 @@ void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id) host1x_fence_signal(fence); } - /* Re-enable interrupt if necessary */ - host1x_intr_update_hw_state(host, sp); + /* + * Re-enable interrupt if necessary. The ISR already disabled the interrupt, + * so if no fences remain, no update is needed. + */ + if (!list_empty(&sp->fences.list)) + host1x_intr_update_hw_state(host, sp); spin_unlock(&sp->fences.lock); } From f85d5a59b3d08c117b5e9629b356bc2fbda4a5e3 Mon Sep 17 00:00:00 2001 From: Souradipto Das Date: Wed, 13 May 2026 15:35:01 +0530 Subject: [PATCH 41/71] drm/tegra: hdmi: Open-code drm_simple_encoder_init() The helper drm_simple_encoder_init() is a trivial wrapper around drm_encoder_init() that only provides a static drm_encoder_funcs with .destroy set to drm_encoder_cleanup(). Open-code the initialization with a driver-specific instance of drm_encoder_funcs and remove the dependency on drm_simple_kms_helper. Suggested-by: Thomas Zimmermann Signed-off-by: Souradipto Das [treding@nvidia.com: fix issues flagged by checkpatch] Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260513100501.6468-1-souradiptodas6@gmail.com --- drivers/gpu/drm/tegra/hdmi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 10f02f35c866..0d2c0ab0a63e 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -25,12 +25,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include "hda.h" #include "hdmi.h" @@ -372,6 +372,10 @@ static const struct tmds_config tegra124_tmds_config[] = { }, }; +static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + static void tegra_hdmi_audio_lock(struct tegra_hdmi *hdmi) { mutex_lock(&hdmi->audio_lock); @@ -1555,8 +1559,8 @@ static int tegra_hdmi_init(struct host1x_client *client) hdmi->output.dev = client->dev; - drm_simple_encoder_init(drm, &hdmi->output.encoder, - DRM_MODE_ENCODER_TMDS); + drm_encoder_init(drm, &hdmi->output.encoder, &tegra_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); drm_encoder_helper_add(&hdmi->output.encoder, &tegra_hdmi_encoder_helper_funcs); From 05a07a31deda59b60483b5359b5c0ccb693d7311 Mon Sep 17 00:00:00 2001 From: Jacob McLemore Date: Fri, 22 May 2026 20:26:24 -0500 Subject: [PATCH 42/71] drm/tegra: sor: Remove usage of drm_simple_encoder_init() Remove the deprecated trivial helper drm_simple_encoder_init(). Inline the call to drm_encoder_init and add instance of drm_encoder_funcs. Signed-off-by: Jacob McLemore Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260523012824.81043-1-jmclemore.lkml@gmail.com --- drivers/gpu/drm/tegra/sor.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 93e5ffd4f206..865b211ae752 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -23,10 +23,10 @@ #include #include #include +#include #include #include #include -#include #include "dc.h" #include "dp.h" @@ -3039,6 +3039,10 @@ static const struct tegra_sor_ops tegra_sor_dp_ops = { .probe = tegra_sor_dp_probe, }; +static const struct drm_encoder_funcs tegra_sor_encoder_funcs_cleanup = { + .destroy = drm_encoder_cleanup, +}; + static int tegra_sor_init(struct host1x_client *client) { struct drm_device *drm = dev_get_drvdata(client->host); @@ -3082,7 +3086,8 @@ static int tegra_sor_init(struct host1x_client *client) &tegra_sor_connector_helper_funcs); sor->output.connector.dpms = DRM_MODE_DPMS_OFF; - drm_simple_encoder_init(drm, &sor->output.encoder, encoder); + drm_encoder_init(drm, &sor->output.encoder, + &tegra_sor_encoder_funcs_cleanup, encoder, NULL); drm_encoder_helper_add(&sor->output.encoder, helpers); drm_connector_attach_encoder(&sor->output.connector, From 71d25f668bc5c0f36ea843462e12307dea45aaa3 Mon Sep 17 00:00:00 2001 From: shayderrr Date: Sun, 17 May 2026 12:04:56 -0500 Subject: [PATCH 43/71] host1x: bus: Fix missing ops null check in error teardown In host1x_device_init(), the error teardown paths do not check client->ops before dereferencing it, unlike the forward init paths which correctly guard with 'client->ops &&'. This can result in a NULL pointer dereference if client->ops is NULL. Fix by adding the missing client->ops check in both the teardown and teardown_late labels. Signed-off-by: shayderrr Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260517170456.84927-1-darknessshayder@gmail.com --- drivers/gpu/host1x/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 772e05a7b45b..70310d1e3162 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -221,7 +221,7 @@ int host1x_device_init(struct host1x_device *device) teardown: list_for_each_entry_continue_reverse(client, &device->clients, list) - if (client->ops->exit) + if (client->ops && client->ops->exit) client->ops->exit(client); /* reset client to end of list for late teardown */ @@ -229,7 +229,7 @@ int host1x_device_init(struct host1x_device *device) teardown_late: list_for_each_entry_continue_reverse(client, &device->clients, list) - if (client->ops->late_exit) + if (client->ops && client->ops->late_exit) client->ops->late_exit(client); mutex_unlock(&device->clients_lock); From 011c4b65e3b48a25f93af4e776b03abeb9060745 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 27 Apr 2026 11:44:42 -0700 Subject: [PATCH 44/71] drm/tegra: dp: fix kernel-doc warnings in dp.h Use correct kernel-doc format and add missing nested struct entries to eliminate kernel-doc warnings: Warning: drivers/gpu/drm/tegra/dp.h:28 Incorrect use of kernel-doc format: * tps3_supported: Warning: drivers/gpu/drm/tegra/dp.h:54 struct member 'tps3_supported' not described in 'drm_dp_link_caps' dp.h:73: warning: Function parameter or struct member 'apply_training' not described in 'drm_dp_link_ops' dp.h:73: warning: Function parameter or struct member 'configure' not described in 'drm_dp_link_ops' dp.h:160: warning: Excess struct member 'cr' description in 'drm_dp_link' Signed-off-by: Randy Dunlap Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260427184442.693768-1-rdunlap@infradead.org --- drivers/gpu/drm/tegra/dp.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/tegra/dp.h b/drivers/gpu/drm/tegra/dp.h index 695060cafac0..774fb13c0861 100644 --- a/drivers/gpu/drm/tegra/dp.h +++ b/drivers/gpu/drm/tegra/dp.h @@ -26,7 +26,7 @@ struct drm_dp_link_caps { bool enhanced_framing; /** - * tps3_supported: + * @tps3_supported: * * training pattern sequence 3 supported for equalization */ @@ -62,12 +62,12 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, */ struct drm_dp_link_ops { /** - * @apply_training: + * @apply_training: apply the link training */ int (*apply_training)(struct drm_dp_link *link); /** - * @configure: + * @configure: configure the DP link */ int (*configure)(struct drm_dp_link *link); }; @@ -113,6 +113,8 @@ struct drm_dp_link_train { * @max_lanes: maximum number of lanes supported on the link * @caps: capabilities supported on the link (see &drm_dp_link_caps) * @aux_rd_interval: AUX read interval to use for training (in microseconds) + * @aux_rd_interval.cr: clock recovery read interval + * @aux_rd_interval.ce: channel equalization read interval * @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...) * @rate: currently configured link rate * @lanes: currently configured number of lanes @@ -126,10 +128,6 @@ struct drm_dp_link { struct drm_dp_link_caps caps; - /** - * @cr: clock recovery read interval - * @ce: channel equalization read interval - */ struct { unsigned int cr; unsigned int ce; From b397cc489ae42c753c608cddd2dd6e9c2b2d86c0 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 27 Apr 2026 11:44:54 -0700 Subject: [PATCH 45/71] drm/tegra: tegra_drm.h: fix all uapi kernel-doc warnings Add 2 struct member descriptions and convert #define macro constants comments to kernel-doc comments to eliminate all kernel-doc warnings: Warning: include/uapi/drm/tegra_drm.h:353 struct member 'cmdbuf' not described in 'drm_tegra_reloc' Warning: include/uapi/drm/tegra_drm.h:353 struct member 'target' not described in 'drm_tegra_reloc' Warning: include/uapi/drm/tegra_drm.h:780 This comment starts with '/**', but isn't a kernel-doc comment. * Specify that bit 39 of the patched-in address should be set to switch Warning: include/uapi/drm/tegra_drm.h:832 This comment starts with '/**', but isn't a kernel-doc comment. * Execute `words` words of Host1x opcodes specified in the `gather_data_ptr` Warning: include/uapi/drm/tegra_drm.h:837 This comment starts with '/**', but isn't a kernel-doc comment. * Wait for a syncpoint to reach a value before continuing with further Warning: include/uapi/drm/tegra_drm.h:842 This comment starts with '/**', but isn't a kernel-doc comment. * Wait for a syncpoint to reach a value before continuing with further Signed-off-by: Randy Dunlap Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260427184454.693794-1-rdunlap@infradead.org --- include/uapi/drm/tegra_drm.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h index 94cfc306d50a..8f21f3a44832 100644 --- a/include/uapi/drm/tegra_drm.h +++ b/include/uapi/drm/tegra_drm.h @@ -304,6 +304,7 @@ struct drm_tegra_cmdbuf { * struct drm_tegra_reloc - GEM object relocation structure */ struct drm_tegra_reloc { + /** @cmdbuf: cmd information */ struct { /** * @cmdbuf.handle: @@ -321,6 +322,7 @@ struct drm_tegra_reloc { */ __u32 offset; } cmdbuf; + /** @target: relocate target information */ struct { /** * @target.handle: @@ -778,6 +780,9 @@ struct drm_tegra_channel_unmap { /* Submission */ /** + * define DRM_TEGRA_SUBMIT_RELOC_SECTOR_LAYOUT - \ + * Select sector layout swizzling for in-memory buffers. + * * Specify that bit 39 of the patched-in address should be set to switch * swizzling between Tegra and non-Tegra sector layout on systems that store * surfaces in system memory in non-Tegra sector layout. @@ -830,16 +835,27 @@ struct drm_tegra_submit_buf { }; /** + * define DRM_TEGRA_SUBMIT_CMD_GATHER_UPTR - \ + * Execute Host1x opcodes from user pointer. + * * Execute `words` words of Host1x opcodes specified in the `gather_data_ptr` * buffer. Each GATHER_UPTR command uses successive words from the buffer. */ #define DRM_TEGRA_SUBMIT_CMD_GATHER_UPTR 0 + /** + * define DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT - \ + * Wait for syncpoint (absolute). + * * Wait for a syncpoint to reach a value before continuing with further * commands. */ #define DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT 1 + /** + * define DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT_RELATIVE - \ + * Wait for syncpoint (relative). + * * Wait for a syncpoint to reach a value before continuing with further * commands. The threshold is calculated relative to the start of the job. */ From d23bd83f3e47a928e783c0d6a004737519dc77dc Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 21 Apr 2026 09:29:05 +0200 Subject: [PATCH 46/71] drm/tegra: fbdev: Do not assign to struct drm_fb_helper.info That field already contains the value being assigned. No need to do this twice. Signed-off-by: Thomas Zimmermann Fixes: 63c971af4036 ("drm/fb-helper: Allocate and release fb_info in single place") Cc: linux-tegra@vger.kernel.org Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421073646.144712-2-tzimmermann@suse.de --- drivers/gpu/drm/tegra/fbdev.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index 8f40882aa76e..19e39fa54bfa 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -110,7 +110,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, helper->funcs = &tegra_fbdev_helper_funcs; helper->fb = fb; - helper->info = info; info->fbops = &tegra_fb_ops; From a18b6e30ecd69096beda4a0c96d2570900c3879a Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 21 Apr 2026 09:29:06 +0200 Subject: [PATCH 47/71] drm/tegra: fbdev: Remove offset into framebuffer memory The screen_buffer field in struct fb_info contains the kernel address of the first byte of framebuffer memory. Do not add the display offset. This offset only describes scrolling during scanout. Signed-off-by: Thomas Zimmermann Fixes: de2ba664c30f ("gpu: host1x: drm: Add memory manager and fb") Cc: dri-devel@lists.freedesktop.org Cc: linux-tegra@vger.kernel.org Cc: # v3.10+ Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421073646.144712-3-tzimmermann@suse.de --- drivers/gpu/drm/tegra/fbdev.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index 19e39fa54bfa..793849199783 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -76,7 +76,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct fb_info *info = helper->info; unsigned int bytes_per_pixel; struct drm_framebuffer *fb; - unsigned long offset; struct tegra_bo *bo; size_t size; int err; @@ -115,9 +114,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, drm_fb_helper_fill_info(info, helper, sizes); - offset = info->var.xoffset * bytes_per_pixel + - info->var.yoffset * fb->pitches[0]; - if (bo->pages) { bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP, pgprot_writecombine(PAGE_KERNEL)); @@ -129,9 +125,9 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, } info->flags |= FBINFO_VIRTFB; - info->screen_buffer = bo->vaddr + offset; + info->screen_buffer = bo->vaddr; info->screen_size = size; - info->fix.smem_start = (unsigned long)(bo->iova + offset); + info->fix.smem_start = (unsigned long)(bo->iova); info->fix.smem_len = size; return 0; From 8e94aa3637a25e5b9eff09161d3cc28c1759f850 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 21 Apr 2026 09:29:07 +0200 Subject: [PATCH 48/71] drm/tegra: fbdev: Calculate buffer geometry with format helpers Replace the geometry and size calculation in tegra's fbdev emulation with DRM format helpers. This consists of a 4CC lookup from the fbdev parameters, format lookup, pitch calculation and size calculation. Then allocate the GEM buffer object for the framebuffer memory from the calculated size. Set up mode_cmd with the calculated values just before allocating the framebuffer. This code will later be replaced with a DRM client buffer. Set framebuffer size fields in struct fb_info from the size stored in the GEM buffer object instead of what has been requested. The requested size is an estimate, while the buffer size is the exact value rounded to the correct alignment. Signed-off-by: Thomas Zimmermann Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421073646.144712-4-tzimmermann@suse.de --- drivers/gpu/drm/tegra/fbdev.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index 793849199783..bcf32cfcf818 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -74,31 +74,29 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct drm_device *drm = helper->dev; struct drm_mode_fb_cmd2 cmd = { 0 }; struct fb_info *info = helper->info; - unsigned int bytes_per_pixel; + u32 fourcc, pitch; + u64 size; + const struct drm_format_info *format; struct drm_framebuffer *fb; struct tegra_bo *bo; - size_t size; int err; - bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); - - cmd.width = sizes->surface_width; - cmd.height = sizes->surface_height; - cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, - tegra->pitch_align); - - cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, - sizes->surface_depth); - - size = cmd.pitches[0] * cmd.height; + fourcc = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); + format = drm_get_format_info(drm, fourcc, DRM_FORMAT_MOD_LINEAR); + pitch = round_up(drm_format_info_min_pitch(format, 0, sizes->surface_width), + tegra->pitch_align); + size = ALIGN(pitch * sizes->surface_height, PAGE_SIZE); bo = tegra_bo_create(drm, size, 0); if (IS_ERR(bo)) return PTR_ERR(bo); - fb = tegra_fb_alloc(drm, - drm_get_format_info(drm, cmd.pixel_format, cmd.modifier[0]), - &cmd, &bo, 1); + cmd.pixel_format = fourcc; + cmd.width = sizes->surface_width; + cmd.height = sizes->surface_height; + cmd.pitches[0] = pitch; + + fb = tegra_fb_alloc(drm, format, &cmd, &bo, 1); if (IS_ERR(fb)) { err = PTR_ERR(fb); dev_err(drm->dev, "failed to allocate DRM framebuffer: %d\n", @@ -126,9 +124,9 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, info->flags |= FBINFO_VIRTFB; info->screen_buffer = bo->vaddr; - info->screen_size = size; + info->screen_size = bo->gem.size; info->fix.smem_start = (unsigned long)(bo->iova); - info->fix.smem_len = size; + info->fix.smem_len = bo->gem.size; return 0; From 0daa4b50fc6a6e636f4f0b2b462860f36ef67a6a Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 21 Apr 2026 09:29:08 +0200 Subject: [PATCH 49/71] drm/tegra: fbdev: Use a DRM client buffer Replace the internal DRM framebuffer with a DRM client buffer. The client buffer allocates the DRM framebuffer on a file and also uses GEM object handles via the regular ADDFB2 interfaces. Using client-buffer interfaces unifies framebuffer allocation for DRM clients in user space and tegra's internal fbdev emulation. It also simplifies the clean-up side of the fbdev emulation. Signed-off-by: Thomas Zimmermann Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421073646.144712-5-tzimmermann@suse.de --- drivers/gpu/drm/tegra/fbdev.c | 59 +++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index bcf32cfcf818..003e80cf7b9a 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -40,8 +40,7 @@ static int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) static void tegra_fbdev_fb_destroy(struct fb_info *info) { struct drm_fb_helper *helper = info->par; - struct drm_framebuffer *fb = helper->fb; - struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); + struct tegra_bo *bo = tegra_fb_get_plane(helper->fb, 0); drm_fb_helper_fini(helper); @@ -50,8 +49,8 @@ static void tegra_fbdev_fb_destroy(struct fb_info *info) vunmap(bo->vaddr); bo->vaddr = NULL; } - drm_framebuffer_remove(fb); + drm_client_buffer_delete(helper->buffer); drm_client_release(&helper->client); } @@ -70,15 +69,18 @@ static const struct drm_fb_helper_funcs tegra_fbdev_helper_funcs = { int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { - struct tegra_drm *tegra = helper->dev->dev_private; - struct drm_device *drm = helper->dev; - struct drm_mode_fb_cmd2 cmd = { 0 }; + struct drm_client_dev *client = &helper->client; + struct drm_device *drm = client->dev; + struct drm_file *file = client->file; + struct tegra_drm *tegra = drm->dev_private; struct fb_info *info = helper->info; u32 fourcc, pitch; u64 size; const struct drm_format_info *format; - struct drm_framebuffer *fb; struct tegra_bo *bo; + struct drm_gem_object *gem; + u32 handle; + struct drm_client_buffer *buffer; int err; fourcc = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); @@ -90,23 +92,22 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, bo = tegra_bo_create(drm, size, 0); if (IS_ERR(bo)) return PTR_ERR(bo); + gem = &bo->gem; - cmd.pixel_format = fourcc; - cmd.width = sizes->surface_width; - cmd.height = sizes->surface_height; - cmd.pitches[0] = pitch; + err = drm_gem_handle_create(file, gem, &handle); + if (err) + goto err_drm_gem_object_put; - fb = tegra_fb_alloc(drm, format, &cmd, &bo, 1); - if (IS_ERR(fb)) { - err = PTR_ERR(fb); - dev_err(drm->dev, "failed to allocate DRM framebuffer: %d\n", - err); - drm_gem_object_put(&bo->gem); - return PTR_ERR(fb); + buffer = drm_client_buffer_create(client, sizes->surface_width, sizes->surface_height, + fourcc, handle, pitch); + if (IS_ERR(buffer)) { + err = PTR_ERR(buffer); + goto err_drm_gem_handle_delete; } helper->funcs = &tegra_fbdev_helper_funcs; - helper->fb = fb; + helper->buffer = buffer; + helper->fb = buffer->fb; info->fbops = &tegra_fb_ops; @@ -118,19 +119,29 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, if (!bo->vaddr) { dev_err(drm->dev, "failed to vmap() framebuffer\n"); err = -ENOMEM; - goto destroy; + goto err_drm_client_buffer_delete; } } info->flags |= FBINFO_VIRTFB; info->screen_buffer = bo->vaddr; - info->screen_size = bo->gem.size; + info->screen_size = gem->size; info->fix.smem_start = (unsigned long)(bo->iova); - info->fix.smem_len = bo->gem.size; + info->fix.smem_len = gem->size; + + /* The handle is only needed for creating the framebuffer. */ + drm_gem_handle_delete(file, handle); + + /* The framebuffer still holds a reference on the GEM object. */ + drm_gem_object_put(gem); return 0; -destroy: - drm_framebuffer_remove(fb); +err_drm_client_buffer_delete: + drm_client_buffer_delete(buffer); +err_drm_gem_handle_delete: + drm_gem_handle_delete(file, handle); +err_drm_gem_object_put: + drm_gem_object_put(gem); return err; } From edb4f5e630d9555461a48db89b4a20d5b7e160bb Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 21 Apr 2026 09:29:09 +0200 Subject: [PATCH 50/71] drm/tegra: Make tegra_fb_alloc() an internal interface Fbdev framebuffer allocation now goes through the regular ioctl call chain. This makes tegra_fb_alloc() an internal helper function. Declare it as static. Signed-off-by: Thomas Zimmermann Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421073646.144712-6-tzimmermann@suse.de --- drivers/gpu/drm/tegra/drm.h | 5 ----- drivers/gpu/drm/tegra/fb.c | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index ae68b03d8483..bc720ae8d95e 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -184,11 +184,6 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer); int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, struct tegra_bo_tiling *tiling); -struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm, - const struct drm_format_info *info, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct tegra_bo **planes, - unsigned int num_planes); struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, struct drm_file *file, const struct drm_format_info *info, diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 1e4803d355dd..0a7cb9e462ff 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -102,6 +102,7 @@ static const struct drm_framebuffer_funcs tegra_fb_funcs = { .create_handle = drm_gem_fb_create_handle, }; +static struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm, const struct drm_format_info *info, const struct drm_mode_fb_cmd2 *mode_cmd, From b217fb77ca4fd0cc267329500b291c0ad8f8b211 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Thu, 16 Apr 2026 20:09:29 +0800 Subject: [PATCH 51/71] gpu: host1x: mipi: Fix device_node reference leak in tegra_mipi_request() In tegra_mipi_request(), when provider.np is not equal with args.np, it returns without calling of_node_put(args.np), causing a reference leak. Convert to use the existing goto out pattern to ensure proper cleanup. Fixes: 767598d447aa ("gpu: host1x: mipi: Update tegra_mipi_request() to be node based") Signed-off-by: Felix Gu Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260416-mipi-v1-1-9c027175abdf@gmail.com --- drivers/gpu/host1x/mipi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c index 01513b775d89..988681423981 100644 --- a/drivers/gpu/host1x/mipi.c +++ b/drivers/gpu/host1x/mipi.c @@ -114,8 +114,10 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device, if (err < 0) return ERR_PTR(err); - if (provider.np != args.np) - return ERR_PTR(-ENODEV); + if (provider.np != args.np) { + err = -ENODEV; + goto out; + } mipidev = kzalloc_obj(*mipidev); if (!mipidev) { From e75717f9aec04355777be41070890c6a815c76df Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Apr 2026 22:15:26 +0800 Subject: [PATCH 52/71] gpu: host1x: Fix device reference leak in host1x_device_parse_dt() error path After device_initialize(), the embedded struct device in struct host1x_device should be released through the device core with put_device(). In host1x_device_add(), if host1x_device_parse_dt() fails, the current error path frees the object directly with kfree(device). That bypasses the normal device lifetime handling and leaks the reference held on the embedded struct device. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fix this by using put_device() in the host1x_device_parse_dt() failure path. Fixes: f4c5cf88fbd50 ("gpu: host1x: Provide a proper struct bus_type") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260413141526.2961841-1-lgs201920130244@gmail.com --- drivers/gpu/host1x/bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 70310d1e3162..e3884096c2fe 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -485,7 +485,7 @@ static int host1x_device_add(struct host1x *host1x, err = host1x_device_parse_dt(device, driver); if (err < 0) { - kfree(device); + put_device(&device->dev); return err; } From ad8357df04765bd9e92a30a5cf14c74071af864b Mon Sep 17 00:00:00 2001 From: Aaron Kling Date: Tue, 7 Apr 2026 02:09:37 -0500 Subject: [PATCH 53/71] drm/tegra: Enable cmu for Tegra186 and Tegra194 Without the cmu, nvdisplay will display colors that are notably darker than intended. The vendor bootloader and the downstream display driver enable the cmu and sets a sRGB table. Loading that table here results in the intended colors. Co-developed-by: Kurt Kiefer Signed-off-by: Kurt Kiefer Signed-off-by: Aaron Kling Tested-by: Jasper Korten Reviewed-by: Mikko Perttunen Tested-by: Mikko Perttunen # Jetson AGX Xavier Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260407-tegra-drm-cmu-v4-1-2fe95f305afd@gmail.com --- drivers/gpu/drm/tegra/dc.c | 116 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/dc.h | 13 ++++ drivers/gpu/drm/tegra/sor.c | 25 ++++++++ 3 files changed, 154 insertions(+) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index de683644d658..445be1b2d3c5 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -36,6 +36,103 @@ #include "hub.h" #include "plane.h" +static const u16 default_srgb_lut[] = { + 0x6000, 0x60CE, 0x619D, 0x626C, 0x632D, 0x63D4, 0x6469, 0x64F0, 0x656B, 0x65DF, 0x664A, + 0x66B0, 0x6711, 0x676D, 0x67C4, 0x6819, 0x686A, 0x68B8, 0x6904, 0x694D, 0x6994, 0x69D8, + 0x6A1B, 0x6A5D, 0x6A9C, 0x6ADA, 0x6B17, 0x6B52, 0x6B8C, 0x6BC5, 0x6BFD, 0x6C33, 0x6C69, + 0x6C9E, 0x6CD1, 0x6D04, 0x6D36, 0x6D67, 0x6D98, 0x6DC7, 0x6DF6, 0x6E25, 0x6E52, 0x6E7F, + 0x6EAC, 0x6ED7, 0x6F03, 0x6F2D, 0x6F58, 0x6F81, 0x6FAA, 0x6FD3, 0x6FFB, 0x7023, 0x704B, + 0x7071, 0x7098, 0x70BE, 0x70E4, 0x7109, 0x712E, 0x7153, 0x7177, 0x719B, 0x71BF, 0x71E2, + 0x7205, 0x7227, 0x724A, 0x726C, 0x728E, 0x72AF, 0x72D0, 0x72F1, 0x7312, 0x7333, 0x7353, + 0x7373, 0x7392, 0x73B2, 0x73D1, 0x73F0, 0x740F, 0x742D, 0x744C, 0x746A, 0x7488, 0x74A6, + 0x74C3, 0x74E0, 0x74FE, 0x751B, 0x7537, 0x7554, 0x7570, 0x758D, 0x75A9, 0x75C4, 0x75E0, + 0x75FC, 0x7617, 0x7632, 0x764D, 0x7668, 0x7683, 0x769E, 0x76B8, 0x76D3, 0x76ED, 0x7707, + 0x7721, 0x773B, 0x7754, 0x776E, 0x7787, 0x77A0, 0x77B9, 0x77D2, 0x77EB, 0x7804, 0x781D, + 0x7835, 0x784E, 0x7866, 0x787E, 0x7896, 0x78AE, 0x78C6, 0x78DD, 0x78F5, 0x790D, 0x7924, + 0x793B, 0x7952, 0x796A, 0x7981, 0x7997, 0x79AE, 0x79C5, 0x79DB, 0x79F2, 0x7A08, 0x7A1F, + 0x7A35, 0x7A4B, 0x7A61, 0x7A77, 0x7A8D, 0x7AA3, 0x7AB8, 0x7ACE, 0x7AE3, 0x7AF9, 0x7B0E, + 0x7B24, 0x7B39, 0x7B4E, 0x7B63, 0x7B78, 0x7B8D, 0x7BA2, 0x7BB6, 0x7BCB, 0x7BE0, 0x7BF4, + 0x7C08, 0x7C1D, 0x7C31, 0x7C45, 0x7C59, 0x7C6E, 0x7C82, 0x7C96, 0x7CA9, 0x7CBD, 0x7CD1, + 0x7CE5, 0x7CF8, 0x7D0C, 0x7D1F, 0x7D33, 0x7D46, 0x7D59, 0x7D6D, 0x7D80, 0x7D93, 0x7DA6, + 0x7DB9, 0x7DCC, 0x7DDF, 0x7DF2, 0x7E04, 0x7E17, 0x7E2A, 0x7E3C, 0x7E4F, 0x7E61, 0x7E74, + 0x7E86, 0x7E98, 0x7EAB, 0x7EBD, 0x7ECF, 0x7EE1, 0x7EF3, 0x7F05, 0x7F17, 0x7F29, 0x7F3B, + 0x7F4D, 0x7F5E, 0x7F70, 0x7F82, 0x7F93, 0x7FA5, 0x7FB6, 0x7FC8, 0x7FD9, 0x7FEB, 0x7FFC, + 0x800D, 0x801E, 0x8030, 0x8041, 0x8052, 0x8063, 0x8074, 0x8085, 0x8096, 0x80A7, 0x80B7, + 0x80C8, 0x80D9, 0x80EA, 0x80FA, 0x810B, 0x811C, 0x812C, 0x813D, 0x814D, 0x815D, 0x816E, + 0x817E, 0x818E, 0x819F, 0x81AF, 0x81BF, 0x81CF, 0x81DF, 0x81EF, 0x81FF, 0x820F, 0x821F, + 0x822F, 0x823F, 0x824F, 0x825F, 0x826F, 0x827E, 0x828E, 0x829E, 0x82AD, 0x82BD, 0x82CC, + 0x82DC, 0x82EB, 0x82FB, 0x830A, 0x831A, 0x8329, 0x8338, 0x8348, 0x8357, 0x8366, 0x8375, + 0x8385, 0x8394, 0x83A3, 0x83B2, 0x83C1, 0x83D0, 0x83DF, 0x83EE, 0x83FD, 0x840C, 0x841A, + 0x8429, 0x8438, 0x8447, 0x8455, 0x8464, 0x8473, 0x8481, 0x8490, 0x849F, 0x84AD, 0x84BC, + 0x84CA, 0x84D9, 0x84E7, 0x84F5, 0x8504, 0x8512, 0x8521, 0x852F, 0x853D, 0x854B, 0x855A, + 0x8568, 0x8576, 0x8584, 0x8592, 0x85A0, 0x85AE, 0x85BC, 0x85CA, 0x85D8, 0x85E6, 0x85F4, + 0x8602, 0x8610, 0x861E, 0x862C, 0x8639, 0x8647, 0x8655, 0x8663, 0x8670, 0x867E, 0x868C, + 0x8699, 0x86A7, 0x86B5, 0x86C2, 0x86D0, 0x86DD, 0x86EB, 0x86F8, 0x8705, 0x8713, 0x8720, + 0x872E, 0x873B, 0x8748, 0x8756, 0x8763, 0x8770, 0x877D, 0x878B, 0x8798, 0x87A5, 0x87B2, + 0x87BF, 0x87CC, 0x87D9, 0x87E6, 0x87F3, 0x8801, 0x880E, 0x881A, 0x8827, 0x8834, 0x8841, + 0x884E, 0x885B, 0x8868, 0x8875, 0x8882, 0x888E, 0x889B, 0x88A8, 0x88B5, 0x88C1, 0x88CE, + 0x88DB, 0x88E7, 0x88F4, 0x8900, 0x890D, 0x891A, 0x8926, 0x8933, 0x893F, 0x894C, 0x8958, + 0x8965, 0x8971, 0x897D, 0x898A, 0x8996, 0x89A3, 0x89AF, 0x89BB, 0x89C8, 0x89D4, 0x89E0, + 0x89EC, 0x89F9, 0x8A05, 0x8A11, 0x8A1D, 0x8A29, 0x8A36, 0x8A42, 0x8A4E, 0x8A5A, 0x8A66, + 0x8A72, 0x8A7E, 0x8A8A, 0x8A96, 0x8AA2, 0x8AAE, 0x8ABA, 0x8AC6, 0x8AD2, 0x8ADE, 0x8AEA, + 0x8AF5, 0x8B01, 0x8B0D, 0x8B19, 0x8B25, 0x8B31, 0x8B3C, 0x8B48, 0x8B54, 0x8B60, 0x8B6B, + 0x8B77, 0x8B83, 0x8B8E, 0x8B9A, 0x8BA6, 0x8BB1, 0x8BBD, 0x8BC8, 0x8BD4, 0x8BDF, 0x8BEB, + 0x8BF6, 0x8C02, 0x8C0D, 0x8C19, 0x8C24, 0x8C30, 0x8C3B, 0x8C47, 0x8C52, 0x8C5D, 0x8C69, + 0x8C74, 0x8C80, 0x8C8B, 0x8C96, 0x8CA1, 0x8CAD, 0x8CB8, 0x8CC3, 0x8CCF, 0x8CDA, 0x8CE5, + 0x8CF0, 0x8CFB, 0x8D06, 0x8D12, 0x8D1D, 0x8D28, 0x8D33, 0x8D3E, 0x8D49, 0x8D54, 0x8D5F, + 0x8D6A, 0x8D75, 0x8D80, 0x8D8B, 0x8D96, 0x8DA1, 0x8DAC, 0x8DB7, 0x8DC2, 0x8DCD, 0x8DD8, + 0x8DE3, 0x8DEE, 0x8DF9, 0x8E04, 0x8E0E, 0x8E19, 0x8E24, 0x8E2F, 0x8E3A, 0x8E44, 0x8E4F, + 0x8E5A, 0x8E65, 0x8E6F, 0x8E7A, 0x8E85, 0x8E90, 0x8E9A, 0x8EA5, 0x8EB0, 0x8EBA, 0x8EC5, + 0x8ECF, 0x8EDA, 0x8EE5, 0x8EEF, 0x8EFA, 0x8F04, 0x8F0F, 0x8F19, 0x8F24, 0x8F2E, 0x8F39, + 0x8F43, 0x8F4E, 0x8F58, 0x8F63, 0x8F6D, 0x8F78, 0x8F82, 0x8F8C, 0x8F97, 0x8FA1, 0x8FAC, + 0x8FB6, 0x8FC0, 0x8FCB, 0x8FD5, 0x8FDF, 0x8FEA, 0x8FF4, 0x8FFE, 0x9008, 0x9013, 0x901D, + 0x9027, 0x9031, 0x903C, 0x9046, 0x9050, 0x905A, 0x9064, 0x906E, 0x9079, 0x9083, 0x908D, + 0x9097, 0x90A1, 0x90AB, 0x90B5, 0x90BF, 0x90C9, 0x90D3, 0x90DD, 0x90E7, 0x90F1, 0x90FB, + 0x9105, 0x910F, 0x9119, 0x9123, 0x912D, 0x9137, 0x9141, 0x914B, 0x9155, 0x915F, 0x9169, + 0x9173, 0x917D, 0x9186, 0x9190, 0x919A, 0x91A4, 0x91AE, 0x91B8, 0x91C1, 0x91CB, 0x91D5, + 0x91DF, 0x91E9, 0x91F2, 0x91FC, 0x9206, 0x9210, 0x9219, 0x9223, 0x922D, 0x9236, 0x9240, + 0x924A, 0x9253, 0x925D, 0x9267, 0x9270, 0x927A, 0x9283, 0x928D, 0x9297, 0x92A0, 0x92AA, + 0x92B3, 0x92BD, 0x92C6, 0x92D0, 0x92DA, 0x92E3, 0x92ED, 0x92F6, 0x9300, 0x9309, 0x9313, + 0x931C, 0x9325, 0x932F, 0x9338, 0x9342, 0x934B, 0x9355, 0x935E, 0x9367, 0x9371, 0x937A, + 0x9384, 0x938D, 0x9396, 0x93A0, 0x93A9, 0x93B2, 0x93BC, 0x93C5, 0x93CE, 0x93D7, 0x93E1, + 0x93EA, 0x93F3, 0x93FC, 0x9406, 0x940F, 0x9418, 0x9421, 0x942B, 0x9434, 0x943D, 0x9446, + 0x944F, 0x9459, 0x9462, 0x946B, 0x9474, 0x947D, 0x9486, 0x948F, 0x9499, 0x94A2, 0x94AB, + 0x94B4, 0x94BD, 0x94C6, 0x94CF, 0x94D8, 0x94E1, 0x94EA, 0x94F3, 0x94FC, 0x9505, 0x950E, + 0x9517, 0x9520, 0x9529, 0x9532, 0x953B, 0x9544, 0x954D, 0x9556, 0x955F, 0x9568, 0x9571, + 0x957A, 0x9583, 0x958C, 0x9595, 0x959D, 0x95A6, 0x95AF, 0x95B8, 0x95C1, 0x95CA, 0x95D3, + 0x95DB, 0x95E4, 0x95ED, 0x95F6, 0x95FF, 0x9608, 0x9610, 0x9619, 0x9622, 0x962B, 0x9633, + 0x963C, 0x9645, 0x964E, 0x9656, 0x965F, 0x9668, 0x9671, 0x9679, 0x9682, 0x968B, 0x9693, + 0x969C, 0x96A5, 0x96AD, 0x96B6, 0x96BF, 0x96C7, 0x96D0, 0x96D9, 0x96E1, 0x96EA, 0x96F2, + 0x96FB, 0x9704, 0x970C, 0x9715, 0x971D, 0x9726, 0x972E, 0x9737, 0x9740, 0x9748, 0x9751, + 0x9759, 0x9762, 0x976A, 0x9773, 0x977B, 0x9784, 0x978C, 0x9795, 0x979D, 0x97A6, 0x97AE, + 0x97B6, 0x97BF, 0x97C7, 0x97D0, 0x97D8, 0x97E1, 0x97E9, 0x97F1, 0x97FA, 0x9802, 0x980B, + 0x9813, 0x981B, 0x9824, 0x982C, 0x9834, 0x983D, 0x9845, 0x984D, 0x9856, 0x985E, 0x9866, + 0x986F, 0x9877, 0x987F, 0x9888, 0x9890, 0x9898, 0x98A0, 0x98A9, 0x98B1, 0x98B9, 0x98C1, + 0x98CA, 0x98D2, 0x98DA, 0x98E2, 0x98EB, 0x98F3, 0x98FB, 0x9903, 0x990B, 0x9914, 0x991C, + 0x9924, 0x992C, 0x9934, 0x993C, 0x9945, 0x994D, 0x9955, 0x995D, 0x9965, 0x996D, 0x9975, + 0x997D, 0x9986, 0x998E, 0x9996, 0x999E, 0x99A6, 0x99AE, 0x99B6, 0x99BE, 0x99C6, 0x99CE, + 0x99D6, 0x99DE, 0x99E6, 0x99EE, 0x99F6, 0x99FE, 0x9A06, 0x9A0E, 0x9A16, 0x9A1E, 0x9A26, + 0x9A2E, 0x9A36, 0x9A3E, 0x9A46, 0x9A4E, 0x9A56, 0x9A5E, 0x9A66, 0x9A6E, 0x9A76, 0x9A7E, + 0x9A86, 0x9A8E, 0x9A96, 0x9A9D, 0x9AA5, 0x9AAD, 0x9AB5, 0x9ABD, 0x9AC5, 0x9ACD, 0x9AD5, + 0x9ADC, 0x9AE4, 0x9AEC, 0x9AF4, 0x9AFC, 0x9B04, 0x9B0C, 0x9B13, 0x9B1B, 0x9B23, 0x9B2B, + 0x9B33, 0x9B3A, 0x9B42, 0x9B4A, 0x9B52, 0x9B59, 0x9B61, 0x9B69, 0x9B71, 0x9B79, 0x9B80, + 0x9B88, 0x9B90, 0x9B97, 0x9B9F, 0x9BA7, 0x9BAF, 0x9BB6, 0x9BBE, 0x9BC6, 0x9BCD, 0x9BD5, + 0x9BDD, 0x9BE5, 0x9BEC, 0x9BF4, 0x9BFC, 0x9C03, 0x9C0B, 0x9C12, 0x9C1A, 0x9C22, 0x9C29, + 0x9C31, 0x9C39, 0x9C40, 0x9C48, 0x9C50, 0x9C57, 0x9C5F, 0x9C66, 0x9C6E, 0x9C75, 0x9C7D, + 0x9C85, 0x9C8C, 0x9C94, 0x9C9B, 0x9CA3, 0x9CAA, 0x9CB2, 0x9CBA, 0x9CC1, 0x9CC9, 0x9CD0, + 0x9CD8, 0x9CDF, 0x9CE7, 0x9CEE, 0x9CF6, 0x9CFD, 0x9D05, 0x9D0C, 0x9D14, 0x9D1B, 0x9D23, + 0x9D2A, 0x9D32, 0x9D39, 0x9D40, 0x9D48, 0x9D4F, 0x9D57, 0x9D5E, 0x9D66, 0x9D6D, 0x9D75, + 0x9D7C, 0x9D83, 0x9D8B, 0x9D92, 0x9D9A, 0x9DA1, 0x9DA8, 0x9DB0, 0x9DB7, 0x9DBE, 0x9DC6, + 0x9DCD, 0x9DD5, 0x9DDC, 0x9DE3, 0x9DEB, 0x9DF2, 0x9DF9, 0x9E01, 0x9E08, 0x9E0F, 0x9E17, + 0x9E1E, 0x9E25, 0x9E2D, 0x9E34, 0x9E3B, 0x9E43, 0x9E4A, 0x9E51, 0x9E58, 0x9E60, 0x9E67, + 0x9E6E, 0x9E75, 0x9E7D, 0x9E84, 0x9E8B, 0x9E92, 0x9E9A, 0x9EA1, 0x9EA8, 0x9EAF, 0x9EB7, + 0x9EBE, 0x9EC5, 0x9ECC, 0x9ED4, 0x9EDB, 0x9EE2, 0x9EE9, 0x9EF0, 0x9EF7, 0x9EFF, 0x9F06, + 0x9F0D, 0x9F14, 0x9F1B, 0x9F23, 0x9F2A, 0x9F31, 0x9F38, 0x9F3F, 0x9F46, 0x9F4D, 0x9F55, + 0x9F5C, 0x9F63, 0x9F6A, 0x9F71, 0x9F78, 0x9F7F, 0x9F86, 0x9F8D, 0x9F95, 0x9F9C, 0x9FA3, + 0x9FAA, 0x9FB1, 0x9FB8, 0x9FBF, 0x9FC6, 0x9FCD, 0x9FD4, 0x9FDB, 0x9FE2, 0x9FE9, 0x9FF0, + 0x9FF7, 0x9FFF, +}; + static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); @@ -3251,6 +3348,25 @@ static int tegra_dc_probe(struct platform_device *pdev) if (dc->irq < 0) return -ENXIO; + if (dc->soc->has_nvdisplay) { + unsigned int i; + u64 r; + + dc->cmu_output_lut = + dmam_alloc_coherent(dc->dev, ARRAY_SIZE(default_srgb_lut) * sizeof(u64), + &dc->cmu_output_lut_phys, GFP_KERNEL); + + if (!dc->cmu_output_lut) { + dev_err(dc->dev, "failed to allocate lut for cmu\n"); + return -ENOMEM; + } + + for (i = 0; i < ARRAY_SIZE(default_srgb_lut); i++) { + r = default_srgb_lut[i]; + dc->cmu_output_lut[i] = (r << 32) | (r << 16) | r; + } + } + err = tegra_dc_rgb_probe(dc); if (err < 0 && err != -ENODEV) return dev_err_probe(&pdev->dev, err, diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 577c2e8e3ab3..951a61673cd7 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -103,6 +103,9 @@ struct tegra_dc { const struct tegra_dc_soc_info *soc; bool has_opp_table; + + u64 *cmu_output_lut; + dma_addr_t cmu_output_lut_phys; }; static inline struct tegra_dc * @@ -447,6 +450,7 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define BASE_COLOR_SIZE_888 ( 8 << 0) #define BASE_COLOR_SIZE_101010 ( 10 << 0) #define BASE_COLOR_SIZE_121212 ( 12 << 0) +#define CMU_ENABLE_ENABLE (1 << 20) #define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431 #define SC1_H_QUALIFIER_NONE (1 << 16) @@ -732,6 +736,15 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define PROTOCOL_MASK (0xf << 8) #define PROTOCOL_SINGLE_TMDS_A (0x1 << 8) +#define DC_DISP_CORE_HEAD_SET_CONTROL_OUTPUT_LUT 0x431 +#define OUTPUT_LUT_MODE_MASK (3 << 5) +#define OUTPUT_LUT_MODE_INTERPOLATE (1 << 5) +#define OUTPUT_LUT_SIZE_MASK (3 << 1) +#define OUTPUT_LUT_SIZE_SIZE_1025 (2 << 1) + +#define DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE 0x432 +#define DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE_HI 0x433 + #define DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR 0x442 #define DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR 0x446 diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 865b211ae752..43409439e9f4 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -2558,6 +2558,17 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL); value &= ~DITHER_CONTROL_MASK; value &= ~BASE_COLOR_SIZE_MASK; + if (dc->soc->has_nvdisplay) { + tegra_dc_writel(dc, lower_32_bits(dc->cmu_output_lut_phys), + DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE); + tegra_dc_writel(dc, upper_32_bits(dc->cmu_output_lut_phys), + DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE_HI); + + tegra_dc_writel(dc, OUTPUT_LUT_MODE_INTERPOLATE | OUTPUT_LUT_SIZE_SIZE_1025, + DC_DISP_CORE_HEAD_SET_CONTROL_OUTPUT_LUT); + + value |= CMU_ENABLE_ENABLE; + } switch (state->bpc) { case 6: @@ -2922,6 +2933,20 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder) if (err < 0) dev_err(sor->dev, "failed to attach SOR: %d\n", err); + if (dc->soc->has_nvdisplay) { + value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL); + tegra_dc_writel(dc, lower_32_bits(dc->cmu_output_lut_phys), + DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE); + tegra_dc_writel(dc, upper_32_bits(dc->cmu_output_lut_phys), + DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE_HI); + + tegra_dc_writel(dc, OUTPUT_LUT_MODE_INTERPOLATE | OUTPUT_LUT_SIZE_SIZE_1025, + DC_DISP_CORE_HEAD_SET_CONTROL_OUTPUT_LUT); + + value |= CMU_ENABLE_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_COLOR_CONTROL); + } + value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value |= SOR_ENABLE(sor->index); tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); From 63a3998d792ab5c45304bf879e385a31fa923b61 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Wed, 28 Jan 2026 00:43:10 +0800 Subject: [PATCH 54/71] drm/tegra: dc: Fix device node reference leak in tegra_dc_has_output() The of_for_each_phandle() macro increments the reference count of the device node it iterates over. If the loop exits early, the reference must be released manually. In tegra_dc_has_output(), the function returns true immediately when a match is found, failing to release the current node's reference. Fix this by adding a call to of_node_put() before returning from the loop. Fixes: c57997bce423 ("drm/tegra: sor: Add Tegra186 support") Signed-off-by: Felix Gu Acked-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260128-dc-v1-1-a88205826301@gmail.com --- drivers/gpu/drm/tegra/dc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 445be1b2d3c5..3842ca34a16e 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -198,8 +198,10 @@ bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) int err; of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) - if (it.node == dev->of_node) + if (it.node == dev->of_node) { + of_node_put(it.node); return true; + } return false; } From 0ec0c5949106c5f1a7b50d4d7f34461cdd522264 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 9 Jan 2026 12:45:07 +0000 Subject: [PATCH 55/71] gpu: host1x: Fix missing 'host1x_context_device_bus_type' The drivers/gpu/host1x/context_bus.c does not include any declaration of host1x_context_device_bus_type, and after including "context.h" it also showed that there are two definitions in the kernel because the extern declaration was missing the const qualifier. Include linux/host1x_context_bus.h and drop the wrong declaration from context.h. While at it, also predeclare struct host1x_memory_context. Fixes the following sparse warning: drivers/gpu/host1x/context_bus.c:9:23: warning: symbol 'host1x_context_device_bus_type' was not declared. Should it be static? Signed-off-by: Ben Dooks [treding@nvidia.com: minor fixups, reword commit message to reflect changes] Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260123140512.609167-1-ben.dooks@codethink.co.uk --- drivers/gpu/host1x/context.c | 1 + drivers/gpu/host1x/context.h | 3 +-- drivers/gpu/host1x/context_bus.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x/context.c b/drivers/gpu/host1x/context.c index d50d41c20561..512f3d189ebf 100644 --- a/drivers/gpu/host1x/context.c +++ b/drivers/gpu/host1x/context.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include diff --git a/drivers/gpu/host1x/context.h b/drivers/gpu/host1x/context.h index 3e03bc1d3bac..5ab5be1a6c63 100644 --- a/drivers/gpu/host1x/context.h +++ b/drivers/gpu/host1x/context.h @@ -12,8 +12,7 @@ #include struct host1x; - -extern struct bus_type host1x_context_device_bus_type; +struct host1x_memory_context; struct host1x_memory_context_list { struct mutex lock; diff --git a/drivers/gpu/host1x/context_bus.c b/drivers/gpu/host1x/context_bus.c index 7cd0e1a5edd1..398d56e94c73 100644 --- a/drivers/gpu/host1x/context_bus.c +++ b/drivers/gpu/host1x/context_bus.c @@ -4,6 +4,7 @@ */ #include +#include #include const struct bus_type host1x_context_device_bus_type = { From 18f74762013a4b6aa6f905c4459e0f506f9c5c7b Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Tue, 21 Apr 2026 13:02:36 +0900 Subject: [PATCH 56/71] gpu: host1x: Fix iommu_map_sgtable() return value check Commit "iommu: return full error code from iommu_map_sg[_atomic]()" changed iommu_map_sgtable() to return an ssize_t and negative values in error cases, rather than a size_t and a zero. pin_job() also was incorrectly assigning to 'int', which could cause overflows into negative values. Update pin_job() to correctly check for errors from iommu_map_sgtable. Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421-iommu_map_sgtable-return-v1-1-fb484c07d2a1@nvidia.com --- drivers/gpu/host1x/job.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index 3ed49e1fd933..70bda32f1ff4 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c @@ -235,6 +235,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) } if (host->domain) { + ssize_t map_err; + for_each_sgtable_sg(map->sgt, sg, j) gather_size += sg->length; @@ -248,11 +250,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) goto put; } - err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc), - map->sgt, IOMMU_READ); - if (err == 0) { + map_err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc), + map->sgt, IOMMU_READ); + if (map_err < 0) { __free_iova(&host->iova, alloc); - err = -EINVAL; + err = map_err; goto put; } From b3f349517de8f4469c385ebb7bfdfcc148790c0f Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Tue, 21 Apr 2026 13:02:37 +0900 Subject: [PATCH 57/71] drm/tegra: Fix iommu_map_sgtable() return value check Commit "iommu: return full error code from iommu_map_sg[_atomic]()" changed iommu_map_sgtable() to return an ssize_t and negative values in error cases, rather than a size_t and a zero. Update tegra_bo_iommu_map() to correctly check for errors from iommu_map_sgtable. Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://patch.msgid.link/20260421-iommu_map_sgtable-return-v1-2-fb484c07d2a1@nvidia.com --- drivers/gpu/drm/tegra/gem.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 2377e2b76397..436394e04812 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -234,6 +234,7 @@ static const struct host1x_bo_ops tegra_bo_ops = { static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) { int prot = IOMMU_READ | IOMMU_WRITE; + ssize_t size; int err; if (bo->mm) @@ -255,13 +256,15 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) bo->iova = bo->mm->start; - bo->size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot); - if (!bo->size) { + size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot); + if (size < 0) { dev_err(tegra->drm->dev, "failed to map buffer\n"); - err = -ENOMEM; + err = size; goto remove; } + bo->size = size; + mutex_unlock(&tegra->mm_lock); return 0; From 281fe11c6c4aebc1a1eb9d21eaab7323ee5af979 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Thu, 28 May 2026 14:24:47 +0800 Subject: [PATCH 58/71] nouveau/gsp: cleanup IS_ERR_OR_NULL in rm_alloc functions The underlying functions already return error pointers, so checking for NULL with IS_ERR_OR_NULL() is redundant. Use IS_ERR() instead. This affects: - nvkm_gsp_rm_alloc_get() - nvkm_gsp_rm_alloc() Signed-off-by: Hongling Zeng Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260528062451.54107-2-zenghongling@kylinos.cn Reviewed-by: Lyude Paul --- drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h index 64fed208e4cf..ab2bd88eebce 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h @@ -373,7 +373,7 @@ nvkm_gsp_rm_alloc_get(struct nvkm_gsp_object *parent, u32 handle, u32 oclass, u3 object->handle = handle; argv = gsp->rm->api->alloc->get(object, oclass, argc); - if (IS_ERR_OR_NULL(argv)) { + if (IS_ERR(argv)) { object->client = NULL; return argv; } @@ -415,8 +415,8 @@ nvkm_gsp_rm_alloc(struct nvkm_gsp_object *parent, u32 handle, u32 oclass, u32 ar { void *argv = nvkm_gsp_rm_alloc_get(parent, handle, oclass, argc, object); - if (IS_ERR_OR_NULL(argv)) - return argv ? PTR_ERR(argv) : -EIO; + if (IS_ERR(argv)) + return PTR_ERR(argv); return nvkm_gsp_rm_alloc_wr(object, argv); } From 47f15f6cf068c14d1a5054066c445bee23f6047e Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Thu, 28 May 2026 14:24:48 +0800 Subject: [PATCH 59/71] nouveau/gsp: cleanup IS_ERR_OR_NULL in rpc_rd The underlying nvkm_gsp_rpc_get() function returns error pointers, so checking for NULL with IS_ERR_OR_NULL() is redundant. Use IS_ERR() instead. This affects nvkm_gsp_rpc_rd(). Signed-off-by: Hongling Zeng Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260528062451.54107-3-zenghongling@kylinos.cn Reviewed-by: Lyude Paul --- drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h index ab2bd88eebce..d771134fa410 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h @@ -293,7 +293,7 @@ nvkm_gsp_rpc_rd(struct nvkm_gsp *gsp, u32 fn, u32 argc) { void *argv = nvkm_gsp_rpc_get(gsp, fn, argc); - if (IS_ERR_OR_NULL(argv)) + if (IS_ERR(argv)) return argv; return nvkm_gsp_rpc_push(gsp, argv, NVKM_GSP_RPC_REPLY_RECV, argc); From 67346c90ce275e835e93a4a13041afee47bd3f9e Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Thu, 28 May 2026 14:24:49 +0800 Subject: [PATCH 60/71] nouveau/gsp/rm: cleanup IS_ERR_OR_NULL in core implementation Clean up IS_ERR_OR_NULL() checks in the core RPC and alloc implementation files. The underlying functions return error pointers, so IS_ERR() is sufficient. This affects: - r535_gsp_rpc_rm_free() in alloc.c - r535_gsp_rpc_rm_alloc_push() in alloc.c - r535_gsp_msgq_recv() in rpc.c Signed-off-by: Hongling Zeng Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260528062451.54107-4-zenghongling@kylinos.cn Reviewed-by: Lyude Paul --- drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c | 4 ++-- drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c index 46e3a29f2ad7..27f275d2e151 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c @@ -35,7 +35,7 @@ r535_gsp_rpc_rm_free(struct nvkm_gsp_object *object) client->object.handle, object->handle); rpc = nvkm_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_FREE, sizeof(*rpc)); - if (WARN_ON(IS_ERR_OR_NULL(rpc))) + if (WARN_ON(IS_ERR(rpc))) return -EIO; rpc->params.hRoot = client->object.handle; @@ -60,7 +60,7 @@ r535_gsp_rpc_rm_alloc_push(struct nvkm_gsp_object *object, void *params) void *ret = NULL; rpc = nvkm_gsp_rpc_push(gsp, rpc, NVKM_GSP_RPC_REPLY_RECV, sizeof(*rpc)); - if (IS_ERR_OR_NULL(rpc)) + if (IS_ERR(rpc)) return rpc; if (rpc->status) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c index 3ca3de8f4340..41301f19729c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c @@ -324,7 +324,7 @@ r535_gsp_msgq_recv(struct nvkm_gsp *gsp, u32 gsp_rpc_len, int *retries) u32 size; rpc = r535_gsp_msgq_peek(gsp, sizeof(*rpc), info.retries); - if (IS_ERR_OR_NULL(rpc)) { + if (IS_ERR(rpc)) { kvfree(buf); return rpc; } @@ -333,7 +333,7 @@ r535_gsp_msgq_recv(struct nvkm_gsp *gsp, u32 gsp_rpc_len, int *retries) info.continuation = true; rpc = r535_gsp_msgq_recv_one_elem(gsp, &info); - if (IS_ERR_OR_NULL(rpc)) { + if (IS_ERR(rpc)) { kvfree(buf); return rpc; } From 6198977a78af8769d3f3108e830901325b97cf03 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Thu, 28 May 2026 14:24:50 +0800 Subject: [PATCH 61/71] nouveau/gsp/rm: cleanup WARN_ON(IS_ERR_OR_NULL) Replace WARN_ON(IS_ERR_OR_NULL()) with WARN_ON(IS_ERR()) in various GSP-RM files. The underlying functions return error pointers, so checking for NULL is redundant. This affects: - r535_bar_bar2_update_pde() in bar.c Signed-off-by: Hongling Zeng Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260528062451.54107-5-zenghongling@kylinos.cn Reviewed-by: Lyude Paul --- drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c index fae08ac3b18c..9cd68f8622d3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c @@ -55,7 +55,7 @@ r535_bar_bar2_update_pde(struct nvkm_gsp *gsp, u8 page_shift, u64 pdbe) rpc_update_bar_pde_v15_00 *rpc; rpc = nvkm_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_UPDATE_BAR_PDE, sizeof(*rpc)); - if (WARN_ON(IS_ERR_OR_NULL(rpc))) + if (WARN_ON(IS_ERR(rpc))) return -EIO; rpc->info.barType = NV_RPC_UPDATE_PDE_BAR_2; From 1a80c009e27b42e8c202d4c5dbd9dad9e22af742 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Thu, 28 May 2026 14:24:51 +0800 Subject: [PATCH 62/71] nouveau/gsp/rm: cleanup remaining IS_ERR_OR_NULL usage Clean up the remaining IS_ERR_OR_NULL() checks in ctrl.c and rpc.c. The underlying functions return error pointers, so IS_ERR() is sufficient. This affects: - r535_gsp_rpc_ctrl() in ctrl.c - r535_gsp_rpc_ctor() in rpc.c Signed-off-by: Hongling Zeng Signed-off-by: Lyude Paul Link: https://patch.msgid.link/20260528062451.54107-6-zenghongling@kylinos.cn Reviewed-by: Lyude Paul --- drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c | 2 +- drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c index 70b9ee911c5e..aa0ebd3dfb17 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c @@ -42,7 +42,7 @@ r535_gsp_rpc_rm_ctrl_push(struct nvkm_gsp_object *object, void **params, u32 rep int ret = 0; rpc = nvkm_gsp_rpc_push(gsp, rpc, NVKM_GSP_RPC_REPLY_RECV, repc); - if (IS_ERR_OR_NULL(rpc)) { + if (IS_ERR(rpc)) { *params = NULL; return PTR_ERR(rpc); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c index 41301f19729c..0c9657cb2dd7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c @@ -459,11 +459,11 @@ r535_gsp_msg_recv(struct nvkm_gsp *gsp, int fn, u32 gsp_rpc_len) retry: rpc = r535_gsp_msgq_peek(gsp, sizeof(*rpc), &retries); - if (IS_ERR_OR_NULL(rpc)) + if (IS_ERR(rpc)) return rpc; rpc = r535_gsp_msgq_recv(gsp, gsp_rpc_len, &retries); - if (IS_ERR_OR_NULL(rpc)) + if (IS_ERR(rpc)) return rpc; if (rpc->rpc_result) { @@ -561,7 +561,7 @@ r535_gsp_rpc_handle_reply(struct nvkm_gsp *gsp, u32 fn, break; case NVKM_GSP_RPC_REPLY_RECV: reply = r535_gsp_msg_recv(gsp, fn, gsp_rpc_len); - if (!IS_ERR_OR_NULL(reply)) + if (!IS_ERR(reply)) repv = reply->data; else repv = reply; From 604d0efb17cc0dd197e089134736ac2698d5fca7 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:30 -0500 Subject: [PATCH 63/71] drm/nouveau/gsp: add SEC2 to GA100 chip table The booter-load and booter-unload firmware run on the SEC2 falcon. During tu102_gsp_oneinit(), the booter constructor needs device->sec2 to access the SEC2 falcon. Without the .sec2 entry, device->sec2 is NULL and this dereference crashes during GSP-RM boot. Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260430223838.2530778-3-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/engine/device/base.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index b101e14f841e..5af12037a5b9 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -2530,6 +2530,7 @@ nv170_chipset = { .vfn = { 0x00000001, ga100_vfn_new }, .ce = { 0x000003ff, ga100_ce_new }, .fifo = { 0x00000001, ga100_fifo_new }, + .sec2 = { 0x00000001, tu102_sec2_new }, }; static const struct nvkm_device_chip From 3cee1a10b8a65af38826e8a57caa6bf613706c48 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:31 -0500 Subject: [PATCH 64/71] drm/nouveau/gsp: use fb.bios.addr for gspFwWprEnd instead of vga_workspace.addr In OpenRM's kgspCalculateFbLayout_TU102(), gspFwWprEnd is derived from vbiosReservedOffset, which is computed as: vbiosReservedOffset = min(mmuLockLo, vgaWorkspaceOffset) The VGA workspace offset is one input into this calculation, not the direct source of gspFwWprEnd. vbiosReservedOffset is the effective top boundary for WPR2 placement, and it may be lower than the VGA workspace when VBIOS has locked a region via MMU_LOCK. In Nouveau, gsp->fb.bios.addr is the equivalent of vbiosReservedOffset, while gsp->fb.bios.vga_workspace.addr corresponds to the raw VGA workspace location. The original code assigned vga_workspace.addr to gspFwWprEnd, which produced the correct result only because bios.addr was always set equal to vga_workspace.addr and never adjusted. Use gsp->fb.bios.addr for gspFwWprEnd to correctly mirror OpenRM's layout logic, so that future adjustments to bios.addr (such as clamping it to an MMU_LOCK boundary) are properly reflected in the WPR metadata passed to the booter. Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260430223838.2530778-4-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c index 19cb269e7a26..aa3ac34989b7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c @@ -246,7 +246,7 @@ tu102_gsp_wpr_meta_init(struct nvkm_gsp *gsp) meta->bootBinOffset = gsp->fb.wpr2.boot.addr; meta->frtsOffset = gsp->fb.wpr2.frts.addr; meta->frtsSize = gsp->fb.wpr2.frts.size; - meta->gspFwWprEnd = ALIGN_DOWN(gsp->fb.bios.vga_workspace.addr, 0x20000); + meta->gspFwWprEnd = ALIGN_DOWN(gsp->fb.bios.addr, 0x20000); meta->fbSize = gsp->fb.size; meta->vgaWorkspaceOffset = gsp->fb.bios.vga_workspace.addr; meta->vgaWorkspaceSize = gsp->fb.bios.vga_workspace.size; From 0094a7a95d52ba86cf66ff42bf5482091364d5c7 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:32 -0500 Subject: [PATCH 65/71] drm/nouveau/gsp: read MMU_LOCK to fix WPR placement on GA100 On GA100, the row remapper hardware reserves a small amount of DRAM at the end of framebuffer for spare rows used to repair memory errors at runtime. When an uncorrectable ECC error is detected in a DRAM row, the row remapper redirects accesses to a spare row, transparently repairing the fault. The LOCAL_MEMORY_RANGE register (0x100ce0) reports the GPU's FB address range, but its encoding rounds to 1GB boundaries. On GA100, VBIOS originally rounded this value down, which could lose up to ~1GB of usable FB. As a workaround, newer VBIOS instead rounds up to the next 1GB boundary and programs MMU_LOCK (registers 0x1fa82c/0x1fa830) to mark the gap between the actual usable FB and the rounded-up range as reserved. OpenRM's kgspCalculateFbLayout_TU102() handles this by reading the MMU_LOCK registers and computing the WPR top boundary as: vbiosReservedOffset = min(mmuLockLo, vgaWorkspaceOffset) Without this, the WPR region is placed at the top of LOCAL_MEMORY_RANGE, which overlaps the reserved region. The booter firmware detects this and rejects the WPR layout. Add ga100_gsp_mmu_lock_lo() to read the MMU_LOCK range and clamp gsp->fb.bios.addr accordingly, mirroring OpenRM's behavior. This is a GA100-only issue. GA102 and later add the NV_USABLE_FB_SIZE_IN_MB register which reports the correct usable FB size directly, eliminating the need for the MMU_LOCK workaround. Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260430223838.2530778-5-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- .../gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c index aa3ac34989b7..66f285b60f1e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c @@ -291,6 +291,42 @@ tu102_gsp_vga_workspace_addr(struct nvkm_gsp *gsp, u64 fb_size) return addr; } +/* + * Read the MMU_LOCK range programmed by VBIOS. + * + * The row remapper reserves a small amount of DRAM at the end of FB for + * spare rows used to repair memory errors. The register used to report + * FB size rounds to 1GB boundaries, and VBIOS rounds up rather than + * down -- reporting a larger size than is actually usable. To + * compensate, VBIOS programs MMU_LOCK to fence off the unusable region + * at the top. We must read this and keep WPR below it. + * + * Returns the low address of the locked region, or 0 if not set. + */ +static u64 +ga100_gsp_mmu_lock_lo(struct nvkm_gsp *gsp) +{ + struct nvkm_device *device = gsp->subdev.device; + u32 lo, hi; + u64 addr_lo, addr_hi; + + /* NV_PFB_PRI_MMU_LOCK_CFG_PRIV_LEVEL_MASK */ + if (!(nvkm_rd32(device, 0x1fa7c8) & 0x00000001)) + return 0; + + lo = nvkm_rd32(device, 0x1fa82c); /* NV_PFB_PRI_MMU_LOCK_ADDR_LO */ + hi = nvkm_rd32(device, 0x1fa830); /* NV_PFB_PRI_MMU_LOCK_ADDR_HI */ + + addr_lo = (u64)(lo >> 4) << 12; + addr_hi = (u64)(hi >> 4) << 12; + + if (addr_hi < addr_lo) + return 0; + + nvkm_debug(&gsp->subdev, "MMU_LOCK range: 0x%llx - 0x%llx\n", addr_lo, addr_hi); + return addr_lo; +} + int tu102_gsp_oneinit(struct nvkm_gsp *gsp) { @@ -304,6 +340,19 @@ tu102_gsp_oneinit(struct nvkm_gsp *gsp) gsp->fb.bios.addr = gsp->fb.bios.vga_workspace.addr; gsp->fb.bios.size = gsp->fb.bios.vga_workspace.size; + /* + * On GA100, VBIOS may lock the top of FB via MMU_LOCK to reserve + * space for the row remapper. Mirror OpenRM's kgspCalculateFbLayout + * by clamping bios.addr to min(mmuLockLo, vgaWorkspace) so that the + * entire WPR2 layout and gspFwWprEnd stay within usable FB. + */ + if (device->chipset == 0x170) { + u64 lock_lo = ga100_gsp_mmu_lock_lo(gsp); + + if (lock_lo) + gsp->fb.bios.addr = min(gsp->fb.bios.addr, lock_lo); + } + ret = gsp->func->booter.ctor(gsp, "booter-load", gsp->fws.booter.load, &device->sec2->falcon, &gsp->booter.load); if (ret) From 0ae50100b228029e8a71dea1d5891ad2f04ddae6 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:33 -0500 Subject: [PATCH 66/71] drm/nouveau: only boot FRTS if its region is allocated On some Nvidia GPUs (i.e. GA100), the FRTS region is not allocated (its size is set to 0). In such cases, FWSEC-FRTS should not be run. Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260430223838.2530778-6-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c index 66f285b60f1e..fc8db6bcaf23 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c @@ -392,9 +392,12 @@ tu102_gsp_oneinit(struct nvkm_gsp *gsp) if (ret) return ret; - ret = nvkm_gsp_fwsec_frts(gsp); - if (WARN_ON(ret)) - return ret; + /* Only boot FWSEC-FRTS if it actually exists */ + if (gsp->fb.wpr2.frts.size) { + ret = nvkm_gsp_fwsec_frts(gsp); + if (WARN_ON(ret)) + return ret; + } /* Reset GSP into RISC-V mode. */ ret = gsp->func->reset(gsp); From 16d4747cd152861fef8d5323c6c1d3ed96c3d102 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:34 -0500 Subject: [PATCH 67/71] drm/nouveau: GA100 has an FRTS region size of zero When booting with GSP-RM, the FRTS data region normally needs to be allocated. However, on GA100, this region is not used and so its size needs to be set to zero. The truth is that GA100 is just special, and the simplest way to determine the proper FRTS data region size is to check for this GPU specifically. Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260430223838.2530778-7-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c index fc8db6bcaf23..11a37963bd4e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c @@ -367,8 +367,13 @@ tu102_gsp_oneinit(struct nvkm_gsp *gsp) if (ret) return ret; - /* Calculate FB layout. */ - gsp->fb.wpr2.frts.size = 0x100000; + /* + * Calculate FB layout. FRTS is a memory region created by running the FWSEC-FRTS + * command, which writes power management data into WPR2. On GA100, the booter + * firmware handles WPR2 setup directly and FRTS data is not needed, so no FRTS + * region is reserved. + */ + gsp->fb.wpr2.frts.size = device->chipset == 0x170 ? 0 : 0x100000; gsp->fb.wpr2.frts.addr = ALIGN_DOWN(gsp->fb.bios.addr, 0x20000) - gsp->fb.wpr2.frts.size; gsp->fb.wpr2.boot.size = gsp->boot.fw.size; From c1cf2d5db80ce91a85855bbaf4da85ff603e089a Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:35 -0500 Subject: [PATCH 68/71] drm/nouveau/bios: specify correct display fuse register for Ampere and Ada The NV_FUSE_STATUS_OPT_DISPLAY register is used to determine whether the GPU has display hardware. The current code that normally reads this register is instead hard-coded to check for GA100 vs later GPUs. Since this function is called only on pre-Hopper GPUs, and this if-statement applies only to GA100 and later, the check works because GA100 is the only non-display Ampere and Ada GPU. However, there actually is a register that can be read, so we should use it. Fixes: a34632482f1e ("drm/nouveau/bios/ga10[024]: initial support") Signed-off-by: Timur Tabi Reviewed-by: Lyude Paul Link: https://patch.msgid.link/20260430223838.2530778-8-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c index d5411d176e3a..0d9e6cdd6119 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c @@ -65,13 +65,14 @@ pramin_init(struct nvkm_bios *bios, const char *name) /* we can't get the bios image pointer without PDISP */ if (device->card_type >= GA100) - addr = device->chipset == 0x170; /*XXX: find the fuse reg for this */ + addr = nvkm_rd32(device, 0x820c04); else if (device->card_type >= GM100) addr = nvkm_rd32(device, 0x021c04); else if (device->card_type >= NV_C0) addr = nvkm_rd32(device, 0x022500); + if (addr & 0x00000001) { nvkm_debug(subdev, "... display disabled\n"); return ERR_PTR(-ENODEV); From 4beeac5d2015df1017732ce4c57d634f557dd304 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:36 -0500 Subject: [PATCH 69/71] drm/nouveau/bios: skip the IFR header if present The GPU's ROM may begin with an Init-from-ROM (IFR) header that precedes the PCI Expansion ROM images (VBIOS). When present, the PROM shadow method must parse this header to determine the offset where the PCI ROM images actually begin, and adjust all subsequent reads accordingly. On most GPUs this is not needed because either the PRAMIN shadow method (which reads from VRAM via the display engine) succeeds first, or the IFR microcode has already applied the ROM offset so that PROM reads transparently skip the header. However, on GA100 neither of these applies: GA100 has no display engine (so PRAMIN is unavailable), and the IFR offset is not applied to PROM reads on this GPU. Signed-off-by: Timur Tabi Reviewed-by: Lyude Paul Link: https://patch.msgid.link/20260430223838.2530778-9-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- .../drm/nouveau/nvkm/subdev/bios/shadowrom.c | 110 ++++++++++++++++-- 1 file changed, 101 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c index 39144ceb117b..9e171b1bad73 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c @@ -24,34 +24,126 @@ #include +#define NV_PBUS_IFR_FMT_FIXED0_SIGNATURE_VALUE 0x4947564E /* "NVGI" */ +#define NV_ROM_DIRECTORY_IDENTIFIER 0x44524652 /* "RFRD" */ + +struct priv { + struct nvkm_device *device; + u32 pci_rom_offset; +}; + static u32 nvbios_prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) { - struct nvkm_device *device = data; + struct priv *priv = data; + struct nvkm_device *device = priv->device; u32 i; - if (offset + length <= 0x00100000) { - for (i = offset; i < offset + length; i += 4) - *(u32 *)&bios->data[i] = nvkm_rd32(device, 0x300000 + i); - return length; - } - return 0; + + /* Make sure we don't try to read past the end of data[] */ + if (offset + length > bios->size) + return 0; + + /* Make sure the read falls within the 1MB PROM window */ + if (offset + priv->pci_rom_offset + length > 0x00100000) + return 0; + + for (i = offset; i < offset + length; i += 4) + *(u32 *)&bios->data[i] = nvkm_rd32(device, 0x300000 + priv->pci_rom_offset + i); + return length; } static void nvbios_prom_fini(void *data) { - struct nvkm_device *device = data; + struct priv *priv = data; + struct nvkm_device *device = priv->device; + nvkm_pci_rom_shadow(device->pci, true); + + kfree(data); } static void * nvbios_prom_init(struct nvkm_bios *bios, const char *name) { struct nvkm_device *device = bios->subdev.device; + struct priv *priv; + u32 fixed0; + + /* There is no PROM on NV4x iGPUs */ if (device->card_type == NV_40 && device->chipset >= 0x4c) return ERR_PTR(-ENODEV); + + priv = kzalloc_obj(*priv); + if (!priv) + return ERR_PTR(-ENOMEM); + + /* Disable the PCI ROM shadow so that we can read PROM. */ nvkm_pci_rom_shadow(device->pci, false); - return device; + + /* + * Check for an IFR header. If present, parse it to find the actual PCI ROM header. + * + * The IFR header is documented in Documentation/gpu/nova/core/vbios.rst + */ + fixed0 = nvkm_rd32(device, 0x300000); + if (fixed0 == NV_PBUS_IFR_FMT_FIXED0_SIGNATURE_VALUE) { + u32 fixed1 = nvkm_rd32(device, 0x300004); + u8 version = (fixed1 >> 8) & 0xff; + u32 fixed2, data_size, offset, signature; + + switch (version) { + case 1: + case 2: + data_size = (fixed1 >> 16) & 0x7fff; + priv->pci_rom_offset = nvkm_rd32(device, 0x300000 + data_size + 4); + break; + case 3: + fixed2 = nvkm_rd32(device, 0x300008); + data_size = fixed2 & 0x000fffff; + + /* ROM directory offset */ + offset = nvkm_rd32(device, 0x300000 + data_size) + 4096; + + signature = nvkm_rd32(device, 0x300000 + offset); + if (signature != NV_ROM_DIRECTORY_IDENTIFIER) { + nvkm_error(&bios->subdev, "could not find IFR ROM directory\n"); + goto fail; + } + + priv->pci_rom_offset = nvkm_rd32(device, 0x300000 + offset + 8); + + break; + default: + nvkm_error(&bios->subdev, "unsupported IFR header version %u\n", + version); + goto fail; + } + + /* Double-check that the offset is valid */ + if (priv->pci_rom_offset >= 0x00100000) { + nvkm_error(&bios->subdev, + "PCI ROM offset of 0x%x is too large\n", priv->pci_rom_offset); + goto fail; + } + + /* If there is an IFR header, there must also be a PCI ROM header. */ + signature = nvkm_rd32(device, 0x300000 + priv->pci_rom_offset) & 0xffff; + if (signature != 0xaa55) { + nvkm_error(&bios->subdev, + "could not find PCI ROM signature at offset 0x%x\n", + priv->pci_rom_offset); + goto fail; + } + } + + priv->device = device; + return priv; + +fail: + nvkm_pci_rom_shadow(device->pci, true); + kfree(priv); + return ERR_PTR(-ENODEV); } const struct nvbios_source From f0de0f89cc1e086157958c296b09caeb74072111 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:37 -0500 Subject: [PATCH 70/71] drm/nouveau/gsp: require GSP-RM for GA100 support Nouveau supports Turing and Ampere GPUs with or without GSP-RM. Support without GSP-RM is mostly academic, since GSP-RM is needed to run the GPU at full clocks. It is also the default mode for these GPUs. GA100 is a special case, however. The current code has some support for running GA100 without GSP-RM, but several features are missing. More importantly, some required firmware images like ucode_ahesasc.bin are not available and would need to be provided by Nvidia. To prevent Nouveau from even trying to boot on GA100 without GSP-RM, remove the non-GSP fallback option in the ga100_gsps[] array. Signed-off-by: Timur Tabi Reviewed-by: Lyude Paul Link: https://patch.msgid.link/20260430223838.2530778-10-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c index 27a13aeccd3c..d5126fe39d27 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c @@ -63,7 +63,6 @@ static struct nvkm_gsp_fwif ga100_gsps[] = { { 1, tu102_gsp_load, &ga100_gsp, &r570_rm_tu102, "570.144" }, { 0, tu102_gsp_load, &ga100_gsp, &r535_rm_tu102, "535.113.01" }, - { -1, gv100_gsp_nofw, &gv100_gsp }, {} }; From 61de054a772a1feda6364931ab1baf9038abf1c8 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 30 Apr 2026 17:38:38 -0500 Subject: [PATCH 71/71] drm/nouveau/gsp: formally support GA100 Now that Nouveau supports GA100 properly, it should no longer require the NvEnableUnsupportedChipsets parameter in order to enable it. Signed-off-by: Timur Tabi Link: https://patch.msgid.link/20260430223838.2530778-11-ttabi@nvidia.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/engine/device/base.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 5af12037a5b9..ea62dc97f118 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -3342,6 +3342,7 @@ nvkm_device_ctor(const struct nvkm_device_func *func, case 0x166: device->chip = &nv166_chipset; break; case 0x167: device->chip = &nv167_chipset; break; case 0x168: device->chip = &nv168_chipset; break; + case 0x170: device->chip = &nv170_chipset; break; case 0x172: device->chip = &nv172_chipset; break; case 0x173: device->chip = &nv173_chipset; break; case 0x174: device->chip = &nv174_chipset; break; @@ -3361,14 +3362,6 @@ nvkm_device_ctor(const struct nvkm_device_func *func, case 0x1b6: device->chip = &nv1b6_chipset; break; case 0x1b7: device->chip = &nv1b7_chipset; break; default: - if (nvkm_boolopt(device->cfgopt, "NvEnableUnsupportedChipsets", false)) { - switch (device->chipset) { - case 0x170: device->chip = &nv170_chipset; break; - default: - break; - } - } - if (!device->chip) { nvdev_error(device, "unknown chipset (%08x)\n", boot0); ret = -ENODEV;