From 5b50bb435629ab206cfa1cca3d71847d4523f88b Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 29 Sep 2025 17:24:54 +0300 Subject: [PATCH 001/128] gpu/drm: panel-edp: add AUO B116XAN02.0 panel entry Add an eDP panel entry for AUO B116XAN02.0 used in Lenovo IdeaPad Yoga 11 with Tegra 3 SoC. The raw edid of the panel is: 00 ff ff ff ff ff ff 00 06 af 5c 20 00 00 00 00 00 16 01 04 90 1a 0e 78 02 99 85 95 55 56 92 28 22 50 54 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 12 1b 56 5a 50 00 19 30 30 20 46 00 00 90 10 00 00 18 00 00 00 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 fe 00 41 55 4f 0a 20 20 20 20 20 20 20 20 20 00 00 00 fe 00 42 31 31 36 58 41 4e 30 32 2e 30 20 0a 00 f1 Signed-off-by: Svyatoslav Ryhel Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20250929142455.24883-9-clamor95@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 ad6b03b59b52..944c7c70de55 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1888,6 +1888,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x1e9b, &delay_200_500_e50, "B133UAN02.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e50, "B116XAK01.6"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x203d, &delay_200_500_e50, "B140HTN02.0"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x205c, &delay_200_500_e50, "B116XAN02.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x208d, &delay_200_500_e50, "B140HTN02.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x235c, &delay_200_500_e50, "B116XTN02.3"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x239b, &delay_200_500_e50, "B116XAN06.1"), From 2be300f9a0b6f6b0ae2a90be97e558ec0535be54 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 26 Sep 2025 16:59:40 +0200 Subject: [PATCH 002/128] drm/display: bridge_connector: get/put the stored bridges drm_bridge_connector_init() takes eight pointers to various bridges, some of which can be identical, and stores them in pointers inside struct drm_bridge_connector. Get a reference to each of the taken bridges and put it on cleanup. This is tricky because the pointers are currently stored directly in the drm_bridge_connector in the loop, but there is no nice and clean way to put those pointers on error return paths. To overcome this, store all pointers in temporary local variables with a cleanup action, and only on success copy them into struct drm_bridge_connector (getting another ref while copying). Additionally four of these pointers (edid, hpd, detect and modes) can be written in multiple loop iterations, in order to eventually store the last matching bridge. However, when one of those pointers is overwritten, we need to put the reference that we got during the previous assignment. Add a drm_bridge_put() before writing them to handle this. Finally, there is also a function-local panel_bridge pointer taken inside the loop and used after the loop. Use a cleanup action as well to ensure it is put on return. Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250926-drm-bridge-alloc-getput-bridge-connector-v2-1-138b4bb70576@bootlin.com Signed-off-by: Luca Ceresoli --- .../gpu/drm/display/drm_bridge_connector.c | 114 ++++++++++++------ 1 file changed, 78 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index a5bdd6c10643..7b18be3ff9a3 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -618,6 +618,20 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f * Bridge Connector Initialisation */ +static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data) +{ + struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data; + + drm_bridge_put(bridge_connector->bridge_edid); + drm_bridge_put(bridge_connector->bridge_hpd); + drm_bridge_put(bridge_connector->bridge_detect); + drm_bridge_put(bridge_connector->bridge_modes); + drm_bridge_put(bridge_connector->bridge_hdmi); + drm_bridge_put(bridge_connector->bridge_hdmi_audio); + drm_bridge_put(bridge_connector->bridge_dp_audio); + drm_bridge_put(bridge_connector->bridge_hdmi_cec); +} + /** * drm_bridge_connector_init - Initialise a connector for a chain of bridges * @drm: the DRM device @@ -638,7 +652,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, struct drm_bridge_connector *bridge_connector; struct drm_connector *connector; struct i2c_adapter *ddc = NULL; - struct drm_bridge *panel_bridge = NULL; + struct drm_bridge *panel_bridge __free(drm_bridge_put) = NULL; + struct drm_bridge *bridge_edid __free(drm_bridge_put) = NULL; + struct drm_bridge *bridge_hpd __free(drm_bridge_put) = NULL; + struct drm_bridge *bridge_detect __free(drm_bridge_put) = NULL; + struct drm_bridge *bridge_modes __free(drm_bridge_put) = NULL; + struct drm_bridge *bridge_hdmi __free(drm_bridge_put) = NULL; + struct drm_bridge *bridge_hdmi_audio __free(drm_bridge_put) = NULL; + struct drm_bridge *bridge_dp_audio __free(drm_bridge_put) = NULL; + struct drm_bridge *bridge_hdmi_cec __free(drm_bridge_put) = NULL; unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB); unsigned int max_bpc = 8; bool support_hdcp = false; @@ -649,6 +671,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, if (!bridge_connector) return ERR_PTR(-ENOMEM); + ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector); + if (ret) + return ERR_PTR(ret); + bridge_connector->encoder = encoder; /* @@ -672,22 +698,30 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, if (!bridge->ycbcr_420_allowed) connector->ycbcr_420_allowed = false; - if (bridge->ops & DRM_BRIDGE_OP_EDID) - bridge_connector->bridge_edid = bridge; - if (bridge->ops & DRM_BRIDGE_OP_HPD) - bridge_connector->bridge_hpd = bridge; - if (bridge->ops & DRM_BRIDGE_OP_DETECT) - bridge_connector->bridge_detect = bridge; - if (bridge->ops & DRM_BRIDGE_OP_MODES) - bridge_connector->bridge_modes = bridge; + if (bridge->ops & DRM_BRIDGE_OP_EDID) { + drm_bridge_put(bridge_edid); + bridge_edid = drm_bridge_get(bridge); + } + if (bridge->ops & DRM_BRIDGE_OP_HPD) { + drm_bridge_put(bridge_hpd); + bridge_hpd = drm_bridge_get(bridge); + } + if (bridge->ops & DRM_BRIDGE_OP_DETECT) { + drm_bridge_put(bridge_detect); + bridge_detect = drm_bridge_get(bridge); + } + if (bridge->ops & DRM_BRIDGE_OP_MODES) { + drm_bridge_put(bridge_modes); + bridge_modes = drm_bridge_get(bridge); + } if (bridge->ops & DRM_BRIDGE_OP_HDMI) { - if (bridge_connector->bridge_hdmi) + if (bridge_hdmi) return ERR_PTR(-EBUSY); if (!bridge->funcs->hdmi_write_infoframe || !bridge->funcs->hdmi_clear_infoframe) return ERR_PTR(-EINVAL); - bridge_connector->bridge_hdmi = bridge; + bridge_hdmi = drm_bridge_get(bridge); if (bridge->supported_formats) supported_formats = bridge->supported_formats; @@ -696,10 +730,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, } if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) { - if (bridge_connector->bridge_hdmi_audio) + if (bridge_hdmi_audio) return ERR_PTR(-EBUSY); - if (bridge_connector->bridge_dp_audio) + if (bridge_dp_audio) return ERR_PTR(-EBUSY); if (!bridge->hdmi_audio_max_i2s_playback_channels && @@ -710,14 +744,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, !bridge->funcs->hdmi_audio_shutdown) return ERR_PTR(-EINVAL); - bridge_connector->bridge_hdmi_audio = bridge; + bridge_hdmi_audio = drm_bridge_get(bridge); } if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) { - if (bridge_connector->bridge_dp_audio) + if (bridge_dp_audio) return ERR_PTR(-EBUSY); - if (bridge_connector->bridge_hdmi_audio) + if (bridge_hdmi_audio) return ERR_PTR(-EBUSY); if (!bridge->hdmi_audio_max_i2s_playback_channels && @@ -728,7 +762,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, !bridge->funcs->dp_audio_shutdown) return ERR_PTR(-EINVAL); - bridge_connector->bridge_dp_audio = bridge; + bridge_dp_audio = drm_bridge_get(bridge); } if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { @@ -739,10 +773,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, } if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { - if (bridge_connector->bridge_hdmi_cec) + if (bridge_hdmi_cec) return ERR_PTR(-EBUSY); - bridge_connector->bridge_hdmi_cec = bridge; + bridge_hdmi_cec = drm_bridge_get(bridge); if (!bridge->funcs->hdmi_cec_enable || !bridge->funcs->hdmi_cec_log_addr || @@ -762,7 +796,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, ddc = bridge->ddc; if (drm_bridge_is_panel(bridge)) - panel_bridge = bridge; + panel_bridge = drm_bridge_get(bridge); if (bridge->support_hdcp) support_hdcp = true; @@ -771,13 +805,13 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, if (connector_type == DRM_MODE_CONNECTOR_Unknown) return ERR_PTR(-EINVAL); - if (bridge_connector->bridge_hdmi) { + if (bridge_hdmi) { if (!connector->ycbcr_420_allowed) supported_formats &= ~BIT(HDMI_COLORSPACE_YUV420); ret = drmm_connector_hdmi_init(drm, connector, - bridge_connector->bridge_hdmi->vendor, - bridge_connector->bridge_hdmi->product, + bridge_hdmi->vendor, + bridge_hdmi->product, &drm_bridge_connector_funcs, &drm_bridge_connector_hdmi_funcs, connector_type, ddc, @@ -793,15 +827,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return ERR_PTR(ret); } - if (bridge_connector->bridge_hdmi_audio || - bridge_connector->bridge_dp_audio) { + if (bridge_hdmi_audio || bridge_dp_audio) { struct device *dev; struct drm_bridge *bridge; - if (bridge_connector->bridge_hdmi_audio) - bridge = bridge_connector->bridge_hdmi_audio; + if (bridge_hdmi_audio) + bridge = bridge_hdmi_audio; else - bridge = bridge_connector->bridge_dp_audio; + bridge = bridge_dp_audio; dev = bridge->hdmi_audio_dev; @@ -815,9 +848,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return ERR_PTR(ret); } - if (bridge_connector->bridge_hdmi_cec && - bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { - struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec; + if (bridge_hdmi_cec && + bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { + struct drm_bridge *bridge = bridge_hdmi_cec; ret = drmm_connector_hdmi_cec_notifier_register(connector, NULL, @@ -826,9 +859,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return ERR_PTR(ret); } - if (bridge_connector->bridge_hdmi_cec && - bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { - struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec; + if (bridge_hdmi_cec && + bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { + struct drm_bridge *bridge = bridge_hdmi_cec; ret = drmm_connector_hdmi_cec_register(connector, &drm_bridge_connector_hdmi_cec_funcs, @@ -841,9 +874,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs); - if (bridge_connector->bridge_hpd) + if (bridge_hpd) connector->polled = DRM_CONNECTOR_POLL_HPD; - else if (bridge_connector->bridge_detect) + else if (bridge_detect) connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; @@ -854,6 +887,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, IS_ENABLED(CONFIG_DRM_DISPLAY_HDCP_HELPER)) drm_connector_attach_content_protection_property(connector, true); + bridge_connector->bridge_edid = drm_bridge_get(bridge_edid); + bridge_connector->bridge_hpd = drm_bridge_get(bridge_hpd); + bridge_connector->bridge_detect = drm_bridge_get(bridge_detect); + bridge_connector->bridge_modes = drm_bridge_get(bridge_modes); + bridge_connector->bridge_hdmi = drm_bridge_get(bridge_hdmi); + bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge_hdmi_audio); + bridge_connector->bridge_dp_audio = drm_bridge_get(bridge_dp_audio); + bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge_hdmi_cec); + return connector; } EXPORT_SYMBOL_GPL(drm_bridge_connector_init); From 0f2efbe6d8305b91c9b2c92ebaf8c24a614bc305 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Wed, 24 Sep 2025 17:11:01 +0200 Subject: [PATCH 003/128] drm/bridge: refcount last_bridge in drm_atomic_bridge_chain_select_bus_fmts() Get a reference for the last_bridge when it is obtained and release it using a cleanup action. Reviewed-by: Maxime Ripard Link: https://lore.kernel.org/r/20250924-b4-drm-bridge-alloc-getput-drm_atomic_bridge_chain_select_bus_fmts-v1-1-f8c2efdb783f@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/drm_bridge.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 630b5e6594e0..53e7ece36dd9 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1086,12 +1086,12 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, struct drm_encoder *encoder = bridge->encoder; struct drm_bridge_state *last_bridge_state; unsigned int i, num_out_bus_fmts = 0; - struct drm_bridge *last_bridge; u32 *out_bus_fmts; int ret = 0; - last_bridge = list_last_entry(&encoder->bridge_chain, - struct drm_bridge, chain_node); + struct drm_bridge *last_bridge __free(drm_bridge_put) = + drm_bridge_get(list_last_entry(&encoder->bridge_chain, + struct drm_bridge, chain_node)); last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, last_bridge); From eb0ed2ad33e740ac8b0d7211b24e1ddd543c2519 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Sat, 13 Sep 2025 08:09:40 +0200 Subject: [PATCH 004/128] drm/sitronix/st7571-i2c: reset position before clearing display We cannot know where the write pointer is, always reset position to (0,0) before clearing display. Signed-off-by: Marcus Folkesson Reviewed-by: Javier Martinez Canillas Link: https://lore.kernel.org/r/20250913-st7571-reset-v1-1-ae5f58acdf8d@gmail.com Signed-off-by: Javier Martinez Canillas --- drivers/gpu/drm/sitronix/st7571-i2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/sitronix/st7571-i2c.c b/drivers/gpu/drm/sitronix/st7571-i2c.c index a6c4a6738ded..32b91d65b768 100644 --- a/drivers/gpu/drm/sitronix/st7571-i2c.c +++ b/drivers/gpu/drm/sitronix/st7571-i2c.c @@ -263,6 +263,7 @@ static int st7571_fb_clear_screen(struct st7571_device *st7571) u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp; char pixelvalue = 0x00; + st7571_set_position(st7571, 0, 0); for (int i = 0; i < npixels; i++) regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1); From a31c6c50da013dcd4de4889d402b47bae552a2e2 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 2 Oct 2025 13:34:52 +0100 Subject: [PATCH 005/128] drm: renesas: rz-du: Drop ARCH_RZG2L dependency The RZ/G2L DU driver is now also used on other Renesas SoCs such as RZ/V2H(P) and RZ/V2N. Restricting it to ARCH_RZG2L prevents enabling it on these newer platforms. Replace the ARCH_RZG2L dependency with ARCH_RENESAS to allow the driver to be built on all relevant Renesas SoCs. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20251002123452.146610-1-prabhakar.mahadev-lad.rj@bp.renesas.com --- drivers/gpu/drm/renesas/rz-du/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig b/drivers/gpu/drm/renesas/rz-du/Kconfig index e57536fd6f4d..7f2ef7137ae5 100644 --- a/drivers/gpu/drm/renesas/rz-du/Kconfig +++ b/drivers/gpu/drm/renesas/rz-du/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 config DRM_RZG2L_DU tristate "DRM Support for RZ/G2L Display Unit" - depends on ARCH_RZG2L || COMPILE_TEST + depends on ARCH_RENESAS || COMPILE_TEST depends on DRM && OF depends on VIDEO_RENESAS_VSP1 select DRM_CLIENT_SELECTION From 4b478288ca0e7a046dc842aa0e4b2eeee68771c2 Mon Sep 17 00:00:00 2001 From: Chia-I Wu Date: Fri, 12 Sep 2025 17:21:54 -0700 Subject: [PATCH 006/128] dt-bindings: gpu: mali-valhall-csf: add MediaTek MT8196 compatible MediaTek MT8196 has Mali-G925-Immortalis GPU. panthor drm driver gained support for it recently. Signed-off-by: Chia-I Wu Acked-by: Conor Dooley Signed-off-by: Steven Price Link: https://lore.kernel.org/r/20250913002155.1163908-2-olvaffe@gmail.com --- Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml index a5b4e0021758..7ad5a3ffc5f5 100644 --- a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml +++ b/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml @@ -18,6 +18,7 @@ properties: oneOf: - items: - enum: + - mediatek,mt8196-mali - rockchip,rk3588-mali - const: arm,mali-valhall-csf # Mali Valhall GPU model/revision is fully discoverable From 4e04683022609d8eb4b6c32c281ebbc2d5ae2e7b Mon Sep 17 00:00:00 2001 From: Chia-I Wu Date: Fri, 12 Sep 2025 17:21:55 -0700 Subject: [PATCH 007/128] drm/panthor: add custom ASN_HASH support for mt8196 Add panthor_soc_data to control custom ASN_HASH. Add compatible string for "mediatek,mt8196-mali" and enable custom ASN_HASH for the soc. Without custom ASN_HASH, FW fails to boot panthor 48000000.gpu: [drm] *ERROR* Unhandled Page fault in AS0 at VA 0x0000000000000000 panthor 48000000.gpu: [drm] *ERROR* Failed to boot MCU (status=fatal) panthor 48000000.gpu: probe with driver panthor failed with error -110 With custom ASN_HASH, panthor probes fine and userspace boots to ui just fine as well panthor 48000000.gpu: [drm] clock rate = 0 panthor 48000000.gpu: EM: created perf domain panthor 48000000.gpu: [drm] Mali-G925-Immortalis id 0xd830 major 0x0 minor 0x1 status 0x5 panthor 48000000.gpu: [drm] Features: L2:0x8130306 Tiler:0x809 Mem:0x301 MMU:0x2830 AS:0xff panthor 48000000.gpu: [drm] shader_present=0xee0077 l2_present=0x1 tiler_present=0x1 panthor 48000000.gpu: [drm] Firmware protected mode entry not be supported, ignoring panthor 48000000.gpu: [drm] Firmware git sha: 27713280172c742d467a4b7d11180930094092ec panthor 48000000.gpu: [drm] CSF FW using interface v3.13.0, Features 0x10 Instrumentation features 0x71 [drm] Initialized panthor 1.5.0 for 48000000.gpu on minor 1 Note that the clock and the regulator drivers are not upstreamed yet. They might as well take a different form when upstreamed. Signed-off-by: Chia-I Wu Reviewed-by: Boris Brezillon Reviewed-by: Steven Price Signed-off-by: Steven Price Link: https://lore.kernel.org/r/20250913002155.1163908-3-olvaffe@gmail.com --- drivers/gpu/drm/panthor/panthor_device.c | 2 ++ drivers/gpu/drm/panthor/panthor_device.h | 14 +++++++++++++ drivers/gpu/drm/panthor/panthor_drv.c | 6 ++++++ drivers/gpu/drm/panthor/panthor_gpu.c | 25 +++++++++++++++++++++++- drivers/gpu/drm/panthor/panthor_regs.h | 4 ++++ 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c index 81df49880bd8..c7033d82cef5 100644 --- a/drivers/gpu/drm/panthor/panthor_device.c +++ b/drivers/gpu/drm/panthor/panthor_device.c @@ -172,6 +172,8 @@ int panthor_device_init(struct panthor_device *ptdev) struct page *p; int ret; + ptdev->soc_data = of_device_get_match_data(ptdev->base.dev); + init_completion(&ptdev->unplug.done); ret = drmm_mutex_init(&ptdev->base, &ptdev->unplug.lock); if (ret) diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index 4fc7cf2aeed5..9f0649ecfc4f 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -31,6 +31,17 @@ struct panthor_perfcnt; struct panthor_vm; struct panthor_vm_pool; +/** + * struct panthor_soc_data - Panthor SoC Data + */ +struct panthor_soc_data { + /** @asn_hash_enable: True if GPU_L2_CONFIG_ASN_HASH_ENABLE must be set. */ + bool asn_hash_enable; + + /** @asn_hash: ASN_HASH values when asn_hash_enable is true. */ + u32 asn_hash[3]; +}; + /** * enum panthor_device_pm_state - PM state */ @@ -93,6 +104,9 @@ struct panthor_device { /** @base: Base drm_device. */ struct drm_device base; + /** @soc_data: Optional SoC data. */ + const struct panthor_soc_data *soc_data; + /** @phys_addr: Physical address of the iomem region. */ phys_addr_t phys_addr; diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index fdbe89ef7f43..fb4b293f17f0 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -1682,7 +1682,13 @@ static struct attribute *panthor_attrs[] = { ATTRIBUTE_GROUPS(panthor); +static const struct panthor_soc_data soc_data_mediatek_mt8196 = { + .asn_hash_enable = true, + .asn_hash = { 0xb, 0xe, 0x0, }, +}; + static const struct of_device_id dt_match[] = { + { .compatible = "mediatek,mt8196-mali", .data = &soc_data_mediatek_mt8196, }, { .compatible = "rockchip,rk3588-mali" }, { .compatible = "arm,mali-valhall-csf" }, {} diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index db69449a5be0..9d98720ce03f 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -52,6 +52,28 @@ static void panthor_gpu_coherency_set(struct panthor_device *ptdev) ptdev->coherent ? GPU_COHERENCY_PROT_BIT(ACE_LITE) : GPU_COHERENCY_NONE); } +static void panthor_gpu_l2_config_set(struct panthor_device *ptdev) +{ + const struct panthor_soc_data *data = ptdev->soc_data; + u32 l2_config; + u32 i; + + if (!data || !data->asn_hash_enable) + return; + + if (GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) < 11) { + drm_err(&ptdev->base, "Custom ASN hash not supported by the device"); + return; + } + + for (i = 0; i < ARRAY_SIZE(data->asn_hash); i++) + gpu_write(ptdev, GPU_ASN_HASH(i), data->asn_hash[i]); + + l2_config = gpu_read(ptdev, GPU_L2_CONFIG); + l2_config |= GPU_L2_CONFIG_ASN_HASH_ENABLE; + gpu_write(ptdev, GPU_L2_CONFIG, l2_config); +} + static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status) { gpu_write(ptdev, GPU_INT_CLEAR, status); @@ -241,8 +263,9 @@ int panthor_gpu_l2_power_on(struct panthor_device *ptdev) hweight64(ptdev->gpu_info.shader_present)); } - /* Set the desired coherency mode before the power up of L2 */ + /* Set the desired coherency mode and L2 config before the power up of L2 */ panthor_gpu_coherency_set(ptdev); + panthor_gpu_l2_config_set(ptdev); return panthor_gpu_power_on(ptdev, L2, 1, 20000); } diff --git a/drivers/gpu/drm/panthor/panthor_regs.h b/drivers/gpu/drm/panthor/panthor_regs.h index 8bee76d01bf8..8fa69f33e911 100644 --- a/drivers/gpu/drm/panthor/panthor_regs.h +++ b/drivers/gpu/drm/panthor/panthor_regs.h @@ -64,6 +64,8 @@ #define GPU_FAULT_STATUS 0x3C #define GPU_FAULT_ADDR 0x40 +#define GPU_L2_CONFIG 0x48 +#define GPU_L2_CONFIG_ASN_HASH_ENABLE BIT(24) #define GPU_PWR_KEY 0x50 #define GPU_PWR_KEY_UNLOCK 0x2968A819 @@ -110,6 +112,8 @@ #define GPU_REVID 0x280 +#define GPU_ASN_HASH(n) (0x2C0 + ((n) * 4)) + #define GPU_COHERENCY_FEATURES 0x300 #define GPU_COHERENCY_PROT_BIT(name) BIT(GPU_COHERENCY_ ## name) From fa02f9b2ff663b1d45df35d67c802f306f22ee48 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:16 +0200 Subject: [PATCH 008/128] drm/atomic: Convert drm_atomic_get_connector_state() to use new connector state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_atomic_get_connector_state() function calls a hand-rolled implementation of the deprecated drm_atomic_get_existing_connector_state() helper to get find if a connector state had already been allocated and was part of the given drm_atomic_state. At the point in time where drm_atomic_get_connector_state() can be called (ie, during atomic_check), the existing state is the new state and drm_atomic_get_existing_connector_state() can thus be replaced by drm_atomic_get_new_connector_state(). Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli # on imx8mp Reviewed-by: Dmitry Baryshkov Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-1-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index ed5359a71f7e..eeae4553894c 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1152,8 +1152,9 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, state->num_connector = alloc; } - if (state->connectors[index].state) - return state->connectors[index].state; + connector_state = drm_atomic_get_new_connector_state(state, connector); + if (connector_state) + return connector_state; connector_state = connector->funcs->atomic_duplicate_state(connector); if (!connector_state) From 54cea7e655fe3d031c9e7eaaf127bffe0496e7cd Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:17 +0200 Subject: [PATCH 009/128] drm/atomic: Remove unused drm_atomic_get_existing_connector_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_atomic_get_existing_connector_state() function is deprecated and isn't used anymore, so let's remove it. Reviewed-by: Dmitry Baryshkov Reviewed-by: Luca Ceresoli Tested-by: Luca Ceresoli Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-2-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 38636a593c9d..321c866d5b0a 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -731,29 +731,6 @@ drm_atomic_get_new_plane_state(const struct drm_atomic_state *state, return state->planes[drm_plane_index(plane)].new_state; } -/** - * drm_atomic_get_existing_connector_state - get connector state, if it exists - * @state: global atomic state object - * @connector: connector to grab - * - * This function returns the connector state for the given connector, - * or NULL if the connector is not part of the global atomic state. - * - * This function is deprecated, @drm_atomic_get_old_connector_state or - * @drm_atomic_get_new_connector_state should be used instead. - */ -static inline struct drm_connector_state * -drm_atomic_get_existing_connector_state(const struct drm_atomic_state *state, - struct drm_connector *connector) -{ - int index = drm_connector_index(connector); - - if (index >= state->num_connector) - return NULL; - - return state->connectors[index].state; -} - /** * drm_atomic_get_old_connector_state - get connector state, if it exists * @state: global atomic state object From 9525f537ce00143f77e4208812cce3aa1d1d1f56 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:18 +0200 Subject: [PATCH 010/128] drm/atomic: Document __drm_connectors_state state pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the old and new state pointers are somewhat self-explanatory, the state pointer and its relation to the other two really isn't. Now that we've cleaned up everything and it isn't used in any modesetting path, we can document what it's still useful for: to free the right state when we free the global state. Reviewed-by: Luca Ceresoli Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-3-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 321c866d5b0a..fe47715c31e4 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -182,7 +182,24 @@ struct __drm_crtcs_state { struct __drm_connnectors_state { struct drm_connector *ptr; - struct drm_connector_state *state, *old_state, *new_state; + + /** + * @state: + * + * Used to track the @drm_connector_state we will need to free + * when tearing down the associated &drm_atomic_state in + * $drm_mode_config_funcs.atomic_state_clear or + * drm_atomic_state_default_clear(). + * + * Before a commit, and the call to + * drm_atomic_helper_swap_state() in particular, it points to + * the same state than @new_state. After a commit, it points to + * the same state than @old_state. + */ + struct drm_connector_state *state; + + struct drm_connector_state *old_state, *new_state; + /** * @out_fence_ptr: * From 6b592431c5b07f4fd2fa3ea123eda3ae92d8a2a0 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:19 +0200 Subject: [PATCH 011/128] drm/atomic: Convert __drm_atomic_get_current_plane_state() to modern accessor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The __drm_atomic_get_current_plane_state() function tries to get and return the existing plane state, and if it doesn't exist returns the one stored in the drm_plane->state field. Using the current nomenclature, it tries to get the existing plane state with an ad-hoc implementation of drm_atomic_get_existing_plane_state(), and falls back to either the old or new plane state, depending on whether it is called before or after drm_atomic_helper_swap_state(). The existing plane state itself is deprecated, because it also changes when swapping states from the new state to the old state. Fortunately for us, we can simplify things. Indeed, __drm_atomic_get_current_plane_state() is only used in two macros: intel_atomic_crtc_state_for_each_plane_state and drm_atomic_crtc_state_for_each_plane_state(). The intel variant is only used through the intel_wm_compute() function that is only ever called in intel_crtc_atomic_check(). The generic variant is more widely used, and can be found in the malidp, msm, tegra and vc4 drivers. All of these call sites though are during atomic_check(), so we end up in the same situation than Intel's. Thus, we only ever use the existing state as the new state, and plane->state is always going to be the old state. Any plane isn't guaranteed to be part of the state though, so we can't rely on drm_atomic_get_old_plane_state() and we still need to use plane->state. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-4-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index fe47715c31e4..c8b88990506e 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -793,11 +793,11 @@ drm_atomic_get_new_connector_state(const struct drm_atomic_state *state, * @state: global atomic state object * @plane: plane to grab * - * This function returns the plane state for the given plane, either from - * @state, or if the plane isn't part of the atomic state update, from @plane. - * This is useful in atomic check callbacks, when drivers need to peek at, but - * not change, state of other planes, since it avoids threading an error code - * back up the call chain. + * This function returns the plane state for the given plane, either the + * new plane state from @state, or if the plane isn't part of the atomic + * state update, from @plane. This is useful in atomic check callbacks, + * when drivers need to peek at, but not change, state of other planes, + * since it avoids threading an error code back up the call chain. * * WARNING: * @@ -818,9 +818,15 @@ static inline const struct drm_plane_state * __drm_atomic_get_current_plane_state(const struct drm_atomic_state *state, struct drm_plane *plane) { - if (state->planes[drm_plane_index(plane)].state) - return state->planes[drm_plane_index(plane)].state; + struct drm_plane_state *plane_state; + plane_state = drm_atomic_get_new_plane_state(state, plane); + if (plane_state) + return plane_state; + + /* + * If the plane isn't part of the state, fallback to the currently active one. + */ return plane->state; } From 618c234740521e74ef81cfbe272c768597267374 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:20 +0200 Subject: [PATCH 012/128] drm/atomic: Convert drm_atomic_get_plane_state() to use new plane state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_atomic_get_plane_state() function calls the deprecated drm_atomic_get_existing_plane_state() helper to get find if a plane state had already been allocated and was part of the given drm_atomic_state. At the point in time where drm_atomic_get_plane_state() can be called (ie, during atomic_check), the existing state is the new state and drm_atomic_get_existing_plane_state() can thus be replaced by drm_atomic_get_new_plane_state(). Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-5-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index eeae4553894c..878ded3220ee 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -534,7 +534,7 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, WARN_ON(plane->old_fb); WARN_ON(plane->crtc); - plane_state = drm_atomic_get_existing_plane_state(state, plane); + plane_state = drm_atomic_get_new_plane_state(state, plane); if (plane_state) return plane_state; From 28af3ca1de3563cca54a83c25b48431fee049d2f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:21 +0200 Subject: [PATCH 013/128] drm/vkms: Convert vkms_crtc_atomic_check() to use new plane state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vkms_crtc_atomic_check() function calls the deprecated drm_atomic_get_existing_plane_state() helper for its internal mode checking logic. During atomic_check, the existing state is the new state and drm_atomic_get_existing_plane_state() can thus be replaced by drm_atomic_get_new_plane_state(). Reviewed-by: Louis Chauvet Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-6-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vkms/vkms_crtc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index bd79f24686dc..bac0790c6577 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -127,7 +127,7 @@ static int vkms_crtc_atomic_check(struct drm_crtc *crtc, return ret; drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) { - plane_state = drm_atomic_get_existing_plane_state(crtc_state->state, plane); + plane_state = drm_atomic_get_new_plane_state(crtc_state->state, plane); WARN_ON(!plane_state); if (!plane_state->visible) @@ -143,7 +143,7 @@ static int vkms_crtc_atomic_check(struct drm_crtc *crtc, i = 0; drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) { - plane_state = drm_atomic_get_existing_plane_state(crtc_state->state, plane); + plane_state = drm_atomic_get_new_plane_state(crtc_state->state, plane); if (!plane_state->visible) continue; From c92e072dfaaa54116cbe1bcf1ef8c61a7e461e5a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:22 +0200 Subject: [PATCH 014/128] drm/tilcdc: crtc: Use drm_atomic_helper_check_crtc_primary_plane() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the tilcdc_crtc_atomic_check(), the tilcdc driver hand-crafts its own implementation of drm_atomic_helper_check_crtc_primary_plane(). And it does so by accessing the state pointer in drm_atomic_state->planes which is deprecated. Let's use the right helper here. Reviewed-by: Ville Syrjälä Acked-by: Jyri Sarha Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-7-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index b5f60b2b2d0e..5718d9d83a49 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -676,14 +676,7 @@ static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc, if (!crtc_state->active) return 0; - if (state->planes[0].ptr != crtc->primary || - state->planes[0].state == NULL || - state->planes[0].state->crtc != crtc) { - dev_dbg(crtc->dev->dev, "CRTC primary plane must be present"); - return -EINVAL; - } - - return 0; + return drm_atomic_helper_check_crtc_primary_plane(crtc_state); } static int tilcdc_crtc_enable_vblank(struct drm_crtc *crtc) From 93d851281755f0a279402e1b88d29584ed8244e9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:23 +0200 Subject: [PATCH 015/128] drm/atomic: Remove unused drm_atomic_get_existing_plane_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_atomic_get_existing_plane_state() function is deprecated and isn't used anymore, so let's remove it. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-8-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index c8b88990506e..b2cc9c4a9beb 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -700,24 +700,6 @@ drm_atomic_get_new_crtc_state(const struct drm_atomic_state *state, return state->crtcs[drm_crtc_index(crtc)].new_state; } -/** - * drm_atomic_get_existing_plane_state - get plane state, if it exists - * @state: global atomic state object - * @plane: plane to grab - * - * This function returns the plane state for the given plane, or NULL - * if the plane is not part of the global atomic state. - * - * This function is deprecated, @drm_atomic_get_old_plane_state or - * @drm_atomic_get_new_plane_state should be used instead. - */ -static inline struct drm_plane_state * -drm_atomic_get_existing_plane_state(const struct drm_atomic_state *state, - struct drm_plane *plane) -{ - return state->planes[drm_plane_index(plane)].state; -} - /** * drm_atomic_get_old_plane_state - get plane state, if it exists * @state: global atomic state object From 7ca4e7fa4d57ce6eac4842e925ea1f98ea54a365 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:24 +0200 Subject: [PATCH 016/128] drm/atomic: Document __drm_planes_state state pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the old and new state pointers are somewhat self-explanatory, the state pointer and its relation to the other two really isn't. Now that we've cleaned up everything and it isn't used in any modesetting path, we can document what it's still useful for: to free the right state when we free the global state. Reviewed-by: Luca Ceresoli Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-9-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index b2cc9c4a9beb..a05012adcf94 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -159,7 +159,23 @@ struct drm_crtc_commit { struct __drm_planes_state { struct drm_plane *ptr; - struct drm_plane_state *state, *old_state, *new_state; + + /** + * @state: + * + * Used to track the @drm_plane_state we will need to free when + * tearing down the associated &drm_atomic_state in + * $drm_mode_config_funcs.atomic_state_clear or + * drm_atomic_state_default_clear(). + * + * Before a commit, and the call to + * drm_atomic_helper_swap_state() in particular, it points to + * the same state than @new_state. After a commit, it points to + * the same state than @old_state. + */ + struct drm_plane_state *state; + + struct drm_plane_state *old_state, *new_state; }; struct __drm_crtcs_state { From b8563c94189a677e129addbc80e817d113e5f113 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:25 +0200 Subject: [PATCH 017/128] drm/atomic: Convert drm_atomic_get_crtc_state() to use new connector state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_atomic_get_crtc_state() function calls the deprecated drm_atomic_get_existing_crtc_state() helper to get find if a crtc state had already been allocated and was part of the given drm_atomic_state. At the point in time where drm_atomic_get_crtc_state() can be called (ie, during atomic_check), the existing state is the new state and drm_atomic_get_existing_crtc_state() can thus be replaced by drm_atomic_get_new_crtc_state(). Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-10-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 878ded3220ee..f310da842f8e 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -349,7 +349,7 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, WARN_ON(!state->acquire_ctx); - crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (crtc_state) return crtc_state; From 78f55a23d110d5fa530bbc47ec39e904abbeff09 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:26 +0200 Subject: [PATCH 018/128] drm/ingenic: ipu: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ingenic IPU atomic_set_property implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called during the state building process, before atomic_check, and thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Paul Cercueil Tested-by: Paul Cercueil Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-11-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/ingenic/ingenic-ipu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c index 26ebf424d63e..2574a4b4d40a 100644 --- a/drivers/gpu/drm/ingenic/ingenic-ipu.c +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c @@ -705,7 +705,7 @@ ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane, ipu->sharpness = val; if (state->crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc); if (WARN_ON(!crtc_state)) return -EINVAL; From 41230da56dac41462e0dff6e6364923617650fcd Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:27 +0200 Subject: [PATCH 019/128] drm/arm/malidp: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The malidp atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Liviu Dudau Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-12-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/arm/malidp_planes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 87f2e5ee8790..f1a5014bcfa1 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -263,7 +263,7 @@ static int malidp_se_check_scaling(struct malidp_plane *mp, struct drm_plane_state *state) { struct drm_crtc_state *crtc_state = - drm_atomic_get_existing_crtc_state(state->state, state->crtc); + drm_atomic_get_new_crtc_state(state->state, state->crtc); struct malidp_crtc_state *mc; u32 src_w, src_h; int ret; From 3d775873747c99f0cdf685432237178a5d0557c0 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:28 +0200 Subject: [PATCH 020/128] drm/armada: Drop always true condition in atomic_check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_atomic_state pointer passed to atomic_check is always valid, so checking if it's NULL or not is pointless. Remove the check. Suggested-by: Ville Syrjälä Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-13-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/armada/armada_plane.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_plane.c b/drivers/gpu/drm/armada/armada_plane.c index cc47c032dbc1..12a01a51d6fb 100644 --- a/drivers/gpu/drm/armada/armada_plane.c +++ b/drivers/gpu/drm/armada/armada_plane.c @@ -94,12 +94,7 @@ int armada_drm_plane_atomic_check(struct drm_plane *plane, return 0; } - if (state) - crtc_state = drm_atomic_get_existing_crtc_state(state, - crtc); - else - crtc_state = crtc->state; - + crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 0, INT_MAX, true, false); From 488b14a0ac0eabf635ca85c6bf05152cb6d7e7f8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:29 +0200 Subject: [PATCH 021/128] drm/armada: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The armada atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-14-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/armada/armada_plane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/armada/armada_plane.c b/drivers/gpu/drm/armada/armada_plane.c index 12a01a51d6fb..dae81ebafdb4 100644 --- a/drivers/gpu/drm/armada/armada_plane.c +++ b/drivers/gpu/drm/armada/armada_plane.c @@ -94,7 +94,7 @@ int armada_drm_plane_atomic_check(struct drm_plane *plane, return 0; } - crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 0, INT_MAX, true, false); From c0253fc4c808d5fd5ced15659a28fb51a1834eed Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:30 +0200 Subject: [PATCH 022/128] drm/atmel-hlcdc: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The atmel-hlcdc atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Reviewed-by: Manikandan Muralidharan Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-15-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 4a7ba0918eca..59be5f64c058 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -714,7 +714,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, if (!hstate->base.crtc || WARN_ON(!fb)) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(state, s->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, s->crtc); mode = &crtc_state->adjusted_mode; ret = drm_atomic_helper_check_plane_state(s, crtc_state, From aa61da43248a673866be10cb74971e3fd51827c2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:31 +0200 Subject: [PATCH 023/128] drm/exynos: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The exynos atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-16-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/exynos/exynos_drm_plane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 7c3aa77186d3..6400070a4c9b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -58,7 +58,7 @@ static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state) struct drm_plane_state *state = &exynos_state->base; struct drm_crtc *crtc = state->crtc; struct drm_crtc_state *crtc_state = - drm_atomic_get_existing_crtc_state(state->state, crtc); + drm_atomic_get_new_crtc_state(state->state, crtc); struct drm_display_mode *mode = &crtc_state->adjusted_mode; int crtc_x, crtc_y; unsigned int crtc_w, crtc_h; From 7f3364784b56e34423d5ed5638832799ca8bfacd Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:32 +0200 Subject: [PATCH 024/128] drm/imx-dc: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The imx-dc atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Reviewed-by: Liu Ying Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-17-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/imx/dc/dc-plane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/imx/dc/dc-plane.c b/drivers/gpu/drm/imx/dc/dc-plane.c index d8b946fb90de..e40d5d66c5c1 100644 --- a/drivers/gpu/drm/imx/dc/dc-plane.c +++ b/drivers/gpu/drm/imx/dc/dc-plane.c @@ -106,7 +106,7 @@ dc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) } crtc_state = - drm_atomic_get_existing_crtc_state(state, plane_state->crtc); + drm_atomic_get_new_crtc_state(state, plane_state->crtc); if (WARN_ON(!crtc_state)) return -EINVAL; From 14ccb2861a573abb2dcb76791cefee2211ca8d7d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:33 +0200 Subject: [PATCH 025/128] drm/imx-dcss: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The imx-dcss atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Laurentiu Palcu Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-18-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/imx/dcss/dcss-plane.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c index ab6d32bad756..3a063a53c8df 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-plane.c +++ b/drivers/gpu/drm/imx/dcss/dcss-plane.c @@ -159,8 +159,8 @@ static int dcss_plane_atomic_check(struct drm_plane *plane, dma_obj = drm_fb_dma_get_gem_obj(fb, 0); WARN_ON(!dma_obj); - crtc_state = drm_atomic_get_existing_crtc_state(state, - new_plane_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, + new_plane_state->crtc); hdisplay = crtc_state->adjusted_mode.hdisplay; vdisplay = crtc_state->adjusted_mode.vdisplay; From ba7c370cbe8632db7981f397521b36540573c024 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:34 +0200 Subject: [PATCH 026/128] drm/imx-ipuv3: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The imx-ipuv3 atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-19-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c index 704c549750f9..df19560e41b4 100644 --- a/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c @@ -386,8 +386,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane, return -EINVAL; crtc_state = - drm_atomic_get_existing_crtc_state(state, - new_state->crtc); + drm_atomic_get_new_crtc_state(state, new_state->crtc); if (WARN_ON(!crtc_state)) return -EINVAL; From 21c73cab4dec12b22cd79b4582cc09c4f1cc7a9d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:35 +0200 Subject: [PATCH 027/128] drm/ingenic: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ingenic atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Paul Cercueil Tested-by: Paul Cercueil Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-20-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 3 +-- drivers/gpu/drm/ingenic/ingenic-ipu.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 9db1ceaed518..05faed933e56 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -471,8 +471,7 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane *plane, if (priv->soc_info->plane_f0_not_working && plane == &priv->f0) return -EINVAL; - crtc_state = drm_atomic_get_existing_crtc_state(state, - crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (WARN_ON(!crtc_state)) return -EINVAL; diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c index 2574a4b4d40a..32638a713241 100644 --- a/drivers/gpu/drm/ingenic/ingenic-ipu.c +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c @@ -580,7 +580,7 @@ static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane, if (!crtc) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (WARN_ON(!crtc_state)) return -EINVAL; From afce57a71dd734338f01d0c9e238dd61a63cdc40 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:36 +0200 Subject: [PATCH 028/128] drm/kmb: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kmb atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-21-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/kmb/kmb_plane.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/kmb/kmb_plane.c b/drivers/gpu/drm/kmb/kmb_plane.c index 9e0562aa2bcb..9562fe6711ff 100644 --- a/drivers/gpu/drm/kmb/kmb_plane.c +++ b/drivers/gpu/drm/kmb/kmb_plane.c @@ -129,8 +129,7 @@ static int kmb_plane_atomic_check(struct drm_plane *plane, } can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY); crtc_state = - drm_atomic_get_existing_crtc_state(state, - new_plane_state->crtc); + drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); return drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, DRM_PLANE_NO_SCALING, From addc98e2f98916630b68fea123ef6cafaf736e04 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:37 +0200 Subject: [PATCH 029/128] drm/logicvc: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The logicvc atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-22-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/logicvc/logicvc_layer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/logicvc/logicvc_layer.c b/drivers/gpu/drm/logicvc/logicvc_layer.c index 464000aea765..eab4d773f92b 100644 --- a/drivers/gpu/drm/logicvc/logicvc_layer.c +++ b/drivers/gpu/drm/logicvc/logicvc_layer.c @@ -96,8 +96,8 @@ static int logicvc_plane_atomic_check(struct drm_plane *drm_plane, if (!new_state->crtc) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(new_state->state, - new_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(new_state->state, + new_state->crtc); if (WARN_ON(!crtc_state)) return -EINVAL; From 97481a9a00d1760f43297c5049a2000d42e96220 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:38 +0200 Subject: [PATCH 030/128] drm/loongson: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loongson atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-23-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/loongson/lsdc_plane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/loongson/lsdc_plane.c b/drivers/gpu/drm/loongson/lsdc_plane.c index aa9a97f9c4dc..2967a5cca069 100644 --- a/drivers/gpu/drm/loongson/lsdc_plane.c +++ b/drivers/gpu/drm/loongson/lsdc_plane.c @@ -196,7 +196,7 @@ static int lsdc_cursor_plane_atomic_async_check(struct drm_plane *plane, return -EINVAL; } - crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); if (!crtc_state->active) return -EINVAL; From afea87890ddace6980fdff405f2ea4cacc5c9b22 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:39 +0200 Subject: [PATCH 031/128] drm/mediatek: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mediatek atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-24-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/mediatek/mtk_plane.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mediatek/mtk_plane.c b/drivers/gpu/drm/mediatek/mtk_plane.c index 02349bd44001..1b5667ddbb03 100644 --- a/drivers/gpu/drm/mediatek/mtk_plane.c +++ b/drivers/gpu/drm/mediatek/mtk_plane.c @@ -122,7 +122,8 @@ static int mtk_plane_atomic_async_check(struct drm_plane *plane, if (ret) return ret; - crtc_state = drm_atomic_get_existing_crtc_state(state, new_plane_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, + new_plane_state->crtc); return drm_atomic_helper_check_plane_state(plane->state, crtc_state, DRM_PLANE_NO_SCALING, From 7be36c7b60c83f53f55191a605a8f079185a0412 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:40 +0200 Subject: [PATCH 032/128] drm/msm/mdp5: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The msm atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Dmitry Baryshkov Acked-by: Dmitry Baryshkov Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-25-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c index 7c790406d533..4ca183fb61a9 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c @@ -336,8 +336,7 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, if (!crtc) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(state, - crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (WARN_ON(!crtc_state)) return -EINVAL; @@ -373,8 +372,8 @@ static int mdp5_plane_atomic_async_check(struct drm_plane *plane, int min_scale, max_scale; int ret; - crtc_state = drm_atomic_get_existing_crtc_state(state, - new_plane_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, + new_plane_state->crtc); if (WARN_ON(!crtc_state)) return -EINVAL; From 0a10e3998239d032ca62e0cab634f7592210d86f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:41 +0200 Subject: [PATCH 033/128] drm/omap: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The omap atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-26-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/omapdrm/omap_plane.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 24a2ded08b45..d74ef6694c10 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -229,7 +229,7 @@ static int omap_plane_atomic_check(struct drm_plane *plane, if (!crtc) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); /* we should have a crtc state if the plane is attached to a crtc */ if (WARN_ON(!crtc_state)) return 0; From 0a36e14befcadb60d6727a5874a51b0aa204cd16 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:42 +0200 Subject: [PATCH 034/128] drm/rockchip: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rockchip atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-27-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 6 +++--- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index ba6b0528d1e5..5369b77ea434 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -826,8 +826,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane, if (!crtc || WARN_ON(!fb)) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(state, - crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (WARN_ON(!crtc_state)) return -EINVAL; @@ -1092,7 +1091,8 @@ static int vop_plane_atomic_async_check(struct drm_plane *plane, if (!plane->state->fb) return -EINVAL; - crtc_state = drm_atomic_get_existing_crtc_state(state, new_plane_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, + new_plane_state->crtc); /* Special case for asynchronous cursor updates. */ if (!crtc_state) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index b50927a824b4..4556cf7a3364 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -1013,7 +1013,7 @@ static int vop2_plane_atomic_check(struct drm_plane *plane, vop2 = vp->vop2; vop2_data = vop2->data; - cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc); + cstate = drm_atomic_get_new_crtc_state(pstate->state, crtc); if (WARN_ON(!cstate)) return -EINVAL; From 9bb3ceb961bd0aa6eb9d2512b0242c9f63a1f59f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:43 +0200 Subject: [PATCH 035/128] drm/sun4i: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sun4i atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-28-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 3 +-- drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c index f97be0040aab..94ac6ad6f306 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c @@ -206,8 +206,7 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane, if (!crtc) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(state, - crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (WARN_ON(!crtc_state)) return -EINVAL; diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c index a09ee4097537..1f77e1d29845 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -327,8 +327,7 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, if (!crtc) return 0; - crtc_state = drm_atomic_get_existing_crtc_state(state, - crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); if (WARN_ON(!crtc_state)) return -EINVAL; From 2ebd2179d3893d70141cfed5d4ba0c80479ed3dd Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:44 +0200 Subject: [PATCH 036/128] drm/tegra: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tegra atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-29-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/tegra/dc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 59d5c1ba145a..0f80da3544c9 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1033,7 +1033,7 @@ static int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_a int min_scale, max_scale; int err; - crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); if (WARN_ON(!crtc_state)) return -EINVAL; From 5d79c8493012dcf923afb7a93b8c3c3cd4567d88 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:45 +0200 Subject: [PATCH 037/128] drm/tilcdc: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tilcdc atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Acked-by: Jyri Sarha Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-30-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/tilcdc/tilcdc_plane.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_plane.c b/drivers/gpu/drm/tilcdc/tilcdc_plane.c index cf77a8ce7398..aa72ca679598 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_plane.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_plane.c @@ -42,8 +42,7 @@ static int tilcdc_plane_atomic_check(struct drm_plane *plane, return -EINVAL; } - crtc_state = drm_atomic_get_existing_crtc_state(state, - new_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); /* we should have a crtc state if the plane is attached to a crtc */ if (WARN_ON(!crtc_state)) return 0; From 218fa3e6a1e5eb9ecfbb9fe083ad454bfa96050c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:46 +0200 Subject: [PATCH 038/128] drm/vboxvideo: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vboxvideo atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Hans de Goede Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-31-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vboxvideo/vbox_mode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c index 9ff3bade9795..aa0dded595b6 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_mode.c +++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c @@ -262,8 +262,8 @@ static int vbox_primary_atomic_check(struct drm_plane *plane, struct drm_crtc_state *crtc_state = NULL; if (new_state->crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state, - new_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, + new_state->crtc); if (WARN_ON(!crtc_state)) return -EINVAL; } @@ -344,8 +344,8 @@ static int vbox_cursor_atomic_check(struct drm_plane *plane, int ret; if (new_state->crtc) { - crtc_state = drm_atomic_get_existing_crtc_state(state, - new_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, + new_state->crtc); if (WARN_ON(!crtc_state)) return -EINVAL; } From 4bcd18bbf51ce84924a2c6a126eda65f5b27d25b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:47 +0200 Subject: [PATCH 039/128] drm/vc4: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vc4 atomic_check implementation uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Ville Syrjälä Acked-by: Dave Stevenson Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-32-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_plane.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 056d344c5411..b4a53f68865b 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -497,8 +497,7 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) u32 v_subsample = fb->format->vsub; int ret; - crtc_state = drm_atomic_get_existing_crtc_state(state->state, - state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc); if (!crtc_state) { DRM_DEBUG_KMS("Invalid crtc state\n"); return -EINVAL; @@ -875,8 +874,7 @@ static void vc4_plane_calc_load(struct drm_plane_state *state) unsigned int vscale_factor; vc4_state = to_vc4_plane_state(state); - crtc_state = drm_atomic_get_existing_crtc_state(state->state, - state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc); vrefresh = drm_mode_vrefresh(&crtc_state->adjusted_mode); /* The HVS is able to process 2 pixels/cycle when scaling the source, From 653ce3a3f683673cb3f8904e08bffc144dd0e972 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:48 +0200 Subject: [PATCH 040/128] drm/atomic: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_atomic_connector_check() function uses the deprecated drm_atomic_get_existing_crtc_state() helper. This hook is called as part of the global atomic_check, thus before the states are swapped. The existing state thus points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Dmitry Baryshkov Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-33-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index f310da842f8e..eb55bdf64fc7 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -480,8 +480,8 @@ static int drm_atomic_connector_check(struct drm_connector *connector, } if (state->crtc) - crtc_state = drm_atomic_get_existing_crtc_state(state->state, - state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state->state, + state->crtc); if (writeback_job->fb && !crtc_state->active) { drm_dbg_atomic(connector->dev, From d8ebc4162fcd2fde6ae70168e6cb63bb64e93544 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:49 +0200 Subject: [PATCH 041/128] drm/framebuffer: Switch to drm_atomic_get_new_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The atomic_remove_fb() function uses the deprecated drm_atomic_get_existing_crtc_state() helper. Despite its name, this function builds and commit a new drm_atomic_state, and the call to drm_atomic_get_existing_crtc_state() is part of the state building, thus happening before the states are swapped. As such, the existing state points to the new state, and we can use drm_atomic_get_new_crtc_state() instead. Reviewed-by: Dmitry Baryshkov Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-34-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_framebuffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index adbb73f00d68..18e753ade001 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -1048,7 +1048,7 @@ retry: plane_state->crtc->base.id, plane_state->crtc->name, fb->base.id); - crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc); + crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); ret = drm_atomic_add_affected_connectors(state, plane_state->crtc); if (ret) From 3c2b2136debfc200b8ebcd8e5302348b6c86158d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:50 +0200 Subject: [PATCH 042/128] drm/atomic: Remove unused drm_atomic_get_existing_crtc_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_atomic_get_existing_crtc_state() function is deprecated and isn't used anymore, so let's remove it. Reviewed-by: Dmitry Baryshkov Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-35-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index a05012adcf94..f53a32688b33 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -669,24 +669,6 @@ struct drm_crtc * drm_atomic_get_new_crtc_for_encoder(struct drm_atomic_state *state, struct drm_encoder *encoder); -/** - * drm_atomic_get_existing_crtc_state - get CRTC state, if it exists - * @state: global atomic state object - * @crtc: CRTC to grab - * - * This function returns the CRTC state for the given CRTC, or NULL - * if the CRTC is not part of the global atomic state. - * - * This function is deprecated, @drm_atomic_get_old_crtc_state or - * @drm_atomic_get_new_crtc_state should be used instead. - */ -static inline struct drm_crtc_state * -drm_atomic_get_existing_crtc_state(const struct drm_atomic_state *state, - struct drm_crtc *crtc) -{ - return state->crtcs[drm_crtc_index(crtc)].state; -} - /** * drm_atomic_get_old_crtc_state - get old CRTC state, if it exists * @state: global atomic state object From 4ae41729a658aee3e68a609cc0275658b724ebc4 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:51 +0200 Subject: [PATCH 043/128] drm/atomic: Document __drm_crtcs_state state pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the old and new state pointers are somewhat self-explanatory, the state pointer and its relation to the other two really isn't. Now that we've cleaned up everything and it isn't used in any modesetting path, we can document what it's still useful for: to free the right state when we free the global state. Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-36-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index f53a32688b33..6b634b747490 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -180,7 +180,23 @@ struct __drm_planes_state { struct __drm_crtcs_state { struct drm_crtc *ptr; - struct drm_crtc_state *state, *old_state, *new_state; + + /** + * @state: + * + * Used to track the @drm_crtc_state we will need to free when + * tearing down the associated &drm_atomic_state in + * $drm_mode_config_funcs.atomic_state_clear or + * drm_atomic_state_default_clear(). + * + * Before a commit, and the call to + * drm_atomic_helper_swap_state() in particular, it points to + * the same state than @new_state. After a commit, it points to + * the same state than @old_state. + */ + struct drm_crtc_state *state; + + struct drm_crtc_state *old_state, *new_state; /** * @commit: From 22c0dd19113a15b5d1a294559f8b3cf16cb6cec7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:52 +0200 Subject: [PATCH 044/128] drm/ingenic: crtc: Switch to ingenic_drm_get_new_priv_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ingenic CRTC atomic_enable() implementation will indirectly call drm_atomic_get_private_obj_state() through ingenic_drm_get_priv_state(). drm_atomic_get_private_obj_state() will either return the new state for the object in the global state if it exists, or will allocate a new one and add it to the global state. atomic_enable() however isn't allowed to modify the global state. So what the implementation should use is the drm_atomic_get_new_private_obj_state() helper to get the new state for the CRTC, without performing an extra allocation. We still need to make sure the private state will be part of the global state by the time atomic_enable runs, so we still need to call ingenic_drm_get_priv_state() in atomic_check. We can then call ingenic_drm_get_new_priv_state() in atomic_enable, which is a wrapper around drm_atomic_get_new_private_obj_state(). Reported-by: Dmitry Baryshkov Suggested-by: Ville Syrjälä Reviewed-by: Paul Cercueil Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-37-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 05faed933e56..d3213fbf22be 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -247,8 +247,8 @@ static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc, struct ingenic_drm_private_state *priv_state; unsigned int next_id; - priv_state = ingenic_drm_get_priv_state(priv, state); - if (WARN_ON(IS_ERR(priv_state))) + priv_state = ingenic_drm_get_new_priv_state(priv, state); + if (WARN_ON(!priv_state)) return; /* Set addresses of our DMA descriptor chains */ @@ -340,6 +340,7 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, crtc); struct ingenic_drm *priv = drm_crtc_get_priv(crtc); struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL; + struct ingenic_drm_private_state *priv_state; if (crtc_state->gamma_lut && drm_color_lut_size(crtc_state->gamma_lut) != ARRAY_SIZE(priv->dma_hwdescs->palette)) { @@ -347,6 +348,11 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, return -EINVAL; } + /* We will need the state in atomic_enable, so let's make sure it's part of the state */ + priv_state = ingenic_drm_get_priv_state(priv, state); + if (IS_ERR(priv_state)) + return PTR_ERR(priv_state); + if (drm_atomic_crtc_needs_modeset(crtc_state) && priv->soc_info->has_osd) { f1_state = drm_atomic_get_plane_state(crtc_state->state, &priv->f1); From 40d2a29d86798c2ae709528f76dcd12feeede6c2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:53 +0200 Subject: [PATCH 045/128] drm/atomic: Convert drm_atomic_get_private_obj_state() to use new plane state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_atomic_get_private_obj_state() function tries to find if a private_obj had already been allocated and was part of the given drm_atomic_state. If one is found, it returns the existing state pointer. At the point in time where drm_atomic_get_private_obj_state() can be called (ie, during atomic_check), the existing state is the new state and we can thus replace the hand-crafted logic by a call to drm_atomic_get_new_private_obj_state(). Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-38-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index eb55bdf64fc7..0fda567c3900 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -831,14 +831,14 @@ struct drm_private_state * drm_atomic_get_private_obj_state(struct drm_atomic_state *state, struct drm_private_obj *obj) { - int index, num_objs, i, ret; + int index, num_objs, ret; size_t size; struct __drm_private_objs_state *arr; struct drm_private_state *obj_state; - for (i = 0; i < state->num_private_objs; i++) - if (obj == state->private_objs[i].ptr) - return state->private_objs[i].state; + obj_state = drm_atomic_get_new_private_obj_state(state, obj); + if (obj_state) + return obj_state; ret = drm_modeset_lock(&obj->lock, state->acquire_ctx); if (ret) From 7a031e8d3528ba0860d282ffd3c88fbda4bf8c4c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Sep 2025 12:59:54 +0200 Subject: [PATCH 046/128] drm/atomic: Document __drm_private_objs_state state pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the old and new state pointers are somewhat self-explanatory, the state pointer and its relation to the other two really isn't. Now that we've cleaned up everything and it isn't used in any modesetting path, we can document what it's still useful for: to free the right state when we free the global state. Reviewed-by: Luca Ceresoli Reviewed-by: Ville Syrjälä Link: https://lore.kernel.org/r/20250930-drm-no-more-existing-state-v5-39-eeb9e1287907@kernel.org Signed-off-by: Maxime Ripard --- include/drm/drm_atomic.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 6b634b747490..c8ab2163bf65 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -391,7 +391,23 @@ struct drm_private_state { struct __drm_private_objs_state { struct drm_private_obj *ptr; - struct drm_private_state *state, *old_state, *new_state; + + /** + * @state: + * + * Used to track the @drm_private_state we will need to free + * when tearing down the associated &drm_atomic_state in + * $drm_mode_config_funcs.atomic_state_clear or + * drm_atomic_state_default_clear(). + * + * Before a commit, and the call to + * drm_atomic_helper_swap_state() in particular, it points to + * the same state than @new_state. After a commit, it points to + * the same state than @old_state. + */ + struct drm_private_state *state; + + struct drm_private_state *old_state, *new_state; }; /** From 78b4d6463e9e69e5103f98b367f8984ad12cdc6f Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Fri, 26 Sep 2025 17:26:27 +0200 Subject: [PATCH 047/128] drm/vgem-fence: Fix potential deadlock on release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A timer that expires a vgem fence automatically in 10 seconds is now released with timer_delete_sync() from fence->ops.release() called on last dma_fence_put(). In some scenarios, it can run in IRQ context, which is not safe unless TIMER_IRQSAFE is used. One potentially risky scenario was demonstrated in Intel DRM CI trybot, BAT run on machine bat-adlp-6, while working on new IGT subtests syncobj_timeline@stress-* as user space replacements of some problematic test cases of a dma-fence-chain selftest [1]. [117.004338] ================================ [117.004340] WARNING: inconsistent lock state [117.004342] 6.17.0-rc7-CI_DRM_17270-g7644974e648c+ #1 Tainted: G S U [117.004346] -------------------------------- [117.004347] inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage. [117.004349] swapper/0/0 [HC1[1]:SC1[1]:HE0:SE0] takes: [117.004352] ffff888138f86aa8 ((&fence->timer)){?.-.}-{0:0}, at: __timer_delete_sync+0x4b/0x190 [117.004361] {HARDIRQ-ON-W} state was registered at: [117.004363] lock_acquire+0xc4/0x2e0 [117.004366] call_timer_fn+0x80/0x2a0 [117.004368] __run_timers+0x231/0x310 [117.004370] run_timer_softirq+0x76/0xe0 [117.004372] handle_softirqs+0xd4/0x4d0 [117.004375] __irq_exit_rcu+0x13f/0x160 [117.004377] irq_exit_rcu+0xe/0x20 [117.004379] sysvec_apic_timer_interrupt+0xa0/0xc0 [117.004382] asm_sysvec_apic_timer_interrupt+0x1b/0x20 [117.004385] cpuidle_enter_state+0x12b/0x8a0 [117.004388] cpuidle_enter+0x2e/0x50 [117.004393] call_cpuidle+0x22/0x60 [117.004395] do_idle+0x1fd/0x260 [117.004398] cpu_startup_entry+0x29/0x30 [117.004401] start_secondary+0x12d/0x160 [117.004404] common_startup_64+0x13e/0x141 [117.004407] irq event stamp: 2282669 [117.004409] hardirqs last enabled at (2282668): [] _raw_spin_unlock_irqrestore+0x51/0x80 [117.004414] hardirqs last disabled at (2282669): [] sysvec_irq_work+0x11/0xc0 [117.004419] softirqs last enabled at (2254702): [] __do_softirq+0x10/0x18 [117.004423] softirqs last disabled at (2254725): [] __irq_exit_rcu+0x13f/0x160 [117.004426] other info that might help us debug this: [117.004429] Possible unsafe locking scenario: [117.004432] CPU0 [117.004433] ---- [117.004434] lock((&fence->timer)); [117.004436] [117.004438] lock((&fence->timer)); [117.004440] *** DEADLOCK *** [117.004443] 1 lock held by swapper/0/0: [117.004445] #0: ffffc90000003d50 ((&fence->timer)){?.-.}-{0:0}, at: call_timer_fn+0x7a/0x2a0 [117.004450] stack backtrace: [117.004453] CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Tainted: G S U 6.17.0-rc7-CI_DRM_17270-g7644974e648c+ #1 PREEMPT(voluntary) [117.004455] Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER [117.004455] Hardware name: Intel Corporation Alder Lake Client Platform/AlderLake-P DDR4 RVP, BIOS RPLPFWI1.R00.4035.A00.2301200723 01/20/2023 [117.004456] Call Trace: [117.004456] [117.004457] dump_stack_lvl+0x91/0xf0 [117.004460] dump_stack+0x10/0x20 [117.004461] print_usage_bug.part.0+0x260/0x360 [117.004463] mark_lock+0x76e/0x9c0 [117.004465] ? register_lock_class+0x48/0x4a0 [117.004467] __lock_acquire+0xbc3/0x2860 [117.004469] lock_acquire+0xc4/0x2e0 [117.004470] ? __timer_delete_sync+0x4b/0x190 [117.004472] ? __timer_delete_sync+0x4b/0x190 [117.004473] __timer_delete_sync+0x68/0x190 [117.004474] ? __timer_delete_sync+0x4b/0x190 [117.004475] timer_delete_sync+0x10/0x20 [117.004476] vgem_fence_release+0x19/0x30 [vgem] [117.004478] dma_fence_release+0xc1/0x3b0 [117.004480] ? dma_fence_release+0xa1/0x3b0 [117.004481] dma_fence_chain_release+0xe7/0x130 [117.004483] dma_fence_release+0xc1/0x3b0 [117.004484] ? _raw_spin_unlock_irqrestore+0x27/0x80 [117.004485] dma_fence_chain_irq_work+0x59/0x80 [117.004487] irq_work_single+0x75/0xa0 [117.004490] irq_work_run_list+0x33/0x60 [117.004491] irq_work_run+0x18/0x40 [117.004493] __sysvec_irq_work+0x35/0x170 [117.004494] sysvec_irq_work+0x47/0xc0 [117.004496] asm_sysvec_irq_work+0x1b/0x20 [117.004497] RIP: 0010:_raw_spin_unlock_irqrestore+0x57/0x80 [117.004499] Code: 00 75 1c 65 ff 0d d9 34 68 01 74 20 5b 41 5c 5d 31 c0 31 d2 31 c9 31 f6 31 ff c3 cc cc cc cc e8 7f 9d d3 fe fb 0f 1f 44 00 00 d7 0f 1f 44 00 00 5b 41 5c 5d 31 c0 31 d2 31 c9 31 f6 31 ff c3 [117.004499] RSP: 0018:ffffc90000003cf0 EFLAGS: 00000246 [117.004500] RAX: 0000000000000000 RBX: ffff888155e94c40 RCX: 0000000000000000 [117.004501] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [117.004502] RBP: ffffc90000003d00 R08: 0000000000000000 R09: 0000000000000000 [117.004502] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000246 [117.004502] R13: 0000000000000001 R14: 0000000000000246 R15: ffff888155e94c80 [117.004506] dma_fence_signal+0x49/0xb0 [117.004507] ? __pfx_vgem_fence_timeout+0x10/0x10 [vgem] [117.004508] vgem_fence_timeout+0x12/0x20 [vgem] [117.004509] call_timer_fn+0xa1/0x2a0 [117.004512] ? __pfx_vgem_fence_timeout+0x10/0x10 [vgem] [117.004513] __run_timers+0x231/0x310 [117.004514] ? tmigr_handle_remote+0x2ac/0x560 [117.004517] timer_expire_remote+0x46/0x70 [117.004518] tmigr_handle_remote+0x433/0x560 [117.004520] ? __run_timers+0x239/0x310 [117.004521] ? run_timer_softirq+0x21/0xe0 [117.004522] ? lock_release+0xce/0x2a0 [117.004524] run_timer_softirq+0xcf/0xe0 [117.004525] handle_softirqs+0xd4/0x4d0 [117.004526] __irq_exit_rcu+0x13f/0x160 [117.004527] irq_exit_rcu+0xe/0x20 [117.004528] sysvec_apic_timer_interrupt+0xa0/0xc0 [117.004529] [117.004529] [117.004529] asm_sysvec_apic_timer_interrupt+0x1b/0x20 [117.004530] RIP: 0010:cpuidle_enter_state+0x12b/0x8a0 [117.004532] Code: 48 0f a3 05 97 ce 0e 01 0f 82 2e 03 00 00 31 ff e8 8a 41 bd fe 80 7d d0 00 0f 85 11 03 00 00 e8 8b 06 d5 fe fb 0f 1f 44 00 00 <45> 85 f6 0f 88 67 02 00 00 4d 63 ee 49 83 fd 0a 0f 83 34 06 00 00 [117.004532] RSP: 0018:ffffffff83403d88 EFLAGS: 00000246 [117.004533] RAX: 0000000000000000 RBX: ffff88888f046440 RCX: 0000000000000000 [117.004533] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [117.004534] RBP: ffffffff83403dd8 R08: 0000000000000000 R09: 0000000000000000 [117.004534] R10: 0000000000000000 R11: 0000000000000000 R12: ffffffff837cbe80 [117.004534] R13: 0000000000000004 R14: 0000000000000004 R15: 0000001ad1df466b [117.004537] ? cpuidle_enter_state+0x125/0x8a0 [117.004538] ? sched_clock_noinstr+0x9/0x10 [117.004540] cpuidle_enter+0x2e/0x50 [117.004542] call_cpuidle+0x22/0x60 [117.004542] do_idle+0x1fd/0x260 [117.004544] cpu_startup_entry+0x29/0x30 [117.004546] rest_init+0x104/0x200 [117.004548] start_kernel+0x93d/0xbd0 [117.004550] ? load_ucode_intel_bsp+0x2a/0x90 [117.004551] ? sme_unmap_bootdata+0x14/0x80 [117.004554] x86_64_start_reservations+0x18/0x30 [117.004555] x86_64_start_kernel+0xfd/0x150 [117.004556] ? soft_restart_cpu+0x14/0x14 [117.004558] common_startup_64+0x13e/0x141 [117.004560] [117.004565] ------------[ cut here ]------------ [117.004692] WARNING: CPU: 0 PID: 0 at kernel/time/timer.c:1610 __timer_delete_sync+0x126/0x190 [117.004697] Modules linked in: vgem snd_hda_codec_intelhdmi snd_hda_codec_hdmi i915 prime_numbers ttm drm_buddy drm_display_helper cec rc_core i2c_algo_bit hid_sensor_custom hid_sensor_hub hid_generic intel_ishtp_hid hid intel_uncore_frequency intel_uncore_frequency_common x86_pkg_temp_thermal intel_powerclamp cmdlinepart ee1004 r8153_ecm spi_nor coretemp cdc_ether mei_pxp mei_hdcp usbnet mtd intel_rapl_msr wmi_bmof kvm_intel snd_hda_intel snd_intel_dspcfg processor_thermal_device_pci kvm snd_hda_codec processor_thermal_device irqbypass processor_thermal_wt_hint polyval_clmulni platform_temperature_control snd_hda_core ghash_clmulni_intel processor_thermal_rfim spi_pxa2xx_platform snd_hwdep aesni_intel processor_thermal_rapl dw_dmac snd_pcm dw_dmac_core intel_rapl_common r8152 rapl mii intel_cstate spi_pxa2xx_core i2c_i801 processor_thermal_wt_req snd_timer i2c_mux mei_me intel_ish_ipc processor_thermal_power_floor e1000e snd i2c_smbus spi_intel_pci processor_thermal_mbox mei soundcore intel_ishtp thunderbolt idma64 [117.004733] spi_intel int340x_thermal_zone igen6_edac binfmt_misc intel_skl_int3472_tps68470 intel_pmc_core tps68470_regulator video clk_tps68470 pmt_telemetry pmt_discovery nls_iso8859_1 pmt_class intel_pmc_ssram_telemetry intel_skl_int3472_discrete int3400_thermal intel_hid intel_skl_int3472_common acpi_thermal_rel intel_vsec wmi pinctrl_tigerlake acpi_tad sparse_keymap acpi_pad dm_multipath msr nvme_fabrics fuse efi_pstore nfnetlink autofs4 [117.004782] CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Tainted: G S U 6.17.0-rc7-CI_DRM_17270-g7644974e648c+ #1 PREEMPT(voluntary) [117.004787] Tainted: [S]=CPU_OUT_OF_SPEC, [U]=USER [117.004789] Hardware name: Intel Corporation Alder Lake Client Platform/AlderLake-P DDR4 RVP, BIOS RPLPFWI1.R00.4035.A00.2301200723 01/20/2023 [117.004793] RIP: 0010:__timer_delete_sync+0x126/0x190 [117.004795] Code: 31 c0 45 31 c9 c3 cc cc cc cc 48 8b 75 d0 45 84 f6 74 63 49 c7 45 18 00 00 00 00 48 89 c7 e8 51 46 39 01 f3 90 e9 66 ff ff ff <0f> 0b e9 5f ff ff ff e8 ee e4 0c 00 49 8d 5d 28 45 31 c9 31 c9 4c [117.004801] RSP: 0018:ffffc90000003a40 EFLAGS: 00010046 [117.004804] RAX: ffffffff815093fb RBX: ffff888138f86aa8 RCX: 0000000000000000 [117.004807] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [117.004809] RBP: ffffc90000003a70 R08: 0000000000000000 R09: 0000000000000000 [117.004812] R10: 0000000000000000 R11: 0000000000000000 R12: ffffffff815093fb [117.004814] R13: ffff888138f86a80 R14: 0000000000000000 R15: 0000000000000000 [117.004817] FS: 0000000000000000(0000) GS:ffff88890b0f7000(0000) knlGS:0000000000000000 [117.004820] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [117.004823] CR2: 00005db8131eb7f0 CR3: 0000000003448000 CR4: 0000000000f52ef0 [117.004826] PKRU: 55555554 [117.004827] Call Trace: [117.004829] [117.004831] timer_delete_sync+0x10/0x20 [117.004833] vgem_fence_release+0x19/0x30 [vgem] [117.004836] dma_fence_release+0xc1/0x3b0 [117.004838] ? dma_fence_release+0xa1/0x3b0 [117.004841] dma_fence_chain_release+0xe7/0x130 [117.004844] dma_fence_release+0xc1/0x3b0 [117.004847] ? _raw_spin_unlock_irqrestore+0x27/0x80 [117.004850] dma_fence_chain_irq_work+0x59/0x80 [117.004853] irq_work_single+0x75/0xa0 [117.004857] irq_work_run_list+0x33/0x60 [117.004860] irq_work_run+0x18/0x40 [117.004863] __sysvec_irq_work+0x35/0x170 [117.004865] sysvec_irq_work+0x47/0xc0 [117.004868] asm_sysvec_irq_work+0x1b/0x20 [117.004871] RIP: 0010:_raw_spin_unlock_irqrestore+0x57/0x80 [117.004874] Code: 00 75 1c 65 ff 0d d9 34 68 01 74 20 5b 41 5c 5d 31 c0 31 d2 31 c9 31 f6 31 ff c3 cc cc cc cc e8 7f 9d d3 fe fb 0f 1f 44 00 00 d7 0f 1f 44 00 00 5b 41 5c 5d 31 c0 31 d2 31 c9 31 f6 31 ff c3 [117.004879] RSP: 0018:ffffc90000003cf0 EFLAGS: 00000246 [117.004882] RAX: 0000000000000000 RBX: ffff888155e94c40 RCX: 0000000000000000 [117.004884] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [117.004887] RBP: ffffc90000003d00 R08: 0000000000000000 R09: 0000000000000000 [117.004890] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000246 [117.004892] R13: 0000000000000001 R14: 0000000000000246 R15: ffff888155e94c80 [117.004897] dma_fence_signal+0x49/0xb0 [117.004899] ? __pfx_vgem_fence_timeout+0x10/0x10 [vgem] [117.004902] vgem_fence_timeout+0x12/0x20 [vgem] [117.004904] call_timer_fn+0xa1/0x2a0 [117.004908] ? __pfx_vgem_fence_timeout+0x10/0x10 [vgem] [117.004910] __run_timers+0x231/0x310 [117.004913] ? tmigr_handle_remote+0x2ac/0x560 [117.004917] timer_expire_remote+0x46/0x70 [117.004919] tmigr_handle_remote+0x433/0x560 [117.004923] ? __run_timers+0x239/0x310 [117.004925] ? run_timer_softirq+0x21/0xe0 [117.004928] ? lock_release+0xce/0x2a0 [117.004931] run_timer_softirq+0xcf/0xe0 [117.004933] handle_softirqs+0xd4/0x4d0 [117.004936] __irq_exit_rcu+0x13f/0x160 [117.004938] irq_exit_rcu+0xe/0x20 [117.004940] sysvec_apic_timer_interrupt+0xa0/0xc0 [117.004943] [117.004944] [117.004946] asm_sysvec_apic_timer_interrupt+0x1b/0x20 [117.004949] RIP: 0010:cpuidle_enter_state+0x12b/0x8a0 [117.004953] Code: 48 0f a3 05 97 ce 0e 01 0f 82 2e 03 00 00 31 ff e8 8a 41 bd fe 80 7d d0 00 0f 85 11 03 00 00 e8 8b 06 d5 fe fb 0f 1f 44 00 00 <45> 85 f6 0f 88 67 02 00 00 4d 63 ee 49 83 fd 0a 0f 83 34 06 00 00 [117.004961] RSP: 0018:ffffffff83403d88 EFLAGS: 00000246 [117.004963] RAX: 0000000000000000 RBX: ffff88888f046440 RCX: 0000000000000000 [117.004966] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [117.004968] RBP: ffffffff83403dd8 R08: 0000000000000000 R09: 0000000000000000 [117.004971] R10: 0000000000000000 R11: 0000000000000000 R12: ffffffff837cbe80 [117.004974] R13: 0000000000000004 R14: 0000000000000004 R15: 0000001ad1df466b [117.004978] ? cpuidle_enter_state+0x125/0x8a0 [117.004981] ? sched_clock_noinstr+0x9/0x10 [117.004985] cpuidle_enter+0x2e/0x50 [117.004989] call_cpuidle+0x22/0x60 [117.004991] do_idle+0x1fd/0x260 [117.005001] cpu_startup_entry+0x29/0x30 [117.005004] rest_init+0x104/0x200 [117.005008] start_kernel+0x93d/0xbd0 [117.005011] ? load_ucode_intel_bsp+0x2a/0x90 [117.005014] ? sme_unmap_bootdata+0x14/0x80 [117.005017] x86_64_start_reservations+0x18/0x30 [117.005020] x86_64_start_kernel+0xfd/0x150 [117.005023] ? soft_restart_cpu+0x14/0x14 [117.005026] common_startup_64+0x13e/0x141 [117.005030] [117.005032] irq event stamp: 2282669 [117.005034] hardirqs last enabled at (2282668): [] _raw_spin_unlock_irqrestore+0x51/0x80 [117.005038] hardirqs last disabled at (2282669): [] sysvec_irq_work+0x11/0xc0 [117.005043] softirqs last enabled at (2254702): [] __do_softirq+0x10/0x18 [117.005047] softirqs last disabled at (2254725): [] __irq_exit_rcu+0x13f/0x160 [117.005051] ---[ end trace 0000000000000000 ]--- Make the timer IRQ safe. [1] https://patchwork.freedesktop.org/series/154987/#rev2 Fixes: 4077798484459 ("drm/vgem: Attach sw fences to exported vGEM dma-buf (ioctl)") Signed-off-by: Janusz Krzysztofik Reviewed-by: Christian König Link: https://lore.kernel.org/r/20250926152628.2165080-2-janusz.krzysztofik@linux.intel.com Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/vgem/vgem_fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vgem/vgem_fence.c b/drivers/gpu/drm/vgem/vgem_fence.c index fd76730fd38c..07db319c3d7f 100644 --- a/drivers/gpu/drm/vgem/vgem_fence.c +++ b/drivers/gpu/drm/vgem/vgem_fence.c @@ -79,7 +79,7 @@ static struct dma_fence *vgem_fence_create(struct vgem_file *vfile, dma_fence_init(&fence->base, &vgem_fence_ops, &fence->lock, dma_fence_context_alloc(1), 1); - timer_setup(&fence->timer, vgem_fence_timeout, 0); + timer_setup(&fence->timer, vgem_fence_timeout, TIMER_IRQSAFE); /* We force the fence to expire within 10s to prevent driver hangs */ mod_timer(&fence->timer, jiffies + VGEM_FENCE_TIMEOUT); From 507211e3c7a110c3f48cd8c731ddc748fe215ec8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 28 Jul 2025 10:21:23 +0200 Subject: [PATCH 048/128] Documentation: dma-buf: heaps: Add naming guidelines We've discussed a number of times of how some heap names are bad, but not really what makes a good heap name. Let's document what we expect the heap names to look like. Reviewed-by: Andrew Davis Reviewed-by: Bagas Sanjaya Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/all/20250728-dma-buf-heap-names-doc-v4-1-f73f71cf0dfd@kernel.org/ Signed-off-by: Sumit Semwal --- Documentation/userspace-api/dma-buf-heaps.rst | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/userspace-api/dma-buf-heaps.rst b/Documentation/userspace-api/dma-buf-heaps.rst index 1dfe5e7acd5a..a0979440d2a4 100644 --- a/Documentation/userspace-api/dma-buf-heaps.rst +++ b/Documentation/userspace-api/dma-buf-heaps.rst @@ -26,3 +26,37 @@ following heaps: ``DMABUF_HEAPS_CMA_LEGACY`` Kconfig option is set, a duplicate node is created following legacy naming conventions; the legacy name might be ``reserved``, ``linux,cma``, or ``default-pool``. +Naming Convention +================= + +``dma-buf`` heaps name should meet a number of constraints: + +- The name must be stable, and must not change from one version to the other. + Userspace identifies heaps by their name, so if the names ever change, we + would be likely to introduce regressions. + +- The name must describe the memory region the heap will allocate from, and + must uniquely identify it in a given platform. Since userspace applications + use the heap name as the discriminant, it must be able to tell which heap it + wants to use reliably if there's multiple heaps. + +- The name must not mention implementation details, such as the allocator. The + heap driver will change over time, and implementation details when it was + introduced might not be relevant in the future. + +- The name should describe properties of the buffers that would be allocated. + Doing so will make heap identification easier for userspace. Such properties + are: + + - ``contiguous`` for physically contiguous buffers; + + - ``protected`` for encrypted buffers not accessible the OS; + +- The name may describe intended usage. Doing so will make heap identification + easier for userspace applications and users. + +For example, assuming a platform with a reserved memory region located +at the RAM address 0x42000000, intended to allocate video framebuffers, +physically contiguous, and backed by the CMA kernel allocator, good +names would be ``memory@42000000-contiguous`` or ``video@42000000``, but +``cma-video`` wouldn't. From 9c815230630cb4e80e78ed3ef40909a71fc92403 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 17 Sep 2025 14:48:04 +0200 Subject: [PATCH 049/128] accel/qaic: Replace kzalloc + copy_from_user with memdup_user Replace kzalloc() followed by copy_from_user() with memdup_user() to improve and simplify qaic_attach_slice_bo_ioctl(). No functional changes intended. Signed-off-by: Thorsten Blum Reviewed-by: Karol Wachowski Reviewed-by: Jeff Hugo Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20250917124805.90395-2-thorsten.blum@linux.dev --- drivers/accel/qaic/qaic_data.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 797289e9d780..202bdca58847 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -984,18 +985,12 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi user_data = u64_to_user_ptr(args->data); - slice_ent = kzalloc(arg_size, GFP_KERNEL); - if (!slice_ent) { - ret = -EINVAL; + slice_ent = memdup_user(user_data, arg_size); + if (IS_ERR(slice_ent)) { + ret = PTR_ERR(slice_ent); goto unlock_dev_srcu; } - ret = copy_from_user(slice_ent, user_data, arg_size); - if (ret) { - ret = -EFAULT; - goto free_slice_ent; - } - obj = drm_gem_object_lookup(file_priv, args->hdr.handle); if (!obj) { ret = -ENOENT; From bed1291240e19782873120c59c305e24d94407a1 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 17 Sep 2025 14:48:06 +0200 Subject: [PATCH 050/128] accel/qaic: Replace kcalloc + copy_from_user with memdup_array_user Replace kcalloc() followed by copy_from_user() with memdup_array_user() to improve and simplify both __qaic_execute_bo_ioctl() and qaic_perf_stats_bo_ioctl(). In __qaic_execute_bo_ioctl(), return early if an error occurs and remove the obsolete 'free_exec' label. Since memdup_array_user() already checks for multiplication overflow, remove the manual check in __qaic_execute_bo_ioctl(). Remove any unused local variables accordingly. Since 'ret = copy_from_user()' has been removed, initialize 'ret = 0' to preserve the same return value on success. No functional changes intended. Signed-off-by: Thorsten Blum Reviewed-by: Jeff Hugo Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20250917124805.90395-4-thorsten.blum@linux.dev --- drivers/accel/qaic/qaic_data.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 202bdca58847..adabc4028bb2 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -1295,8 +1295,6 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr int usr_rcu_id, qdev_rcu_id; struct qaic_device *qdev; struct qaic_user *usr; - u8 __user *user_data; - unsigned long n; u64 received_ts; u32 queue_level; u64 submit_ts; @@ -1309,20 +1307,12 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr received_ts = ktime_get_ns(); size = is_partial ? sizeof(struct qaic_partial_execute_entry) : sizeof(*exec); - n = (unsigned long)size * args->hdr.count; - if (args->hdr.count == 0 || n / args->hdr.count != size) + if (args->hdr.count == 0) return -EINVAL; - user_data = u64_to_user_ptr(args->data); - - exec = kcalloc(args->hdr.count, size, GFP_KERNEL); - if (!exec) - return -ENOMEM; - - if (copy_from_user(exec, user_data, n)) { - ret = -EFAULT; - goto free_exec; - } + exec = memdup_array_user(u64_to_user_ptr(args->data), args->hdr.count, size); + if (IS_ERR(exec)) + return PTR_ERR(exec); usr = file_priv->driver_priv; usr_rcu_id = srcu_read_lock(&usr->qddev_lock); @@ -1383,7 +1373,6 @@ unlock_dev_srcu: srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id); unlock_usr_srcu: srcu_read_unlock(&usr->qddev_lock, usr_rcu_id); -free_exec: kfree(exec); return ret; } @@ -1736,7 +1725,8 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file struct qaic_device *qdev; struct qaic_user *usr; struct qaic_bo *bo; - int ret, i; + int ret = 0; + int i; usr = file_priv->driver_priv; usr_rcu_id = srcu_read_lock(&usr->qddev_lock); @@ -1757,18 +1747,12 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file goto unlock_dev_srcu; } - ent = kcalloc(args->hdr.count, sizeof(*ent), GFP_KERNEL); - if (!ent) { - ret = -EINVAL; + ent = memdup_array_user(u64_to_user_ptr(args->data), args->hdr.count, sizeof(*ent)); + if (IS_ERR(ent)) { + ret = PTR_ERR(ent); goto unlock_dev_srcu; } - ret = copy_from_user(ent, u64_to_user_ptr(args->data), args->hdr.count * sizeof(*ent)); - if (ret) { - ret = -EFAULT; - goto free_ent; - } - for (i = 0; i < args->hdr.count; i++) { obj = drm_gem_object_lookup(file_priv, ent[i].handle); if (!obj) { From e68c994445e37322eac1f255489eb4564935969a Mon Sep 17 00:00:00 2001 From: Chelsy Ratnawat Date: Fri, 22 Aug 2025 04:28:04 -0700 Subject: [PATCH 051/128] accel/qaic: Replace snprintf() with sysfs_emit() in sysfs show functions Documentation/filesystems/sysfs.rst mentions that show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. So replace snprintf() with sysfs_emit(). Signed-off-by: Chelsy Ratnawat Reviewed-by: Jeff Hugo [jhugo: Fix commit text typos] Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20250822112804.1726592-1-chelsyratnawat2001@gmail.com --- drivers/accel/qaic/qaic_ras.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/accel/qaic/qaic_ras.c b/drivers/accel/qaic/qaic_ras.c index 914ffc4a9970..f1d52a710136 100644 --- a/drivers/accel/qaic/qaic_ras.c +++ b/drivers/accel/qaic/qaic_ras.c @@ -514,21 +514,21 @@ static ssize_t ce_count_show(struct device *dev, struct device_attribute *attr, { struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev)); - return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ce_count); + return sysfs_emit(buf, "%d\n", qdev->ce_count); } static ssize_t ue_count_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev)); - return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ue_count); + return sysfs_emit(buf, "%d\n", qdev->ue_count); } static ssize_t ue_nonfatal_count_show(struct device *dev, struct device_attribute *attr, char *buf) { struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev)); - return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ue_nf_count); + return sysfs_emit(buf, "%d\n", qdev->ue_nf_count); } static DEVICE_ATTR_RO(ce_count); From c178e534fff1d5a74da80ea03b20e2b948a00113 Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Mon, 6 Oct 2025 15:21:22 +0530 Subject: [PATCH 052/128] drm/buddy: Optimize free block management with RB tree Replace the freelist (O(n)) used for free block management with a red-black tree, providing more efficient O(log n) search, insert, and delete operations. This improves scalability and performance when managing large numbers of free blocks per order (e.g., hundreds or thousands). In the VK-CTS memory stress subtest, the buddy manager merges fragmented memory and inserts freed blocks into the freelist. Since freelist insertion is O(n), this becomes a bottleneck as fragmentation increases. Benchmarking shows list_insert_sorted() consumes ~52.69% CPU with the freelist, compared to just 0.03% with the RB tree (rbtree_insert.isra.0), despite performing the same sorted insert. This also improves performance in heavily fragmented workloads, such as games or graphics tests that stress memory. As the buddy allocator evolves with new features such as clear-page tracking, the resulting fragmentation and complexity have grown. These RB-tree based design changes are introduced to address that growth and ensure the allocator continues to perform efficiently under fragmented conditions. The RB tree implementation with separate clear/dirty trees provides: - O(n log n) aggregate complexity for all operations instead of O(n^2) - Elimination of soft lockups and system instability - Improved code maintainability and clarity - Better scalability for large memory systems - Predictable performance under fragmentation v3(Matthew): - Remove RB_EMPTY_NODE check in force_merge function. - Rename rb for loop macros to have less generic names and move to .c file. - Make the rb node rb and link field as union. v4(Jani Nikula): - The kernel-doc comment should be "/**" - Move all the rbtree macros to rbtree.h and add parens to ensure correct precedence. v5: - Remove the inline in a .c file (Jani Nikula). v6(Peter Zijlstra): - Add rb_add() function replacing the existing rbtree_insert() code. v7: - A full walk iteration in rbtree is slower than the list (Peter Zijlstra). - The existing rbtree_postorder_for_each_entry_safe macro should be used in scenarios where traversal order is not a critical factor (Christian). v8(Matthew): - Remove the rbtree_is_empty() check in this patch as well. Cc: stable@vger.kernel.org Fixes: a68c7eaa7a8f ("drm/amdgpu: Enable clear page functionality") Signed-off-by: Arunpravin Paneer Selvam Reviewed-by: Matthew Auld Link: https://lore.kernel.org/r/20251006095124.1663-1-Arunpravin.PaneerSelvam@amd.com --- drivers/gpu/drm/drm_buddy.c | 195 ++++++++++++++++++++++-------------- include/drm/drm_buddy.h | 11 +- 2 files changed, 126 insertions(+), 80 deletions(-) diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index a94061f373de..c87210a06c31 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -14,6 +14,8 @@ static struct kmem_cache *slab_blocks; +#define rbtree_get_free_block(node) rb_entry((node), struct drm_buddy_block, rb) + static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm, struct drm_buddy_block *parent, unsigned int order, @@ -31,6 +33,8 @@ static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm, block->header |= order; block->parent = parent; + RB_CLEAR_NODE(&block->rb); + BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED); return block; } @@ -41,23 +45,49 @@ static void drm_block_free(struct drm_buddy *mm, kmem_cache_free(slab_blocks, block); } -static void list_insert_sorted(struct drm_buddy *mm, - struct drm_buddy_block *block) +static bool drm_buddy_block_offset_less(const struct drm_buddy_block *block, + const struct drm_buddy_block *node) { - struct drm_buddy_block *node; - struct list_head *head; + return drm_buddy_block_offset(block) < drm_buddy_block_offset(node); +} - head = &mm->free_list[drm_buddy_block_order(block)]; - if (list_empty(head)) { - list_add(&block->link, head); - return; - } +static bool rbtree_block_offset_less(struct rb_node *block, + const struct rb_node *node) +{ + return drm_buddy_block_offset_less(rbtree_get_free_block(block), + rbtree_get_free_block(node)); +} - list_for_each_entry(node, head, link) - if (drm_buddy_block_offset(block) < drm_buddy_block_offset(node)) - break; +static void rbtree_insert(struct drm_buddy *mm, + struct drm_buddy_block *block) +{ + rb_add(&block->rb, + &mm->free_tree[drm_buddy_block_order(block)], + rbtree_block_offset_less); +} - __list_add(&block->link, node->link.prev, &node->link); +static void rbtree_remove(struct drm_buddy *mm, + struct drm_buddy_block *block) +{ + struct rb_root *root; + + root = &mm->free_tree[drm_buddy_block_order(block)]; + rb_erase(&block->rb, root); + + RB_CLEAR_NODE(&block->rb); +} + +static struct drm_buddy_block * +rbtree_last_entry(struct drm_buddy *mm, unsigned int order) +{ + struct rb_node *node = rb_last(&mm->free_tree[order]); + + return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL; +} + +static bool rbtree_is_empty(struct drm_buddy *mm, unsigned int order) +{ + return RB_EMPTY_ROOT(&mm->free_tree[order]); } static void clear_reset(struct drm_buddy_block *block) @@ -70,12 +100,13 @@ static void mark_cleared(struct drm_buddy_block *block) block->header |= DRM_BUDDY_HEADER_CLEAR; } -static void mark_allocated(struct drm_buddy_block *block) +static void mark_allocated(struct drm_buddy *mm, + struct drm_buddy_block *block) { block->header &= ~DRM_BUDDY_HEADER_STATE; block->header |= DRM_BUDDY_ALLOCATED; - list_del(&block->link); + rbtree_remove(mm, block); } static void mark_free(struct drm_buddy *mm, @@ -84,15 +115,16 @@ static void mark_free(struct drm_buddy *mm, block->header &= ~DRM_BUDDY_HEADER_STATE; block->header |= DRM_BUDDY_FREE; - list_insert_sorted(mm, block); + rbtree_insert(mm, block); } -static void mark_split(struct drm_buddy_block *block) +static void mark_split(struct drm_buddy *mm, + struct drm_buddy_block *block) { block->header &= ~DRM_BUDDY_HEADER_STATE; block->header |= DRM_BUDDY_SPLIT; - list_del(&block->link); + rbtree_remove(mm, block); } static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2) @@ -148,7 +180,7 @@ static unsigned int __drm_buddy_free(struct drm_buddy *mm, mark_cleared(parent); } - list_del(&buddy->link); + rbtree_remove(mm, buddy); if (force_merge && drm_buddy_block_is_clear(buddy)) mm->clear_avail -= drm_buddy_block_size(mm, buddy); @@ -179,13 +211,19 @@ static int __force_merge(struct drm_buddy *mm, return -EINVAL; for (i = min_order - 1; i >= 0; i--) { - struct drm_buddy_block *block, *prev; + struct rb_root *root = &mm->free_tree[i]; + struct rb_node *iter; - list_for_each_entry_safe_reverse(block, prev, &mm->free_list[i], link) { - struct drm_buddy_block *buddy; + iter = rb_last(root); + + while (iter) { + struct drm_buddy_block *block, *buddy; u64 block_start, block_end; - if (!block->parent) + block = rbtree_get_free_block(iter); + iter = rb_prev(iter); + + if (!block || !block->parent) continue; block_start = drm_buddy_block_offset(block); @@ -201,15 +239,10 @@ static int __force_merge(struct drm_buddy *mm, WARN_ON(drm_buddy_block_is_clear(block) == drm_buddy_block_is_clear(buddy)); - /* - * If the prev block is same as buddy, don't access the - * block in the next iteration as we would free the - * buddy block as part of the free function. - */ - if (prev == buddy) - prev = list_prev_entry(prev, link); + if (iter == &buddy->rb) + iter = rb_prev(iter); - list_del(&block->link); + rbtree_remove(mm, block); if (drm_buddy_block_is_clear(block)) mm->clear_avail -= drm_buddy_block_size(mm, block); @@ -237,7 +270,7 @@ static int __force_merge(struct drm_buddy *mm, int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) { unsigned int i; - u64 offset; + u64 offset = 0; if (size < chunk_size) return -EINVAL; @@ -258,14 +291,14 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER); - mm->free_list = kmalloc_array(mm->max_order + 1, - sizeof(struct list_head), + mm->free_tree = kmalloc_array(mm->max_order + 1, + sizeof(struct rb_root), GFP_KERNEL); - if (!mm->free_list) + if (!mm->free_tree) return -ENOMEM; for (i = 0; i <= mm->max_order; ++i) - INIT_LIST_HEAD(&mm->free_list[i]); + mm->free_tree[i] = RB_ROOT; mm->n_roots = hweight64(size); @@ -273,9 +306,8 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) sizeof(struct drm_buddy_block *), GFP_KERNEL); if (!mm->roots) - goto out_free_list; + goto out_free_tree; - offset = 0; i = 0; /* @@ -312,8 +344,8 @@ out_free_roots: while (i--) drm_block_free(mm, mm->roots[i]); kfree(mm->roots); -out_free_list: - kfree(mm->free_list); +out_free_tree: + kfree(mm->free_tree); return -ENOMEM; } EXPORT_SYMBOL(drm_buddy_init); @@ -323,7 +355,7 @@ EXPORT_SYMBOL(drm_buddy_init); * * @mm: DRM buddy manager to free * - * Cleanup memory manager resources and the freelist + * Cleanup memory manager resources and the freetree */ void drm_buddy_fini(struct drm_buddy *mm) { @@ -350,7 +382,7 @@ void drm_buddy_fini(struct drm_buddy *mm) WARN_ON(mm->avail != mm->size); kfree(mm->roots); - kfree(mm->free_list); + kfree(mm->free_tree); } EXPORT_SYMBOL(drm_buddy_fini); @@ -383,7 +415,7 @@ static int split_block(struct drm_buddy *mm, clear_reset(block); } - mark_split(block); + mark_split(mm, block); return 0; } @@ -412,7 +444,7 @@ EXPORT_SYMBOL(drm_get_buddy); * @is_clear: blocks clear state * * Reset the clear state based on @is_clear value for each block - * in the freelist. + * in the freetree. */ void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear) { @@ -431,9 +463,9 @@ void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear) } for (i = 0; i <= mm->max_order; ++i) { - struct drm_buddy_block *block; + struct drm_buddy_block *block, *tmp; - list_for_each_entry_reverse(block, &mm->free_list[i], link) { + rbtree_postorder_for_each_entry_safe(block, tmp, &mm->free_tree[i], rb) { if (is_clear != drm_buddy_block_is_clear(block)) { if (is_clear) { mark_cleared(block); @@ -639,14 +671,18 @@ get_maxblock(struct drm_buddy *mm, unsigned int order, unsigned int i; for (i = order; i <= mm->max_order; ++i) { + struct rb_node *iter = rb_last(&mm->free_tree[i]); struct drm_buddy_block *tmp_block; - list_for_each_entry_reverse(tmp_block, &mm->free_list[i], link) { - if (block_incompatible(tmp_block, flags)) - continue; + while (iter) { + tmp_block = rbtree_get_free_block(iter); - block = tmp_block; - break; + if (!block_incompatible(tmp_block, flags)) { + block = tmp_block; + break; + } + + iter = rb_prev(iter); } if (!block) @@ -667,7 +703,7 @@ get_maxblock(struct drm_buddy *mm, unsigned int order, } static struct drm_buddy_block * -alloc_from_freelist(struct drm_buddy *mm, +alloc_from_freetree(struct drm_buddy *mm, unsigned int order, unsigned long flags) { @@ -682,14 +718,18 @@ alloc_from_freelist(struct drm_buddy *mm, tmp = drm_buddy_block_order(block); } else { for (tmp = order; tmp <= mm->max_order; ++tmp) { + struct rb_node *iter = rb_last(&mm->free_tree[tmp]); struct drm_buddy_block *tmp_block; - list_for_each_entry_reverse(tmp_block, &mm->free_list[tmp], link) { - if (block_incompatible(tmp_block, flags)) - continue; + while (iter) { + tmp_block = rbtree_get_free_block(iter); - block = tmp_block; - break; + if (!block_incompatible(tmp_block, flags)) { + block = tmp_block; + break; + } + + iter = rb_prev(iter); } if (block) @@ -700,13 +740,9 @@ alloc_from_freelist(struct drm_buddy *mm, if (!block) { /* Fallback method */ for (tmp = order; tmp <= mm->max_order; ++tmp) { - if (!list_empty(&mm->free_list[tmp])) { - block = list_last_entry(&mm->free_list[tmp], - struct drm_buddy_block, - link); - if (block) - break; - } + block = rbtree_last_entry(mm, tmp); + if (block) + break; } if (!block) @@ -771,7 +807,7 @@ static int __alloc_range(struct drm_buddy *mm, if (contains(start, end, block_start, block_end)) { if (drm_buddy_block_is_free(block)) { - mark_allocated(block); + mark_allocated(mm, block); total_allocated += drm_buddy_block_size(mm, block); mm->avail -= drm_buddy_block_size(mm, block); if (drm_buddy_block_is_clear(block)) @@ -849,8 +885,8 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm, { u64 rhs_offset, lhs_offset, lhs_size, filled; struct drm_buddy_block *block; - struct list_head *list; LIST_HEAD(blocks_lhs); + struct rb_node *iter; unsigned long pages; unsigned int order; u64 modify_size; @@ -862,11 +898,14 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm, if (order == 0) return -ENOSPC; - list = &mm->free_list[order]; - if (list_empty(list)) + if (rbtree_is_empty(mm, order)) return -ENOSPC; - list_for_each_entry_reverse(block, list, link) { + iter = rb_last(&mm->free_tree[order]); + + while (iter) { + block = rbtree_get_free_block(iter); + /* Allocate blocks traversing RHS */ rhs_offset = drm_buddy_block_offset(block); err = __drm_buddy_alloc_range(mm, rhs_offset, size, @@ -891,6 +930,8 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm, } /* Free blocks for the next iteration */ drm_buddy_free_list_internal(mm, blocks); + + iter = rb_prev(iter); } return -ENOSPC; @@ -976,7 +1017,7 @@ int drm_buddy_block_trim(struct drm_buddy *mm, list_add(&block->tmp_link, &dfs); err = __alloc_range(mm, &dfs, new_start, new_size, blocks, NULL); if (err) { - mark_allocated(block); + mark_allocated(mm, block); mm->avail -= drm_buddy_block_size(mm, block); if (drm_buddy_block_is_clear(block)) mm->clear_avail -= drm_buddy_block_size(mm, block); @@ -999,8 +1040,8 @@ __drm_buddy_alloc_blocks(struct drm_buddy *mm, return __drm_buddy_alloc_range_bias(mm, start, end, order, flags); else - /* Allocate from freelist */ - return alloc_from_freelist(mm, order, flags); + /* Allocate from freetree */ + return alloc_from_freetree(mm, order, flags); } /** @@ -1017,8 +1058,8 @@ __drm_buddy_alloc_blocks(struct drm_buddy *mm, * alloc_range_bias() called on range limitations, which traverses * the tree and returns the desired block. * - * alloc_from_freelist() called when *no* range restrictions - * are enforced, which picks the block from the freelist. + * alloc_from_freetree() called when *no* range restrictions + * are enforced, which picks the block from the freetree. * * Returns: * 0 on success, error code on failure. @@ -1120,7 +1161,7 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm, } } while (1); - mark_allocated(block); + mark_allocated(mm, block); mm->avail -= drm_buddy_block_size(mm, block); if (drm_buddy_block_is_clear(block)) mm->clear_avail -= drm_buddy_block_size(mm, block); @@ -1201,10 +1242,10 @@ void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - struct drm_buddy_block *block; + struct drm_buddy_block *block, *tmp; u64 count = 0, free; - list_for_each_entry(block, &mm->free_list[order], link) { + rbtree_postorder_for_each_entry_safe(block, tmp, &mm->free_tree[order], rb) { BUG_ON(!drm_buddy_block_is_free(block)); count++; } diff --git a/include/drm/drm_buddy.h b/include/drm/drm_buddy.h index 513837632b7d..9ee105d4309f 100644 --- a/include/drm/drm_buddy.h +++ b/include/drm/drm_buddy.h @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -53,7 +54,11 @@ struct drm_buddy_block { * a list, if so desired. As soon as the block is freed with * drm_buddy_free* ownership is given back to the mm. */ - struct list_head link; + union { + struct rb_node rb; + struct list_head link; + }; + struct list_head tmp_link; }; @@ -68,7 +73,7 @@ struct drm_buddy_block { */ struct drm_buddy { /* Maintain a free list for each order. */ - struct list_head *free_list; + struct rb_root *free_tree; /* * Maintain explicit binary tree(s) to track the allocation of the @@ -94,7 +99,7 @@ struct drm_buddy { }; static inline u64 -drm_buddy_block_offset(struct drm_buddy_block *block) +drm_buddy_block_offset(const struct drm_buddy_block *block) { return block->header & DRM_BUDDY_HEADER_OFFSET; } From d4cd665c98c144dd6ad5d66d30396e13d23118c9 Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Mon, 6 Oct 2025 15:21:23 +0530 Subject: [PATCH 053/128] drm/buddy: Separate clear and dirty free block trees Maintain two separate RB trees per order - one for clear (zeroed) blocks and another for dirty (uncleared) blocks. This separation improves code clarity and makes it more obvious which tree is being searched during allocation. It also improves scalability and efficiency when searching for a specific type of block, avoiding unnecessary checks and making the allocator more predictable under fragmentation. The changes have been validated using the existing drm_buddy_test KUnit test cases, along with selected graphics workloads, to ensure correctness and avoid regressions. v2: Missed adding the suggested-by tag. Added it in v2. v3(Matthew): - Remove the double underscores from the internal functions. - Rename the internal functions to have less generic names. - Fix the error handling code. - Pass tree argument for the tree macro. - Use the existing dirty/free bit instead of new tree field. - Make free_trees[] instead of clear_tree and dirty_tree for more cleaner approach. v4: - A bug was reported by Intel CI and it is fixed by Matthew Auld. - Replace the get_root function with &mm->free_trees[tree][order] (Matthew) - Remove the unnecessary rbtree_is_empty() check (Matthew) - Remove the unnecessary get_tree_for_flags() function. - Rename get_tree_for_block() name with get_block_tree() for more clarity. v5(Jani Nikula): - Don't use static inline in .c files. - enum free_tree and enumerator names are quite generic for a header and usage and the whole enum should be an implementation detail. v6: - Rewrite the __force_merge() function using the rb_last() and rb_prev(). v7(Matthew): - Replace the open-coded tree iteration for loops with the for_each_free_tree() macro throughout the code. - Fixed out_free_roots to prevent double decrement of i, addressing potential crash. - Replaced enum drm_buddy_free_tree with unsigned int in for_each_free_tree loops. Cc: stable@vger.kernel.org Fixes: a68c7eaa7a8f ("drm/amdgpu: Enable clear page functionality") Signed-off-by: Arunpravin Paneer Selvam Suggested-by: Matthew Auld Reviewed-by: Matthew Auld Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4260 Link: https://lore.kernel.org/r/20251006095124.1663-2-Arunpravin.PaneerSelvam@amd.com --- drivers/gpu/drm/drm_buddy.c | 335 ++++++++++++++++++++---------------- include/drm/drm_buddy.h | 2 +- 2 files changed, 189 insertions(+), 148 deletions(-) diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index c87210a06c31..f2c92902e4a3 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -12,9 +12,16 @@ #include +enum drm_buddy_free_tree { + DRM_BUDDY_CLEAR_TREE = 0, + DRM_BUDDY_DIRTY_TREE, + DRM_BUDDY_MAX_FREE_TREES, +}; + static struct kmem_cache *slab_blocks; -#define rbtree_get_free_block(node) rb_entry((node), struct drm_buddy_block, rb) +#define for_each_free_tree(tree) \ + for ((tree) = 0; (tree) < DRM_BUDDY_MAX_FREE_TREES; (tree)++) static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm, struct drm_buddy_block *parent, @@ -45,6 +52,30 @@ static void drm_block_free(struct drm_buddy *mm, kmem_cache_free(slab_blocks, block); } +static enum drm_buddy_free_tree +get_block_tree(struct drm_buddy_block *block) +{ + return drm_buddy_block_is_clear(block) ? + DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE; +} + +static struct drm_buddy_block * +rbtree_get_free_block(const struct rb_node *node) +{ + return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL; +} + +static struct drm_buddy_block * +rbtree_last_free_block(struct rb_root *root) +{ + return rbtree_get_free_block(rb_last(root)); +} + +static bool rbtree_is_empty(struct rb_root *root) +{ + return RB_EMPTY_ROOT(root); +} + static bool drm_buddy_block_offset_less(const struct drm_buddy_block *block, const struct drm_buddy_block *node) { @@ -59,37 +90,28 @@ static bool rbtree_block_offset_less(struct rb_node *block, } static void rbtree_insert(struct drm_buddy *mm, - struct drm_buddy_block *block) + struct drm_buddy_block *block, + enum drm_buddy_free_tree tree) { rb_add(&block->rb, - &mm->free_tree[drm_buddy_block_order(block)], + &mm->free_trees[tree][drm_buddy_block_order(block)], rbtree_block_offset_less); } static void rbtree_remove(struct drm_buddy *mm, struct drm_buddy_block *block) { + unsigned int order = drm_buddy_block_order(block); + enum drm_buddy_free_tree tree; struct rb_root *root; - root = &mm->free_tree[drm_buddy_block_order(block)]; + tree = get_block_tree(block); + root = &mm->free_trees[tree][order]; + rb_erase(&block->rb, root); - RB_CLEAR_NODE(&block->rb); } -static struct drm_buddy_block * -rbtree_last_entry(struct drm_buddy *mm, unsigned int order) -{ - struct rb_node *node = rb_last(&mm->free_tree[order]); - - return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL; -} - -static bool rbtree_is_empty(struct drm_buddy *mm, unsigned int order) -{ - return RB_EMPTY_ROOT(&mm->free_tree[order]); -} - static void clear_reset(struct drm_buddy_block *block) { block->header &= ~DRM_BUDDY_HEADER_CLEAR; @@ -112,10 +134,13 @@ static void mark_allocated(struct drm_buddy *mm, static void mark_free(struct drm_buddy *mm, struct drm_buddy_block *block) { + enum drm_buddy_free_tree tree; + block->header &= ~DRM_BUDDY_HEADER_STATE; block->header |= DRM_BUDDY_FREE; - rbtree_insert(mm, block); + tree = get_block_tree(block); + rbtree_insert(mm, block, tree); } static void mark_split(struct drm_buddy *mm, @@ -201,7 +226,7 @@ static int __force_merge(struct drm_buddy *mm, u64 end, unsigned int min_order) { - unsigned int order; + unsigned int tree, order; int i; if (!min_order) @@ -210,45 +235,48 @@ static int __force_merge(struct drm_buddy *mm, if (min_order > mm->max_order) return -EINVAL; - for (i = min_order - 1; i >= 0; i--) { - struct rb_root *root = &mm->free_tree[i]; - struct rb_node *iter; + for_each_free_tree(tree) { + for (i = min_order - 1; i >= 0; i--) { + struct rb_node *iter = rb_last(&mm->free_trees[tree][i]); - iter = rb_last(root); + while (iter) { + struct drm_buddy_block *block, *buddy; + u64 block_start, block_end; - while (iter) { - struct drm_buddy_block *block, *buddy; - u64 block_start, block_end; - - block = rbtree_get_free_block(iter); - iter = rb_prev(iter); - - if (!block || !block->parent) - continue; - - block_start = drm_buddy_block_offset(block); - block_end = block_start + drm_buddy_block_size(mm, block) - 1; - - if (!contains(start, end, block_start, block_end)) - continue; - - buddy = __get_buddy(block); - if (!drm_buddy_block_is_free(buddy)) - continue; - - WARN_ON(drm_buddy_block_is_clear(block) == - drm_buddy_block_is_clear(buddy)); - - if (iter == &buddy->rb) + block = rbtree_get_free_block(iter); iter = rb_prev(iter); - rbtree_remove(mm, block); - if (drm_buddy_block_is_clear(block)) - mm->clear_avail -= drm_buddy_block_size(mm, block); + if (!block || !block->parent) + continue; - order = __drm_buddy_free(mm, block, true); - if (order >= min_order) - return 0; + block_start = drm_buddy_block_offset(block); + block_end = block_start + drm_buddy_block_size(mm, block) - 1; + + if (!contains(start, end, block_start, block_end)) + continue; + + buddy = __get_buddy(block); + if (!drm_buddy_block_is_free(buddy)) + continue; + + WARN_ON(drm_buddy_block_is_clear(block) == + drm_buddy_block_is_clear(buddy)); + + /* + * Advance to the next node when the current node is the buddy, + * as freeing the block will also remove its buddy from the tree. + */ + if (iter == &buddy->rb) + iter = rb_prev(iter); + + rbtree_remove(mm, block); + if (drm_buddy_block_is_clear(block)) + mm->clear_avail -= drm_buddy_block_size(mm, block); + + order = __drm_buddy_free(mm, block, true); + if (order >= min_order) + return 0; + } } } @@ -269,7 +297,7 @@ static int __force_merge(struct drm_buddy *mm, */ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) { - unsigned int i; + unsigned int i, j, root_count = 0; u64 offset = 0; if (size < chunk_size) @@ -291,14 +319,22 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER); - mm->free_tree = kmalloc_array(mm->max_order + 1, - sizeof(struct rb_root), - GFP_KERNEL); - if (!mm->free_tree) + mm->free_trees = kmalloc_array(DRM_BUDDY_MAX_FREE_TREES, + sizeof(*mm->free_trees), + GFP_KERNEL); + if (!mm->free_trees) return -ENOMEM; - for (i = 0; i <= mm->max_order; ++i) - mm->free_tree[i] = RB_ROOT; + for_each_free_tree(i) { + mm->free_trees[i] = kmalloc_array(mm->max_order + 1, + sizeof(struct rb_root), + GFP_KERNEL); + if (!mm->free_trees[i]) + goto out_free_tree; + + for (j = 0; j <= mm->max_order; ++j) + mm->free_trees[i][j] = RB_ROOT; + } mm->n_roots = hweight64(size); @@ -308,8 +344,6 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) if (!mm->roots) goto out_free_tree; - i = 0; - /* * Split into power-of-two blocks, in case we are given a size that is * not itself a power-of-two. @@ -328,24 +362,26 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size) mark_free(mm, root); - BUG_ON(i > mm->max_order); + BUG_ON(root_count > mm->max_order); BUG_ON(drm_buddy_block_size(mm, root) < chunk_size); - mm->roots[i] = root; + mm->roots[root_count] = root; offset += root_size; size -= root_size; - i++; + root_count++; } while (size); return 0; out_free_roots: - while (i--) - drm_block_free(mm, mm->roots[i]); + while (root_count--) + drm_block_free(mm, mm->roots[root_count]); kfree(mm->roots); out_free_tree: - kfree(mm->free_tree); + while (i--) + kfree(mm->free_trees[i]); + kfree(mm->free_trees); return -ENOMEM; } EXPORT_SYMBOL(drm_buddy_init); @@ -381,8 +417,9 @@ void drm_buddy_fini(struct drm_buddy *mm) WARN_ON(mm->avail != mm->size); + for_each_free_tree(i) + kfree(mm->free_trees[i]); kfree(mm->roots); - kfree(mm->free_tree); } EXPORT_SYMBOL(drm_buddy_fini); @@ -406,8 +443,7 @@ static int split_block(struct drm_buddy *mm, return -ENOMEM; } - mark_free(mm, block->left); - mark_free(mm, block->right); + mark_split(mm, block); if (drm_buddy_block_is_clear(block)) { mark_cleared(block->left); @@ -415,7 +451,8 @@ static int split_block(struct drm_buddy *mm, clear_reset(block); } - mark_split(mm, block); + mark_free(mm, block->left); + mark_free(mm, block->right); return 0; } @@ -448,6 +485,7 @@ EXPORT_SYMBOL(drm_get_buddy); */ void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear) { + enum drm_buddy_free_tree src_tree, dst_tree; u64 root_size, size, start; unsigned int order; int i; @@ -462,19 +500,24 @@ void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear) size -= root_size; } + src_tree = is_clear ? DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE; + dst_tree = is_clear ? DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE; + for (i = 0; i <= mm->max_order; ++i) { + struct rb_root *root = &mm->free_trees[src_tree][i]; struct drm_buddy_block *block, *tmp; - rbtree_postorder_for_each_entry_safe(block, tmp, &mm->free_tree[i], rb) { - if (is_clear != drm_buddy_block_is_clear(block)) { - if (is_clear) { - mark_cleared(block); - mm->clear_avail += drm_buddy_block_size(mm, block); - } else { - clear_reset(block); - mm->clear_avail -= drm_buddy_block_size(mm, block); - } + rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { + rbtree_remove(mm, block); + if (is_clear) { + mark_cleared(block); + mm->clear_avail += drm_buddy_block_size(mm, block); + } else { + clear_reset(block); + mm->clear_avail -= drm_buddy_block_size(mm, block); } + + rbtree_insert(mm, block, dst_tree); } } } @@ -664,27 +707,17 @@ __drm_buddy_alloc_range_bias(struct drm_buddy *mm, } static struct drm_buddy_block * -get_maxblock(struct drm_buddy *mm, unsigned int order, - unsigned long flags) +get_maxblock(struct drm_buddy *mm, + unsigned int order, + enum drm_buddy_free_tree tree) { struct drm_buddy_block *max_block = NULL, *block = NULL; + struct rb_root *root; unsigned int i; for (i = order; i <= mm->max_order; ++i) { - struct rb_node *iter = rb_last(&mm->free_tree[i]); - struct drm_buddy_block *tmp_block; - - while (iter) { - tmp_block = rbtree_get_free_block(iter); - - if (!block_incompatible(tmp_block, flags)) { - block = tmp_block; - break; - } - - iter = rb_prev(iter); - } - + root = &mm->free_trees[tree][i]; + block = rbtree_last_free_block(root); if (!block) continue; @@ -708,39 +741,37 @@ alloc_from_freetree(struct drm_buddy *mm, unsigned long flags) { struct drm_buddy_block *block = NULL; + struct rb_root *root; + enum drm_buddy_free_tree tree; unsigned int tmp; int err; + tree = (flags & DRM_BUDDY_CLEAR_ALLOCATION) ? + DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE; + if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) { - block = get_maxblock(mm, order, flags); + block = get_maxblock(mm, order, tree); if (block) /* Store the obtained block order */ tmp = drm_buddy_block_order(block); } else { for (tmp = order; tmp <= mm->max_order; ++tmp) { - struct rb_node *iter = rb_last(&mm->free_tree[tmp]); - struct drm_buddy_block *tmp_block; - - while (iter) { - tmp_block = rbtree_get_free_block(iter); - - if (!block_incompatible(tmp_block, flags)) { - block = tmp_block; - break; - } - - iter = rb_prev(iter); - } - + /* Get RB tree root for this order and tree */ + root = &mm->free_trees[tree][tmp]; + block = rbtree_last_free_block(root); if (block) break; } } if (!block) { - /* Fallback method */ + /* Try allocating from the other tree */ + tree = (tree == DRM_BUDDY_CLEAR_TREE) ? + DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE; + for (tmp = order; tmp <= mm->max_order; ++tmp) { - block = rbtree_last_entry(mm, tmp); + root = &mm->free_trees[tree][tmp]; + block = rbtree_last_free_block(root); if (block) break; } @@ -885,10 +916,9 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm, { u64 rhs_offset, lhs_offset, lhs_size, filled; struct drm_buddy_block *block; + unsigned int tree, order; LIST_HEAD(blocks_lhs); - struct rb_node *iter; unsigned long pages; - unsigned int order; u64 modify_size; int err; @@ -898,40 +928,45 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm, if (order == 0) return -ENOSPC; - if (rbtree_is_empty(mm, order)) - return -ENOSPC; + for_each_free_tree(tree) { + struct rb_root *root; + struct rb_node *iter; - iter = rb_last(&mm->free_tree[order]); + root = &mm->free_trees[tree][order]; + if (rbtree_is_empty(root)) + continue; - while (iter) { - block = rbtree_get_free_block(iter); + iter = rb_last(root); + while (iter) { + block = rbtree_get_free_block(iter); - /* Allocate blocks traversing RHS */ - rhs_offset = drm_buddy_block_offset(block); - err = __drm_buddy_alloc_range(mm, rhs_offset, size, - &filled, blocks); - if (!err || err != -ENOSPC) - return err; + /* Allocate blocks traversing RHS */ + rhs_offset = drm_buddy_block_offset(block); + err = __drm_buddy_alloc_range(mm, rhs_offset, size, + &filled, blocks); + if (!err || err != -ENOSPC) + return err; - lhs_size = max((size - filled), min_block_size); - if (!IS_ALIGNED(lhs_size, min_block_size)) - lhs_size = round_up(lhs_size, min_block_size); + lhs_size = max((size - filled), min_block_size); + if (!IS_ALIGNED(lhs_size, min_block_size)) + lhs_size = round_up(lhs_size, min_block_size); - /* Allocate blocks traversing LHS */ - lhs_offset = drm_buddy_block_offset(block) - lhs_size; - err = __drm_buddy_alloc_range(mm, lhs_offset, lhs_size, - NULL, &blocks_lhs); - if (!err) { - list_splice(&blocks_lhs, blocks); - return 0; - } else if (err != -ENOSPC) { + /* Allocate blocks traversing LHS */ + lhs_offset = drm_buddy_block_offset(block) - lhs_size; + err = __drm_buddy_alloc_range(mm, lhs_offset, lhs_size, + NULL, &blocks_lhs); + if (!err) { + list_splice(&blocks_lhs, blocks); + return 0; + } else if (err != -ENOSPC) { + drm_buddy_free_list_internal(mm, blocks); + return err; + } + /* Free blocks for the next iteration */ drm_buddy_free_list_internal(mm, blocks); - return err; - } - /* Free blocks for the next iteration */ - drm_buddy_free_list_internal(mm, blocks); - iter = rb_prev(iter); + iter = rb_prev(iter); + } } return -ENOSPC; @@ -1243,11 +1278,17 @@ void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p) for (order = mm->max_order; order >= 0; order--) { struct drm_buddy_block *block, *tmp; + struct rb_root *root; u64 count = 0, free; + unsigned int tree; - rbtree_postorder_for_each_entry_safe(block, tmp, &mm->free_tree[order], rb) { - BUG_ON(!drm_buddy_block_is_free(block)); - count++; + for_each_free_tree(tree) { + root = &mm->free_trees[tree][order]; + + rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { + BUG_ON(!drm_buddy_block_is_free(block)); + count++; + } } drm_printf(p, "order-%2d ", order); diff --git a/include/drm/drm_buddy.h b/include/drm/drm_buddy.h index 9ee105d4309f..d7891d08f67a 100644 --- a/include/drm/drm_buddy.h +++ b/include/drm/drm_buddy.h @@ -73,7 +73,7 @@ struct drm_buddy_block { */ struct drm_buddy { /* Maintain a free list for each order. */ - struct rb_root *free_tree; + struct rb_root **free_trees; /* * Maintain explicit binary tree(s) to track the allocation of the From c07823f85012b80d8b2e251acbc23a0bdc2925c2 Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Mon, 6 Oct 2025 15:21:24 +0530 Subject: [PATCH 054/128] drm/buddy: Add KUnit tests for allocator performance under fragmentation Add KUnit test cases that create severe memory fragmentation and measure allocation/free performance. The tests simulate two scenarios - 1. Allocation under severe fragmentation - Allocate the entire 4 GiB space as 8 KiB blocks with 64 KiB alignment, split them into two groups and free with mixed flags to block coalescing. - Repeatedly allocate and free 64 KiB blocks while timing the loop. - Freelist runtime: 76475 ms(76.5 seconds), soft-lockup triggered. RB-tree runtime: 186 ms. 2. Reverse free order under fragmentation - Create a similarly fragmented space, free half the blocks, reverse the order of the remainder, and release them with the cleared flag. - Freelist runtime: 85620 ms(85.6 seconds). RB-tree runtime: 114 ms. Signed-off-by: Arunpravin Paneer Selvam Reviewed-by: Matthew Auld Link: https://lore.kernel.org/r/20251006095124.1663-3-Arunpravin.PaneerSelvam@amd.com --- drivers/gpu/drm/tests/drm_buddy_test.c | 105 +++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/drivers/gpu/drm/tests/drm_buddy_test.c b/drivers/gpu/drm/tests/drm_buddy_test.c index 7a0e523651f0..5f40b5343bd8 100644 --- a/drivers/gpu/drm/tests/drm_buddy_test.c +++ b/drivers/gpu/drm/tests/drm_buddy_test.c @@ -21,6 +21,110 @@ static inline u64 get_size(int order, u64 chunk_size) return (1 << order) * chunk_size; } +static void drm_test_buddy_fragmentation_performance(struct kunit *test) +{ + struct drm_buddy_block *block, *tmp; + int num_blocks, i, ret, count = 0; + LIST_HEAD(allocated_blocks); + unsigned long elapsed_ms; + LIST_HEAD(reverse_list); + LIST_HEAD(test_blocks); + LIST_HEAD(clear_list); + LIST_HEAD(dirty_list); + LIST_HEAD(free_list); + struct drm_buddy mm; + u64 mm_size = SZ_4G; + ktime_t start, end; + + /* + * Allocation under severe fragmentation + * + * Create severe fragmentation by allocating the entire 4 GiB address space + * as tiny 8 KiB blocks but forcing a 64 KiB alignment. The resulting pattern + * leaves many scattered holes. Split the allocations into two groups and + * return them with different flags to block coalescing, then repeatedly + * allocate and free 64 KiB blocks while timing the loop. This stresses how + * quickly the allocator can satisfy larger, aligned requests from a pool of + * highly fragmented space. + */ + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, SZ_4K), + "buddy_init failed\n"); + + num_blocks = mm_size / SZ_64K; + + start = ktime_get(); + /* Allocate with maximum fragmentation - 8K blocks with 64K alignment */ + for (i = 0; i < num_blocks; i++) + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, SZ_8K, SZ_64K, + &allocated_blocks, 0), + "buddy_alloc hit an error size=%u\n", SZ_8K); + + list_for_each_entry_safe(block, tmp, &allocated_blocks, link) { + if (count % 4 == 0 || count % 4 == 3) + list_move_tail(&block->link, &clear_list); + else + list_move_tail(&block->link, &dirty_list); + count++; + } + + /* Free with different flags to ensure no coalescing */ + drm_buddy_free_list(&mm, &clear_list, DRM_BUDDY_CLEARED); + drm_buddy_free_list(&mm, &dirty_list, 0); + + for (i = 0; i < num_blocks; i++) + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, SZ_64K, SZ_64K, + &test_blocks, 0), + "buddy_alloc hit an error size=%u\n", SZ_64K); + drm_buddy_free_list(&mm, &test_blocks, 0); + + end = ktime_get(); + elapsed_ms = ktime_to_ms(ktime_sub(end, start)); + + kunit_info(test, "Fragmented allocation took %lu ms\n", elapsed_ms); + + drm_buddy_fini(&mm); + + /* + * Reverse free order under fragmentation + * + * Construct a fragmented 4 GiB space by allocating every 8 KiB block with + * 64 KiB alignment, creating a dense scatter of small regions. Half of the + * blocks are selectively freed to form sparse gaps, while the remaining + * allocations are preserved, reordered in reverse, and released back with + * the cleared flag. This models a pathological reverse-ordered free pattern + * and measures how quickly the allocator can merge and reclaim space when + * deallocation occurs in the opposite order of allocation, exposing the + * cost difference between a linear freelist scan and an ordered tree lookup. + */ + ret = drm_buddy_init(&mm, mm_size, SZ_4K); + KUNIT_ASSERT_EQ(test, ret, 0); + + start = ktime_get(); + /* Allocate maximum fragmentation */ + for (i = 0; i < num_blocks; i++) + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, SZ_8K, SZ_64K, + &allocated_blocks, 0), + "buddy_alloc hit an error size=%u\n", SZ_8K); + + list_for_each_entry_safe(block, tmp, &allocated_blocks, link) { + if (count % 2 == 0) + list_move_tail(&block->link, &free_list); + count++; + } + drm_buddy_free_list(&mm, &free_list, DRM_BUDDY_CLEARED); + + list_for_each_entry_safe_reverse(block, tmp, &allocated_blocks, link) + list_move(&block->link, &reverse_list); + drm_buddy_free_list(&mm, &reverse_list, DRM_BUDDY_CLEARED); + + end = ktime_get(); + elapsed_ms = ktime_to_ms(ktime_sub(end, start)); + + kunit_info(test, "Reverse-ordered free took %lu ms\n", elapsed_ms); + + drm_buddy_fini(&mm); +} + static void drm_test_buddy_alloc_range_bias(struct kunit *test) { u32 mm_size, size, ps, bias_size, bias_start, bias_end, bias_rem; @@ -772,6 +876,7 @@ static struct kunit_case drm_buddy_tests[] = { KUNIT_CASE(drm_test_buddy_alloc_contiguous), KUNIT_CASE(drm_test_buddy_alloc_clear), KUNIT_CASE(drm_test_buddy_alloc_range_bias), + KUNIT_CASE(drm_test_buddy_fragmentation_performance), {} }; From 3379655524e613eb690d4c296ba0d2711a815f86 Mon Sep 17 00:00:00 2001 From: Eslam Khafagy Date: Thu, 14 Aug 2025 01:39:52 +0300 Subject: [PATCH 055/128] drm: atmel-hlcdc: replace dev_* print functions with drm_* variants Update the Atmel HLCDC code to use DRM print macros drm_*() instead of dev_warn() and dev_err(). This change ensures consistency with DRM subsystem logging conventions [1]. [1] Link: https://docs.kernel.org/gpu/todo.html#convert-logging-to-drm-functions-with-drm-device-parameter Signed-off-by: Eslam Khafagy Reviewed-by: Manikandan Muralidharan Link: https://lore.kernel.org/r/20250813224000.130292-1-eslam.medhat1993@gmail.com Signed-off-by: Dharma Balasubiramani --- .../gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 21 ++++++++++--------- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 14 ++++++------- .../gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 3 ++- .../gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 6 +++--- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 0f7ffb3ced20..e0efc7309b1b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -215,32 +216,32 @@ static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c, if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, !(status & ATMEL_XLCDC_CM), 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register CMSTS timeout\n"); regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_XLCDC_SD); if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, status & ATMEL_XLCDC_SD, 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register SDSTS timeout\n"); } regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP); if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, !(status & ATMEL_HLCDC_DISP), 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register DISPSTS timeout\n"); regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC); if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, !(status & ATMEL_HLCDC_SYNC), 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register LCDSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register LCDSTS timeout\n"); regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK); if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, !(status & ATMEL_HLCDC_PIXEL_CLK), 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register CLKSTS timeout\n"); clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); pinctrl_pm_select_sleep_state(dev->dev); @@ -269,32 +270,32 @@ static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c, if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, status & ATMEL_HLCDC_PIXEL_CLK, 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register CLKSTS timeout\n"); regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC); if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, status & ATMEL_HLCDC_SYNC, 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register LCDSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register LCDSTS timeout\n"); regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP); if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, status & ATMEL_HLCDC_DISP, 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register DISPSTS timeout\n"); if (crtc->dc->desc->is_xlcdc) { regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_CM); if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, status & ATMEL_XLCDC_CM, 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register CMSTS timeout\n"); regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_SD); if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, !(status & ATMEL_XLCDC_SD), 10, 1000)) - dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n"); + drm_warn(dev, "Atmel LCDC status register SDSTS timeout\n"); } pm_runtime_put_sync(dev->dev); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index fa8ad94e431a..acb017a2486b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -724,19 +724,19 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) ret = atmel_hlcdc_create_outputs(dev); if (ret) { - dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret); + drm_err(dev, "failed to create HLCDC outputs: %d\n", ret); return ret; } ret = atmel_hlcdc_create_planes(dev); if (ret) { - dev_err(dev->dev, "failed to create planes: %d\n", ret); + drm_err(dev, "failed to create planes: %d\n", ret); return ret; } ret = atmel_hlcdc_crtc_create(dev); if (ret) { - dev_err(dev->dev, "failed to create crtc\n"); + drm_err(dev, "failed to create crtc\n"); return ret; } @@ -778,7 +778,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) ret = clk_prepare_enable(dc->hlcdc->periph_clk); if (ret) { - dev_err(dev->dev, "failed to enable periph_clk\n"); + drm_err(dev, "failed to enable periph_clk\n"); return ret; } @@ -786,13 +786,13 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) ret = drm_vblank_init(dev, 1); if (ret < 0) { - dev_err(dev->dev, "failed to initialize vblank\n"); + drm_err(dev, "failed to initialize vblank\n"); goto err_periph_clk_disable; } ret = atmel_hlcdc_dc_modeset_init(dev); if (ret < 0) { - dev_err(dev->dev, "failed to initialize mode setting\n"); + drm_err(dev, "failed to initialize mode setting\n"); goto err_periph_clk_disable; } @@ -802,7 +802,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev) ret = atmel_hlcdc_dc_irq_install(dev, dc->hlcdc->irq); pm_runtime_put_sync(dev->dev); if (ret < 0) { - dev_err(dev->dev, "failed to install IRQ handler\n"); + drm_err(dev, "failed to install IRQ handler\n"); goto err_periph_clk_disable; } diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 50fee6a93964..0b8a86afb096 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "atmel_hlcdc_dc.h" @@ -92,7 +93,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep); of_node_put(ep); if (output->bus_fmt < 0) { - dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint); + drm_err(dev, "endpoint %d: invalid bus width\n", endpoint); return -EINVAL; } diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 59be5f64c058..6dd38018b47f 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -1034,7 +1034,7 @@ static void atmel_hlcdc_irq_dbg(struct atmel_hlcdc_plane *plane, if (isr & (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) | ATMEL_HLCDC_LAYER_OVR_IRQ(2))) - dev_dbg(plane->base.dev->dev, "overrun on plane %s\n", + drm_dbg(plane->base.dev, "overrun on plane %s\n", desc->name); } @@ -1051,7 +1051,7 @@ static void atmel_xlcdc_irq_dbg(struct atmel_hlcdc_plane *plane, if (isr & (ATMEL_XLCDC_LAYER_OVR_IRQ(0) | ATMEL_XLCDC_LAYER_OVR_IRQ(1) | ATMEL_XLCDC_LAYER_OVR_IRQ(2))) - dev_dbg(plane->base.dev->dev, "overrun on plane %s\n", + drm_dbg(plane->base.dev, "overrun on plane %s\n", desc->name); } @@ -1140,7 +1140,7 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p) if (state) { if (atmel_hlcdc_plane_alloc_dscrs(p, state)) { kfree(state); - dev_err(p->dev->dev, + drm_err(p->dev, "Failed to allocate initial plane state\n"); return; } From e00f78679f5a3d98b650fc9397c4d358ab570725 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Tue, 7 Oct 2025 21:53:24 -0700 Subject: [PATCH 056/128] accel/amdxdna: Resume power for creating and destroying hardware context When the hardware is powered down by auto-suspend, creating or destroying a hardware context without resuming power will fail. Call amdxdna_pm_resume_get() before requesting the hardware to create or destroy a hardware context. Fixes: 063db451832b ("accel/amdxdna: Enhance runtime power management") Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://lore.kernel.org/r/20251008045324.4171807-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 691fdb3b008f..ab4d66f1325d 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -610,10 +610,14 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) goto free_entity; } + ret = amdxdna_pm_resume_get(xdna); + if (ret) + goto free_col_list; + ret = aie2_alloc_resource(hwctx); if (ret) { XDNA_ERR(xdna, "Alloc hw resource failed, ret %d", ret); - goto free_col_list; + goto suspend_put; } ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id, @@ -628,6 +632,7 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) XDNA_ERR(xdna, "Create syncobj failed, ret %d", ret); goto release_resource; } + amdxdna_pm_suspend_put(xdna); hwctx->status = HWCTX_STAT_INIT; ndev = xdna->dev_handle; @@ -640,6 +645,8 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) release_resource: aie2_release_resource(hwctx); +suspend_put: + amdxdna_pm_suspend_put(xdna); free_col_list: kfree(hwctx->col_list); free_entity: From 39a0283dbaa5483400fa344ce43d543101817e07 Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Tue, 7 Oct 2025 10:34:51 +0200 Subject: [PATCH 057/128] accel/ivpu: Update JSM API header to 3.33.0 New API header includes additional status codes and range definitions for error handling and improved API documentation. Signed-off-by: Andrzej Kacprowski Reviewed-by: Jeff Hugo Signed-off-by: Karol Wachowski Link: https://lore.kernel.org/r/20251007083451.2816990-1-karol.wachowski@linux.intel.com --- drivers/accel/ivpu/vpu_jsm_api.h | 150 ++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 54 deletions(-) diff --git a/drivers/accel/ivpu/vpu_jsm_api.h b/drivers/accel/ivpu/vpu_jsm_api.h index de1b37ea1251..bca6a44dc041 100644 --- a/drivers/accel/ivpu/vpu_jsm_api.h +++ b/drivers/accel/ivpu/vpu_jsm_api.h @@ -23,12 +23,12 @@ /* * Minor version changes when API backward compatibility is preserved. */ -#define VPU_JSM_API_VER_MINOR 32 +#define VPU_JSM_API_VER_MINOR 33 /* * API header changed (field names, documentation, formatting) but API itself has not been changed */ -#define VPU_JSM_API_VER_PATCH 5 +#define VPU_JSM_API_VER_PATCH 0 /* * Index in the API version table @@ -76,8 +76,11 @@ #define VPU_JSM_STATUS_PREEMPTED_MID_INFERENCE 0xDU /* Job status returned when the job was preempted mid-command */ #define VPU_JSM_STATUS_PREEMPTED_MID_COMMAND 0xDU +/* Range of status codes that require engine reset */ +#define VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN 0xEU #define VPU_JSM_STATUS_MVNCI_CONTEXT_VIOLATION_HW 0xEU #define VPU_JSM_STATUS_MVNCI_PREEMPTION_TIMED_OUT 0xFU +#define VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX 0x1FU /* * Host <-> VPU IPC channels. @@ -404,8 +407,8 @@ struct vpu_hws_native_fence_log_header { /** Index of the first free entry in buffer. */ u32 first_free_entry_idx; /** - * Incremented each time NPU wraps around - * the buffer to write next entry. + * Incremented whenever the NPU wraps around the buffer and writes + * to the first entry again. */ u32 wraparound_count; }; @@ -454,10 +457,17 @@ struct vpu_hws_native_fence_log_buffer { * Host <-> VPU IPC messages types. */ enum vpu_ipc_msg_type { + /** Unsupported command */ VPU_JSM_MSG_UNKNOWN = 0xFFFFFFFF, - /* IPC Host -> Device, Async commands */ + /** IPC Host -> Device, base id for async commands */ VPU_JSM_MSG_ASYNC_CMD = 0x1100, + /** + * Reset engine. The NPU cancels all the jobs currently executing on the target + * engine making the engine become idle and then does a HW reset, before returning + * to the host. + * @see struct vpu_ipc_msg_payload_engine_reset + */ VPU_JSM_MSG_ENGINE_RESET = VPU_JSM_MSG_ASYNC_CMD, /** * Preempt engine. The NPU stops (preempts) all the jobs currently @@ -467,6 +477,7 @@ enum vpu_ipc_msg_type { * the target engine, but it stops processing them (until the queue doorbell * is rung again); the host is responsible to reset the job queue, either * after preemption or when resubmitting jobs to the queue. + * @see vpu_ipc_msg_payload_engine_preempt */ VPU_JSM_MSG_ENGINE_PREEMPT = 0x1101, /** @@ -583,23 +594,32 @@ enum vpu_ipc_msg_type { * @see vpu_ipc_msg_payload_hws_resume_engine */ VPU_JSM_MSG_HWS_ENGINE_RESUME = 0x111b, - /* Control command: Enable survivability/DCT mode */ + /** + * Control command: Enable survivability/DCT mode + * @see vpu_ipc_msg_payload_pwr_dct_control + */ VPU_JSM_MSG_DCT_ENABLE = 0x111c, - /* Control command: Disable survivability/DCT mode */ + /** + * Control command: Disable survivability/DCT mode + * This command has no payload + */ VPU_JSM_MSG_DCT_DISABLE = 0x111d, /** * Dump VPU state. To be used for debug purposes only. - * NOTE: Please introduce new ASYNC commands before this one. * + * This command has no payload. + * NOTE: Please introduce new ASYNC commands before this one. */ VPU_JSM_MSG_STATE_DUMP = 0x11FF, - /* IPC Host -> Device, General commands */ + /** IPC Host -> Device, base id for general commands */ VPU_JSM_MSG_GENERAL_CMD = 0x1200, + /** Unsupported command */ VPU_JSM_MSG_BLOB_DEINIT_DEPRECATED = VPU_JSM_MSG_GENERAL_CMD, /** * Control dyndbg behavior by executing a dyndbg command; equivalent to * Linux command: * @verbatim echo '' > /dynamic_debug/control @endverbatim + * @see vpu_ipc_msg_payload_dyndbg_control */ VPU_JSM_MSG_DYNDBG_CONTROL = 0x1201, /** @@ -607,7 +627,10 @@ enum vpu_ipc_msg_type { */ VPU_JSM_MSG_PWR_D0I3_ENTER = 0x1202, - /* IPC Device -> Host, Job completion */ + /** + * IPC Device -> Host, Job completion + * @see struct vpu_ipc_msg_payload_job_done + */ VPU_JSM_MSG_JOB_DONE = 0x2100, /** * IPC Device -> Host, Fence signalled @@ -622,6 +645,10 @@ enum vpu_ipc_msg_type { * @see vpu_ipc_msg_payload_engine_reset_done */ VPU_JSM_MSG_ENGINE_RESET_DONE = VPU_JSM_MSG_ASYNC_CMD_DONE, + /** + * Preempt complete message + * @see vpu_ipc_msg_payload_engine_preempt_done + */ VPU_JSM_MSG_ENGINE_PREEMPT_DONE = 0x2201, VPU_JSM_MSG_REGISTER_DB_DONE = 0x2202, VPU_JSM_MSG_UNREGISTER_DB_DONE = 0x2203, @@ -729,13 +756,20 @@ enum vpu_ipc_msg_type { * @see vpu_ipc_msg_payload_hws_resume_engine */ VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE = 0x221c, - /* Response to control command: Enable survivability/DCT mode */ + /** + * Response to control command: Enable survivability/DCT mode + * This command has no payload + */ VPU_JSM_MSG_DCT_ENABLE_DONE = 0x221d, - /* Response to control command: Disable survivability/DCT mode */ + /** + * Response to control command: Disable survivability/DCT mode + * This command has no payload + */ VPU_JSM_MSG_DCT_DISABLE_DONE = 0x221e, /** * Response to state dump control command. - * NOTE: Please introduce new ASYNC responses before this one. * + * This command has no payload. + * NOTE: Please introduce new ASYNC responses before this one. */ VPU_JSM_MSG_STATE_DUMP_RSP = 0x22FF, @@ -753,20 +787,25 @@ enum vpu_ipc_msg_type { enum vpu_ipc_msg_status { VPU_JSM_MSG_FREE, VPU_JSM_MSG_ALLOCATED }; -/* - * Host <-> LRT IPC message payload definitions +/** + * Engine reset request payload + * @see VPU_JSM_MSG_ENGINE_RESET */ struct vpu_ipc_msg_payload_engine_reset { - /* Engine to be reset. */ + /** Engine to be reset. */ u32 engine_idx; - /* Reserved */ + /** Reserved */ u32 reserved_0; }; +/** + * Engine preemption request struct + * @see VPU_JSM_MSG_ENGINE_PREEMPT + */ struct vpu_ipc_msg_payload_engine_preempt { - /* Engine to be preempted. */ + /** Engine to be preempted. */ u32 engine_idx; - /* ID of the preemption request. */ + /** ID of the preemption request. */ u32 preempt_id; }; @@ -935,20 +974,24 @@ struct vpu_jsm_metric_streamer_update { u64 next_buffer_size; }; +/** + * Device -> host job completion message. + * @see VPU_JSM_MSG_JOB_DONE + */ struct vpu_ipc_msg_payload_job_done { - /* Engine to which the job was submitted. */ + /** Engine to which the job was submitted. */ u32 engine_idx; - /* Index of the doorbell to which the job was submitted */ + /** Index of the doorbell to which the job was submitted */ u32 db_idx; - /* ID of the completed job */ + /** ID of the completed job */ u32 job_id; - /* Status of the completed job */ + /** Status of the completed job */ u32 job_status; - /* Host SSID */ + /** Host SSID */ u32 host_ssid; - /* Zero Padding */ + /** Zero Padding */ u32 reserved_0; - /* Command queue id */ + /** Command queue id */ u64 cmdq_id; }; @@ -997,10 +1040,14 @@ struct vpu_ipc_msg_payload_engine_reset_done { impacted_contexts[VPU_MAX_ENGINE_RESET_IMPACTED_CONTEXTS]; }; +/** + * Preemption response struct + * @see VPU_JSM_MSG_ENGINE_PREEMPT_DONE + */ struct vpu_ipc_msg_payload_engine_preempt_done { - /* Engine preempted. */ + /** Engine preempted. */ u32 engine_idx; - /* ID of the preemption request. */ + /** ID of the preemption request. */ u32 preempt_id; }; @@ -1552,29 +1599,24 @@ struct vpu_jsm_metric_counter_descriptor { }; /** - * Payload for VPU_JSM_MSG_DYNDBG_CONTROL requests. + * Payload for @ref VPU_JSM_MSG_DYNDBG_CONTROL requests. * - * VPU_JSM_MSG_DYNDBG_CONTROL are used to control the VPU FW Dynamic Debug - * feature, which allows developers to selectively enable / disable MVLOG_DEBUG - * messages. This is equivalent to the Dynamic Debug functionality provided by - * Linux - * (https://www.kernel.org/doc/html/latest/admin-guide/dynamic-debug-howto.html) - * The host can control Dynamic Debug behavior by sending dyndbg commands, which - * have the same syntax as Linux - * dyndbg commands. + * VPU_JSM_MSG_DYNDBG_CONTROL requests are used to control the VPU FW dynamic debug + * feature, which allows developers to selectively enable/disable code to obtain + * additional FW information. This is equivalent to the dynamic debug functionality + * provided by Linux. The host can control dynamic debug behavior by sending dyndbg + * commands, using the same syntax as for Linux dynamic debug commands. * - * NOTE: in order for MVLOG_DEBUG messages to be actually printed, the host - * still has to set the logging level to MVLOG_DEBUG, using the - * VPU_JSM_MSG_TRACE_SET_CONFIG command. + * @see https://www.kernel.org/doc/html/latest/admin-guide/dynamic-debug-howto.html. * - * The host can see the current dynamic debug configuration by executing a - * special 'show' command. The dyndbg configuration will be printed to the - * configured logging destination using MVLOG_INFO logging level. + * NOTE: + * As the dynamic debug feature uses MVLOG messages to provide information, the host + * must first set the logging level to MVLOG_DEBUG, using the @ref VPU_JSM_MSG_TRACE_SET_CONFIG + * command. */ struct vpu_ipc_msg_payload_dyndbg_control { /** - * Dyndbg command (same format as Linux dyndbg); must be a NULL-terminated - * string. + * Dyndbg command to be executed. */ char dyndbg_cmd[VPU_DYNDBG_CMD_MAX_LEN]; }; @@ -1595,7 +1637,7 @@ struct vpu_ipc_msg_payload_pwr_d0i3_enter { }; /** - * Payload for VPU_JSM_MSG_DCT_ENABLE message. + * Payload for @ref VPU_JSM_MSG_DCT_ENABLE message. * * Default values for DCT active/inactive times are 5.3ms and 30ms respectively, * corresponding to a 85% duty cycle. This payload allows the host to tune these @@ -1652,28 +1694,28 @@ union vpu_ipc_msg_payload { struct vpu_ipc_msg_payload_pwr_dct_control pwr_dct_control; }; -/* - * Host <-> LRT IPC message base structure. +/** + * Host <-> NPU IPC message base structure. * * NOTE: All instances of this object must be aligned on a 64B boundary * to allow proper handling of VPU cache operations. */ struct vpu_jsm_msg { - /* Reserved */ + /** Reserved */ u64 reserved_0; - /* Message type, see vpu_ipc_msg_type enum. */ + /** Message type, see @ref vpu_ipc_msg_type. */ u32 type; - /* Buffer status, see vpu_ipc_msg_status enum. */ + /** Buffer status, see @ref vpu_ipc_msg_status. */ u32 status; - /* + /** * Request ID, provided by the host in a request message and passed * back by VPU in the response message. */ u32 request_id; - /* Request return code set by the VPU, see VPU_JSM_STATUS_* defines. */ + /** Request return code set by the VPU, see VPU_JSM_STATUS_* defines. */ u32 result; u64 reserved_1; - /* Message payload depending on message type, see vpu_ipc_msg_payload union. */ + /** Message payload depending on message type, see vpu_ipc_msg_payload union. */ union vpu_ipc_msg_payload payload; }; From 4139eb2490cbc67cdeabea3cc3a08c3d7e4ce973 Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Tue, 7 Oct 2025 10:35:11 +0200 Subject: [PATCH 058/128] accel/ivpu: Trigger engine reset for additional job status codes Trigger engine reset for any status code in the range. This allows to add additional status codes in the future without breaking compatibility between the firmware and the driver. Signed-off-by: Andrzej Kacprowski Reviewed-by: Jeff Hugo Signed-off-by: Karol Wachowski Link: https://lore.kernel.org/r/20251007083511.2817021-1-karol.wachowski@linux.intel.com --- drivers/accel/ivpu/ivpu_job.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 17273c68f84c..fd548028f1d6 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -574,7 +574,11 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 if (!job) return -ENOENT; - if (job_status == VPU_JSM_STATUS_MVNCI_CONTEXT_VIOLATION_HW) { + switch (job_status) { + case VPU_JSM_STATUS_PROCESSING_ERR: + case VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN ... VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX: + { + /* Trigger an engine reset */ guard(mutex)(&job->file_priv->lock); if (job->file_priv->has_mmu_faults) @@ -589,6 +593,10 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 queue_work(system_wq, &vdev->context_abort_work); return 0; } + default: + /* Complete job with error status, engine reset not required */ + break; + } job = ivpu_job_remove_from_submitted_jobs(vdev, job_id); if (!job) From 40527034d1d5719f9e87ccd4382a83c6865b3d8f Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Wed, 8 Oct 2025 08:12:55 +0200 Subject: [PATCH 059/128] accel/ivpu: Return correct job error status Currently the driver returns ABORTED for all errors that trigger engine reset. It is better to distinguish between different error types by returning the actual error code reported by firmware. This allows userspace to take different actions based on the error type and improves debuggability. Refactor ivpu_job_signal_and_destroy() by extracting engine error handling logic into a new function ivpu_job_handle_engine_error(). This simplifies engine error handling logic by removing necessity of calling ivpu_job_singal_and_destroy() multiple times by a single job changing it's behavior based on job status. Signed-off-by: Andrzej Kacprowski Reviewed-by: Jeff Hugo Signed-off-by: Karol Wachowski Link: https://lore.kernel.org/r/20251008061255.2909794-1-karol.wachowski@linux.intel.com --- drivers/accel/ivpu/ivpu_job.c | 79 ++++++++++++++++++++++------------- drivers/accel/ivpu/ivpu_job.h | 3 ++ 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index fd548028f1d6..ba4535a75aa7 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -564,6 +564,44 @@ static struct ivpu_job *ivpu_job_remove_from_submitted_jobs(struct ivpu_device * return job; } +bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status) +{ + lockdep_assert_held(&vdev->submitted_jobs_lock); + + switch (job_status) { + case VPU_JSM_STATUS_PROCESSING_ERR: + case VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN ... VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX: + { + struct ivpu_job *job = xa_load(&vdev->submitted_jobs_xa, job_id); + + if (!job) + return false; + + /* Trigger an engine reset */ + guard(mutex)(&job->file_priv->lock); + + job->job_status = job_status; + + if (job->file_priv->has_mmu_faults) + return false; + + /* + * Mark context as faulty and defer destruction of the job to jobs abort thread + * handler to synchronize between both faults and jobs returning context violation + * status and ensure both are handled in the same way + */ + job->file_priv->has_mmu_faults = true; + queue_work(system_wq, &vdev->context_abort_work); + return true; + } + default: + /* Complete job with error status, engine reset not required */ + break; + } + + return false; +} + static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status) { struct ivpu_job *job; @@ -574,43 +612,22 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 if (!job) return -ENOENT; - switch (job_status) { - case VPU_JSM_STATUS_PROCESSING_ERR: - case VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN ... VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX: - { - /* Trigger an engine reset */ - guard(mutex)(&job->file_priv->lock); + ivpu_job_remove_from_submitted_jobs(vdev, job_id); + if (job->job_status == VPU_JSM_STATUS_SUCCESS) { if (job->file_priv->has_mmu_faults) - return 0; - - /* - * Mark context as faulty and defer destruction of the job to jobs abort thread - * handler to synchronize between both faults and jobs returning context violation - * status and ensure both are handled in the same way - */ - job->file_priv->has_mmu_faults = true; - queue_work(system_wq, &vdev->context_abort_work); - return 0; - } - default: - /* Complete job with error status, engine reset not required */ - break; + job->job_status = DRM_IVPU_JOB_STATUS_ABORTED; + else + job->job_status = job_status; } - job = ivpu_job_remove_from_submitted_jobs(vdev, job_id); - if (!job) - return -ENOENT; - - if (job->file_priv->has_mmu_faults) - job_status = DRM_IVPU_JOB_STATUS_ABORTED; - - job->bos[CMD_BUF_IDX]->job_status = job_status; + job->bos[CMD_BUF_IDX]->job_status = job->job_status; dma_fence_signal(job->done_fence); trace_job("done", job); ivpu_dbg(vdev, JOB, "Job complete: id %3u ctx %2d cmdq_id %u engine %d status 0x%x\n", - job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx, job_status); + job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx, + job->job_status); ivpu_job_destroy(job); ivpu_stop_job_timeout_detection(vdev); @@ -1030,7 +1047,9 @@ ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr, payload = (struct vpu_ipc_msg_payload_job_done *)&jsm_msg->payload; mutex_lock(&vdev->submitted_jobs_lock); - ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status); + if (!ivpu_job_handle_engine_error(vdev, payload->job_id, payload->job_status)) + /* No engine error, complete the job normally */ + ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status); mutex_unlock(&vdev->submitted_jobs_lock); } diff --git a/drivers/accel/ivpu/ivpu_job.h b/drivers/accel/ivpu/ivpu_job.h index d2fc4c151614..3ab61e6a5616 100644 --- a/drivers/accel/ivpu/ivpu_job.h +++ b/drivers/accel/ivpu/ivpu_job.h @@ -51,6 +51,7 @@ struct ivpu_cmdq { * @cmdq_id: Command queue ID used for submission * @job_id: Unique job ID for tracking and status reporting * @engine_idx: Engine index for job execution + * @job_status: Status reported by firmware for this job * @primary_preempt_buf: Primary preemption buffer for job * @secondary_preempt_buf: Secondary preemption buffer for job (optional) * @bo_count: Number of buffer objects associated with this job @@ -64,6 +65,7 @@ struct ivpu_job { u32 cmdq_id; u32 job_id; u32 engine_idx; + u32 job_status; struct ivpu_bo *primary_preempt_buf; struct ivpu_bo *secondary_preempt_buf; size_t bo_count; @@ -83,6 +85,7 @@ void ivpu_cmdq_abort_all_jobs(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id) void ivpu_job_done_consumer_init(struct ivpu_device *vdev); void ivpu_job_done_consumer_fini(struct ivpu_device *vdev); +bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status); void ivpu_context_abort_work_fn(struct work_struct *work); void ivpu_jobs_abort_all(struct ivpu_device *vdev); From dc84dbcc54d2f0f35942ca54aa7a04770aa97199 Mon Sep 17 00:00:00 2001 From: Alexandr Sapozhnikov Date: Mon, 22 Sep 2025 17:44:13 +0300 Subject: [PATCH 060/128] drm/virtio: Handle drm_crtc_init_with_planes() errors Return value of function drm_crtc_init_with_planes(), called by vgdev_output_init(), is not checked, but it is usually checked for this function. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Alexandr Sapozhnikov Reviewed-by: Dmitry Osipenko Signed-off-by: Dmitry Osipenko [dmitry.osipenko@collabora.com: coding style fix, edit commit message] Link: https://lore.kernel.org/r/20250922144418.41-1-alsp705@gmail.com --- drivers/gpu/drm/virtio/virtgpu_display.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index c3315935d8bc..4b1252c04b7a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -257,6 +257,7 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index) struct drm_encoder *encoder = &output->enc; struct drm_crtc *crtc = &output->crtc; struct drm_plane *primary, *cursor; + int ret; output->index = index; if (index == 0) { @@ -271,8 +272,10 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index) cursor = virtio_gpu_plane_init(vgdev, DRM_PLANE_TYPE_CURSOR, index); if (IS_ERR(cursor)) return PTR_ERR(cursor); - drm_crtc_init_with_planes(dev, crtc, primary, cursor, - &virtio_gpu_crtc_funcs, NULL); + ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor, + &virtio_gpu_crtc_funcs, NULL); + if (ret) + return ret; drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs); drm_connector_init(dev, connector, &virtio_gpu_connector_funcs, From a036f5fceedb9fbd715565fef7b824a121503de7 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 Oct 2025 15:06:51 +0200 Subject: [PATCH 061/128] drm/virtgpu: Use vblank timer Use a vblank timer to simulate the vblank interrupt. The DRM vblank helpers provide an implementation on top of Linux' hrtimer. Virtgpu enables and disables the timer as part of the CRTC. The atomic_flush callback sets up the event. Like vblank interrupts, the vblank timer fires at the rate of the display refresh. Most userspace limits its page flip rate according to the DRM vblank event. Virtgpu's virtual hardware does not provide vblank interrupts, so DRM sends each event ASAP. With the fast access times of virtual display memory, the event rate is much higher than the display mode's refresh rate; creating the next page flip almost immediately. This leads to excessive CPU overhead from even small display updates, such as moving the mouse pointer. This problem affects virtgpu and all other virtual displays. See [1] for a discussion in the context of hypervdrm. Signed-off-by: Thomas Zimmermann Link: https://lore.kernel.org/dri-devel/SN6PR02MB415702B00D6D52B0EE962C98D46CA@SN6PR02MB4157.namprd02.prod.outlook.com/ # [1] Acked-by: Gerd Hoffmann Reviewed-by: Dmitry Osipenko Tested-by: Dmitry Osipenko Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20251008130701.246988-1-tzimmermann@suse.de --- drivers/gpu/drm/virtio/virtgpu_display.c | 29 ++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 4b1252c04b7a..e972d9b015a9 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include "virtgpu_drv.h" @@ -55,6 +57,7 @@ static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = { .reset = drm_atomic_helper_crtc_reset, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + DRM_CRTC_VBLANK_TIMER_FUNCS, }; static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = { @@ -99,6 +102,7 @@ static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc *crtc) static void virtio_gpu_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { + drm_crtc_vblank_on(crtc); } static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc, @@ -108,6 +112,8 @@ static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc, struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); + drm_crtc_vblank_off(crtc); + virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0); virtio_gpu_notify(vgdev); } @@ -121,9 +127,10 @@ static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc, static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) { - struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, - crtc); + struct drm_device *dev = crtc->dev; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc); + struct drm_pending_vblank_event *event; /* * virtio-gpu can't do modeset and plane update operations @@ -133,6 +140,20 @@ static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc, */ if (drm_atomic_crtc_needs_modeset(crtc_state)) output->needs_modeset = true; + + spin_lock_irq(&dev->event_lock); + + event = crtc_state->event; + crtc_state->event = NULL; + + if (event) { + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + } + + spin_unlock_irq(&dev->event_lock); } static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = { @@ -359,6 +380,10 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) for (i = 0 ; i < vgdev->num_scanouts; ++i) vgdev_output_init(vgdev, i); + ret = drm_vblank_init(vgdev->ddev, vgdev->num_scanouts); + if (ret) + return ret; + drm_mode_config_reset(vgdev->ddev); return 0; } From 02df3543f3e0ea572e2c739605ebd6c20e1149c4 Mon Sep 17 00:00:00 2001 From: Rain Yang Date: Sun, 28 Sep 2025 17:03:33 +0800 Subject: [PATCH 062/128] dt-bindings: gpu: mali-valhall: make mali-supply optional Not all platforms require the mali-supply regulator. This change removes it from the required list in the binding schema, and make mali-supply required for rk3588 only. Signed-off-by: Rain Yang Acked-by: Rob Herring (Arm) Reviewed-by: Boris Brezillon Signed-off-by: Steven Price Link: https://lore.kernel.org/r/20250928090334.35389-1-jiyu.yang@oss.nxp.com --- .../devicetree/bindings/gpu/arm,mali-valhall-csf.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml index 7ad5a3ffc5f5..613040fdb444 100644 --- a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml +++ b/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml @@ -92,7 +92,6 @@ required: - interrupts - interrupt-names - clocks - - mali-supply additionalProperties: false @@ -109,6 +108,8 @@ allOf: power-domains: maxItems: 1 power-domain-names: false + required: + - mali-supply examples: - | From a8cb5ca53690aa809f4f65e14192753073e61a71 Mon Sep 17 00:00:00 2001 From: Rain Yang Date: Sun, 28 Sep 2025 17:03:34 +0800 Subject: [PATCH 063/128] drm/panthor: skip regulator setup if no such prop The regulator is optional, skip the setup instead of returning an error if it is not present Signed-off-by: Rain Yang Reviewed-by: Boris Brezillon Reviewed-by: Steven Price Signed-off-by: Steven Price Link: https://lore.kernel.org/r/20250928090334.35389-2-jiyu.yang@oss.nxp.com --- drivers/gpu/drm/panthor/panthor_devfreq.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c b/drivers/gpu/drm/panthor/panthor_devfreq.c index 3686515d368d..2df1d76d84a0 100644 --- a/drivers/gpu/drm/panthor/panthor_devfreq.c +++ b/drivers/gpu/drm/panthor/panthor_devfreq.c @@ -146,10 +146,9 @@ int panthor_devfreq_init(struct panthor_device *ptdev) ptdev->devfreq = pdevfreq; ret = devm_pm_opp_set_regulators(dev, reg_names); - if (ret) { + if (ret && ret != -ENODEV) { if (ret != -EPROBE_DEFER) DRM_DEV_ERROR(dev, "Couldn't set OPP regulators\n"); - return ret; } From c95ea2aa7f12b8160e474e5e0b53fdd545603a0e Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Tue, 7 Oct 2025 15:36:46 +0000 Subject: [PATCH 064/128] drm: Prevent sign extension on hdisplay and vdisplay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some functions in drm multiply hdisplay and vdisplay with a third factor, which can result in a sign extension according to static analysis due to an implicit s32 promotion. Use a cast to u32 to prevent this. Suggested-by: Rodrigo Vivi Signed-off-by: Krzystof Karas Signed-off-by: Jonathan Cavitt Cc: Matthew Auld Cc: Himal Prasad Ghimiray Cc: Matthew Brost Cc: Ville Syrjälä Cc: Jani Nikula Cc: Andi Shyti Link: https://lore.kernel.org/r/20251007153645.90920-2-jonathan.cavitt@intel.com Reviewed-by: Rodrigo Vivi Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/drm_gem_vram_helper.c | 2 +- drivers/gpu/drm/drm_mipi_dbi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index 90760d0ca071..0bec6f66682b 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -967,7 +967,7 @@ drm_vram_helper_mode_valid_internal(struct drm_device *dev, max_fbpages = (vmm->vram_size / 2) >> PAGE_SHIFT; - fbsize = mode->hdisplay * mode->vdisplay * max_bpp; + fbsize = (u32)mode->hdisplay * mode->vdisplay * max_bpp; fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE); if (fbpages > max_fbpages) diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index e33c78fc8fbd..b488c91c20a5 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -691,7 +691,7 @@ int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, const struct drm_simple_display_pipe_funcs *funcs, const struct drm_display_mode *mode, unsigned int rotation) { - size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16); + size_t bufsize = (u32)mode->vdisplay * mode->hdisplay * sizeof(u16); dbidev->drm.mode_config.preferred_depth = 16; From d847dc29d07a18911837863c88206547a1768d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Larumbe?= Date: Thu, 9 Oct 2025 12:43:00 +0100 Subject: [PATCH 065/128] drm/panfrost: Name scheduler queues after their job slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drawing from commit d2624d90a0b7 ("drm/panthor: assign unique names to queues"), give scheduler queues proper names that reflect the function of their JM slot, so that this will be shown when gathering DRM scheduler tracepoints. Signed-off-by: Adrián Larumbe Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Signed-off-by: Steven Price Link: https://lore.kernel.org/r/20251009114313.1374948-1-adrian.larumbe@collabora.com --- drivers/gpu/drm/panfrost/panfrost_drv.c | 16 ++++++---------- drivers/gpu/drm/panfrost/panfrost_job.c | 8 +++++++- drivers/gpu/drm/panfrost/panfrost_job.h | 2 ++ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 22350ce8a08f..607a5b8448d0 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -668,23 +668,19 @@ static void panfrost_gpu_show_fdinfo(struct panfrost_device *pfdev, * job spent on the GPU. */ - static const char * const engine_names[] = { - "fragment", "vertex-tiler", "compute-only" - }; - - BUILD_BUG_ON(ARRAY_SIZE(engine_names) != NUM_JOB_SLOTS); - for (i = 0; i < NUM_JOB_SLOTS - 1; i++) { if (pfdev->profile_mode) { drm_printf(p, "drm-engine-%s:\t%llu ns\n", - engine_names[i], panfrost_priv->engine_usage.elapsed_ns[i]); + panfrost_engine_names[i], + panfrost_priv->engine_usage.elapsed_ns[i]); drm_printf(p, "drm-cycles-%s:\t%llu\n", - engine_names[i], panfrost_priv->engine_usage.cycles[i]); + panfrost_engine_names[i], + panfrost_priv->engine_usage.cycles[i]); } drm_printf(p, "drm-maxfreq-%s:\t%lu Hz\n", - engine_names[i], pfdev->pfdevfreq.fast_rate); + panfrost_engine_names[i], pfdev->pfdevfreq.fast_rate); drm_printf(p, "drm-curfreq-%s:\t%lu Hz\n", - engine_names[i], pfdev->pfdevfreq.current_frequency); + panfrost_engine_names[i], pfdev->pfdevfreq.current_frequency); } } diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index c47d14eabbae..0cc80da12562 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -28,6 +28,10 @@ #define job_write(dev, reg, data) writel(data, dev->iomem + (reg)) #define job_read(dev, reg) readl(dev->iomem + (reg)) +const char * const panfrost_engine_names[] = { + "fragment", "vertex-tiler", "compute-only" +}; + struct panfrost_queue_state { struct drm_gpu_scheduler sched; u64 fence_context; @@ -846,12 +850,13 @@ int panfrost_job_init(struct panfrost_device *pfdev) .num_rqs = DRM_SCHED_PRIORITY_COUNT, .credit_limit = 2, .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS), - .name = "pan_js", .dev = pfdev->dev, }; struct panfrost_job_slot *js; int ret, j; + BUILD_BUG_ON(ARRAY_SIZE(panfrost_engine_names) != NUM_JOB_SLOTS); + /* All GPUs have two entries per queue, but without jobchain * disambiguation stopping the right job in the close path is tricky, * so let's just advertise one entry in that case. @@ -887,6 +892,7 @@ int panfrost_job_init(struct panfrost_device *pfdev) for (j = 0; j < NUM_JOB_SLOTS; j++) { js->queue[j].fence_context = dma_fence_context_alloc(1); + args.name = panfrost_engine_names[j]; ret = drm_sched_init(&js->queue[j].sched, &args); if (ret) { diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h index 5a30ff1503c6..458666bf684b 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.h +++ b/drivers/gpu/drm/panfrost/panfrost_job.h @@ -53,6 +53,8 @@ struct panfrost_jm_ctx { struct drm_sched_entity slot_entity[NUM_JOB_SLOTS]; }; +extern const char * const panfrost_engine_names[]; + int panfrost_jm_ctx_create(struct drm_file *file, struct drm_panfrost_jm_ctx_create *args); int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle); From 6035258463167e2cda7af64669f925a43e0b7e88 Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hadj Khelifa Date: Tue, 7 Oct 2025 09:32:40 +0100 Subject: [PATCH 066/128] drm/gud: Use kmalloc_array() instead of kmalloc() Replace kmalloc with kmalloc array in drm/gud/gud_pipe.c since the calculation inside kmalloc is dynamic 'width * height' Signed-off-by: Mehdi Ben Hadj Khelifa Acked-by: Ruben Wauters Acked-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://lore.kernel.org/r/20251007083320.29018-1-mehdi.benhadjkhelifa@gmail.com --- drivers/gpu/drm/gud/gud_pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c index 3a208e956dff..76d77a736d84 100644 --- a/drivers/gpu/drm/gud/gud_pipe.c +++ b/drivers/gpu/drm/gud/gud_pipe.c @@ -69,7 +69,7 @@ static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format height = drm_rect_height(rect); len = drm_format_info_min_pitch(format, 0, width) * height; - buf = kmalloc(width * height, GFP_KERNEL); + buf = kmalloc_array(height, width, GFP_KERNEL); if (!buf) return 0; From 5385871282e5c2831c226d32cf2ce26b45a7b164 Mon Sep 17 00:00:00 2001 From: Ruben Wauters Date: Wed, 24 Sep 2025 22:10:32 +0100 Subject: [PATCH 067/128] MAINTAINERS: add myself as maintainer for GUD Since GUD has been without a maintainer for some time, and I have been working on it, This patch adds myself as maintainer for the GUD driver. Signed-off-by: Ruben Wauters Acked-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://lore.kernel.org/r/20250924211557.6687-2-rubenru09@aol.com --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5257d52679d6..c0fc2fb54387 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7628,7 +7628,8 @@ F: Documentation/devicetree/bindings/display/panel/panel-edp.yaml F: drivers/gpu/drm/panel/panel-edp.c DRM DRIVER FOR GENERIC USB DISPLAY -S: Orphan +M: Ruben Wauters +S: Maintained W: https://github.com/notro/gud/wiki T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/gud/ From 1226cd7c7686aee9363ec39a8a80292e27216c6b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 8 Oct 2025 15:11:42 +0200 Subject: [PATCH 068/128] drm/atomic: Change state pointers to a more meaningful name The state pointer found in the struct drm_atomic_state internals for most object is a bit ambiguous, and confusing when those internals also have old state and new state. After the recent cleanups, the state pointer only use is to point to the state we need to free when destroying the atomic state. We can thus rename it something less ambiguous, and hopefully more meaningful. Acked-by: Thomas Zimmermann Link: https://lore.kernel.org/r/20251008-drm-rename-state-v2-1-49b490b2676a@kernel.org Signed-off-by: Maxime Ripard --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 ++-- drivers/gpu/drm/drm_atomic.c | 24 +++++++++---------- drivers/gpu/drm/drm_atomic_helper.c | 8 +++---- include/drm/drm_atomic.h | 16 ++++++------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 0d03e324d5b9..fe41494635c5 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -12450,7 +12450,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, int j = state->num_private_objs-1; dm_atomic_destroy_state(obj, - state->private_objs[i].state); + state->private_objs[i].state_to_destroy); /* If i is not at the end of the array then the * last element needs to be moved to where i was @@ -12461,7 +12461,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, state->private_objs[j]; state->private_objs[j].ptr = NULL; - state->private_objs[j].state = NULL; + state->private_objs[j].state_to_destroy = NULL; state->private_objs[j].old_state = NULL; state->private_objs[j].new_state = NULL; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 0fda567c3900..be2cb6e43cb0 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -207,9 +207,9 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) continue; connector->funcs->atomic_destroy_state(connector, - state->connectors[i].state); + state->connectors[i].state_to_destroy); state->connectors[i].ptr = NULL; - state->connectors[i].state = NULL; + state->connectors[i].state_to_destroy = NULL; state->connectors[i].old_state = NULL; state->connectors[i].new_state = NULL; drm_connector_put(connector); @@ -222,10 +222,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) continue; crtc->funcs->atomic_destroy_state(crtc, - state->crtcs[i].state); + state->crtcs[i].state_to_destroy); state->crtcs[i].ptr = NULL; - state->crtcs[i].state = NULL; + state->crtcs[i].state_to_destroy = NULL; state->crtcs[i].old_state = NULL; state->crtcs[i].new_state = NULL; @@ -242,9 +242,9 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) continue; plane->funcs->atomic_destroy_state(plane, - state->planes[i].state); + state->planes[i].state_to_destroy); state->planes[i].ptr = NULL; - state->planes[i].state = NULL; + state->planes[i].state_to_destroy = NULL; state->planes[i].old_state = NULL; state->planes[i].new_state = NULL; } @@ -253,9 +253,9 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) struct drm_private_obj *obj = state->private_objs[i].ptr; obj->funcs->atomic_destroy_state(obj, - state->private_objs[i].state); + state->private_objs[i].state_to_destroy); state->private_objs[i].ptr = NULL; - state->private_objs[i].state = NULL; + state->private_objs[i].state_to_destroy = NULL; state->private_objs[i].old_state = NULL; state->private_objs[i].new_state = NULL; } @@ -361,7 +361,7 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, if (!crtc_state) return ERR_PTR(-ENOMEM); - state->crtcs[index].state = crtc_state; + state->crtcs[index].state_to_destroy = crtc_state; state->crtcs[index].old_state = crtc->state; state->crtcs[index].new_state = crtc_state; state->crtcs[index].ptr = crtc; @@ -546,7 +546,7 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, if (!plane_state) return ERR_PTR(-ENOMEM); - state->planes[index].state = plane_state; + state->planes[index].state_to_destroy = plane_state; state->planes[index].ptr = plane; state->planes[index].old_state = plane->state; state->planes[index].new_state = plane_state; @@ -858,7 +858,7 @@ drm_atomic_get_private_obj_state(struct drm_atomic_state *state, if (!obj_state) return ERR_PTR(-ENOMEM); - state->private_objs[index].state = obj_state; + state->private_objs[index].state_to_destroy = obj_state; state->private_objs[index].old_state = obj->state; state->private_objs[index].new_state = obj_state; state->private_objs[index].ptr = obj; @@ -1161,7 +1161,7 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, return ERR_PTR(-ENOMEM); drm_connector_get(connector); - state->connectors[index].state = connector_state; + state->connectors[index].state_to_destroy = connector_state; state->connectors[index].old_state = connector->state; state->connectors[index].new_state = connector_state; state->connectors[index].ptr = connector; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index d5ebe6ea0acb..5a473a274ff0 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3236,7 +3236,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state, old_conn_state->state = state; new_conn_state->state = NULL; - state->connectors[i].state = old_conn_state; + state->connectors[i].state_to_destroy = old_conn_state; connector->state = new_conn_state; } @@ -3246,7 +3246,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state, old_crtc_state->state = state; new_crtc_state->state = NULL; - state->crtcs[i].state = old_crtc_state; + state->crtcs[i].state_to_destroy = old_crtc_state; crtc->state = new_crtc_state; if (new_crtc_state->commit) { @@ -3266,7 +3266,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state, old_plane_state->state = state; new_plane_state->state = NULL; - state->planes[i].state = old_plane_state; + state->planes[i].state_to_destroy = old_plane_state; plane->state = new_plane_state; } drm_panic_unlock(state->dev, flags); @@ -3277,7 +3277,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state, old_obj_state->state = state; new_obj_state->state = NULL; - state->private_objs[i].state = old_obj_state; + state->private_objs[i].state_to_destroy = old_obj_state; obj->state = new_obj_state; } diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index c8ab2163bf65..155e82f87e4d 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -161,7 +161,7 @@ struct __drm_planes_state { struct drm_plane *ptr; /** - * @state: + * @state_to_destroy: * * Used to track the @drm_plane_state we will need to free when * tearing down the associated &drm_atomic_state in @@ -173,7 +173,7 @@ struct __drm_planes_state { * the same state than @new_state. After a commit, it points to * the same state than @old_state. */ - struct drm_plane_state *state; + struct drm_plane_state *state_to_destroy; struct drm_plane_state *old_state, *new_state; }; @@ -182,7 +182,7 @@ struct __drm_crtcs_state { struct drm_crtc *ptr; /** - * @state: + * @state_to_destroy: * * Used to track the @drm_crtc_state we will need to free when * tearing down the associated &drm_atomic_state in @@ -194,7 +194,7 @@ struct __drm_crtcs_state { * the same state than @new_state. After a commit, it points to * the same state than @old_state. */ - struct drm_crtc_state *state; + struct drm_crtc_state *state_to_destroy; struct drm_crtc_state *old_state, *new_state; @@ -216,7 +216,7 @@ struct __drm_connnectors_state { struct drm_connector *ptr; /** - * @state: + * @state_to_destroy: * * Used to track the @drm_connector_state we will need to free * when tearing down the associated &drm_atomic_state in @@ -228,7 +228,7 @@ struct __drm_connnectors_state { * the same state than @new_state. After a commit, it points to * the same state than @old_state. */ - struct drm_connector_state *state; + struct drm_connector_state *state_to_destroy; struct drm_connector_state *old_state, *new_state; @@ -393,7 +393,7 @@ struct __drm_private_objs_state { struct drm_private_obj *ptr; /** - * @state: + * @state_to_destroy: * * Used to track the @drm_private_state we will need to free * when tearing down the associated &drm_atomic_state in @@ -405,7 +405,7 @@ struct __drm_private_objs_state { * the same state than @new_state. After a commit, it points to * the same state than @old_state. */ - struct drm_private_state *state; + struct drm_private_state *state_to_destroy; struct drm_private_state *old_state, *new_state; }; From dba5f91829aef3ab1b3bdd7933d544a397aa9af5 Mon Sep 17 00:00:00 2001 From: Pranjal Ramajor Asha Kanojiya Date: Tue, 7 Oct 2025 07:38:53 +0200 Subject: [PATCH 069/128] accel/qaic: Add support to export dmabuf fd Add support to export BO as DMABUF to enable userspace to reuse buffers and reduce number of copy. Signed-off-by: Pranjal Ramajor Asha Kanojiya Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007053853.193608-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/qaic_data.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index adabc4028bb2..85c155c74fca 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -644,8 +644,36 @@ static void qaic_free_object(struct drm_gem_object *obj) kfree(bo); } +static struct sg_table *qaic_get_sg_table(struct drm_gem_object *obj) +{ + struct qaic_bo *bo = to_qaic_bo(obj); + struct scatterlist *sg, *sg_in; + struct sg_table *sgt, *sgt_in; + int i; + + sgt_in = bo->sgt; + + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return ERR_PTR(-ENOMEM); + + if (sg_alloc_table(sgt, sgt_in->orig_nents, GFP_KERNEL)) { + kfree(sgt); + return ERR_PTR(-ENOMEM); + } + + sg = sgt->sgl; + for_each_sgtable_sg(sgt_in, sg_in, i) { + memcpy(sg, sg_in, sizeof(*sg)); + sg = sg_next(sg); + } + + return sgt; +} + static const struct drm_gem_object_funcs qaic_gem_funcs = { .free = qaic_free_object, + .get_sg_table = qaic_get_sg_table, .print_info = qaic_gem_print_info, .mmap = qaic_gem_object_mmap, .vm_ops = &drm_vm_ops, From 74e2c12ea287089f093a1e37fd2b8b8c7dd41c9f Mon Sep 17 00:00:00 2001 From: "Jesse.Zhang" Date: Mon, 13 Oct 2025 16:58:25 +0800 Subject: [PATCH 070/128] drm/ttm: Add safety check for NULL man->bdev in ttm_resource_manager_usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `ttm_resource_manager_usage()` function currently assumes `man->bdev` is non-NULL when accessing `man->bdev->lru_lock`. However, in scenarios where the resource manager is not fully initialized (e.g., APU platforms that lack dedicated VRAM, or incomplete manager setup), `man->bdev` may remain NULL. This leads to a NULL pointer dereference when attempting to acquire the `lru_lock`, triggering kernel OOPS. Fix this by adding an explicit safety check for `man->bdev` before accessing its members: - Use `WARN_ON_ONCE(!man->bdev)` to emit a one-time warning (a soft assertion) when `man->bdev` is NULL. This helps catch invalid usage patterns during debugging without breaking production workflows. - Return 0 immediately if `man->bdev` is NULL, as a non-initialized manager cannot have valid resource usage to report. Suggested-by: Christian König Suggested-by: Lijo Lazar Signed-off-by: Jesse Zhang Reviewed-by: Christian König Signed-off-by: Arunpravin Paneer Selvam Link: https://lore.kernel.org/r/20251013085849.1735612-1-Jesse.Zhang@amd.com --- drivers/gpu/drm/ttm/ttm_resource.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index e2c82ad07eb4..d93d1bef6768 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -587,6 +587,9 @@ uint64_t ttm_resource_manager_usage(struct ttm_resource_manager *man) { uint64_t usage; + if (WARN_ON_ONCE(!man->bdev)) + return 0; + spin_lock(&man->bdev->lru_lock); usage = man->usage; spin_unlock(&man->bdev->lru_lock); From 3ab602de769b318b4ce8db87df184eb84426b098 Mon Sep 17 00:00:00 2001 From: Manikandan Muralidharan Date: Mon, 14 Oct 2024 12:16:44 +0530 Subject: [PATCH 071/128] drm: atmel-hlcdc: update the LCDC_ATTRE register in plane atomic_disable update the LCDC_ATTRE register in drm plane atomic_disable to handle the configuration changes of each layer when a plane is disabled. Reviewed-by: Nicolas Ferre Link: https://lore.kernel.org/r/20241014064644.292943-1-manikandan.m@microchip.com Signed-off-by: Manikandan Muralidharan --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 3 ++- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index e1a0bb24b511..53d47f01db0b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -378,7 +378,8 @@ struct atmel_lcdc_dc_ops { void (*lcdc_update_buffers)(struct atmel_hlcdc_plane *plane, struct atmel_hlcdc_plane_state *state, u32 sr, int i); - void (*lcdc_atomic_disable)(struct atmel_hlcdc_plane *plane); + void (*lcdc_atomic_disable)(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_dc *dc); void (*lcdc_update_general_settings)(struct atmel_hlcdc_plane *plane, struct atmel_hlcdc_plane_state *state); void (*lcdc_atomic_update)(struct atmel_hlcdc_plane *plane, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 6dd38018b47f..eee042685559 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -816,7 +816,8 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p, return 0; } -static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane) +static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_dc *dc) { /* Disable interrupts */ atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR, @@ -832,7 +833,8 @@ static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane) atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); } -static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane) +static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_dc *dc) { /* Disable interrupts */ atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IDR, @@ -842,6 +844,15 @@ static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane) atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_ENR, 0); + /* + * Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN, + * (where xxx indicates each layer) requires writing one to the + * Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7. + */ + regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE | + ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE | + ATMEL_XLCDC_HEO_UPDATE); + /* Clear all pending interrupts */ atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR); } @@ -852,7 +863,7 @@ static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private; - dc->desc->ops->lcdc_atomic_disable(plane); + dc->desc->ops->lcdc_atomic_disable(plane, dc); } static void atmel_hlcdc_atomic_update(struct atmel_hlcdc_plane *plane, From a312acdcec57b3955fbf1f3057c13a6d38e4aa2a Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Mon, 14 Oct 2024 15:19:42 +0530 Subject: [PATCH 072/128] drm: atmel-hlcdc: fix atmel_xlcdc_plane_setup_scaler() On SoCs, like the SAM9X75, which embed the XLCDC ip, the registers that configure the unified scaling engine were not filled with proper values. Indeed, for YCbCr formats, the VXSCFACT bitfield of the HEOCFG25 register and the HXSCFACT bitfield of the HEOCFG27 register were incorrect. For 4:2:0 formats, both vertical and horizontal factors for chroma chanels should be divided by 2 from the factors for the luma channel. Hence: HEOCFG24.VXSYFACT = VFACTOR HEOCFG25.VSXCFACT = VFACTOR / 2 HEOCFG26.HXSYFACT = HFACTOR HEOCFG27.HXSCFACT = HFACTOR / 2 However, for 4:2:2 formats, only the horizontal factor for chroma chanels should be divided by 2 from the factor for the luma channel; the vertical factor is the same for all the luma and chroma channels. Hence: HEOCFG24.VXSYFACT = VFACTOR HEOCFG25.VXSCFACT = VFACTOR HEOCFG26.HXSYFACT = HFACTOR HEOCFG27.HXSCFACT = HFACTOR / 2 Fixes: d498771b0b83 ("drm: atmel_hlcdc: Add support for XLCDC using IP specific driver ops") Signed-off-by: Cyrille Pitchen Reviewed-by: Dmitry Baryshkov Acked-by: Nicolas Ferre Link: https://lore.kernel.org/r/20241014094942.325211-1-manikandan.m@microchip.com Signed-off-by: Manikandan Muralidharan --- .../gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index eee042685559..38f60befd7d7 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -365,13 +365,34 @@ void atmel_xlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane, xfactor); /* - * With YCbCr 4:2:2 and YCbYcr 4:2:0 window resampling, configuration - * register LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT is half + * With YCbCr 4:2:0 window resampling, configuration register + * LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT values are half * the value of yfactor and xfactor. + * + * On the other hand, with YCbCr 4:2:2 window resampling, only the + * configuration register LCDC_HEOCFG27.HXSCFACT value is half the value + * of the xfactor; the value of LCDC_HEOCFG25.VXSCFACT is yfactor (no + * division by 2). */ - if (state->base.fb->format->format == DRM_FORMAT_YUV420) { + switch (state->base.fb->format->format) { + /* YCbCr 4:2:2 */ + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_NV61: + xfactor /= 2; + break; + + /* YCbCr 4:2:0 */ + case DRM_FORMAT_YUV420: + case DRM_FORMAT_NV21: yfactor /= 2; xfactor /= 2; + break; + default: + break; } atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 2, From 0dc25a11b8936bb752d6217f9db9857641c3d266 Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Sun, 28 Sep 2025 18:37:34 +0800 Subject: [PATCH 073/128] drm/rockchip: analogix_dp: Apply devm_clk_get_optional() for &rockchip_dp_device.grfclk The "grf" clock is optional for Rockchip eDP controller(RK3399 needs while RK3288 and RK3588 do not). It can make the code more concise to use devm_clk_get_optional() instead of devm_clk_get() with extra checks. In addtion, DRM_DEV_ERROR() is replaced by dev_err_probe(). Signed-off-by: Damon Ding Reviewed-by: Sebastian Reichel Signed-off-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250928103734.4007257-1-damon.ding@rock-chips.com --- drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index d30f0983a53a..937f83cf42fc 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -335,15 +335,9 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp) return PTR_ERR(dp->grf); } - dp->grfclk = devm_clk_get(dev, "grf"); - if (PTR_ERR(dp->grfclk) == -ENOENT) { - dp->grfclk = NULL; - } else if (PTR_ERR(dp->grfclk) == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (IS_ERR(dp->grfclk)) { - DRM_DEV_ERROR(dev, "failed to get grf clock\n"); - return PTR_ERR(dp->grfclk); - } + dp->grfclk = devm_clk_get_optional(dev, "grf"); + if (IS_ERR(dp->grfclk)) + return dev_err_probe(dev, PTR_ERR(dp->grfclk), "failed to get grf clock\n"); dp->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(dp->pclk)) { From 2b756d321bf9f4e93437198d212c6ccec137b295 Mon Sep 17 00:00:00 2001 From: WeiHao Li Date: Fri, 5 Sep 2025 10:56:32 +0800 Subject: [PATCH 074/128] dt-bindings: display: rockchip,dw-mipi-dsi: Document RK3368 DSI Document the MIPI DSI controller for Rockchip RK3368. Signed-off-by: WeiHao Li Acked-by: Rob Herring (Arm) Signed-off-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250905025632.222422-9-cn.liweihao@gmail.com --- .../bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml index c59df3c1a3f7..632b48bfabb9 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml @@ -17,6 +17,7 @@ properties: - rockchip,px30-mipi-dsi - rockchip,rk3128-mipi-dsi - rockchip,rk3288-mipi-dsi + - rockchip,rk3368-mipi-dsi - rockchip,rk3399-mipi-dsi - rockchip,rk3568-mipi-dsi - rockchip,rv1126-mipi-dsi @@ -73,6 +74,7 @@ allOf: enum: - rockchip,px30-mipi-dsi - rockchip,rk3128-mipi-dsi + - rockchip,rk3368-mipi-dsi - rockchip,rk3568-mipi-dsi - rockchip,rv1126-mipi-dsi From 6dd6949c76afbec037a66e6b9bcb6e2c5dee933e Mon Sep 17 00:00:00 2001 From: WeiHao Li Date: Fri, 5 Sep 2025 10:56:25 +0800 Subject: [PATCH 075/128] drm/rockchip: dsi: Add support for RK3368 RK3368 has DesignWare MIPI DSI controller and an external inno D-PHY. Signed-off-by: WeiHao Li Signed-off-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250905025632.222422-2-cn.liweihao@gmail.com --- .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 5523911b990d..de8405ee8241 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -163,6 +163,11 @@ #define RK3288_DSI0_LCDC_SEL BIT(6) #define RK3288_DSI1_LCDC_SEL BIT(9) +#define RK3368_GRF_SOC_CON7 0x41c +#define RK3368_DSI_FORCETXSTOPMODE (0xf << 7) +#define RK3368_DSI_FORCERXMODE BIT(6) +#define RK3368_DSI_TURNDISABLE BIT(5) + #define RK3399_GRF_SOC_CON20 0x6250 #define RK3399_DSI0_LCDC_SEL BIT(0) #define RK3399_DSI1_LCDC_SEL BIT(4) @@ -1528,6 +1533,18 @@ static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = { { /* sentinel */ } }; +static const struct rockchip_dw_dsi_chip_data rk3368_chip_data[] = { + { + .reg = 0xff960000, + .lanecfg1_grf_reg = RK3368_GRF_SOC_CON7, + .lanecfg1 = FIELD_PREP_WM16_CONST((RK3368_DSI_TURNDISABLE | + RK3368_DSI_FORCETXSTOPMODE | + RK3368_DSI_FORCERXMODE), 0), + .max_data_lanes = 4, + }, + { /* sentinel */ } +}; + static int rk3399_dphy_tx1rx1_init(struct phy *phy) { struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy); @@ -1687,6 +1704,9 @@ static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = { }, { .compatible = "rockchip,rk3288-mipi-dsi", .data = &rk3288_chip_data, + }, { + .compatible = "rockchip,rk3368-mipi-dsi", + .data = &rk3368_chip_data, }, { .compatible = "rockchip,rk3399-mipi-dsi", .data = &rk3399_chip_data, From 8e944ab8196e421f20386f51c5ffc43baa145932 Mon Sep 17 00:00:00 2001 From: WeiHao Li Date: Fri, 5 Sep 2025 10:56:26 +0800 Subject: [PATCH 076/128] drm/rockchip: vop: add lut_size for RK3368 vop_data VOP driver need a correct lut_size to work normally. According to rockchip downstream kernel source [1], the lut_size is 0x400. [1] https://github.com/rockchip-linux/kernel/blob/develop-4.4/arch/arm64/boot/dts/rockchip/rk3368.dtsi#L1497 Signed-off-by: WeiHao Li Signed-off-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250905025632.222422-3-cn.liweihao@gmail.com --- drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index d1f788763318..219f8c2fa88e 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -880,6 +880,7 @@ static const struct vop_data rk3368_vop = { .win = rk3368_vop_win_data, .win_size = ARRAY_SIZE(rk3368_vop_win_data), .max_output = { 4096, 2160 }, + .lut_size = 1024, }; static const struct vop_intr rk3366_vop_intr = { From f6d9329aefe2829aaa95feb6bbdcd3cbe32900f9 Mon Sep 17 00:00:00 2001 From: Youssef Samir Date: Tue, 7 Oct 2025 14:18:45 +0200 Subject: [PATCH 077/128] accel/qaic: Use kvcalloc() for slice requests allocation When a BO is created, qaic will use the page allocator to request the memory chunks that the BO will be composed of in-memory. The number of chunks increases when memory is segmented. For example, a 16MB BO can be composed of four 4MB chunks or 4096 4KB chunks. A BO is then sliced into a single or multiple slices to be transferred to the device on the DBC's xfer queue. For that to happen, the slice needs to encode its memory chunks into DBC requests and keep track of them in an array, which is allocated using kcalloc(). Knowing that the BO might be very fragmented, this array can grow so large that the allocation may fail to find contiguous memory for it. Replace kcalloc() with kvcalloc() to allocate the DBC requests array for a slice. Signed-off-by: Youssef Samir Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007121845.337382-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/qaic_data.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 85c155c74fca..42feb7509ca9 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -166,7 +166,7 @@ static void free_slice(struct kref *kref) drm_gem_object_put(&slice->bo->base); sg_free_table(slice->sgt); kfree(slice->sgt); - kfree(slice->reqs); + kvfree(slice->reqs); kfree(slice); } @@ -405,7 +405,7 @@ static int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo, goto free_sgt; } - slice->reqs = kcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL); + slice->reqs = kvcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL); if (!slice->reqs) { ret = -ENOMEM; goto free_slice; @@ -431,7 +431,7 @@ static int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo, return 0; free_req: - kfree(slice->reqs); + kvfree(slice->reqs); free_slice: kfree(slice); free_sgt: From 5f63e033874a03b1773eaa140809241aa0be6f77 Mon Sep 17 00:00:00 2001 From: Zack McKevitt Date: Tue, 7 Oct 2025 17:45:25 +0200 Subject: [PATCH 078/128] accel/qaic: Include signal.h in qaic_control.c Include linux/sched/signal.h in qaic_control.c to avoid implicit inclusion of signal_pending(). Signed-off-by: Zack McKevitt Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007154525.415039-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/qaic_control.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index d8bdab69f800..e47a48de7876 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include From 754fcd22d18026fcf0fec0e07ab095f84cd941fd Mon Sep 17 00:00:00 2001 From: Youssef Samir Date: Tue, 7 Oct 2025 18:11:48 +0200 Subject: [PATCH 079/128] accel/qaic: Remove redundant retry_count = 0 statement If msg_xfer() is called and the channel ring does not have enough room to accommodate the whole message, the function sleeps and tries again. It uses retry_count to keep track of the number of retrials done. This variable is not used after the space check succeeds. So, remove the retry_count = 0 statement used later in the function. Signed-off-by: Youssef Samir Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007161148.422744-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/qaic_control.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index e47a48de7876..a51a808aa2e8 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -1080,7 +1080,6 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u list_for_each_entry(w, &wrappers->list, list) { kref_get(&w->ref_count); - retry_count = 0; ret = mhi_queue_buf(qdev->cntl_ch, DMA_TO_DEVICE, &w->msg, w->len, list_is_last(&w->list, &wrappers->list) ? MHI_EOT : MHI_CHAIN); if (ret) { From 8134da2c9f9c803ae4027e5612b7226ed257da3c Mon Sep 17 00:00:00 2001 From: Aswin Venkatesan Date: Tue, 7 Oct 2025 19:01:30 +0200 Subject: [PATCH 080/128] accel/qaic: Fix incorrect error return path Found via code inspection that when encode_message() fails in the middle of processing, instead of returning the actual error code, it always returns -EINVAL. This is because the entire message length has not been processed, and the error code is set to -EINVAL. Instead, take the 'out' path on failure to return the actual error code. Signed-off-by: Aswin Venkatesan Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007170130.445878-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/qaic_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index a51a808aa2e8..7566c6efffa2 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -811,7 +811,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg, } if (ret) - break; + goto out; } if (user_len != user_msg->len) From 4981c2a0663beea1affdf77714e1c4afee9cc91b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 14 Oct 2025 11:36:28 +0200 Subject: [PATCH 081/128] MAINTAINERS: Remove myself for sun4i DRM driver I haven't been involved with the driver for a while. Let's reflect that. Acked-by: Jernej Skrabec Link: https://lore.kernel.org/r/20251014093628.564265-1-mripard@kernel.org Signed-off-by: Maxime Ripard --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5980801ddc94..88e7672c1d41 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7636,7 +7636,6 @@ F: drivers/accel/ F: include/drm/drm_accel.h DRM DRIVER FOR ALLWINNER DE2 AND DE3 ENGINE -M: Maxime Ripard M: Chen-Yu Tsai R: Jernej Skrabec L: dri-devel@lists.freedesktop.org @@ -8251,7 +8250,6 @@ F: drivers/gpu/nova-core/ F: rust/kernel/drm/ DRM DRIVERS FOR ALLWINNER A10 -M: Maxime Ripard M: Chen-Yu Tsai L: dri-devel@lists.freedesktop.org S: Supported From 6bee90901fb16f99808da8423a976a99aef4d6c9 Mon Sep 17 00:00:00 2001 From: Carl Vanderlip Date: Tue, 7 Oct 2025 19:42:18 +0200 Subject: [PATCH 082/128] accel/qaic: Use overflow check function instead of division Division is an expensive operation. Overflow check functions exist already. Use existing overflow check functions rather than dividing to check for overflow. Signed-off-by: Carl Vanderlip Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007174218.469867-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/qaic_control.c | 5 +++-- drivers/accel/qaic/qaic_data.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index 7566c6efffa2..82b38b9b81f7 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -656,8 +656,9 @@ static int encode_activate(struct qaic_device *qdev, void *trans, struct wrapper return -EINVAL; nelem = in_trans->queue_size; - size = (get_dbc_req_elem_size() + get_dbc_rsp_elem_size()) * nelem; - if (size / nelem != get_dbc_req_elem_size() + get_dbc_rsp_elem_size()) + if (check_mul_overflow((u32)(get_dbc_req_elem_size() + get_dbc_rsp_elem_size()), + nelem, + &size)) return -EINVAL; if (size + QAIC_DBC_Q_GAP + QAIC_DBC_Q_BUF_ALIGN < size) diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 42feb7509ca9..45f065488fdb 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -982,8 +982,9 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi if (args->hdr.count == 0) return -EINVAL; - arg_size = args->hdr.count * sizeof(*slice_ent); - if (arg_size / args->hdr.count != sizeof(*slice_ent)) + if (check_mul_overflow((unsigned long)args->hdr.count, + (unsigned long)sizeof(*slice_ent), + &arg_size)) return -EINVAL; if (!(args->hdr.dir == DMA_TO_DEVICE || args->hdr.dir == DMA_FROM_DEVICE)) From 4ddf4ddfceb40c8656600757c94d6ce80af8ad19 Mon Sep 17 00:00:00 2001 From: Youssef Samir Date: Wed, 8 Oct 2025 00:12:12 +0200 Subject: [PATCH 083/128] accel/qaic: Ensure entry belongs to DBC in qaic_perf_stats_bo_ioctl() struct qaic_perf_stats is defined to have a DBC specified in the header, followed by struct qaic_perf_stats_entry instances, each pointing to a BO that is associated with the DBC. Currently, qaic_perf_stats_bo_ioctl() does not check if the entries belong to the DBC specified in the header. Therefore, add checks to ensure that each entry in the request is sliced and belongs to hdr.dbc_id. Co-developed-by: Carl Vanderlip Signed-off-by: Carl Vanderlip Signed-off-by: Youssef Samir Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007221212.559474-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/qaic_data.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 45f065488fdb..8ac2761e763f 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -1789,6 +1789,16 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file goto free_ent; } bo = to_qaic_bo(obj); + if (!bo->sliced) { + drm_gem_object_put(obj); + ret = -EINVAL; + goto free_ent; + } + if (bo->dbc->id != args->hdr.dbc_id) { + drm_gem_object_put(obj); + ret = -EINVAL; + goto free_ent; + } /* * perf stats ioctl is called before wait ioctl is complete then * the latency information is invalid. From 7fb19ea1ec6aa85c75905b1fd732d50801e7fb28 Mon Sep 17 00:00:00 2001 From: Jeff Hugo Date: Wed, 8 Oct 2025 00:40:45 +0200 Subject: [PATCH 084/128] accel/qaic: Support the new READ_DATA implementation AIC200 uses the newer "XBL" firmware implementation which changes the expectations of how READ_DATA is performed. Larger data requests are supported via streaming the data over the transport instead of requiring a single transport transfer for everything. Co-developed-by: Carl Vanderlip Signed-off-by: Carl Vanderlip Signed-off-by: Jeff Hugo Signed-off-by: Youssef Samir Reviewed-by: Jeff Hugo Reviewed-by: Carl Vanderlip Signed-off-by: Jeff Hugo Link: https://lore.kernel.org/r/20251007224045.605374-1-youssef.abdulrahman@oss.qualcomm.com --- drivers/accel/qaic/sahara.c | 144 +++++++++++++++++++++++++++--------- 1 file changed, 107 insertions(+), 37 deletions(-) diff --git a/drivers/accel/qaic/sahara.c b/drivers/accel/qaic/sahara.c index 3ebcc1f7ff58..b126cca937a9 100644 --- a/drivers/accel/qaic/sahara.c +++ b/drivers/accel/qaic/sahara.c @@ -159,6 +159,7 @@ struct sahara_context { struct sahara_packet *rx; struct work_struct fw_work; struct work_struct dump_work; + struct work_struct read_data_work; struct mhi_device *mhi_dev; const char * const *image_table; u32 table_size; @@ -174,7 +175,10 @@ struct sahara_context { u64 dump_image_offset; void *mem_dump_freespace; u64 dump_images_left; + u32 read_data_offset; + u32 read_data_length; bool is_mem_dump_mode; + bool non_streaming; }; static const char * const aic100_image_table[] = { @@ -216,6 +220,11 @@ static const char * const aic200_image_table[] = { [75] = "qcom/aic200/pvs.bin", }; +static bool is_streaming(struct sahara_context *context) +{ + return !context->non_streaming; +} + static int sahara_find_image(struct sahara_context *context, u32 image_id) { int ret; @@ -265,6 +274,8 @@ static void sahara_send_reset(struct sahara_context *context) int ret; context->is_mem_dump_mode = false; + context->read_data_offset = 0; + context->read_data_length = 0; context->tx[0]->cmd = cpu_to_le32(SAHARA_RESET_CMD); context->tx[0]->length = cpu_to_le32(SAHARA_RESET_LENGTH); @@ -319,9 +330,39 @@ static void sahara_hello(struct sahara_context *context) dev_err(&context->mhi_dev->dev, "Unable to send hello response %d\n", ret); } +static int read_data_helper(struct sahara_context *context, int buf_index) +{ + enum mhi_flags mhi_flag; + u32 pkt_data_len; + int ret; + + pkt_data_len = min(context->read_data_length, SAHARA_PACKET_MAX_SIZE); + + memcpy(context->tx[buf_index], + &context->firmware->data[context->read_data_offset], + pkt_data_len); + + context->read_data_offset += pkt_data_len; + context->read_data_length -= pkt_data_len; + + if (is_streaming(context) || !context->read_data_length) + mhi_flag = MHI_EOT; + else + mhi_flag = MHI_CHAIN; + + ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, + context->tx[buf_index], pkt_data_len, mhi_flag); + if (ret) { + dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n", ret); + return ret; + } + + return 0; +} + static void sahara_read_data(struct sahara_context *context) { - u32 image_id, data_offset, data_len, pkt_data_len; + u32 image_id, data_offset, data_len; int ret; int i; @@ -357,7 +398,7 @@ static void sahara_read_data(struct sahara_context *context) * and is not needed here on error. */ - if (data_len > SAHARA_TRANSFER_MAX_SIZE) { + if (context->non_streaming && data_len > SAHARA_TRANSFER_MAX_SIZE) { dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data len %d exceeds max xfer size %d\n", data_len, SAHARA_TRANSFER_MAX_SIZE); sahara_send_reset(context); @@ -378,22 +419,18 @@ static void sahara_read_data(struct sahara_context *context) return; } - for (i = 0; i < SAHARA_NUM_TX_BUF && data_len; ++i) { - pkt_data_len = min(data_len, SAHARA_PACKET_MAX_SIZE); + context->read_data_offset = data_offset; + context->read_data_length = data_len; - memcpy(context->tx[i], &context->firmware->data[data_offset], pkt_data_len); + if (is_streaming(context)) { + schedule_work(&context->read_data_work); + return; + } - data_offset += pkt_data_len; - data_len -= pkt_data_len; - - ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, - context->tx[i], pkt_data_len, - !data_len ? MHI_EOT : MHI_CHAIN); - if (ret) { - dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n", - ret); - return; - } + for (i = 0; i < SAHARA_NUM_TX_BUF && context->read_data_length; ++i) { + ret = read_data_helper(context, i); + if (ret) + break; } } @@ -742,6 +779,13 @@ error: sahara_send_reset(context); } +static void sahara_read_data_processing(struct work_struct *work) +{ + struct sahara_context *context = container_of(work, struct sahara_context, read_data_work); + + read_data_helper(context, 0); +} + static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) { struct sahara_context *context; @@ -756,35 +800,57 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_ if (!context->rx) return -ENOMEM; - /* - * AIC100 defines SAHARA_TRANSFER_MAX_SIZE as the largest value it - * will request for READ_DATA. This is larger than - * SAHARA_PACKET_MAX_SIZE, and we need 9x SAHARA_PACKET_MAX_SIZE to - * cover SAHARA_TRANSFER_MAX_SIZE. When the remote side issues a - * READ_DATA, it requires a transfer of the exact size requested. We - * can use MHI_CHAIN to link multiple buffers into a single transfer - * but the remote side will not consume the buffers until it sees an - * EOT, thus we need to allocate enough buffers to put in the tx fifo - * to cover an entire READ_DATA request of the max size. - */ - for (i = 0; i < SAHARA_NUM_TX_BUF; ++i) { - context->tx[i] = devm_kzalloc(&mhi_dev->dev, SAHARA_PACKET_MAX_SIZE, GFP_KERNEL); - if (!context->tx[i]) - return -ENOMEM; - } - - context->mhi_dev = mhi_dev; - INIT_WORK(&context->fw_work, sahara_processing); - INIT_WORK(&context->dump_work, sahara_dump_processing); - if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) { context->image_table = aic200_image_table; context->table_size = ARRAY_SIZE(aic200_image_table); } else { context->image_table = aic100_image_table; context->table_size = ARRAY_SIZE(aic100_image_table); + context->non_streaming = true; } + /* + * There are two firmware implementations for READ_DATA handling. + * The older "SBL" implementation defines a Sahara transfer size, and + * expects that the response is a single transport transfer. If the + * FW wants to transfer a file that is larger than the transfer size, + * the FW will issue multiple READ_DATA commands. For this + * implementation, we need to allocate enough buffers to contain the + * entire Sahara transfer size. + * + * The newer "XBL" implementation does not define a maximum transfer + * size and instead expects the data to be streamed over using the + * transport level MTU. The FW will issue a single READ_DATA command + * of whatever size, and consume multiple transport level transfers + * until the expected amount of data is consumed. For this + * implementation we only need a single buffer of the transport MTU + * but we'll need to be able to use it multiple times for a single + * READ_DATA request. + * + * AIC100 is the SBL implementation and defines SAHARA_TRANSFER_MAX_SIZE + * and we need 9x SAHARA_PACKET_MAX_SIZE to cover that. We can use + * MHI_CHAIN to link multiple buffers into a single transfer but the + * remote side will not consume the buffers until it sees an EOT, thus + * we need to allocate enough buffers to put in the tx fifo to cover an + * entire READ_DATA request of the max size. + * + * AIC200 is the XBL implementation, and so a single buffer will work. + */ + for (i = 0; i < SAHARA_NUM_TX_BUF; ++i) { + context->tx[i] = devm_kzalloc(&mhi_dev->dev, + SAHARA_PACKET_MAX_SIZE, + GFP_KERNEL); + if (!context->tx[i]) + return -ENOMEM; + if (is_streaming(context)) + break; + } + + context->mhi_dev = mhi_dev; + INIT_WORK(&context->fw_work, sahara_processing); + INIT_WORK(&context->dump_work, sahara_dump_processing); + INIT_WORK(&context->read_data_work, sahara_read_data_processing); + context->active_image_id = SAHARA_IMAGE_ID_NONE; dev_set_drvdata(&mhi_dev->dev, context); @@ -814,6 +880,10 @@ static void sahara_mhi_remove(struct mhi_device *mhi_dev) static void sahara_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) { + struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev); + + if (!mhi_result->transaction_status && context->read_data_length && is_streaming(context)) + schedule_work(&context->read_data_work); } static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) From 63c7870fab67b2ab2bfe75e8b46f3c37b88c47a8 Mon Sep 17 00:00:00 2001 From: "Wludzik, Jozef" Date: Tue, 14 Oct 2025 09:17:25 +0200 Subject: [PATCH 085/128] accel/ivpu: Fix race condition when mapping dmabuf Fix a race that can occur when multiple jobs submit the same dmabuf. This could cause the sg_table to be mapped twice, leading to undefined behavior. Fixes: e0c0891cd63b ("accel/ivpu: Rework bind/unbind of imported buffers") Signed-off-by: Wludzik, Jozef Reviewed-by: Jeff Hugo Signed-off-by: Karol Wachowski Link: https://lore.kernel.org/r/20251014071725.3047287-1-karol.wachowski@linux.intel.com --- drivers/accel/ivpu/ivpu_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c index e9830ad48d4b..e7277e02840a 100644 --- a/drivers/accel/ivpu/ivpu_gem.c +++ b/drivers/accel/ivpu/ivpu_gem.c @@ -46,12 +46,13 @@ static inline void ivpu_bo_unlock(struct ivpu_bo *bo) static struct sg_table *ivpu_bo_map_attachment(struct ivpu_device *vdev, struct ivpu_bo *bo) { - struct sg_table *sgt = bo->base.sgt; + struct sg_table *sgt; drm_WARN_ON(&vdev->drm, !bo->base.base.import_attach); ivpu_bo_lock(bo); + sgt = bo->base.sgt; if (!sgt) { sgt = dma_buf_map_attachment(bo->base.base.import_attach, DMA_BIDIRECTIONAL); if (IS_ERR(sgt)) From b076ed3017529ec528f2f28b8f37242a6a29a4b0 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Thu, 2 Oct 2025 16:57:35 -0700 Subject: [PATCH 086/128] MAINTAINERS: Update Jessica Zhang's email address My current email will stop working soon. Update my email address to jesszhan0024@gmail.com Signed-off-by: Jessica Zhang Reviewed-by: Dmitry Baryshkov Acked-by: Neil Armstrong Signed-off-by: Neil Armstrong Link: https://lore.kernel.org/r/20251002-quit-qcom-v1-1-0898a63ffddd@oss.qualcomm.com --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 88e7672c1d41..f9f985c7d747 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7887,7 +7887,7 @@ DRM DRIVER for Qualcomm display hardware M: Rob Clark M: Dmitry Baryshkov R: Abhinav Kumar -R: Jessica Zhang +R: Jessica Zhang R: Sean Paul R: Marijn Suijten L: linux-arm-msm@vger.kernel.org @@ -8599,7 +8599,7 @@ F: drivers/gpu/drm/clients/drm_log.c DRM PANEL DRIVERS M: Neil Armstrong -R: Jessica Zhang +R: Jessica Zhang L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git From 4b3374d790567e5eb3c83f1f70c396ddc5da603d Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 7 Oct 2025 16:54:42 +0200 Subject: [PATCH 087/128] drm/ast: Store DRAM clock table in struct ast_device Init the new field dclk_table in struct ast_device to the per-gen table of DRAM clock parameters. Use the field during modesetting. The table is static, so a setup is only required once. Removes the call to IS_AST_GEN() from the atomic commit's code path. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20251007150343.273718-2-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_2000.c | 2 ++ drivers/gpu/drm/ast/ast_2100.c | 2 ++ drivers/gpu/drm/ast/ast_2200.c | 2 ++ drivers/gpu/drm/ast/ast_2300.c | 2 ++ drivers/gpu/drm/ast/ast_2400.c | 2 ++ drivers/gpu/drm/ast/ast_2500.c | 2 ++ drivers/gpu/drm/ast/ast_2600.c | 2 ++ drivers/gpu/drm/ast/ast_drv.h | 2 ++ drivers/gpu/drm/ast/ast_mode.c | 7 +------ 9 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_2000.c b/drivers/gpu/drm/ast/ast_2000.c index 03b0dcea43d1..74a041e99061 100644 --- a/drivers/gpu/drm/ast/ast_2000.c +++ b/drivers/gpu/drm/ast/ast_2000.c @@ -230,6 +230,8 @@ struct drm_device *ast_2000_device_create(struct pci_dev *pdev, ast_device_init(ast, chip, config_mode, regs, ioregs); + ast->dclk_table = ast_2000_dclk_table; + ast_2000_detect_tx_chip(ast, need_post); if (need_post) { diff --git a/drivers/gpu/drm/ast/ast_2100.c b/drivers/gpu/drm/ast/ast_2100.c index 540972daec52..eb3336c84833 100644 --- a/drivers/gpu/drm/ast/ast_2100.c +++ b/drivers/gpu/drm/ast/ast_2100.c @@ -451,6 +451,8 @@ struct drm_device *ast_2100_device_create(struct pci_dev *pdev, ast_device_init(ast, chip, config_mode, regs, ioregs); + ast->dclk_table = ast_2000_dclk_table; + ast_2000_detect_tx_chip(ast, need_post); if (need_post) { diff --git a/drivers/gpu/drm/ast/ast_2200.c b/drivers/gpu/drm/ast/ast_2200.c index 4795966dc2a7..391034d5f425 100644 --- a/drivers/gpu/drm/ast/ast_2200.c +++ b/drivers/gpu/drm/ast/ast_2200.c @@ -62,6 +62,8 @@ struct drm_device *ast_2200_device_create(struct pci_dev *pdev, ast_device_init(ast, chip, config_mode, regs, ioregs); + ast->dclk_table = ast_2000_dclk_table; + ast_2000_detect_tx_chip(ast, need_post); if (need_post) { diff --git a/drivers/gpu/drm/ast/ast_2300.c b/drivers/gpu/drm/ast/ast_2300.c index d1d63e58f3d6..3c9e1960b90c 100644 --- a/drivers/gpu/drm/ast/ast_2300.c +++ b/drivers/gpu/drm/ast/ast_2300.c @@ -1426,6 +1426,8 @@ struct drm_device *ast_2300_device_create(struct pci_dev *pdev, ast_device_init(ast, chip, config_mode, regs, ioregs); + ast->dclk_table = ast_2000_dclk_table; + ast_2300_detect_tx_chip(ast); if (need_post) { diff --git a/drivers/gpu/drm/ast/ast_2400.c b/drivers/gpu/drm/ast/ast_2400.c index 596338ea22f4..be866d1cd06a 100644 --- a/drivers/gpu/drm/ast/ast_2400.c +++ b/drivers/gpu/drm/ast/ast_2400.c @@ -63,6 +63,8 @@ struct drm_device *ast_2400_device_create(struct pci_dev *pdev, ast_device_init(ast, chip, config_mode, regs, ioregs); + ast->dclk_table = ast_2000_dclk_table; + ast_2300_detect_tx_chip(ast); if (need_post) { diff --git a/drivers/gpu/drm/ast/ast_2500.c b/drivers/gpu/drm/ast/ast_2500.c index 2c56db644f06..086c74682a55 100644 --- a/drivers/gpu/drm/ast/ast_2500.c +++ b/drivers/gpu/drm/ast/ast_2500.c @@ -637,6 +637,8 @@ struct drm_device *ast_2500_device_create(struct pci_dev *pdev, ast_device_init(ast, chip, config_mode, regs, ioregs); + ast->dclk_table = ast_2500_dclk_table; + ast_2300_detect_tx_chip(ast); if (need_post) { diff --git a/drivers/gpu/drm/ast/ast_2600.c b/drivers/gpu/drm/ast/ast_2600.c index 30490c473797..1f709486f491 100644 --- a/drivers/gpu/drm/ast/ast_2600.c +++ b/drivers/gpu/drm/ast/ast_2600.c @@ -78,6 +78,8 @@ struct drm_device *ast_2600_device_create(struct pci_dev *pdev, ast_device_init(ast, chip, config_mode, regs, ioregs); + ast->dclk_table = ast_2500_dclk_table; + ast_2300_detect_tx_chip(ast); switch (ast->tx_chip) { diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 35c476c85b9a..b2b30a0e4ffb 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -174,6 +174,8 @@ struct ast_device { enum ast_config_mode config_mode; enum ast_chip chip; + const struct ast_vbios_dclk_info *dclk_table; + void __iomem *vram; unsigned long vram_base; unsigned long vram_size; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 6b9d510c509d..a9ffda1b1dea 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -370,12 +370,7 @@ static void ast_set_dclk_reg(struct ast_device *ast, struct drm_display_mode *mode, const struct ast_vbios_enhtable *vmode) { - const struct ast_vbios_dclk_info *clk_info; - - if (IS_AST_GEN6(ast) || IS_AST_GEN7(ast)) - clk_info = &ast_2500_dclk_table[vmode->dclk_index]; - else - clk_info = &ast_2000_dclk_table[vmode->dclk_index]; + const struct ast_vbios_dclk_info *clk_info = &ast->dclk_table[vmode->dclk_index]; ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc0, 0x00, clk_info->param1); ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc1, 0x00, clk_info->param2); From 703f4731264843161ee074232a7060ea2cdebbdf Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 7 Oct 2025 16:54:43 +0200 Subject: [PATCH 088/128] drm/ast: Support device quirks Define struct ast_device_quirks and add an instance for each hardware generation. The type will provide information about per-gen constants and oddities. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe > Link: https://lore.kernel.org/r/20251007150343.273718-3-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_2000.c | 5 ++++- drivers/gpu/drm/ast/ast_2100.c | 5 ++++- drivers/gpu/drm/ast/ast_2200.c | 5 ++++- drivers/gpu/drm/ast/ast_2300.c | 5 ++++- drivers/gpu/drm/ast/ast_2400.c | 5 ++++- drivers/gpu/drm/ast/ast_2500.c | 5 ++++- drivers/gpu/drm/ast/ast_2600.c | 5 ++++- drivers/gpu/drm/ast/ast_drv.c | 4 +++- drivers/gpu/drm/ast/ast_drv.h | 8 +++++++- 9 files changed, 38 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_2000.c b/drivers/gpu/drm/ast/ast_2000.c index 74a041e99061..ae4d7a2a0ec8 100644 --- a/drivers/gpu/drm/ast/ast_2000.c +++ b/drivers/gpu/drm/ast/ast_2000.c @@ -211,6 +211,9 @@ void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post) __ast_device_set_tx_chip(ast, tx_chip); } +static const struct ast_device_quirks ast_2000_device_quirks = { +}; + struct drm_device *ast_2000_device_create(struct pci_dev *pdev, const struct drm_driver *drv, enum ast_chip chip, @@ -228,7 +231,7 @@ struct drm_device *ast_2000_device_create(struct pci_dev *pdev, return ERR_CAST(ast); dev = &ast->base; - ast_device_init(ast, chip, config_mode, regs, ioregs); + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2000_device_quirks); ast->dclk_table = ast_2000_dclk_table; diff --git a/drivers/gpu/drm/ast/ast_2100.c b/drivers/gpu/drm/ast/ast_2100.c index eb3336c84833..01e064e67cf7 100644 --- a/drivers/gpu/drm/ast/ast_2100.c +++ b/drivers/gpu/drm/ast/ast_2100.c @@ -432,6 +432,9 @@ static void ast_2100_detect_widescreen(struct ast_device *ast) ast->support_wuxga = true; } +static const struct ast_device_quirks ast_2100_device_quirks = { +}; + struct drm_device *ast_2100_device_create(struct pci_dev *pdev, const struct drm_driver *drv, enum ast_chip chip, @@ -449,7 +452,7 @@ struct drm_device *ast_2100_device_create(struct pci_dev *pdev, return ERR_CAST(ast); dev = &ast->base; - ast_device_init(ast, chip, config_mode, regs, ioregs); + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2100_device_quirks); ast->dclk_table = ast_2000_dclk_table; diff --git a/drivers/gpu/drm/ast/ast_2200.c b/drivers/gpu/drm/ast/ast_2200.c index 391034d5f425..46b6fc6cf7eb 100644 --- a/drivers/gpu/drm/ast/ast_2200.c +++ b/drivers/gpu/drm/ast/ast_2200.c @@ -43,6 +43,9 @@ static void ast_2200_detect_widescreen(struct ast_device *ast) ast->support_wuxga = true; } +static const struct ast_device_quirks ast_2200_device_quirks = { +}; + struct drm_device *ast_2200_device_create(struct pci_dev *pdev, const struct drm_driver *drv, enum ast_chip chip, @@ -60,7 +63,7 @@ struct drm_device *ast_2200_device_create(struct pci_dev *pdev, return ERR_CAST(ast); dev = &ast->base; - ast_device_init(ast, chip, config_mode, regs, ioregs); + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2200_device_quirks); ast->dclk_table = ast_2000_dclk_table; diff --git a/drivers/gpu/drm/ast/ast_2300.c b/drivers/gpu/drm/ast/ast_2300.c index 3c9e1960b90c..b76a80d6aea6 100644 --- a/drivers/gpu/drm/ast/ast_2300.c +++ b/drivers/gpu/drm/ast/ast_2300.c @@ -1407,6 +1407,9 @@ static void ast_2300_detect_widescreen(struct ast_device *ast) ast->support_wuxga = true; } +static const struct ast_device_quirks ast_2300_device_quirks = { +}; + struct drm_device *ast_2300_device_create(struct pci_dev *pdev, const struct drm_driver *drv, enum ast_chip chip, @@ -1424,7 +1427,7 @@ struct drm_device *ast_2300_device_create(struct pci_dev *pdev, return ERR_CAST(ast); dev = &ast->base; - ast_device_init(ast, chip, config_mode, regs, ioregs); + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2300_device_quirks); ast->dclk_table = ast_2000_dclk_table; diff --git a/drivers/gpu/drm/ast/ast_2400.c b/drivers/gpu/drm/ast/ast_2400.c index be866d1cd06a..2b41ff69ab0b 100644 --- a/drivers/gpu/drm/ast/ast_2400.c +++ b/drivers/gpu/drm/ast/ast_2400.c @@ -44,6 +44,9 @@ static void ast_2400_detect_widescreen(struct ast_device *ast) ast->support_wuxga = true; } +static const struct ast_device_quirks ast_2400_device_quirks = { +}; + struct drm_device *ast_2400_device_create(struct pci_dev *pdev, const struct drm_driver *drv, enum ast_chip chip, @@ -61,7 +64,7 @@ struct drm_device *ast_2400_device_create(struct pci_dev *pdev, return ERR_CAST(ast); dev = &ast->base; - ast_device_init(ast, chip, config_mode, regs, ioregs); + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2400_device_quirks); ast->dclk_table = ast_2000_dclk_table; diff --git a/drivers/gpu/drm/ast/ast_2500.c b/drivers/gpu/drm/ast/ast_2500.c index 086c74682a55..6d305a8ccc51 100644 --- a/drivers/gpu/drm/ast/ast_2500.c +++ b/drivers/gpu/drm/ast/ast_2500.c @@ -618,6 +618,9 @@ static void ast_2500_detect_widescreen(struct ast_device *ast) ast->support_wuxga = true; } +static const struct ast_device_quirks ast_2500_device_quirks = { +}; + struct drm_device *ast_2500_device_create(struct pci_dev *pdev, const struct drm_driver *drv, enum ast_chip chip, @@ -635,7 +638,7 @@ struct drm_device *ast_2500_device_create(struct pci_dev *pdev, return ERR_CAST(ast); dev = &ast->base; - ast_device_init(ast, chip, config_mode, regs, ioregs); + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2500_device_quirks); ast->dclk_table = ast_2500_dclk_table; diff --git a/drivers/gpu/drm/ast/ast_2600.c b/drivers/gpu/drm/ast/ast_2600.c index 1f709486f491..df3b429e8174 100644 --- a/drivers/gpu/drm/ast/ast_2600.c +++ b/drivers/gpu/drm/ast/ast_2600.c @@ -59,6 +59,9 @@ static void ast_2600_detect_widescreen(struct ast_device *ast) ast->support_wuxga = true; } +static const struct ast_device_quirks ast_2600_device_quirks = { +}; + struct drm_device *ast_2600_device_create(struct pci_dev *pdev, const struct drm_driver *drv, enum ast_chip chip, @@ -76,7 +79,7 @@ struct drm_device *ast_2600_device_create(struct pci_dev *pdev, return ERR_CAST(ast); dev = &ast->base; - ast_device_init(ast, chip, config_mode, regs, ioregs); + ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2600_device_quirks); ast->dclk_table = ast_2500_dclk_table; diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index a89735c6a462..b9a9b050b546 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -51,8 +51,10 @@ void ast_device_init(struct ast_device *ast, enum ast_chip chip, enum ast_config_mode config_mode, void __iomem *regs, - void __iomem *ioregs) + void __iomem *ioregs, + const struct ast_device_quirks *quirks) { + ast->quirks = quirks; ast->chip = chip; ast->config_mode = config_mode; ast->regs = regs; diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index b2b30a0e4ffb..2a2c28693dc6 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -164,9 +164,14 @@ to_ast_connector(struct drm_connector *connector) * Device */ +struct ast_device_quirks { +}; + struct ast_device { struct drm_device base; + const struct ast_device_quirks *quirks; + void __iomem *regs; void __iomem *ioregs; void __iomem *dp501_fw_buf; @@ -414,7 +419,8 @@ void ast_device_init(struct ast_device *ast, enum ast_chip chip, enum ast_config_mode config_mode, void __iomem *regs, - void __iomem *ioregs); + void __iomem *ioregs, + const struct ast_device_quirks *quirks); void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip); /* ast_2000.c */ From 2f9d9041adbe9193f69de3aae8f09db4f23699f1 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 7 Oct 2025 16:54:44 +0200 Subject: [PATCH 089/128] drm/ast: Store CRTC memory request threshold in device quirks Store each hardware's CRTC memory threshold in the specific instance of struct ast_device_quirks. Removes the calls to IS_AST_GENn() from ast_set_crtthd_reg(). The values stored in the registers appear to be plain limits. Hence write them in the driver in decimal format instead of hexadecimal. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20251007150343.273718-4-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_2000.c | 2 ++ drivers/gpu/drm/ast/ast_2100.c | 2 ++ drivers/gpu/drm/ast/ast_2200.c | 2 ++ drivers/gpu/drm/ast/ast_2300.c | 2 ++ drivers/gpu/drm/ast/ast_2400.c | 2 ++ drivers/gpu/drm/ast/ast_2500.c | 2 ++ drivers/gpu/drm/ast/ast_2600.c | 2 ++ drivers/gpu/drm/ast/ast_drv.h | 5 +++++ drivers/gpu/drm/ast/ast_mode.c | 19 +++++-------------- 9 files changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_2000.c b/drivers/gpu/drm/ast/ast_2000.c index ae4d7a2a0ec8..fa3bc23ce098 100644 --- a/drivers/gpu/drm/ast/ast_2000.c +++ b/drivers/gpu/drm/ast/ast_2000.c @@ -212,6 +212,8 @@ void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post) } static const struct ast_device_quirks ast_2000_device_quirks = { + .crtc_mem_req_threshold_low = 31, + .crtc_mem_req_threshold_high = 47, }; struct drm_device *ast_2000_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_2100.c b/drivers/gpu/drm/ast/ast_2100.c index 01e064e67cf7..05aeb0624d41 100644 --- a/drivers/gpu/drm/ast/ast_2100.c +++ b/drivers/gpu/drm/ast/ast_2100.c @@ -433,6 +433,8 @@ static void ast_2100_detect_widescreen(struct ast_device *ast) } static const struct ast_device_quirks ast_2100_device_quirks = { + .crtc_mem_req_threshold_low = 47, + .crtc_mem_req_threshold_high = 63, }; struct drm_device *ast_2100_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_2200.c b/drivers/gpu/drm/ast/ast_2200.c index 46b6fc6cf7eb..b64345d11ffa 100644 --- a/drivers/gpu/drm/ast/ast_2200.c +++ b/drivers/gpu/drm/ast/ast_2200.c @@ -44,6 +44,8 @@ static void ast_2200_detect_widescreen(struct ast_device *ast) } static const struct ast_device_quirks ast_2200_device_quirks = { + .crtc_mem_req_threshold_low = 47, + .crtc_mem_req_threshold_high = 63, }; struct drm_device *ast_2200_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_2300.c b/drivers/gpu/drm/ast/ast_2300.c index b76a80d6aea6..5f50d9f91ffd 100644 --- a/drivers/gpu/drm/ast/ast_2300.c +++ b/drivers/gpu/drm/ast/ast_2300.c @@ -1408,6 +1408,8 @@ static void ast_2300_detect_widescreen(struct ast_device *ast) } static const struct ast_device_quirks ast_2300_device_quirks = { + .crtc_mem_req_threshold_low = 96, + .crtc_mem_req_threshold_high = 120, }; struct drm_device *ast_2300_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_2400.c b/drivers/gpu/drm/ast/ast_2400.c index 2b41ff69ab0b..2e6befd24f91 100644 --- a/drivers/gpu/drm/ast/ast_2400.c +++ b/drivers/gpu/drm/ast/ast_2400.c @@ -45,6 +45,8 @@ static void ast_2400_detect_widescreen(struct ast_device *ast) } static const struct ast_device_quirks ast_2400_device_quirks = { + .crtc_mem_req_threshold_low = 96, + .crtc_mem_req_threshold_high = 120, }; struct drm_device *ast_2400_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_2500.c b/drivers/gpu/drm/ast/ast_2500.c index 6d305a8ccc51..416bce9ea757 100644 --- a/drivers/gpu/drm/ast/ast_2500.c +++ b/drivers/gpu/drm/ast/ast_2500.c @@ -619,6 +619,8 @@ static void ast_2500_detect_widescreen(struct ast_device *ast) } static const struct ast_device_quirks ast_2500_device_quirks = { + .crtc_mem_req_threshold_low = 96, + .crtc_mem_req_threshold_high = 120, }; struct drm_device *ast_2500_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_2600.c b/drivers/gpu/drm/ast/ast_2600.c index df3b429e8174..bb0a50b25766 100644 --- a/drivers/gpu/drm/ast/ast_2600.c +++ b/drivers/gpu/drm/ast/ast_2600.c @@ -60,6 +60,8 @@ static void ast_2600_detect_widescreen(struct ast_device *ast) } static const struct ast_device_quirks ast_2600_device_quirks = { + .crtc_mem_req_threshold_low = 160, + .crtc_mem_req_threshold_high = 224, }; struct drm_device *ast_2600_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 2a2c28693dc6..926e1c7de6f8 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -165,6 +165,11 @@ to_ast_connector(struct drm_connector *connector) */ struct ast_device_quirks { + /* + * CRTC memory request threshold + */ + unsigned char crtc_mem_req_threshold_low; + unsigned char crtc_mem_req_threshold_high; }; struct ast_device { diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index a9ffda1b1dea..da374af9596d 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -410,20 +410,11 @@ static void ast_set_color_reg(struct ast_device *ast, static void ast_set_crtthd_reg(struct ast_device *ast) { - /* Set Threshold */ - if (IS_AST_GEN7(ast)) { - ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0xe0); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0xa0); - } else if (IS_AST_GEN6(ast) || IS_AST_GEN5(ast) || IS_AST_GEN4(ast)) { - ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0x78); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0x60); - } else if (IS_AST_GEN3(ast) || IS_AST_GEN2(ast)) { - ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0x3f); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0x2f); - } else { - ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0x2f); - ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0x1f); - } + u8 vgacra6 = ast->quirks->crtc_mem_req_threshold_low; + u8 vgacra7 = ast->quirks->crtc_mem_req_threshold_high; + + ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, vgacra7); + ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, vgacra6); } static void ast_set_sync_reg(struct ast_device *ast, From d251db1f31be975b4d37ed71cbfe23d6560a5134 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 7 Oct 2025 16:54:45 +0200 Subject: [PATCH 090/128] drm/ast: Store precatch settings in struct ast_device_quirks Add a precatch flag in struct ast_device_info and set it on AST2500 and AST2600. Remove calls to IS_AST_GENn() from ast_set_crtc_reg(). Also fix the coding style in several places. Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe Link: https://lore.kernel.org/r/20251007150343.273718-5-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_2500.c | 1 + drivers/gpu/drm/ast/ast_2600.c | 1 + drivers/gpu/drm/ast/ast_drv.h | 6 ++++++ drivers/gpu/drm/ast/ast_mode.c | 17 ++++++++--------- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_2500.c b/drivers/gpu/drm/ast/ast_2500.c index 416bce9ea757..2a52af0ded56 100644 --- a/drivers/gpu/drm/ast/ast_2500.c +++ b/drivers/gpu/drm/ast/ast_2500.c @@ -621,6 +621,7 @@ static void ast_2500_detect_widescreen(struct ast_device *ast) static const struct ast_device_quirks ast_2500_device_quirks = { .crtc_mem_req_threshold_low = 96, .crtc_mem_req_threshold_high = 120, + .crtc_hsync_precatch_needed = true, }; struct drm_device *ast_2500_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_2600.c b/drivers/gpu/drm/ast/ast_2600.c index bb0a50b25766..7cde5ce9c41f 100644 --- a/drivers/gpu/drm/ast/ast_2600.c +++ b/drivers/gpu/drm/ast/ast_2600.c @@ -62,6 +62,7 @@ static void ast_2600_detect_widescreen(struct ast_device *ast) static const struct ast_device_quirks ast_2600_device_quirks = { .crtc_mem_req_threshold_low = 160, .crtc_mem_req_threshold_high = 224, + .crtc_hsync_precatch_needed = true, }; struct drm_device *ast_2600_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 926e1c7de6f8..76969244d36f 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -170,6 +170,12 @@ struct ast_device_quirks { */ unsigned char crtc_mem_req_threshold_low; unsigned char crtc_mem_req_threshold_high; + + /* + * Adjust hsync values to load next scanline early. Signalled + * by AST2500PreCatchCRT in VBIOS mode flags. + */ + bool crtc_hsync_precatch_needed; }; struct ast_device { diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index da374af9596d..ebb1ec82d904 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -241,16 +241,15 @@ static void ast_set_std_reg(struct ast_device *ast, ast_set_index_reg(ast, AST_IO_VGAGRI, i, stdtable->gr[i]); } -static void ast_set_crtc_reg(struct ast_device *ast, - struct drm_display_mode *mode, +static void ast_set_crtc_reg(struct ast_device *ast, struct drm_display_mode *mode, const struct ast_vbios_enhtable *vmode) { u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0; - u16 temp, precache = 0; + u16 temp; + unsigned char crtc_hsync_precatch = 0; - if ((IS_AST_GEN6(ast) || IS_AST_GEN7(ast)) && - (vmode->flags & AST2500PreCatchCRT)) - precache = 40; + if (ast->quirks->crtc_hsync_precatch_needed && (vmode->flags & AST2500PreCatchCRT)) + crtc_hsync_precatch = 40; ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x7f, 0x00); @@ -276,12 +275,12 @@ static void ast_set_crtc_reg(struct ast_device *ast, jregAD |= 0x01; /* HBE D[5] */ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x03, 0xE0, (temp & 0x1f)); - temp = ((mode->crtc_hsync_start-precache) >> 3) - 1; + temp = ((mode->crtc_hsync_start - crtc_hsync_precatch) >> 3) - 1; if (temp & 0x100) jregAC |= 0x40; /* HRS D[5] */ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x04, 0x00, temp); - temp = (((mode->crtc_hsync_end-precache) >> 3) - 1) & 0x3f; + temp = (((mode->crtc_hsync_end - crtc_hsync_precatch) >> 3) - 1) & 0x3f; if (temp & 0x20) jregAD |= 0x04; /* HRE D[5] */ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x05, 0x60, (u8)((temp & 0x1f) | jreg05)); @@ -348,7 +347,7 @@ static void ast_set_crtc_reg(struct ast_device *ast, ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x09, 0xdf, jreg09); ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAE, 0x00, (jregAE | 0x80)); - if (precache) + if (crtc_hsync_precatch) ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x80); else ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x00); From b245bc23ff5840f1f0d54d2d76ff5facc03a51bb Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 7 Oct 2025 16:54:46 +0200 Subject: [PATCH 091/128] drm/ast: Store HSync adjustment in device quirks Add the add4 flag to struct ast_device_quirks and set it on AST2600. Replaces a call to IS_AST_GEN7() in ast_set_crtc_reg(). Signed-off-by: Thomas Zimmermann Reviewed-by: Jocelyn Falempe > Link: https://lore.kernel.org/r/20251007150343.273718-6-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_2600.c | 1 + drivers/gpu/drm/ast/ast_drv.h | 6 ++++++ drivers/gpu/drm/ast/ast_mode.c | 3 +-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_2600.c b/drivers/gpu/drm/ast/ast_2600.c index 7cde5ce9c41f..dee78fd5b022 100644 --- a/drivers/gpu/drm/ast/ast_2600.c +++ b/drivers/gpu/drm/ast/ast_2600.c @@ -63,6 +63,7 @@ static const struct ast_device_quirks ast_2600_device_quirks = { .crtc_mem_req_threshold_low = 160, .crtc_mem_req_threshold_high = 224, .crtc_hsync_precatch_needed = true, + .crtc_hsync_add4_needed = true, }; struct drm_device *ast_2600_device_create(struct pci_dev *pdev, diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 76969244d36f..7be36a358e74 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -176,6 +176,12 @@ struct ast_device_quirks { * by AST2500PreCatchCRT in VBIOS mode flags. */ bool crtc_hsync_precatch_needed; + + /* + * Workaround for modes with HSync Time that is not a multiple + * of 8 (e.g., 1920x1080@60Hz, HSync +44 pixels). + */ + bool crtc_hsync_add4_needed; }; struct ast_device { diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index ebb1ec82d904..a8917ab7028c 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -288,8 +288,7 @@ static void ast_set_crtc_reg(struct ast_device *ast, struct drm_display_mode *mo ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAC, 0x00, jregAC); ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAD, 0x00, jregAD); - // Workaround for HSync Time non octave pixels (1920x1080@60Hz HSync 44 pixels); - if (IS_AST_GEN7(ast) && (mode->crtc_vdisplay == 1080)) + if (ast->quirks->crtc_hsync_add4_needed && mode->crtc_vdisplay == 1080) ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x02); else ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x00); From bde44378397b1969f2be5908f41083e900eb5294 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 8 Oct 2025 11:36:55 +0200 Subject: [PATCH 092/128] drm/bochs: Use vblank timer Use a vblank timer to simulate the vblank interrupt. The DRM vblank helpers provide an implementation on top of Linux' hrtimer. Bochs enables and disables the timer as part of the CRTC. The atomic_flush callback sets up the event. Like vblank interrupts, the vblank timer fires at the rate of the display refresh. Most userspace limits its page flip rate according to the DRM vblank event. Bochs' virtual hardware does not provide vblank interrupts, so DRM sends each event ASAP. With the fast access time of virtual display memory, the event rate is much higher than the display mode's refresh rate; creating the next page flip almost immediately. This leads to excessive CPU overhead from even small display updates, such as moving the mouse pointer. This problem affects bochs and all other virtual displays. See [1] for a discussion in the context of hypervdrm. Signed-off-by: Thomas Zimmermann Link: https://lore.kernel.org/dri-devel/SN6PR02MB415702B00D6D52B0EE962C98D46CA@SN6PR02MB4157.namprd02.prod.outlook.com/ # [1] Acked-by: Gerd Hoffmann Link: https://lore.kernel.org/r/20251008093931.19138-1-tzimmermann@suse.de --- drivers/gpu/drm/tiny/bochs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index d2d5e9f1269f..71e874c19610 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include