From b7088d58dccfba87fe8dd2ab7c493ee1d9d09277 Mon Sep 17 00:00:00 2001 From: Jens Glathe Date: Mon, 8 Jun 2026 06:54:39 +0200 Subject: [PATCH 001/121] drm/msm/dp: add missing drm_edid_connector_update() before add_modes on cached EDID After the refactor to struct drm_edid, the fast path in msm_dp_panel_get_modes() that already held a cached EDID called drm_edid_connector_add_modes() directly without first calling drm_edid_connector_update(). The new API requires the update step to associate the EDID with the connector. Add the missing call. This restores correct behaviour for the cached-EDID path. Fixes: 5bea90ad9743 ("drm/msm/dp: switch to struct drm_edid") Reviewed-by: Dmitry Baryshkov Signed-off-by: Jens Glathe Patchwork: https://patchwork.freedesktop.org/patch/731125/ Link: https://lore.kernel.org/r/20260608-drm_plug_flaky_edid-v3-1-1ca632938e7f@oldschoolsolutions.biz Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_panel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index 6bb021820d7c..5b4954e7cb74 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -332,8 +332,10 @@ int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel, return -EINVAL; } - if (msm_dp_panel->drm_edid) + if (msm_dp_panel->drm_edid) { + drm_edid_connector_update(connector, msm_dp_panel->drm_edid); return drm_edid_connector_add_modes(connector); + } return 0; } From 6cd33b6f4155efc20485929fd0b56bb704641db9 Mon Sep 17 00:00:00 2001 From: Kavan Smith Date: Mon, 6 Jul 2026 18:32:40 -0700 Subject: [PATCH 002/121] drm/msm/dsi: round 6G byte clock rate to the PLL-achievable value MSM8916 runtime DSI commands still go through msm_dsi_host_xfer_prepare(), which re-applies the link clock rate before enabling the link clocks. That is fine in principle, but on DSI 6G the requested byte clock rate often does not exactly match the DSI PHY PLL's realizable rate. For example, the driver can request 56250000 Hz while the PLL actually runs at 56246337 Hz. Because the requested and actual rates differ slightly, every later link_clk_set_rate() call is treated as a real clock change and re-locks the PLL. On a video-mode panel without an internal timing generator, such as samsung,s6d7aa0 / lsl080al03 on MSM8916, that live-clock glitch makes the panel lose pixel lock and visibly corrupts scanout on each runtime DCS command, including backlight writes. Fix this by rounding the computed 6G byte clock rate up front, before it is stored in msm_host->byte_clk_rate and reused by later transfers. Once the host carries the PLL-achievable rate instead of the idealized one, repeated link_clk_set_rate() calls become no-ops in the common clock framework and no longer re-lock the PLL. This keeps the normal transfer callback sequencing intact, preserves the OPP vote path in link_clk_set_rate(), and matches the fix direction suggested in the original 2018 discussion. Reported-by: Daniel Mack Closes: https://lore.kernel.org/all/1a682c5b-7fc9-3aaa-120b-64b239a355a3@zonque.org/ Fixes: 6b16f05aa39f ("drm/msm/dsi: Split clk rate setting and enable") Cc: stable@vger.kernel.org Signed-off-by: Kavan Smith Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/738234/ Link: https://lore.kernel.org/r/20260707013240.681012-1-kavansmith82@gmail.com [DB: dropped extra chunk from the patch] Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 982abaaac00d..5e1b313f04c0 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -670,12 +670,24 @@ static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi) int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi) { + long rounded_byte_clk_rate; + if (!msm_host->mode) { pr_err("%s: mode not set\n", __func__); return -EINVAL; } dsi_calc_pclk(msm_host, is_bonded_dsi); + + rounded_byte_clk_rate = clk_round_rate(msm_host->byte_clk, + msm_host->byte_clk_rate); + if (rounded_byte_clk_rate < 0) { + pr_err("%s: failed to round byte clock rate, %ld\n", + __func__, rounded_byte_clk_rate); + return rounded_byte_clk_rate; + } + + msm_host->byte_clk_rate = rounded_byte_clk_rate; msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk); return 0; } From 14ea7c582879978a7ec0ddbd65d485f2a826053c Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 30 May 2026 13:13:42 -0700 Subject: [PATCH 003/121] drm/msm/dpu: fix parameter name in dpu_core_perf_adjusted_mode_clk kernel-doc The kernel-doc referred to @crtc_clk_rate but the actual parameter is @mode_clk_rate. Assisted-by: Opencode:Big-pickle Signed-off-by: Rosen Penev Fixes: 62b7d6835288 ("drm/msm/dpu: Filter modes based on adjusted mode clock") Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/729413/ Link: https://lore.kernel.org/r/20260530201342.10538-1-rosenp@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c index 13cc658065c5..2ff255d7795e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -34,7 +34,7 @@ enum dpu_perf_mode { /** * dpu_core_perf_adjusted_mode_clk - Adjust given mode clock rate according to * the perf clock factor. - * @crtc_clk_rate - Unadjusted mode clock rate + * @mode_clk_rate: unadjusted mode clock rate * @perf_cfg: performance configuration */ u64 dpu_core_perf_adjusted_mode_clk(u64 mode_clk_rate, From 44784327815b2a1ad8bb56b9236770cb538c7c27 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 12 Jul 2026 14:56:55 +0300 Subject: [PATCH 004/121] Revert "drm/msm: dsi: fix PLL init in bonded mode" Commit 93c97bc8d85d ("drm/msm: dsi: fix PLL init in bonded mode") fixed one of the issues with the DSI bonded mode, but broke non-bonded usecase for DSI as reported by Mohit Dsor. Clock divider is being programmed incorrectly, resultin in the wrong display mode being selected. Revert the offending commit, letting Neil to work on a better fix. Fixes: 93c97bc8d85d ("drm/msm: dsi: fix PLL init in bonded mode") Reported-by: Mohit Dsor Closes: https://lore.kernel.org/r/ae07cef84AmXK43H@hu-mdsor-hyd.qualcomm.com Cc: Neil Armstrong Cc: Thorsten Leemhuis Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/739459/ Link: https://lore.kernel.org/r/20260712-msm-revert-dsi-pll-fix-v1-1-40122689ea25@oss.qualcomm.com --- drivers/gpu/drm/msm/dsi/phy/dsi_phy.h | 1 + drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h index 21a59d66e8dc..f5d3e806f8fd 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h @@ -111,6 +111,7 @@ struct msm_dsi_phy { struct msm_dsi_dphy_timing timing; const struct msm_dsi_phy_cfg *cfg; void *tuning_cfg; + void *pll_data; enum msm_dsi_phy_usecase usecase; bool regulator_ldo_mode; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c index 984a66085dfb..5d805a797abd 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c @@ -426,8 +426,11 @@ static void dsi_pll_enable_pll_bias(struct dsi_pll_7nm *pll) u32 data; spin_lock_irqsave(&pll->pll_enable_lock, flags); - pll->pll_enable_cnt++; - WARN_ON(pll->pll_enable_cnt == INT_MAX); + if (pll->pll_enable_cnt++) { + spin_unlock_irqrestore(&pll->pll_enable_lock, flags); + WARN_ON(pll->pll_enable_cnt == INT_MAX); + return; + } data = readl(pll->phy->base + REG_DSI_7nm_PHY_CMN_CTRL_0); data |= DSI_7nm_PHY_CMN_CTRL_0_PLL_SHUTDOWNB; @@ -873,6 +876,7 @@ static int dsi_pll_7nm_init(struct msm_dsi_phy *phy) spin_lock_init(&pll_7nm->pll_enable_lock); pll_7nm->phy = phy; + phy->pll_data = pll_7nm; ret = pll_7nm_register(pll_7nm, phy->provided_clocks->hws); if (ret) { @@ -961,8 +965,10 @@ static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy, u32 const delay_us = 5; u32 const timeout_us = 1000; struct msm_dsi_dphy_timing *timing = &phy->timing; + struct dsi_pll_7nm *pll = phy->pll_data; void __iomem *base = phy->base; bool less_than_1500_mhz; + unsigned long flags; u32 vreg_ctrl_0, vreg_ctrl_1, lane_ctrl0; u32 glbl_pemph_ctrl_0; u32 glbl_str_swi_cal_sel_ctrl, glbl_hstx_str_ctrl_0; @@ -1084,10 +1090,13 @@ static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy, glbl_rescode_bot_ctrl = 0x3c; } + spin_lock_irqsave(&pll->pll_enable_lock, flags); + pll->pll_enable_cnt = 1; /* de-assert digital and pll power down */ data = DSI_7nm_PHY_CMN_CTRL_0_DIGTOP_PWRDN_B | DSI_7nm_PHY_CMN_CTRL_0_PLL_SHUTDOWNB; writel(data, base + REG_DSI_7nm_PHY_CMN_CTRL_0); + spin_unlock_irqrestore(&pll->pll_enable_lock, flags); /* Assert PLL core reset */ writel(0x00, base + REG_DSI_7nm_PHY_CMN_PLL_CNTRL); @@ -1200,7 +1209,9 @@ static bool dsi_7nm_set_continuous_clock(struct msm_dsi_phy *phy, bool enable) static void dsi_7nm_phy_disable(struct msm_dsi_phy *phy) { + struct dsi_pll_7nm *pll = phy->pll_data; void __iomem *base = phy->base; + unsigned long flags; u32 data; DBG(""); @@ -1227,8 +1238,11 @@ static void dsi_7nm_phy_disable(struct msm_dsi_phy *phy) writel(data, base + REG_DSI_7nm_PHY_CMN_CTRL_0); writel(0, base + REG_DSI_7nm_PHY_CMN_LANE_CTRL0); + spin_lock_irqsave(&pll->pll_enable_lock, flags); + pll->pll_enable_cnt = 0; /* Turn off all PHY blocks */ writel(0x00, base + REG_DSI_7nm_PHY_CMN_CTRL_0); + spin_unlock_irqrestore(&pll->pll_enable_lock, flags); /* make sure phy is turned off */ wmb(); From b8a9c9c5787bed1243e5364c89ca66c26b4e4d83 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 11 Jun 2026 13:27:30 +0300 Subject: [PATCH 005/121] drm/msm/adreno: fix use after free on error path in a6xx_gpu_init() The a6xx_destroy() function frees "a6xx_gpu" and so "adreno_gpu" points to freed memory. Preserve the error code before freeing the memory to avoid a use after free. Fixes: d158886cba08 ("drm/msm/adreno: Trust the SSoT UBWC config") Signed-off-by: Dan Carpenter Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/732275/ Message-ID: Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 8b3bb2fd433b..a44380316aaa 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -2770,8 +2770,9 @@ static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) adreno_gpu->ubwc_config = qcom_ubwc_config_get_data(); if (IS_ERR(adreno_gpu->ubwc_config)) { + ret = PTR_ERR(adreno_gpu->ubwc_config); a6xx_destroy(&(a6xx_gpu->base.base)); - return ERR_CAST(adreno_gpu->ubwc_config); + return ERR_PTR(ret); } /* Set up the preemption specific bits and pieces for each ringbuffer */ From be0e82b8e0c96649b8bc77a99ab0d185243b7659 Mon Sep 17 00:00:00 2001 From: Alexandre MINETTE Date: Wed, 10 Jun 2026 09:49:17 +0200 Subject: [PATCH 006/121] drm/msm/a3xx: Drain VBIF before GPU suspend A3xx hangs after every runtime suspend on the Samsung Galaxy S4 GT-I9505. Even simple GPU workloads, such as drawing a single triangle, hang reliably once the GPU has been suspended by runtime PM. The generic MSM GPU suspend path disables clocks/power, but A3xx also needs to ensure that pending VBIF transactions are drained before that happens. Add an A3xx-specific pm_suspend callback. Wait for the GPU to become idle, halt all VBIF XIN clients, wait for the corresponding acknowledgment, and only then enter the generic MSM GPU suspend path. This fixes reliable A3xx GPU hangs observed after runtime PM on the Samsung Galaxy S4 GT-I9505, codename jflte. The failure is reported as: mdp4 5100000.display-controller: [drm:hangcheck_handler] *ERROR* 3.2.0.2: hangcheck detected gpu lockup rb 0! mdp4 5100000.display-controller: [drm:hangcheck_handler] *ERROR* 3.2.0.2: completed fence: 4294967041 mdp4 5100000.display-controller: [drm:hangcheck_handler] *ERROR* 3.2.0.2: submitted fence: 4294967049 mdp4 5100000.display-controller: [drm:recover_worker] *ERROR* 3.2.0.2: hangcheck recover! Link: https://github.com/freedreno-zz/freedreno/issues/12 Signed-off-by: Alexandre MINETTE Patchwork: https://patchwork.freedesktop.org/patch/731919/ Message-ID: <20260610-mainline-fix-a3xx-gpu-hang-sending-v1-1-9282182840b5@alex-min.fr> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 36 ++++++++++++++++++- drivers/gpu/drm/msm/registers/adreno/a3xx.xml | 2 ++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index c17e9777beae..0259e60b8011 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -25,6 +25,8 @@ extern bool hang_debug; +#define A3XX_VBIF_XIN_HALT_CTRL0_MASK GENMASK(5, 0) + static void a3xx_dump(struct msm_gpu *gpu); static bool a3xx_idle(struct msm_gpu *gpu); @@ -496,6 +498,38 @@ static u64 a3xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate) return busy_cycles; } +static int a3xx_vbif_halt(struct msm_gpu *gpu) +{ + u32 ack; + int ret; + + gpu_write(gpu, REG_A3XX_VBIF_XIN_HALT_CTRL0, + A3XX_VBIF_XIN_HALT_CTRL0_MASK); + ret = spin_until(((ack = gpu_read(gpu, REG_A3XX_VBIF_XIN_HALT_CTRL1)) & + A3XX_VBIF_XIN_HALT_CTRL0_MASK) == + A3XX_VBIF_XIN_HALT_CTRL0_MASK); + gpu_write(gpu, REG_A3XX_VBIF_XIN_HALT_CTRL0, 0); + + if (ret) + return -EBUSY; + + return 0; +} + +static int a3xx_pm_suspend(struct msm_gpu *gpu) +{ + int ret; + + if (!a3xx_idle(gpu)) + return -EBUSY; + + ret = a3xx_vbif_halt(gpu); + if (ret) + return ret; + + return msm_gpu_pm_suspend(gpu); +} + static u32 a3xx_get_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring) { ring->memptrs->rptr = gpu_read(gpu, REG_AXXX_CP_RB_RPTR); @@ -581,7 +615,7 @@ const struct adreno_gpu_funcs a3xx_gpu_funcs = { .get_param = adreno_get_param, .set_param = adreno_set_param, .hw_init = a3xx_hw_init, - .pm_suspend = msm_gpu_pm_suspend, + .pm_suspend = a3xx_pm_suspend, .pm_resume = msm_gpu_pm_resume, .recover = a3xx_recover, .submit = a3xx_submit, diff --git a/drivers/gpu/drm/msm/registers/adreno/a3xx.xml b/drivers/gpu/drm/msm/registers/adreno/a3xx.xml index 09c9606fc3e1..21b00a17ef3e 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a3xx.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a3xx.xml @@ -1495,6 +1495,8 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> + + From d9108bfdb746edacdb05bd27959a4ae63c6c7f3f Mon Sep 17 00:00:00 2001 From: Shivam Rawat Date: Fri, 5 Jun 2026 01:38:17 +0530 Subject: [PATCH 007/121] drm/msm/a6xx: Fix stale rpmh votes after suspend There are stale RPMH votes (BCM votes) observed after GMU suspend. This is because the rpmh stop sequences are skipped during gmu suspend. Fix this and also move GMU to reset state to avoid any further activity. Fixes: f248d5d5159a ("drm/msm/a6xx: Fix PDC sleep sequence") Signed-off-by: Shivam Rawat Signed-off-by: Akhil P Oommen Tested-by: Neil Armstrong # on SM8650-HDK Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/730652/ Message-ID: <20260605-assorted-fixes-june-v1-1-2caa04f7287c@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index 2e5d7b53a0c3..a2f6918c4f7f 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -642,7 +642,7 @@ static void a6xx_rpmh_stop(struct a6xx_gmu *gmu) int ret; u32 val; - if (test_and_clear_bit(GMU_STATUS_FW_START, &gmu->status)) + if (!test_and_clear_bit(GMU_STATUS_FW_START, &gmu->status)) return; if (adreno_is_a840(adreno_gpu)) @@ -1465,6 +1465,9 @@ static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu) /* Stop the interrupts and mask the hardware */ a6xx_gmu_irq_disable(gmu); + /* Halt the gmu cm3 core */ + gmu_write(gmu, REG_A6XX_GMU_CM3_SYSRESET, 1); + /* Tell RPMh to power off the GPU */ a6xx_rpmh_stop(gmu); From b303e1d52811de7d1bcf793560754d4df68d4a1c Mon Sep 17 00:00:00 2001 From: Jie Zhang Date: Fri, 5 Jun 2026 01:38:18 +0530 Subject: [PATCH 008/121] drm/msm: Recover HW before retire hung submit During recovery, it is not safe to retire the hung submit before we recover the GPU. Retiring the submit triggers BO free and that can result in GPU pagefaults since the GPU may be actively accessing those BOs. To fix this, retire the submits after gpu recovery is complete in recover_worker(). Fixes: 1a370be9ac51 ("drm/msm: restart queued submits after hang") Signed-off-by: Jie Zhang Signed-off-by: Akhil P Oommen Acked-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/730655/ Message-ID: <20260605-assorted-fixes-june-v1-2-2caa04f7287c@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 18ed00e5f143..9ac7740a87f0 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -552,11 +552,11 @@ static void recover_worker(struct kthread_work *work) msm_update_fence(ring->fctx, fence); } + gpu->funcs->recover(gpu); + /* retire completed submits, plus the one that hung: */ retire_submits(gpu); - gpu->funcs->recover(gpu); - /* * Replay all remaining submits starting with highest priority * ring From fc7ccbc6174b79ffab5be5dca5b6e253df22f030 Mon Sep 17 00:00:00 2001 From: Jie Zhang Date: Fri, 5 Jun 2026 01:38:19 +0530 Subject: [PATCH 009/121] drm/msm/a6xx: Fix A663 GPUCC register list for state capture The GPUCC register list for A663 is incorrect, which can cause out-of-bounds register access during GPU state capture. Update it to use the correct register ranges. Fixes: 5773cce8615c ("drm/msm/a6xx: Add support for A663") Signed-off-by: Jie Zhang Signed-off-by: Akhil P Oommen Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/730656/ Message-ID: <20260605-assorted-fixes-june-v1-3-2caa04f7287c@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index 166365359fa6..2a62a22077f9 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -1244,7 +1244,9 @@ static void a6xx_get_gmu_registers(struct msm_gpu *gpu, _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gmu_reglist[1], &a6xx_state->gmu_registers[1], true); - if (adreno_is_a621(adreno_gpu) || adreno_is_a623(adreno_gpu)) + if (adreno_is_a621(adreno_gpu) || + adreno_is_a623(adreno_gpu) || + adreno_is_a663(adreno_gpu)) _a6xx_get_gmu_registers(gpu, a6xx_state, &a621_gpucc_reg, &a6xx_state->gmu_registers[2], false); else From d052d0358fb89b59718b9c24871d72006d4b89b0 Mon Sep 17 00:00:00 2001 From: Jie Zhang Date: Fri, 5 Jun 2026 01:38:20 +0530 Subject: [PATCH 010/121] drm/msm/a6xx: Fix A621 GPUCC register list for state capture A621 uses an incorrect GPUCC register list during state capture. The existing list matches A623/A663. Rename it accordingly and add a dedicated A621 GPUCC register list. Fixes: 11cdb81b3c1b ("drm/msm/a6xx: Fix gpucc register block for A621") Signed-off-by: Jie Zhang Signed-off-by: Akhil P Oommen Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/730659/ Message-ID: <20260605-assorted-fixes-june-v1-4-2caa04f7287c@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 7 ++++--- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index 2a62a22077f9..3ea8ff8c7404 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -1244,11 +1244,12 @@ static void a6xx_get_gmu_registers(struct msm_gpu *gpu, _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gmu_reglist[1], &a6xx_state->gmu_registers[1], true); - if (adreno_is_a621(adreno_gpu) || - adreno_is_a623(adreno_gpu) || - adreno_is_a663(adreno_gpu)) + if (adreno_is_a621(adreno_gpu)) _a6xx_get_gmu_registers(gpu, a6xx_state, &a621_gpucc_reg, &a6xx_state->gmu_registers[2], false); + else if (adreno_is_a623(adreno_gpu) || adreno_is_a663(adreno_gpu)) + _a6xx_get_gmu_registers(gpu, a6xx_state, &a623_gpucc_reg, + &a6xx_state->gmu_registers[2], false); else _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gpucc_reg, &a6xx_state->gmu_registers[2], false); diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h index b49d8427b59e..0a13a65f89ac 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h @@ -377,6 +377,17 @@ static const u32 a6xx_gmu_gpucc_registers[] = { }; static const u32 a621_gmu_gpucc_registers[] = { + /* GPU CC */ + 0x24000, 0x2400e, 0x24400, 0x2440e, 0x24800, 0x24805, 0x24c00, 0x24cff, + 0x25800, 0x25804, 0x25c00, 0x25c04, 0x26000, 0x26004, 0x26400, 0x26405, + 0x26414, 0x2641d, 0x2642a, 0x26430, 0x26432, 0x26432, 0x26441, 0x26455, + 0x26466, 0x26468, 0x26478, 0x2647a, 0x26489, 0x2648a, 0x2649c, 0x2649e, + 0x264a0, 0x264a3, 0x264b3, 0x264b5, 0x264c5, 0x264c7, 0x264d6, 0x264d8, + 0x264e8, 0x264e9, 0x264f9, 0x264fc, 0x2650b, 0x2650c, 0x2651c, 0x2651e, + 0x26540, 0x26570, 0x26600, 0x26616, 0x26620, 0x2662d, +}; + +static const u32 a623_gmu_gpucc_registers[] = { /* GPU CC */ 0x24000, 0x2400e, 0x24400, 0x2440e, 0x25800, 0x25804, 0x25c00, 0x25c04, 0x26000, 0x26004, 0x26400, 0x26405, 0x26414, 0x2641d, 0x2642a, 0x26430, @@ -402,6 +413,7 @@ static const struct a6xx_registers a6xx_gmu_reglist[] = { static const struct a6xx_registers a6xx_gpucc_reg = REGS(a6xx_gmu_gpucc_registers, 0, 0); static const struct a6xx_registers a621_gpucc_reg = REGS(a621_gmu_gpucc_registers, 0, 0); +static const struct a6xx_registers a623_gpucc_reg = REGS(a623_gmu_gpucc_registers, 0, 0); static u32 a6xx_get_cp_roq_size(struct msm_gpu *gpu); static u32 a7xx_get_cp_roq_size(struct msm_gpu *gpu); From bc024d325e98b6b2806e00455e030622fb8e1820 Mon Sep 17 00:00:00 2001 From: Jie Zhang Date: Fri, 5 Jun 2026 01:38:21 +0530 Subject: [PATCH 011/121] drm/msm/a6xx: Fix IRQ storm during msm_recovery test Once a hang is triggered by the msm_recovery test, the gpu error irq remains asserted and triggers an interrupt storm. In the worst case, this IRQ storm lands on the CPU core where the hangcheck timer is scheduled, blocking it from running. This eventually leads to CPU watchdog timeouts. To fix this, mask the gpu error irqs during msm_recovery test and enable them back during the recovery. Fixes: 5edf2750d998 ("drm/msm: Add debugfs to disable hw err handling") Signed-off-by: Jie Zhang Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/730660/ Message-ID: <20260605-assorted-fixes-june-v1-5-2caa04f7287c@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 5 +++++ drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 5 ++++- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 5 ++++- drivers/gpu/drm/msm/msm_gpu.c | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 2c0bbac43c52..f1df2514c613 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -1275,6 +1275,11 @@ static irqreturn_t a5xx_irq(struct msm_gpu *gpu) status & ~A5XX_RBBM_INT_0_MASK_RBBM_AHB_ERROR); if (priv->disable_err_irq) { + /* Turn off interrupts to avoid interrupt storm */ + gpu_write(gpu, REG_A5XX_RBBM_INT_0_MASK, + A5XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS | + A5XX_RBBM_INT_0_MASK_CP_SW); + status &= A5XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS | A5XX_RBBM_INT_0_MASK_CP_SW; } diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index a44380316aaa..e293b4ca808a 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -1911,8 +1911,11 @@ static irqreturn_t a6xx_irq(struct msm_gpu *gpu) gpu_write(gpu, REG_A6XX_RBBM_INT_CLEAR_CMD, status); - if (priv->disable_err_irq) + if (priv->disable_err_irq) { + /* Turn off interrupts to avoid interrupt storm */ + gpu_write(gpu, REG_A6XX_RBBM_INT_0_MASK, A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS); status &= A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS; + } if (status & A6XX_RBBM_INT_0_MASK_RBBM_HANG_DETECT) a6xx_fault_detect_irq(gpu); diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 9e44fd1ae634..0f6fd35bd587 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -1211,8 +1211,11 @@ irqreturn_t a8xx_irq(struct msm_gpu *gpu) gpu_write(gpu, REG_A8XX_RBBM_INT_CLEAR_CMD, status); - if (priv->disable_err_irq) + if (priv->disable_err_irq) { + /* Turn off interrupts to avoid interrupt storm */ + gpu_write(gpu, REG_A8XX_RBBM_INT_0_MASK, A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS); status &= A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS; + } if (status & A6XX_RBBM_INT_0_MASK_RBBM_HANG_DETECT) a8xx_fault_detect_irq(gpu); diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 9ac7740a87f0..48ac51f4119b 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -552,6 +552,8 @@ static void recover_worker(struct kthread_work *work) msm_update_fence(ring->fctx, fence); } + priv->disable_err_irq = false; + gpu->funcs->recover(gpu); /* retire completed submits, plus the one that hung: */ From 40b793714ad8f393ab3d469f9d00b20ebda46257 Mon Sep 17 00:00:00 2001 From: Jie Zhang Date: Fri, 5 Jun 2026 01:38:22 +0530 Subject: [PATCH 012/121] drm/msm: Fix task_struct reference leak in recover_worker get_pid_task() increments the task reference count, but the corresponding put_task_struct() was missing in the else branch, leaking a reference on every GPU hang recovery. Fixes: 25654a1756a4 ("drm/msm: Update global fault counter when faulty process has already ended") Signed-off-by: Jie Zhang Signed-off-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/730662/ Message-ID: <20260605-assorted-fixes-june-v1-6-2caa04f7287c@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 48ac51f4119b..03c057856065 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -505,6 +505,8 @@ static void recover_worker(struct kthread_work *work) */ if (!vm->managed) msm_gem_vm_unusable(submit->vm); + + put_task_struct(task); } noreclaim_flag = memalloc_noreclaim_save(); From e2332abed2a4d3caa59052095dc16e4ce44791ea Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 9 Jul 2026 14:23:09 +0800 Subject: [PATCH 013/121] drm/msm: Only fini scheduler after successful init msm_ringbuffer_new() destroys a partially initialized ring through msm_ringbuffer_destroy() when an allocation or scheduler setup step fails. If drm_sched_init() fails before it finishes initializing the scheduler, the failure path still calls drm_sched_fini(). That teardown path assumes the scheduler work items, lists, and workqueue state were initialized. Track successful scheduler initialization and call drm_sched_fini() only after drm_sched_init() returned 0. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 1d8a5ca436ee ("drm/msm: Conversion to drm scheduler") Signed-off-by: Ruoyu Wang Patchwork: https://patchwork.freedesktop.org/patch/738905/ Message-ID: <20260709062309.4168362-1-ruoyuw560@gmail.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_ringbuffer.c | 7 ++++--- drivers/gpu/drm/msm/msm_ringbuffer.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c index 30ddb5351e98..f060e6362ae4 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.c +++ b/drivers/gpu/drm/msm/msm_ringbuffer.c @@ -109,9 +109,9 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id, ring->memptrs_iova = memptrs_iova; ret = drm_sched_init(&ring->sched, &args); - if (ret) { + if (ret) goto fail; - } + ring->sched_initialized = true; INIT_LIST_HEAD(&ring->submits); spin_lock_init(&ring->submit_lock); @@ -133,7 +133,8 @@ void msm_ringbuffer_destroy(struct msm_ringbuffer *ring) if (IS_ERR_OR_NULL(ring)) return; - drm_sched_fini(&ring->sched); + if (ring->sched_initialized) + drm_sched_fini(&ring->sched); msm_fence_context_free(ring->fctx); diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h index 28ca8c9f7463..3631ec283c6e 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.h +++ b/drivers/gpu/drm/msm/msm_ringbuffer.h @@ -56,6 +56,7 @@ struct msm_ringbuffer { * The job scheduler for this ring. */ struct drm_gpu_scheduler sched; + bool sched_initialized; /* * List of in-flight submits on this ring. Protected by submit_lock. From e44580b601d44238ac615f441ce1179bc88a47ad Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Tue, 7 Jul 2026 03:10:19 +0530 Subject: [PATCH 014/121] dt-bindings: display/msm: gpu: Document Adreno 840 Adreno 840 GPU found in Kaanapali chipsets belongs to the A8x family. It is a new IP which features the new slice architecture with 3 slices, raytracing support, and the highest GMEM size seen so far on a Snapdragon mobile chipsets. Update the dt bindings documentation to describe this GPU. Acked-by: Krzysztof Kozlowski Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/738143/ Message-ID: <20260707-kaana-gpu-dt-v3-1-8dac9a60dd5c@oss.qualcomm.com> Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/display/msm/gpu.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/gpu.yaml b/Documentation/devicetree/bindings/display/msm/gpu.yaml index dbbd8b814189..d0b7304c17af 100644 --- a/Documentation/devicetree/bindings/display/msm/gpu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gpu.yaml @@ -413,6 +413,7 @@ allOf: contains: enum: - qcom,adreno-44010000 + - qcom,adreno-44050a01 - qcom,adreno-44070001 then: properties: @@ -448,6 +449,7 @@ allOf: - qcom,adreno-43050c01 - qcom,adreno-43051401 - qcom,adreno-44010000 + - qcom,adreno-44050a01 - qcom,adreno-44070001 then: # Starting with A6xx, the clocks are usually defined in the GMU node From 1f69339da06255a7f8560b4768fff83992c03deb Mon Sep 17 00:00:00 2001 From: Aditya Sherawat Date: Fri, 10 Jul 2026 00:12:00 +0530 Subject: [PATCH 015/121] drm/msm/adreno: Add support for A704 GPU Adreno A704 GPU found in Shikra is an IP reuse of A702 GPU with very minimal changes. The only KMD facing difference is the chipid and the zap firmware which is specified via devicetree. Just add the new chipid to enable support for A704 GPU in Shikra. Signed-off-by: Aditya Sherawat Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/738931/ Message-ID: <20260710-shikra-gpu-v6-1-b388ec5dce77@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index 3e6f409d13a2..2de3ab010135 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -1454,7 +1454,7 @@ DECLARE_ADRENO_REGLIST_PIPE_LIST(a7xx_dyn_pwrup_reglist); static const struct adreno_info a7xx_gpus[] = { { - .chip_ids = ADRENO_CHIP_IDS(0x07000200), + .chip_ids = ADRENO_CHIP_IDS(0x07000200, 0x07000400), .family = ADRENO_6XX_GEN1, /* NOT a mistake! */ .fw = { [ADRENO_FW_SQE] = "a702_sqe.fw", From 9479a45e5f59c1fab40e0d43410ac7193d662624 Mon Sep 17 00:00:00 2001 From: Aditya Sherawat Date: Fri, 10 Jul 2026 00:12:01 +0530 Subject: [PATCH 016/121] dt-bindings: display/msm/gpu: Add support for A704 GPU Adreno A704 GPU found Shikra SoC is an IP reuse of A702 GPU with very minimal changes. Signed-off-by: Aditya Sherawat Reviewed-by: Krzysztof Kozlowski Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/738933/ Message-ID: <20260710-shikra-gpu-v6-2-b388ec5dce77@oss.qualcomm.com> Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/display/msm/gpu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/gpu.yaml b/Documentation/devicetree/bindings/display/msm/gpu.yaml index d0b7304c17af..992d3de7c117 100644 --- a/Documentation/devicetree/bindings/display/msm/gpu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gpu.yaml @@ -353,6 +353,7 @@ allOf: - qcom,adreno-610.0 - qcom,adreno-619.1 - qcom,adreno-07000200 + - qcom,adreno-07000400 then: properties: clocks: From 504a65121cf15b07c9af9f04e0ec723db531c2fa Mon Sep 17 00:00:00 2001 From: Bibek Kumar Patro Date: Fri, 10 Jul 2026 00:12:02 +0530 Subject: [PATCH 017/121] dt-bindings: arm-smmu: Document GPU SMMU for Shikra SoC Add specific compatible strings to document the GPU SMMU present in the Shikra SoC. Signed-off-by: Bibek Kumar Patro Reviewed-by: Krzysztof Kozlowski Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/738935/ Message-ID: <20260710-shikra-gpu-v6-3-b388ec5dce77@oss.qualcomm.com> Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/iommu/arm,smmu.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml index a701dec2fa0a..ad15fda5c25e 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml @@ -108,6 +108,7 @@ properties: - qcom,sc7280-smmu-500 - qcom,sc8180x-smmu-500 - qcom,sc8280xp-smmu-500 + - qcom,shikra-smmu-500 - qcom,sm6115-smmu-500 - qcom,sm6125-smmu-500 - qcom,sm8150-smmu-500 @@ -543,6 +544,7 @@ allOf: - enum: - qcom,milos-smmu-500 - qcom,sar2130p-smmu-500 + - qcom,shikra-smmu-500 - qcom,sm8550-smmu-500 - qcom,sm8650-smmu-500 - qcom,x1e80100-smmu-500 From 01bcc0398f43099acb407a6067481e635c3e1b84 Mon Sep 17 00:00:00 2001 From: Puranam V G Tejaswi Date: Sat, 18 Jul 2026 02:11:26 +0530 Subject: [PATCH 018/121] drm/msm/a6xx: Fix RBBM_CLOCK_CNTL3_TP0 value in a730_hwcg The RBBM_CLOCK_CNTL3_TP0 entry in a730_hwcg has bits[19:16] set to 2 (clock gating enabled for that TP0 stage). As per the latest recommendation, clear this nibble to disable clock gating for this particular stage. Fixes: 9588d2f860a4 ("drm/msm/a6xx: Add A730 support") Signed-off-by: Puranam V G Tejaswi Reviewed-by: Konrad Dybcio Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/740955/ Message-ID: <20260718-eliza-gpu-v2-1-64379dbebd7a@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index 2de3ab010135..8f74b2558035 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -1199,7 +1199,7 @@ static const struct adreno_reglist a730_hwcg[] = { { REG_A6XX_RBBM_CLOCK_DELAY_SP0, 0x00000080 }, { REG_A6XX_RBBM_CLOCK_CNTL_TP0, 0x22222220 }, { REG_A6XX_RBBM_CLOCK_CNTL2_TP0, 0x22222222 }, - { REG_A6XX_RBBM_CLOCK_CNTL3_TP0, 0x22222222 }, + { REG_A6XX_RBBM_CLOCK_CNTL3_TP0, 0x22220222 }, { REG_A6XX_RBBM_CLOCK_CNTL4_TP0, 0x00222222 }, { REG_A6XX_RBBM_CLOCK_HYST_TP0, 0x77777777 }, { REG_A6XX_RBBM_CLOCK_HYST2_TP0, 0x77777777 }, From 352116dbc4fc61ae22eae748da36e46a3f45f65c Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Sat, 18 Jul 2026 02:11:27 +0530 Subject: [PATCH 019/121] drm/msm/a6xx: Rename GBIF_CX_CONFIG to a A6XX- variant register The GBIF_CX_CONFIG register exists on GPUs prior to A8XX (it is used on A722, for example), so it should be tagged as an A6XX variant to match the register spec. Widen its variant range from "A8XX-" to "A6XX-" in the register XML and rename the generated macro accordingly at all existing usage sites. Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/740957/ Message-ID: <20260718-eliza-gpu-v2-2-64379dbebd7a@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 2 +- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 2 +- drivers/gpu/drm/msm/adreno/a8xx_gpu.c | 2 +- drivers/gpu/drm/msm/registers/adreno/a6xx.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index 8f74b2558035..b89da68145bc 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -2180,7 +2180,7 @@ static const struct adreno_reglist a840_gbif[] = { { REG_A6XX_GBIF_QSB_SIDE1, 0x00071e20 }, { REG_A6XX_GBIF_QSB_SIDE2, 0x00071e20 }, { REG_A6XX_GBIF_QSB_SIDE3, 0x00071e20 }, - { REG_A8XX_GBIF_CX_CONFIG, 0x20023000 }, + { REG_A6XX_GBIF_CX_CONFIG, 0x20023000 }, { }, }; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index a2f6918c4f7f..a5a5f3534fc0 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -1032,7 +1032,7 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state) gpu_write(gpu, gbif_cx[i].offset, gbif_cx[i].value); if (adreno_is_a8xx(adreno_gpu)) { - gpu_write(gpu, REG_A8XX_GBIF_CX_CONFIG, 0x20023000); + gpu_write(gpu, REG_A6XX_GBIF_CX_CONFIG, 0x20023000); gmu_write(gmu, REG_A6XX_GMU_MRC_GBIF_QOS_CTRL, 0x33); } diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c index 0f6fd35bd587..add93c1fa039 100644 --- a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c @@ -228,7 +228,7 @@ static void a8xx_set_hwcg(struct msm_gpu *gpu, bool state) * GMU enables clk gating in GBIF during boot up. So, * override that here when hwcg feature is disabled */ - gpu_rmw(gpu, REG_A8XX_GBIF_CX_CONFIG, BIT(0), 0); + gpu_rmw(gpu, REG_A6XX_GBIF_CX_CONFIG, BIT(0), 0); } } diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml index 3349c01646e1..69dd0446f8d2 100644 --- a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml +++ b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml @@ -1268,7 +1268,7 @@ by a particular renderpass/blit. - + From a25fd4227f59b9147ae6d23336878c28e5437a4d Mon Sep 17 00:00:00 2001 From: Puranam V G Tejaswi Date: Sat, 18 Jul 2026 02:11:28 +0530 Subject: [PATCH 020/121] drm/msm/a6xx: Add Adreno 722 support Add support for Adreno A722, a member of the GEN1 A7xx family. It is derived from A730 and shares the same IP-level configurations: HWCG registers, protected registers, GBIF CX registers and gmu_cgc_mode. Major differences include lower cache/core counts, 1MB GMEM, no Concurrent Binning & LPAC support. Some of the peripheral blocks like RSCC are from A740 that resulted in updates to RSC layout. Add a new entry to the catalog to describe the usual configuration and few additional fixup mainly due to missing CB/LPAC features and updated RSC layout. Signed-off-by: Puranam V G Tejaswi Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/740959/ Message-ID: <20260718-eliza-gpu-v2-3-64379dbebd7a@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 34 ++ drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 15 +- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 4 +- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 81 +++- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h | 5 + .../msm/adreno/adreno_gen7_17_0_snapshot.h | 428 ++++++++++++++++++ drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 + 7 files changed, 549 insertions(+), 23 deletions(-) create mode 100644 drivers/gpu/drm/msm/adreno/adreno_gen7_17_0_snapshot.h diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index b89da68145bc..90a0c7ffb3c8 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -1500,6 +1500,40 @@ static const struct adreno_info a7xx_gpus[] = { .gmu_cgc_mode = 0x00020000, }, .preempt_record_size = 2860 * SZ_1K, + }, { + .chip_ids = ADRENO_CHIP_IDS(0x43020100), + .family = ADRENO_7XX_GEN1, + .fw = { + [ADRENO_FW_SQE] = "qcom/gen71700_sqe.fw", + [ADRENO_FW_GMU] = "qcom/gen71700_gmu.bin", + }, + .gmem = SZ_1M, + .inactive_period = DRM_MSM_INACTIVE_PERIOD, + .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT | + ADRENO_QUIRK_HAS_HW_APRIV | + ADRENO_QUIRK_PREEMPTION, + .funcs = &a7xx_gpu_funcs, + .a6xx = &(const struct a6xx_info) { + .hwcg = a730_hwcg, + .protect = &a730_protect, + .pwrup_reglist = &a7xx_pwrup_reglist, + .dyn_pwrup_reglist = &a7xx_dyn_pwrup_reglist, + .gbif_cx = a640_gbif, + .gmu_chipid = 0x07110000, + .gmu_cgc_mode = 0x00020000, + .bcms = (const struct a6xx_bcm[]) { + { .name = "SH0", .buswidth = 16 }, + { .name = "MC0", .buswidth = 4 }, + { + .name = "ACV", + .fixed = true, + .perfmode = BIT(3), + .perfmode_bw = 16500000, + }, + { /* sentinel */ }, + }, + }, + .preempt_record_size = 1536 * SZ_1K, }, { .chip_ids = ADRENO_CHIP_IDS(0x43050a01), /* "C510v2" */ .family = ADRENO_7XX_GEN2, diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index a5a5f3534fc0..e97d8d8ee65a 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -710,7 +710,8 @@ static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu) gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + 2, 0); gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + 2, 0); gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + 4, - adreno_is_a740_family(adreno_gpu) ? 0x80000021 : 0x80000000); + (adreno_is_a740_family(adreno_gpu) || + adreno_is_a722(adreno_gpu)) ? 0x80000021 : 0x80000000); gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + 4, 0); gmu_write_rscc(gmu, REG_A6XX_RSCC_OVERRIDE_START_ADDR, 0); gmu_write_rscc(gmu, REG_A6XX_RSCC_PDC_SEQ_START_ADDR, 0x4520); @@ -718,7 +719,7 @@ static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu) gmu_write_rscc(gmu, REG_A6XX_RSCC_PDC_MATCH_VALUE_HI, 0x4514); /* The second spin of A7xx GPUs messed with some register offsets.. */ - if (adreno_is_a740_family(adreno_gpu)) + if (adreno_is_a740_family(adreno_gpu) || adreno_is_a722(adreno_gpu)) seqmem0_drv0_reg = REG_A7XX_RSCC_SEQ_MEM_0_DRV0_A740; /* Load RSC sequencer uCode for sleep and wakeup */ @@ -1034,7 +1035,9 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state) if (adreno_is_a8xx(adreno_gpu)) { gpu_write(gpu, REG_A6XX_GBIF_CX_CONFIG, 0x20023000); gmu_write(gmu, REG_A6XX_GMU_MRC_GBIF_QOS_CTRL, 0x33); - } + } else if (adreno_is_a722(adreno_gpu)) + gpu_rmw(gpu, REG_A6XX_GBIF_CX_CONFIG, GENMASK(31, 29), + FIELD_PREP(GENMASK(31, 29), 2)); /* Set up the lowest idle level on the GMU */ a6xx_gmu_power_config(gmu); @@ -1087,7 +1090,8 @@ static void a6xx_gmu_rpmh_off(struct a6xx_gmu *gmu) u32 val, seqmem_off = 0; /* The second spin of A7xx GPUs messed with some register offsets.. */ - if (adreno_is_a740_family(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) + if (adreno_is_a740_family(adreno_gpu) || adreno_is_a722(adreno_gpu) || + adreno_is_a8xx(adreno_gpu)) seqmem_off = 4; /* Make sure there are no outstanding RPMh votes */ @@ -1100,7 +1104,8 @@ static void a6xx_gmu_rpmh_off(struct a6xx_gmu *gmu) gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS3_DRV0_STATUS + seqmem_off, val, (val & 1), 100, 1000); - if (!adreno_is_a740_family(adreno_gpu) && !adreno_is_a8xx(adreno_gpu)) + if (!adreno_is_a740_family(adreno_gpu) && !adreno_is_a722(adreno_gpu) && + !adreno_is_a8xx(adreno_gpu)) return; gmu_poll_timeout_rscc(gmu, REG_A7XX_RSCC_TCS4_DRV0_STATUS + seqmem_off, diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index e293b4ca808a..2c5faee881b3 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -1273,7 +1273,8 @@ static int hw_init(struct msm_gpu *gpu) if (!(adreno_is_a650_family(adreno_gpu) || adreno_is_a702(adreno_gpu) || adreno_is_a730(adreno_gpu))) { - gmem_range_min = adreno_is_a740_family(adreno_gpu) ? SZ_16M : SZ_1M; + gmem_range_min = (adreno_is_a740_family(adreno_gpu) || + adreno_is_a722(adreno_gpu)) ? SZ_16M : SZ_1M; /* Set the GMEM VA range [0x100000:0x100000 + gpu->gmem - 1] */ gpu_write64(gpu, REG_A6XX_UCHE_GMEM_RANGE_MIN, gmem_range_min); @@ -1338,6 +1339,7 @@ static int hw_init(struct msm_gpu *gpu) /* Enable fault detection */ if (adreno_is_a612(adreno_gpu) || + adreno_is_a722(adreno_gpu) || adreno_is_a730(adreno_gpu) || adreno_is_a740_family(adreno_gpu)) gpu_write(gpu, REG_A6XX_RBBM_INTERFACE_HANG_INT_CNTL, (1 << 30) | 0xcfffff); diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index 3ea8ff8c7404..26c17861cd5d 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -11,12 +11,14 @@ static const unsigned int *gen7_0_0_external_core_regs[] __always_unused; static const unsigned int *gen7_2_0_external_core_regs[] __always_unused; static const unsigned int *gen7_9_0_external_core_regs[] __always_unused; +static const unsigned int *gen7_17_0_external_core_regs[] __always_unused; static const struct gen7_sptp_cluster_registers gen7_9_0_sptp_clusters[] __always_unused; static const u32 gen7_9_0_cx_debugbus_blocks[] __always_unused; #include "adreno_gen7_0_0_snapshot.h" #include "adreno_gen7_2_0_snapshot.h" #include "adreno_gen7_9_0_snapshot.h" +#include "adreno_gen7_17_0_snapshot.h" struct a6xx_gpu_state_obj { const void *handle; @@ -404,8 +406,13 @@ static void a7xx_get_debugbus_blocks(struct msm_gpu *gpu, int i; if (adreno_gpu->info->family == ADRENO_7XX_GEN1) { - debugbus_blocks = gen7_0_0_debugbus_blocks; - debugbus_blocks_count = ARRAY_SIZE(gen7_0_0_debugbus_blocks); + if (adreno_is_a722(adreno_gpu)) { + debugbus_blocks = gen7_17_0_debugbus_blocks; + debugbus_blocks_count = ARRAY_SIZE(gen7_17_0_debugbus_blocks); + } else { + debugbus_blocks = gen7_0_0_debugbus_blocks; + debugbus_blocks_count = ARRAY_SIZE(gen7_0_0_debugbus_blocks); + } gbif_debugbus_blocks = a7xx_gbif_debugbus_blocks; gbif_debugbus_blocks_count = ARRAY_SIZE(a7xx_gbif_debugbus_blocks); } else if (adreno_gpu->info->family == ADRENO_7XX_GEN2) { @@ -678,8 +685,13 @@ static void a7xx_get_dbgahb_clusters(struct msm_gpu *gpu, unsigned dbgahb_clusters_size; if (adreno_gpu->info->family == ADRENO_7XX_GEN1) { - dbgahb_clusters = gen7_0_0_sptp_clusters; - dbgahb_clusters_size = ARRAY_SIZE(gen7_0_0_sptp_clusters); + if (adreno_is_a722(adreno_gpu)) { + dbgahb_clusters = gen7_17_0_sptp_clusters; + dbgahb_clusters_size = ARRAY_SIZE(gen7_17_0_sptp_clusters); + } else { + dbgahb_clusters = gen7_0_0_sptp_clusters; + dbgahb_clusters_size = ARRAY_SIZE(gen7_0_0_sptp_clusters); + } } else if (adreno_gpu->info->family == ADRENO_7XX_GEN2) { dbgahb_clusters = gen7_2_0_sptp_clusters; dbgahb_clusters_size = ARRAY_SIZE(gen7_2_0_sptp_clusters); @@ -839,8 +851,13 @@ static void a7xx_get_clusters(struct msm_gpu *gpu, unsigned clusters_size; if (adreno_gpu->info->family == ADRENO_7XX_GEN1) { - clusters = gen7_0_0_clusters; - clusters_size = ARRAY_SIZE(gen7_0_0_clusters); + if (adreno_is_a722(adreno_gpu)) { + clusters = gen7_17_0_clusters; + clusters_size = ARRAY_SIZE(gen7_17_0_clusters); + } else { + clusters = gen7_0_0_clusters; + clusters_size = ARRAY_SIZE(gen7_0_0_clusters); + } } else if (adreno_gpu->info->family == ADRENO_7XX_GEN2) { clusters = gen7_2_0_clusters; clusters_size = ARRAY_SIZE(gen7_2_0_clusters); @@ -977,8 +994,13 @@ static void a7xx_get_shaders(struct msm_gpu *gpu, int i; if (adreno_gpu->info->family == ADRENO_7XX_GEN1) { - shader_blocks = gen7_0_0_shader_blocks; - num_shader_blocks = ARRAY_SIZE(gen7_0_0_shader_blocks); + if (adreno_is_a722(adreno_gpu)) { + shader_blocks = gen7_17_0_shader_blocks; + num_shader_blocks = ARRAY_SIZE(gen7_17_0_shader_blocks); + } else { + shader_blocks = gen7_0_0_shader_blocks; + num_shader_blocks = ARRAY_SIZE(gen7_0_0_shader_blocks); + } } else if (adreno_gpu->info->family == ADRENO_7XX_GEN2) { shader_blocks = gen7_2_0_shader_blocks; num_shader_blocks = ARRAY_SIZE(gen7_2_0_shader_blocks); @@ -1379,8 +1401,13 @@ static void a7xx_get_registers(struct msm_gpu *gpu, const struct gen7_reg_list *reglist; if (adreno_gpu->info->family == ADRENO_7XX_GEN1) { - reglist = gen7_0_0_reg_list; - pre_crashdumper_regs = gen7_0_0_pre_crashdumper_gpu_registers; + if (adreno_is_a722(adreno_gpu)) { + reglist = gen7_17_0_reg_list; + pre_crashdumper_regs = gen7_9_0_pre_crashdumper_gpu_registers; + } else { + reglist = gen7_0_0_reg_list; + pre_crashdumper_regs = gen7_0_0_pre_crashdumper_gpu_registers; + } } else if (adreno_gpu->info->family == ADRENO_7XX_GEN2) { reglist = gen7_2_0_reg_list; pre_crashdumper_regs = gen7_0_0_pre_crashdumper_gpu_registers; @@ -1436,7 +1463,9 @@ static void a7xx_get_post_crashdumper_registers(struct msm_gpu *gpu, const u32 *regs; BUG_ON(adreno_gpu->info->family > ADRENO_7XX_GEN3); - regs = gen7_0_0_post_crashdumper_registers; + regs = adreno_is_a722(adreno_gpu) ? + gen7_17_0_post_crashdumper_registers : + gen7_0_0_post_crashdumper_registers; a7xx_get_ahb_gpu_registers(gpu, a6xx_state, regs, @@ -1543,19 +1572,35 @@ static void a7xx_get_indexed_registers(struct msm_gpu *gpu, { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); const struct a6xx_indexed_registers *indexed_regs; + const struct a6xx_indexed_registers *mempool_regs; int i, indexed_count, mempool_count; + bool concurrent_binning; - if (adreno_gpu->info->family <= ADRENO_7XX_GEN2) { + if (adreno_is_a722(adreno_gpu)) { + /* + * Eliza has no BV or LPAC SQE — skip the BV/LPAC indexed + * registers and the BV mempool + */ + indexed_regs = gen7_17_0_cp_indexed_reglist; + indexed_count = ARRAY_SIZE(gen7_17_0_cp_indexed_reglist); + mempool_regs = a7xx_cp_mempool_indexed; + mempool_count = ARRAY_SIZE(a7xx_cp_mempool_indexed); + concurrent_binning = false; + } else if (adreno_gpu->info->family <= ADRENO_7XX_GEN2) { indexed_regs = a7xx_indexed_reglist; indexed_count = ARRAY_SIZE(a7xx_indexed_reglist); + mempool_regs = a7xx_cp_bv_mempool_indexed; + mempool_count = ARRAY_SIZE(a7xx_cp_bv_mempool_indexed); + concurrent_binning = true; } else { BUG_ON(adreno_gpu->info->family != ADRENO_7XX_GEN3); indexed_regs = gen7_9_0_cp_indexed_reg_list; indexed_count = ARRAY_SIZE(gen7_9_0_cp_indexed_reg_list); + mempool_regs = a7xx_cp_bv_mempool_indexed; + mempool_count = ARRAY_SIZE(a7xx_cp_bv_mempool_indexed); + concurrent_binning = true; } - mempool_count = ARRAY_SIZE(a7xx_cp_bv_mempool_indexed); - a6xx_state->indexed_regs = state_kcalloc(a6xx_state, indexed_count + mempool_count, sizeof(*a6xx_state->indexed_regs)); @@ -1570,15 +1615,17 @@ static void a7xx_get_indexed_registers(struct msm_gpu *gpu, &a6xx_state->indexed_regs[i]); gpu_rmw(gpu, REG_A6XX_CP_CHICKEN_DBG, 0, BIT(2)); - gpu_rmw(gpu, REG_A7XX_CP_BV_CHICKEN_DBG, 0, BIT(2)); + if (concurrent_binning) + gpu_rmw(gpu, REG_A7XX_CP_BV_CHICKEN_DBG, 0, BIT(2)); /* Get the contents of the CP_BV mempool */ for (i = 0; i < mempool_count; i++) - a6xx_get_indexed_regs(gpu, a6xx_state, &a7xx_cp_bv_mempool_indexed[i], + a6xx_get_indexed_regs(gpu, a6xx_state, &mempool_regs[i], &a6xx_state->indexed_regs[indexed_count + i]); gpu_rmw(gpu, REG_A6XX_CP_CHICKEN_DBG, BIT(2), 0); - gpu_rmw(gpu, REG_A7XX_CP_BV_CHICKEN_DBG, BIT(2), 0); + if (concurrent_binning) + gpu_rmw(gpu, REG_A7XX_CP_BV_CHICKEN_DBG, BIT(2), 0); return; } diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h index 0a13a65f89ac..6f592394f866 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h @@ -474,6 +474,11 @@ static const struct a6xx_indexed_registers a7xx_cp_bv_mempool_indexed[] = { REG_A7XX_CP_BV_MEM_POOL_DBG_DATA, 0x2200, NULL }, }; +static const struct a6xx_indexed_registers a7xx_cp_mempool_indexed[] = { + { "CP_MEM_POOL_DBG", REG_A6XX_CP_MEM_POOL_DBG_ADDR, + REG_A6XX_CP_MEM_POOL_DBG_DATA, 0x2200, NULL }, +}; + #define DEBUGBUS(_id, _count) { .id = _id, .name = #_id, .count = _count } static const struct a6xx_debugbus_block { diff --git a/drivers/gpu/drm/msm/adreno/adreno_gen7_17_0_snapshot.h b/drivers/gpu/drm/msm/adreno/adreno_gen7_17_0_snapshot.h new file mode 100644 index 000000000000..00a4a0fc97d2 --- /dev/null +++ b/drivers/gpu/drm/msm/adreno/adreno_gen7_17_0_snapshot.h @@ -0,0 +1,428 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef __ADRENO_GEN7_17_0_SNAPSHOT_H +#define __ADRENO_GEN7_17_0_SNAPSHOT_H + +#include "a6xx_gpu_state.h" + +/* + * Snapshot tables for Adreno A722 (Eliza). + * Cluster sub-arrays that are identical to A730 reference gen7_0_0_* + * symbols; adreno_gen7_0_0_snapshot.h is included first in the TU. + */ + +static const u32 gen7_17_0_rscc_registers[] = { + 0x14000, 0x14034, 0x14036, 0x14036, 0x14040, 0x14042, 0x14044, 0x14045, + 0x14047, 0x14047, 0x14080, 0x14084, 0x14089, 0x1408c, 0x14091, 0x14094, + 0x14099, 0x1409c, 0x140a1, 0x140a4, 0x140a9, 0x140ac, 0x140b1, 0x140b4, + 0x140b9, 0x140bc, 0x14100, 0x14104, 0x14114, 0x14119, 0x14124, 0x14132, + 0x14154, 0x1416b, 0x14340, 0x14341, 0x14344, 0x14344, 0x14346, 0x1437c, + 0x143f0, 0x143f8, 0x143fa, 0x143fe, 0x14400, 0x14404, 0x14406, 0x1440a, + 0x1440c, 0x14410, 0x14412, 0x14416, 0x14418, 0x1441c, 0x1441e, 0x14422, + 0x14424, 0x14424, 0x14498, 0x144a0, 0x144a2, 0x144a6, 0x144a8, 0x144ac, + 0x144ae, 0x144b2, 0x144b4, 0x144b8, 0x144ba, 0x144be, 0x144c0, 0x144c4, + 0x144c6, 0x144ca, 0x144cc, 0x144cc, 0x14540, 0x14548, 0x1454a, 0x1454e, + 0x14550, 0x14554, 0x14556, 0x1455a, 0x1455c, 0x14560, 0x14562, 0x14566, + 0x14568, 0x1456c, 0x1456e, 0x14572, 0x14574, 0x14574, 0x145e8, 0x145f0, + 0x145f2, 0x145f6, 0x145f8, 0x145fc, 0x145fe, 0x14602, 0x14604, 0x14608, + 0x1460a, 0x1460e, 0x14610, 0x14614, 0x14616, 0x1461a, 0x1461c, 0x1461c, + 0x14690, 0x14698, 0x1469a, 0x1469e, 0x146a0, 0x146a4, 0x146a6, 0x146aa, + 0x146ac, 0x146b0, 0x146b2, 0x146b6, 0x146b8, 0x146bc, 0x146be, 0x146c2, + 0x146c4, 0x146c4, 0x14738, 0x14740, 0x14742, 0x14746, 0x14748, 0x1474c, + 0x1474e, 0x14752, 0x14754, 0x14758, 0x1475a, 0x1475e, 0x14760, 0x14764, + 0x14766, 0x1476a, 0x1476c, 0x1476c, 0x147e0, 0x147e8, 0x147ea, 0x147ee, + 0x147f0, 0x147f4, 0x147f6, 0x147fa, 0x147fc, 0x14800, 0x14802, 0x14806, + 0x14808, 0x1480c, 0x1480e, 0x14812, 0x14814, 0x14814, 0x14888, 0x14890, + 0x14892, 0x14896, 0x14898, 0x1489c, 0x1489e, 0x148a2, 0x148a4, 0x148a8, + 0x148aa, 0x148ae, 0x148b0, 0x148b4, 0x148b6, 0x148ba, 0x148bc, 0x148bc, + 0x14930, 0x14938, 0x1493a, 0x1493e, 0x14940, 0x14944, 0x14946, 0x1494a, + 0x1494c, 0x14950, 0x14952, 0x14956, 0x14958, 0x1495c, 0x1495e, 0x14962, + 0x14964, 0x14964, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_rscc_registers), 8)); + +static const u32 gen7_17_0_cpr_registers[] = { + 0x26800, 0x26805, 0x26808, 0x2680c, 0x26814, 0x26814, 0x2681c, 0x2681c, + 0x26820, 0x26838, 0x26840, 0x26840, 0x26848, 0x26848, 0x26850, 0x26850, + 0x26880, 0x2688e, 0x26980, 0x269b0, 0x269c0, 0x269c2, 0x269c6, 0x269c8, + 0x269e0, 0x269ee, 0x269fb, 0x269ff, 0x26a02, 0x26a07, 0x26a09, 0x26a0b, + 0x26a10, 0x26b0f, 0x27440, 0x27441, 0x27444, 0x27444, 0x27480, 0x274a2, + 0x274ac, 0x274c4, 0x274c8, 0x274da, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_cpr_registers), 8)); + +static const u32 gen7_17_0_gpucc_registers[] = { + 0x24000, 0x2400f, 0x24400, 0x2440f, 0x24800, 0x24805, 0x24c00, 0x24cff, + 0x25400, 0x25404, 0x25800, 0x25804, 0x25c00, 0x25c04, 0x26000, 0x26004, + 0x26400, 0x26405, 0x26414, 0x2641d, 0x2642a, 0x2642c, 0x2642e, 0x26432, + 0x26434, 0x26434, 0x26443, 0x26457, 0x26459, 0x2645d, 0x2645f, 0x26464, + 0x26477, 0x26479, 0x26489, 0x2648b, 0x2649a, 0x2649b, 0x264ad, 0x264af, + 0x264b1, 0x264b5, 0x264d6, 0x264d8, 0x264e7, 0x264e9, 0x264f9, 0x264fa, + 0x2650a, 0x2650d, 0x2651f, 0x26520, 0x2652d, 0x2652f, 0x2653e, 0x2653e, + 0x26540, 0x2654e, 0x26554, 0x26573, 0x26576, 0x26576, 0x26593, 0x26593, + 0x26600, 0x26616, 0x26620, 0x2662d, 0x26630, 0x26631, 0x26635, 0x26635, + 0x26637, 0x26637, 0x2663a, 0x2663a, 0x26642, 0x26642, 0x26656, 0x26658, + 0x2665b, 0x2665d, 0x2665f, 0x26662, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_gpucc_registers), 8)); + +static const u32 *gen7_17_0_external_core_regs[] = { + gen7_17_0_gpucc_registers, + gen7_17_0_cpr_registers, +}; + +static const u32 gen7_17_0_debugbus_blocks[] = { + A7XX_DBGBUS_CP_0_0, + A7XX_DBGBUS_CP_0_1, + A7XX_DBGBUS_RBBM, + A7XX_DBGBUS_HLSQ, + A7XX_DBGBUS_UCHE_0, + A7XX_DBGBUS_TESS_BR, + A7XX_DBGBUS_PC_BR, + A7XX_DBGBUS_VFDP_BR, + A7XX_DBGBUS_VPC_BR, + A7XX_DBGBUS_TSE_BR, + A7XX_DBGBUS_RAS_BR, + A7XX_DBGBUS_VSC, + A7XX_DBGBUS_COM_0, + A7XX_DBGBUS_LRZ_BR, + A7XX_DBGBUS_UFC_0, + A7XX_DBGBUS_UFC_1, + A7XX_DBGBUS_GMU_GX, + A7XX_DBGBUS_DBGC, + A7XX_DBGBUS_GPC_BR, + A7XX_DBGBUS_LARC, + A7XX_DBGBUS_HLSQ_SPTP, + A7XX_DBGBUS_RB_0, + A7XX_DBGBUS_RB_1, + A7XX_DBGBUS_UCHE_WRAPPER, + A7XX_DBGBUS_CCU_0, + A7XX_DBGBUS_CCU_1, + A7XX_DBGBUS_VFD_BR_0, + A7XX_DBGBUS_VFD_BR_1, + A7XX_DBGBUS_VFD_BR_2, + A7XX_DBGBUS_VFD_BR_3, + A7XX_DBGBUS_USP_0, + A7XX_DBGBUS_USP_1, + A7XX_DBGBUS_TP_0, + A7XX_DBGBUS_TP_1, + A7XX_DBGBUS_TP_2, + A7XX_DBGBUS_TP_3, + A7XX_DBGBUS_USPTP_0, + A7XX_DBGBUS_USPTP_1, + A7XX_DBGBUS_USPTP_2, + A7XX_DBGBUS_USPTP_3, +}; + +static const struct gen7_sel_reg gen7_17_0_rb_rac_sel = { + .host_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_HOST, + .cd_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD, + .val = 0x0, +}; + +static const struct gen7_sel_reg gen7_17_0_rb_rbp_sel = { + .host_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_HOST, + .cd_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD, + .val = 0x9, +}; + +static const u32 gen7_17_0_post_crashdumper_registers[] = { + 0x00535, 0x00535, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_post_crashdumper_registers), 8)); + +static const u32 gen7_17_0_gpu_registers[] = { + 0x00000, 0x00000, 0x00002, 0x00002, 0x00011, 0x00012, 0x00016, 0x0001b, + 0x0001f, 0x00032, 0x00038, 0x0003c, 0x00042, 0x00042, 0x00044, 0x00044, + 0x00047, 0x00047, 0x00049, 0x0004a, 0x0004c, 0x0004c, 0x00050, 0x00050, + 0x00056, 0x00056, 0x00073, 0x00075, 0x000ad, 0x000ae, 0x000b0, 0x000b0, + 0x000b4, 0x000b4, 0x000b8, 0x000b8, 0x000bc, 0x000bc, 0x000c0, 0x000c0, + 0x000c4, 0x000c4, 0x000c8, 0x000c8, 0x000cc, 0x000cc, 0x000d0, 0x000d0, + 0x000d4, 0x000d4, 0x000d8, 0x000d8, 0x000dc, 0x000dc, 0x000e0, 0x000e0, + 0x000e4, 0x000e4, 0x000e8, 0x000e8, 0x000ec, 0x000ec, 0x000f0, 0x000f0, + 0x000f4, 0x000f4, 0x000f8, 0x000f8, 0x00100, 0x00100, 0x00104, 0x0010b, + 0x0010f, 0x0011d, 0x0012f, 0x0012f, 0x00200, 0x0020d, 0x00215, 0x00243, + 0x00260, 0x00268, 0x00272, 0x00274, 0x00286, 0x00286, 0x0028a, 0x0028a, + 0x0028c, 0x0028c, 0x00300, 0x00401, 0x00500, 0x00500, 0x00507, 0x0050b, + 0x0050f, 0x0050f, 0x00511, 0x00511, 0x00533, 0x00534, 0x00540, 0x00555, + 0x00564, 0x00567, 0x00800, 0x00808, 0x00810, 0x00813, 0x00820, 0x00821, + 0x00823, 0x00827, 0x00830, 0x00834, 0x00840, 0x00841, 0x00843, 0x00847, + 0x0084f, 0x00886, 0x008a0, 0x008ab, 0x008c0, 0x008c0, 0x008c4, 0x008c5, + 0x008d0, 0x008dd, 0x008f0, 0x008f3, 0x00900, 0x00903, 0x00908, 0x00911, + 0x00928, 0x0093e, 0x00942, 0x0094d, 0x00980, 0x00984, 0x0098d, 0x0098f, + 0x009b0, 0x009b4, 0x009c2, 0x009c9, 0x009ce, 0x009d7, 0x00a00, 0x00a00, + 0x00a02, 0x00a03, 0x00a10, 0x00a4f, 0x00a67, 0x00a6c, 0x00a9c, 0x00a9f, + 0x00c00, 0x00c00, 0x00c02, 0x00c04, 0x00c06, 0x00c06, 0x00c10, 0x00cd9, + 0x00ce0, 0x00d0c, 0x00df0, 0x00df4, 0x00e01, 0x00e02, 0x00e07, 0x00e0e, + 0x00e10, 0x00e12, 0x00e17, 0x00e17, 0x00e19, 0x00e19, 0x00e1b, 0x00e2b, + 0x00e30, 0x00e32, 0x00e38, 0x00e3c, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_gpu_registers), 8)); + +static const u32 gen7_17_0_dbgc_registers[] = { + 0x00600, 0x0061c, 0x0061e, 0x00634, 0x00640, 0x0065a, 0x00679, 0x0067a, + 0x00699, 0x00699, 0x0069b, 0x0069e, 0x18400, 0x1841c, 0x1841e, 0x18434, + 0x18440, 0x1845c, 0x18479, 0x1847c, 0x18580, 0x18581, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_dbgc_registers), 8)); + +static const u32 gen7_17_0_noncontext_pipe_br_registers[] = { + 0x00887, 0x0088c, 0x08600, 0x08600, 0x08602, 0x08602, 0x08610, 0x0861b, + 0x08620, 0x08620, 0x08630, 0x08630, 0x08637, 0x08639, 0x08640, 0x08640, + 0x09600, 0x09600, 0x09602, 0x09603, 0x0960a, 0x09616, 0x09624, 0x0963a, + 0x09640, 0x09640, 0x09e00, 0x09e00, 0x09e02, 0x09e07, 0x09e0a, 0x09e16, + 0x09e19, 0x09e19, 0x09e1c, 0x09e1c, 0x09e20, 0x09e25, 0x09e30, 0x09e31, + 0x09e40, 0x09e51, 0x09e64, 0x09e64, 0x09e70, 0x09e72, 0x09e78, 0x09e79, + 0x09e80, 0x09fff, 0x0a600, 0x0a600, 0x0a603, 0x0a603, 0x0a610, 0x0a61f, + 0x0a630, 0x0a631, 0x0a638, 0x0a638, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_noncontext_pipe_br_registers), 8)); + +static const u32 gen7_17_0_noncontext_rb_rac_pipe_br_registers[] = { + 0x08e10, 0x08e1c, 0x08e20, 0x08e25, 0x08e51, 0x08e54, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_noncontext_rb_rac_pipe_br_registers), 8)); + +static const u32 gen7_17_0_noncontext_rb_rbp_pipe_br_registers[] = { + 0x08e01, 0x08e01, 0x08e04, 0x08e04, 0x08e06, 0x08e09, 0x08e0c, 0x08e0c, + 0x08e28, 0x08e28, 0x08e2c, 0x08e35, 0x08e3b, 0x08e3f, 0x08e50, 0x08e50, + 0x08e5b, 0x08e5d, 0x08e5f, 0x08e5f, 0x08e61, 0x08e61, 0x08e63, 0x08e65, + 0x08e68, 0x08e68, 0x08e70, 0x08e79, 0x08e80, 0x08e8f, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_noncontext_rb_rbp_pipe_br_registers), 8)); + +static const u32 gen7_17_0_pc_cluster_fe_pipe_br_registers[] = { + 0x09800, 0x09804, 0x09806, 0x0980a, 0x09810, 0x09811, 0x09884, 0x09886, + 0x09970, 0x09972, 0x09b00, 0x09b08, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_pc_cluster_fe_pipe_br_registers), 8)); + +static const u32 gen7_17_0_sp_cluster_sp_ps_pipe_lpac_hlsq_state_registers[] = { + 0x0aa40, 0x0aabf, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_sp_cluster_sp_ps_pipe_lpac_hlsq_state_registers), 8)); + +static const u32 gen7_17_0_sp_cluster_sp_ps_pipe_lpac_usptp_registers[] = { + 0x0aa40, 0x0aabf, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_sp_cluster_sp_ps_pipe_lpac_usptp_registers), 8)); + +static const u32 gen7_17_0_non_context_tpl1_pipe_none_usptp_registers[] = { + 0x0b602, 0x0b602, 0x0b604, 0x0b604, 0x0b608, 0x0b60c, 0x0b60f, 0x0b621, + 0x0b630, 0x0b633, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_non_context_tpl1_pipe_none_usptp_registers), 8)); + +static const u32 gen7_17_0_non_context_tpl1_pipe_br_usptp_registers[] = { + 0x0b600, 0x0b600, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_non_context_tpl1_pipe_br_usptp_registers), 8)); + +static const u32 gen7_17_0_tpl1_cluster_sp_vs_pipe_br_usptp_registers[] = { + 0x0b300, 0x0b307, 0x0b309, 0x0b309, 0x0b310, 0x0b310, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_tpl1_cluster_sp_vs_pipe_br_usptp_registers), 8)); + +static const u32 gen7_17_0_tpl1_cluster_sp_ps_pipe_br_usptp_registers[] = { + 0x0b180, 0x0b183, 0x0b190, 0x0b195, 0x0b2c0, 0x0b2d5, 0x0b300, 0x0b307, + 0x0b309, 0x0b309, 0x0b310, 0x0b310, + UINT_MAX, UINT_MAX, +}; +static_assert(IS_ALIGNED(sizeof(gen7_17_0_tpl1_cluster_sp_ps_pipe_br_usptp_registers), 8)); + +/* No BV pipe — gen7_0_0_* sub-arrays are shared from adreno_gen7_0_0_snapshot.h */ +static struct gen7_cluster_registers gen7_17_0_clusters[] = { + { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT, + gen7_17_0_noncontext_pipe_br_registers, }, + { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT, + gen7_17_0_noncontext_rb_rac_pipe_br_registers, &gen7_17_0_rb_rac_sel, }, + { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT, + gen7_17_0_noncontext_rb_rbp_pipe_br_registers, &gen7_17_0_rb_rbp_sel, }, + { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_0, + gen7_0_0_rb_rac_cluster_ps_pipe_br_registers, &gen7_17_0_rb_rac_sel, }, + { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_1, + gen7_0_0_rb_rac_cluster_ps_pipe_br_registers, &gen7_17_0_rb_rac_sel, }, + { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_0, + gen7_0_0_rb_rbp_cluster_ps_pipe_br_registers, &gen7_17_0_rb_rbp_sel, }, + { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_1, + gen7_0_0_rb_rbp_cluster_ps_pipe_br_registers, &gen7_17_0_rb_rbp_sel, }, + { A7XX_CLUSTER_GRAS, PIPE_BR, STATE_FORCE_CTXT_0, + gen7_0_0_gras_cluster_gras_pipe_br_registers, }, + { A7XX_CLUSTER_GRAS, PIPE_BR, STATE_FORCE_CTXT_1, + gen7_0_0_gras_cluster_gras_pipe_br_registers, }, + { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0, + gen7_17_0_pc_cluster_fe_pipe_br_registers, }, + { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1, + gen7_17_0_pc_cluster_fe_pipe_br_registers, }, + { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0, + gen7_0_0_vfd_cluster_fe_pipe_bv_registers, }, + { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1, + gen7_0_0_vfd_cluster_fe_pipe_bv_registers, }, + { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0, + gen7_0_0_vpc_cluster_fe_pipe_br_registers, }, + { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1, + gen7_0_0_vpc_cluster_fe_pipe_br_registers, }, + { A7XX_CLUSTER_PC_VS, PIPE_BR, STATE_FORCE_CTXT_0, + gen7_0_0_vpc_cluster_pc_vs_pipe_br_registers, }, + { A7XX_CLUSTER_PC_VS, PIPE_BR, STATE_FORCE_CTXT_1, + gen7_0_0_vpc_cluster_pc_vs_pipe_br_registers, }, + { A7XX_CLUSTER_VPC_PS, PIPE_BR, STATE_FORCE_CTXT_0, + gen7_0_0_vpc_cluster_vpc_ps_pipe_br_registers, }, + { A7XX_CLUSTER_VPC_PS, PIPE_BR, STATE_FORCE_CTXT_1, + gen7_0_0_vpc_cluster_vpc_ps_pipe_br_registers, }, +}; + +/* No BV pipe; 2 SPs, 2 USPTPs */ +static struct gen7_sptp_cluster_registers gen7_17_0_sptp_clusters[] = { + { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_HLSQ_STATE, + gen7_0_0_sp_noncontext_pipe_br_hlsq_state_registers, 0xae00 }, + { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_SP_TOP, + gen7_0_0_sp_noncontext_pipe_br_sp_top_registers, 0xae00 }, + { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_USPTP, + gen7_0_0_sp_noncontext_pipe_br_usptp_registers, 0xae00 }, + { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_HLSQ_STATE, + gen7_0_0_sp_cluster_sp_vs_pipe_br_hlsq_state_registers, 0xa800 }, + { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_SP_TOP, + gen7_0_0_sp_cluster_sp_vs_pipe_br_sp_top_registers, 0xa800 }, + { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_USPTP, + gen7_0_0_sp_cluster_sp_vs_pipe_br_usptp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_HLSQ_STATE, + gen7_0_0_sp_cluster_sp_vs_pipe_br_hlsq_state_registers, 0xa800 }, + { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_SP_TOP, + gen7_0_0_sp_cluster_sp_vs_pipe_br_sp_top_registers, 0xa800 }, + { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_USPTP, + gen7_0_0_sp_cluster_sp_vs_pipe_br_usptp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_HLSQ_STATE, + gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_HLSQ_DP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_SP_TOP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_USPTP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_HLSQ_STATE, + gen7_17_0_sp_cluster_sp_ps_pipe_lpac_hlsq_state_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_USPTP, + gen7_17_0_sp_cluster_sp_ps_pipe_lpac_usptp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_HLSQ_STATE, + gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_HLSQ_DP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_SP_TOP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_USPTP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_HLSQ_DP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_SP_TOP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_USPTP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_HLSQ_DP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_SP_TOP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 }, + { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_USPTP, + gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 }, + { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_NONE, 0, A7XX_USPTP, + gen7_17_0_non_context_tpl1_pipe_none_usptp_registers, 0xb600 }, + { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_BR, 0, A7XX_USPTP, + gen7_17_0_non_context_tpl1_pipe_br_usptp_registers, 0xb600 }, + { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_USPTP, + gen7_17_0_tpl1_cluster_sp_vs_pipe_br_usptp_registers, 0xb000 }, + { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_USPTP, + gen7_17_0_tpl1_cluster_sp_vs_pipe_br_usptp_registers, 0xb000 }, + { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_USPTP, + gen7_17_0_tpl1_cluster_sp_ps_pipe_br_usptp_registers, 0xb000 }, + { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_USPTP, + gen7_17_0_tpl1_cluster_sp_ps_pipe_br_usptp_registers, 0xb000 }, + { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_USPTP, + gen7_17_0_tpl1_cluster_sp_ps_pipe_br_usptp_registers, 0xb000 }, + { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_USPTP, + gen7_17_0_tpl1_cluster_sp_ps_pipe_br_usptp_registers, 0xb000 }, +}; + +static struct gen7_shader_block gen7_17_0_shader_blocks[] = { + { A7XX_TP0_TMO_DATA, 0x0200, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_TP0_SMO_DATA, 0x0080, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_TP0_MIPMAP_BASE_DATA, 0x03c0, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_INST_DATA, 0x0800, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_INST_DATA_1, 0x0800, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_LB_0_DATA, 0x0800, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_LB_1_DATA, 0x0800, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_LB_2_DATA, 0x0800, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_LB_3_DATA, 0x0800, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_LB_4_DATA, 0x0800, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_LB_5_DATA, 0x0800, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_CB_RAM, 0x0390, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_INST_TAG, 0x0090, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_TMO_TAG, 0x0080, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_SMO_TAG, 0x0080, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_STATE_DATA, 0x0040, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_HWAVE_RAM, 0x0100, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_SP_L0_INST_BUF, 0x0050, 2, 2, PIPE_BR, A7XX_USPTP }, + { A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CPS_BE_CTXT_BUF_RAM_TAG, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x0300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_GFX_CPS_BE_CTXT_BUF_RAM, 0x0300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CHUNK_CVS_RAM, 0x01c0, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CHUNK_CPS_RAM, 0x0300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x0040, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x0040, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CVS_MISC_RAM, 0x0280, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CPS_MISC_RAM, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CPS_MISC_RAM_1, 0x0200, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_INST_RAM, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_INST_RAM_TAG, 0x0080, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x0064, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x0064, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_INST_RAM_1, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_STPROC_META, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_BV_BE_META, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_DATAPATH_META, 0x0020, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_FRONTEND_META, 0x0040, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_INDIRECT_META, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, + { A7XX_HLSQ_BACKEND_META, 0x0040, 1, 1, PIPE_BR, A7XX_HLSQ_STATE }, +}; + +static struct gen7_reg_list gen7_17_0_reg_list[] = { + { gen7_17_0_gpu_registers, NULL }, + { gen7_17_0_dbgc_registers, NULL }, + { NULL, NULL }, +}; + +static const struct a6xx_indexed_registers gen7_17_0_cp_indexed_reglist[] = { + { "CP_SQE_STAT", REG_A6XX_CP_SQE_STAT_ADDR, + REG_A6XX_CP_SQE_STAT_DATA, 0x40, NULL }, + { "CP_DRAW_STATE", REG_A6XX_CP_DRAW_STATE_ADDR, + REG_A6XX_CP_DRAW_STATE_DATA, 0x100, NULL }, + { "CP_SQE_UCODE_DBG", REG_A6XX_CP_SQE_UCODE_DBG_ADDR, + REG_A6XX_CP_SQE_UCODE_DBG_DATA, 0x8000, NULL }, + { "CP_ROQ_DBG", REG_A6XX_CP_ROQ_DBG_ADDR, + REG_A6XX_CP_ROQ_DBG_DATA, 0, a7xx_get_cp_roq_size }, +}; + +#endif /* __ADRENO_GEN7_17_0_SNAPSHOT_H */ diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 1f201322cb6e..114a40f79ef3 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -562,6 +562,11 @@ static inline int adreno_is_x185(struct adreno_gpu *gpu) return gpu->info->chip_ids[0] == 0x43050c01; } +static inline int adreno_is_a722(struct adreno_gpu *gpu) +{ + return gpu->info->chip_ids[0] == 0x43020100; +} + static inline int adreno_is_a740_family(struct adreno_gpu *gpu) { if (WARN_ON_ONCE(!gpu->info)) From cee19f08f460bc7a9b531472f25137aa8626cd51 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Sat, 18 Jul 2026 02:11:29 +0530 Subject: [PATCH 021/121] dt-bindings: arm-smmu: Document GPU SMMU for Eliza SoC Add specific compatible strings to document the GPU SMMU present in the Eliza SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/740962/ Message-ID: <20260718-eliza-gpu-v2-4-64379dbebd7a@oss.qualcomm.com> Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/iommu/arm,smmu.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml index ad15fda5c25e..d87281026a71 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml @@ -95,6 +95,7 @@ properties: - description: Qcom Adreno GPUs implementing "qcom,smmu-500" and "arm,mmu-500" items: - enum: + - qcom,eliza-smmu-500 - qcom,glymur-smmu-500 - qcom,hawi-smmu-500 - qcom,kaanapali-smmu-500 @@ -572,6 +573,7 @@ allOf: compatible: items: - enum: + - qcom,eliza-smmu-500 - qcom,glymur-smmu-500 - qcom,hawi-smmu-500 - qcom,kaanapali-smmu-500 From 0b69e4f4ee325ee0b50396657fd19d4d7b0a6294 Mon Sep 17 00:00:00 2001 From: Puranam V G Tejaswi Date: Sat, 18 Jul 2026 02:11:30 +0530 Subject: [PATCH 022/121] dt-bindings: display/msm: Document Adreno 722 GPU and GMU Adreno 722 found in Eliza chipset belongs to the A7x Gen1 family. It is derived from A730 and shares the same IP-level configurations: HWCG registers, protected registers, GBIF CX registers and gmu_cgc_mode. Major differences include lower cache/core counts, 1MB GMEM, no Concurrent Binning & LPAC support. Some of the peripheral blocks like RSCC are from A740 that resulted in updates to RSC layout. Update the dt-binding docs to document this GPU and GMU. Signed-off-by: Puranam V G Tejaswi Acked-by: Krzysztof Kozlowski Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/740964/ Message-ID: <20260718-eliza-gpu-v2-5-64379dbebd7a@oss.qualcomm.com> Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/display/msm/gmu.yaml | 1 + Documentation/devicetree/bindings/display/msm/gpu.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/gmu.yaml b/Documentation/devicetree/bindings/display/msm/gmu.yaml index 8578c2f8122e..9e459f12ce3f 100644 --- a/Documentation/devicetree/bindings/display/msm/gmu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gmu.yaml @@ -262,6 +262,7 @@ allOf: compatible: contains: enum: + - qcom,adreno-gmu-722.0 - qcom,adreno-gmu-730.1 - qcom,adreno-gmu-740.1 - qcom,adreno-gmu-750.1 diff --git a/Documentation/devicetree/bindings/display/msm/gpu.yaml b/Documentation/devicetree/bindings/display/msm/gpu.yaml index 992d3de7c117..e5efe2d35ddc 100644 --- a/Documentation/devicetree/bindings/display/msm/gpu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gpu.yaml @@ -445,6 +445,7 @@ allOf: - qcom,adreno-680.1 - qcom,adreno-690.0 - qcom,adreno-730.1 + - qcom,adreno-43020100 - qcom,adreno-43030c00 - qcom,adreno-43050a01 - qcom,adreno-43050c01 From c08b809d8c2cd32ce14715f6c5712dbcdd920b45 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Jul 2026 11:26:09 +0200 Subject: [PATCH 023/121] drm/msm: Fix stale comments in uapi/drm/msm_drm.h At some point drm_msm_gem_syncobj was renamed to drm_msm_syncobj and MSM_SUBMIT_SYNCOBJ_FLAGS was renamed to MSM_SYNCOBJ_FLAGS but some comments still refer to the old names. Update the comments with the new names. Signed-off-by: Hans de Goede Patchwork: https://patchwork.freedesktop.org/patch/742717/ Message-ID: <20260728092609.22049-1-johannes.goede@oss.qualcomm.com> Signed-off-by: Rob Clark --- include/uapi/drm/msm_drm.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 7f2e594be4eb..3d363f12f66a 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -228,7 +228,7 @@ struct drm_msm_gem_cpu_fini { struct drm_msm_syncobj { __u32 handle; /* in, syncobj handle. */ - __u32 flags; /* in, from MSM_SUBMIT_SYNCOBJ_FLAGS */ + __u32 flags; /* in, from MSM_SYNCOBJ_FLAGS */ __u64 point; /* in, timepoint for timeline syncobjs. */ }; @@ -393,9 +393,9 @@ struct drm_msm_vm_bind { __s32 fence_fd; /** @queue_id: in, submitqueue id */ __u32 queue_id; - /** @in_syncobjs: in, ptr to array of drm_msm_gem_syncobj */ + /** @in_syncobjs: in, ptr to array of drm_msm_syncobj */ __u64 in_syncobjs; - /** @out_syncobjs: in, ptr to array of drm_msm_gem_syncobj */ + /** @out_syncobjs: in, ptr to array of drm_msm_syncobj */ __u64 out_syncobjs; /** @nr_in_syncobjs: in, number of entries in in_syncobj */ __u32 nr_in_syncobjs; From 83723f32cb3de23d45c1ac09241b5e0cfb32cc9b Mon Sep 17 00:00:00 2001 From: Anna Maniscalco Date: Thu, 23 Jul 2026 21:23:22 +0200 Subject: [PATCH 024/121] drm/msm: remove objects from evit list after pinning them Once objects are pinned they should not be kept in the evict list as that will cause drm_gpuvm_validate to keep ieterating a growing list of objects needlessly. Once an object is pinned remove it from the list. Fixes: 2e6a8a1fe2b2 ("drm/msm: Add VM_BIND ioctl") Signed-off-by: Anna Maniscalco Patchwork: https://patchwork.freedesktop.org/patch/742166/ Message-ID: <20260723-evict_list_fix-v2-1-bd0725e56253@gmail.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gem_vma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 3ed05ab0eeef..50d4812bdb4c 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -458,6 +458,8 @@ msm_gem_vm_bo_validate(struct drm_gpuvm_bo *vm_bo, struct drm_exec *exec) return ret; } + drm_gpuvm_bo_evict(vm_bo, false); + return 0; } From ae88499d71ce80ecd25c055eddddace108266321 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:38 -0700 Subject: [PATCH 025/121] drm/msm: Fix barriers accessing ctx vm Don't rely on store ordering to protect us from caller seeing a partially initialized vm. Reported-by: Sashiko Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743079/ Message-ID: <20260729155609.20190-2-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_drv.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index ac86b427c0e5..29075aafc5cc 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -224,18 +224,19 @@ struct drm_gpuvm *msm_context_vm(struct drm_device *dev, struct msm_context *ctx { static DEFINE_MUTEX(init_lock); struct msm_drm_private *priv = dev->dev_private; + struct drm_gpuvm *vm = smp_load_acquire(&ctx->vm); /* Once ctx->vm is created it is valid for the lifetime of the context: */ - if (ctx->vm) - return ctx->vm; + if (vm) + return vm; + + guard(mutex)(&init_lock); - mutex_lock(&init_lock); if (!ctx->vm) { - ctx->vm = msm_gpu_create_private_vm( + vm = msm_gpu_create_private_vm( priv->gpu, current, !ctx->userspace_managed_vm); - + smp_store_release(&ctx->vm, vm); } - mutex_unlock(&init_lock); return ctx->vm; } From ea69d489d3a6822e2cf10d6806a889dd2e3271f0 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:39 -0700 Subject: [PATCH 026/121] drm/msm: Rework queuelock Rename to ctxlock, and use cleanup guards to manage releasing the lock. This will let us re-use it for other per-context read/write serial- ization, such as VM creation. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743080/ Message-ID: <20260729155609.20190-3-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_drv.c | 2 +- drivers/gpu/drm/msm/msm_gpu.h | 4 ++-- drivers/gpu/drm/msm/msm_submitqueue.c | 14 +++----------- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 29075aafc5cc..77681cb4fdc2 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -251,7 +251,7 @@ static int context_init(struct drm_device *dev, struct drm_file *file) return -ENOMEM; INIT_LIST_HEAD(&ctx->submitqueues); - rwlock_init(&ctx->queuelock); + init_rwsem(&ctx->ctxlock); kref_init(&ctx->ref); msm_submitqueue_init(dev, ctx); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 6c83b8cbbb90..d27d54bdb7a7 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -392,8 +392,8 @@ msm_gpu_sysprof_no_ifpc(struct msm_gpu *gpu) * struct msm_context - per-drm_file context */ struct msm_context { - /** @queuelock: synchronizes access to submitqueues list */ - rwlock_t queuelock; + /** @ctxlock: synchronizes access to submitqueues list, etc */ + struct rw_semaphore ctxlock; /** @submitqueues: list of &msm_gpu_submitqueue created by userspace */ struct list_head submitqueues; /** diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c index 1a5a77b28016..8b5980d5a6a7 100644 --- a/drivers/gpu/drm/msm/msm_submitqueue.c +++ b/drivers/gpu/drm/msm/msm_submitqueue.c @@ -93,18 +93,15 @@ struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_context *ctx, if (!ctx) return NULL; - read_lock(&ctx->queuelock); + guard(rwsem_read)(&ctx->ctxlock); list_for_each_entry(entry, &ctx->submitqueues, node) { if (entry->id == id) { kref_get(&entry->ref); - read_unlock(&ctx->queuelock); - return entry; } } - read_unlock(&ctx->queuelock); return NULL; } @@ -237,7 +234,7 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_context *ctx, return ret; } - write_lock(&ctx->queuelock); + guard(rwsem_write)(&ctx->ctxlock); queue->ctx = msm_context_get(ctx); queue->id = ctx->queueid++; @@ -251,8 +248,6 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_context *ctx, list_add_tail(&queue->node, &ctx->submitqueues); - write_unlock(&ctx->queuelock); - return 0; } @@ -335,19 +330,16 @@ int msm_submitqueue_remove(struct msm_context *ctx, u32 id) if (!id) return -ENOENT; - write_lock(&ctx->queuelock); + guard(rwsem_write)(&ctx->ctxlock); list_for_each_entry(entry, &ctx->submitqueues, node) { if (entry->id == id) { list_del(&entry->node); - write_unlock(&ctx->queuelock); - msm_submitqueue_put(entry); return 0; } } - write_unlock(&ctx->queuelock); return -ENOENT; } From 2c26f9e46d39571a11ad8a6c3cdf150af70e4f12 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:40 -0700 Subject: [PATCH 027/121] drm/msm: Synchronize VM creation on ctxlock And serialize setting EN_VM_BIND against VM creation. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743083/ Message-ID: <20260729155609.20190-4-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 5 ++++- drivers/gpu/drm/msm/msm_drv.c | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index c62c45bb0ddb..0d54141ff089 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -503,7 +503,9 @@ int adreno_set_param(struct msm_gpu *gpu, struct msm_context *ctx, if (!perfmon_capable()) return UERR(EPERM, drm, "invalid permissions"); return msm_context_set_sysprof(ctx, gpu, value); - case MSM_PARAM_EN_VM_BIND: + case MSM_PARAM_EN_VM_BIND: { + guard(rwsem_read)(&ctx->ctxlock); + /* We can only support VM_BIND with per-process pgtables: */ if (ctx->vm == gpu->vm) return UERR(EINVAL, drm, "requires per-process pgtables"); @@ -518,6 +520,7 @@ int adreno_set_param(struct msm_gpu *gpu, struct msm_context *ctx, ctx->userspace_managed_vm = value; return 0; + } default: return UERR(EINVAL, drm, "%s: invalid param: %u", gpu->name, param); } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 77681cb4fdc2..a5714e24fcc6 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -222,7 +222,6 @@ static void load_gpu(struct drm_device *dev) */ struct drm_gpuvm *msm_context_vm(struct drm_device *dev, struct msm_context *ctx) { - static DEFINE_MUTEX(init_lock); struct msm_drm_private *priv = dev->dev_private; struct drm_gpuvm *vm = smp_load_acquire(&ctx->vm); @@ -230,7 +229,7 @@ struct drm_gpuvm *msm_context_vm(struct drm_device *dev, struct msm_context *ctx if (vm) return vm; - guard(mutex)(&init_lock); + guard(rwsem_write)(&ctx->ctxlock); if (!ctx->vm) { vm = msm_gpu_create_private_vm( From 2b1bfcb59d3f9bd4ab37eb6747fc783c67a7f82b Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:41 -0700 Subject: [PATCH 028/121] drm/msm: Synchronize set_sysprof on ctxlock A user that was perfmon_capable() could try to race setting SYSPROF param on multiple threads to trigger a reference leak. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743091/ Message-ID: <20260729155609.20190-5-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_submitqueue.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c index 8b5980d5a6a7..233c4f35ab12 100644 --- a/drivers/gpu/drm/msm/msm_submitqueue.c +++ b/drivers/gpu/drm/msm/msm_submitqueue.c @@ -9,6 +9,8 @@ int msm_context_set_sysprof(struct msm_context *ctx, struct msm_gpu *gpu, int sysprof) { + guard(rwsem_write)(&ctx->ctxlock); + /* * Since pm_runtime and sysprof_active are both refcounts, we * call apply the new value first, and then unwind the previous From 9ee2884ed58e6b39da35732146446d5930e4746b Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:42 -0700 Subject: [PATCH 029/121] drm/msm: Move nr_cmds initialization Previously if we entered an error path between these two points, we could leak the relocs tables due to submit->nr_cmds still being zero. In practice, relocs are disallowed on a6xx+, and non-ancient userspace will not use relocs on earlier gens unless running on an ancient kernel. But userspace could use this to trigger a memory leak. Reported-by: Sashiko Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743085/ Message-ID: <20260729155609.20190-6-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem_submit.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 3c6bc90c3d48..aa48ea4e7f58 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -260,6 +260,9 @@ static int submit_lookup_cmds(struct msm_gem_submit *submit, ret = -ENOMEM; goto out; } + + submit->nr_cmds = i + 1; + ret = copy_from_user(submit->cmd[i].relocs, userptr, sz); if (ret) { ret = -EFAULT; @@ -719,8 +722,6 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, goto out; } - submit->nr_cmds = args->nr_cmds; - idr_preload(GFP_KERNEL); spin_lock(&queue->idr_lock); From 0369a2619c50bab5e9ef33e13f1def0c8aca25e8 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:43 -0700 Subject: [PATCH 030/121] drm/msm: Remove redundant SIZE_MAX check kmalloc() will already fail and return NULL if passed SIZE_MAX. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743082/ Message-ID: <20260729155609.20190-7-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem_submit.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index aa48ea4e7f58..040354ee16d1 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -250,11 +250,6 @@ static int submit_lookup_cmds(struct msm_gem_submit *submit, sz = array_size(submit_cmd.nr_relocs, sizeof(struct drm_msm_gem_submit_reloc)); - /* check for overflow: */ - if (sz == SIZE_MAX) { - ret = -ENOMEM; - goto out; - } submit->cmd[i].relocs = kmalloc(sz, GFP_KERNEL | __GFP_NOWARN); if (!submit->cmd[i].relocs) { ret = -ENOMEM; From b87c50d2cabc48a74b2d0d79d9ff852eb8b73b58 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:44 -0700 Subject: [PATCH 031/121] drm/msm/a6xx: Access VM directly in submit path The GEM_SUBMIT ioctl has already ensured that the VM is created, so we aren't expecting to lazily create the VM this deep into the ioctl. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743101/ Message-ID: <20260729155609.20190-8-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 2c5faee881b3..f9de9329dee3 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -229,7 +229,7 @@ static void a6xx_set_pagetable(struct a6xx_gpu *a6xx_gpu, { bool sysprof = msm_gpu_sysprof_no_perfcntr_zap(&a6xx_gpu->base.base); struct msm_context *ctx = submit->queue->ctx; - struct drm_gpuvm *vm = msm_context_vm(submit->dev, ctx); + struct drm_gpuvm *vm = ctx->vm; struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; phys_addr_t ttbr; u32 asid; From 517ca9a86a4aaed7a79e5133c9a3e399cec6cdf5 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:45 -0700 Subject: [PATCH 032/121] drm/msm: Add helper to check for per-process pgtables VM This will simplify a following commit to allow lazy VM creation to fail. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743103/ Message-ID: <20260729155609.20190-9-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 0d54141ff089..5738eac9aebd 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -356,6 +356,12 @@ int adreno_fault_handler(struct msm_gpu *gpu, unsigned long iova, int flags, return 0; } +static bool +valid_per_process_vm(struct msm_gpu *gpu, struct drm_gpuvm *vm) +{ + return (vm != gpu->vm); +} + int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, uint32_t param, uint64_t *value, uint32_t *len) { @@ -414,12 +420,12 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, *value = gpu->suspend_count; return 0; case MSM_PARAM_VA_START: - if (vm == gpu->vm) + if (!valid_per_process_vm(gpu, vm)) return UERR(EINVAL, drm, "requires per-process pgtables"); *value = vm->mm_start; return 0; case MSM_PARAM_VA_SIZE: - if (vm == gpu->vm) + if (!valid_per_process_vm(gpu, vm)) return UERR(EINVAL, drm, "requires per-process pgtables"); *value = vm->mm_range; return 0; From e6863b085606f632082192d847d8d578013d4a66 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:46 -0700 Subject: [PATCH 033/121] drm/msm/gem: Fix dma_buf import error paths Set import_attach early, so that if we hit an error path msm_gem_free_object() goes down the drm_gem_is_imported() path. Set sgt late so _free_object() skips drm_prime_gem_destroy() as this is done by drm_gem_prime_import_dev(). Reported-by: Sashiko Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743084/ Message-ID: <20260729155609.20190-10-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem.c | 17 ++++++++++++++--- drivers/gpu/drm/msm/msm_gem.h | 3 ++- drivers/gpu/drm/msm/msm_gem_prime.c | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index efd3d3c9a449..74c3728f45bd 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -1093,7 +1093,9 @@ static void msm_gem_free_object(struct drm_gem_object *obj) */ kvfree(msm_obj->pages); - drm_prime_gem_destroy(obj, msm_obj->sgt); + /* In msm_gem_import() error path, sgt won't be set yet: */ + if (msm_obj->sgt) + drm_prime_gem_destroy(obj, msm_obj->sgt); } else { msm_gem_vunmap(obj); put_pages(obj); @@ -1282,11 +1284,13 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, size_t size, uint32_t } struct drm_gem_object *msm_gem_import(struct drm_device *dev, - struct dma_buf *dmabuf, struct sg_table *sgt) + struct dma_buf_attachment *attach, + struct sg_table *sgt) { struct msm_drm_private *priv = dev->dev_private; struct msm_gem_object *msm_obj; struct drm_gem_object *obj; + struct dma_buf *dmabuf = attach->dmabuf; size_t size, npages; int ret; @@ -1296,13 +1300,17 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, if (ret) return ERR_PTR(ret); + /* + * Set import_attach here in case we hit an error path that ends + * up in drm_gem_object_put() -> msm_gem_free_object() + */ + obj->import_attach = attach; drm_gem_private_object_init(dev, obj, size); npages = size / PAGE_SIZE; msm_obj = to_msm_bo(obj); msm_gem_lock(obj); - msm_obj->sgt = sgt; msm_obj->pages = kvmalloc_objs(struct page *, npages); if (!msm_obj->pages) { msm_gem_unlock(obj); @@ -1328,6 +1336,9 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, if (ret) goto fail; + /* Now that we are past potential failure points, set sgt: */ + msm_obj->sgt = sgt; + return obj; fail: diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 762e546d25ef..86dc93cc1804 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -302,7 +302,8 @@ void *msm_gem_kernel_new(struct drm_device *dev, size_t size, uint32_t flags, uint64_t *iova); void msm_gem_kernel_put(struct drm_gem_object *bo, struct drm_gpuvm *vm); struct drm_gem_object *msm_gem_import(struct drm_device *dev, - struct dma_buf *dmabuf, struct sg_table *sgt); + struct dma_buf_attachment *attach, + struct sg_table *sgt); __printf(2, 3) void msm_gem_object_set_name(struct drm_gem_object *bo, const char *fmt, ...); diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c index 036d34c674d9..beb7f22fd694 100644 --- a/drivers/gpu/drm/msm/msm_gem_prime.c +++ b/drivers/gpu/drm/msm/msm_gem_prime.c @@ -83,7 +83,7 @@ struct drm_gem_object *msm_gem_prime_import(struct drm_device *dev, struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sg) { - return msm_gem_import(dev, attach->dmabuf, sg); + return msm_gem_import(dev, attach, sg); } struct dma_buf *msm_gem_prime_export(struct drm_gem_object *obj, int flags) From 4e67b8fc55c4389652321355ff71ae8d4cb39783 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:47 -0700 Subject: [PATCH 034/121] drm/msm/gem: Remove useless locking in GEM import The locking has changed a few times over the years, and this extra locking was the mistake of evolution. Harmless but useless. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743087/ Message-ID: <20260729155609.20190-11-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 74c3728f45bd..cc45392691e5 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -1310,22 +1310,17 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, npages = size / PAGE_SIZE; msm_obj = to_msm_bo(obj); - msm_gem_lock(obj); msm_obj->pages = kvmalloc_objs(struct page *, npages); if (!msm_obj->pages) { - msm_gem_unlock(obj); ret = -ENOMEM; goto fail; } ret = drm_prime_sg_to_page_array(sgt, msm_obj->pages, npages); if (ret) { - msm_gem_unlock(obj); goto fail; } - msm_gem_unlock(obj); - drm_gem_lru_move_tail(&priv->lru.pinned, obj); mutex_lock(&priv->obj_lock); From 695d2b042af032bdbbfe4a1db475de2a2024e320 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:48 -0700 Subject: [PATCH 035/121] drm/msm/gem: Extract bookkeeping init helper Clean up duplicated logic between import and new paths. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743089/ Message-ID: <20260729155609.20190-12-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem.c | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index cc45392691e5..3c36b1c4a4d5 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -1234,10 +1234,25 @@ static int msm_gem_new_impl(struct drm_device *dev, uint32_t flags, return 0; } +static int msm_gem_init_bookkeeping(struct drm_gem_object *obj) +{ + struct msm_drm_private *priv = obj->dev->dev_private; + + if (drm_gem_is_imported(obj)) { + drm_gem_lru_move_tail(&priv->lru.pinned, obj); + } else { + drm_gem_lru_move_tail(&priv->lru.unbacked, obj); + } + + mutex_lock(&priv->obj_lock); + list_add_tail(&to_msm_bo(obj)->node, &priv->objects); + mutex_unlock(&priv->obj_lock); + + return drm_gem_create_mmap_offset(obj); +} + struct drm_gem_object *msm_gem_new(struct drm_device *dev, size_t size, uint32_t flags) { - struct msm_drm_private *priv = dev->dev_private; - struct msm_gem_object *msm_obj; struct drm_gem_object *obj = NULL; int ret; @@ -1253,8 +1268,6 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, size_t size, uint32_t if (ret) return ERR_PTR(ret); - msm_obj = to_msm_bo(obj); - ret = drm_gem_object_init(dev, obj, size); if (ret) goto fail; @@ -1266,13 +1279,7 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, size_t size, uint32_t */ mapping_set_gfp_mask(obj->filp->f_mapping, GFP_HIGHUSER); - drm_gem_lru_move_tail(&priv->lru.unbacked, obj); - - mutex_lock(&priv->obj_lock); - list_add_tail(&msm_obj->node, &priv->objects); - mutex_unlock(&priv->obj_lock); - - ret = drm_gem_create_mmap_offset(obj); + ret = msm_gem_init_bookkeeping(obj); if (ret) goto fail; @@ -1287,7 +1294,6 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sgt) { - struct msm_drm_private *priv = dev->dev_private; struct msm_gem_object *msm_obj; struct drm_gem_object *obj; struct dma_buf *dmabuf = attach->dmabuf; @@ -1321,13 +1327,7 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, goto fail; } - drm_gem_lru_move_tail(&priv->lru.pinned, obj); - - mutex_lock(&priv->obj_lock); - list_add_tail(&msm_obj->node, &priv->objects); - mutex_unlock(&priv->obj_lock); - - ret = drm_gem_create_mmap_offset(obj); + ret = msm_gem_init_bookkeeping(obj); if (ret) goto fail; From df68029e639b490e8f5f7715135a8af29e1b6cda Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:49 -0700 Subject: [PATCH 036/121] drm/msm/gem: Set resv before exposing obj Don't swap the resv object _after_ exposing the newly created obj in LRU or global objects list, as that creates a race condition where another thread could lock the object using the original (per-obj) resv, but then unlock after the resv is replaced. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743109/ Message-ID: <20260729155609.20190-13-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 2 +- drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c | 2 +- drivers/gpu/drm/msm/msm_fb.c | 4 +-- drivers/gpu/drm/msm/msm_gem.c | 32 ++++++++++++++---------- drivers/gpu/drm/msm/msm_gem.h | 2 +- 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index e97d8d8ee65a..27cac853975f 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -1554,7 +1554,7 @@ static int a6xx_gmu_memory_alloc(struct a6xx_gmu *gmu, struct a6xx_gmu_bo *bo, flags |= MSM_BO_MAP_PRIV; } - bo->obj = msm_gem_new(dev, size, flags); + bo->obj = msm_gem_new(dev, size, flags, NULL); if (IS_ERR(bo->obj)) return PTR_ERR(bo->obj); diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c index 7726edb0d4ed..c289dff78cd5 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c @@ -463,7 +463,7 @@ static int mdp4_kms_init(struct drm_device *dev) goto fail; } - mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC | MSM_BO_SCANOUT); + mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC | MSM_BO_SCANOUT, NULL); if (IS_ERR(mdp4_kms->blank_cursor_bo)) { ret = PTR_ERR(mdp4_kms->blank_cursor_bo); DRM_DEV_ERROR(dev->dev, "could not allocate blank-cursor bo: %d\n", ret); diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index 9b681e144c07..ee17a47f31c4 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -267,11 +267,11 @@ msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format /* allocate backing bo */ size = mode_cmd.pitches[0] * mode_cmd.height; DBG("allocating %d bytes for fb %d", size, dev->primary->index); - bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN); + bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN, NULL); if (IS_ERR(bo)) { dev_warn(dev->dev, "could not allocate stolen bo\n"); /* try regular bo: */ - bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC); + bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC, NULL); } if (IS_ERR(bo)) { DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n"); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 3c36b1c4a4d5..535c6e1acb3c 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -1136,10 +1136,17 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, size_t size, uint32_t flags, uint32_t *handle, char *name) { - struct drm_gem_object *obj; + struct drm_gem_object *obj, *r_obj = NULL; int ret; - obj = msm_gem_new(dev, size, flags); + if (flags & MSM_BO_NO_SHARE) { + struct msm_context *ctx = file->driver_priv; + struct drm_gpuvm *vm = msm_context_vm(dev, ctx); + + r_obj = drm_gpuvm_resv_obj(vm); + } + + obj = msm_gem_new(dev, size, flags, r_obj); if (IS_ERR(obj)) return PTR_ERR(obj); @@ -1147,15 +1154,6 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, if (name) msm_gem_object_set_name(obj, "%s", name); - if (flags & MSM_BO_NO_SHARE) { - struct msm_context *ctx = file->driver_priv; - struct drm_gem_object *r_obj = drm_gpuvm_resv_obj(ctx->vm); - - drm_gem_object_get(r_obj); - - obj->resv = r_obj->resv; - } - ret = drm_gem_handle_create(file, obj, handle); /* drop reference from allocate - handle holds it now */ @@ -1251,7 +1249,9 @@ static int msm_gem_init_bookkeeping(struct drm_gem_object *obj) return drm_gem_create_mmap_offset(obj); } -struct drm_gem_object *msm_gem_new(struct drm_device *dev, size_t size, uint32_t flags) +struct drm_gem_object * +msm_gem_new(struct drm_device *dev, size_t size, uint32_t flags, + struct drm_gem_object *r_obj) { struct drm_gem_object *obj = NULL; int ret; @@ -1268,6 +1268,11 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, size_t size, uint32_t if (ret) return ERR_PTR(ret); + if (flags & MSM_BO_NO_SHARE) { + drm_gem_object_get(r_obj); + obj->resv = r_obj->resv; + } + ret = drm_gem_object_init(dev, obj, size); if (ret) goto fail; @@ -1311,6 +1316,7 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, * up in drm_gem_object_put() -> msm_gem_free_object() */ obj->import_attach = attach; + obj->resv = dmabuf->resv; drm_gem_private_object_init(dev, obj, size); npages = size / PAGE_SIZE; @@ -1346,7 +1352,7 @@ void *msm_gem_kernel_new(struct drm_device *dev, size_t size, uint32_t flags, uint64_t *iova) { void *vaddr; - struct drm_gem_object *obj = msm_gem_new(dev, size, flags); + struct drm_gem_object *obj = msm_gem_new(dev, size, flags, NULL); int ret; if (IS_ERR(obj)) diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 86dc93cc1804..dff60cbc9d95 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -296,7 +296,7 @@ int msm_gem_cpu_fini(struct drm_gem_object *obj); int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, size_t size, uint32_t flags, uint32_t *handle, char *name); struct drm_gem_object *msm_gem_new(struct drm_device *dev, - size_t size, uint32_t flags); + size_t size, uint32_t flags, struct drm_gem_object *r_obj); void *msm_gem_kernel_new(struct drm_device *dev, size_t size, uint32_t flags, struct drm_gpuvm *vm, struct drm_gem_object **bo, uint64_t *iova); From a6d87a272b2c8fe1366ea9a8e2e4cccddbb4157f Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:50 -0700 Subject: [PATCH 037/121] drm/msm/gem: Validate lazy VM in GEM_NEW Otherwise creating a _NO_SHARE BO before any BOs are mapped could cause a NPE. Reported-by: Sashiko Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743104/ Message-ID: <20260729155609.20190-14-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 535c6e1acb3c..de4e60e28a71 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -1140,9 +1140,13 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, int ret; if (flags & MSM_BO_NO_SHARE) { + struct msm_drm_private *priv = dev->dev_private; struct msm_context *ctx = file->driver_priv; struct drm_gpuvm *vm = msm_context_vm(dev, ctx); + if (!priv->gpu || !vm) + return UERR(EINVAL, dev, "not supported with shared VM"); + r_obj = drm_gpuvm_resv_obj(vm); } From 00dfa76bdfc2fa807881df260f30053e2515c665 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:51 -0700 Subject: [PATCH 038/121] drm/msm: Allow lazy VM creation to fail In the next commit, we'll stop falling back to shared VM if private VM creation fails. This isn't expected to happen in practice, it would either require small memory allocations to fail, or missing support in arm-smmu-qcom for setting up per-process pgtable support (ie. missing patch during bringup). Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743088/ Message-ID: <20260729155609.20190-15-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 2 +- drivers/gpu/drm/msm/msm_drv.c | 9 ++++++++- drivers/gpu/drm/msm/msm_gem_submit.c | 12 ++++++++---- drivers/gpu/drm/msm/msm_gem_vma.c | 12 ++++++++---- drivers/gpu/drm/msm/msm_submitqueue.c | 8 +++++++- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 5738eac9aebd..84101e534ebe 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -359,7 +359,7 @@ int adreno_fault_handler(struct msm_gpu *gpu, unsigned long iova, int flags, static bool valid_per_process_vm(struct msm_gpu *gpu, struct drm_gpuvm *vm) { - return (vm != gpu->vm); + return vm && (vm != gpu->vm); } int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index a5714e24fcc6..84b126c6347f 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -422,10 +422,14 @@ static int msm_ioctl_gem_info_iova(struct drm_device *dev, { struct msm_drm_private *priv = dev->dev_private; struct msm_context *ctx = file->driver_priv; + struct drm_gpuvm *vm = msm_context_vm(dev, ctx); if (!priv->gpu) return -EINVAL; + if (!vm) + return UERR(ENOMEM, dev, "no VM"); + if (msm_context_is_vmbind(ctx)) return UERR(EINVAL, dev, "VM_BIND is enabled"); @@ -436,7 +440,7 @@ static int msm_ioctl_gem_info_iova(struct drm_device *dev, * Don't pin the memory here - just get an address so that userspace can * be productive */ - return msm_gem_get_iova(obj, msm_context_vm(dev, ctx), iova); + return msm_gem_get_iova(obj, vm, iova); } static int msm_ioctl_gem_info_set_iova(struct drm_device *dev, @@ -450,6 +454,9 @@ static int msm_ioctl_gem_info_set_iova(struct drm_device *dev, if (!priv->gpu) return -EINVAL; + if (!vm) + return UERR(ENOMEM, dev, "no VM"); + if (msm_context_is_vmbind(ctx)) return UERR(EINVAL, dev, "VM_BIND is enabled"); diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 040354ee16d1..6b0bee6c39bc 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -30,7 +30,7 @@ */ static struct msm_gem_submit *submit_create(struct drm_device *dev, - struct msm_gpu *gpu, + struct msm_gpu *gpu, struct drm_gpuvm *vm, struct msm_gpu_submitqueue *queue, uint32_t nr_bos, uint32_t nr_cmds, u64 drm_client_id) { @@ -66,7 +66,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, kref_init(&submit->ref); submit->dev = dev; - submit->vm = msm_context_vm(dev, queue->ctx); + submit->vm = vm; submit->gpu = gpu; submit->cmd = (void *)&submit->bos[nr_bos]; submit->queue = queue; @@ -552,6 +552,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct msm_drm_private *priv = dev->dev_private; struct drm_msm_gem_submit *args = data; struct msm_context *ctx = file->driver_priv; + struct drm_gpuvm *vm = msm_context_vm(dev, ctx); struct msm_gem_submit *submit = NULL; struct msm_gpu *gpu = priv->gpu; struct msm_gpu_submitqueue *queue; @@ -567,10 +568,13 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (!gpu) return -ENXIO; + if (!vm) + return UERR(ENOMEM, dev, "no VM"); + if (args->pad) return -EINVAL; - if (to_msm_vm(ctx->vm)->unusable) + if (to_msm_vm(vm)->unusable) return UERR(EPIPE, dev, "context is unusable"); /* for now, we just have 3d pipe.. eventually this would need to @@ -607,7 +611,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, } } - submit = submit_create(dev, gpu, queue, args->nr_bos, args->nr_cmds, + submit = submit_create(dev, gpu, vm, queue, args->nr_bos, args->nr_cmds, file->client_id); if (IS_ERR(submit)) { ret = PTR_ERR(submit); diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 50d4812bdb4c..1f9b75fc12fd 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -956,7 +956,7 @@ msm_gem_vm_close(struct drm_gpuvm *gpuvm) static struct msm_vm_bind_job * -vm_bind_job_create(struct drm_device *dev, struct drm_file *file, +vm_bind_job_create(struct drm_device *dev, struct drm_file *file, struct drm_gpuvm *vm, struct msm_gpu_submitqueue *queue, uint32_t nr_ops) { struct msm_vm_bind_job *job; @@ -973,7 +973,7 @@ vm_bind_job_create(struct drm_device *dev, struct drm_file *file, return ERR_PTR(ret); } - job->vm = msm_context_vm(dev, queue->ctx); + job->vm = vm; job->queue = queue; INIT_LIST_HEAD(&job->vm_ops); @@ -1432,6 +1432,7 @@ msm_ioctl_vm_bind(struct drm_device *dev, void *data, struct drm_file *file) struct msm_drm_private *priv = dev->dev_private; struct drm_msm_vm_bind *args = data; struct msm_context *ctx = file->driver_priv; + struct drm_gpuvm *vm = msm_context_vm(dev, ctx); struct msm_vm_bind_job *job = NULL; struct msm_gpu *gpu = priv->gpu; struct msm_gpu_submitqueue *queue; @@ -1446,11 +1447,14 @@ msm_ioctl_vm_bind(struct drm_device *dev, void *data, struct drm_file *file) if (!gpu) return -ENXIO; + if (!vm) + return UERR(ENOMEM, dev, "no VM"); + /* * Maybe we could allow just UNMAP ops? OTOH userspace should just * immediately close the device file and all will be torn down. */ - if (to_msm_vm(msm_context_vm(dev, ctx))->unusable) + if (to_msm_vm(vm)->unusable) return UERR(EPIPE, dev, "context is unusable"); /* @@ -1481,7 +1485,7 @@ msm_ioctl_vm_bind(struct drm_device *dev, void *data, struct drm_file *file) } } - job = vm_bind_job_create(dev, file, queue, args->nr_ops); + job = vm_bind_job_create(dev, file, vm, queue, args->nr_ops); if (IS_ERR(job)) { ret = PTR_ERR(job); goto out_post_unlock; diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c index 233c4f35ab12..d307cdd17d90 100644 --- a/drivers/gpu/drm/msm/msm_submitqueue.c +++ b/drivers/gpu/drm/msm/msm_submitqueue.c @@ -174,6 +174,7 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_context *ctx, struct msm_drm_private *priv = drm->dev_private; struct msm_gpu_submitqueue *queue; enum drm_sched_priority sched_prio; + struct drm_gpuvm *vm = NULL; unsigned ring_nr; int ret; @@ -186,6 +187,11 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_context *ctx, if (flags & MSM_SUBMITQUEUE_VM_BIND) { unsigned sz; + vm = msm_context_vm(drm, ctx); + + if (!vm) + return UERR(ENOMEM, drm, "no VM"); + /* Not allowed for kernel managed VMs (ie. kernel allocs VA) */ if (!msm_context_is_vmbind(ctx)) return -EINVAL; @@ -217,7 +223,7 @@ int msm_submitqueue_create(struct drm_device *drm, struct msm_context *ctx, queue->flags = flags; if (flags & MSM_SUBMITQUEUE_VM_BIND) { - struct drm_gpu_scheduler *sched = &to_msm_vm(msm_context_vm(drm, ctx))->sched; + struct drm_gpu_scheduler *sched = &to_msm_vm(vm)->sched; queue->entity = &queue->_vm_bind_entity[0]; From 3b35a5c528baf3c8cff89b44ae1c5174007b127a Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:52 -0700 Subject: [PATCH 039/121] drm/msm: Don't fallback to shared VM for VM_BIND If the user wants a userspace managed VM (EN_VM_BIND) don't silently fall back to shared VM. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743110/ Message-ID: <20260729155609.20190-16-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_drv.c | 3 ++- drivers/gpu/drm/msm/msm_gpu.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 84b126c6347f..db1b655dd055 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -234,7 +234,8 @@ struct drm_gpuvm *msm_context_vm(struct drm_device *dev, struct msm_context *ctx if (!ctx->vm) { vm = msm_gpu_create_private_vm( priv->gpu, current, !ctx->userspace_managed_vm); - smp_store_release(&ctx->vm, vm); + if (!IS_ERR_OR_NULL(vm)) + smp_store_release(&ctx->vm, vm); } return ctx->vm; diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 03c057856065..0c2c35636251 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -879,7 +879,7 @@ msm_gpu_create_private_vm(struct msm_gpu *gpu, struct task_struct *task, to_msm_vm(vm)->pid = get_pid(task_pid(task)); } - if (IS_ERR_OR_NULL(vm)) + if (IS_ERR_OR_NULL(vm) && kernel_managed) vm = drm_gpuvm_get(gpu->vm); return vm; From 1b8029394fb77adde9c494a3acd40b0b39793b55 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:53 -0700 Subject: [PATCH 040/121] drm/msm: Fix per-process-pgtables check ctx->vm should not be inialized yet (or if it has, an error is returned immediately following this check), so this isn't a valid way to check for per-process-pgtable support. Instead just check if create_private_vm() is supported. Reported-by: Sashiko Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743096/ Message-ID: <20260729155609.20190-17-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 84101e534ebe..3370cd44382f 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -513,7 +513,7 @@ int adreno_set_param(struct msm_gpu *gpu, struct msm_context *ctx, guard(rwsem_read)(&ctx->ctxlock); /* We can only support VM_BIND with per-process pgtables: */ - if (ctx->vm == gpu->vm) + if (!gpu->funcs->create_private_vm) return UERR(EINVAL, drm, "requires per-process pgtables"); /* From 7aeada642ebfd52ddb87aaf1f0324a7fcf636610 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 29 Jul 2026 08:55:54 -0700 Subject: [PATCH 041/121] drm/msm: Fixup invalid overflow check On overflow struct_size() would return SIZE_MAX. But kzalloc() (and friends) check this already, so we can just remove the check. On the other hand, we should be using the overflow helpers to calculate the cmd array size. Reported-by: Sashiko Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/743111/ Message-ID: <20260729155609.20190-18-robin.clark@oss.qualcomm.com> --- drivers/gpu/drm/msm/msm_gem_submit.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 6b0bee6c39bc..5862db05297a 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -36,14 +36,11 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, { static atomic_t ident = ATOMIC_INIT(0); struct msm_gem_submit *submit; - uint64_t sz; + size_t sz; int ret; - sz = struct_size(submit, bos, nr_bos) + - ((u64)nr_cmds * sizeof(submit->cmd[0])); - - if (sz > SIZE_MAX) - return ERR_PTR(-ENOMEM); + sz = size_add(struct_size(submit, bos, nr_bos), + array_size(sizeof(submit->cmd[0]), nr_cmds)); submit = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN); if (!submit) From c8ec4b10864ee5cfd4ea88bca4238bf7df24b10b Mon Sep 17 00:00:00 2001 From: Nabige Aala Date: Mon, 8 Jun 2026 12:31:42 +0530 Subject: [PATCH 042/121] dt-bindings: display: msm: qcm2290: Add Shikra MDSS Shikra reuses the same MDSS/DPU 6.5 hardware as QCM2290. Extend the existing qcm2290 bindings to cover Shikra by adding fallback compatible chains for MDSS, DPU and DSI controller nodes rather than introducing a separate binding file. Signed-off-by: Nabige Aala Reviewed-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Reviewed-by: Loic Poulain Patchwork: https://patchwork.freedesktop.org/patch/731152/ Link: https://lore.kernel.org/r/20260608-shikra-display-v4-1-88a846afdd5d@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- .../display/msm/dsi-controller-main.yaml | 4 ++++ .../display/msm/qcom,qcm2290-dpu.yaml | 6 +++++- .../display/msm/qcom,qcm2290-mdss.yaml | 19 ++++++++++++------- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml index dbc0613e427e..fd0834d09ad6 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml @@ -47,6 +47,10 @@ properties: - qcom,sm8650-dsi-ctrl - qcom,sm8750-dsi-ctrl - const: qcom,mdss-dsi-ctrl + - items: + - const: qcom,shikra-dsi-ctrl + - const: qcom,qcm2290-dsi-ctrl + - const: qcom,mdss-dsi-ctrl - items: - enum: - qcom,qcs8300-dsi-ctrl diff --git a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-dpu.yaml b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-dpu.yaml index be6cd8adb3b6..034d3df8d247 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-dpu.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-dpu.yaml @@ -13,7 +13,11 @@ $ref: /schemas/display/msm/dpu-common.yaml# properties: compatible: - const: qcom,qcm2290-dpu + oneOf: + - const: qcom,qcm2290-dpu + - items: + - const: qcom,shikra-dpu + - const: qcom,qcm2290-dpu reg: items: diff --git a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml index bb09ecd1a5b4..49a7b5c4c678 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/display/msm/qcom,qcm2290-mdss.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm QCM220 Display MDSS +title: Qualcomm QCM2290 and Shikra Display MDSS maintainers: - Loic Poulain @@ -12,13 +12,18 @@ maintainers: description: Device tree bindings for MSM Mobile Display Subsystem(MDSS) that encapsulates sub-blocks like DPU display controller and DSI. Device tree bindings of MDSS - are mentioned for QCM2290 target. + are mentioned for QCM2290 and Shikra targets. Shikra uses the same MDSS/DPU/DSI + hardware as QCM2290 (DPU 6.5) and shares the same register layout. $ref: /schemas/display/msm/mdss-common.yaml# properties: compatible: - const: qcom,qcm2290-mdss + oneOf: + - const: qcom,qcm2290-mdss + - items: + - const: qcom,shikra-mdss + - const: qcom,qcm2290-mdss clocks: items: @@ -52,7 +57,8 @@ patternProperties: properties: compatible: - const: qcom,qcm2290-dpu + contains: + const: qcom,qcm2290-dpu "^dsi@[0-9a-f]+$": type: object @@ -60,9 +66,8 @@ patternProperties: properties: compatible: - items: - - const: qcom,qcm2290-dsi-ctrl - - const: qcom,mdss-dsi-ctrl + contains: + const: qcom,qcm2290-dsi-ctrl "^phy@[0-9a-f]+$": type: object From d858770e86e75bf78429b9f5210b7556ef431033 Mon Sep 17 00:00:00 2001 From: Kumar Anurag Date: Tue, 16 Jun 2026 08:12:52 -0700 Subject: [PATCH 043/121] drm/msm/dp: return 0 from audio_prepare when cable is disconnected PipeWire treats a non-zero return from prepare as fatal, marking the DP audio device as a dummy sink when the cable is unplugged. The active_stream_cnt guard already prevents any unclocked hardware access, so return success instead of -EINVAL when the link is not active. Signed-off-by: Kumar Anurag Suggested-by: Srinivas Kandagatla Reviewed-by: Dmitry Baryshkov # same behaviour Patchwork: https://patchwork.freedesktop.org/patch/733663/ Link: https://lore.kernel.org/r/20260616151252.3599089-2-kumar.singh@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_audio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c index 41018e82efa1..df222943ae57 100644 --- a/drivers/gpu/drm/msm/dp/dp_audio.c +++ b/drivers/gpu/drm/msm/dp/dp_audio.c @@ -284,10 +284,8 @@ int msm_dp_audio_prepare(struct drm_bridge *bridge, * such cases check for connection status and bail out if not * connected. */ - if (!msm_dp_display->power_on) { - rc = -EINVAL; + if (!msm_dp_display->power_on) goto end; - } audio = msm_dp_audio_get_data(msm_dp_display); if (IS_ERR(audio)) { From 4f2b89b0341126ddddcfcf8a5a9895d226930e73 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 16:11:32 +0200 Subject: [PATCH 044/121] drm/msm: Do not declare msm_framebuffer_init() as static Declare msm_framebuffer_init() in msm_drv.h and remove the static qualifier. The function will be required in msm_fbdev.c after inlining msm_alloc_stolen_fb(). Also move msm_framebuffer_init() before msm_framebuffer_create(), so that it can later be made static again. Prepares msm's fbdev emulation for using client buffers. Signed-off-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/733883/ Link: https://lore.kernel.org/r/20260618141249.151338-2-tzimmermann@suse.de Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_drv.h | 4 ++ drivers/gpu/drm/msm/msm_fb.c | 72 +++++++++++++++++------------------ 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 3787db8770ad..7dccf0713b02 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -258,6 +258,10 @@ void msm_framebuffer_cleanup(struct drm_framebuffer *fb, bool needed_dirtyfb); uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb, int plane); struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane); const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb); +struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object **bos); struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_format_info *info, const struct drm_mode_fb_cmd2 *mode_cmd); diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index ee17a47f31c4..c98710569a2e 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -29,10 +29,6 @@ struct msm_framebuffer { }; #define to_msm_framebuffer(x) container_of(x, struct msm_framebuffer, base) -static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, - const struct drm_format_info *info, - const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos); - static int msm_framebuffer_dirtyfb(struct drm_framebuffer *fb, struct drm_file *file_priv, unsigned int flags, unsigned int color, struct drm_clip_rect *clips, @@ -139,39 +135,10 @@ const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb) return msm_fb->format; } -struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, - struct drm_file *file, const struct drm_format_info *info, - const struct drm_mode_fb_cmd2 *mode_cmd) -{ - struct drm_gem_object *bos[4] = {0}; - struct drm_framebuffer *fb; - int ret, i, n = info->num_planes; - - for (i = 0; i < n; i++) { - bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); - if (!bos[i]) { - ret = -ENXIO; - goto out_unref; - } - } - - fb = msm_framebuffer_init(dev, info, mode_cmd, bos); - if (IS_ERR(fb)) { - ret = PTR_ERR(fb); - goto out_unref; - } - - return fb; - -out_unref: - for (i = 0; i < n; i++) - drm_gem_object_put(bos[i]); - return ERR_PTR(ret); -} - -static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, - const struct drm_format_info *info, - const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) +struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object **bos) { struct msm_drm_private *priv = dev->dev_private; struct msm_kms *kms = priv->kms; @@ -251,6 +218,37 @@ static struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, return ERR_PTR(ret); } +struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, + struct drm_file *file, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *bos[4] = {0}; + struct drm_framebuffer *fb; + int ret, i, n = info->num_planes; + + for (i = 0; i < n; i++) { + bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); + if (!bos[i]) { + ret = -ENXIO; + goto out_unref; + } + } + + fb = msm_framebuffer_init(dev, info, mode_cmd, bos); + if (IS_ERR(fb)) { + ret = PTR_ERR(fb); + goto out_unref; + } + + return fb; + +out_unref: + for (i = 0; i < n; i++) + drm_gem_object_put(bos[i]); + return ERR_PTR(ret); +} + struct drm_framebuffer * msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format) { From 0cfd468c995644a512c3fe82f391814f7ea30e77 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 16:11:33 +0200 Subject: [PATCH 045/121] drm/msm: fbdev: Inline msm_alloc_stolen_fb() Inline msm_alloc_stolen_fb() into its only caller. This is necessary for converting fbdev emulation to use client buffers. There are some minor changes: - Handle errors for the non-stolen BO in the respective branch. - Fill mode_cmd right before using it with msm_framebuffer_init(). Both will later be replaced with client-buffer interfaces. - Set the modifier[0] to DRM_FORMAT_MOD_LINEAR. No functional change. - Integrate the error handling with the existing clean-up. Signed-off-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/733884/ Link: https://lore.kernel.org/r/20260618141249.151338-3-tzimmermann@suse.de Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_drv.h | 2 -- drivers/gpu/drm/msm/msm_fb.c | 46 --------------------------------- drivers/gpu/drm/msm/msm_fbdev.c | 46 ++++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 7dccf0713b02..01a64a32b843 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -265,8 +265,6 @@ struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_format_info *info, const struct drm_mode_fb_cmd2 *mode_cmd); -struct drm_framebuffer * msm_alloc_stolen_fb(struct drm_device *dev, - int w, int h, int p, uint32_t format); #ifdef CONFIG_DRM_MSM_KMS_FBDEV int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index c98710569a2e..dec550e6cf75 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -248,48 +247,3 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, drm_gem_object_put(bos[i]); return ERR_PTR(ret); } - -struct drm_framebuffer * -msm_alloc_stolen_fb(struct drm_device *dev, int w, int h, int p, uint32_t format) -{ - struct drm_mode_fb_cmd2 mode_cmd = { - .pixel_format = format, - .width = w, - .height = h, - .pitches = { p }, - }; - struct drm_gem_object *bo; - struct drm_framebuffer *fb; - int size; - - /* allocate backing bo */ - size = mode_cmd.pitches[0] * mode_cmd.height; - DBG("allocating %d bytes for fb %d", size, dev->primary->index); - bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN, NULL); - if (IS_ERR(bo)) { - dev_warn(dev->dev, "could not allocate stolen bo\n"); - /* try regular bo: */ - bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC, NULL); - } - if (IS_ERR(bo)) { - DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n"); - return ERR_CAST(bo); - } - - msm_gem_object_set_name(bo, "stolenfb"); - - fb = msm_framebuffer_init(dev, - drm_get_format_info(dev, mode_cmd.pixel_format, - mode_cmd.modifier[0]), - &mode_cmd, &bo); - if (IS_ERR(fb)) { - DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n"); - /* note: if fb creation failed, we can't rely on fb destroy - * to unref the bo: - */ - drm_gem_object_put(bo); - return ERR_CAST(fb); - } - - return fb; -} diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index fd19995b12b5..f834a595e55e 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -92,11 +93,13 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct drm_device *dev = helper->dev; struct msm_drm_private *priv = dev->dev_private; struct fb_info *fbi = helper->info; + struct drm_mode_fb_cmd2 mode_cmd = { }; struct drm_framebuffer *fb = NULL; struct drm_gem_object *bo; uint64_t paddr; uint32_t format; int ret, pitch; + int size; format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); @@ -105,15 +108,38 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, sizes->fb_width, sizes->fb_height); pitch = align_pitch(sizes->surface_width, sizes->surface_bpp); - fb = msm_alloc_stolen_fb(dev, sizes->surface_width, - sizes->surface_height, pitch, format); - if (IS_ERR(fb)) { - DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n"); - return PTR_ERR(fb); + /* allocate backing bo */ + size = pitch * sizes->surface_height; + DBG("allocating %d bytes for fb %d", size, dev->primary->index); + bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN, NULL); + if (IS_ERR(bo)) { + dev_warn(dev->dev, "could not allocate stolen bo\n"); + /* try regular bo: */ + bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC, NULL); + if (IS_ERR(bo)) { + DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n"); + return PTR_ERR(bo); + } } - bo = msm_framebuffer_bo(fb, 0); + msm_gem_object_set_name(bo, "stolenfb"); + + mode_cmd.pixel_format = format; + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = pitch; + mode_cmd.modifier[0] = DRM_FORMAT_MOD_LINEAR; + + fb = msm_framebuffer_init(dev, + drm_get_format_info(dev, mode_cmd.pixel_format, + mode_cmd.modifier[0]), + &mode_cmd, &bo); + if (IS_ERR(fb)) { + DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n"); + ret = PTR_ERR(fb); + goto err_drm_gem_object_put; + } /* * NOTE: if we can be guaranteed to be able to map buffer @@ -123,7 +149,7 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, ret = msm_gem_get_and_pin_iova(bo, priv->kms->vm, &paddr); if (ret) { DRM_DEV_ERROR(dev->dev, "failed to get buffer obj iova: %d\n", ret); - goto fail; + goto err_drm_framebuffer_remove; } DBG("fbi=%p, dev=%p", fbi, dev); @@ -138,7 +164,7 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, fbi->screen_buffer = msm_gem_get_vaddr(bo); if (IS_ERR(fbi->screen_buffer)) { ret = PTR_ERR(fbi->screen_buffer); - goto fail; + goto err_drm_framebuffer_remove; } fbi->screen_size = bo->size; fbi->fix.smem_start = paddr; @@ -149,7 +175,9 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, return 0; -fail: +err_drm_framebuffer_remove: drm_framebuffer_remove(fb); +err_drm_gem_object_put: + drm_gem_object_put(bo); return ret; } From e78d1bc9e4d062d1c95cd262ffe0c6fd66c40e1f Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 16:11:34 +0200 Subject: [PATCH 046/121] drm/msm: fbdev: Fix error reporting Replace deprecated error reporting in msm_fbdev_driver_fbdev_probe(). Use drm_warn() and drm_err() instead. Signed-off-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/733887/ Link: https://lore.kernel.org/r/20260618141249.151338-4-tzimmermann@suse.de Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_fbdev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index f834a595e55e..6af08b7b8082 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -114,11 +114,11 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, DBG("allocating %d bytes for fb %d", size, dev->primary->index); bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN, NULL); if (IS_ERR(bo)) { - dev_warn(dev->dev, "could not allocate stolen bo\n"); + drm_warn(dev, "could not allocate stolen bo\n"); /* try regular bo: */ bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC, NULL); if (IS_ERR(bo)) { - DRM_DEV_ERROR(dev->dev, "failed to allocate buffer object\n"); + drm_err(dev, "failed to allocate buffer object\n"); return PTR_ERR(bo); } } @@ -136,7 +136,7 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, mode_cmd.modifier[0]), &mode_cmd, &bo); if (IS_ERR(fb)) { - DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n"); + drm_err(dev, "failed to allocate fb\n"); ret = PTR_ERR(fb); goto err_drm_gem_object_put; } @@ -148,7 +148,7 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, */ ret = msm_gem_get_and_pin_iova(bo, priv->kms->vm, &paddr); if (ret) { - DRM_DEV_ERROR(dev->dev, "failed to get buffer obj iova: %d\n", ret); + drm_err(dev, "failed to get buffer obj iova: %d\n", ret); goto err_drm_framebuffer_remove; } From 6cf3fb14f33ea73ee4a05fe4c406a6623c72d353 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 16:11:35 +0200 Subject: [PATCH 047/121] drm/msm: fbdev: Calculate buffer geometry with format helpers Replace the geometry and size calculation in msm's fbdev emulation with DRM format helpers. This consists of a 4CC lookup from the fbdev parameters, format lookup, pitch calculation and size calculation. Then allocate the GEM buffer object for the framebuffer memory from the calculated size. Explicitly align the size of the allocated GEM buffer object to full pages. The contained memory is the framebuffer memory as seen by fbdev. The page alignment is required for mmap. v2: - clarify the page alignment of the buffer size (Dmitry) Signed-off-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/733885/ Link: https://lore.kernel.org/r/20260618141249.151338-5-tzimmermann@suse.de Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_drv.h | 7 ------- drivers/gpu/drm/msm/msm_fbdev.c | 26 ++++++++++++-------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 01a64a32b843..b21f8971dfa1 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -505,13 +505,6 @@ void msm_hrtimer_work_init(struct msm_hrtimer_work *work, #define DBG(fmt, ...) DRM_DEBUG_DRIVER(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG_DRIVER(fmt"\n", ##__VA_ARGS__) -static inline int align_pitch(int width, int bpp) -{ - int bytespp = (bpp + 7) / 8; - /* adreno needs pitch aligned to 32 pixels: */ - return bytespp * ALIGN(width, 32); -} - /* for the generated headers: */ #define INVALID_IDX(idx) ({BUG(); 0;}) #define fui(x) ({BUG(); 0;}) diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 6af08b7b8082..35b26830afb4 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -95,23 +95,25 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct fb_info *fbi = helper->info; struct drm_mode_fb_cmd2 mode_cmd = { }; struct drm_framebuffer *fb = NULL; + const struct drm_format_info *format; + u32 fourcc, pitch; + u64 size; struct drm_gem_object *bo; uint64_t paddr; - uint32_t format; - int ret, pitch; - int size; - - format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); + int ret; DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width, sizes->surface_height, sizes->surface_bpp, sizes->fb_width, sizes->fb_height); - pitch = align_pitch(sizes->surface_width, sizes->surface_bpp); + fourcc = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); + format = drm_get_format_info(dev, fourcc, DRM_FORMAT_MOD_LINEAR); + /* adreno needs pitch aligned to 32 pixels: */ + pitch = drm_format_info_min_pitch(format, 0, ALIGN(sizes->surface_width, 32)); + size = ALIGN(pitch * sizes->surface_height, PAGE_SIZE); /* allocate backing bo */ - size = pitch * sizes->surface_height; - DBG("allocating %d bytes for fb %d", size, dev->primary->index); + DBG("allocating %llu bytes for fb %d", size, dev->primary->index); bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN, NULL); if (IS_ERR(bo)) { drm_warn(dev, "could not allocate stolen bo\n"); @@ -125,16 +127,12 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, msm_gem_object_set_name(bo, "stolenfb"); - mode_cmd.pixel_format = format; + mode_cmd.pixel_format = fourcc; mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; mode_cmd.pitches[0] = pitch; - mode_cmd.modifier[0] = DRM_FORMAT_MOD_LINEAR; - fb = msm_framebuffer_init(dev, - drm_get_format_info(dev, mode_cmd.pixel_format, - mode_cmd.modifier[0]), - &mode_cmd, &bo); + fb = msm_framebuffer_init(dev, format, &mode_cmd, &bo); if (IS_ERR(fb)) { drm_err(dev, "failed to allocate fb\n"); ret = PTR_ERR(fb); From eb714953c44122fa4d9cb74cf98f22b46d3a5941 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 16:11:36 +0200 Subject: [PATCH 048/121] drm/msm: fbdev: Use a DRM client buffer Replace the internal DRM framebuffer with a DRM client buffer. The client buffer allocates the DRM framebuffer on a file and also uses GEM object handles via the regular ADDFB2 interfaces. Using client-buffer interfaces unifies framebuffer allocation for DRM clients in user space and msm's internal fbdev emulation. It also simplifies the clean-up side of the fbdev emulation. Signed-off-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/733891/ Link: https://lore.kernel.org/r/20260618141249.151338-6-tzimmermann@suse.de [DB: fixed error handling in msm_fbdev_driver_fbdev_probe] Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_fbdev.c | 56 +++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 35b26830afb4..89ca9da3e1f2 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -41,17 +41,15 @@ static int msm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma) static void msm_fbdev_fb_destroy(struct fb_info *info) { struct drm_fb_helper *helper = (struct drm_fb_helper *)info->par; - struct drm_framebuffer *fb = helper->fb; - struct drm_gem_object *bo = msm_framebuffer_bo(fb, 0); + struct drm_gem_object *bo = msm_framebuffer_bo(helper->fb, 0); DBG(); drm_fb_helper_fini(helper); - /* this will free the backing object */ msm_gem_put_vaddr(bo); - drm_framebuffer_remove(fb); + drm_client_buffer_delete(helper->buffer); drm_client_release(&helper->client); } @@ -90,15 +88,16 @@ static const struct drm_fb_helper_funcs msm_fbdev_helper_funcs = { int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { - struct drm_device *dev = helper->dev; + struct drm_client_dev *client = &helper->client; + struct drm_device *dev = client->dev; + struct drm_file *file = client->file; struct msm_drm_private *priv = dev->dev_private; struct fb_info *fbi = helper->info; - struct drm_mode_fb_cmd2 mode_cmd = { }; - struct drm_framebuffer *fb = NULL; const struct drm_format_info *format; - u32 fourcc, pitch; + u32 fourcc, pitch, handle; u64 size; struct drm_gem_object *bo; + struct drm_client_buffer *buffer; uint64_t paddr; int ret; @@ -127,16 +126,15 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, msm_gem_object_set_name(bo, "stolenfb"); - mode_cmd.pixel_format = fourcc; - mode_cmd.width = sizes->surface_width; - mode_cmd.height = sizes->surface_height; - mode_cmd.pitches[0] = pitch; - - fb = msm_framebuffer_init(dev, format, &mode_cmd, &bo); - if (IS_ERR(fb)) { - drm_err(dev, "failed to allocate fb\n"); - ret = PTR_ERR(fb); + ret = drm_gem_handle_create(file, bo, &handle); + if (ret) goto err_drm_gem_object_put; + + buffer = drm_client_buffer_create(client, sizes->surface_width, sizes->surface_height, + fourcc, handle, pitch); + if (IS_ERR(buffer)) { + ret = PTR_ERR(buffer); + goto err_drm_gem_handle_delete; } /* @@ -147,13 +145,14 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, ret = msm_gem_get_and_pin_iova(bo, priv->kms->vm, &paddr); if (ret) { drm_err(dev, "failed to get buffer obj iova: %d\n", ret); - goto err_drm_framebuffer_remove; + goto err_drm_client_buffer_delete; } DBG("fbi=%p, dev=%p", fbi, dev); helper->funcs = &msm_fbdev_helper_funcs; - helper->fb = fb; + helper->buffer = buffer; + helper->fb = buffer->fb; fbi->fbops = &msm_fb_ops; @@ -162,19 +161,30 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, fbi->screen_buffer = msm_gem_get_vaddr(bo); if (IS_ERR(fbi->screen_buffer)) { ret = PTR_ERR(fbi->screen_buffer); - goto err_drm_framebuffer_remove; + goto err_msm_gem_unpin; } fbi->screen_size = bo->size; fbi->fix.smem_start = paddr; fbi->fix.smem_len = bo->size; DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); - DBG("allocated %dx%d fb", fb->width, fb->height); + DBG("allocated %dx%d fb", buffer->fb->width, buffer->fb->height); + + /* The handle is only needed for creating the framebuffer. */ + drm_gem_handle_delete(file, handle); + + /* The framebuffer still holds a reference on the GEM object. */ + drm_gem_object_put(bo); return 0; -err_drm_framebuffer_remove: - drm_framebuffer_remove(fb); +err_msm_gem_unpin: + msm_gem_unpin_iova(bo, priv->kms->vm); + msm_gem_vma_put(bo); +err_drm_client_buffer_delete: + drm_client_buffer_delete(buffer); +err_drm_gem_handle_delete: + drm_gem_handle_delete(file, handle); err_drm_gem_object_put: drm_gem_object_put(bo); return ret; From 1a2bfb1cf7dff01646657606f5a8bddb8b3ea868 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 18 Jun 2026 16:11:37 +0200 Subject: [PATCH 049/121] drm/msm: Make msm_framebuffer_init() an internal interface again The only caller of msm_framebuffer_init() is msm_framebuffer_create() from the same source file. Declare the former as static. Signed-off-by: Thomas Zimmermann Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/733889/ Link: https://lore.kernel.org/r/20260618141249.151338-7-tzimmermann@suse.de Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_drv.h | 4 ---- drivers/gpu/drm/msm/msm_fb.c | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index b21f8971dfa1..2a3ce6afca1c 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -258,10 +258,6 @@ void msm_framebuffer_cleanup(struct drm_framebuffer *fb, bool needed_dirtyfb); uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb, int plane); struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane); const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb); -struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, - const struct drm_format_info *info, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object **bos); struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_format_info *info, const struct drm_mode_fb_cmd2 *mode_cmd); diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index dec550e6cf75..60c108d35d2a 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -134,10 +134,10 @@ const struct msm_format *msm_framebuffer_format(struct drm_framebuffer *fb) return msm_fb->format; } -struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, - const struct drm_format_info *info, - const struct drm_mode_fb_cmd2 *mode_cmd, - struct drm_gem_object **bos) +static struct drm_framebuffer * +msm_framebuffer_init(struct drm_device *dev, const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object **bos) { struct msm_drm_private *priv = dev->dev_private; struct msm_kms *kms = priv->kms; From eecba818a962b74f569cef5f388f41d7a889122f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 2 Jul 2026 11:36:40 +0200 Subject: [PATCH 050/121] drm/msm/hdmi_bridge: Simplify register bit updates Simplify reister updates (read, apply mask, write) with a wrapper to make code more obvious and avoid possible errors of reading and writing to different registers. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/737201/ Link: https://lore.kernel.org/r/20260702-drm-msm-hdmi-cleanup-v2-1-a4a4f0e8895b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/hdmi/hdmi.h | 19 +++++ drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 98 +++++++++++--------------- 2 files changed, 60 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 49433f7727c3..436d4f9fe346 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -112,6 +112,25 @@ static inline u32 hdmi_read(struct hdmi *hdmi, u32 reg) return readl(hdmi->mmio + reg); } +static inline void hdmi_clear_bits(struct hdmi *hdmi, u32 reg, u32 mask) +{ + u32 val; + + val = hdmi_read(hdmi, reg); + val &= ~mask; + hdmi_write(hdmi, reg, val); +} + +static inline void hdmi_update_bits(struct hdmi *hdmi, u32 reg, u32 mask, u32 data) +{ + u32 val; + + val = hdmi_read(hdmi, reg); + val &= ~mask; + val |= data & mask; + hdmi_write(hdmi, reg, val); +} + static inline u32 hdmi_qfprom_read(struct hdmi *hdmi, u32 reg) { return readl(hdmi->qfprom_mmio + reg); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 7abb9243dba5..157f19bd90b4 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -58,16 +58,13 @@ static int msm_hdmi_bridge_clear_avi_infoframe(struct drm_bridge *bridge) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; - u32 val; - val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0); - val &= ~(HDMI_INFOFRAME_CTRL0_AVI_SEND | - HDMI_INFOFRAME_CTRL0_AVI_CONT); - hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val); + hdmi_clear_bits(hdmi, REG_HDMI_INFOFRAME_CTRL0, + HDMI_INFOFRAME_CTRL0_AVI_SEND | + HDMI_INFOFRAME_CTRL0_AVI_CONT); - val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1); - val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK; - hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val); + hdmi_clear_bits(hdmi, REG_HDMI_INFOFRAME_CTRL1, + HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK); return 0; } @@ -76,18 +73,15 @@ static int msm_hdmi_bridge_clear_audio_infoframe(struct drm_bridge *bridge) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; - u32 val; - val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0); - val &= ~(HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND | - HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT | - HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE | - HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE); - hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val); + hdmi_clear_bits(hdmi, REG_HDMI_INFOFRAME_CTRL0, + HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND | + HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT | + HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE | + HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE); - val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1); - val &= ~HDMI_INFOFRAME_CTRL1_AUDIO_INFO_LINE__MASK; - hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val); + hdmi_clear_bits(hdmi, REG_HDMI_INFOFRAME_CTRL1, + HDMI_INFOFRAME_CTRL1_AUDIO_INFO_LINE__MASK); return 0; } @@ -96,13 +90,11 @@ static int msm_hdmi_bridge_clear_spd_infoframe(struct drm_bridge *bridge) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; - u32 val; - val = hdmi_read(hdmi, REG_HDMI_GEN_PKT_CTRL); - val &= ~(HDMI_GEN_PKT_CTRL_GENERIC1_SEND | - HDMI_GEN_PKT_CTRL_GENERIC1_CONT | - HDMI_GEN_PKT_CTRL_GENERIC1_LINE__MASK); - hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, val); + hdmi_clear_bits(hdmi, REG_HDMI_GEN_PKT_CTRL, + HDMI_GEN_PKT_CTRL_GENERIC1_SEND | + HDMI_GEN_PKT_CTRL_GENERIC1_CONT | + HDMI_GEN_PKT_CTRL_GENERIC1_LINE__MASK); return 0; } @@ -111,14 +103,12 @@ static int msm_hdmi_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; - u32 val; - val = hdmi_read(hdmi, REG_HDMI_GEN_PKT_CTRL); - val &= ~(HDMI_GEN_PKT_CTRL_GENERIC0_SEND | - HDMI_GEN_PKT_CTRL_GENERIC0_CONT | - HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE | - HDMI_GEN_PKT_CTRL_GENERIC0_LINE__MASK); - hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, val); + hdmi_clear_bits(hdmi, REG_HDMI_GEN_PKT_CTRL, + HDMI_GEN_PKT_CTRL_GENERIC0_SEND | + HDMI_GEN_PKT_CTRL_GENERIC0_CONT | + HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE | + HDMI_GEN_PKT_CTRL_GENERIC0_LINE__MASK); return 0; } @@ -129,7 +119,6 @@ static int msm_hdmi_bridge_write_avi_infoframe(struct drm_bridge *bridge, struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; u32 buf[4] = {}; - u32 val; int i; if (len != HDMI_INFOFRAME_SIZE(AVI) || len - 3 > sizeof(buf)) { @@ -153,15 +142,13 @@ static int msm_hdmi_bridge_write_avi_infoframe(struct drm_bridge *bridge, for (i = 0; i < ARRAY_SIZE(buf); i++) hdmi_write(hdmi, REG_HDMI_AVI_INFO(i), buf[i]); - val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0); - val |= HDMI_INFOFRAME_CTRL0_AVI_SEND | - HDMI_INFOFRAME_CTRL0_AVI_CONT; - hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val); + hdmi_update_bits(hdmi, REG_HDMI_INFOFRAME_CTRL0, + HDMI_INFOFRAME_CTRL0_AVI_SEND | HDMI_INFOFRAME_CTRL0_AVI_CONT, + HDMI_INFOFRAME_CTRL0_AVI_SEND | HDMI_INFOFRAME_CTRL0_AVI_CONT); - val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1); - val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK; - val |= HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE(AVI_IFRAME_LINE_NUMBER); - hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val); + hdmi_update_bits(hdmi, REG_HDMI_INFOFRAME_CTRL1, + HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK, + HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE(AVI_IFRAME_LINE_NUMBER)); return 0; } @@ -193,12 +180,11 @@ static int msm_hdmi_bridge_write_audio_infoframe(struct drm_bridge *bridge, buffer[9] << 16 | buffer[10] << 24); - val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0); - val |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND | - HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT | - HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE | - HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE; - hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val); + val = HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND | + HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT | + HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE | + HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE; + hdmi_update_bits(hdmi, REG_HDMI_INFOFRAME_CTRL0, val, val); return 0; } @@ -231,11 +217,10 @@ static int msm_hdmi_bridge_write_spd_infoframe(struct drm_bridge *bridge, for (i = 0; i < ARRAY_SIZE(buf); i++) hdmi_write(hdmi, REG_HDMI_GENERIC1(i), buf[i]); - val = hdmi_read(hdmi, REG_HDMI_GEN_PKT_CTRL); - val |= HDMI_GEN_PKT_CTRL_GENERIC1_SEND | - HDMI_GEN_PKT_CTRL_GENERIC1_CONT | - HDMI_GEN_PKT_CTRL_GENERIC1_LINE(SPD_IFRAME_LINE_NUMBER); - hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, val); + val = HDMI_GEN_PKT_CTRL_GENERIC1_SEND | + HDMI_GEN_PKT_CTRL_GENERIC1_CONT | + HDMI_GEN_PKT_CTRL_GENERIC1_LINE(SPD_IFRAME_LINE_NUMBER); + hdmi_update_bits(hdmi, REG_HDMI_GEN_PKT_CTRL, val, val); return 0; } @@ -269,12 +254,11 @@ static int msm_hdmi_bridge_write_hdmi_infoframe(struct drm_bridge *bridge, for (i = 0; i < ARRAY_SIZE(buf); i++) hdmi_write(hdmi, REG_HDMI_GENERIC0(i), buf[i]); - val = hdmi_read(hdmi, REG_HDMI_GEN_PKT_CTRL); - val |= HDMI_GEN_PKT_CTRL_GENERIC0_SEND | - HDMI_GEN_PKT_CTRL_GENERIC0_CONT | - HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE | - HDMI_GEN_PKT_CTRL_GENERIC0_LINE(VENSPEC_IFRAME_LINE_NUMBER); - hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, val); + val = HDMI_GEN_PKT_CTRL_GENERIC0_SEND | + HDMI_GEN_PKT_CTRL_GENERIC0_CONT | + HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE | + HDMI_GEN_PKT_CTRL_GENERIC0_LINE(VENSPEC_IFRAME_LINE_NUMBER); + hdmi_update_bits(hdmi, REG_HDMI_GEN_PKT_CTRL, val, val); return 0; } From c5f6b1abca25ab00f9ad99bfeff5c45b1557ad2c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 2 Jul 2026 11:36:41 +0200 Subject: [PATCH 051/121] drm/msm/hdmi_hdcp: Simplify register bit updates Simplify reister updates (read, apply mask, write) with a wrapper to make code more obvious and avoid possible errors of reading and writing to different registers. Reviewed-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/737203/ Link: https://lore.kernel.org/r/20260702-drm-msm-hdmi-cleanup-v2-2-a4a4f0e8895b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c | 85 +++++++++------------------- 1 file changed, 28 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c index 8fb5497aac9f..7862bd67d154 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c @@ -306,9 +306,9 @@ static int msm_reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl) HDMI_HDCP_DDC_CTRL_0_DISABLE); /* ACK the Failure to Clear it */ - reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_CTRL_1); - reg_val |= HDMI_HDCP_DDC_CTRL_1_FAILED_ACK; - hdmi_write(hdmi, REG_HDMI_HDCP_DDC_CTRL_1, reg_val); + hdmi_update_bits(hdmi, REG_HDMI_HDCP_DDC_CTRL_1, + HDMI_HDCP_DDC_CTRL_1_FAILED_ACK, + HDMI_HDCP_DDC_CTRL_1_FAILED_ACK); /* Check if the FAILURE got Cleared */ reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DDC_STATUS); @@ -324,28 +324,22 @@ static int msm_reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl) DBG("Before: HDMI_DDC_SW_STATUS=0x%08x", hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS)); /* Reset HDMI DDC software status */ - reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); - reg_val |= HDMI_DDC_CTRL_SW_STATUS_RESET; - hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); + hdmi_update_bits(hdmi, REG_HDMI_DDC_CTRL, HDMI_DDC_CTRL_SW_STATUS_RESET, + HDMI_DDC_CTRL_SW_STATUS_RESET); rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); - reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); - reg_val &= ~HDMI_DDC_CTRL_SW_STATUS_RESET; - hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_DDC_CTRL, HDMI_DDC_CTRL_SW_STATUS_RESET); /* Reset HDMI DDC Controller */ - reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); - reg_val |= HDMI_DDC_CTRL_SOFT_RESET; - hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); + hdmi_update_bits(hdmi, REG_HDMI_DDC_CTRL, HDMI_DDC_CTRL_SOFT_RESET, + HDMI_DDC_CTRL_SOFT_RESET); /* If previous msleep is aborted, skip this msleep */ if (!rc) rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); - reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); - reg_val &= ~HDMI_DDC_CTRL_SOFT_RESET; - hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_DDC_CTRL, HDMI_DDC_CTRL_SOFT_RESET); DBG("After: HDMI_DDC_SW_STATUS=0x%08x", hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS)); } @@ -399,7 +393,6 @@ static void msm_hdmi_hdcp_reauth_work(struct work_struct *work) struct hdmi_hdcp_ctrl, hdcp_reauth_work); struct hdmi *hdmi = hdcp_ctrl->hdmi; unsigned long flags; - u32 reg_val; DBG("HDCP REAUTH WORK"); /* @@ -409,9 +402,7 @@ static void msm_hdmi_hdcp_reauth_work(struct work_struct *work) * AN1_READY bits in HDMI_HDCP_LINK0_STATUS register */ spin_lock_irqsave(&hdmi->reg_lock, flags); - reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); - reg_val &= ~HDMI_HPD_CTRL_ENABLE; - hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_HPD_CTRL, HDMI_HPD_CTRL_ENABLE); /* Disable HDCP interrupts */ hdmi_write(hdmi, REG_HDMI_HDCP_INT_CTRL, 0); @@ -431,9 +422,8 @@ static void msm_hdmi_hdcp_reauth_work(struct work_struct *work) /* Enable HPD circuitry */ spin_lock_irqsave(&hdmi->reg_lock, flags); - reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); - reg_val |= HDMI_HPD_CTRL_ENABLE; - hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); + hdmi_update_bits(hdmi, REG_HDMI_HPD_CTRL, HDMI_HPD_CTRL_ENABLE, + HDMI_HPD_CTRL_ENABLE); spin_unlock_irqrestore(&hdmi->reg_lock, flags); /* @@ -456,7 +446,6 @@ static int msm_hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; u32 link0_status; - u32 reg_val; unsigned long flags; int rc; @@ -472,14 +461,11 @@ static int msm_hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) spin_lock_irqsave(&hdmi->reg_lock, flags); /* disable HDMI Encrypt */ - reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); - reg_val &= ~HDMI_CTRL_ENCRYPTED; - hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_CTRL, HDMI_CTRL_ENCRYPTED); /* Enabling Software DDC */ - reg_val = hdmi_read(hdmi, REG_HDMI_DDC_ARBITRATION); - reg_val &= ~HDMI_DDC_ARBITRATION_HW_ARBITRATION; - hdmi_write(hdmi, REG_HDMI_DDC_ARBITRATION, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_DDC_ARBITRATION, + HDMI_DDC_ARBITRATION_HW_ARBITRATION); spin_unlock_irqrestore(&hdmi->reg_lock, flags); /* @@ -498,9 +484,8 @@ static int msm_hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) hdmi_write(hdmi, REG_HDMI_HDCP_ENTROPY_CTRL1, 0xF00DFACE); /* Disable the RngCipher state */ - reg_val = hdmi_read(hdmi, REG_HDMI_HDCP_DEBUG_CTRL); - reg_val &= ~HDMI_HDCP_DEBUG_CTRL_RNG_CIPHER; - hdmi_write(hdmi, REG_HDMI_HDCP_DEBUG_CTRL, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_HDCP_DEBUG_CTRL, + HDMI_HDCP_DEBUG_CTRL_RNG_CIPHER); DBG("HDCP_DEBUG_CTRL=0x%08x", hdmi_read(hdmi, REG_HDMI_HDCP_DEBUG_CTRL)); @@ -537,15 +522,12 @@ static int msm_hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) static void msm_hdmi_hdcp_auth_fail(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; - u32 reg_val; unsigned long flags; DBG("hdcp auth failed, queue reauth work"); /* clear HDMI Encrypt */ spin_lock_irqsave(&hdmi->reg_lock, flags); - reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); - reg_val &= ~HDMI_CTRL_ENCRYPTED; - hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_CTRL, HDMI_CTRL_ENCRYPTED); spin_unlock_irqrestore(&hdmi->reg_lock, flags); hdcp_ctrl->hdcp_state = HDCP_STATE_AUTH_FAILED; @@ -555,7 +537,6 @@ static void msm_hdmi_hdcp_auth_fail(struct hdmi_hdcp_ctrl *hdcp_ctrl) static void msm_hdmi_hdcp_auth_done(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; - u32 reg_val; unsigned long flags; /* @@ -563,16 +544,15 @@ static void msm_hdmi_hdcp_auth_done(struct hdmi_hdcp_ctrl *hdcp_ctrl) * there is no Arbitration between software and hardware for DDC */ spin_lock_irqsave(&hdmi->reg_lock, flags); - reg_val = hdmi_read(hdmi, REG_HDMI_DDC_ARBITRATION); - reg_val |= HDMI_DDC_ARBITRATION_HW_ARBITRATION; - hdmi_write(hdmi, REG_HDMI_DDC_ARBITRATION, reg_val); + hdmi_update_bits(hdmi, REG_HDMI_DDC_ARBITRATION, + HDMI_DDC_ARBITRATION_HW_ARBITRATION, + HDMI_DDC_ARBITRATION_HW_ARBITRATION); spin_unlock_irqrestore(&hdmi->reg_lock, flags); /* enable HDMI Encrypt */ spin_lock_irqsave(&hdmi->reg_lock, flags); - reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); - reg_val |= HDMI_CTRL_ENCRYPTED; - hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + hdmi_update_bits(hdmi, REG_HDMI_CTRL, HDMI_CTRL_ENCRYPTED, + HDMI_CTRL_ENCRYPTED); spin_unlock_irqrestore(&hdmi->reg_lock, flags); hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATED; @@ -1304,7 +1284,6 @@ static void msm_hdmi_hdcp_auth_work(struct work_struct *work) void msm_hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; - u32 reg_val; unsigned long flags; if ((HDCP_STATE_INACTIVE != hdcp_ctrl->hdcp_state) || @@ -1315,9 +1294,7 @@ void msm_hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl) /* clear HDMI Encrypt */ spin_lock_irqsave(&hdmi->reg_lock, flags); - reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); - reg_val &= ~HDMI_CTRL_ENCRYPTED; - hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_CTRL, HDMI_CTRL_ENCRYPTED); spin_unlock_irqrestore(&hdmi->reg_lock, flags); hdcp_ctrl->auth_event = 0; @@ -1330,7 +1307,6 @@ void msm_hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; unsigned long flags; - u32 reg_val; if ((HDCP_STATE_INACTIVE == hdcp_ctrl->hdcp_state) || (HDCP_STATE_NO_AKSV == hdcp_ctrl->hdcp_state)) { @@ -1345,9 +1321,7 @@ void msm_hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl) * AN1_READY bits in HDMI_HDCP_LINK0_STATUS register */ spin_lock_irqsave(&hdmi->reg_lock, flags); - reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); - reg_val &= ~HDMI_HPD_CTRL_ENABLE; - hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_HPD_CTRL, HDMI_HPD_CTRL_ENABLE); /* * Disable HDCP interrupts. @@ -1375,14 +1349,11 @@ void msm_hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl) hdmi_write(hdmi, REG_HDMI_HDCP_CTRL, 0); spin_lock_irqsave(&hdmi->reg_lock, flags); - reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); - reg_val &= ~HDMI_CTRL_ENCRYPTED; - hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); + hdmi_clear_bits(hdmi, REG_HDMI_CTRL, HDMI_CTRL_ENCRYPTED); /* Enable HPD circuitry */ - reg_val = hdmi_read(hdmi, REG_HDMI_HPD_CTRL); - reg_val |= HDMI_HPD_CTRL_ENABLE; - hdmi_write(hdmi, REG_HDMI_HPD_CTRL, reg_val); + hdmi_update_bits(hdmi, REG_HDMI_HPD_CTRL, HDMI_HPD_CTRL_ENABLE, + HDMI_HPD_CTRL_ENABLE); spin_unlock_irqrestore(&hdmi->reg_lock, flags); hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; From 8834f5494ab5e169b1c0fb4cbcb5464fd81d1ed0 Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Mon, 20 Jul 2026 15:56:08 +0800 Subject: [PATCH 052/121] drm/msm/dpu: Fix DMA SSPP REC block offsets on DPU v13 On DPU v13, the DMA SSPP REC0 and REC1 blocks are located at offsets 0x1000 and 0x3000 from the SSPP common base. The existing DMA SSPP sub-block descriptor does not initialize sspp_rec0_blk and sspp_rec1_blk, causing REC register accesses to be performed at offset 0 instead of the corresponding REC block. As a result, DMA SSPP pipes are not programmed correctly and fail to produce output. Introduce a DPU v13 specific DMA SSPP descriptor with the correct REC block offsets and use it for all DMA SSPPs in the Kaanapali catalog. Signed-off-by: Yongxing Mou Fixes: 83fe2cd56b1d ("drm/msm/dpu: Add support for Kaanapali DPU") Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/741230/ Link: https://lore.kernel.org/r/20260720-dpu-v13-dma-sspp-rec-fix-v1-1-10d69b4875e7@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- .../drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h | 12 ++++++------ drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 12 ++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h index 06da1583fb1e..85f455a9f29c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_13_0_kaanapali.h @@ -86,42 +86,42 @@ static const struct dpu_sspp_cfg kaanapali_sspp[] = { .name = "sspp_8", .id = SSPP_DMA0, .base = 0x97000, .len = 0x84, .features = DMA_SDM845_MASK_SDMA, - .sblk = &dpu_dma_sblk, + .sblk = &dpu_dma_sblk_v13, .xin_id = 1, .type = SSPP_TYPE_DMA, }, { .name = "sspp_9", .id = SSPP_DMA1, .base = 0xa0000, .len = 0x84, .features = DMA_SDM845_MASK_SDMA, - .sblk = &dpu_dma_sblk, + .sblk = &dpu_dma_sblk_v13, .xin_id = 5, .type = SSPP_TYPE_DMA, }, { .name = "sspp_10", .id = SSPP_DMA2, .base = 0xa9000, .len = 0x84, .features = DMA_SDM845_MASK_SDMA, - .sblk = &dpu_dma_sblk, + .sblk = &dpu_dma_sblk_v13, .xin_id = 9, .type = SSPP_TYPE_DMA, }, { .name = "sspp_11", .id = SSPP_DMA3, .base = 0xb2000, .len = 0x84, .features = DMA_SDM845_MASK_SDMA, - .sblk = &dpu_dma_sblk, + .sblk = &dpu_dma_sblk_v13, .xin_id = 13, .type = SSPP_TYPE_DMA, }, { .name = "sspp_12", .id = SSPP_DMA4, .base = 0xbb000, .len = 0x84, .features = DMA_CURSOR_SDM845_MASK_SDMA, - .sblk = &dpu_dma_sblk, + .sblk = &dpu_dma_sblk_v13, .xin_id = 14, .type = SSPP_TYPE_DMA, }, { .name = "sspp_13", .id = SSPP_DMA5, .base = 0xc4000, .len = 0x84, .features = DMA_CURSOR_SDM845_MASK_SDMA, - .sblk = &dpu_dma_sblk, + .sblk = &dpu_dma_sblk_v13, .xin_id = 15, .type = SSPP_TYPE_DMA, }, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index 2e10add84fd7..9a993cdfab85 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -303,6 +303,16 @@ static const u32 wb2_formats_rgb_yuv[] = { .num_formats = ARRAY_SIZE(plane_formats), \ } +#define _DMA_SBLK_V13() \ + { \ + .sspp_rec0_blk = {.name = "sspp_rec0", \ + .base = 0x1000, .len = 0x180,}, \ + .sspp_rec1_blk = {.name = "sspp_rec1", \ + .base = 0x3000, .len = 0x180,}, \ + .format_list = plane_formats, \ + .num_formats = ARRAY_SIZE(plane_formats), \ + } + static const struct dpu_rotation_cfg dpu_rot_sc7280_cfg_v2 = { .rot_maxheight = 1088, .rot_num_formats = ARRAY_SIZE(rotation_v2_formats), @@ -353,6 +363,8 @@ static const struct dpu_sspp_sub_blks dpu_rgb_sblk = _RGB_SBLK(); static const struct dpu_sspp_sub_blks dpu_dma_sblk = _DMA_SBLK(); +static const struct dpu_sspp_sub_blks dpu_dma_sblk_v13 = _DMA_SBLK_V13(); + /************************************************************* * MIXER sub blocks config *************************************************************/ From 93c125e4ea98fb25f927ba5a334d85845127d667 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 23 Jul 2026 12:52:12 +0300 Subject: [PATCH 053/121] drm/msm: don't tear down KMS twice when KMS init fails When priv->kms_init() (mdp4_kms_init() / mdp5_kms_init()) fails partway through, both display drivers already tear their KMS state down via mdp4_destroy() / mdp5_kms_destroy() before returning the error. The common error path in msm_drm_init() then runs msm_drm_uninit() -> msm_drm_kms_uninit(), which tries to destroy the very same KMS a second time, which causes a use-after-free crash. Bring MDP4/MDP5 in line with the DPU driver whose dpu_kms_init() doesn't perform error cleanup on the failure. Let the common path own the cleanup, instead of freeing the KMS from their error paths. The crash trace for the reference: __lock_acquire from lock_acquire (kernel/locking/lockdep.c:5906 kernel/locking/lockdep.c:5863) lock_acquire from touch_wq_lockdep_map (kernel/workqueue.c:4094 (discriminator 1)) touch_wq_lockdep_map from __flush_workqueue (kernel/workqueue.c:4136) __flush_workqueue from msm_drm_kms_uninit (drivers/gpu/drm/msm/msm_kms.c:243 (discriminator 33)) msm_drm_kms_uninit from msm_drm_uninit (drivers/gpu/drm/msm/msm_drv.c:93) msm_drm_uninit from msm_drm_init (drivers/gpu/drm/msm/msm_drv.c:184) msm_drm_init from try_to_bring_up_aggregate_device (drivers/base/component.c:249 drivers/base/component.c:227) try_to_bring_up_aggregate_device from __component_add (drivers/base/component.c:269 drivers/base/component.c:748) __component_add from dsi_host_attach (drivers/gpu/drm/msm/dsi/dsi_host.c:1739) dsi_host_attach from mipi_dsi_attach (drivers/gpu/drm/drm_mipi_dsi.c:383) mipi_dsi_attach from sharp_nt_panel_probe (drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c:247) Fixes: 506efcba3129 ("drm/msm: carve out KMS code from msm_drv.c") Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/742068/ Link: https://lore.kernel.org/r/20260723-msm-fix-crash-v1-1-78fb4721c2d9@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c | 22 ++++++++-------------- drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c | 11 +++-------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c index c289dff78cd5..9b1d1982e683 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c @@ -398,7 +398,7 @@ static int mdp4_kms_init(struct drm_device *dev) ret = mdp_kms_init(&mdp4_kms->base, &kms_funcs); if (ret) { DRM_DEV_ERROR(dev->dev, "failed to init kms\n"); - goto fail; + return ret; } kms = priv->kms; @@ -409,7 +409,7 @@ static int mdp4_kms_init(struct drm_device *dev) ret = regulator_enable(mdp4_kms->vdd); if (ret) { DRM_DEV_ERROR(dev->dev, "failed to enable regulator vdd: %d\n", ret); - goto fail; + return ret; } } @@ -421,7 +421,7 @@ static int mdp4_kms_init(struct drm_device *dev) DRM_DEV_ERROR(dev->dev, "unexpected MDP version: v%d.%d\n", major, minor); ret = -ENXIO; - goto fail; + return ret; } mdp4_kms->rev = minor; @@ -430,7 +430,7 @@ static int mdp4_kms_init(struct drm_device *dev) if (!mdp4_kms->lut_clk) { DRM_DEV_ERROR(dev->dev, "failed to get lut_clk\n"); ret = -ENODEV; - goto fail; + return ret; } clk_set_rate(mdp4_kms->lut_clk, max_clk); } @@ -452,7 +452,7 @@ static int mdp4_kms_init(struct drm_device *dev) vm = msm_kms_init_vm(mdp4_kms->dev, NULL); if (IS_ERR(vm)) { ret = PTR_ERR(vm); - goto fail; + return ret; } kms->vm = vm; @@ -460,7 +460,7 @@ static int mdp4_kms_init(struct drm_device *dev) ret = modeset_init(mdp4_kms); if (ret) { DRM_DEV_ERROR(dev->dev, "modeset_init failed: %d\n", ret); - goto fail; + return ret; } mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC | MSM_BO_SCANOUT, NULL); @@ -468,14 +468,14 @@ static int mdp4_kms_init(struct drm_device *dev) ret = PTR_ERR(mdp4_kms->blank_cursor_bo); DRM_DEV_ERROR(dev->dev, "could not allocate blank-cursor bo: %d\n", ret); mdp4_kms->blank_cursor_bo = NULL; - goto fail; + return ret; } ret = msm_gem_get_and_pin_iova(mdp4_kms->blank_cursor_bo, kms->vm, &mdp4_kms->blank_cursor_iova); if (ret) { DRM_DEV_ERROR(dev->dev, "could not pin blank-cursor bo: %d\n", ret); - goto fail; + return ret; } dev->mode_config.min_width = 0; @@ -484,12 +484,6 @@ static int mdp4_kms_init(struct drm_device *dev) dev->mode_config.max_height = 2048; return 0; - -fail: - if (kms) - mdp4_destroy(kms); - - return ret; } static const struct dev_pm_ops mdp4_pm_ops = { diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c index 0a004ab9fc85..3934cd060b27 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c @@ -517,7 +517,7 @@ static int mdp5_kms_init(struct drm_device *dev) ret = mdp_kms_init(&mdp5_kms->base, &kms_funcs); if (ret) { DRM_DEV_ERROR(&pdev->dev, "failed to init kms\n"); - goto fail; + return ret; } config = mdp5_cfg_get_config(mdp5_kms->cfg); @@ -540,7 +540,7 @@ static int mdp5_kms_init(struct drm_device *dev) vm = msm_kms_init_vm(mdp5_kms->dev, pdev->dev.parent); if (IS_ERR(vm)) { ret = PTR_ERR(vm); - goto fail; + return ret; } kms->vm = vm; @@ -550,7 +550,7 @@ static int mdp5_kms_init(struct drm_device *dev) ret = modeset_init(mdp5_kms); if (ret) { DRM_DEV_ERROR(&pdev->dev, "modeset_init failed: %d\n", ret); - goto fail; + return ret; } dev->mode_config.min_width = 0; @@ -562,11 +562,6 @@ static int mdp5_kms_init(struct drm_device *dev) dev->vblank_disable_immediate = true; return 0; -fail: - if (kms) - mdp5_kms_destroy(kms); - - return ret; } static void mdp5_destroy(struct mdp5_kms *mdp5_kms) From 684f95fb4e9ad10aac39fbb1fa7592a59d7f54ea Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 22 Jul 2026 09:36:16 +0300 Subject: [PATCH 054/121] drm/msm/dp: reject YUV420-only modes without VSC SDP support DP conveys YUV 420 colorimetry through a VSC SDP. A sink that advertises a mode as YUV-420-only therefore cannot be driven at all unless the panel supports VSC SDP, yet msm_dp_bridge_mode_valid() only used the VSC SDP capability to decide whether to halve the pixel clock, otherwise letting such modes through to be validated (and possibly accepted) at the full RGB clock the sink cannot display. Reject 420-only modes with MODE_NO_420 when the panel does not support VSC SDP. With those modes filtered out, being a 420-only mode implies VSC SDP support, so the YUV-420 test reduces to drm_mode_is_420_only(): drop msm_dp_is_yuv_420_enabled() and call the DRM helper directly at its two callers (the DPU encoder already has the connector from the atomic state). Fixes: df9cf852ca30 ("drm/msm/dp: account for widebus and yuv420 during mode validation") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/741713/ Link: https://lore.kernel.org/r/20260722-drm-msm-display-interface-v1-1-368c10fe62fd@oss.qualcomm.com --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 3 +-- drivers/gpu/drm/msm/dp/dp_display.c | 28 +++++++++------------ drivers/gpu/drm/msm/msm_drv.h | 8 ------ 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 778e231d4967..1f20695f81e3 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -710,8 +710,7 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc, if (fb && MSM_FORMAT_IS_YUV(msm_framebuffer_format(fb))) topology->num_cdm++; } else if (disp_info->intf_type == INTF_DP) { - if (msm_dp_is_yuv_420_enabled(priv->kms->dp[disp_info->h_tile_instance[0]], - adj_mode)) + if (drm_mode_is_420_only(&connector->display_info, adj_mode)) topology->num_cdm++; } } diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index dc6f33809ca5..e0c44eef3aba 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -698,6 +698,7 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0; struct msm_dp *dp; int mode_pclk_khz = mode->clock; + bool is_yuv_420; dp = to_dp_bridge(bridge)->msm_dp_display; @@ -709,9 +710,16 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); link_info = &msm_dp_display->panel->link_info; - if ((drm_mode_is_420_only(&dp->connector->display_info, mode) && - msm_dp_display->panel->vsc_sdp_supported) || - msm_dp_wide_bus_available(dp)) + is_yuv_420 = drm_mode_is_420_only(&dp->connector->display_info, mode); + + /* + * YUV 420 is carried over DP by signalling the colorimetry through a + * VSC SDP, so a 420-only mode cannot be driven without VSC SDP support. + */ + if (is_yuv_420 && !msm_dp_display->panel->vsc_sdp_supported) + return MODE_NO_420; + + if (is_yuv_420 || msm_dp_wide_bus_available(dp)) mode_pclk_khz /= 2; if (mode_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) @@ -1277,22 +1285,10 @@ void __exit msm_dp_unregister(void) platform_driver_unregister(&msm_dp_display_driver); } -bool msm_dp_is_yuv_420_enabled(const struct msm_dp *msm_dp_display, - const struct drm_display_mode *mode) -{ - struct msm_dp_display_private *dp; - const struct drm_display_info *info; - - dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); - info = &msm_dp_display->connector->display_info; - - return dp->panel->vsc_sdp_supported && drm_mode_is_420_only(info, mode); -} - bool msm_dp_needs_periph_flush(const struct msm_dp *msm_dp_display, const struct drm_display_mode *mode) { - return msm_dp_is_yuv_420_enabled(msm_dp_display, mode); + return drm_mode_is_420_only(&msm_dp_display->connector->display_info, mode); } bool msm_dp_wide_bus_available(const struct msm_dp *msm_dp_display) diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 2a3ce6afca1c..eb4bbae8557b 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -354,8 +354,6 @@ void __exit msm_dp_unregister(void); int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev, struct drm_encoder *encoder, bool yuv_supported); void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp_display); -bool msm_dp_is_yuv_420_enabled(const struct msm_dp *dp_display, - const struct drm_display_mode *mode); bool msm_dp_needs_periph_flush(const struct msm_dp *dp_display, const struct drm_display_mode *mode); bool msm_dp_wide_bus_available(const struct msm_dp *dp_display); @@ -380,12 +378,6 @@ static inline void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm { } -static inline bool msm_dp_is_yuv_420_enabled(const struct msm_dp *dp_display, - const struct drm_display_mode *mode) -{ - return false; -} - static inline bool msm_dp_needs_periph_flush(const struct msm_dp *dp_display, const struct drm_display_mode *mode) { From bd926e62d355879133452bc3889447f8e89757f2 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 22 Jul 2026 09:36:30 +0300 Subject: [PATCH 055/121] drm/msm/dp: do not reject wide-bus modes while a YUV420 mode is active msm_dp_bridge_mode_valid() halves the candidate mode's pixel clock when the sink either uses YUV 420 output or drives the wide bus, so that modes relying on those to stay under DP_MAX_PIXEL_CLK_KHZ are accepted. The wide bus part is queried through msm_dp_wide_bus_available(), which returns false whenever the currently committed mode uses YUV 420 output: it inspects the stored msm_dp_mode.out_fmt_is_yuv_420 of the active mode, not the mode being validated. Consequently, while a YUV 420 mode is active, an RGB mode that needs the wide bus to fit under DP_MAX_PIXEL_CLK_KHZ has its pixel clock left un-halved and is wrongly rejected as MODE_CLOCK_HIGH. The candidate mode's YUV 420 status is already evaluated as is_yuv_420, and the wide bus is disabled precisely for YUV 420 output, so halving the pixel clock for either case is equivalent to halving it when the candidate is YUV 420 or the controller supports the wide bus. Test wide_bus_supported directly, so the decision no longer depends on the format of the active mode. Fixes: df9cf852ca30 ("drm/msm/dp: account for widebus and yuv420 during mode validation") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/741740/ Link: https://lore.kernel.org/r/20260722-drm-msm-display-interface-v1-15-368c10fe62fd@oss.qualcomm.com --- drivers/gpu/drm/msm/dp/dp_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index e0c44eef3aba..79e2b171e269 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -719,7 +719,7 @@ enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, if (is_yuv_420 && !msm_dp_display->panel->vsc_sdp_supported) return MODE_NO_420; - if (is_yuv_420 || msm_dp_wide_bus_available(dp)) + if (is_yuv_420 || msm_dp_display->wide_bus_supported) mode_pclk_khz /= 2; if (mode_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) From db6f98b9515f09cc345949b1002d6d71c0bef473 Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Tue, 28 Jul 2026 18:21:30 +0800 Subject: [PATCH 056/121] drm/msm/dp: remove cached drm_edid from panel The cached drm_edid seems unnecessary here. Use the drm_edid pointer directly in the plug stage instead of caching it. Remove the cached drm_edid and the corresponding oneliner to simplify the code. Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742727/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-1-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_display.c | 28 +++++++++----- drivers/gpu/drm/msm/dp/dp_panel.c | 59 +++-------------------------- drivers/gpu/drm/msm/dp/dp_panel.h | 13 ++----- 3 files changed, 27 insertions(+), 73 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 79e2b171e269..ba3804a12a57 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -269,6 +269,7 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) const struct drm_display_info *info = &connector->display_info; int rc = 0; u8 dpcd[DP_RECEIVER_CAP_SIZE]; + const struct drm_edid *drm_edid = NULL; rc = drm_dp_read_dpcd_caps(dp->aux, dpcd); if (rc) @@ -276,10 +277,20 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) dp->link->lttpr_count = msm_dp_display_lttpr_init(dp, dpcd); - rc = msm_dp_panel_read_sink_caps(dp->panel, connector); + rc = msm_dp_panel_read_link_caps(dp->panel, connector); if (rc) goto end; + drm_edid = drm_edid_read_ddc(connector, &dp->aux->ddc); + drm_edid_connector_update(connector, drm_edid); + + if (!drm_edid) { + DRM_ERROR("panel edid read failed\n"); + /* check edid read fail is due to unplug */ + if (!msm_dp_aux_is_link_connected(dp->aux)) + return -ETIMEDOUT; + } + msm_dp_link_process_request(dp->link); if (!dp->msm_dp_display.is_edp) @@ -291,7 +302,7 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) dp->msm_dp_display.psr_supported = dp->panel->psr_cap.version && psr_enabled; dp->audio_supported = info->has_audio; - msm_dp_panel_handle_sink_request(dp->panel); + msm_dp_panel_handle_sink_request(dp->panel, drm_edid); /* * set sink to normal operation mode -- D0 @@ -302,6 +313,7 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) msm_dp_link_reset_phy_params_vx_px(dp->link); end: + drm_edid_free(drm_edid); return rc; } @@ -453,7 +465,7 @@ static int msm_dp_hpd_unplug_handle(struct msm_dp_display_private *dp) /* Don't forget modes for eDP */ if (!dp->msm_dp_display.is_edp) - msm_dp_panel_unplugged(dp->panel, dp->msm_dp_display.connector); + drm_edid_connector_update(dp->msm_dp_display.connector, NULL); /* triggered by irq_hdp with sink_count = 0 */ if (dp->link->sink_count == 0) @@ -515,7 +527,6 @@ static int msm_dp_irq_hpd_handle(struct msm_dp_display_private *dp) static void msm_dp_display_deinit_sub_modules(struct msm_dp_display_private *dp) { msm_dp_audio_put(dp->audio); - msm_dp_panel_put(dp->panel); msm_dp_aux_put(dp->aux); } @@ -566,7 +577,7 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp) rc = PTR_ERR(dp->ctrl); DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc); dp->ctrl = NULL; - goto error_ctrl; + goto error_link; } dp->audio = msm_dp_audio_get(dp->msm_dp_display.pdev, dp->link_base); @@ -574,13 +585,11 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp) rc = PTR_ERR(dp->audio); pr_err("failed to initialize audio, rc = %d\n", rc); dp->audio = NULL; - goto error_ctrl; + goto error_link; } return rc; -error_ctrl: - msm_dp_panel_put(dp->panel); error_link: msm_dp_aux_put(dp->aux); error: @@ -752,8 +761,7 @@ int msm_dp_display_get_modes(struct msm_dp *dp) msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); - return msm_dp_panel_get_modes(msm_dp_display->panel, - dp->connector); + return drm_edid_connector_add_modes(msm_dp_display->panel->connector); } bool msm_dp_display_check_video_test(struct msm_dp *dp) diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index 5b4954e7cb74..bde4a772d22c 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -232,8 +232,8 @@ static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel, return min_supported_bpp; } -int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, - struct drm_connector *connector) +int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel, + struct drm_connector *connector) { int rc, bw_code; int count; @@ -271,36 +271,9 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, rc = drm_dp_read_downstream_info(panel->aux, msm_dp_panel->dpcd, msm_dp_panel->downstream_ports); - if (rc) - return rc; - - drm_edid_free(msm_dp_panel->drm_edid); - - msm_dp_panel->drm_edid = drm_edid_read_ddc(connector, &panel->aux->ddc); - - drm_edid_connector_update(connector, msm_dp_panel->drm_edid); - - if (!msm_dp_panel->drm_edid) { - DRM_ERROR("panel edid read failed\n"); - /* check edid read fail is due to unplug */ - if (!msm_dp_aux_is_link_connected(panel->aux)) { - rc = -ETIMEDOUT; - goto end; - } - } - -end: return rc; } -void msm_dp_panel_unplugged(struct msm_dp_panel *msm_dp_panel, - struct drm_connector *connector) -{ - drm_edid_connector_update(connector, NULL); - drm_edid_free(msm_dp_panel->drm_edid); - msm_dp_panel->drm_edid = NULL; -} - u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, u32 mode_edid_bpp, u32 mode_pclk_khz) { @@ -324,22 +297,6 @@ u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, return bpp; } -int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel, - struct drm_connector *connector) -{ - if (!msm_dp_panel) { - DRM_ERROR("invalid input\n"); - return -EINVAL; - } - - if (msm_dp_panel->drm_edid) { - drm_edid_connector_update(connector, msm_dp_panel->drm_edid); - return drm_edid_connector_add_modes(connector); - } - - return 0; -} - static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid) { edid += edid->extensions; @@ -347,7 +304,8 @@ static u8 msm_dp_panel_get_edid_checksum(const struct edid *edid) return edid->checksum; } -void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel) +void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel, + const struct drm_edid *drm_edid) { struct msm_dp_panel_private *panel; @@ -360,7 +318,7 @@ void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel) if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) { /* FIXME: get rid of drm_edid_raw() */ - const struct edid *edid = drm_edid_raw(msm_dp_panel->drm_edid); + const struct edid *edid = drm_edid_raw(drm_edid); u8 checksum; if (edid) @@ -757,10 +715,3 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux return msm_dp_panel; } -void msm_dp_panel_put(struct msm_dp_panel *msm_dp_panel) -{ - if (!msm_dp_panel) - return; - - drm_edid_free(msm_dp_panel->drm_edid); -} diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h index 9173e90a5053..53b7b4463551 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.h +++ b/drivers/gpu/drm/msm/dp/dp_panel.h @@ -33,7 +33,6 @@ struct msm_dp_panel { u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct msm_dp_link_info link_info; - const struct drm_edid *drm_edid; struct drm_connector *connector; struct msm_dp_display_mode msm_dp_mode; struct msm_dp_panel_psr psr_cap; @@ -47,15 +46,12 @@ struct msm_dp_panel { int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel); int msm_dp_panel_deinit(struct msm_dp_panel *msm_dp_panel); int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en); -int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, - struct drm_connector *connector); -void msm_dp_panel_unplugged(struct msm_dp_panel *msm_dp_panel, - struct drm_connector *connector); +int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel, + struct drm_connector *connector); u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, u32 mode_max_bpp, u32 mode_pclk_khz); -int msm_dp_panel_get_modes(struct msm_dp_panel *msm_dp_panel, - struct drm_connector *connector); -void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel); +void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel, + const struct drm_edid *drm_edid); void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable); void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel); @@ -94,5 +90,4 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux struct msm_dp_link *link, void __iomem *link_base, void __iomem *p0_base); -void msm_dp_panel_put(struct msm_dp_panel *msm_dp_panel); #endif /* _DP_PANEL_H_ */ From cd07d635884018b25573617fdd20924b0bf0af28 Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Tue, 28 Jul 2026 18:21:31 +0800 Subject: [PATCH 057/121] drm/msm/dp: drop deprecated .mode_set() and use .atomic_pre_enable The bridge .mode_set() callback is deprecated. Remove it and move the mode setup logic to .atomic_pre_enable(), where the adjusted_mode is available from the atomic CRTC state. .atomic_pre_enable() is used rather than .atomic_enable() because the DPU encoder's .atomic_enable() reads the output mode's YUV420 / wide bus state through the msm_display callbacks, and it runs after all bridges' .atomic_pre_enable() but before their .atomic_enable(). Programming the mode from the DP bridge's .atomic_enable() would leave the encoder reading the previously committed mode's state. Drop msm_dp_mode from msm_dp_display_private and store the mode directly in the panel, as it was only used as a temporary cache. Signed-off-by: Abhinav Kumar Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Assisted-by: Claude:claude-opus-4-8 [DB: moved to atomic_pre_enable] Patchwork: https://patchwork.freedesktop.org/patch/742729/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-2-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_display.c | 103 +++++++++++++--------------- drivers/gpu/drm/msm/dp/dp_drm.c | 4 +- drivers/gpu/drm/msm/dp/dp_drm.h | 5 +- 3 files changed, 51 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index ba3804a12a57..3ff7360a6833 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -63,7 +63,6 @@ struct msm_dp_display_private { struct msm_dp_panel *panel; struct msm_dp_ctrl *ctrl; - struct msm_dp_display_mode msm_dp_mode; struct msm_dp msm_dp_display; /* wait for audio signaling */ @@ -597,16 +596,33 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp) } static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display, - struct msm_dp_display_mode *mode) + const struct drm_display_mode *adjusted_mode, + struct msm_dp_panel *msm_dp_panel) { struct msm_dp_display_private *dp; + u32 bpp; dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); - drm_mode_copy(&dp->panel->msm_dp_mode.drm_mode, &mode->drm_mode); - dp->panel->msm_dp_mode.bpp = mode->bpp; - dp->panel->msm_dp_mode.out_fmt_is_yuv_420 = mode->out_fmt_is_yuv_420; - msm_dp_panel_init_panel_info(dp->panel); + drm_mode_copy(&msm_dp_panel->msm_dp_mode.drm_mode, adjusted_mode); + if (msm_dp_display_check_video_test(msm_dp_display)) + bpp = msm_dp_display_get_test_bpp(msm_dp_display); + else + bpp = msm_dp_panel->connector->display_info.bpc * 3; + + msm_dp_panel->msm_dp_mode.bpp = bpp ? bpp : 24; /* Default bpp */ + msm_dp_panel->msm_dp_mode.v_active_low = + !!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC); + msm_dp_panel->msm_dp_mode.h_active_low = + !!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC); + msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 = + drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) && + msm_dp_panel->vsc_sdp_supported; + msm_dp_panel_init_panel_info(msm_dp_panel); + + /* populate wide_bus_support to different layers */ + dp->ctrl->wide_bus_en = + msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 ? false : dp->wide_bus_supported; return 0; } @@ -1305,7 +1321,7 @@ bool msm_dp_wide_bus_available(const struct msm_dp *msm_dp_display) dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); - if (dp->msm_dp_mode.out_fmt_is_yuv_420) + if (dp->panel->msm_dp_mode.out_fmt_is_yuv_420) return false; return dp->wide_bus_supported; @@ -1356,6 +1372,30 @@ int msm_dp_modeset_init(struct msm_dp *msm_dp_display, struct drm_device *dev, return 0; } +void msm_dp_bridge_atomic_pre_enable(struct drm_bridge *drm_bridge, + struct drm_atomic_commit *state) +{ + struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge); + struct msm_dp *dp = msm_dp_bridge->msm_dp_display; + struct msm_dp_display_private *msm_dp_display; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + + msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); + + crtc = drm_atomic_get_new_crtc_for_encoder(state, drm_bridge->encoder); + if (!crtc) + return; + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + /* + * The DPU encoder's .atomic_enable() reads the mode's YUV420 / wide bus + * state and runs before the bridge's .atomic_enable(), so the mode must + * be programmed here, in .atomic_pre_enable(). + */ + msm_dp_display_set_mode(dp, &crtc_state->adjusted_mode, msm_dp_display->panel); +} + void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, struct drm_atomic_commit *state) { @@ -1366,10 +1406,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, bool force_link_train = false; msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); - if (!msm_dp_display->msm_dp_mode.drm_mode.clock) { - DRM_ERROR("invalid params\n"); - return; - } if (dp->is_edp) msm_dp_hpd_plug_handle(msm_dp_display); @@ -1382,12 +1418,6 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, if (msm_dp_display->link->sink_count == 0) return; - rc = msm_dp_display_set_mode(dp, &msm_dp_display->msm_dp_mode); - if (rc) { - DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc); - return; - } - if (!dp->power_on) { msm_dp_display_host_phy_init(msm_dp_display); force_link_train = true; @@ -1442,45 +1472,6 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, pm_runtime_put_sync(&dp->pdev->dev); } -void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adjusted_mode) -{ - struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge); - struct msm_dp *dp = msm_dp_bridge->msm_dp_display; - struct msm_dp_display_private *msm_dp_display; - struct msm_dp_panel *msm_dp_panel; - - msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); - msm_dp_panel = msm_dp_display->panel; - - memset(&msm_dp_display->msm_dp_mode, 0x0, sizeof(struct msm_dp_display_mode)); - - if (msm_dp_display_check_video_test(dp)) - msm_dp_display->msm_dp_mode.bpp = msm_dp_display_get_test_bpp(dp); - else /* Default num_components per pixel = 3 */ - msm_dp_display->msm_dp_mode.bpp = dp->connector->display_info.bpc * 3; - - if (!msm_dp_display->msm_dp_mode.bpp) - msm_dp_display->msm_dp_mode.bpp = 24; /* Default bpp */ - - drm_mode_copy(&msm_dp_display->msm_dp_mode.drm_mode, adjusted_mode); - - msm_dp_display->msm_dp_mode.v_active_low = - !!(msm_dp_display->msm_dp_mode.drm_mode.flags & DRM_MODE_FLAG_NVSYNC); - - msm_dp_display->msm_dp_mode.h_active_low = - !!(msm_dp_display->msm_dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC); - - msm_dp_display->msm_dp_mode.out_fmt_is_yuv_420 = - drm_mode_is_420_only(&dp->connector->display_info, adjusted_mode) && - msm_dp_panel->vsc_sdp_supported; - - /* populate wide_bus_support to different layers */ - msm_dp_display->ctrl->wide_bus_en = - msm_dp_display->msm_dp_mode.out_fmt_is_yuv_420 ? false : msm_dp_display->wide_bus_supported; -} - void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge) { struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(bridge); diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index 06881bfd6181..794a0df60414 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -53,10 +53,10 @@ static const struct drm_bridge_funcs msm_dp_bridge_ops = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_create_state = drm_atomic_helper_bridge_create_state, + .atomic_pre_enable = msm_dp_bridge_atomic_pre_enable, .atomic_enable = msm_dp_bridge_atomic_enable, .atomic_disable = msm_dp_bridge_atomic_disable, .atomic_post_disable = msm_dp_bridge_atomic_post_disable, - .mode_set = msm_dp_bridge_mode_set, .mode_valid = msm_dp_bridge_mode_valid, .get_modes = msm_dp_bridge_get_modes, .detect = msm_dp_bridge_detect, @@ -230,10 +230,10 @@ static void msm_edp_bridge_debugfs_init(struct drm_bridge *bridge, struct dentry } static const struct drm_bridge_funcs msm_edp_bridge_ops = { + .atomic_pre_enable = msm_dp_bridge_atomic_pre_enable, .atomic_enable = msm_edp_bridge_atomic_enable, .atomic_disable = msm_edp_bridge_atomic_disable, .atomic_post_disable = msm_edp_bridge_atomic_post_disable, - .mode_set = msm_dp_bridge_mode_set, .mode_valid = msm_edp_bridge_mode_valid, .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h index 041aa026ae2e..4f733d8118b1 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.h +++ b/drivers/gpu/drm/msm/dp/dp_drm.h @@ -27,6 +27,8 @@ int msm_dp_bridge_init(struct msm_dp *msm_dp_display, struct drm_device *dev, enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector); +void msm_dp_bridge_atomic_pre_enable(struct drm_bridge *drm_bridge, + struct drm_atomic_commit *state); void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, struct drm_atomic_commit *state); void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge, @@ -36,9 +38,6 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, const struct drm_display_mode *mode); -void msm_dp_bridge_mode_set(struct drm_bridge *drm_bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adjusted_mode); void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge); void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge); void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge, From 1c96c88ec172d3d7e0b46c00f0b3dc0361436fdb Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Tue, 28 Jul 2026 18:21:32 +0800 Subject: [PATCH 058/121] drm/msm/dp: move mode setup into msm_dp_panel_init_panel_info() The display layer directly assigns msm_dp_panel mode fields (bpp, sync polarity, yuv420 flag) instead of letting the panel manage its own state. Pass adjusted_mode and bpp as parameters to msm_dp_panel_init_panel_info() and move the assignments inside it. Suggested-by: Dmitry Baryshkov Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742731/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-3-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_display.c | 11 +---------- drivers/gpu/drm/msm/dp/dp_panel.c | 18 +++++++++++++++--- drivers/gpu/drm/msm/dp/dp_panel.h | 4 +++- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 3ff7360a6833..cc94ce9635b7 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -604,21 +604,12 @@ static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display, dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); - drm_mode_copy(&msm_dp_panel->msm_dp_mode.drm_mode, adjusted_mode); if (msm_dp_display_check_video_test(msm_dp_display)) bpp = msm_dp_display_get_test_bpp(msm_dp_display); else bpp = msm_dp_panel->connector->display_info.bpc * 3; - msm_dp_panel->msm_dp_mode.bpp = bpp ? bpp : 24; /* Default bpp */ - msm_dp_panel->msm_dp_mode.v_active_low = - !!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC); - msm_dp_panel->msm_dp_mode.h_active_low = - !!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC); - msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 = - drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) && - msm_dp_panel->vsc_sdp_supported; - msm_dp_panel_init_panel_info(msm_dp_panel); + msm_dp_panel_init_panel_info(msm_dp_panel, adjusted_mode, bpp ? bpp : 24); /* populate wide_bus_support to different layers */ dp->ctrl->wide_bus_en = diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index bde4a772d22c..e76dad0f6663 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -647,15 +647,27 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en) return 0; } -int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel) +int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel, + const struct drm_display_mode *adjusted_mode, + u32 bpp) { struct drm_display_mode *drm_mode; struct msm_dp_panel_private *panel; - drm_mode = &msm_dp_panel->msm_dp_mode.drm_mode; - panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); + drm_mode_copy(&msm_dp_panel->msm_dp_mode.drm_mode, adjusted_mode); + msm_dp_panel->msm_dp_mode.bpp = bpp; + msm_dp_panel->msm_dp_mode.v_active_low = + !!(adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC); + msm_dp_panel->msm_dp_mode.h_active_low = + !!(adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC); + msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420 = + drm_mode_is_420_only(&msm_dp_panel->connector->display_info, adjusted_mode) && + msm_dp_panel->vsc_sdp_supported; + + drm_mode = &msm_dp_panel->msm_dp_mode.drm_mode; + /* * print resolution info as this is a result * of user initiated action of cable connection diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h index 53b7b4463551..4519ac374220 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.h +++ b/drivers/gpu/drm/msm/dp/dp_panel.h @@ -43,7 +43,9 @@ struct msm_dp_panel { u32 max_bw_code; }; -int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel); +int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel, + const struct drm_display_mode *adjusted_mode, + u32 bpp); int msm_dp_panel_deinit(struct msm_dp_panel *msm_dp_panel); int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en); int msm_dp_panel_read_link_caps(struct msm_dp_panel *msm_dp_panel, From 7de83c21291ff861a66c602ddcaf45ff8f43f547 Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Tue, 28 Jul 2026 18:21:33 +0800 Subject: [PATCH 059/121] drm/msm/dp: split msm_dp_ctrl_config_ctrl() into link parts and stream parts The DP_CONFIGURATION_CTRL register contains both link-level and stream-specific fields. Currently, msm_dp_ctrl_config_ctrl() configures all of them together. Separate the configuration into link parts and stream parts to support MST. Clear the stream-specific fields before OR-ing new values in the stream path to avoid bit accumulation across repeated calls. Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742733/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-4-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 51 ++++++++++++++++++++++---------- drivers/gpu/drm/msm/dp/dp_reg.h | 2 ++ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 86ef8c89ad44..7c0649d5318f 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -388,26 +388,48 @@ void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl) drm_dbg_dp(ctrl->drm_dev, "mainlink off\n"); } -static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl) +static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *msm_dp_panel) { u32 config = 0, tbd; + + /* + * RMW: called from atomic_enable(), serialized by the DRM atomic framework. + * Clear stream-specific fields before OR-ing to avoid bit accumulation. + */ + config = msm_dp_read_link(ctrl, REG_DP_CONFIGURATION_CTRL); + config &= ~(DP_CONFIGURATION_CTRL_RGB_YUV_MASK | + DP_CONFIGURATION_CTRL_BPC_MASK | + DP_CONFIGURATION_CTRL_SEND_VSC); + + if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420) + config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */ + + tbd = msm_dp_link_get_test_bits_depth(ctrl->link, + msm_dp_panel->msm_dp_mode.bpp); + + config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT; + + if (msm_dp_panel->psr_cap.version) + config |= DP_CONFIGURATION_CTRL_SEND_VSC; + + drm_dbg_dp(ctrl->drm_dev, "stream DP_CONFIGURATION_CTRL=0x%x\n", config); + + msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config); +} + +static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl) +{ + u32 config = 0; const u8 *dpcd = ctrl->panel->dpcd; /* Default-> LSCLK DIV: 1/4 LCLK */ config |= (2 << DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT); - if (ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420) - config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */ - /* Scrambler reset enable */ if (drm_dp_alternate_scrambler_reset_cap(dpcd)) config |= DP_CONFIGURATION_CTRL_ASSR; - tbd = msm_dp_link_get_test_bits_depth(ctrl->link, - ctrl->panel->msm_dp_mode.bpp); - - config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT; - /* Num of Lanes */ config |= ((ctrl->link->link_params.num_lanes - 1) << DP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT); @@ -421,10 +443,7 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl) config |= DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN; config |= DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK; - if (ctrl->panel->psr_cap.version) - config |= DP_CONFIGURATION_CTRL_SEND_VSC; - - drm_dbg_dp(ctrl->drm_dev, "DP_CONFIGURATION_CTRL=0x%x\n", config); + drm_dbg_dp(ctrl->drm_dev, "link DP_CONFIGURATION_CTRL=0x%x\n", config); msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config); } @@ -450,7 +469,8 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl msm_dp_ctrl_lane_mapping(ctrl); msm_dp_setup_peripheral_flush(ctrl); - msm_dp_ctrl_config_ctrl(ctrl); + msm_dp_ctrl_config_ctrl_link(ctrl); + msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel); test_bits_depth = msm_dp_link_get_test_bits_depth(ctrl->link, ctrl->panel->msm_dp_mode.bpp); colorimetry_cfg = msm_dp_link_get_colorimetry_config(ctrl->link); @@ -1628,7 +1648,8 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl, u8 assr; struct msm_dp_link_info link_info = {0}; - msm_dp_ctrl_config_ctrl(ctrl); + msm_dp_ctrl_config_ctrl_link(ctrl); + msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel); link_info.num_lanes = ctrl->link->link_params.num_lanes; link_info.rate = ctrl->link->link_params.rate; diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h index 3689642b7fc0..dda4b642c81b 100644 --- a/drivers/gpu/drm/msm/dp/dp_reg.h +++ b/drivers/gpu/drm/msm/dp/dp_reg.h @@ -149,8 +149,10 @@ #define DP_CONFIGURATION_CTRL_ENHANCED_FRAMING (0x00000040) #define DP_CONFIGURATION_CTRL_SEND_VSC (0x00000080) #define DP_CONFIGURATION_CTRL_BPC (0x00000100) +#define DP_CONFIGURATION_CTRL_BPC_MASK GENMASK(9, 8) #define DP_CONFIGURATION_CTRL_ASSR (0x00000400) #define DP_CONFIGURATION_CTRL_RGB_YUV (0x00000800) +#define DP_CONFIGURATION_CTRL_RGB_YUV_MASK GENMASK(12, 11) #define DP_CONFIGURATION_CTRL_LSCLK_DIV (0x00002000) #define DP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT (0x04) #define DP_CONFIGURATION_CTRL_BPC_SHIFT (0x08) From 3a1edd5ac0226f06aef31b492c56b09f876acf5b Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Tue, 28 Jul 2026 18:21:34 +0800 Subject: [PATCH 060/121] drm/msm/dp: extract MISC1_MISC0 configuration into a separate function Refactor the MISC1_MISC0 register configuration into a standalone helper function to support MST. Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742735/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-5-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 7c0649d5318f..c557e0f01dc6 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -462,17 +462,13 @@ static void msm_dp_ctrl_lane_mapping(struct msm_dp_ctrl_private *ctrl) ln_mapping); } -static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl) +static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *msm_dp_panel) { u32 colorimetry_cfg, test_bits_depth, misc_val; - msm_dp_ctrl_lane_mapping(ctrl); - msm_dp_setup_peripheral_flush(ctrl); - - msm_dp_ctrl_config_ctrl_link(ctrl); - msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel); - - test_bits_depth = msm_dp_link_get_test_bits_depth(ctrl->link, ctrl->panel->msm_dp_mode.bpp); + test_bits_depth = msm_dp_link_get_test_bits_depth(ctrl->link, + msm_dp_panel->msm_dp_mode.bpp); colorimetry_cfg = msm_dp_link_get_colorimetry_config(ctrl->link); misc_val = msm_dp_read_link(ctrl, REG_DP_MISC1_MISC0); @@ -486,6 +482,17 @@ static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl drm_dbg_dp(ctrl->drm_dev, "misc settings = 0x%x\n", misc_val); msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0, misc_val); +} + +static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl) +{ + msm_dp_ctrl_lane_mapping(ctrl); + msm_dp_setup_peripheral_flush(ctrl); + + msm_dp_ctrl_config_ctrl_link(ctrl); + msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel); + + msm_dp_ctrl_config_misc1_misc0(ctrl, ctrl->panel); msm_dp_panel_timing_cfg(ctrl->panel, ctrl->msm_dp_ctrl.wide_bus_en); } From 0a19867dfb8dc4491737e64987566fc2c5962778 Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Tue, 28 Jul 2026 18:21:35 +0800 Subject: [PATCH 061/121] drm/msm/dp: split link setup from source params msm_dp_ctrl_configure_source_params() should only handle stream-related configuration. Move the link setup out of it so MST can program link and stream settings separately. Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742737/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-6-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index c557e0f01dc6..f343bd6c2b81 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -486,10 +486,6 @@ static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl, static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl) { - msm_dp_ctrl_lane_mapping(ctrl); - msm_dp_setup_peripheral_flush(ctrl); - - msm_dp_ctrl_config_ctrl_link(ctrl); msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel); msm_dp_ctrl_config_misc1_misc0(ctrl, ctrl->panel); @@ -2556,6 +2552,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train */ reinit_completion(&ctrl->video_comp); + msm_dp_ctrl_lane_mapping(ctrl); + msm_dp_setup_peripheral_flush(ctrl); + msm_dp_ctrl_config_ctrl_link(ctrl); + msm_dp_ctrl_configure_source_params(ctrl); msm_dp_ctrl_config_msa(ctrl, From 38e4b121497923b0f1a9636bf947af730f38bb3b Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Tue, 28 Jul 2026 18:21:36 +0800 Subject: [PATCH 062/121] drm/msm/dp: move the pixel clock control to its own API Enable/Disable of DP pixel clock happens in multiple code paths leading to code duplication. Move it into individual helpers so that the helpers can be called wherever necessary. Signed-off-by: Abhinav Kumar Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742738/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-7-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 80 +++++++++++++++++--------------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index f343bd6c2b81..068d4f8b673f 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -2176,6 +2176,41 @@ static bool msm_dp_ctrl_send_phy_test_pattern(struct msm_dp_ctrl_private *ctrl) return success; } +static int msm_dp_ctrl_on_pixel_clk(struct msm_dp_ctrl_private *ctrl, unsigned long pixel_rate) +{ + int ret; + + ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000); + if (ret) { + DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret); + return ret; + } + + if (WARN_ON_ONCE(ctrl->stream_clks_on)) + return 0; + + ret = clk_prepare_enable(ctrl->pixel_clk); + if (ret) { + DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret); + return ret; + } + ctrl->stream_clks_on = true; + + return ret; +} + +static void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl) +{ + struct msm_dp_ctrl_private *ctrl; + + ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); + + if (ctrl->stream_clks_on) { + clk_disable_unprepare(ctrl->pixel_clk); + ctrl->stream_clks_on = false; + } +} + static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl) { int ret; @@ -2201,22 +2236,9 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl } pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock; - ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000); - if (ret) { - DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret); + ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate); + if (ret) return ret; - } - - if (ctrl->stream_clks_on) { - drm_dbg_dp(ctrl->drm_dev, "pixel clks already enabled\n"); - } else { - ret = clk_prepare_enable(ctrl->pixel_clk); - if (ret) { - DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret); - return ret; - } - ctrl->stream_clks_on = true; - } msm_dp_ctrl_send_phy_test_pattern(ctrl); @@ -2519,26 +2541,13 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl); if (ret) { DRM_ERROR("Failed to start link clocks. ret=%d\n", ret); - goto end; + return ret; } } - ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000); - if (ret) { - DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret); - goto end; - } - - if (ctrl->stream_clks_on) { - drm_dbg_dp(ctrl->drm_dev, "pixel clks already enabled\n"); - } else { - ret = clk_prepare_enable(ctrl->pixel_clk); - if (ret) { - DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret); - goto end; - } - ctrl->stream_clks_on = true; - } + ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate); + if (ret) + return ret; if (force_link_train || !msm_dp_ctrl_channel_eq_ok(ctrl)) msm_dp_ctrl_link_retrain(ctrl); @@ -2577,7 +2586,6 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train drm_dbg_dp(ctrl->drm_dev, "mainlink %s\n", mainlink_ready ? "READY" : "NOT READY"); -end: return ret; } @@ -2625,11 +2633,7 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl) msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl); - if (ctrl->stream_clks_on) { - clk_disable_unprepare(ctrl->pixel_clk); - ctrl->stream_clks_on = false; - } - + msm_dp_ctrl_off_pixel_clk(msm_dp_ctrl); dev_pm_opp_set_rate(ctrl->dev, 0); msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); From ca16db3d18cfe2d3e45a2cb3f3f67f2b19685a02 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 28 Jul 2026 18:21:37 +0800 Subject: [PATCH 063/121] drm/msm/dp: break up dp_display_enable into two parts dp_display_enable() currently re-trains the link if needed and then enables the pixel clock, programs the controller to start sending the pixel stream. Split these two parts into prepare/enable APIs, to support MST bridges_enable insert the MST payloads funcs between enable stream_clks and program register. Signed-off-by: Abhinav Kumar Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742740/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-8-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 48 ++++++++----- drivers/gpu/drm/msm/dp/dp_ctrl.h | 3 +- drivers/gpu/drm/msm/dp/dp_display.c | 105 ++++++++++++++++++---------- 3 files changed, 103 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 068d4f8b673f..43094dda3434 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -2511,27 +2511,19 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl, msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID, nvid); } -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train) +int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train) { int ret = 0; - bool mainlink_ready = false; struct msm_dp_ctrl_private *ctrl; - unsigned long pixel_rate; - unsigned long pixel_rate_orig; if (!msm_dp_ctrl) return -EINVAL; ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); - pixel_rate = pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock; - - if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420) - pixel_rate >>= 1; - - drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n", - ctrl->link->link_params.rate, - ctrl->link->link_params.num_lanes, pixel_rate); + drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d\n", + ctrl->link->link_params.rate, + ctrl->link->link_params.num_lanes); drm_dbg_dp(ctrl->drm_dev, "core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n", @@ -2545,16 +2537,40 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train } } - ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate); - if (ret) - return ret; - if (force_link_train || !msm_dp_ctrl_channel_eq_ok(ctrl)) msm_dp_ctrl_link_retrain(ctrl); /* stop txing train pattern to end link training */ msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); + return ret; +} + +int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl) +{ + int ret = 0; + bool mainlink_ready = false; + struct msm_dp_ctrl_private *ctrl; + unsigned long pixel_rate; + unsigned long pixel_rate_orig; + + if (!msm_dp_ctrl) + return -EINVAL; + + ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); + + pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock; + pixel_rate = pixel_rate_orig; + + if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420) + pixel_rate >>= 1; + + drm_dbg_dp(ctrl->drm_dev, "pixel_rate=%lu\n", pixel_rate); + + ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate); + if (ret) + return ret; + /* * Set up transfer unit values and set controller state to send * video. diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h index f68bee62713f..1497f1a8fc2f 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h @@ -17,7 +17,8 @@ struct msm_dp_ctrl { struct phy; int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl); -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train); +int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl); +int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train); void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl); diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index cc94ce9635b7..e2b4f0276737 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -617,7 +617,42 @@ static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display, return 0; } -static int msm_dp_display_enable(struct msm_dp_display_private *dp, bool force_link_train) +static int msm_dp_display_prepare_link(struct msm_dp_display_private *dp) +{ + struct msm_dp *msm_dp_display = &dp->msm_dp_display; + int rc = 0; + bool force_link_train = false; + + drm_dbg_dp(dp->drm_dev, "sink_count=%d\n", dp->link->sink_count); + + if (msm_dp_display->is_edp) + msm_dp_hpd_plug_handle(dp); + + rc = pm_runtime_resume_and_get(&msm_dp_display->pdev->dev); + if (rc) { + DRM_ERROR("failed to pm_runtime_resume\n"); + return rc; + } + + if (dp->link->sink_count == 0) + return -ENOTCONN; + + if (!msm_dp_display->power_on) { + msm_dp_display_host_phy_init(dp); + force_link_train = true; + } + + rc = msm_dp_ctrl_on_link(dp->ctrl); + if (rc) { + DRM_ERROR("Failed link training (rc=%d)\n", rc); + // TODO: schedule drm_connector_set_link_status_property() + return rc; + } + + return msm_dp_ctrl_prepare_stream_on(dp->ctrl, force_link_train); +} + +static int msm_dp_display_enable(struct msm_dp_display_private *dp) { int rc = 0; struct msm_dp *msm_dp_display = &dp->msm_dp_display; @@ -628,7 +663,7 @@ static int msm_dp_display_enable(struct msm_dp_display_private *dp, bool force_l return 0; } - rc = msm_dp_ctrl_on_stream(dp->ctrl, force_link_train); + rc = msm_dp_ctrl_on_stream(dp->ctrl); if (!rc) msm_dp_display->power_on = true; @@ -658,13 +693,10 @@ static int msm_dp_display_post_enable(struct msm_dp *msm_dp_display) return 0; } -static int msm_dp_display_disable(struct msm_dp_display_private *dp) +static void msm_dp_display_audio_notify_disable(struct msm_dp_display_private *dp) { struct msm_dp *msm_dp_display = &dp->msm_dp_display; - if (!msm_dp_display->power_on) - return 0; - /* wait only if audio was enabled */ if (msm_dp_display->audio_enabled) { /* signal the disconnect event */ @@ -675,6 +707,14 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp) } msm_dp_display->audio_enabled = false; +} + +static int msm_dp_display_disable(struct msm_dp_display_private *dp) +{ + struct msm_dp *msm_dp_display = &dp->msm_dp_display; + + if (!msm_dp_display->power_on) + return 0; if (dp->link->sink_count == 0) { /* @@ -1391,45 +1431,29 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, struct drm_atomic_commit *state) { struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge); - struct msm_dp *dp = msm_dp_bridge->msm_dp_display; + struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display; int rc = 0; - struct msm_dp_display_private *msm_dp_display; - bool force_link_train = false; + struct msm_dp_display_private *dp; - msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); - if (dp->is_edp) - msm_dp_hpd_plug_handle(msm_dp_display); - - if (pm_runtime_resume_and_get(&dp->pdev->dev)) { - DRM_ERROR("failed to pm_runtime_resume\n"); - return; - } - - if (msm_dp_display->link->sink_count == 0) - return; - - if (!dp->power_on) { - msm_dp_display_host_phy_init(msm_dp_display); - force_link_train = true; - } - - rc = msm_dp_ctrl_on_link(msm_dp_display->ctrl); + rc = msm_dp_display_prepare_link(dp); if (rc) { - DRM_ERROR("Failed link training (rc=%d)\n", rc); - // TODO: schedule drm_connector_set_link_status_property() + DRM_ERROR("DP display prepare failed, rc=%d\n", rc); return; } - msm_dp_display_enable(msm_dp_display, force_link_train); + rc = msm_dp_display_enable(dp); + if (rc) + DRM_ERROR("DP display enable failed, rc=%d\n", rc); - rc = msm_dp_display_post_enable(dp); + rc = msm_dp_display_post_enable(msm_dp_display); if (rc) { DRM_ERROR("DP display post enable failed, rc=%d\n", rc); - msm_dp_display_disable(msm_dp_display); + msm_dp_display_disable(dp); } - drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type); + drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type); } void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge, @@ -1444,6 +1468,15 @@ void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge, msm_dp_ctrl_push_idle(msm_dp_display->ctrl); } +static void msm_dp_display_unprepare(struct msm_dp_display_private *dp) +{ + struct msm_dp *msm_dp_display = &dp->msm_dp_display; + + pm_runtime_put_sync(&msm_dp_display->pdev->dev); + + drm_dbg_dp(dp->drm_dev, "type=%d Done\n", msm_dp_display->connector_type); +} + void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, struct drm_atomic_commit *state) { @@ -1456,11 +1489,11 @@ void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, if (dp->is_edp) msm_dp_hpd_unplug_handle(msm_dp_display); + msm_dp_display_audio_notify_disable(msm_dp_display); + msm_dp_display_disable(msm_dp_display); - drm_dbg_dp(dp->drm_dev, "type=%d Done\n", dp->connector_type); - - pm_runtime_put_sync(&dp->pdev->dev); + msm_dp_display_unprepare(msm_dp_display); } void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge) From 477d23a69569720be64bd79cafae01b3b569db2e Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 28 Jul 2026 18:21:38 +0800 Subject: [PATCH 064/121] drm/msm/dp: re-arrange dp_display_disable() into functional parts dp_display_disable() handles special case of when monitor is disconnected from the dongle while the dongle stays connected thereby needing a separate function dp_ctrl_off_link_stream() for this. However with a slight rework this can still be handled by keeping common paths same for regular and special case. Signed-off-by: Abhinav Kumar Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742742/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-9-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 19 +------------------ drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +- drivers/gpu/drm/msm/dp/dp_display.c | 10 +++++++++- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 43094dda3434..272ac3b25b13 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -2605,7 +2605,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl) return ret; } -void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl) +void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl) { struct msm_dp_ctrl_private *ctrl; struct phy *phy; @@ -2613,23 +2613,6 @@ void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl) ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); phy = ctrl->phy; - msm_dp_panel_disable_vsc_sdp(ctrl->panel); - - /* set dongle to D3 (power off) mode */ - msm_dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true); - - msm_dp_ctrl_mainlink_disable(ctrl); - - if (ctrl->stream_clks_on) { - clk_disable_unprepare(ctrl->pixel_clk); - ctrl->stream_clks_on = false; - } - - dev_pm_opp_set_rate(ctrl->dev, 0); - msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); - - phy_power_off(phy); - /* aux channel down, reinit phy */ phy_exit(phy); phy_init(phy); diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h index 1497f1a8fc2f..5d615f50d13b 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h @@ -19,7 +19,6 @@ struct phy; int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl); int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl); int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train); -void msm_dp_ctrl_off_link_stream(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl); irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl); @@ -46,4 +45,5 @@ void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_enable_irq(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_disable_irq(struct msm_dp_ctrl *msm_dp_ctrl); +void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl); #endif /* _DP_CTRL_H_ */ diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index e2b4f0276737..f8f8f3889ea5 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -716,12 +716,20 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp) if (!msm_dp_display->power_on) return 0; + msm_dp_panel_disable_vsc_sdp(dp->panel); + + /* dongle is still connected but sinks are disconnected */ if (dp->link->sink_count == 0) { /* * irq_hpd with sink_count = 0 * hdmi unplugged out of dongle */ - msm_dp_ctrl_off_link_stream(dp->ctrl); + + /* set dongle to D3 (power off) mode */ + msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true); + msm_dp_ctrl_off(dp->ctrl); + /* re-init the PHY so that we can listen to Dongle disconnect */ + msm_dp_ctrl_reinit_phy(dp->ctrl); } else { /* * unplugged interrupt From 2f0e61b6c592ded91a0a3544872c98b69bf33fba Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 28 Jul 2026 18:21:39 +0800 Subject: [PATCH 065/121] drm/msm/dp: allow dp_ctrl stream APIs to use any panel passed to it With MST, multiple sinks share a single DP controller, so a cached panel in msm_dp_ctrl_private can no longer represent the per-stream sink. Drop the cache and pass panel explicitly to all stream-related dp_ctrl APIs. Signed-off-by: Abhinav Kumar Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742745/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-10-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 201 ++++++++++++++++------------ drivers/gpu/drm/msm/dp/dp_ctrl.h | 28 ++-- drivers/gpu/drm/msm/dp/dp_display.c | 26 ++-- 3 files changed, 143 insertions(+), 112 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 272ac3b25b13..9bed5a2fd2e7 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -114,7 +114,6 @@ struct msm_dp_ctrl_private { struct drm_device *drm_dev; struct device *dev; struct drm_dp_aux *aux; - struct msm_dp_panel *panel; struct msm_dp_link *link; void __iomem *ahb_base; void __iomem *link_base; @@ -202,7 +201,8 @@ static int msm_dp_aux_link_configure(struct drm_dp_aux *aux, /* * NOTE: resetting DP controller will also clear any pending HPD related interrupts */ -void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl) +void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel) { struct msm_dp_ctrl_private *ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); @@ -217,10 +217,9 @@ void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl) sw_reset &= ~DP_SW_RESET; msm_dp_write_ahb(ctrl, REG_DP_SW_RESET, sw_reset); - if (!ctrl->hw_revision) { + if (!ctrl->hw_revision) ctrl->hw_revision = msm_dp_read_ahb(ctrl, REG_DP_HW_VERSION); - ctrl->panel->hw_revision = ctrl->hw_revision; - } + panel->hw_revision = ctrl->hw_revision; } static u32 msm_dp_ctrl_get_aux_interrupt(struct msm_dp_ctrl_private *ctrl) @@ -418,10 +417,11 @@ static void msm_dp_ctrl_config_ctrl_streams(struct msm_dp_ctrl_private *ctrl, msm_dp_write_link(ctrl, REG_DP_CONFIGURATION_CTRL, config); } -static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl) +static void msm_dp_ctrl_config_ctrl_link(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { u32 config = 0; - const u8 *dpcd = ctrl->panel->dpcd; + const u8 *dpcd = panel->dpcd; /* Default-> LSCLK DIV: 1/4 LCLK */ config |= (2 << DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT); @@ -484,13 +484,14 @@ static void msm_dp_ctrl_config_misc1_misc0(struct msm_dp_ctrl_private *ctrl, msm_dp_write_link(ctrl, REG_DP_MISC1_MISC0, misc_val); } -static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl) +static void msm_dp_ctrl_configure_source_params(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { - msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel); + msm_dp_ctrl_config_ctrl_streams(ctrl, panel); - msm_dp_ctrl_config_misc1_misc0(ctrl, ctrl->panel); + msm_dp_ctrl_config_misc1_misc0(ctrl, panel); - msm_dp_panel_timing_cfg(ctrl->panel, ctrl->msm_dp_ctrl.wide_bus_en); + msm_dp_panel_timing_cfg(panel, ctrl->msm_dp_ctrl.wide_bus_en); } /* @@ -1260,20 +1261,21 @@ static void _dp_ctrl_calc_tu(struct msm_dp_ctrl_private *ctrl, } static void msm_dp_ctrl_calc_tu_parameters(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel, struct msm_dp_vc_tu_mapping_table *tu_table) { struct msm_dp_tu_calc_input in; - struct drm_display_mode *drm_mode; + const struct drm_display_mode *drm_mode; - drm_mode = &ctrl->panel->msm_dp_mode.drm_mode; + drm_mode = &panel->msm_dp_mode.drm_mode; in.lclk = ctrl->link->link_params.rate / 1000; in.pclk_khz = drm_mode->clock; in.hactive = drm_mode->hdisplay; in.hporch = drm_mode->htotal - drm_mode->hdisplay; in.nlanes = ctrl->link->link_params.num_lanes; - in.bpp = ctrl->panel->msm_dp_mode.bpp; - in.pixel_enc = ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420 ? 420 : 444; + in.bpp = panel->msm_dp_mode.bpp; + in.pixel_enc = panel->msm_dp_mode.out_fmt_is_yuv_420 ? 420 : 444; in.dsc_en = 0; in.async_en = 0; in.fec_en = 0; @@ -1283,14 +1285,15 @@ static void msm_dp_ctrl_calc_tu_parameters(struct msm_dp_ctrl_private *ctrl, _dp_ctrl_calc_tu(ctrl, &in, tu_table); } -static void msm_dp_ctrl_setup_tr_unit(struct msm_dp_ctrl_private *ctrl) +static void msm_dp_ctrl_setup_tr_unit(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { u32 msm_dp_tu = 0x0; u32 valid_boundary = 0x0; u32 valid_boundary2 = 0x0; struct msm_dp_vc_tu_mapping_table tu_calc_table; - msm_dp_ctrl_calc_tu_parameters(ctrl, &tu_calc_table); + msm_dp_ctrl_calc_tu_parameters(ctrl, panel, &tu_calc_table); msm_dp_tu |= tu_calc_table.tu_size_minus1; valid_boundary |= tu_calc_table.valid_boundary_link; @@ -1442,6 +1445,7 @@ static int msm_dp_ctrl_set_pattern_state_bit(struct msm_dp_ctrl_private *ctrl, } static int msm_dp_ctrl_link_train_1(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel, int *training_step, enum drm_dp_phy dp_phy) { int delay_us; @@ -1450,7 +1454,7 @@ static int msm_dp_ctrl_link_train_1(struct msm_dp_ctrl_private *ctrl, int const maximum_retries = 4; delay_us = drm_dp_read_clock_recovery_delay(ctrl->aux, - ctrl->panel->dpcd, dp_phy, false); + panel->dpcd, dp_phy, false); msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, 0); @@ -1536,14 +1540,15 @@ static int msm_dp_ctrl_link_rate_down_shift(struct msm_dp_ctrl_private *ctrl) return ret; } -static int msm_dp_ctrl_link_lane_down_shift(struct msm_dp_ctrl_private *ctrl) +static int msm_dp_ctrl_link_lane_down_shift(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { if (ctrl->link->link_params.num_lanes == 1) return -1; ctrl->link->link_params.num_lanes /= 2; - ctrl->link->link_params.rate = ctrl->panel->link_info.rate; + ctrl->link->link_params.rate = panel->link_info.rate; ctrl->link->phy_params.p_level = 0; ctrl->link->phy_params.v_level = 0; @@ -1552,6 +1557,7 @@ static int msm_dp_ctrl_link_lane_down_shift(struct msm_dp_ctrl_private *ctrl) } static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel, enum drm_dp_phy dp_phy) { int delay_us; @@ -1559,11 +1565,12 @@ static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private *ctrl, msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_DISABLE, dp_phy); delay_us = drm_dp_read_channel_eq_delay(ctrl->aux, - ctrl->panel->dpcd, dp_phy, false); + panel->dpcd, dp_phy, false); fsleep(delay_us); } static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel, int *training_step, enum drm_dp_phy dp_phy) { int delay_us; @@ -1574,16 +1581,16 @@ static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl, u8 link_status[DP_LINK_STATUS_SIZE]; delay_us = drm_dp_read_channel_eq_delay(ctrl->aux, - ctrl->panel->dpcd, dp_phy, false); + panel->dpcd, dp_phy, false); msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, 0); *training_step = DP_TRAINING_2; - if (drm_dp_tps4_supported(ctrl->panel->dpcd)) { + if (drm_dp_tps4_supported(panel->dpcd)) { pattern = DP_TRAINING_PATTERN_4; state_ctrl_bit = 4; - } else if (drm_dp_tps3_supported(ctrl->panel->dpcd)) { + } else if (drm_dp_tps3_supported(panel->dpcd)) { pattern = DP_TRAINING_PATTERN_3; state_ctrl_bit = 3; } else { @@ -1620,18 +1627,19 @@ static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl, } static int msm_dp_ctrl_link_train_1_2(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel, int *training_step, enum drm_dp_phy dp_phy) { int ret; - ret = msm_dp_ctrl_link_train_1(ctrl, training_step, dp_phy); + ret = msm_dp_ctrl_link_train_1(ctrl, panel, training_step, dp_phy); if (ret) { DRM_ERROR("link training #1 on phy %d failed. ret=%d\n", dp_phy, ret); return ret; } drm_dbg_dp(ctrl->drm_dev, "link training #1 on phy %d successful\n", dp_phy); - ret = msm_dp_ctrl_link_train_2(ctrl, training_step, dp_phy); + ret = msm_dp_ctrl_link_train_2(ctrl, panel, training_step, dp_phy); if (ret) { DRM_ERROR("link training #2 on phy %d failed. ret=%d\n", dp_phy, ret); return ret; @@ -1642,17 +1650,18 @@ static int msm_dp_ctrl_link_train_1_2(struct msm_dp_ctrl_private *ctrl, } static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel, int *training_step) { int i; int ret = 0; - const u8 *dpcd = ctrl->panel->dpcd; + const u8 *dpcd = panel->dpcd; u8 encoding[] = { 0, DP_SET_ANSI_8B10B }; u8 assr; struct msm_dp_link_info link_info = {0}; - msm_dp_ctrl_config_ctrl_link(ctrl); - msm_dp_ctrl_config_ctrl_streams(ctrl, ctrl->panel); + msm_dp_ctrl_config_ctrl_link(ctrl, panel); + msm_dp_ctrl_config_ctrl_streams(ctrl, panel); link_info.num_lanes = ctrl->link->link_params.num_lanes; link_info.rate = ctrl->link->link_params.rate; @@ -1675,8 +1684,8 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl, for (i = ctrl->link->lttpr_count - 1; i >= 0; i--) { enum drm_dp_phy dp_phy = DP_PHY_LTTPR(i); - ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, dp_phy); - msm_dp_ctrl_clear_training_pattern(ctrl, dp_phy); + ret = msm_dp_ctrl_link_train_1_2(ctrl, panel, training_step, dp_phy); + msm_dp_ctrl_clear_training_pattern(ctrl, panel, dp_phy); if (ret) break; @@ -1687,7 +1696,7 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl, goto end; } - ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, DP_PHY_DPRX); + ret = msm_dp_ctrl_link_train_1_2(ctrl, panel, training_step, DP_PHY_DPRX); if (ret) { DRM_ERROR("link training on sink failed. ret=%d\n", ret); goto end; @@ -1700,6 +1709,7 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl, } static int msm_dp_ctrl_setup_main_link(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel, int *training_step) { int ret = 0; @@ -1715,7 +1725,7 @@ static int msm_dp_ctrl_setup_main_link(struct msm_dp_ctrl_private *ctrl, * a link training pattern, we have to first do soft reset. */ - ret = msm_dp_ctrl_link_train(ctrl, training_step); + ret = msm_dp_ctrl_link_train(ctrl, panel, training_step); return ret; } @@ -1814,11 +1824,12 @@ static void msm_dp_ctrl_link_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl) str_on_off(ctrl->core_clks_on)); } -static int msm_dp_ctrl_enable_mainlink_clocks(struct msm_dp_ctrl_private *ctrl) +static int msm_dp_ctrl_enable_mainlink_clocks(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { int ret = 0; struct phy *phy = ctrl->phy; - const u8 *dpcd = ctrl->panel->dpcd; + const u8 *dpcd = panel->dpcd; ctrl->phy_opts.dp.lanes = ctrl->link->link_params.num_lanes; ctrl->phy_opts.dp.link_rate = ctrl->link->link_params.rate / 100; @@ -1870,13 +1881,14 @@ static void msm_dp_ctrl_psr_exit(struct msm_dp_ctrl_private *ctrl) msm_dp_write_link(ctrl, REG_PSR_CMD, cmd); } -void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl) +void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel) { struct msm_dp_ctrl_private *ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); u32 cfg; - if (!ctrl->panel->psr_cap.version) + if (!panel->psr_cap.version) return; /* enable PSR1 function */ @@ -1891,12 +1903,13 @@ void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl) drm_dp_dpcd_write(ctrl->aux, DP_PSR_EN_CFG, &cfg, 1); } -void msm_dp_ctrl_set_psr(struct msm_dp_ctrl *msm_dp_ctrl, bool enter) +void msm_dp_ctrl_set_psr(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel, bool enter) { struct msm_dp_ctrl_private *ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); - if (!ctrl->panel->psr_cap.version) + if (!panel->psr_cap.version) return; /* @@ -1966,7 +1979,8 @@ void msm_dp_ctrl_phy_exit(struct msm_dp_ctrl *msm_dp_ctrl) phy_exit(phy); } -static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl) +static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { struct phy *phy = ctrl->phy; int ret = 0; @@ -1987,7 +2001,7 @@ static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl) /* hw recommended delay before re-enabling clocks */ msleep(20); - ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl); + ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl, panel); if (ret) { DRM_ERROR("Failed to enable mainlink clks. ret=%d\n", ret); return ret; @@ -1996,7 +2010,8 @@ static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl) return ret; } -static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl) +static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { struct phy *phy; @@ -2004,7 +2019,7 @@ static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl) msm_dp_ctrl_mainlink_disable(ctrl); - msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl); + msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl, panel); dev_pm_opp_set_rate(ctrl->dev, 0); msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); @@ -2018,7 +2033,8 @@ static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl) return 0; } -static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl) +static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { int ret = 0; int training_step = DP_TRAINING_NONE; @@ -2028,11 +2044,11 @@ static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl) ctrl->link->phy_params.p_level = 0; ctrl->link->phy_params.v_level = 0; - ret = msm_dp_ctrl_setup_main_link(ctrl, &training_step); + ret = msm_dp_ctrl_setup_main_link(ctrl, panel, &training_step); if (ret) goto end; - msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); + msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX); msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO); @@ -2211,7 +2227,8 @@ static void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl) } } -static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl) +static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { int ret; unsigned long pixel_rate; @@ -2227,15 +2244,15 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl * running. Add the global reset just before disabling the * link clocks and core clocks. */ - msm_dp_ctrl_off(&ctrl->msm_dp_ctrl); + msm_dp_ctrl_off(&ctrl->msm_dp_ctrl, panel); - ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl); + ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl, panel); if (ret) { DRM_ERROR("failed to enable DP link controller\n"); return ret; } - pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock; + pixel_rate = panel->msm_dp_mode.drm_mode.clock; ret = msm_dp_ctrl_on_pixel_clk(ctrl, pixel_rate); if (ret) return ret; @@ -2245,7 +2262,8 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl return 0; } -void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl) +void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel) { struct msm_dp_ctrl_private *ctrl; u32 sink_request = 0x0; @@ -2260,14 +2278,14 @@ void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl) if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { drm_dbg_dp(ctrl->drm_dev, "PHY_TEST_PATTERN request\n"); - if (msm_dp_ctrl_process_phy_test_request(ctrl)) { + if (msm_dp_ctrl_process_phy_test_request(ctrl, panel)) { DRM_ERROR("process phy_test_req failed\n"); return; } } if (sink_request & DP_LINK_STATUS_UPDATED) { - if (msm_dp_ctrl_link_maintenance(ctrl)) { + if (msm_dp_ctrl_link_maintenance(ctrl, panel)) { DRM_ERROR("LM failed: TEST_LINK_TRAINING\n"); return; } @@ -2275,7 +2293,7 @@ void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl) if (sink_request & DP_TEST_LINK_TRAINING) { msm_dp_link_send_test_response(ctrl->link); - if (msm_dp_ctrl_link_maintenance(ctrl)) { + if (msm_dp_ctrl_link_maintenance(ctrl, panel)) { DRM_ERROR("LM failed: TEST_LINK_TRAINING\n"); return; } @@ -2311,7 +2329,8 @@ static bool msm_dp_ctrl_channel_eq_ok(struct msm_dp_ctrl_private *ctrl) return drm_dp_channel_eq_ok(link_status, num_lanes); } -int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) +int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel) { int rc = 0; struct msm_dp_ctrl_private *ctrl; @@ -2327,8 +2346,8 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); - rate = ctrl->panel->link_info.rate; - pixel_rate = ctrl->panel->msm_dp_mode.drm_mode.clock; + rate = panel->link_info.rate; + pixel_rate = panel->msm_dp_mode.drm_mode.clock; msm_dp_ctrl_core_clk_enable(&ctrl->msm_dp_ctrl); @@ -2340,8 +2359,8 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) } else { ctrl->link->link_params.rate = rate; ctrl->link->link_params.num_lanes = - ctrl->panel->link_info.num_lanes; - if (ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420) + panel->link_info.num_lanes; + if (panel->msm_dp_mode.out_fmt_is_yuv_420) pixel_rate >>= 1; } @@ -2349,13 +2368,13 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) ctrl->link->link_params.rate, ctrl->link->link_params.num_lanes, pixel_rate); - rc = msm_dp_ctrl_enable_mainlink_clocks(ctrl); + rc = msm_dp_ctrl_enable_mainlink_clocks(ctrl, panel); if (rc) return rc; while (--link_train_max_retries) { training_step = DP_TRAINING_NONE; - rc = msm_dp_ctrl_setup_main_link(ctrl, &training_step); + rc = msm_dp_ctrl_setup_main_link(ctrl, panel, &training_step); if (rc == 0) { /* training completed successfully */ break; @@ -2374,7 +2393,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) * some lanes are ready, * reduce lane number */ - rc = msm_dp_ctrl_link_lane_down_shift(ctrl); + rc = msm_dp_ctrl_link_lane_down_shift(ctrl, panel); if (rc < 0) { /* lane == 1 already */ /* end with failure */ break; @@ -2395,7 +2414,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) ctrl->link->link_params.num_lanes)) rc = msm_dp_ctrl_link_rate_down_shift(ctrl); else - rc = msm_dp_ctrl_link_lane_down_shift(ctrl); + rc = msm_dp_ctrl_link_lane_down_shift(ctrl, panel); if (rc < 0) { /* end with failure */ @@ -2403,10 +2422,10 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) } /* stop link training before start re training */ - msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); + msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX); } - rc = msm_dp_ctrl_reinitialize_mainlink(ctrl); + rc = msm_dp_ctrl_reinitialize_mainlink(ctrl, panel); if (rc) { DRM_ERROR("Failed to reinitialize mainlink. rc=%d\n", rc); break; @@ -2427,20 +2446,21 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl) * link training failed * end txing train pattern here */ - msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); + msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX); - msm_dp_ctrl_deinitialize_mainlink(ctrl); + msm_dp_ctrl_deinitialize_mainlink(ctrl, panel); rc = -ECONNRESET; } return rc; } -static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl) +static int msm_dp_ctrl_link_retrain(struct msm_dp_ctrl_private *ctrl, + struct msm_dp_panel *panel) { int training_step = DP_TRAINING_NONE; - return msm_dp_ctrl_setup_main_link(ctrl, &training_step); + return msm_dp_ctrl_setup_main_link(ctrl, panel, &training_step); } static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl, @@ -2511,7 +2531,9 @@ static void msm_dp_ctrl_config_msa(struct msm_dp_ctrl_private *ctrl, msm_dp_write_link(ctrl, REG_DP_SOFTWARE_NVID, nvid); } -int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train) +int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel, + bool force_link_train) { int ret = 0; struct msm_dp_ctrl_private *ctrl; @@ -2530,7 +2552,7 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li ctrl->core_clks_on, ctrl->link_clks_on, ctrl->stream_clks_on); if (!ctrl->link_clks_on) { /* link clk is off */ - ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl); + ret = msm_dp_ctrl_enable_mainlink_clocks(ctrl, panel); if (ret) { DRM_ERROR("Failed to start link clocks. ret=%d\n", ret); return ret; @@ -2538,15 +2560,15 @@ int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_li } if (force_link_train || !msm_dp_ctrl_channel_eq_ok(ctrl)) - msm_dp_ctrl_link_retrain(ctrl); + msm_dp_ctrl_link_retrain(ctrl, panel); /* stop txing train pattern to end link training */ - msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX); + msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX); return ret; } -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl) +int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *panel) { int ret = 0; bool mainlink_ready = false; @@ -2559,10 +2581,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl) ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); - pixel_rate_orig = ctrl->panel->msm_dp_mode.drm_mode.clock; + pixel_rate_orig = panel->msm_dp_mode.drm_mode.clock; pixel_rate = pixel_rate_orig; - if (msm_dp_ctrl->wide_bus_en || ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420) + if (msm_dp_ctrl->wide_bus_en || panel->msm_dp_mode.out_fmt_is_yuv_420) pixel_rate >>= 1; drm_dbg_dp(ctrl->drm_dev, "pixel_rate=%lu\n", pixel_rate); @@ -2579,18 +2601,18 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl) msm_dp_ctrl_lane_mapping(ctrl); msm_dp_setup_peripheral_flush(ctrl); - msm_dp_ctrl_config_ctrl_link(ctrl); + msm_dp_ctrl_config_ctrl_link(ctrl, panel); - msm_dp_ctrl_configure_source_params(ctrl); + msm_dp_ctrl_configure_source_params(ctrl, panel); msm_dp_ctrl_config_msa(ctrl, ctrl->link->link_params.rate, pixel_rate_orig, - ctrl->panel->msm_dp_mode.out_fmt_is_yuv_420); + panel->msm_dp_mode.out_fmt_is_yuv_420); - msm_dp_panel_clear_dsc_dto(ctrl->panel); + msm_dp_panel_clear_dsc_dto(panel); - msm_dp_ctrl_setup_tr_unit(ctrl); + msm_dp_ctrl_setup_tr_unit(ctrl, panel); msm_dp_write_link(ctrl, REG_DP_STATE_CTRL, DP_STATE_CTRL_SEND_VIDEO); @@ -2618,7 +2640,8 @@ void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl) phy_init(phy); } -void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl) +void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel) { struct msm_dp_ctrl_private *ctrl; struct phy *phy; @@ -2626,11 +2649,11 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl) ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); phy = ctrl->phy; - msm_dp_panel_disable_vsc_sdp(ctrl->panel); + msm_dp_panel_disable_vsc_sdp(panel); msm_dp_ctrl_mainlink_disable(ctrl); - msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl); + msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl, panel); msm_dp_ctrl_off_pixel_clk(msm_dp_ctrl); dev_pm_opp_set_rate(ctrl->dev, 0); @@ -2639,7 +2662,8 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl) phy_power_off(phy); } -irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl) +irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel) { struct msm_dp_ctrl_private *ctrl; u32 isr; @@ -2650,7 +2674,7 @@ irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl) ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl); - if (ctrl->panel->psr_cap.version) { + if (panel->psr_cap.version) { isr = msm_dp_ctrl_get_psr_interrupt(ctrl); if (isr) @@ -2739,7 +2763,7 @@ static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl) } struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link, - struct msm_dp_panel *panel, struct drm_dp_aux *aux, + struct drm_dp_aux *aux, struct phy *phy, void __iomem *ahb_base, void __iomem *link_base) @@ -2747,7 +2771,7 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link struct msm_dp_ctrl_private *ctrl; int ret; - if (!dev || !panel || !aux || !link) { + if (!dev || !aux || !link) { DRM_ERROR("invalid input\n"); return ERR_PTR(-EINVAL); } @@ -2775,7 +2799,6 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link init_completion(&ctrl->video_comp); /* in parameters */ - ctrl->panel = panel; ctrl->aux = aux; ctrl->link = link; ctrl->dev = dev; diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h index 5d615f50d13b..00b430392a52 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h @@ -16,28 +16,36 @@ struct msm_dp_ctrl { struct phy; -int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl); -int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl); -int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train); -void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl); +int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel); +int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *panel); +int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel, + bool force_link_train); +void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel); void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl); -irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl); -void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl); +irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel); +void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel); struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link, - struct msm_dp_panel *panel, struct drm_dp_aux *aux, struct phy *phy, void __iomem *ahb_base, void __iomem *link_base); -void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl); +void msm_dp_ctrl_reset(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel); void msm_dp_ctrl_phy_init(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_phy_exit(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_irq_phy_exit(struct msm_dp_ctrl *msm_dp_ctrl); -void msm_dp_ctrl_set_psr(struct msm_dp_ctrl *msm_dp_ctrl, bool enable); -void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl); +void msm_dp_ctrl_set_psr(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel, bool enable); +void msm_dp_ctrl_config_psr(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel); int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl); diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index f8f8f3889ea5..26810b8c3fff 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -359,7 +359,7 @@ static void msm_dp_display_host_init(struct msm_dp_display_private *dp) dp->phy_initialized); msm_dp_ctrl_core_clk_enable(dp->ctrl); - msm_dp_ctrl_reset(dp->ctrl); + msm_dp_ctrl_reset(dp->ctrl, dp->panel); msm_dp_ctrl_enable_irq(dp->ctrl); msm_dp_aux_init(dp->aux); dp->core_initialized = true; @@ -371,7 +371,7 @@ static void msm_dp_display_host_deinit(struct msm_dp_display_private *dp) dp->msm_dp_display.connector_type, dp->core_initialized, dp->phy_initialized); - msm_dp_ctrl_reset(dp->ctrl); + msm_dp_ctrl_reset(dp->ctrl, dp->panel); msm_dp_ctrl_disable_irq(dp->ctrl); msm_dp_aux_deinit(dp->aux); msm_dp_ctrl_core_clk_disable(dp->ctrl); @@ -392,7 +392,7 @@ static int msm_dp_display_handle_irq_hpd(struct msm_dp_display_private *dp) drm_dbg_dp(dp->drm_dev, "%d\n", sink_request); - msm_dp_ctrl_handle_sink_request(dp->ctrl); + msm_dp_ctrl_handle_sink_request(dp->ctrl, dp->panel); if (sink_request & DP_TEST_LINK_VIDEO_PATTERN) msm_dp_display_handle_video_request(dp); @@ -570,8 +570,8 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp) goto error_link; } - dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->panel, dp->aux, - phy, dp->ahb_base, dp->link_base); + dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->aux, + phy, dp->ahb_base, dp->link_base); if (IS_ERR(dp->ctrl)) { rc = PTR_ERR(dp->ctrl); DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc); @@ -642,14 +642,14 @@ static int msm_dp_display_prepare_link(struct msm_dp_display_private *dp) force_link_train = true; } - rc = msm_dp_ctrl_on_link(dp->ctrl); + rc = msm_dp_ctrl_on_link(dp->ctrl, dp->panel); if (rc) { DRM_ERROR("Failed link training (rc=%d)\n", rc); // TODO: schedule drm_connector_set_link_status_property() return rc; } - return msm_dp_ctrl_prepare_stream_on(dp->ctrl, force_link_train); + return msm_dp_ctrl_prepare_stream_on(dp->ctrl, dp->panel, force_link_train); } static int msm_dp_display_enable(struct msm_dp_display_private *dp) @@ -663,7 +663,7 @@ static int msm_dp_display_enable(struct msm_dp_display_private *dp) return 0; } - rc = msm_dp_ctrl_on_stream(dp->ctrl); + rc = msm_dp_ctrl_on_stream(dp->ctrl, dp->panel); if (!rc) msm_dp_display->power_on = true; @@ -688,7 +688,7 @@ static int msm_dp_display_post_enable(struct msm_dp *msm_dp_display) msm_dp_display_handle_plugged_change(msm_dp_display, true); if (msm_dp_display->psr_supported) - msm_dp_ctrl_config_psr(dp->ctrl); + msm_dp_ctrl_config_psr(dp->ctrl, dp->panel); return 0; } @@ -727,7 +727,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp) /* set dongle to D3 (power off) mode */ msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true); - msm_dp_ctrl_off(dp->ctrl); + msm_dp_ctrl_off(dp->ctrl, dp->panel); /* re-init the PHY so that we can listen to Dongle disconnect */ msm_dp_ctrl_reinit_phy(dp->ctrl); } else { @@ -735,7 +735,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp) * unplugged interrupt * dongle unplugged out of DUT */ - msm_dp_ctrl_off(dp->ctrl); + msm_dp_ctrl_off(dp->ctrl, dp->panel); msm_dp_display_host_phy_exit(dp); } @@ -879,7 +879,7 @@ void msm_dp_display_set_psr(struct msm_dp *msm_dp_display, bool enter) } dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); - msm_dp_ctrl_set_psr(dp->ctrl, enter); + msm_dp_ctrl_set_psr(dp->ctrl, dp->panel, enter); } /** @@ -989,7 +989,7 @@ static irqreturn_t msm_dp_display_irq_handler(int irq, void *dev_id) } /* DP controller isr */ - ret |= msm_dp_ctrl_isr(dp->ctrl); + ret |= msm_dp_ctrl_isr(dp->ctrl, dp->panel); return ret; } From a57a81926277e9d054b0a8af034bb6a7ccdcfbf7 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 28 Jul 2026 18:21:40 +0800 Subject: [PATCH 066/121] drm/msm/dp: split dp_ctrl_off() into stream and link parts Split dp_ctrl_off() into stream and link parts so that for MST cases we can control the link and pixel parts separately. Signed-off-by: Abhinav Kumar Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742746/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-11-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 10 +++++----- drivers/gpu/drm/msm/dp/dp_ctrl.h | 5 +++-- drivers/gpu/drm/msm/dp/dp_display.c | 7 ++++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 9bed5a2fd2e7..5d896593771e 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -2215,7 +2215,7 @@ static int msm_dp_ctrl_on_pixel_clk(struct msm_dp_ctrl_private *ctrl, unsigned l return ret; } -static void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl) +void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl) { struct msm_dp_ctrl_private *ctrl; @@ -2244,7 +2244,8 @@ static int msm_dp_ctrl_process_phy_test_request(struct msm_dp_ctrl_private *ctrl * running. Add the global reset just before disabling the * link clocks and core clocks. */ - msm_dp_ctrl_off(&ctrl->msm_dp_ctrl, panel); + msm_dp_ctrl_off_pixel_clk(&ctrl->msm_dp_ctrl); + msm_dp_ctrl_off_link(&ctrl->msm_dp_ctrl, panel); ret = msm_dp_ctrl_on_link(&ctrl->msm_dp_ctrl, panel); if (ret) { @@ -2640,8 +2641,8 @@ void msm_dp_ctrl_reinit_phy(struct msm_dp_ctrl *msm_dp_ctrl) phy_init(phy); } -void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl, - struct msm_dp_panel *panel) +void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel) { struct msm_dp_ctrl_private *ctrl; struct phy *phy; @@ -2655,7 +2656,6 @@ void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl, msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl, panel); - msm_dp_ctrl_off_pixel_clk(msm_dp_ctrl); dev_pm_opp_set_rate(ctrl->dev, 0); msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h index 00b430392a52..5902cf7e746a 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h @@ -22,8 +22,9 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel * int msm_dp_ctrl_prepare_stream_on(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *panel, bool force_link_train); -void msm_dp_ctrl_off(struct msm_dp_ctrl *msm_dp_ctrl, - struct msm_dp_panel *panel); +void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl, + struct msm_dp_panel *panel); +void msm_dp_ctrl_off_pixel_clk(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_push_idle(struct msm_dp_ctrl *msm_dp_ctrl); irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *panel); diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 26810b8c3fff..44f4c2312fc3 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -727,15 +727,16 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp) /* set dongle to D3 (power off) mode */ msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true); - msm_dp_ctrl_off(dp->ctrl, dp->panel); - /* re-init the PHY so that we can listen to Dongle disconnect */ + msm_dp_ctrl_off_pixel_clk(dp->ctrl); + msm_dp_ctrl_off_link(dp->ctrl, dp->panel); msm_dp_ctrl_reinit_phy(dp->ctrl); } else { /* * unplugged interrupt * dongle unplugged out of DUT */ - msm_dp_ctrl_off(dp->ctrl, dp->panel); + msm_dp_ctrl_off_pixel_clk(dp->ctrl); + msm_dp_ctrl_off_link(dp->ctrl, dp->panel); msm_dp_display_host_phy_exit(dp); } From 894a6e16abbec9a523078dbd5f4701fe3c11c283 Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Tue, 28 Jul 2026 18:21:41 +0800 Subject: [PATCH 067/121] drm/msm/dp: simplify link and clock disable sequence Move the common disable steps out of the sink_count check to make the flow easier to follow. No functional change intended. Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742749/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-12-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_display.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 44f4c2312fc3..b4e1feead765 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -718,27 +718,19 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp) msm_dp_panel_disable_vsc_sdp(dp->panel); - /* dongle is still connected but sinks are disconnected */ - if (dp->link->sink_count == 0) { - /* - * irq_hpd with sink_count = 0 - * hdmi unplugged out of dongle - */ + msm_dp_ctrl_off_pixel_clk(dp->ctrl); - /* set dongle to D3 (power off) mode */ + /* dongle is still connected but sinks are disconnected */ + if (dp->link->sink_count == 0) msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true); - msm_dp_ctrl_off_pixel_clk(dp->ctrl); - msm_dp_ctrl_off_link(dp->ctrl, dp->panel); + + msm_dp_ctrl_off_link(dp->ctrl, dp->panel); + + if (dp->link->sink_count == 0) + /* re-init the PHY so that we can listen to Dongle disconnect */ msm_dp_ctrl_reinit_phy(dp->ctrl); - } else { - /* - * unplugged interrupt - * dongle unplugged out of DUT - */ - msm_dp_ctrl_off_pixel_clk(dp->ctrl); - msm_dp_ctrl_off_link(dp->ctrl, dp->panel); + else msm_dp_display_host_phy_exit(dp); - } msm_dp_display->power_on = false; From e8d91d17f73bf3ba314bbc2953dc3f9551b48770 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Tue, 28 Jul 2026 18:21:42 +0800 Subject: [PATCH 068/121] drm/msm/dp: make bridge helpers use dp_display to allow re-use dp_bridge helpers take drm_bridge as an input and extract the dp_display object to be used in the dp_display module. Rather than doing it in a roundabout way, directly pass the dp_display object to these helpers so that the MST bridge can also re-use the same helpers. Signed-off-by: Abhinav Kumar Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742750/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-13-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_display.c | 41 +++++++++--------------- drivers/gpu/drm/msm/dp/dp_display.h | 9 ++++++ drivers/gpu/drm/msm/dp/dp_drm.c | 48 ++++++++++++++++++++++++++++- drivers/gpu/drm/msm/dp/dp_drm.h | 11 ------- 4 files changed, 70 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index b4e1feead765..66990493917f 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -740,25 +740,22 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp) /** * msm_dp_bridge_mode_valid - callback to determine if specified mode is valid - * @bridge: Pointer to drm bridge structure + * @dp: Pointer to dp display structure * @info: display info * @mode: Pointer to drm mode structure * Returns: Validity status for specified mode */ -enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_info *info, - const struct drm_display_mode *mode) +enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp, + const struct drm_display_info *info, + const struct drm_display_mode *mode) { const u32 num_components = 3, default_bpp = 24; struct msm_dp_display_private *msm_dp_display; struct msm_dp_link_info *link_info; u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0; - struct msm_dp *dp; int mode_pclk_khz = mode->clock; bool is_yuv_420; - dp = to_dp_bridge(bridge)->msm_dp_display; - if (!dp || !mode_pclk_khz || !dp->connector) { DRM_ERROR("invalid params\n"); return -EINVAL; @@ -1404,18 +1401,16 @@ int msm_dp_modeset_init(struct msm_dp *msm_dp_display, struct drm_device *dev, return 0; } -void msm_dp_bridge_atomic_pre_enable(struct drm_bridge *drm_bridge, - struct drm_atomic_commit *state) +void msm_dp_display_atomic_pre_enable(struct msm_dp *msm_dp_display, + struct drm_atomic_commit *state) { - struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge); - struct msm_dp *dp = msm_dp_bridge->msm_dp_display; - struct msm_dp_display_private *msm_dp_display; + struct msm_dp_display_private *dp; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; - msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); + dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display); - crtc = drm_atomic_get_new_crtc_for_encoder(state, drm_bridge->encoder); + crtc = drm_atomic_get_new_crtc_for_encoder(state, msm_dp_display->bridge->encoder); if (!crtc) return; crtc_state = drm_atomic_get_new_crtc_state(state, crtc); @@ -1425,14 +1420,12 @@ void msm_dp_bridge_atomic_pre_enable(struct drm_bridge *drm_bridge, * state and runs before the bridge's .atomic_enable(), so the mode must * be programmed here, in .atomic_pre_enable(). */ - msm_dp_display_set_mode(dp, &crtc_state->adjusted_mode, msm_dp_display->panel); + msm_dp_display_set_mode(msm_dp_display, &crtc_state->adjusted_mode, dp->panel); } -void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, - struct drm_atomic_commit *state) +void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display, + struct drm_atomic_commit *state) { - struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge); - struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display; int rc = 0; struct msm_dp_display_private *dp; @@ -1457,11 +1450,8 @@ void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type); } -void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge, - struct drm_atomic_commit *state) +void msm_dp_display_atomic_disable(struct msm_dp *dp) { - struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge); - struct msm_dp *dp = msm_dp_bridge->msm_dp_display; struct msm_dp_display_private *msm_dp_display; msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); @@ -1478,11 +1468,8 @@ static void msm_dp_display_unprepare(struct msm_dp_display_private *dp) drm_dbg_dp(dp->drm_dev, "type=%d Done\n", msm_dp_display->connector_type); } -void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, - struct drm_atomic_commit *state) +void msm_dp_display_atomic_post_disable(struct msm_dp *dp) { - struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(drm_bridge); - struct msm_dp *dp = msm_dp_bridge->msm_dp_display; struct msm_dp_display_private *msm_dp_display; msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display); diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h index 0b65e16c790d..83117e7619f3 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.h +++ b/drivers/gpu/drm/msm/dp/dp_display.h @@ -33,5 +33,14 @@ void msm_dp_display_signal_audio_start(struct msm_dp *msm_dp_display); void msm_dp_display_signal_audio_complete(struct msm_dp *msm_dp_display); void msm_dp_display_set_psr(struct msm_dp *dp, bool enter); void msm_dp_display_debugfs_init(struct msm_dp *msm_dp_display, struct dentry *dentry, bool is_edp); +void msm_dp_display_atomic_post_disable(struct msm_dp *dp_display); +void msm_dp_display_atomic_disable(struct msm_dp *dp_display); +void msm_dp_display_atomic_pre_enable(struct msm_dp *dp_display, + struct drm_atomic_commit *state); +void msm_dp_display_atomic_enable(struct msm_dp *dp_display, + struct drm_atomic_commit *state); +enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp, + const struct drm_display_info *info, + const struct drm_display_mode *mode); #endif /* _DP_DISPLAY_H_ */ diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index 794a0df60414..c1e4147bfef1 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -49,6 +49,52 @@ static void msm_dp_bridge_debugfs_init(struct drm_bridge *bridge, struct dentry msm_dp_display_debugfs_init(dp, root, false); } +static void msm_dp_bridge_atomic_pre_enable(struct drm_bridge *drm_bridge, + struct drm_atomic_commit *state) +{ + struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge); + struct msm_dp *dp = dp_bridge->msm_dp_display; + + msm_dp_display_atomic_pre_enable(dp, state); +} + +static void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, + struct drm_atomic_commit *state) +{ + struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge); + struct msm_dp *dp = dp_bridge->msm_dp_display; + + msm_dp_display_atomic_enable(dp, state); +} + +static void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge, + struct drm_atomic_commit *state) +{ + struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge); + struct msm_dp *dp = dp_bridge->msm_dp_display; + + msm_dp_display_atomic_disable(dp); +} + +static void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, + struct drm_atomic_commit *state) +{ + struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge); + struct msm_dp *dp = dp_bridge->msm_dp_display; + + msm_dp_display_atomic_post_disable(dp); +} + +static enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *drm_bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge); + struct msm_dp *dp = dp_bridge->msm_dp_display; + + return msm_dp_display_mode_valid(dp, info, mode); +} + static const struct drm_bridge_funcs msm_dp_bridge_ops = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, @@ -116,7 +162,7 @@ static void msm_edp_bridge_atomic_enable(struct drm_bridge *drm_bridge, return; } - msm_dp_bridge_atomic_enable(drm_bridge, state); + msm_dp_display_atomic_enable(dp, state); } static void msm_edp_bridge_atomic_disable(struct drm_bridge *drm_bridge, diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h index 4f733d8118b1..da412c788503 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.h +++ b/drivers/gpu/drm/msm/dp/dp_drm.h @@ -27,17 +27,6 @@ int msm_dp_bridge_init(struct msm_dp *msm_dp_display, struct drm_device *dev, enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector); -void msm_dp_bridge_atomic_pre_enable(struct drm_bridge *drm_bridge, - struct drm_atomic_commit *state); -void msm_dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, - struct drm_atomic_commit *state); -void msm_dp_bridge_atomic_disable(struct drm_bridge *drm_bridge, - struct drm_atomic_commit *state); -void msm_dp_bridge_atomic_post_disable(struct drm_bridge *drm_bridge, - struct drm_atomic_commit *state); -enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_info *info, - const struct drm_display_mode *mode); void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge); void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge); void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge, From c16ba6ba3da4672713dd809d5b9031f1583848fa Mon Sep 17 00:00:00 2001 From: Yongxing Mou Date: Tue, 28 Jul 2026 18:21:43 +0800 Subject: [PATCH 069/121] drm/msm/dp: pass panel to display enable/disable helpers Pass struct msm_dp_panel to the display enable/disable helpers to make them easier to reuse for MST stream handling. Signed-off-by: Yongxing Mou Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742752/ Link: https://lore.kernel.org/r/20260728-dp_mstclean-v9-14-f7779fce10f4@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_display.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 66990493917f..bc646d172abe 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -652,7 +652,8 @@ static int msm_dp_display_prepare_link(struct msm_dp_display_private *dp) return msm_dp_ctrl_prepare_stream_on(dp->ctrl, dp->panel, force_link_train); } -static int msm_dp_display_enable(struct msm_dp_display_private *dp) +static int msm_dp_display_enable(struct msm_dp_display_private *dp, + struct msm_dp_panel *msm_dp_panel) { int rc = 0; struct msm_dp *msm_dp_display = &dp->msm_dp_display; @@ -663,7 +664,7 @@ static int msm_dp_display_enable(struct msm_dp_display_private *dp) return 0; } - rc = msm_dp_ctrl_on_stream(dp->ctrl, dp->panel); + rc = msm_dp_ctrl_on_stream(dp->ctrl, msm_dp_panel); if (!rc) msm_dp_display->power_on = true; @@ -709,22 +710,23 @@ static void msm_dp_display_audio_notify_disable(struct msm_dp_display_private *d msm_dp_display->audio_enabled = false; } -static int msm_dp_display_disable(struct msm_dp_display_private *dp) +static int msm_dp_display_disable(struct msm_dp_display_private *dp, + struct msm_dp_panel *msm_dp_panel) { struct msm_dp *msm_dp_display = &dp->msm_dp_display; if (!msm_dp_display->power_on) return 0; - msm_dp_panel_disable_vsc_sdp(dp->panel); + msm_dp_panel_disable_vsc_sdp(msm_dp_panel); msm_dp_ctrl_off_pixel_clk(dp->ctrl); /* dongle is still connected but sinks are disconnected */ if (dp->link->sink_count == 0) - msm_dp_link_psm_config(dp->link, &dp->panel->link_info, true); + msm_dp_link_psm_config(dp->link, &msm_dp_panel->link_info, true); - msm_dp_ctrl_off_link(dp->ctrl, dp->panel); + msm_dp_ctrl_off_link(dp->ctrl, msm_dp_panel); if (dp->link->sink_count == 0) /* re-init the PHY so that we can listen to Dongle disconnect */ @@ -739,7 +741,7 @@ static int msm_dp_display_disable(struct msm_dp_display_private *dp) } /** - * msm_dp_bridge_mode_valid - callback to determine if specified mode is valid + * msm_dp_display_mode_valid - callback to determine if specified mode is valid * @dp: Pointer to dp display structure * @info: display info * @mode: Pointer to drm mode structure @@ -1437,14 +1439,14 @@ void msm_dp_display_atomic_enable(struct msm_dp *msm_dp_display, return; } - rc = msm_dp_display_enable(dp); + rc = msm_dp_display_enable(dp, dp->panel); if (rc) DRM_ERROR("DP display enable failed, rc=%d\n", rc); rc = msm_dp_display_post_enable(msm_dp_display); if (rc) { DRM_ERROR("DP display post enable failed, rc=%d\n", rc); - msm_dp_display_disable(dp); + msm_dp_display_disable(dp, dp->panel); } drm_dbg_dp(msm_dp_display->drm_dev, "type=%d Done\n", msm_dp_display->connector_type); @@ -1479,7 +1481,7 @@ void msm_dp_display_atomic_post_disable(struct msm_dp *dp) msm_dp_display_audio_notify_disable(msm_dp_display); - msm_dp_display_disable(msm_dp_display); + msm_dp_display_disable(msm_dp_display, msm_dp_display->panel); msm_dp_display_unprepare(msm_dp_display); } From 811c38907eab0f66c22c5e5708e6f8eab14d76fa Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 28 Jul 2026 15:20:45 +0200 Subject: [PATCH 070/121] drm/msm/dpu: Drop sneaky dev_pm_opp_set_rate(0) dev_pm_opp_set_rate(0) removes the vote specified in required-opps but does not actually park the clock, making it run without the necessary power backing. Prevent that from happening when _dpu_core_perf_get_core_clk_rate() returns 0. Fixes: 25fdd5933e4c ("drm/msm: Add SDM845 DPU support") Signed-off-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/742779/ Link: https://lore.kernel.org/r/20260728-topic-dpu_power-v1-1-e7783b859a70@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c index 2ff255d7795e..fea173e37464 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -394,6 +394,10 @@ int dpu_core_perf_crtc_update(struct drm_crtc *crtc, trace_dpu_core_perf_update_clk(kms->dev, !crtc->enabled, clk_rate); + /* If we're going offline, PM callbacks will disable the clocks instead */ + if (!clk_rate) + return 0; + clk_rate = min(clk_rate, kms->perf.max_core_clk_rate); ret = dev_pm_opp_set_rate(&kms->pdev->dev, clk_rate); if (ret) { From cebfa9909e27ec7b7cbaec25ee5516cf886baa39 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 28 Jul 2026 15:20:46 +0200 Subject: [PATCH 071/121] drm/msm/dp: Drop dev_pm_opp_set_rate(0) dev_pm_opp_set_rate(0) removes the vote specified in required-opps but does not actually park the clock, making it run without the necessary power backing. Drop the explicit calls to it. Fixes: c943b4948b58 ("drm/msm/dp: add displayPort driver support") Signed-off-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742781/ Link: https://lore.kernel.org/r/20260728-topic-dpu_power-v1-2-e7783b859a70@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 5d896593771e..59070f399e2d 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -1988,13 +1988,12 @@ static int msm_dp_ctrl_reinitialize_mainlink(struct msm_dp_ctrl_private *ctrl, msm_dp_ctrl_mainlink_disable(ctrl); ctrl->phy_opts.dp.lanes = ctrl->link->link_params.num_lanes; phy_configure(phy, &ctrl->phy_opts); + /* * Disable and re-enable the mainlink clock since the * link clock might have been adjusted as part of the * link maintenance. */ - dev_pm_opp_set_rate(ctrl->dev, 0); - msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); phy_power_off(phy); @@ -2021,7 +2020,6 @@ static int msm_dp_ctrl_deinitialize_mainlink(struct msm_dp_ctrl_private *ctrl, msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl, panel); - dev_pm_opp_set_rate(ctrl->dev, 0); msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); phy_power_off(phy); @@ -2656,7 +2654,6 @@ void msm_dp_ctrl_off_link(struct msm_dp_ctrl *msm_dp_ctrl, msm_dp_ctrl_reset(&ctrl->msm_dp_ctrl, panel); - dev_pm_opp_set_rate(ctrl->dev, 0); msm_dp_ctrl_link_clk_disable(&ctrl->msm_dp_ctrl); phy_power_off(phy); From 06b7ba206561619bb34116f49e0ef26b867ce3aa Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 28 Jul 2026 15:20:47 +0200 Subject: [PATCH 072/121] drm/msm/dsi: Drop dev_pm_opp_set_rate(0) dev_pm_opp_set_rate(0) removes the vote specified in required-opps but does not actually park the clock, making it run without the necessary power backing. Drop the explicit call to it. Every call site of ops->link_clk_disable() is followed by pm_runtime_put(), so the power vote will be rescinded if deemed safe. Fixes: 32d3e0feccfe ("drm/msm: dsi: Use OPP API to set clk/perf state") Signed-off-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/742783/ Link: https://lore.kernel.org/r/20260728-topic-dpu_power-v1-3-e7783b859a70@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 59125d342fcb..7e4e3718b536 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -550,8 +550,6 @@ int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host) void dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host) { - /* Drop the performance state vote */ - dev_pm_opp_set_rate(&msm_host->pdev->dev, 0); clk_disable_unprepare(msm_host->esc_clk); clk_disable_unprepare(msm_host->pixel_clk); clk_disable_unprepare(msm_host->byte_intf_clk); From 140b13475302601368c0cf4e193e66126a49feb3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 30 Jul 2026 19:04:56 +0300 Subject: [PATCH 073/121] drm/msm: detach the ARM DMA mapping before attaching our own domain On ARM32 with CONFIG_ARM_DMA_USE_IOMMU, arch_setup_dma_ops() creates a dma_iommu_mapping for every IOMMU-backed device and attaches its domain to the device's IOMMU group. That domain is neither the group's default nor its blocking domain, so when msm_iommu_new() later attaches the domain the driver manages itself, __iommu_attach_group() refuses it: if (group->domain && group->domain != group->default_domain && group->domain != group->blocking_domain) return -EBUSY; Both the GPU and the display controller are hit by this on apq8064 (IFC6410), leaving the board with no GPU and no display: adreno 4300000.gpu: failed to load adreno gpu adreno 4300000.gpu: probe with driver adreno failed with error -16 mdp4 5100000.display-controller: [drm:msm_drm_kms_init] *ERROR* failed to load kms mdp4 5100000.display-controller: adev bind failed: -16 Other ARM32 DRM drivers that manage their own domains (tegra, rockchip, exynos) drop the arch mapping first. Do the same in msm_iommu_new(), which both the display and the GPU paths go through. With this the GPU and the KMS device both initialise: [drm] Initialized msm 1.13.0 for 4300000.gpu on minor 0 [drm] Initialized msm-kms 1.13.0 for 5100000.display-controller on minor 1 Assisted-by: Claude:claude-opus-5 Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/743333/ Link: https://lore.kernel.org/r/20260730-fix-qcom-smmu-v2-3-18e0daf2d836@oss.qualcomm.com --- drivers/gpu/drm/msm/msm_iommu.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 897e42c8d5c8..da6782fca6bd 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -7,6 +7,15 @@ #include #include #include + +#if defined(CONFIG_ARM_DMA_USE_IOMMU) +#include +#else +#define arm_iommu_detach_device(...) ({ }) +#define arm_iommu_release_mapping(...) ({ }) +#define to_dma_iommu_mapping(dev) NULL +#endif + #include "msm_drv.h" #include "msm_gpu_trace.h" #include "msm_mmu.h" @@ -749,6 +758,19 @@ struct msm_mmu *msm_iommu_new(struct device *dev, unsigned long quirks) mutex_init(&iommu->init_lock); + /* + * ARM32 attaches a DMA mapping domain to every IOMMU-backed device, + * which would make attaching our own domain fail with -EBUSY. + */ + if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) { + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + + if (mapping) { + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(mapping); + } + } + ret = iommu_attach_device(iommu->domain, dev); if (ret) { iommu_domain_free(domain); From 271e90eb5f9ff34951647e5ed33c1775eebcca50 Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Fri, 31 Jul 2026 12:42:32 -0300 Subject: [PATCH 074/121] drm: use drm_warn() in validate_blend_mode_for_alpha_formats() Commit 860e748bddcc ("drm: ensure blend mode supported if pixel format with alpha exposed") introduced a WARN() to let driver developers know that a previously valid behavior should now be changed. But WARN() should not be used for that, as it's a kernel warning report mechanism for conditions that are not expected to happen. It also produces a stack trace. Instead, a simple warning-level log message should have been used, as drivers were expected to trigger the condition. This is causing problems for fuzzers, as they may stop when encountering a "BUG:" or "WARNING:" in the logs. Replace WARN() with drm_warn() in this function, avoiding these issues. Fixes: 860e748bddcc ("drm: ensure blend mode supported if pixel format with alpha exposed") Signed-off-by: Leandro Ribeiro Reviewed-by: Daniel Stone Link: https://patch.msgid.link/20260731154232.37020-2-leandro.ribeiro@collabora.com Signed-off-by: Daniel Stone --- drivers/gpu/drm/drm_mode_config.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 3bcc7bf0900c..366f6d821242 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -869,8 +869,9 @@ static void validate_blend_mode_for_alpha_formats(struct drm_plane *plane) for (i = 0; i < plane->format_count; i++) { fmt = drm_format_info(plane->format_types[i]); if (fmt->has_alpha) { - WARN(1, "[PLANE:%d:%s] pixel format with alpha exposed but blend mode not setup", - plane->base.id, plane->name); + drm_warn(plane->dev, + "[PLANE:%d:%s] pixel format with alpha exposed but blend mode not setup. Please fix.\n", + plane->base.id, plane->name); break; } } From 3d318fe4e99a627aa3c2980699e2e9f13ad18516 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 13 Aug 2026 10:09:47 +0300 Subject: [PATCH 075/121] drm/xe: tests: fix error message in xe_migrate_sanity_test() This is supposed to print the error code but there is a copy and paste bug so it prints "bo" instead of "err". Fixes: dd08ebf6c352 ("drm/xe: Introduce a new DRM driver for Intel GPUs") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/an1tu0z3T-qX1ogn@stanley.mountain Signed-off-by: Rodrigo Vivi (cherry picked from commit 28a4198c52a1468fc1b620a9837557ea1dc1766d) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/tests/xe_migrate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/tests/xe_migrate.c b/drivers/gpu/drm/xe/tests/xe_migrate.c index 3c1be809be82..f10d9513747b 100644 --- a/drivers/gpu/drm/xe/tests/xe_migrate.c +++ b/drivers/gpu/drm/xe/tests/xe_migrate.c @@ -198,8 +198,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test, err = xe_bo_vmap(bo); if (err) { - KUNIT_FAIL(test, "Failed to vmap our pagetables: %li\n", - PTR_ERR(bo)); + KUNIT_FAIL(test, "Failed to vmap our pagetables: %d\n", err); return; } From c52feb4365396b6a881b5e8a95540517ffabb3b7 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 5 Aug 2026 11:44:54 +0800 Subject: [PATCH 076/121] drm/amdgpu: Disable runtime PM for externally attached dGPUs pci_is_thunderbolt_attached() requires an upstream PCI bridge with is_thunderbolt set from an Intel Thunderbolt VSEC. This does not cover the affected ASM4242 USB4 PCI hierarchy: 00:02.2 \- 0f:00.0 [1b21:2421] +- 10:01.0 [1b21:2423] -> 45:00.0 -> 46:00.0 | -> 47:00.0 -> 48:00.0 -> 49:00.0 [1002:7590] \- 10:03.0 -> 76:00.0 [1b21:2425] USB4 Host Router The host router is outside the GPU upstream bridge chain, leaving no ancestor with is_thunderbolt set. PCI core propagates DEVICE_REMOVABLE below the external-facing PCIe tunnel. Disable Runtime PM when either pci_is_thunderbolt_attached() or dev_is_removable() is true. Cc: stable@vger.kernel.org Signed-off-by: Yang Wang Reviewed-by: Candice Li Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 520fd59036d5..57c8ce814931 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -609,6 +609,13 @@ void amdgpu_device_detect_runtime_pm_mode(struct amdgpu_device *adev) int bamaco_support; adev->pm.rpm_mode = AMDGPU_RUNPM_NONE; + if (pci_is_thunderbolt_attached(adev->pdev) || + dev_is_removable(&adev->pdev->dev)) { + dev_info(adev->dev, + "Runtime PM disabled for externally attached device\n"); + return; + } + bamaco_support = amdgpu_device_supports_baco(adev); switch (amdgpu_runtime_pm) { From ef5fcf2a6c320676bf8be2dadac93d9023b468b7 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sat, 8 Aug 2026 20:09:34 +0800 Subject: [PATCH 077/121] drm/amdgpu: fix autosuspend cleanup during removal amdgpu_pci_probe() calls pm_runtime_use_autosuspend(), but amdgpu_pci_remove() does not call the matching pm_runtime_dont_use_autosuspend(). If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during teardown, this reference is not dropped and usage_count remains unbalanced. The documentation for pm_runtime_use_autosuspend() also notes that it is important to undo it with pm_runtime_dont_use_autosuspend() at driver exit time, unless runtime PM was initially enabled with devm_pm_runtime_enable(). Add the missing pm_runtime_dont_use_autosuspend() call to the remove path. This issue was found by manual code inspection. Fixes: d38ceaf99ed0 ("drm/amdgpu: add core driver (v4)") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260808120934.2813010-1-lgs201920130244@gmail.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 0ab380ca7e64..5c33c19fd9bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2557,6 +2557,7 @@ amdgpu_pci_remove(struct pci_dev *pdev) if (adev->pm.rpm_mode != AMDGPU_RUNPM_NONE) { pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); + pm_runtime_dont_use_autosuspend(dev->dev); } amdgpu_driver_unload_kms(dev); From ffdb7a8104f51d552dea4b319c8ce5169f63e724 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 22 Jul 2026 18:13:44 +0800 Subject: [PATCH 078/121] drm/amdgpu: ensure all userq VAs mapped before restore amdgpu_userq_buffer_vas_mapped() checks whether all VAs of a queue are mapped before restoring it. So that HW won't access any invalid addresses. Currently, this function assumes all VAs are mapped if any VA of a queue has been mapped, which is wrong. This commit fixes this problem by examining all VAs of a queue and reporting false if any of them is not mapped. Signed-off-by: Zhu Lingshan Reviewed-by: Sunil Khatri Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index bcfbd7213dd6..04639f894903 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -287,22 +287,24 @@ static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr) static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue) { - int i, r = 0; + int i; + bool mapped; for (i = 0; i < ARRAY_SIZE(queue->userq_vas.va_array); i++) { if (!queue->userq_vas.va_array[i]) continue; - r += amdgpu_userq_buffer_va_mapped(queue->vm, + + mapped = amdgpu_userq_buffer_va_mapped(queue->vm, queue->userq_vas.va_array[i]); dev_dbg(queue->userq_mgr->adev->dev, "validate the userq mapping:%p va:%llx r:%d\n", - queue, queue->userq_vas.va_array[i], r); + queue, queue->userq_vas.va_array[i], mapped); + + if (!mapped) + return false; } - if (r != 0) - return true; - - return false; + return true; } From 3438e964c916eb1a51f9f3ac018758e44abe5539 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Thu, 13 Aug 2026 13:59:07 +0530 Subject: [PATCH 079/121] drm/amdgpu/userq: ignore duplicate BO locks in userq signal ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_userq_signal_ioctl() calls drm_exec_init() without DRM_EXEC_IGNORE_DUPLICATES. When the same GEM object appears more than once across the read/write BO handle lists submitted by userspace, drm_exec_lock_obj() returns -EALREADY the second time it locks that object, which aborts the ioctl instead of treating the repeat as a no-op. Add DRM_EXEC_IGNORE_DUPLICATES so duplicate objects are silently skipped on the second lock attempt, matching the intended semantics of locking a set of (possibly overlapping) BOs before publishing a fence on them. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index 7e80442ec3e5..cd0bd016a24d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -537,7 +537,7 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data, * amdgpu_userq_ensure_ev_fence() can't be called while holding the resv * locks. */ - drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES, (num_read_bo_handles + num_write_bo_handles)); drm_exec_until_all_locked(&exec) { From 2411e499385c7df58afedb59238817579bc2168b Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Thu, 13 Aug 2026 14:02:09 +0530 Subject: [PATCH 080/121] drm/amdgpu/userq: ignore duplicate BO locks when counting wait fences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_userq_wait_count_fences() calls drm_exec_init() without DRM_EXEC_IGNORE_DUPLICATES. When the same GEM object appears more than once across the read/write BO handle lists submitted by userspace, drm_exec_lock_obj() returns -EALREADY the second time it locks that object, which aborts the fence-counting pass instead of treating the repeat as a no-op. Add DRM_EXEC_IGNORE_DUPLICATES so duplicate objects are silently skipped on the second lock attempt. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index cd0bd016a24d..981cb1dd5dda 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -643,7 +643,7 @@ amdgpu_userq_wait_count_fences(struct drm_file *filp, /* TODO: It is actually not necessary to lock them */ num_read_bo_handles = wait_info->num_bo_read_handles; num_write_bo_handles = wait_info->num_bo_write_handles; - drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES, num_read_bo_handles + num_write_bo_handles); drm_exec_until_all_locked(&exec) { From 5827c7ad72b6826d9b90be15af139dd2287e3ca3 Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Thu, 13 Aug 2026 14:02:35 +0530 Subject: [PATCH 081/121] drm/amdgpu/userq: ignore duplicate BO locks when returning wait fence info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit amdgpu_userq_wait_return_fence_info() calls drm_exec_init() without DRM_EXEC_IGNORE_DUPLICATES. When the same GEM object appears more than once across the read/write BO handle lists submitted by userspace, drm_exec_lock_obj() returns -EALREADY the second time it locks that object, which aborts the fence resolution pass instead of treating the repeat as a no-op. Add DRM_EXEC_IGNORE_DUPLICATES so duplicate objects are silently skipped on the second lock attempt. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index 981cb1dd5dda..a33dbe978798 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -778,7 +778,7 @@ amdgpu_userq_wait_return_fence_info(struct drm_device *dev, struct drm_file *fil /* Lock all the GEM objects */ num_read_bo_handles = wait_info->num_bo_read_handles; num_write_bo_handles = wait_info->num_bo_write_handles; - drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, + drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES, num_read_bo_handles + num_write_bo_handles); drm_exec_until_all_locked(&exec) { From 6fd83a1c2cdea48c396f600795217fbdfb8124f6 Mon Sep 17 00:00:00 2001 From: Akhmed Zhitaev Date: Thu, 13 Aug 2026 22:09:59 +0500 Subject: [PATCH 082/121] drm/amd/display: Scale custom brightness curve from full range Custom brightness curves use an 8-bit input signal. After exporting the full PWM range to userspace, the curve normalizer still divides requests by the physical PWM span. On panels with a nonzero minimum PWM level, this can produce a curve input greater than 255 and send an invalid backlight level to DC. Scale the userspace [0..max] range to the curve's [0..255] range instead. This retains the full advertised range and keeps the reverse readback conversion unchanged. Fixes: 8dbd72cb7900 ("drm/amd/display: Export full brightness range to userspace") Cc: stable@vger.kernel.org Signed-off-by: Akhmed Zhitaev Reviewed-by: Mario Limonciello (AMD) (Move to amdgpu_dm_backlight.c) Link: https://patch.msgid.link/20260813170959.22073-1-zhitaevakh@gmail.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c index b66ca60e697d..e61bbc310f33 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c @@ -106,10 +106,10 @@ int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps, } EXPORT_IF_KUNIT(get_brightness_range); -/* Rescale from [min..max] to [0..AMDGPU_MAX_BL_LEVEL] */ -static inline u32 scale_input_to_fw(int min, int max, u64 input) +/* Rescale userspace [0..max] to the firmware curve's [0..255]. */ +static inline u32 scale_input_to_fw(int max, u64 input) { - return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max - min); + return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max); } /* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */ @@ -123,7 +123,7 @@ void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps, unsigned int min, unsigned int max, uint32_t *user_brightness) { - u32 brightness = scale_input_to_fw(min, max, *user_brightness); + u32 brightness = scale_input_to_fw(max, *user_brightness); u8 lower_signal, upper_signal, upper_lum, lower_lum, lum; int left, right; From 59db985bc99e0589536a2116b93b0a6c45df41ab Mon Sep 17 00:00:00 2001 From: Mukul Joshi Date: Thu, 13 Aug 2026 11:02:24 -0400 Subject: [PATCH 083/121] drm/amdgpu: fix sysfs ip base addr for 64bit in standalone mode In standalone mode the ip_discovery sysfs tree is built from a verbatim copy of the discovery binary taken before reg_base_init() collapses the 64bit base addresses in place. Decoding as 32bit there yields interleaved zeros. Decode base_address_64[] in that case; keep reading the already collapsed adev->discovery.bin as-is otherwise. Fixes: 402e04f11ff7 ("drm/amdgpu: Export ip_discovery sysfs on probe failure") Cc: stable@vger.kernel.org Signed-off-by: Mukul Joshi Acked-by: Alex Deucher Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index a404d8aa13ee..164e85b66e2d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -1307,8 +1307,19 @@ static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev, ip_hw_instance->num_instance); ip_hw_instance->num_base_addresses = ip->num_base_address; - for (kk = 0; kk < ip_hw_instance->num_base_addresses; kk++) - ip_hw_instance->base_addr[kk] = ip->base_address[kk]; + for (kk = 0; kk < ip_hw_instance->num_base_addresses; kk++) { + /* + * Standalone mode uses a raw copy of the discovery + * binary; decode 64-bit addresses here. The shared + * bin is already collapsed to 32-bit in place. + */ + if (reg_base_64 && ip_top->standalone_mode) + ip_hw_instance->base_addr[kk] = + lower_32_bits(le64_to_cpu(ip->base_address_64[kk])) & 0x3FFFFFFF; + else + ip_hw_instance->base_addr[kk] = + le32_to_cpu(ip->base_address[kk]); + } kobject_init(&ip_hw_instance->kobj, &ip_hw_instance_ktype); ip_hw_instance->kobj.kset = &ip_hw_id->hw_id_kset; From 48dc279c3010ac8f91b1845b2abb3a1e9943a0f5 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Thu, 13 Aug 2026 12:27:53 +0800 Subject: [PATCH 084/121] drm/amdgpu: force complete the MES ring fences on reset The MES scheduler ring has no drm scheduler (no_scheduler = true), so it is skipped by the force-completion loop in amdgpu_device_pre_asic_reset(). It uses a polling fence whose hw value lives in wb (GTT) memory and survives a MODE1 reset, while fence_drv.sync_seq keeps advancing for every packet. When the reset is triggered because MES itself stopped responding, the timed-out packets advance sync_seq past the last hw fence value MES wrote. After resume the first MES submission polls forever on a seq that is never written back, failing the resume and wedging the box on a second reset: amdgpu: MES ring buffer is full. amdgpu: *ERROR* ring gfx_0.0.0 test failed (-110) amdgpu: resume of IP block failed -110 amdgpu: GPU reset end with ret = -110 Force complete the MES scheduler ring fences together with the scheduler rings so their hw fence is realigned to sync_seq. v2: cover all XCCs (one scheduler ring each), not just mes.ring[0]. Cc: stable@vger.kernel.org Signed-off-by: Jesse Zhang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 57c8ce814931..be4c74491554 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -5043,6 +5043,19 @@ int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev, amdgpu_fence_driver_force_completion(ring, fence); } + /* + * MES scheduler rings have no drm scheduler, so they are missed by the + * loop above. Realign their polling fence too (one per XCC), otherwise the + * first post-reset submission polls forever on a stale seq. sched.ready is + * only set while the driver owns the ring. + */ + for (i = 0; i < AMDGPU_MAX_MES_INST_PIPES; i++) { + struct amdgpu_ring *mes_ring = &adev->mes.ring[i]; + + if (mes_ring->fence_drv.initialized && mes_ring->sched.ready) + amdgpu_fence_driver_force_completion(mes_ring, fence); + } + amdgpu_fence_driver_isr_toggle(adev, false); r = amdgpu_reset_prepare_hwcontext(adev, reset_context); From 556488b08638aad240bdb524cebf22fa7569843c Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Thu, 13 Aug 2026 17:36:53 +0800 Subject: [PATCH 085/121] drm/amdgpu: validate rptr and wptr of a userq rptr and wptr of a userq are 8 bytes aligned, and may not placed on a page boundary. This commit checks whether rptr and wptr are 8 bytes aligned, and expectes 8 bytes when validates rptr/wptr VA. With above changes, this commit fixes an regression in amdgpu_userq_input_va_validate, where end_addr is caculated by: check_add_overflow(start_addr, expected_size - 1, &end_addr). Wptr and rptr are very likely not to be page aligned, when validating rptr and wptr, if they are located in the last mapped page(or only one page is mapped) and expected_size is PAGE_SIZE, end_addr will exceed the last mapped page, means (end_addr >> AMDGPU_GPU_PAGE_SHIFT) > va_map->last, and causing an -EINVAL, even it is a valid VA. Signed-off-by: Zhu Lingshan Acked-by: Alex Deucher Fixes: c0122bf2ccb1 ("drm/amdgpu: fix userq VA validation for sub-page buffers") Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 04639f894903..17cc48d87c4d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -702,10 +702,10 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) args->in.queue_size, &queue->userq_vas.va.queue_rb) || amdgpu_userq_input_va_validate(adev, queue, args->in.rptr_va, - AMDGPU_GPU_PAGE_SIZE, + sizeof(u64), &queue->userq_vas.va.rptr) || amdgpu_userq_input_va_validate(adev, queue, args->in.wptr_va, - AMDGPU_GPU_PAGE_SIZE, + sizeof(u64), &queue->userq_vas.va.wptr)) { r = -EINVAL; amdgpu_bo_unreserve(fpriv->vm.root.bo); @@ -850,6 +850,12 @@ static int amdgpu_userq_input_args_validate(struct drm_device *dev, drm_file_err(filp, "invalidate userq queue rptr or wptr\n"); return -EINVAL; } + + if (!IS_ALIGNED(args->in.wptr_va, sizeof(u64)) || + !IS_ALIGNED(args->in.rptr_va, sizeof(u64))) { + drm_file_err(filp, "user queue rptr or wptr is not 8-byte aligned\n"); + return -EINVAL; + } break; case AMDGPU_USERQ_OP_FREE: if (args->in.ip_type || From fd65d1742992361fc2201ecb4e43411e6e417fcb Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Thu, 13 Aug 2026 12:28:04 +0800 Subject: [PATCH 086/121] drm/amdgpu: force complete the KIQ ring fences on reset Like the MES scheduler ring, the KIQ ring sets no_scheduler = true and uses a polling fence, so it is skipped by the force-completion loop in amdgpu_device_pre_asic_reset(). Its hw fence value lives in wb (GTT) memory and survives a MODE1 reset while fence_drv.sync_seq keeps advancing, so after a reset the first KIQ submission can poll forever on a seq that is never written back. Force complete the KIQ ring fences too so their hw fence is realigned to sync_seq. Cc: stable@vger.kernel.org Reviewed-by: Alex Deucher Suggested-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index be4c74491554..0b7cdea4b9e0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -5056,6 +5056,18 @@ int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev, amdgpu_fence_driver_force_completion(mes_ring, fence); } + /* + * KIQ rings are polling-fence/no_scheduler like MES, so realign their + * fence too (one ring per XCC), otherwise the first post-reset KIQ + * submission polls forever on a stale seq. + */ + for (i = 0; i < AMDGPU_MAX_GC_INSTANCES; i++) { + struct amdgpu_ring *kiq_ring = &adev->gfx.kiq[i].ring; + + if (kiq_ring->fence_drv.initialized && kiq_ring->sched.ready) + amdgpu_fence_driver_force_completion(kiq_ring, fence); + } + amdgpu_fence_driver_isr_toggle(adev, false); r = amdgpu_reset_prepare_hwcontext(adev, reset_context); From 8587d48d694da5aca580f92461658ec14470592b Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 12 Aug 2026 11:08:40 +0800 Subject: [PATCH 087/121] drm/amdgpu: check thunderbolt before switcheroo registration Introduce a helper to consolidate the vga_switcheroo registration condition used by the init and fini paths. Keep the explicit pci_is_thunderbolt_attached() check, as dev_is_removable() does not provide equivalent coverage for Thunderbolt-attached GPUs. This ensures such devices remain excluded from switcheroo registration while preserving the existing PX and Apple gmux handling. Cc: stable@vger.kernel.org Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 0b7cdea4b9e0..ca385ed15bc6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3739,6 +3739,14 @@ static void amdgpu_device_sys_interface_fini(struct amdgpu_device *adev) amdgpu_ptl_sysfs_fini(adev); } +static bool +amdgpu_device_should_register_switcheroo(struct amdgpu_device *adev, bool px) +{ + return !pci_is_thunderbolt_attached(adev->pdev) && + (px || (!dev_is_removable(&adev->pdev->dev) && + apple_gmux_detect(NULL, NULL))); +} + /** * amdgpu_device_init - initialize the driver * @@ -4189,8 +4197,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, px = amdgpu_device_supports_px(adev); - if (px || (!dev_is_removable(&adev->pdev->dev) && - apple_gmux_detect(NULL, NULL))) + if (amdgpu_device_should_register_switcheroo(adev, px)) vga_switcheroo_register_client(adev->pdev, &amdgpu_switcheroo_ops, px); @@ -4355,8 +4362,7 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev) px = amdgpu_device_supports_px(adev); - if (px || (!dev_is_removable(&adev->pdev->dev) && - apple_gmux_detect(NULL, NULL))) + if (amdgpu_device_should_register_switcheroo(adev, px)) vga_switcheroo_unregister_client(adev->pdev); if (px) From c675dea86a000e9550077c5bca97c6431786d1b7 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Mon, 15 Jun 2026 13:48:23 +0200 Subject: [PATCH 088/121] drm/amdgpu: delay ttm buffer func enablement on xgmi When amdgpu_init_minimal_xgmi is used, SDMA engines init is delayed so amdgpu_ttm_enable_buffer_funcs must be called later. Without this, the check for num_buffer_funcs_scheds will fail and using ttm buffer funcs later will fail. Given that amdgpu_ttm_enable_buffer_funcs is a no-op if amdgpu_in_reset() returns true, the call has to occur after the reset lock is dropped. Cc: stable@vger.kernel.org Fixes: e4029f7a9474 ("drm/amdgpu: only use working sdma schedulers for ttm") Signed-off-by: Pierre-Eric Pelloux-Prayer Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 8 +++++--- drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index ca385ed15bc6..b61c641b5be4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -2529,7 +2529,11 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev) if (r) goto init_failed; - amdgpu_ttm_enable_buffer_funcs(adev); + /* If SDMA is not brought up during hwini, the ttm buffer funcs enablement + * is delayed after reset-on-init completes. + */ + if (amdgpu_ip_member_of_hwini(adev, AMD_IP_BLOCK_TYPE_SDMA)) + amdgpu_ttm_enable_buffer_funcs(adev); /* Don't init kfd if whole hive need to be reset during init */ if (adev->init_lvl->level != AMDGPU_INIT_LEVEL_MINIMAL_XGMI) { @@ -5174,8 +5178,6 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context) if (r) goto out; - amdgpu_ttm_enable_buffer_funcs(tmp_adev); - r = amdgpu_device_ip_resume_phase3(tmp_adev); if (r) goto out; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c index 2725230aa5e3..45e31b3daf06 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c @@ -1380,6 +1380,9 @@ static void amdgpu_xgmi_reset_on_init_work(struct work_struct *work) amdgpu_device_unlock_reset_domain(tmp_adev->reset_domain); list_for_each_entry(tmp_adev, &hive->device_list, gmc.xgmi.head) { + /* Enable ttm buffers funcs after the reset lock has been dropped. */ + amdgpu_ttm_enable_buffer_funcs(tmp_adev); + r = amdgpu_ras_init_badpage_info(tmp_adev); if (r && r != -EHWPOISON) dev_err(tmp_adev->dev, From 8fce9b0f93e222451d3f586c129c7b9f53a53fd2 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Fri, 14 Aug 2026 14:51:32 +0800 Subject: [PATCH 089/121] amdkfd: let profile_lock_device return an int other than uint32 profile_lock_device() may return negive error code, so the type of the return value should be int, not uint32 Signed-off-by: Zhu Lingshan Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 309510e23315..6fd18488d5cf 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -3329,7 +3329,7 @@ static int kfd_ioctl_create_process(struct file *filep, struct kfd_process *p, v return 0; } -static inline uint32_t profile_lock_device(struct kfd_process *p, +static inline int profile_lock_device(struct kfd_process *p, uint32_t gpu_id, uint32_t op) { struct kfd_process_device *pdd; From 5c082f4cd17601e2357c1c4a686a85a53411c3d0 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Fri, 14 Aug 2026 16:37:01 +0800 Subject: [PATCH 090/121] drm/amdgpu: fix hang and race in userq destroy When a queue is hung, the hang_detect_work is the only way to recover it. However in amdgpu_userq_destroy(), the hang_detect_work is cancelled too early, resulting in amdgpu_userq_wait_for_last_fence() may never return, leaving an uninterruptible dma_fence_wait() hang there. To fix this problem, this commit moves the cancelling of hang_detect_work after amdgpu_userq_wait_for_last_fence(), and it has to be before the unmap helper, because hang_detect_work resets the queue, so it races with amdgpu_userq_unmap_helper() for MES operations and queue state. This commit splits amdgpu_userq_cleanup() into two parts: 1) amdgpu_userq_detach_doorbell(), which detaches the queue from userq_doorbell_xa. This has to be called before the cancel, otherwise the IRQ handlers (for example amdgpu_userq_process_fence_irq) can re-schedule the hang_detect_work and the cancel is not final. 2) amdgpu_userq_fence_driver_free(), this has to be called after the unmap helper, because it can release the seq64 slot that the GPU writes fence values to. Only one cancel_delayed_work_sync(&queue->hang_detect_work) is needed, so other redundancies are removed. Signed-off-by: Zhu Lingshan Acked-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 17cc48d87c4d..24adad7be251 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -418,19 +418,12 @@ static void amdgpu_userq_wait_for_last_fence(struct amdgpu_usermode_queue *queue dma_fence_wait(f, false); } -static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue) +static void amdgpu_userq_detach_doorbell(struct amdgpu_usermode_queue *queue) { - struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; - struct amdgpu_device *adev = uq_mgr->adev; + struct amdgpu_device *adev = queue->userq_mgr->adev; - /* Wait for mode-1 reset to complete */ down_read(&adev->reset_domain->sem); - - /* Use interrupt-safe locking since IRQ handlers may access these XArrays */ xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index); - amdgpu_userq_fence_driver_free(queue); - queue->fence_drv = NULL; - up_read(&adev->reset_domain->sem); } @@ -551,18 +544,19 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que cancel_delayed_work_sync(&uq_mgr->resume_work); - /* Cancel any pending hang detection work and cleanup */ - cancel_delayed_work_sync(&queue->hang_detect_work); - mutex_lock(&uq_mgr->userq_mutex); amdgpu_userq_wait_for_last_fence(queue); + amdgpu_userq_detach_doorbell(queue); + cancel_delayed_work_sync(&queue->hang_detect_work); + #if defined(CONFIG_DEBUG_FS) debugfs_remove_recursive(queue->debugfs_queue); #endif r = amdgpu_userq_unmap_helper(queue); atomic_dec(&uq_mgr->userq_count[queue->queue_type]); - amdgpu_userq_cleanup(queue); + amdgpu_userq_fence_driver_free(queue); + queue->fence_drv = NULL; mutex_unlock(&uq_mgr->userq_mutex); /* @@ -574,7 +568,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que if (r) queue_work(adev->reset_domain->wq, &uq_mgr->reset_work); - cancel_delayed_work_sync(&queue->hang_detect_work); uq_funcs->mqd_destroy(queue); queue->userq_mgr = NULL; @@ -748,7 +741,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) ((queue->queue_type != AMDGPU_HW_IP_GFX) && (queue->queue_type != AMDGPU_HW_IP_COMPUTE))) { /* Serialize the map against an in-progress GPU reset (MES is - * unresponsive during recovery), matching amdgpu_userq_cleanup(). + * unresponsive during recovery), matching amdgpu_userq_detach_doorbell(). */ down_read(&adev->reset_domain->sem); r = amdgpu_userq_map_helper(queue); From 275c3332585bbabcefad109a6978cc0cbecf2008 Mon Sep 17 00:00:00 2001 From: Gilles Risch Date: Mon, 17 Aug 2026 02:43:26 +0200 Subject: [PATCH 091/121] drm/radeon: fix internal display on iMac11, 1 (RV770/DCE3.1) The Apple iMac11,1 (27-inch, Late 2009) uses a Mobility Radeon HD 4850 (RV770/DCE3.1) with a 2560x1440 internal panel on an internal DisplayPort path. Without this fix the display stays dark under KMS. This machine suffers from the same issue as iMac10,1 and iMac11,2: Apple routes the internal display through Link B of the DIG encoder instead of Link A. Add iMac11,1 to the existing DMI quirk and move the Apple-specific encoder assignment into its own block, independent of the DCE version check. Additionally, the 2560x1440 panel requires RADEON_PLL_USE_FRAC_FB_DIV and ATOM_ENCODER_CMD_DP_VIDEO_ON, limited to iMac11,1 via dmi_match() to avoid affecting other boards. Reviewed-by: Lukas Wunner Signed-off-by: Gilles Risch Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/atombios_crtc.c | 5 ++++- drivers/gpu/drm/radeon/atombios_encoders.c | 23 ++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 2fc0334e0d6c..075eba2d47f3 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -24,6 +24,8 @@ * Alex Deucher */ +#include + #include #include #include @@ -594,7 +596,8 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) && !radeon_crtc->ss_enabled) radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; - if (ASIC_IS_DCE32(rdev) && mode->clock > 165000) + if ((ASIC_IS_DCE32(rdev) || dmi_match(DMI_PRODUCT_NAME, "iMac11,1")) + && mode->clock > 165000) radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; } else { radeon_crtc->pll_flags |= RADEON_PLL_LEGACY; diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 5cfd8fcfa5e8..8b3f8303a967 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -1707,7 +1707,7 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) { /* DP_SET_POWER_D0 is set in radeon_dp_link_train */ radeon_dp_link_train(encoder, connector); - if (ASIC_IS_DCE4(rdev)) + if (ASIC_IS_DCE4(rdev) || dmi_match(DMI_PRODUCT_NAME, "iMac11,1")) atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); } if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { @@ -2123,17 +2123,20 @@ int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder, int fe_idx) } /* - * On DCE32 any encoder can drive any block so usually just use crtc id, - * but Apple thinks different at least on iMac10,1 and iMac11,2, so there use linkb, - * otherwise the internal eDP panel will stay dark. + * Apple routes the internal eDP panel through Link B of the DIG encoder + * instead of Link A on the iMac10,1, iMac11,1 and iMac11,2. + * Use linkb to avoid a dark display. */ - if (ASIC_IS_DCE32(rdev)) { - if (dmi_match(DMI_PRODUCT_NAME, "iMac10,1") || - dmi_match(DMI_PRODUCT_NAME, "iMac11,2")) - enc_idx = (dig->linkb) ? 1 : 0; - else - enc_idx = radeon_crtc->crtc_id; + if (dmi_match(DMI_PRODUCT_NAME, "iMac10,1") || + dmi_match(DMI_PRODUCT_NAME, "iMac11,1") || + dmi_match(DMI_PRODUCT_NAME, "iMac11,2")) { + enc_idx = (dig->linkb) ? 1 : 0; + goto assigned; + } + /* on DCE32 and encoder can driver any block so just crtc id */ + if (ASIC_IS_DCE32(rdev)) { + enc_idx = radeon_crtc->crtc_id; goto assigned; } From 4d7390530853eb7befda9cc786e4c86e8ad7ac9e Mon Sep 17 00:00:00 2001 From: "David (Ming Qiang) Wu" Date: Fri, 7 Aug 2026 15:12:14 -0400 Subject: [PATCH 092/121] drm/amdgpu/vcn: fix integer overflow in dec_msg buffer count check If the supplied msg[2] (num_buffers) is 0x3FFFFFFF, the expression 6 + num_buffers * 4 wraps to 2 and the bounds check passes, letting the parser loop far past the end of the message BO. Triggering it additionally requires a ~4GiB mapping so that msg[1] survives the earlier "header does not fit in BO" check. Rewrite the test in division form, which is overflow-free by construction. Also update the message to reflect that msg is invalid. Fixes: b193019860d6 ("drm/amdgpu/vcn3: Prevent OOB reads when parsing dec msg") Fixes: 0a78f2bac142 ("drm/amdgpu/vcn4: Prevent OOB reads when parsing dec msg") Cc: stable@vger.kernel.org Signed-off-by: David (Ming Qiang) Wu Reviewed-by: Leo Liu Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c | 10 +++++++--- drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c index 81bba3ec2a93..00d8f35846f2 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v3_0.c @@ -1964,9 +1964,13 @@ static int vcn_v3_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, len_dw = msg[1] / 4; num_buffers = msg[2]; - /* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */ - if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) { - DRM_ERROR("VCN message has too many buffers!\n"); + /* Verify that all indices fit within the claimed length. + * There are 6 dwords in the header before the first buffer. + * Each buffer has 4 dwords. Any trailing dwords after the + * last buffer are ignored. + */ + if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) { + DRM_ERROR("Invalid VCN message!\n"); r = -EINVAL; goto out; } diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index 0cce78b205a8..c2ddf3cb368f 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -1880,9 +1880,13 @@ static int vcn_v4_0_dec_msg(struct amdgpu_cs_parser *p, struct amdgpu_job *job, len_dw = msg[1] / 4; num_buffers = msg[2]; - /* Verify that all indices fit within the claimed length. Each index is 4 DWORDs */ - if (num_buffers > len_dw || 6 + num_buffers * 4 > len_dw) { - DRM_ERROR("VCN message has too many buffers!\n"); + /* Verify that all indices fit within the claimed length. + * There are 6 dwords in the header before the first buffer. + * Each buffer has 4 dwords. Any trailing dwords after the + * last buffer are ignored. + */ + if (len_dw < 6 || num_buffers > (len_dw - 6) / 4) { + DRM_ERROR("Invalid VCN message!\n"); r = -EINVAL; goto out; } From 0e4ef0ead600e367b4dd431daa97a345a5ff8a86 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 14 Aug 2026 11:39:16 -0400 Subject: [PATCH 093/121] drm/amdgpu: handle pipeline sync without a VM fence If we end up emitting a VM fence keep pipeline sync associated with that fence. If not, emit them as part of the IB fence. v2: fix need_pipe_sync handling v3: simplify the function Cc: David Rosca Fixes: cb1e657ccac8 ("drm/amdgpu: handle GDS and SPM without a VM fence") Reviewed-by: David Rosca Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c | 6 +++++- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 27 +++++++++++++------------- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 2 +- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c index da4dc489e80b..360e6f00cb7c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c @@ -222,7 +222,7 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs, vm_af = job->hw_vm_fence; /* VM sequence */ vm_af->ib_wptr = ring->wptr; - amdgpu_vm_flush(ring, job, need_pipe_sync, &emit_spm_needed, + amdgpu_vm_flush(ring, job, &need_pipe_sync, &emit_spm_needed, &emit_gds_needed); vm_af->ib_dw_size = amdgpu_ring_get_dw_distance(ring, vm_af->ib_wptr, ring->wptr); @@ -235,6 +235,10 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs, if (ring->funcs->insert_start) ring->funcs->insert_start(ring); + /* this may have been handled by amdgpu_vm_flush */ + if (need_pipe_sync) + amdgpu_ring_emit_pipeline_sync(ring); + if (emit_spm_needed) adev->gfx.rlc.funcs->update_spm_vmid(adev, ring->xcc_id, ring, job->vmid); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 71050a86bcc3..f6c5de63eae6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -772,7 +772,7 @@ bool amdgpu_vm_need_pipeline_sync(struct amdgpu_ring *ring, * Emit a VM flush when it is necessary. */ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, - bool need_pipe_sync, bool *emit_spm_needed, + bool *need_pipe_sync, bool *emit_spm_needed, bool *emit_gds_needed) { struct amdgpu_device *adev = ring->adev; @@ -827,8 +827,7 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, if (gds_switch_needed && emit_fence) *emit_gds_needed = false; - if (!vm_flush_needed && !gds_switch_needed && !need_pipe_sync && - !cleaner_shader_needed && !spm_update_needed) + if (!emit_fence) return; amdgpu_ring_ib_begin(ring); @@ -847,8 +846,10 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, patch = amdgpu_ring_init_cond_exec(ring, ring->cond_exe_gpu_addr); - if (need_pipe_sync) + if (*need_pipe_sync) { amdgpu_ring_emit_pipeline_sync(ring); + *need_pipe_sync = false; + } if (cleaner_shader_needed) ring->funcs->emit_cleaner_shader(ring); @@ -861,21 +862,19 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, if (pasid_mapping_needed) amdgpu_gmc_emit_pasid_mapping(ring, job->vmid, job->pasid); - if (emit_fence) { - if (spm_update_needed) - adev->gfx.rlc.funcs->update_spm_vmid(adev, ring->xcc_id, ring, job->vmid); + if (spm_update_needed) + adev->gfx.rlc.funcs->update_spm_vmid(adev, ring->xcc_id, ring, job->vmid); - if (gds_switch_needed) - amdgpu_ring_emit_gds_switch(ring, job->vmid, job->gds_base, + if (gds_switch_needed) + amdgpu_ring_emit_gds_switch(ring, job->vmid, job->gds_base, job->gds_size, job->gws_base, job->gws_size, job->oa_base, job->oa_size); - amdgpu_fence_emit(ring, job->hw_vm_fence, 0); - fence = &job->hw_vm_fence->base; - /* get a ref for the job */ - dma_fence_get(fence); - } + amdgpu_fence_emit(ring, job->hw_vm_fence, 0); + fence = &job->hw_vm_fence->base; + /* get a ref for the job */ + dma_fence_get(fence); if (vm_flush_needed) { mutex_lock(&id_mgr->lock); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 7f2ba728e3ed..d32183cd9e0f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -512,7 +512,7 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, int (*callback)(void *p, struct amdgpu_bo *bo), void *param); void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, - bool need_pipe_sync, bool *emit_spm_needed, + bool *need_pipe_sync, bool *emit_spm_needed, bool *emit_gds_needed); int amdgpu_vm_update_pdes(struct amdgpu_device *adev, struct amdgpu_vm *vm, bool immediate); From 4f40873f8a4107df2b9c8e68c947c4fd0cd519d2 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 4 Aug 2026 17:04:04 -0400 Subject: [PATCH 094/121] drm/amd/display: avoid divide-by-zero in __is_lut_linear() __is_lut_linear() computes the expected value of each entry with expected = i * MAX_DRM_LUT_VALUE / (size - 1); If it is ever called with a single-entry LUT, size - 1 is zero and the kernel takes a divide error (#DE). A LUT with fewer than two entries cannot describe a linear mapping anyway, so return false early instead of dividing by zero. Fixes: 086247a4b2fb ("drm/amd/display: Use 4096 lut entries") Cc: stable@vger.kernel.org Signed-off-by: Harry Wentland Reviewed-by: Melissa Wen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index 7b68c6846039..26e5c89375a5 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -471,6 +471,12 @@ bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size) uint32_t expected; int delta; + /* A LUT with fewer than two entries can't be interpolated and would + * divide by zero below (size - 1); it can't be treated as linear. + */ + if (size < 2) + return false; + for (i = 0; i < size; i++) { /* All color values should equal */ if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue)) From e4c3ab59021e7c146a84b6671f0d530972bd58b4 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Tue, 4 Aug 2026 17:04:05 -0400 Subject: [PATCH 095/121] drm/amd/display: validate plane degamma LUT size for private color prop Unlike the CRTC degamma path, which is guarded by amdgpu_dm_verify_lut_sizes(), the per-plane degamma LUT size was never validated before use. __set_dm_plane_degamma() passed the user-supplied size straight into __is_lut_linear() and, for a non-linear LUT, into __set_input_tf() -> __drm_lut_to_dc_gamma(), the latter always iterating MAX_COLOR_LUT_ENTRIES entries regardless of the actual LUT size. A malformed AMD_PLANE_DEGAMMA_LUT blob (e.g. a single entry) could thus trigger a divide-by-zero in __is_lut_linear() or an out-of-bounds read in __drm_lut_to_dc_gamma(). Reject any plane degamma LUT whose size does not match MAX_COLOR_LUT_ENTRIES, mirroring the invariant the code already asserts a few lines below (and which the CRTC path enforces). The AMD_PLANE_DEGAMMA_LUT property is only exposed on builds with AMD_PRIVATE_COLOR defined. Fixes: 980f8710075a ("drm/amd/display: add plane degamma TF and LUT support") Cc: stable@vger.kernel.org Signed-off-by: Harry Wentland Reviewed-by: Melissa Wen Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index 26e5c89375a5..d55dc06167a8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -1495,6 +1495,13 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state, degamma_lut = __extract_blob_lut(dm_plane_state->degamma_lut, °amma_size); + if (degamma_lut && degamma_size != MAX_COLOR_LUT_ENTRIES) { + drm_dbg(plane_state->state->dev, + "Invalid Plane Degamma LUT size. Should be %u but got %u.\n", + MAX_COLOR_LUT_ENTRIES, degamma_size); + return -EINVAL; + } + has_degamma_lut = degamma_lut && !__is_lut_linear(degamma_lut, degamma_size); From 13087ad7817e6e5f210064518bfc4d6d58a9c2f3 Mon Sep 17 00:00:00 2001 From: Nitin Gote Date: Fri, 14 Aug 2026 13:11:07 +0530 Subject: [PATCH 096/121] drm/xe: don't WARN on kernel job timeout when device already wedged igt@xe_wedged@wedged-at-any-timeout wedges the device in mode 2 (UPON_ANY_HANG_NO_RESET) and then rebinds the driver. During unbind, a GSC proxy kernel submission can still time out; with the device wedged and the GuC CT stopped it can never complete, so its kernel job times out. Tile0: GT1: Kernel-submitted job timed out WARNING: drivers/gpu/drm/xe/xe_guc_submit.c:... at guc_exec_queue_timedout_job() Workqueue: gt-ordered-wq drm_sched_job_timedout Killed queues skip guc_submit_hint_wedged(), leaving 'wedged' false even though the device is already wedged. The timeout handler then treats the kernel queue timeout as unexpected and taints the kernel. Honour an already-wedged device even for killed queues so the expected teardown timeout no longer trips the WARN. Fixes: 5a2f117a80c2 ("drm/xe: Do not wedge device on killed exec queues") Cc: Matthew Brost Signed-off-by: Nitin Gote Reviewed-by: Tejas Upadhyay Link: https://patch.msgid.link/20260814074106.92670-2-nitin.r.gote@intel.com Signed-off-by: Tejas Upadhyay (cherry picked from commit a1c1dbd0f047bb05de6aaf6abe9103031179bf19) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_guc_submit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 8aaed4fd13ea..864859bd0ccc 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1559,8 +1559,14 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) if (!skip_timeout_check && !check_timeout(q, job)) goto rearm; + /* + * Killed queues must not newly wedge the device, but preserve an + * already-wedged state to avoid warning on teardown timeouts. + */ if (!exec_queue_killed(q)) wedged = guc_submit_hint_wedged(exec_queue_to_guc(q)); + else + wedged = xe_device_wedged(xe); set_exec_queue_banned(q); From aef2ca9353c2f26dbacfb3b8e6f33fecfbf2e67d Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Wed, 19 Aug 2026 15:23:56 +0800 Subject: [PATCH 097/121] drm/amdgpu/mes: fix the inconsistent indenting for mes_userq_map() Fix the inconsistent indenting warning for mes_userq_map(). Fixes: d0827dda8fa7 ("drm/amdgpu/mes: refactor the amdgpu_mes_alloc/free_proc|gang()") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202608190252.8XCa0HqR-lkp@intel.com/ Signed-off-by: Prike Liang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index fae709f134bb..14a5abe42d1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -146,7 +146,7 @@ static int mes_userq_map(struct amdgpu_usermode_queue *queue) queue_input.wptr_mc_addr = queue->wptr_obj.gpu_addr; if (mes->use_rs64mem) { - if (!uq_mgr->proc_ctx_allocated) { + if (!uq_mgr->proc_ctx_allocated) { r = amdgpu_mes_alloc_proc_ctx_index(mes, &uq_mgr->proc_ctx_array_index); if (r) { DRM_ERROR("Failed to allocate userq process index err:%d\n", r); From d36fbf82189319e9af564c93900d30e55e87e7e0 Mon Sep 17 00:00:00 2001 From: Jesse Zhang Date: Fri, 14 Aug 2026 15:01:44 +0800 Subject: [PATCH 098/121] drm/amdgpu/userq: lock and validate wptr BOs before reading their GPU offset on restore On resume, amdgpu_userq_vm_validate_and_restore_queue() updates each queue's wptr GPU address via amdgpu_bo_gpu_offset(). WPTR BOs are VM-mapped, but each BO has its own reservation object and is not implicitly covered by the VM validation path here. This can leave offset reads without proper BO locking/placement state and trigger WARN_ONs. ------------[ cut here ]------------ WARNING: amdgpu_object.c:1486 at amdgpu_bo_gpu_offset+0x75/0xa0 [amdgpu], CPU#3: kworker/3:1/116 Workqueue: events amdgpu_userq_restore_worker [amdgpu] RIP: 0010:amdgpu_bo_gpu_offset+0x75/0xa0 [amdgpu] Call Trace: amdgpu_userq_vm_validate_and_restore_queue+0x629/0x960 [amdgpu] amdgpu_userq_restore_worker+0xa6/0x180 [amdgpu] process_scheduled_works+0xa6/0x460 worker_thread+0x13c/0x290 kthread+0xfb/0x140 ret_from_fork+0x1b6/0x2b0 ret_from_fork_asm+0x1a/0x30 ---[ end trace 0000000000000000 ]--- ------------[ cut here ]------------ WARNING: amdgpu_object.c:1485 at amdgpu_bo_gpu_offset+0x9a/0xa0 [amdgpu], CPU#2: kworker/2:1/127 Workqueue: events amdgpu_userq_restore_worker [amdgpu] RIP: 0010:amdgpu_bo_gpu_offset+0x9a/0xa0 [amdgpu] Add each queue's WPTR BO to the drm_exec ww context and validate it to its allowed placement before the later offset update. v2: - Clarify that WPTR BOs are VM-mapped (fix incorrect "not part of VM" wording). (Christian) - Describe both parts of the fix: lock BO reservations in drm_exec and validate BO placement before offset reads. Acked-by: Alex Deucher Signed-off-by: Jesse Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 24adad7be251..0a816b3c5ff9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -1047,6 +1047,30 @@ amdgpu_userq_vm_validate_and_restore_queue(struct amdgpu_userq_mgr *uq_mgr) drm_exec_retry_on_contention(&exec); if (unlikely(ret)) goto unlock_all; + + /* + * WPTR BOs are VM-mapped, but each BO has its own reservation + * object. Lock them into this drm_exec ww context so the later + * amdgpu_bo_gpu_offset() reads are done with the BO resv locked. + */ + xa_for_each(&uq_mgr->userq_xa, tmp_key, queue) { + struct ttm_operation_ctx wptr_ctx = { false, false }; + + bo = queue->wptr_obj.obj; + if (!bo) + continue; + + ret = drm_exec_prepare_obj(&exec, &bo->tbo.base, + TTM_NUM_MOVE_FENCES + 1); + drm_exec_retry_on_contention(&exec); + if (unlikely(ret)) + goto unlock_all; + + amdgpu_bo_placement_from_domain(bo, bo->allowed_domains); + ret = ttm_bo_validate(&bo->tbo, &bo->placement, &wptr_ctx); + if (unlikely(ret)) + goto unlock_all; + } } if (invalidated) { From 6760f5cb12d2366ddd58a2d8637f7583d73f596b Mon Sep 17 00:00:00 2001 From: Bob Zhou Date: Wed, 19 Aug 2026 14:23:49 +0800 Subject: [PATCH 099/121] drm/amdgpu: avoid force-completing uninitialized UVD rings uvd_v7_0_sw_init() does not initialize the UVD decode ring for an SR-IOV VF. However, amdgpu_uvd_resume() unconditionally force-completes the decode ring when restoring its fence sequence. Skip fence completion when the fence driver is not initialized. Fixes: 0a33b11d26c6 ("drm/amdgpu: mark force completed fences with -ECANCELED") Cc: stable@vger.kernel.org Signed-off-by: Bob Zhou Acked-by: Leo Liu Acked-by: Frank Min Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 228a405a94c4..ecd7caa95d4b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -517,7 +517,8 @@ int amdgpu_uvd_resume(struct amdgpu_device *adev) } memset_io(ptr, 0, size); /* to restore uvd fence seq */ - amdgpu_fence_driver_force_completion(&adev->uvd.inst[i].ring, NULL); + if (adev->uvd.inst[i].ring.fence_drv.initialized) + amdgpu_fence_driver_force_completion(&adev->uvd.inst[i].ring, NULL); } } return 0; From 290e0be2abb7d9f1473d12ef2ed3220b071c42c3 Mon Sep 17 00:00:00 2001 From: David Belanger Date: Fri, 14 Aug 2026 12:57:18 -0400 Subject: [PATCH 100/121] drm/kfd: Add CU occupancy support to GFX11 Port changes from GFX9 to GFX11 mostly as-is. Minor changes to register access code. Assisted-by: Claude:Sonnet-4-6 Signed-off-by: David Belanger Acked-by: Alex Deucher Reviewed-by: Sreekant Somasekharan Signed-off-by: Alex Deucher --- .../drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c index 724beb96ed1a..04fab30ab5dd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v11.c @@ -807,6 +807,153 @@ static uint32_t kgd_gfx_v11_hqd_sdma_get_doorbell(struct amdgpu_device *adev, return 0; } +static void lock_spi_csq_mutexes(struct amdgpu_device *adev) +{ + mutex_lock(&adev->srbm_mutex); + mutex_lock(&adev->grbm_idx_mutex); + +} + +static void unlock_spi_csq_mutexes(struct amdgpu_device *adev) +{ + mutex_unlock(&adev->grbm_idx_mutex); + mutex_unlock(&adev->srbm_mutex); +} + +/** + * get_wave_count: Read device registers to get number of waves in flight for + * a particular queue. The method also returns the doorbell offset associated + * with the queue. + * + * @adev: Handle of device whose registers are to be read + * @queue_idx: Index of queue in the queue-map bit-field + * @queue_cnt: Stores the wave count and doorbell offset for an active queue + * @inst: xcc's instance number on a multi-XCC setup + */ +static void get_wave_count(struct amdgpu_device *adev, int queue_idx, + struct kfd_cu_occupancy *queue_cnt, uint32_t inst) +{ + int pipe_idx; + int queue_slot; + unsigned int reg_val; + unsigned int wave_cnt; + /* + * Program GRBM with appropriate MEID, PIPEID, QUEUEID and VMID + * parameters to read out waves in flight. Get doorbell offset if there are + * non-zero waves in flight. + */ + pipe_idx = queue_idx / adev->gfx.mec.num_queue_per_pipe; + queue_slot = queue_idx % adev->gfx.mec.num_queue_per_pipe; + soc21_grbm_select(adev, 1, pipe_idx, queue_slot, 0); + reg_val = RREG32_SOC15_IP(GC, SOC15_REG_OFFSET(GC, 0, + regSPI_CSQ_WF_ACTIVE_COUNT_0) + queue_slot); + wave_cnt = reg_val & SPI_CSQ_WF_ACTIVE_COUNT_0__COUNT_MASK; + if (wave_cnt != 0) { + queue_cnt->wave_cnt += wave_cnt; + queue_cnt->doorbell_off = + (RREG32_SOC15(GC, 0, regCP_HQD_PQ_DOORBELL_CONTROL) & + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >> + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + } +} + +/** + * kgd_gfx_v11_get_cu_occupancy: Reads relevant registers associated with each + * shader engine and aggregates the number of waves that are in flight for the + * process whose pasid is provided as a parameter. The process could have ZERO + * or more queues running and submitting waves to compute units. + * + * @adev: Handle of device from which to get number of waves in flight + * @cu_occupancy: Array that gets filled with wave_cnt and doorbell offset + * for comparison later. + * @max_waves_per_cu: Output parameter updated with maximum number of waves + * possible per Compute Unit + * @inst: xcc's instance number on a multi-XCC setup + * + * Note: It's possible that the device has too many queues (oversubscription) + * in which case a VMID could be remapped to a different PASID. This could lead + * to an inaccurate wave count. Following is a high-level sequence: + * Time T1: vmid = getVmid(); vmid is associated with Pasid P1 + * Time T2: passId = getPasId(vmid); vmid is associated with Pasid P2 + * In the sequence above wave count obtained from time T1 will be incorrectly + * lost or added to total wave count. + * + * The registers that provide the waves in flight are: + * + * SPI_CSQ_WF_ACTIVE_STATUS - bit-map of queues per pipe. The bit is ON if a + * queue is slotted, OFF if there is no queue. A process could have ZERO or + * more queues slotted and submitting waves to be run on compute units. Even + * when there is a queue it is possible there could be zero wave fronts, this + * can happen when queue is waiting on top-of-pipe events - e.g. waitRegMem + * command + * + * For each bit that is ON from above: + * + * Read (SPI_CSQ_WF_ACTIVE_COUNT_0 + queue_idx) register. It provides the + * number of waves that are in flight for the queue at specified index. The + * index ranges from 0 to 7. + * + * If non-zero waves are in flight, store the corresponding doorbell offset + * of the queue, along with the wave count. + * + * Determine if the queue belongs to the process by comparing the doorbell + * offset against the process's queues. If it matches, aggregate the wave + * count for the process. + * + * Reading registers referenced above involves programming GRBM appropriately + */ +static void kgd_gfx_v11_get_cu_occupancy(struct amdgpu_device *adev, + struct kfd_cu_occupancy *cu_occupancy, + int *max_waves_per_cu, uint32_t inst) +{ + int qidx; + int se_idx; + int se_cnt; + int queue_map; + int max_queue_cnt; + DECLARE_BITMAP(cp_queue_bitmap, AMDGPU_MAX_QUEUES); + + lock_spi_csq_mutexes(adev); + soc21_grbm_select(adev, 1, 0, 0, 0); + + /* + * Iterate through the shader engines and arrays of the device + * to get number of waves in flight + */ + bitmap_complement(cp_queue_bitmap, adev->gfx.mec_bitmap[0].queue_bitmap, + AMDGPU_MAX_QUEUES); + max_queue_cnt = adev->gfx.mec.num_pipe_per_mec * + adev->gfx.mec.num_queue_per_pipe; + se_cnt = adev->gfx.config.max_shader_engines; + for (se_idx = 0; se_idx < se_cnt; se_idx++) { + amdgpu_gfx_select_se_sh(adev, se_idx, 0, 0xffffffff, inst); + queue_map = RREG32_SOC15(GC, 0, + regSPI_CSQ_WF_ACTIVE_STATUS); + + for (qidx = 0; qidx < max_queue_cnt; qidx++) { + /* Skip queues that are not associated with + * compute functions + */ + if (!test_bit(qidx, cp_queue_bitmap)) + continue; + + if (!(queue_map & (1 << qidx))) + continue; + + /* Get number of waves in flight and aggregate them */ + get_wave_count(adev, qidx, &cu_occupancy[qidx], inst); + } + } + + amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, inst); + soc21_grbm_select(adev, 0, 0, 0, 0); + unlock_spi_csq_mutexes(adev); + + /* Update the output parameters and return */ + *max_waves_per_cu = adev->gfx.cu_info.simd_per_cu * + adev->gfx.cu_info.max_waves_per_simd; +} + const struct kfd2kgd_calls gfx_v11_kfd2kgd = { .program_sh_mem_settings = program_sh_mem_settings_v11, .set_pasid_vmid_mapping = set_pasid_vmid_mapping_v11, @@ -832,5 +979,6 @@ const struct kfd2kgd_calls gfx_v11_kfd2kgd = { .clear_address_watch = kgd_gfx_v11_clear_address_watch, .hqd_get_pq_addr = kgd_gfx_v11_hqd_get_pq_addr, .hqd_reset = kgd_gfx_v11_hqd_reset, + .get_cu_occupancy = kgd_gfx_v11_get_cu_occupancy, .hqd_sdma_get_doorbell = kgd_gfx_v11_hqd_sdma_get_doorbell }; From fb1e65a80dd9167e8dfdfdfef178d1d012d8bb58 Mon Sep 17 00:00:00 2001 From: David Belanger Date: Fri, 14 Aug 2026 13:25:56 -0400 Subject: [PATCH 101/121] drm/kfd: Add CU occupancy support to GFX12 Port changes from GFX9 to GFX12 mostly as-is. Minor changes to register access code. Assisted-by: Claude:Sonnet-4-6 Signed-off-by: David Belanger Acked-by: Alex Deucher Reviewed-by: Sreekant Somasekharan Signed-off-by: Alex Deucher --- .../drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c | 150 +++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c index e11ba3e91841..62b9db64368e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12.c @@ -368,6 +368,153 @@ static uint32_t kgd_gfx_v12_hqd_sdma_get_doorbell(struct amdgpu_device *adev, return 0; } +static void lock_spi_csq_mutexes(struct amdgpu_device *adev) +{ + mutex_lock(&adev->srbm_mutex); + mutex_lock(&adev->grbm_idx_mutex); + +} + +static void unlock_spi_csq_mutexes(struct amdgpu_device *adev) +{ + mutex_unlock(&adev->grbm_idx_mutex); + mutex_unlock(&adev->srbm_mutex); +} + +/** + * get_wave_count: Read device registers to get number of waves in flight for + * a particular queue. The method also returns the doorbell offset associated + * with the queue. + * + * @adev: Handle of device whose registers are to be read + * @queue_idx: Index of queue in the queue-map bit-field + * @queue_cnt: Stores the wave count and doorbell offset for an active queue + * @inst: xcc's instance number on a multi-XCC setup + */ +static void get_wave_count(struct amdgpu_device *adev, int queue_idx, + struct kfd_cu_occupancy *queue_cnt, uint32_t inst) +{ + int pipe_idx; + int queue_slot; + unsigned int reg_val; + unsigned int wave_cnt; + /* + * Program GRBM with appropriate MEID, PIPEID, QUEUEID and VMID + * parameters to read out waves in flight. Get doorbell offset if there are + * non-zero waves in flight. + */ + pipe_idx = queue_idx / adev->gfx.mec.num_queue_per_pipe; + queue_slot = queue_idx % adev->gfx.mec.num_queue_per_pipe; + soc24_grbm_select(adev, 1, pipe_idx, queue_slot, 0); + reg_val = RREG32_SOC15_IP(GC, SOC15_REG_OFFSET(GC, 0, + regSPI_CSQ_WF_ACTIVE_COUNT_0) + queue_slot); + wave_cnt = reg_val & SPI_CSQ_WF_ACTIVE_COUNT_0__COUNT_MASK; + if (wave_cnt != 0) { + queue_cnt->wave_cnt += wave_cnt; + queue_cnt->doorbell_off = + (RREG32_SOC15(GC, 0, regCP_HQD_PQ_DOORBELL_CONTROL) & + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >> + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + } +} + +/** + * kgd_gfx_v12_get_cu_occupancy: Reads relevant registers associated with each + * shader engine and aggregates the number of waves that are in flight for the + * process whose pasid is provided as a parameter. The process could have ZERO + * or more queues running and submitting waves to compute units. + * + * @adev: Handle of device from which to get number of waves in flight + * @cu_occupancy: Array that gets filled with wave_cnt and doorbell offset + * for comparison later. + * @max_waves_per_cu: Output parameter updated with maximum number of waves + * possible per Compute Unit + * @inst: xcc's instance number on a multi-XCC setup + * + * Note: It's possible that the device has too many queues (oversubscription) + * in which case a VMID could be remapped to a different PASID. This could lead + * to an inaccurate wave count. Following is a high-level sequence: + * Time T1: vmid = getVmid(); vmid is associated with Pasid P1 + * Time T2: passId = getPasId(vmid); vmid is associated with Pasid P2 + * In the sequence above wave count obtained from time T1 will be incorrectly + * lost or added to total wave count. + * + * The registers that provide the waves in flight are: + * + * SPI_CSQ_WF_ACTIVE_STATUS - bit-map of queues per pipe. The bit is ON if a + * queue is slotted, OFF if there is no queue. A process could have ZERO or + * more queues slotted and submitting waves to be run on compute units. Even + * when there is a queue it is possible there could be zero wave fronts, this + * can happen when queue is waiting on top-of-pipe events - e.g. waitRegMem + * command + * + * For each bit that is ON from above: + * + * Read (SPI_CSQ_WF_ACTIVE_COUNT_0 + queue_idx) register. It provides the + * number of waves that are in flight for the queue at specified index. The + * index ranges from 0 to 7. + * + * If non-zero waves are in flight, store the corresponding doorbell offset + * of the queue, along with the wave count. + * + * Determine if the queue belongs to the process by comparing the doorbell + * offset against the process's queues. If it matches, aggregate the wave + * count for the process. + * + * Reading registers referenced above involves programming GRBM appropriately + */ +static void kgd_gfx_v12_get_cu_occupancy(struct amdgpu_device *adev, + struct kfd_cu_occupancy *cu_occupancy, + int *max_waves_per_cu, uint32_t inst) +{ + int qidx; + int se_idx; + int se_cnt; + int queue_map; + int max_queue_cnt; + DECLARE_BITMAP(cp_queue_bitmap, AMDGPU_MAX_QUEUES); + + lock_spi_csq_mutexes(adev); + soc24_grbm_select(adev, 1, 0, 0, 0); + + /* + * Iterate through the shader engines and arrays of the device + * to get number of waves in flight + */ + bitmap_complement(cp_queue_bitmap, adev->gfx.mec_bitmap[0].queue_bitmap, + AMDGPU_MAX_QUEUES); + max_queue_cnt = adev->gfx.mec.num_pipe_per_mec * + adev->gfx.mec.num_queue_per_pipe; + se_cnt = adev->gfx.config.max_shader_engines; + for (se_idx = 0; se_idx < se_cnt; se_idx++) { + amdgpu_gfx_select_se_sh(adev, se_idx, 0, 0xffffffff, inst); + queue_map = RREG32_SOC15(GC, 0, + regSPI_CSQ_WF_ACTIVE_STATUS); + + for (qidx = 0; qidx < max_queue_cnt; qidx++) { + /* Skip queues that are not associated with + * compute functions + */ + if (!test_bit(qidx, cp_queue_bitmap)) + continue; + + if (!(queue_map & (1 << qidx))) + continue; + + /* Get number of waves in flight and aggregate them */ + get_wave_count(adev, qidx, &cu_occupancy[qidx], inst); + } + } + + amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, inst); + soc24_grbm_select(adev, 0, 0, 0, 0); + unlock_spi_csq_mutexes(adev); + + /* Update the output parameters and return */ + *max_waves_per_cu = adev->gfx.cu_info.simd_per_cu * + adev->gfx.cu_info.max_waves_per_simd; +} + const struct kfd2kgd_calls gfx_v12_kfd2kgd = { .init_interrupts = init_interrupts_v12, .hqd_dump = hqd_dump_v12, @@ -381,5 +528,6 @@ const struct kfd2kgd_calls gfx_v12_kfd2kgd = { .set_wave_launch_mode = kgd_gfx_v12_set_wave_launch_mode, .set_address_watch = kgd_gfx_v12_set_address_watch, .clear_address_watch = kgd_gfx_v12_clear_address_watch, - .hqd_sdma_get_doorbell = kgd_gfx_v12_hqd_sdma_get_doorbell + .hqd_sdma_get_doorbell = kgd_gfx_v12_hqd_sdma_get_doorbell, + .get_cu_occupancy = kgd_gfx_v12_get_cu_occupancy, }; From fb62f7f031155fe6c1095d2fc921654ad51f87c2 Mon Sep 17 00:00:00 2001 From: David Belanger Date: Fri, 14 Aug 2026 13:26:17 -0400 Subject: [PATCH 102/121] drm/kfd: Add CU occupancy support to GFX12.1 Port changes from GFX9 to GFX12.1 mostly as-is. Minor changes to register access code. Assisted-by: Claude:Sonnet 4.6 Signed-off-by: David Belanger Reviewed-by: Sreekant Somasekharan Signed-off-by: Alex Deucher --- .../drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c | 150 +++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c index 38ca1aea33b2..070001fd34b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c @@ -371,6 +371,153 @@ static uint32_t kgd_gfx_v12_1_hqd_sdma_get_doorbell(struct amdgpu_device *adev, return 0; } +static void lock_spi_csq_mutexes(struct amdgpu_device *adev) +{ + mutex_lock(&adev->srbm_mutex); + mutex_lock(&adev->grbm_idx_mutex); + +} + +static void unlock_spi_csq_mutexes(struct amdgpu_device *adev) +{ + mutex_unlock(&adev->grbm_idx_mutex); + mutex_unlock(&adev->srbm_mutex); +} + +/** + * get_wave_count: Read device registers to get number of waves in flight for + * a particular queue. The method also returns the doorbell offset associated + * with the queue. + * + * @adev: Handle of device whose registers are to be read + * @queue_idx: Index of queue in the queue-map bit-field + * @queue_cnt: Stores the wave count and doorbell offset for an active queue + * @inst: xcc's instance number on a multi-XCC setup + */ +static void get_wave_count(struct amdgpu_device *adev, int queue_idx, + struct kfd_cu_occupancy *queue_cnt, uint32_t inst) +{ + int pipe_idx; + int queue_slot; + unsigned int reg_val; + unsigned int wave_cnt; + /* + * Program GRBM with appropriate MEID, PIPEID, QUEUEID and VMID + * parameters to read out waves in flight. Get doorbell offset if there are + * non-zero waves in flight. + */ + pipe_idx = queue_idx / adev->gfx.mec.num_queue_per_pipe; + queue_slot = queue_idx % adev->gfx.mec.num_queue_per_pipe; + amdgpu_gfx_select_me_pipe_q(adev, 1, pipe_idx, queue_slot, 0, inst); + reg_val = RREG32_SOC15_IP(GC, SOC15_REG_OFFSET(GC, GET_INST(GC, inst), + regSPI_CSQ_WF_ACTIVE_COUNT_0) + queue_slot); + wave_cnt = reg_val & SPI_CSQ_WF_ACTIVE_COUNT_0__COUNT_MASK; + if (wave_cnt != 0) { + queue_cnt->wave_cnt += wave_cnt; + queue_cnt->doorbell_off = + (RREG32_SOC15(GC, GET_INST(GC, inst), regCP_HQD_PQ_DOORBELL_CONTROL) & + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >> + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + } +} + +/** + * kgd_gfx_v12_1_get_cu_occupancy: Reads relevant registers associated with + * each shader engine and aggregates the number of waves that are in flight + * for the process whose pasid is provided as a parameter. The process could + * have ZERO or more queues running and submitting waves to compute units. + * + * @adev: Handle of device from which to get number of waves in flight + * @cu_occupancy: Array that gets filled with wave_cnt and doorbell offset + * for comparison later. + * @max_waves_per_cu: Output parameter updated with maximum number of waves + * possible per Compute Unit + * @inst: xcc's instance number on a multi-XCC setup + * + * Note: It's possible that the device has too many queues (oversubscription) + * in which case a VMID could be remapped to a different PASID. This could lead + * to an inaccurate wave count. Following is a high-level sequence: + * Time T1: vmid = getVmid(); vmid is associated with Pasid P1 + * Time T2: passId = getPasId(vmid); vmid is associated with Pasid P2 + * In the sequence above wave count obtained from time T1 will be incorrectly + * lost or added to total wave count. + * + * The registers that provide the waves in flight are: + * + * SPI_CSQ_WF_ACTIVE_STATUS - bit-map of queues per pipe. The bit is ON if a + * queue is slotted, OFF if there is no queue. A process could have ZERO or + * more queues slotted and submitting waves to be run on compute units. Even + * when there is a queue it is possible there could be zero wave fronts, this + * can happen when queue is waiting on top-of-pipe events - e.g. waitRegMem + * command + * + * For each bit that is ON from above: + * + * Read (SPI_CSQ_WF_ACTIVE_COUNT_0 + queue_idx) register. It provides the + * number of waves that are in flight for the queue at specified index. The + * index ranges from 0 to 7. + * + * If non-zero waves are in flight, store the corresponding doorbell offset + * of the queue, along with the wave count. + * + * Determine if the queue belongs to the process by comparing the doorbell + * offset against the process's queues. If it matches, aggregate the wave + * count for the process. + * + * Reading registers referenced above involves programming GRBM appropriately + */ +static void kgd_gfx_v12_1_get_cu_occupancy(struct amdgpu_device *adev, + struct kfd_cu_occupancy *cu_occupancy, + int *max_waves_per_cu, uint32_t inst) +{ + int qidx; + int se_idx; + int se_cnt; + int queue_map; + int max_queue_cnt; + DECLARE_BITMAP(cp_queue_bitmap, AMDGPU_MAX_QUEUES); + + lock_spi_csq_mutexes(adev); + amdgpu_gfx_select_me_pipe_q(adev, 1, 0, 0, 0, inst); + + /* + * Iterate through the shader engines and arrays of the device + * to get number of waves in flight + */ + bitmap_complement(cp_queue_bitmap, adev->gfx.mec_bitmap[0].queue_bitmap, + AMDGPU_MAX_QUEUES); + max_queue_cnt = adev->gfx.mec.num_pipe_per_mec * + adev->gfx.mec.num_queue_per_pipe; + se_cnt = adev->gfx.config.max_shader_engines; + for (se_idx = 0; se_idx < se_cnt; se_idx++) { + amdgpu_gfx_select_se_sh(adev, se_idx, 0, 0xffffffff, inst); + queue_map = RREG32_SOC15(GC, GET_INST(GC, inst), + regSPI_CSQ_WF_ACTIVE_STATUS); + + for (qidx = 0; qidx < max_queue_cnt; qidx++) { + /* Skip queues that are not associated with + * compute functions + */ + if (!test_bit(qidx, cp_queue_bitmap)) + continue; + + if (!(queue_map & (1 << qidx))) + continue; + + /* Get number of waves in flight and aggregate them */ + get_wave_count(adev, qidx, &cu_occupancy[qidx], inst); + } + } + + amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, inst); + amdgpu_gfx_select_me_pipe_q(adev, 0, 0, 0, 0, inst); + unlock_spi_csq_mutexes(adev); + + /* Update the output parameters and return */ + *max_waves_per_cu = adev->gfx.cu_info.simd_per_cu * + adev->gfx.cu_info.max_waves_per_simd; +} + const struct kfd2kgd_calls gfx_v12_1_kfd2kgd = { .init_interrupts = init_interrupts_v12_1, .hqd_dump = hqd_dump_v12_1, @@ -384,5 +531,6 @@ const struct kfd2kgd_calls gfx_v12_1_kfd2kgd = { .set_wave_launch_mode = kgd_gfx_v12_1_set_wave_launch_mode, .set_address_watch = kgd_gfx_v12_1_set_address_watch, .clear_address_watch = kgd_gfx_v12_1_clear_address_watch, - .hqd_sdma_get_doorbell = kgd_gfx_v12_1_hqd_sdma_get_doorbell + .hqd_sdma_get_doorbell = kgd_gfx_v12_1_hqd_sdma_get_doorbell, + .get_cu_occupancy = kgd_gfx_v12_1_get_cu_occupancy }; From 2ee9836545e690c9e66ec203445d7705959fd7a8 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Thu, 20 Aug 2026 16:32:51 +0200 Subject: [PATCH 103/121] drm/amdgpu: Fix VCE 3 ring align_mask The largest frame is 20 dwords, so 0xf mask is too small. This was always wrong, but we were lucky with the VCE_CMD_END commands inserted after fence and vm_flush. Fixes: 8897ea8c761b ("drm/amdgpu: Implement insert_end for VCE 3") Cc: stable@vger.kernel.org Acked-by: Alex Deucher Signed-off-by: David Rosca Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/vce_v3_0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c index a9497e2e07f7..4dbbeaf97ad1 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c @@ -891,7 +891,7 @@ static const struct amdgpu_ring_funcs vce_v3_0_ring_phys_funcs = { static const struct amdgpu_ring_funcs vce_v3_0_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCE, - .align_mask = 0xf, + .align_mask = 0x1f, .nop = VCE_CMD_NO_OP, .support_64bit_ptrs = false, .no_user_fence = true, From 40ba09e11188d1b7f79d51fc28aca5ea45e0c138 Mon Sep 17 00:00:00 2001 From: Sunday Clement Date: Thu, 6 Aug 2026 10:59:34 -0400 Subject: [PATCH 104/121] drm/amdkfd: Reject zero-sized AQL queue allocations after size halving KFD_IOC_ALLOC_MEMORY_OF_GPU with flag KFD_IOC_ALLOC_MEM_FLAGS_AQL_QUEUE_MEM and size=1 triggers the AQL wraparound workaround (size >>= 1), reducing size to 0. The resulting zero passes through PAGE_ALIGN(0) = 0 without validation, bypassing the per-process VRAM quota check in reserve_mem_limit() (vram_used + 0 > vram_available is always false). The fix adds post-halving zero-size validation in the primary allocation path (amdgpu_amdkfd_gpuvm.c). The check happens after size halving but before reserve_mem_limit(), and uses err_alignment_size error path to properly clean up the allocated kgd_mem structure and mutex. Cc: stable@vger.kernel.org Signed-off-by: Sunday Clement Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 7 +++++++ drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 34481ee7065a..d66881684ee5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -1795,6 +1795,12 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu( size >>= 1; aligned_size = PAGE_ALIGN(size); + /* reject AQL queue with size < 2 */ + if (!aligned_size) { + ret = -EINVAL; + goto err_alignment_size; + } + (*mem)->alloc_flags = flags; amdgpu_sync_create(&(*mem)->sync); @@ -1886,6 +1892,7 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu( amdgpu_amdkfd_unreserve_mem_limit(adev, aligned_size, flags, xcp_id); err_reserve_limit: amdgpu_sync_free(&(*mem)->sync); +err_alignment_size: mutex_destroy(&(*mem)->lock); if (gobj) drm_gem_object_put(gobj); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 6fd18488d5cf..7fcfc150a7fc 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1200,7 +1200,8 @@ static int kfd_ioctl_alloc_memory_of_gpu(struct file *filep, if (flags & KFD_IOC_ALLOC_MEM_FLAGS_AQL_QUEUE_MEM) size >>= 1; - atomic64_add(PAGE_ALIGN(size), &pdd->vram_usage); + size = PAGE_ALIGN(size); + atomic64_add(size, &pdd->vram_usage); } mutex_unlock(&p->mutex); From b30900566642ceb2c9e12b56c2afec28d0fd91a0 Mon Sep 17 00:00:00 2001 From: Xiang Liu Date: Fri, 21 Aug 2026 17:41:57 +0800 Subject: [PATCH 105/121] drm/amdgpu: clamp the isolation index for rings outside a partition adev->isolation[] has one slot per partition, but a ring that is not assigned to one keeps AMDGPU_XCP_NO_PARTITION, which is ~0, so indexing the array with it is out of bounds. SDMA submissions hit this on both the isolation enforcement and the VM flush path and trip UBSAN. Fall back to the first slot the way the cleaner shader path already does, and stop taking the address before the ring type check that makes it relevant. Cc: stable@vger.kernel.org Signed-off-by: Xiang Liu Reviewed-by: Hawking Zhang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 5 ++++- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 019581577603..44bed0ba64a3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -6687,8 +6687,8 @@ struct dma_fence *amdgpu_device_enforce_isolation(struct amdgpu_device *adev, struct amdgpu_ring *ring, struct amdgpu_job *job) { - struct amdgpu_isolation *isolation = &adev->isolation[ring->xcp_id]; struct drm_sched_fence *f = job->base.s_fence; + struct amdgpu_isolation *isolation; struct dma_fence *dep; void *owner; int r; @@ -6701,6 +6701,9 @@ struct dma_fence *amdgpu_device_enforce_isolation(struct amdgpu_device *adev, ring->funcs->type != AMDGPU_RING_TYPE_COMPUTE) return NULL; + isolation = &adev->isolation[ring->xcp_id == AMDGPU_XCP_NO_PARTITION ? + 0 : ring->xcp_id]; + /* * All submissions where enforce isolation is false are handled as if * they come from a single client. Use ~0l as the owner to distinct it diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index a3758c654dd4..aedf72c2333e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -776,7 +776,9 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool *emit_gds_needed) { struct amdgpu_device *adev = ring->adev; - struct amdgpu_isolation *isolation = &adev->isolation[ring->xcp_id]; + struct amdgpu_isolation *isolation = + &adev->isolation[ring->xcp_id == AMDGPU_XCP_NO_PARTITION ? + 0 : ring->xcp_id]; unsigned vmhub = ring->vm_hub; struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; struct amdgpu_vmid *id = &id_mgr->ids[job->vmid]; From 52536ce677a3470c0e5323b940791efb33975450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Fri, 21 Aug 2026 23:50:58 +0200 Subject: [PATCH 106/121] drm/amd/display: Fix HPD consideration for VGA/LVDS connectors on DCE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After a refactor that landed in Linux 7.0, DC now crashes when it is initialized on GPUs that have a VGA or LVDS connector. This is because these connectors have no HPD so the hpd_gpio is NULL and therefore DC takes the code path meant for DCN 4.2+ which sets irq_source_hpd = 255 that causes the subsequent code to try to register the HPD interrupt, which fails, and causes a crash. This commit should be backported to Linux 7.0 and newer. Cc: stable@vger.kernel.org Cc: Dmytro Laktyushkin Cc: Roman Li Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5490 Fixes: def3488eb0fd ("drm/amd/display: refactor HPD to increase flexibility") Signed-off-by: Timur Kristóf Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260821215059.312868-1-timur.kristof@gmail.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/link/link_factory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index 89265b083935..7146f8356233 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -639,7 +639,7 @@ static bool construct_phy(struct dc_link *link, DC_LOG_DC("BIOS object table - hpd_gpio id: %d", enc_init_data.hpd_gpio->id); DC_LOG_DC("BIOS object table - hpd_gpio en: %d", enc_init_data.hpd_gpio->en); - } else { + } else if (link->ctx->dce_version > DCN_VERSION_4_01) { struct graphics_object_hpd_info hpd_info; if (link->ctx->dc_bios->funcs->get_hpd_info(link->ctx->dc_bios, link->link_id, &hpd_info) == BP_RESULT_OK) { From 9e8bcfde0039238e904b4e720e66b7f4fbf82c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Fri, 21 Aug 2026 23:50:59 +0200 Subject: [PATCH 107/121] drm/amd/display: Log details when failing to register HPD IRQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should help diagnose HPD IRQ related issues in the future. Signed-off-by: Timur Kristóf Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/20260821215059.312868-2-timur.kristof@gmail.com Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c index 9be63996b062..d0239a3de2e1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c @@ -1735,7 +1735,9 @@ int amdgpu_dm_register_hpd_handlers(struct amdgpu_device *adev) if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || int_params.irq_source < DC_IRQ_SOURCE_HPD1 || int_params.irq_source > DC_IRQ_SOURCE_HPD6) { - drm_err(adev_to_drm(adev), "Failed to register hpd irq!\n"); + drm_err(adev_to_drm(adev), + "Failed to register hpd irq %u for %s!\n", + int_params.irq_source, connector->name); return -EINVAL; } @@ -1753,7 +1755,9 @@ int amdgpu_dm_register_hpd_handlers(struct amdgpu_device *adev) if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || int_params.irq_source < DC_IRQ_SOURCE_HPD1RX || int_params.irq_source > DC_IRQ_SOURCE_HPD6RX) { - drm_err(adev_to_drm(adev), "Failed to register hpd rx irq!\n"); + drm_err(adev_to_drm(adev), + "Failed to register hpd rx irq %u for %s!\n", + int_params.irq_source, connector->name); return -EINVAL; } From 960c4a8069bfd352c48cc88592618f1ebe24c69e Mon Sep 17 00:00:00 2001 From: Xiaogang Chen Date: Sun, 23 Aug 2026 15:22:54 -0500 Subject: [PATCH 108/121] drm/amdkfd: Fix error path at svm_migrate_copy_to_ram If page migration from device to sys ram fails for some reasons driver needs release and unlock allocated system pages. To do that driver should use page physical address, or pfn, then get struct page*. Current driver uses dma address(for adev) that is not correct with IOMMU enabled, or even in general. The patch releases and unlocks allocated system pages based on where migration failed by struct page* of sys ram pages. Also dma_unmap correspodent system ram pages at error path. Cc: stable@vger.kernel.org Signed-off-by: Xiaogang Chen Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 45 ++++++++++++++++-------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index f5af1dd3b70e..a6bb41fdc8c1 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -258,15 +258,6 @@ svm_migrate_get_sys_page(struct vm_area_struct *vma, unsigned long addr) return page; } -static void svm_migrate_put_sys_page(unsigned long addr) -{ - struct page *page; - - page = pfn_to_page(addr >> PAGE_SHIFT); - unlock_page(page); - put_page(page); -} - static unsigned long svm_migrate_successful_pages(struct migrate_vma *migrate) { unsigned long mpages = 0; @@ -591,9 +582,10 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, dma_addr_t *scratch, u64 npages) { struct device *dev = adev->dev; - u64 *src; + struct page *dpage = NULL; dma_addr_t *dst; - struct page *dpage; + u64 *src; + u64 i = 0, j; u64 addr; int r = 0; @@ -647,6 +639,7 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, r = dma_mapping_error(dev, dst[i]); if (r) { dev_err(adev->dev, "%s: fail %d dma_map_page\n", __func__, r); + dst[i] = 0; goto out_oom; } @@ -654,17 +647,39 @@ svm_migrate_copy_to_ram(struct amdgpu_device *adev, struct svm_range *prange, dst[i] >> PAGE_SHIFT, page_to_pfn(dpage)); migrate->dst[i] = migrate_pfn(page_to_pfn(dpage)); + + dpage = NULL; j++; } - r = svm_migrate_copy_memory_gart(adev, dst + i - j, src + i - j, j, - FROM_VRAM_TO_RAM, mfence); - + if (j > 0) + r = svm_migrate_copy_memory_gart(adev, dst + i - j, src + i - j, j, + FROM_VRAM_TO_RAM, mfence); out_oom: if (r) { pr_debug("failed %d copy to ram\n", r); + + /* first release current dpage when dma_map_page fail */ + if (dpage) { + unlock_page(dpage); + put_page(dpage); + } + + /* release previous allocated sys pages and unmap dma address */ while (i--) { - svm_migrate_put_sys_page(dst[i]); + + if (dst[i]) { + dma_unmap_page(dev, dst[i], PAGE_SIZE, + DMA_BIDIRECTIONAL); + dst[i] = 0; + } + + dpage = migrate_pfn_to_page(migrate->dst[i]); + if (!dpage) + continue; + + unlock_page(dpage); + put_page(dpage); migrate->dst[i] = 0; } } From 520e345ffe05aabef1db82beda4288afb1757ff2 Mon Sep 17 00:00:00 2001 From: Xiaogang Chen Date: Sun, 23 Aug 2026 15:47:15 -0500 Subject: [PATCH 109/121] drm/amdkfd: Fix the case that vm range is hole at svm_migrate_copy_to_vram When migration vm range is hole at cpu side(MIGRATE_PFN_MIGRATE set + MIGRATE_PFN_VALID unset) driver still allocates device pages. There is no dma map of src pages and migration. j is 0 and svm_migrate_copy_memory_gart() will return an uninitialized r. That can trigger out_free_vram_pages to drop all VRAM just set up. Initialize r and only call the last svm_migrate_copy_memory_gart if j > 0. Current code postponed the last page to the final copy. This patch flushes on the last page when reach to the end of current drm_buddy_block; avoids another svm_migrate_copy_memory_gart. Cc: stable@vger.kernel.org Signed-off-by: Xiaogang Chen Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index a6bb41fdc8c1..253365a8257e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -134,7 +134,7 @@ svm_migrate_copy_memory_gart(struct amdgpu_device *adev, dma_addr_t *sys, u64 gart_s, gart_d; struct dma_fence *next; u64 size; - int r; + int r = 0; ring = to_amdgpu_ring(adev->mman.buffer_funcs_scheds[0]); entity = &adev->mman.move_entities[0]; @@ -284,7 +284,7 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, dma_addr_t *src; u64 *dst; u64 i, j; - int r; + int r = 0; pr_debug("svms 0x%p [0x%lx 0x%lx 0x%llx]\n", prange->svms, prange->start, prange->last, ttm_res_offset); @@ -310,6 +310,7 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, DMA_BIDIRECTIONAL); r = dma_mapping_error(dev, src[i]); if (r) { + src[i] = 0; dev_err(dev, "%s: fail %d dma_map_page\n", __func__, r); goto out_free_vram_pages; @@ -334,7 +335,8 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, pr_debug_ratelimited("dma mapping src to 0x%llx, pfn 0x%lx\n", src[i] >> PAGE_SHIFT, page_to_pfn(spage)); - if (j >= (cursor.size >> PAGE_SHIFT) - 1 && i < npages - 1) { + /* accumulated j + 1 pages reach end of current drm_buddy_block */ + if (j + 1 >= (cursor.size >> PAGE_SHIFT)) { r = svm_migrate_copy_memory_gart(adev, src + i - j, dst + i - j, j + 1, FROM_RAM_TO_VRAM, @@ -348,7 +350,8 @@ svm_migrate_copy_to_vram(struct kfd_node *node, struct svm_range *prange, } } - r = svm_migrate_copy_memory_gart(adev, src + i - j, dst + i - j, j, + if (j > 0) + r = svm_migrate_copy_memory_gart(adev, src + i - j, dst + i - j, j, FROM_RAM_TO_VRAM, mfence); out_free_vram_pages: From a04ea08ddb516f9f21f17574b6a2e7b540dfadf8 Mon Sep 17 00:00:00 2001 From: Prike Liang Date: Fri, 7 Aug 2026 09:46:22 +0800 Subject: [PATCH 110/121] drm/amdgpu/userq: fix lock missing for userq fence error set amdgpu_userq_fence_driver() and amdgpu_userq_fence_driver_destroy() don't acquire the dma_fence spinlock, so locking the dma_fence lock before test the signaled state and set error state to avoid missing lock assert error. Signed-off-by: Prike Liang Acked-by: Alex Deucher Signed-off-by: Alex Deucher --- .../gpu/drm/amd/amdgpu/amdgpu_userq_fence.c | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index a33dbe978798..4b023e024d9f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -191,14 +191,15 @@ void amdgpu_userq_fence_driver_destroy(struct kref *ref) struct dma_fence *f; spin_lock_irqsave(&fence_drv->fence_list_lock, flags); + lockdep_assert_held(&fence_drv->fence_list_lock); list_for_each_entry_safe(fence, tmp, &fence_drv->fences, link) { f = &fence->base; - - if (!dma_fence_is_signaled(f)) { + spin_lock(dma_fence_spinlock(f)); + if (!dma_fence_is_signaled_locked(f)) { dma_fence_set_error(f, -ECANCELED); - dma_fence_signal(f); + dma_fence_signal_locked(f); } - + spin_unlock(dma_fence_spinlock(f)); list_del(&fence->link); dma_fence_put(f); } @@ -423,11 +424,16 @@ amdgpu_userq_fence_driver_set_error(struct amdgpu_userq_fence *fence, struct dma_fence *f; spin_lock_irqsave(&fence_drv->fence_list_lock, flags); - + lockdep_assert_held(&fence_drv->fence_list_lock); f = rcu_dereference_protected(&fence->base, lockdep_is_held(&fence_drv->fence_list_lock)); - if (f && !dma_fence_is_signaled_locked(f)) - dma_fence_set_error(f, error); + if (f) { + /* nest f->lock inside fence_list_lock */ + spin_lock(dma_fence_spinlock(f)); + if (!dma_fence_is_signaled_locked(f)) + dma_fence_set_error(f, error); + spin_unlock(dma_fence_spinlock(f)); + } spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags); } From 6aa530642f95d5c48aa336416f94a35e7949b647 Mon Sep 17 00:00:00 2001 From: Vladimir Marioukhine Date: Wed, 12 Aug 2026 12:58:12 -0400 Subject: [PATCH 111/121] drm/amdkfd: guard against NULL restore_mqd in CRIU queue restore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both create_queue_cpsch() and create_queue_nocpsch() unconditionally call mqd_mgr->restore_mqd() when a CRIU restore is in progress (qd != NULL), with no NULL guard. On any system where restore_mqd is not implemented for the given queue type, a user holding CAP_CHECKPOINT_RESTORE can trigger a kernel NULL pointer dereference and panic the machine by issuing KFD_IOC_CRIU_OP_RESTORE with a crafted queue restore object. Note that checkpoint_mqd is likewise unimplemented on GFX12, so no legitimate CRIU image can reach this path — only a hand-crafted restore payload. Add a NULL guard for restore_mqd immediately after mqd_mgr is resolved, unwinding via the existing error labels and returning -EOPNOTSUPP if the callback is not implemented. This mirrors the existing checkpoint_mqd guard in checkpoint_mqd(). Fixes: 48f0bdf4e38e ("drm/amdkfd: Added MQD manager files for GFX12.") Cc: stable@vger.kernel.org Signed-off-by: Vladimir Marioukhine Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index a23384571193..4bc947c3bd0d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -769,6 +769,11 @@ static int create_queue_nocpsch(struct device_queue_manager *dqm, mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type( q->properties.type)]; + if (qd && !mqd_mgr->restore_mqd) { + pr_debug("restore_mqd not implemented for this GPU\n"); + retval = -EOPNOTSUPP; + goto deallocate_vmid; + } if (q->properties.type == KFD_QUEUE_TYPE_COMPUTE) { retval = allocate_hqd(dqm, q); if (retval) @@ -2236,6 +2241,11 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q, mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type( q->properties.type)]; + if (qd && !mqd_mgr->restore_mqd) { + pr_debug("restore_mqd not implemented for this GPU\n"); + retval = -EOPNOTSUPP; + goto out_deallocate_doorbell; + } if (q->properties.type == KFD_QUEUE_TYPE_SDMA || q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI) From fdc290ff4ab19c7e0dde36c4cd1e2771b61f6bf5 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Thu, 20 Aug 2026 15:29:43 +0530 Subject: [PATCH 112/121] drm/amd/display: Fix wrong bytes-per-pixel value for dml2_422_packed_10 The pixel format dml2_422_packed_10 needs BytePerPixelDETY set to 8.0/3. But it was accidentally placed in the wrong group that sets it to 4, so the correct value was never used. This caused wrong DET buffer size and bandwidth calculations whenever this format was used. Fix it by moving dml2_422_packed_10 out of the wrong group so it gets the correct value of 8.0/3. Fixes: 7f7d7ea1fa51 ("drm/amd/display: Add new sources for DCN6") Reported-by: Dan Carpenter Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Aurabindo Pillai Signed-off-by: Srinivasan Shanmugam Reviewed-by: George Zhang Signed-off-by: Alex Deucher --- .../dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c index 05a99c4f761b..38ccf9dab31f 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c @@ -74,7 +74,9 @@ void dcn5_calculate_byte_per_pixel_and_block_sizes( *BytePerPixelDETC = 0; *BytePerPixelY = 8; *BytePerPixelC = 0; - } else if (SourcePixelFormat == dml2_444_32 || SourcePixelFormat == dml2_rgbe || SourcePixelFormat == dml2_422_packed_10 || SourcePixelFormat == dml2_422_packed_12) { + } else if (SourcePixelFormat == dml2_444_32 || + SourcePixelFormat == dml2_rgbe || + SourcePixelFormat == dml2_422_packed_12) { *BytePerPixelDETY = 4; *BytePerPixelDETC = 0; *BytePerPixelY = 4; From 84298acf1c8be2b1b03c0339bf1eb63102c51728 Mon Sep 17 00:00:00 2001 From: Srinivasan Shanmugam Date: Thu, 20 Aug 2026 15:46:01 +0530 Subject: [PATCH 113/121] drm/amd/display: Fix redundant GPUVMEnable checks in dcn6 flip schedule Inside dcn6_calculate_flip_schedule(), GPUVMEnable is already checked in the outer if block. But the same GPUVMEnable is checked again in two inner if blocks inside it. Since GPUVMEnable is always true at that point, the inner else branches that assign meta_row_height are never reached. Remove the redundant inner GPUVMEnable checks and directly assign dpte_row_height, which is always the correct value here. Fixes: 7f7d7ea1fa51 ("drm/amd/display: Add new sources for DCN6") Reported-by: Dan Carpenter Cc: Roman Li Cc: Alex Hung Cc: Tom Chung Cc: Aurabindo Pillai Signed-off-by: Srinivasan Shanmugam Reviewed-by: George Zhang Signed-off-by: Alex Deucher --- .../src/dml2_core/dml2_core_dcn6_calcs_dchub.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c index cae6bee93fe3..a4e2f8604650 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c @@ -569,20 +569,11 @@ void dcn6_calculate_flip_schedule( if (GPUVMEnable) { if (l->dual_plane) { - if (GPUVMEnable) { - l->min_row_height = dpte_row_height; - l->min_row_height_chroma = dpte_row_height_chroma; - } else { - l->min_row_height = meta_row_height; - l->min_row_height_chroma = meta_row_height_chroma; - } + l->min_row_height = dpte_row_height; + l->min_row_height_chroma = dpte_row_height_chroma; l->min_row_time = math_min2(l->min_row_height * LineTime / VRatio, l->min_row_height_chroma * LineTime / VRatioChroma); } else { - if (GPUVMEnable) - l->min_row_height = dpte_row_height; - else - l->min_row_height = meta_row_height; - + l->min_row_height = dpte_row_height; l->min_row_time = l->min_row_height * LineTime / VRatio; } DML_LOG_VERBOSE("DML::%s: min_row_time = %f\n", __func__, l->min_row_time); From 92a9eebd2a1f892fe482154d83f9f1626bc73d3b Mon Sep 17 00:00:00 2001 From: Linkai Gong Date: Wed, 19 Aug 2026 13:47:42 +0800 Subject: [PATCH 114/121] drm/amd/display: fix dc_lock leak on GPU reset error paths On GPU reset, dm_suspend() takes dc_lock and leaves it for dm_resume() to drop. If amdgpu_dm_commit_zero_streams() or dm_dmub_hw_init() fails, the function returns with the lock still held. The matching resume path is then skipped, so every later dc_lock take hangs. Release the cached DC state and unlock before returning the error. Fixes: 3cf7a0bc87f0 ("drm/amd/display: Catch failures for amdgpu_dm_commit_zero_streams()") Fixes: 2b6943df5413 ("drm/amd/display: Pass up errors for reset GPU that fails to init HW") Cc: stable@vger.kernel.org Signed-off-by: Linkai Gong Reviewed-by: Mario Limonciello Signed-off-by: Mario Limonciello Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 6 ++++++ 1 file changed, 6 insertions(+) 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 284aac4d96bc..ec483276d753 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1589,6 +1589,9 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block) res = amdgpu_dm_commit_zero_streams(dm->dc); if (res != DC_OK) { drm_err(adev_to_drm(adev), "Failed to commit zero streams: %d\n", res); + dc_state_release(dm->cached_dc_state); + dm->cached_dc_state = NULL; + mutex_unlock(&dm->dc_lock); return -EINVAL; } @@ -1884,6 +1887,9 @@ static int dm_resume(struct amdgpu_ip_block *ip_block) r = dm_dmub_hw_init(adev); if (r) { drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r); + dc_state_release(dm->cached_dc_state); + dm->cached_dc_state = NULL; + mutex_unlock(&dm->dc_lock); return r; } From f0feab6e9e008faa406d189fb40cb7ab1bed420a Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Tue, 18 Aug 2026 21:35:20 +0000 Subject: [PATCH 115/121] drm/xe: Do not apply WA 14025883347 to media 3503 The database was updated and the WA is no longer listed as applicable to media 3503, so don't enable it there. Fixes: c57db41b8d2c ("drm/xe/guc: Add Wa_14025883347 for GuC DMA failure on reset") Signed-off-by: Daniele Ceraolo Spurio Cc: Sk Anirban Cc: Badal Nilawar Cc: Matt Roper Reviewed-by: Matt Roper Link: https://patch.msgid.link/20260818213520.283063-1-daniele.ceraolospurio@intel.com (cherry picked from commit fae59d5de5de39bc51ac2839f74970312e0c8905) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_wa_oob.rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules index f02ac9bf7424..dd69ad07f7a9 100644 --- a/drivers/gpu/drm/xe/xe_wa_oob.rules +++ b/drivers/gpu/drm/xe/xe_wa_oob.rules @@ -63,7 +63,7 @@ 16026007364 MEDIA_VERSION(3000) 14020316580 MEDIA_VERSION(1301) -14025883347 MEDIA_VERSION_RANGE(1301, 3503) +14025883347 MEDIA_VERSION_RANGE(1301, 3500) GRAPHICS_VERSION_RANGE(2004, 3005) 16029380221 MEDIA_VERSION(3500) 22022079272 MEDIA_VERSION(3503) From 369ba0d1efe91cccabe98ae53c53b7425f327edf Mon Sep 17 00:00:00 2001 From: Balasubramani Vivekanandan Date: Wed, 19 Aug 2026 13:04:58 +0530 Subject: [PATCH 116/121] drm/xe/xe_gt_idle: Add CCS to the powergating info print While reading the main GT powergating info from debugfs, include both RCS and CCS engine masks. Fixes: 0914c1e45d3a1 ("drm/xe/xe_gt_idle: add debugfs entry for powergating info") Signed-off-by: Balasubramani Vivekanandan Link: https://patch.msgid.link/20260819073457.1812722-2-balasubramani.vivekanandan@intel.com Reviewed-by: Matt Roper Signed-off-by: Matt Roper (cherry picked from commit 8899e413c5ab85443ec9bbc50cffe924c6b596de) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_gt_idle.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_gt_idle.c b/drivers/gpu/drm/xe/xe_gt_idle.c index 04b24e1c8b78..7dc9873aef54 100644 --- a/drivers/gpu/drm/xe/xe_gt_idle.c +++ b/drivers/gpu/drm/xe/xe_gt_idle.c @@ -248,7 +248,8 @@ int xe_gt_idle_pg_print(struct xe_gt *gt, struct drm_printer *p) pg_status = xe_mmio_read32(>->mmio, POWERGATE_DOMAIN_STATUS); } - if (gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK) { + if (gt->info.engine_mask & + (XE_HW_ENGINE_RCS_MASK | XE_HW_ENGINE_CCS_MASK)) { drm_printf(p, "Render Power Gating Enabled: %s\n", str_yes_no(pg_enabled & RENDER_POWERGATE_ENABLE)); From 5e977521d21717edb8e91d004434697d6e3f248c Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 20 Aug 2026 12:24:45 +0530 Subject: [PATCH 117/121] drm/xe: Reject page faults from non-fault-mode scratch VMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having scratch enabled does not make a VM capable of handling recoverable page faults. Allowing scratch VMs through the ASID lookup also admits dma-fence mode VMs. If such a VM faults on an already valid VMA, the handler reports success without fixing the fault, causing the GPU to retry indefinitely. Only allow fault-mode VMs through the ASID lookup. Fault-mode VMs using scratch remain supported, while faults from 3D VMs are rejected. Fixes: ad9843aac91a ("drm/xe/madvise: Implement purgeable buffer object support") Cc: Matthew Brost Cc: Thomas Hellström Cc: Himal Prasad Ghimiray Suggested-by: Matthew Brost Signed-off-by: Arvind Yadav Reviewed-by: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260820065445.567228-1-arvind.yadav@intel.com (cherry picked from commit bfb24a06405b652d37831f3fb66b71d33a6605de) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pagefault.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c index dd3c068e1a39..dbf8f71d3328 100644 --- a/drivers/gpu/drm/xe/xe_pagefault.c +++ b/drivers/gpu/drm/xe/xe_pagefault.c @@ -158,7 +158,7 @@ static struct xe_vm *xe_pagefault_asid_to_vm(struct xe_device *xe, u32 asid) down_read(&xe->usm.lock); vm = xa_load(&xe->usm.asid_to_vm, asid); - if (vm && (xe_vm_in_fault_mode(vm) || xe_vm_has_scratch(vm))) + if (vm && xe_vm_in_fault_mode(vm)) xe_vm_get(vm); else vm = ERR_PTR(-EINVAL); From 874ef9a6f2fc45d3f6021842d92fa5420fa4c825 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Aug 2026 14:10:06 +0200 Subject: [PATCH 118/121] i2c: designware: Global register definitions Moving the register definitions to a global header file include/linux/designware_i2c.h. That removes the need to duplicate them in the adaptation layers for this driver outside of drivers/i2c/busses/. There is at least one of those in drivers/gpu/drm/xe/xe_i2c.c. Suggested-by: Andy Shevchenko Suggested-by: Raag Jadav Reviewed-by: Raag Jadav Reviewed-by: Mika Westerberg Reviewed-by: Andy Shevchenko Signed-off-by: Heikki Krogerus Acked-by: Mika Westerberg Link: https://patch.msgid.link/20260811121008.1493015-2-heikki.krogerus@linux.intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 2ab2fb31411a494e4579dfacda986a2672f80e65) Signed-off-by: Rodrigo Vivi --- MAINTAINERS | 1 + drivers/i2c/busses/i2c-designware-common.c | 2 + drivers/i2c/busses/i2c-designware-core.h | 85 +--------------- drivers/i2c/busses/i2c-designware-master.c | 2 + drivers/i2c/busses/i2c-designware-slave.c | 2 + include/linux/designware_i2c.h | 107 +++++++++++++++++++++ 6 files changed, 116 insertions(+), 83 deletions(-) create mode 100644 include/linux/designware_i2c.h diff --git a/MAINTAINERS b/MAINTAINERS index 928b3ba23a76..1d0685b2cfdb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26253,6 +26253,7 @@ R: Andy Shevchenko L: linux-i2c@vger.kernel.org S: Supported F: drivers/i2c/busses/i2c-designware-* +F: include/linux/designware_i2c.h SYNOPSYS DESIGNWARE I2C DRIVER - AMDISP M: Nirujogi Pratap diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index e4dfa2ec58bb..a1eca6cd4b75 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -33,6 +33,8 @@ #include #include +#include + #include "i2c-designware-core.h" #define DW_IC_DEFAULT_BUS_CAPACITANCE_pF 100 diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index c71aa2dd368d..2c929a6e8da2 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -18,6 +18,8 @@ #include #include +#include + #define DW_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \ I2C_FUNC_SMBUS_BYTE | \ I2C_FUNC_SMBUS_BYTE_DATA | \ @@ -25,23 +27,6 @@ I2C_FUNC_SMBUS_BLOCK_DATA | \ I2C_FUNC_SMBUS_I2C_BLOCK) -#define DW_IC_CON_MASTER BIT(0) -#define DW_IC_CON_SPEED_STD (1 << 1) -#define DW_IC_CON_SPEED_FAST (2 << 1) -#define DW_IC_CON_SPEED_HIGH (3 << 1) -#define DW_IC_CON_SPEED_MASK GENMASK(2, 1) -#define DW_IC_CON_10BITADDR_SLAVE BIT(3) -#define DW_IC_CON_10BITADDR_MASTER BIT(4) -#define DW_IC_CON_RESTART_EN BIT(5) -#define DW_IC_CON_SLAVE_DISABLE BIT(6) -#define DW_IC_CON_STOP_DET_IFADDRESSED BIT(7) -#define DW_IC_CON_TX_EMPTY_CTRL BIT(8) -#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9) -#define DW_IC_CON_BUS_CLEAR_CTRL BIT(11) - -#define DW_IC_DATA_CMD_DAT GENMASK(7, 0) -#define DW_IC_DATA_CMD_FIRST_DATA_BYTE BIT(11) - /* * Register access parameters */ @@ -55,65 +40,9 @@ #define DW_IC_FIFO_RX_FIELD GENMASK(15, 8) #define DW_IC_FIFO_MIN_DEPTH 2 -/* - * Registers offset - */ -#define DW_IC_CON 0x00 -#define DW_IC_TAR 0x04 -#define DW_IC_SAR 0x08 -#define DW_IC_DATA_CMD 0x10 -#define DW_IC_SS_SCL_HCNT 0x14 -#define DW_IC_SS_SCL_LCNT 0x18 -#define DW_IC_FS_SCL_HCNT 0x1c -#define DW_IC_FS_SCL_LCNT 0x20 -#define DW_IC_HS_SCL_HCNT 0x24 -#define DW_IC_HS_SCL_LCNT 0x28 -#define DW_IC_INTR_STAT 0x2c -#define DW_IC_INTR_MASK 0x30 -#define DW_IC_RAW_INTR_STAT 0x34 -#define DW_IC_RX_TL 0x38 -#define DW_IC_TX_TL 0x3c -#define DW_IC_CLR_INTR 0x40 -#define DW_IC_CLR_RX_UNDER 0x44 -#define DW_IC_CLR_RX_OVER 0x48 -#define DW_IC_CLR_TX_OVER 0x4c -#define DW_IC_CLR_RD_REQ 0x50 -#define DW_IC_CLR_TX_ABRT 0x54 -#define DW_IC_CLR_RX_DONE 0x58 -#define DW_IC_CLR_ACTIVITY 0x5c -#define DW_IC_CLR_STOP_DET 0x60 -#define DW_IC_CLR_START_DET 0x64 -#define DW_IC_CLR_GEN_CALL 0x68 -#define DW_IC_ENABLE 0x6c -#define DW_IC_STATUS 0x70 -#define DW_IC_TXFLR 0x74 -#define DW_IC_RXFLR 0x78 -#define DW_IC_SDA_HOLD 0x7c -#define DW_IC_TX_ABRT_SOURCE 0x80 -#define DW_IC_ENABLE_STATUS 0x9c -#define DW_IC_CLR_RESTART_DET 0xa8 -#define DW_IC_SMBUS_INTR_MASK 0xcc -#define DW_IC_COMP_PARAM_1 0xf4 -#define DW_IC_COMP_VERSION 0xf8 #define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */ -#define DW_IC_COMP_TYPE 0xfc #define DW_IC_COMP_TYPE_VALUE 0x44570140 /* "DW" + 0x0140 */ -#define DW_IC_INTR_RX_UNDER BIT(0) -#define DW_IC_INTR_RX_OVER BIT(1) -#define DW_IC_INTR_RX_FULL BIT(2) -#define DW_IC_INTR_TX_OVER BIT(3) -#define DW_IC_INTR_TX_EMPTY BIT(4) -#define DW_IC_INTR_RD_REQ BIT(5) -#define DW_IC_INTR_TX_ABRT BIT(6) -#define DW_IC_INTR_RX_DONE BIT(7) -#define DW_IC_INTR_ACTIVITY BIT(8) -#define DW_IC_INTR_STOP_DET BIT(9) -#define DW_IC_INTR_START_DET BIT(10) -#define DW_IC_INTR_GEN_CALL BIT(11) -#define DW_IC_INTR_RESTART_DET BIT(12) -#define DW_IC_INTR_MST_ON_HOLD BIT(13) - #define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \ DW_IC_INTR_TX_ABRT | \ DW_IC_INTR_STOP_DET) @@ -123,16 +52,6 @@ DW_IC_INTR_RX_UNDER | \ DW_IC_INTR_RD_REQ) -#define DW_IC_ENABLE_ENABLE BIT(0) -#define DW_IC_ENABLE_ABORT BIT(1) - -#define DW_IC_STATUS_ACTIVITY BIT(0) -#define DW_IC_STATUS_TFE BIT(2) -#define DW_IC_STATUS_RFNE BIT(3) -#define DW_IC_STATUS_MASTER_ACTIVITY BIT(5) -#define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6) -#define DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY BIT(7) - #define DW_IC_SDA_HOLD_RX_SHIFT 16 #define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, 16) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 7a301c8b604e..a1bcc3797e4f 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -25,6 +25,8 @@ #include #include +#include + #include "i2c-designware-core.h" #define AMD_TIMEOUT_MIN_US 25 diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c index ad0d5fbfa6d5..0abcc7757b23 100644 --- a/drivers/i2c/busses/i2c-designware-slave.c +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -19,6 +19,8 @@ #include #include +#include + #include "i2c-designware-core.h" int i2c_dw_reg_slave(struct i2c_client *slave) diff --git a/include/linux/designware_i2c.h b/include/linux/designware_i2c.h new file mode 100644 index 000000000000..53f37f18a722 --- /dev/null +++ b/include/linux/designware_i2c.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Synopsys DesignWare I2C register definitions + * + * Copyright (C) 2026, Intel Corporation + */ + +#ifndef __LINUX_DESIGNWARE_I2C_H +#define __LINUX_DESIGNWARE_I2C_H + +#include + +/* + * Registers offset + */ +#define DW_IC_CON 0x00 +#define DW_IC_TAR 0x04 +#define DW_IC_SAR 0x08 +#define DW_IC_DATA_CMD 0x10 +#define DW_IC_SS_SCL_HCNT 0x14 +#define DW_IC_SS_SCL_LCNT 0x18 +#define DW_IC_FS_SCL_HCNT 0x1c +#define DW_IC_FS_SCL_LCNT 0x20 +#define DW_IC_HS_SCL_HCNT 0x24 +#define DW_IC_HS_SCL_LCNT 0x28 +#define DW_IC_INTR_STAT 0x2c +#define DW_IC_INTR_MASK 0x30 +#define DW_IC_RAW_INTR_STAT 0x34 +#define DW_IC_RX_TL 0x38 +#define DW_IC_TX_TL 0x3c +#define DW_IC_CLR_INTR 0x40 +#define DW_IC_CLR_RX_UNDER 0x44 +#define DW_IC_CLR_RX_OVER 0x48 +#define DW_IC_CLR_TX_OVER 0x4c +#define DW_IC_CLR_RD_REQ 0x50 +#define DW_IC_CLR_TX_ABRT 0x54 +#define DW_IC_CLR_RX_DONE 0x58 +#define DW_IC_CLR_ACTIVITY 0x5c +#define DW_IC_CLR_STOP_DET 0x60 +#define DW_IC_CLR_START_DET 0x64 +#define DW_IC_CLR_GEN_CALL 0x68 +#define DW_IC_ENABLE 0x6c +#define DW_IC_STATUS 0x70 +#define DW_IC_TXFLR 0x74 +#define DW_IC_RXFLR 0x78 +#define DW_IC_SDA_HOLD 0x7c +#define DW_IC_TX_ABRT_SOURCE 0x80 +#define DW_IC_ENABLE_STATUS 0x9c +#define DW_IC_CLR_RESTART_DET 0xa8 +#define DW_IC_SMBUS_INTR_STAT 0xc8 +#define DW_IC_SMBUS_INTR_MASK 0xcc +#define DW_IC_CLR_SMBUS_INTR 0xd4 +#define DW_IC_COMP_PARAM_1 0xf4 +#define DW_IC_COMP_VERSION 0xf8 +#define DW_IC_COMP_TYPE 0xfc + +/* DW_IC_CON bits */ +#define DW_IC_CON_MASTER BIT(0) +#define DW_IC_CON_SPEED_STD (1 << 1) +#define DW_IC_CON_SPEED_FAST (2 << 1) +#define DW_IC_CON_SPEED_HIGH (3 << 1) +#define DW_IC_CON_SPEED_MASK GENMASK(2, 1) +#define DW_IC_CON_10BITADDR_SLAVE BIT(3) +#define DW_IC_CON_10BITADDR_MASTER BIT(4) +#define DW_IC_CON_RESTART_EN BIT(5) +#define DW_IC_CON_SLAVE_DISABLE BIT(6) +#define DW_IC_CON_STOP_DET_IFADDRESSED BIT(7) +#define DW_IC_CON_TX_EMPTY_CTRL BIT(8) +#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9) +#define DW_IC_CON_BUS_CLEAR_CTRL BIT(11) + +/* DW_IC_DATA_CMD bits */ +#define DW_IC_DATA_CMD_DAT GENMASK(7, 0) +#define DW_IC_DATA_CMD_FIRST_DATA_BYTE BIT(11) + +/* DW_IC_INTR_* bits */ +#define DW_IC_INTR_RX_UNDER BIT(0) +#define DW_IC_INTR_RX_OVER BIT(1) +#define DW_IC_INTR_RX_FULL BIT(2) +#define DW_IC_INTR_TX_OVER BIT(3) +#define DW_IC_INTR_TX_EMPTY BIT(4) +#define DW_IC_INTR_RD_REQ BIT(5) +#define DW_IC_INTR_TX_ABRT BIT(6) +#define DW_IC_INTR_RX_DONE BIT(7) +#define DW_IC_INTR_ACTIVITY BIT(8) +#define DW_IC_INTR_STOP_DET BIT(9) +#define DW_IC_INTR_START_DET BIT(10) +#define DW_IC_INTR_GEN_CALL BIT(11) +#define DW_IC_INTR_RESTART_DET BIT(12) +#define DW_IC_INTR_MST_ON_HOLD BIT(13) + +/* DW_IC_ENABLE bits */ +#define DW_IC_ENABLE_ENABLE BIT(0) +#define DW_IC_ENABLE_ABORT BIT(1) + +/* DW_IC_STATUS bits */ +#define DW_IC_STATUS_ACTIVITY BIT(0) +#define DW_IC_STATUS_TFE BIT(2) +#define DW_IC_STATUS_RFNE BIT(3) +#define DW_IC_STATUS_MASTER_ACTIVITY BIT(5) +#define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6) +#define DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY BIT(7) + +/* DW_IC_SMBUS_INTR_* bits */ +#define DW_IC_SMBUS_INTR_ALERT BIT(10) + +#endif /* __LINUX_DESIGNWARE_I2C_H */ From f43fa4b8522ba6038b77e86e5f0d94be35effcde Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Aug 2026 14:10:07 +0200 Subject: [PATCH 119/121] drm/xe/i2c: Fix the interrupt handling The platforms that support the interrupt from the I2C adapter can not handle the amount of interrupts the adapter generates because of the way the IRQ is routed in the hardware. The I2C controller driver has to be kept in polling mode because of that. The AMC MCU can still generate critical alerts that have to be handled. The interrupt from SMBus Alert is left enabled and handled separately in the Xe. The alerts from the AMC will cause the device to be declared wedged for now. Fixes: f0e53aadd702 ("drm/xe: Support for I2C attached MCUs") Cc: stable@vger.kernel.org Reviewed-by: Raag Jadav Co-developed-by: Ramesh Babu B Signed-off-by: Ramesh Babu B Signed-off-by: Heikki Krogerus Link: https://patch.msgid.link/20260811121008.1493015-3-heikki.krogerus@linux.intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit a55b76b8bc2c49b11d753c1c6d06ec3a2c61c85e) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/Makefile | 4 +- drivers/gpu/drm/xe/regs/xe_i2c_regs.h | 2 + drivers/gpu/drm/xe/xe_amc.c | 197 ++++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_amc.h | 25 ++++ drivers/gpu/drm/xe/xe_i2c.c | 128 +++++++---------- drivers/gpu/drm/xe/xe_i2c.h | 13 +- 6 files changed, 282 insertions(+), 87 deletions(-) create mode 100644 drivers/gpu/drm/xe/xe_amc.c create mode 100644 drivers/gpu/drm/xe/xe_amc.h diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 67ada1d6c2fb..c92468cb9b89 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -153,7 +153,9 @@ xe-y += xe_bb.o \ xe_wait_user_fence.o \ xe_wopcm.o -xe-$(CONFIG_I2C) += xe_i2c.o +xe-$(CONFIG_I2C) += xe_i2c.o \ + xe_amc.o + xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o xe-$(CONFIG_DRM_GPUSVM) += xe_userptr.o diff --git a/drivers/gpu/drm/xe/regs/xe_i2c_regs.h b/drivers/gpu/drm/xe/regs/xe_i2c_regs.h index f2e455e2bfe4..37550e4a20f8 100644 --- a/drivers/gpu/drm/xe/regs/xe_i2c_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_i2c_regs.h @@ -20,4 +20,6 @@ #define I2C_CONFIG_CMD XE_REG(I2C_CONFIG_SPACE_OFFSET + PCI_COMMAND) #define I2C_CONFIG_PMCSR XE_REG(I2C_CONFIG_SPACE_OFFSET + 0x84) +#define I2C_REG(reg) XE_REG((reg) + I2C_MEM_SPACE_OFFSET) + #endif /* _XE_I2C_REGS_H_ */ diff --git a/drivers/gpu/drm/xe/xe_amc.c b/drivers/gpu/drm/xe/xe_amc.c new file mode 100644 index 000000000000..8ecadee6eea3 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_amc.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "regs/xe_i2c_regs.h" + +#include "xe_amc.h" +#include "xe_device.h" +#include "xe_i2c.h" +#include "xe_mmio.h" + +/** + * DOC: Add-In Management Controller (AMC) + * + * Handler for the SMBus Alerts from the AMC. All the alerts from AMC will cause + * the device to be declared wedged. + */ + +#define AMC_COMMAND 0x0f +#define AMC_GPU_I2C_ADDR 0x8f +#define AMC_VERSION_V1 0x01 +#define AMC_DESTINATION_ID 12 +#define AMC_SOURCE_ID 8 +#define AMC_FLAGS 0xc8 + +#define AMC_MSG_TYPE 0x7e +#define AMC_GET_ALERT_REASON 0x01 + +enum xe_amc_alert { + AMC_ALERT_UNKNOWN, + AMC_ALERT_FW_DOWNLOAD, + AMC_ALERT_THERMAL_TRIP, + AMC_ALERT_OOB_REQUEST, + AMC_ALERT_OOB_RESET, + AMC_ALERT_CATERR, +}; + +static const char * const amc_alert[] = { + [AMC_ALERT_FW_DOWNLOAD] = "Firmware Download", + [AMC_ALERT_THERMAL_TRIP] = "Thermal Trip", + [AMC_ALERT_OOB_REQUEST] = "OOB Request", + [AMC_ALERT_OOB_RESET] = "OOB Reset", + [AMC_ALERT_CATERR] = "Catastrophic", +}; + +struct xe_amc { + struct xe_i2c *i2c; + struct work_struct work; +}; + +struct amc_header { + u8 command; + u8 len; + u8 address; + u8 version; + u8 destination; + u8 source; + u8 flags; +} __packed; + +struct amc_message { + u8 type; + u16 vendor; + u8 command; +} __packed; + +struct amc_request { + struct amc_header header; + struct amc_message message; + u32 reserved; +} __packed; + +struct amc_response { + struct amc_header header; + struct amc_message message; + u8 error; + u8 value; +} __packed; + +static const struct amc_request amc_get_alert_reason = { + .header = { + .command = AMC_COMMAND, + .len = sizeof(struct amc_request) - 2, + .address = AMC_GPU_I2C_ADDR, + .version = AMC_VERSION_V1, + .destination = AMC_DESTINATION_ID, + .source = AMC_SOURCE_ID, + .flags = AMC_FLAGS, + }, + .message = { + .type = AMC_MSG_TYPE, + .vendor = htons(PCI_VENDOR_ID_INTEL), + .command = AMC_GET_ALERT_REASON, + }, +}; + +static void xe_amc_work(struct work_struct *work) +{ + const struct amc_request *request = &amc_get_alert_reason; + struct xe_amc *amc = from_work(amc, work, work); + u8 alert_reason = AMC_ALERT_UNKNOWN; + struct amc_response response; + struct i2c_client *client; + int ret; + + client = amc->i2c->client[XE_I2C_CLIENT_AMC]; + if (IS_ERR_OR_NULL(client)) + goto out_reassert_interrupt; + + ret = i2c_master_send(client, (u8 *)request, sizeof(*request)); + if (ret < 0) { + dev_err(&client->dev, "failed to send request (%d)\n", ret); + goto out_reassert_interrupt; + } + + /* AMC needs 20ms to generate the response. */ + fsleep(20 * USEC_PER_MSEC); + + ret = i2c_master_recv(client, (u8 *)&response, sizeof(response)); + if (ret < 0) { + dev_err(&client->dev, "failed to read response (%d)\n", ret); + goto out_reassert_interrupt; + } + + if (!response.header.len) { + dev_err(&client->dev, "empty response from AMC\n"); + goto out_reassert_interrupt; + } + + if (memcmp(&response.message, &request->message, sizeof(struct amc_message))) { + dev_err(&client->dev, "response does not match the request\n"); + goto out_reassert_interrupt; + } + + if (response.error) { + dev_err(&client->dev, "AMC error 0x%02x\n", response.error); + goto out_reassert_interrupt; + } + + alert_reason = response.value; + dev_dbg(&client->dev, "Alert reason: %d\n", alert_reason); + +out_reassert_interrupt: + xe_mmio_rmw32(amc->i2c->mmio, I2C_CONFIG_CMD, PCI_COMMAND_INTX_DISABLE, 0); + + switch (alert_reason) { + case AMC_ALERT_FW_DOWNLOAD: + case AMC_ALERT_THERMAL_TRIP: + case AMC_ALERT_OOB_REQUEST: + case AMC_ALERT_OOB_RESET: + case AMC_ALERT_CATERR: + dev_warn(amc->i2c->drm_dev, "AMC Alert: %s\n", amc_alert[alert_reason]); + xe_device_declare_wedged(i2c_client_to_xe_device(client)); + break; + default: + dev_warn(amc->i2c->drm_dev, "unknown AMC alert: %d\n", alert_reason); + break; + } +} + +void xe_amc_handle_alert(struct xe_i2c *i2c) +{ + queue_work(system_long_wq, &i2c->amc->work); +} + +int xe_amc_init(struct xe_i2c *i2c) +{ + struct xe_amc *amc; + + amc = kzalloc(sizeof(*amc), GFP_KERNEL); + if (!amc) + return -ENOMEM; + + INIT_WORK(&amc->work, xe_amc_work); + i2c->amc = amc; + amc->i2c = i2c; + + return 0; +} + +void xe_amc_exit(struct xe_i2c *i2c) +{ + if (i2c->amc) { + cancel_work_sync(&i2c->amc->work); + kfree(i2c->amc); + } +} diff --git a/drivers/gpu/drm/xe/xe_amc.h b/drivers/gpu/drm/xe/xe_amc.h new file mode 100644 index 000000000000..b1d5311fee53 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_amc.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _XE_AMC_H_ +#define _XE_AMC_H_ + +#include + +#include "xe_device.h" + +struct xe_i2c; + +static inline struct xe_device *i2c_adapter_to_xe_device(struct i2c_adapter *adapter) +{ + return kdev_to_xe_device(adapter->dev.parent->parent); +} + +static inline struct xe_device *i2c_client_to_xe_device(struct i2c_client *client) +{ + return i2c_adapter_to_xe_device(client->adapter); +} + +int xe_amc_init(struct xe_i2c *i2c); +void xe_amc_exit(struct xe_i2c *i2c); +void xe_amc_handle_alert(struct xe_i2c *i2c); + +#endif /* _XE_AMC_H_ */ diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index a26c38bb17a1..32767570e43d 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -12,8 +12,6 @@ #include #include #include -#include -#include #include #include #include @@ -24,9 +22,12 @@ #include #include +#include + #include "regs/xe_i2c_regs.h" #include "regs/xe_irq_regs.h" +#include "xe_amc.h" #include "xe_device.h" #include "xe_i2c.h" #include "xe_mmio.h" @@ -61,16 +62,32 @@ static inline void xe_i2c_read_endpoint(struct xe_mmio *mmio, void *ep) val[1] = xe_mmio_read32(mmio, REG_SG_REMAP_ADDR_POSTFIX); } +static void xe_i2c_handle_smbus_alert(struct xe_i2c *i2c) +{ + u32 stat; + + stat = xe_mmio_read32(i2c->mmio, I2C_REG(DW_IC_SMBUS_INTR_STAT)); + if (!stat) + return; + + xe_mmio_write32(i2c->mmio, I2C_REG(DW_IC_CLR_SMBUS_INTR), stat); + + if (stat & DW_IC_SMBUS_INTR_ALERT && i2c->amc) + xe_amc_handle_alert(i2c); + else + xe_mmio_rmw32(i2c->mmio, I2C_CONFIG_CMD, PCI_COMMAND_INTX_DISABLE, 0); +} + static void xe_i2c_client_work(struct work_struct *work) { struct xe_i2c *i2c = container_of(work, struct xe_i2c, work); struct i2c_board_info info = { .type = "amc", .flags = I2C_CLIENT_HOST_NOTIFY, - .addr = i2c->ep.addr[1], + .addr = i2c->ep.addr[XE_I2C_CLIENT_AMC], }; - i2c->client[0] = i2c_new_client_device(i2c->adapter, &info); + i2c->client[XE_I2C_CLIENT_AMC] = i2c_new_client_device(i2c->adapter, &info); } static int xe_i2c_notifier(struct notifier_block *nb, unsigned long action, void *data) @@ -115,16 +132,6 @@ static int xe_i2c_register_adapter(struct xe_i2c *i2c) goto err_fwnode_remove; } - if (i2c->adapter_irq) { - struct resource res; - - res = DEFINE_RES_IRQ_NAMED(i2c->adapter_irq, "xe_i2c"); - - ret = platform_device_add_resources(pdev, &res, 1); - if (ret) - goto err_pdev_put; - } - pdev->dev.parent = i2c->drm_dev; pdev->dev.fwnode = fwnode; i2c->adapter_node = fwnode; @@ -166,7 +173,8 @@ bool xe_i2c_present(struct xe_device *xe) static bool xe_i2c_irq_present(struct xe_device *xe) { - return xe->i2c && xe->i2c->adapter_irq; + return xe->i2c && xe->i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ && + !xe_survivability_mode_is_boot_enabled(xe); } /** @@ -179,18 +187,10 @@ static bool xe_i2c_irq_present(struct xe_device *xe) */ void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl) { - struct xe_mmio *mmio = xe_root_tile_mmio(xe); - if (!(master_ctl & I2C_IRQ) || !xe_i2c_irq_present(xe)) return; - /* Forward interrupt to I2C adapter */ - generic_handle_irq_safe(xe->i2c->adapter_irq); - - /* Deassert after I2C adapter clears the interrupt */ - xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, 0, PCI_COMMAND_INTX_DISABLE); - /* Reassert to allow subsequent interrupt generation */ - xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, PCI_COMMAND_INTX_DISABLE, 0); + xe_i2c_handle_smbus_alert(xe->i2c); } void xe_i2c_irq_reset(struct xe_device *xe) @@ -215,45 +215,6 @@ void xe_i2c_irq_postinstall(struct xe_device *xe) xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, PCI_COMMAND_INTX_DISABLE, 0); } -static int xe_i2c_irq_map(struct irq_domain *h, unsigned int virq, - irq_hw_number_t hw_irq_num) -{ - irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq); - return 0; -} - -static const struct irq_domain_ops xe_i2c_irq_ops = { - .map = xe_i2c_irq_map, -}; - -static int xe_i2c_create_irq(struct xe_device *xe) -{ - struct xe_i2c *i2c = xe->i2c; - struct irq_domain *domain; - - if (!(i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ) || - xe_survivability_mode_is_boot_enabled(xe)) - return 0; - - domain = irq_domain_create_linear(dev_fwnode(i2c->drm_dev), 1, &xe_i2c_irq_ops, NULL); - if (!domain) - return -ENOMEM; - - i2c->adapter_irq = irq_create_mapping(domain, 0); - i2c->irqdomain = domain; - - return 0; -} - -static void xe_i2c_remove_irq(struct xe_i2c *i2c) -{ - if (!i2c->irqdomain) - return; - - irq_dispose_mapping(i2c->adapter_irq); - irq_domain_remove(i2c->irqdomain); -} - static int xe_i2c_read(void *context, unsigned int reg, unsigned int *val) { struct xe_i2c *i2c = context; @@ -267,8 +228,16 @@ static int xe_i2c_write(void *context, unsigned int reg, unsigned int val) { struct xe_i2c *i2c = context; - xe_mmio_write32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET), val); + switch (reg) { + case DW_IC_SMBUS_INTR_MASK: + /* Make sure the Alert is never masked. */ + val |= DW_IC_SMBUS_INTR_ALERT; + break; + default: + break; + } + xe_mmio_write32(i2c->mmio, I2C_REG(reg), val); return 0; } @@ -310,12 +279,15 @@ static void xe_i2c_remove(void *data) struct xe_i2c *i2c = data; unsigned int i; - for (i = 0; i < XE_I2C_MAX_CLIENTS; i++) + xe_amc_exit(i2c); + + for (i = 0; i < XE_I2C_MAX_CLIENTS; i++) { i2c_unregister_device(i2c->client[i]); + i2c->client[i] = NULL; + } bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier); xe_i2c_unregister_adapter(i2c); - xe_i2c_remove_irq(i2c); } /** @@ -363,22 +335,18 @@ int xe_i2c_probe(struct xe_device *xe) if (ret) return ret; - ret = xe_i2c_create_irq(xe); - if (ret) - goto err_unregister_notifier; - ret = xe_i2c_register_adapter(i2c); - if (ret) - goto err_remove_irq; + if (ret) { + bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier); + return ret; + } + + ret = xe_amc_init(i2c); + if (ret) { + xe_i2c_remove(i2c); + return ret; + } xe_i2c_irq_postinstall(xe); return devm_add_action_or_reset(drm_dev, xe_i2c_remove, i2c); - -err_remove_irq: - xe_i2c_remove_irq(i2c); - -err_unregister_notifier: - bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier); - - return ret; } diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h index 425d8160835f..b200966b0048 100644 --- a/drivers/gpu/drm/xe/xe_i2c.h +++ b/drivers/gpu/drm/xe/xe_i2c.h @@ -11,18 +11,21 @@ struct device; struct fwnode_handle; struct i2c_adapter; struct i2c_client; -struct irq_domain; struct platform_device; +struct xe_amc; struct xe_device; struct xe_mmio; -#define XE_I2C_MAX_CLIENTS 3 - #define XE_I2C_EP_COOKIE_DEVICE 0xde /* Endpoint Capabilities */ #define XE_I2C_EP_CAP_IRQ BIT(0) +enum XE_I2C_CLIENT { + XE_I2C_CLIENT_AMC = 1, + XE_I2C_MAX_CLIENTS = 3, +}; + struct xe_i2c_endpoint { u8 cookie; u8 capabilities; @@ -38,13 +41,11 @@ struct xe_i2c { struct notifier_block bus_notifier; struct work_struct work; - struct irq_domain *irqdomain; - int adapter_irq; - struct xe_i2c_endpoint ep; struct device *drm_dev; struct xe_mmio *mmio; + struct xe_amc *amc; }; #if IS_ENABLED(CONFIG_I2C) From 244abef7f280a6a84297bfab5fd2e77147bc419a Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Aug 2026 14:10:08 +0200 Subject: [PATCH 120/121] drm/xe/i2c: Keep the i2c controller always enabled Some platforms make an assumption that the i2c controller's enabled state indicates also the power state of the controller. This can create a problem when the controller is in disabled state, because the hardware may assume incorrectly that it is then also in low-power state. To fix this, the controller is kept enabled by taking over the IC_ENABLE register. The controller has to be disabled when the configuration is updated and when the target address or the slave address are assigned, so disabling it when IC_CON, IC_TAR or IC_SAR registers are programmed, and then re-enabling it again. Fixes: f0e53aadd702 ("drm/xe: Support for I2C attached MCUs") Cc: stable@vger.kernel.org Signed-off-by: Heikki Krogerus Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260811121008.1493015-4-heikki.krogerus@linux.intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 76cc14e2faed1adae20f4ee144ead0e3a7566c49) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_i2c.c | 49 ++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/xe/xe_i2c.h | 1 + 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index 32767570e43d..d8fa68206f41 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -215,11 +216,40 @@ void xe_i2c_irq_postinstall(struct xe_device *xe) xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, PCI_COMMAND_INTX_DISABLE, 0); } +/* See "Disabling DW_apb_i2c" in the DesignWare DW_abp_i2c databook. */ +static void xe_i2c_disable(struct xe_i2c *i2c) +{ + int timeout = 100; + u32 status; + + xe_mmio_rmw32(i2c->mmio, I2C_REG(DW_IC_ENABLE), DW_IC_ENABLE_ENABLE, 0); + + do { + status = xe_mmio_read32(i2c->mmio, I2C_REG(DW_IC_ENABLE_STATUS)); + if (!(status & DW_IC_ENABLE_ENABLE)) + return; + /* Can't sleep here. */ + udelay(25); + } while (timeout--); + + dev_warn(i2c->drm_dev, "timeout in disabling i2c adapter\n"); +} + static int xe_i2c_read(void *context, unsigned int reg, unsigned int *val) { struct xe_i2c *i2c = context; - *val = xe_mmio_read32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET)); + *val = xe_mmio_read32(i2c->mmio, I2C_REG(reg)); + + switch (reg) { + case DW_IC_ENABLE: + case DW_IC_ENABLE_STATUS: + FIELD_MODIFY(DW_IC_ENABLE_ENABLE, val, + i2c->ic_enable & DW_IC_ENABLE_ENABLE); + break; + default: + break; + } return 0; } @@ -229,6 +259,23 @@ static int xe_i2c_write(void *context, unsigned int reg, unsigned int val) struct xe_i2c *i2c = context; switch (reg) { + case DW_IC_CON: + case DW_IC_TAR: + case DW_IC_SAR: + /* Disable the controller. */ + xe_i2c_disable(i2c); + + /* Write the register. */ + xe_mmio_write32(i2c->mmio, I2C_REG(reg), val); + + /* Enable the controller. */ + xe_mmio_rmw32(i2c->mmio, I2C_REG(DW_IC_ENABLE), 0, DW_IC_ENABLE_ENABLE); + return 0; + case DW_IC_ENABLE: + i2c->ic_enable = val; + /* Other fields can be updated except the enable bit. */ + val |= DW_IC_ENABLE_ENABLE; + break; case DW_IC_SMBUS_INTR_MASK: /* Make sure the Alert is never masked. */ val |= DW_IC_SMBUS_INTR_ALERT; diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h index b200966b0048..d63adacfefe7 100644 --- a/drivers/gpu/drm/xe/xe_i2c.h +++ b/drivers/gpu/drm/xe/xe_i2c.h @@ -37,6 +37,7 @@ struct xe_i2c { struct platform_device *pdev; struct i2c_adapter *adapter; struct i2c_client *client[XE_I2C_MAX_CLIENTS]; + unsigned int ic_enable; struct notifier_block bus_notifier; struct work_struct work; From a62212b35a214c2ff3bd1c785a440d7ad8205ec9 Mon Sep 17 00:00:00 2001 From: Anoop Vijay Date: Tue, 25 Aug 2026 10:28:28 -0700 Subject: [PATCH 121/121] drm/xe/sysctrl: Read mailbox phase bit from hardware The mailbox PHASE bit in SYSCTRL_MB_CTRL is toggled per-message and was tracked in software as sc->phase_bit, reset to 0 on error paths. If the cached value ever drifts from what the hardware last saw, all following messages carry the wrong phase and get silently misread by firmware. Drop the cache and read PHASE directly from SYSCTRL_MB_CTRL before each frame instead, removing xe_sysctrl_mailbox_init() and its call sites along with it. Fixes: 1f95f618182b ("drm/xe/xe_sysctrl: Add System Controller mailbox communication support") Signed-off-by: Anoop Vijay Reviewed-by: Umesh Nerlige Ramappa Reviewed-by: Rodrigo Vivi Link: https://patch.msgid.link/20260825172827.3801591-2-anoop.c.vijay@intel.com Signed-off-by: Rodrigo Vivi (cherry picked from commit 04984fcdbf6876c940c01026a7404c1e9cc91ba7) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_sysctrl.c | 7 +------ drivers/gpu/drm/xe/xe_sysctrl_mailbox.c | 22 ++++------------------ drivers/gpu/drm/xe/xe_sysctrl_mailbox.h | 1 - drivers/gpu/drm/xe/xe_sysctrl_types.h | 3 --- 4 files changed, 5 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_sysctrl.c b/drivers/gpu/drm/xe/xe_sysctrl.c index 1db20be8158b..62ccc9be71b4 100644 --- a/drivers/gpu/drm/xe/xe_sysctrl.c +++ b/drivers/gpu/drm/xe/xe_sysctrl.c @@ -85,7 +85,6 @@ int xe_sysctrl_init(struct xe_device *xe) return ret; xe->soc_remapper.set_sysctrl_region(xe, SYSCTRL_MAILBOX_INDEX); - xe_sysctrl_mailbox_init(sc); INIT_WORK(&sc->work, xe_sysctrl_work); return devm_add_action_or_reset(xe->drm.dev, sysctrl_fini, xe); @@ -114,12 +113,10 @@ void xe_sysctrl_irq_handler(struct xe_device *xe, u32 master_ctl) * @xe: xe device instance * * Invoked during system resume (S3/S4 to S0) and runtime resume from D3cold. - * Restores SoC remapper configuration and reinitializes mailbox interface. + * Restores SoC remapper configuration. */ void xe_sysctrl_pm_resume(struct xe_device *xe) { - struct xe_sysctrl *sc = &xe->sc; - if (!xe->info.has_soc_remapper_sysctrl) return; @@ -127,6 +124,4 @@ void xe_sysctrl_pm_resume(struct xe_device *xe) return; xe->soc_remapper.set_sysctrl_region(xe, SYSCTRL_MAILBOX_INDEX); - - xe_sysctrl_mailbox_init(sc); } diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c index e13eebaac1d0..72baf1aa4b3a 100644 --- a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c +++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c @@ -145,6 +145,7 @@ static int sysctrl_send_frames(struct xe_sysctrl *sc, struct xe_device *xe = sc_to_xe(sc); u32 ctrl_reg, total_frames, frame; size_t bytes_sent, frame_size; + bool phase; total_frames = DIV_ROUND_UP(cmd_size, XE_SYSCTRL_MB_FRAME_SIZE); @@ -153,7 +154,8 @@ static int sysctrl_send_frames(struct xe_sysctrl *sc, return -EBUSY; } - sc->phase_bit ^= 1; + ctrl_reg = xe_mmio_read32(sc->mmio, SYSCTRL_MB_CTRL); + phase = !(ctrl_reg & SYSCTRL_FRAME_PHASE); bytes_sent = 0; for (frame = 0; frame < total_frames; frame++) { @@ -161,7 +163,6 @@ static int sysctrl_send_frames(struct xe_sysctrl *sc, if (sysctrl_write_frame(sc, mbox_cmd + bytes_sent, frame_size)) { xe_err(xe, "sysctrl: Failed to write frame %u\n", frame); - sc->phase_bit = 0; return -EIO; } @@ -169,13 +170,12 @@ static int sysctrl_send_frames(struct xe_sysctrl *sc, REG_FIELD_PREP(SYSCTRL_FRAME_CURRENT_MASK, frame) | REG_FIELD_PREP(SYSCTRL_FRAME_TOTAL_MASK, total_frames - 1) | SYSCTRL_MB_CTRL_CMD | - (sc->phase_bit ? SYSCTRL_FRAME_PHASE : 0); + (phase ? SYSCTRL_FRAME_PHASE : 0); xe_mmio_write32(sc->mmio, SYSCTRL_MB_CTRL, ctrl_reg); if (!sysctrl_wait_bit_clear(sc, SYSCTRL_MB_CTRL_RUN_BUSY, timeout_ms)) { xe_err(xe, "sysctrl: Frame %u acknowledgment timeout\n", frame); - sc->phase_bit = 0; return -ETIMEDOUT; } @@ -321,20 +321,6 @@ void xe_sysctrl_create_command(struct xe_sysctrl_mailbox_command *command, u8 gr command->data_out_len = response_len; } -/** - * xe_sysctrl_mailbox_init - Initialize System Controller mailbox interface - * @sc: System controller structure - * - * Initialize system controller mailbox interface for communication. - */ -void xe_sysctrl_mailbox_init(struct xe_sysctrl *sc) -{ - u32 ctrl_reg; - - ctrl_reg = xe_mmio_read32(sc->mmio, SYSCTRL_MB_CTRL); - sc->phase_bit = (ctrl_reg & SYSCTRL_FRAME_PHASE) ? 1 : 0; -} - /** * xe_sysctrl_send_command() - Send mailbox command to System Controller * @sc: System Controller instance diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h index fb434cc165b2..f0d5e3d7f5e3 100644 --- a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h +++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h @@ -26,7 +26,6 @@ struct xe_sysctrl_mailbox_command; void xe_sysctrl_create_command(struct xe_sysctrl_mailbox_command *command, u8 group_id, u8 cmd_id, void *request, size_t request_len, void *response, size_t response_len); -void xe_sysctrl_mailbox_init(struct xe_sysctrl *sc); int xe_sysctrl_send_command(struct xe_sysctrl *sc, struct xe_sysctrl_mailbox_command *cmd, size_t *rdata_len); diff --git a/drivers/gpu/drm/xe/xe_sysctrl_types.h b/drivers/gpu/drm/xe/xe_sysctrl_types.h index 66ba24f43017..98c2f473f7c6 100644 --- a/drivers/gpu/drm/xe/xe_sysctrl_types.h +++ b/drivers/gpu/drm/xe/xe_sysctrl_types.h @@ -26,9 +26,6 @@ struct xe_sysctrl { /** @cmd_lock: Mutex protecting mailbox command operations */ struct mutex cmd_lock; - /** @phase_bit: Message boundary phase toggle bit (0 or 1) */ - bool phase_bit; - /** @work: Pending events worker */ struct work_struct work;