From 23c0a9d36f78e52b62dcc7623555723cf20ca83c Mon Sep 17 00:00:00 2001 From: Fange Zhang Date: Tue, 14 Jan 2025 16:55:24 +0800 Subject: [PATCH 01/86] drm/msm/dpu: Add writeback support for SM6150 On the SM6150 platform there is WB_2 block. Add it to the SM6150 catalog. Reviewed-by: Dmitry Baryshkov Signed-off-by: Fange Zhang Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/632337/ Link: https://lore.kernel.org/r/20250114-add-writeback-support-for-sm6150-v2-1-d707b31aad5c@quicinc.com Signed-off-by: Dmitry Baryshkov --- .../drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h index d761ed705bac..744012892b7c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h @@ -27,6 +27,7 @@ static const struct dpu_mdp_cfg sm6150_mdp = { [DPU_CLK_CTRL_DMA1] = { .reg_off = 0x2b4, .bit_off = 8 }, [DPU_CLK_CTRL_DMA2] = { .reg_off = 0x2bc, .bit_off = 8 }, [DPU_CLK_CTRL_DMA3] = { .reg_off = 0x2c4, .bit_off = 8 }, + [DPU_CLK_CTRL_WB2] = { .reg_off = 0x2bc, .bit_off = 16 }, }, }; @@ -162,6 +163,21 @@ static const struct dpu_pingpong_cfg sm6150_pp[] = { }, }; +static const struct dpu_wb_cfg sm6150_wb[] = { + { + .name = "wb_2", .id = WB_2, + .base = 0x65000, .len = 0x2c8, + .features = WB_SM8250_MASK, + .format_list = wb2_formats_rgb, + .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .clk_ctrl = DPU_CLK_CTRL_WB2, + .xin_id = 6, + .vbif_idx = VBIF_RT, + .maxlinewidth = 2160, + .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4), + }, +}; + static const struct dpu_intf_cfg sm6150_intf[] = { { .name = "intf_0", .id = INTF_0, @@ -242,6 +258,8 @@ const struct dpu_mdss_cfg dpu_sm6150_cfg = { .dspp = sm6150_dspp, .pingpong_count = ARRAY_SIZE(sm6150_pp), .pingpong = sm6150_pp, + .wb_count = ARRAY_SIZE(sm6150_wb), + .wb = sm6150_wb, .intf_count = ARRAY_SIZE(sm6150_intf), .intf = sm6150_intf, .vbif_count = ARRAY_SIZE(sdm845_vbif), From 629ac9f0a68c22f4cfba3ab5cdbf14cce792ac59 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 14 Jan 2025 16:59:59 +0100 Subject: [PATCH 02/86] drm/msm/dpu: Simplify using local 'ctl' variable In few places we store 'phys_enc->hw_ctl' to local 'ctl' variable so use it everywhere. No functional change. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/632389/ Link: https://lore.kernel.org/r/20250114155959.583889-1-krzysztof.kozlowski@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 48e6e8d74c85..503dfd79b8f2 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -2183,8 +2183,8 @@ static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc) memset(&mixer, 0, sizeof(mixer)); /* reset all mixers for this encoder */ - if (phys_enc->hw_ctl->ops.clear_all_blendstages) - phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl); + if (ctl->ops.clear_all_blendstages) + ctl->ops.clear_all_blendstages(ctl); global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms); @@ -2193,12 +2193,12 @@ static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc) for (i = 0; i < num_lm; i++) { hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]); - if (phys_enc->hw_ctl->ops.update_pending_flush_mixer) - phys_enc->hw_ctl->ops.update_pending_flush_mixer(ctl, hw_mixer[i]->idx); + if (ctl->ops.update_pending_flush_mixer) + ctl->ops.update_pending_flush_mixer(ctl, hw_mixer[i]->idx); /* clear all blendstages */ - if (phys_enc->hw_ctl->ops.setup_blendstage) - phys_enc->hw_ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL); + if (ctl->ops.setup_blendstage) + ctl->ops.setup_blendstage(ctl, hw_mixer[i]->idx, NULL); } } @@ -2250,7 +2250,7 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) dpu_enc = to_dpu_encoder_virt(phys_enc->parent); - phys_enc->hw_ctl->ops.reset(ctl); + ctl->ops.reset(ctl); dpu_encoder_helper_reset_mixers(phys_enc); @@ -2265,8 +2265,8 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) phys_enc->hw_wb->ops.bind_pingpong_blk(phys_enc->hw_wb, PINGPONG_NONE); /* mark WB flush as pending */ - if (phys_enc->hw_ctl->ops.update_pending_flush_wb) - phys_enc->hw_ctl->ops.update_pending_flush_wb(ctl, phys_enc->hw_wb->idx); + if (ctl->ops.update_pending_flush_wb) + ctl->ops.update_pending_flush_wb(ctl, phys_enc->hw_wb->idx); } else { for (i = 0; i < dpu_enc->num_phys_encs; i++) { if (dpu_enc->phys_encs[i] && phys_enc->hw_intf->ops.bind_pingpong_blk) @@ -2275,8 +2275,8 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) PINGPONG_NONE); /* mark INTF flush as pending */ - if (phys_enc->hw_ctl->ops.update_pending_flush_intf) - phys_enc->hw_ctl->ops.update_pending_flush_intf(phys_enc->hw_ctl, + if (ctl->ops.update_pending_flush_intf) + ctl->ops.update_pending_flush_intf(ctl, dpu_enc->phys_encs[i]->hw_intf->idx); } } @@ -2288,8 +2288,8 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) if (phys_enc->hw_pp && phys_enc->hw_pp->merge_3d) { phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d, BLEND_3D_NONE); - if (phys_enc->hw_ctl->ops.update_pending_flush_merge_3d) - phys_enc->hw_ctl->ops.update_pending_flush_merge_3d(ctl, + if (ctl->ops.update_pending_flush_merge_3d) + ctl->ops.update_pending_flush_merge_3d(ctl, phys_enc->hw_pp->merge_3d->idx); } @@ -2297,9 +2297,9 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) if (phys_enc->hw_cdm->ops.bind_pingpong_blk && phys_enc->hw_pp) phys_enc->hw_cdm->ops.bind_pingpong_blk(phys_enc->hw_cdm, PINGPONG_NONE); - if (phys_enc->hw_ctl->ops.update_pending_flush_cdm) - phys_enc->hw_ctl->ops.update_pending_flush_cdm(phys_enc->hw_ctl, - phys_enc->hw_cdm->idx); + if (ctl->ops.update_pending_flush_cdm) + ctl->ops.update_pending_flush_cdm(ctl, + phys_enc->hw_cdm->idx); } if (dpu_enc->dsc) { From 25dc6948a06f91164aebeba8e5bb4203448bca0d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 14 Jan 2025 20:17:24 +0100 Subject: [PATCH 03/86] drm/msm: Use str_enable_disable-like helpers Replace ternary (condition ? "enable" : "disable") syntax with helpers from string_choices.h because: 1. Simple function call with one argument is easier to read. Ternary operator has three arguments and with wrapping might lead to quite long code. 2. Is slightly shorter thus also easier to read. 3. It brings uniformity in the text - same string. 4. Allows deduping by the linker, which results in a smaller binary file. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/632406/ Link: https://lore.kernel.org/r/20250114191724.861601-1-krzysztof.kozlowski@linaro.org Signed-off-by: Dmitry Baryshkov --- .../drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c | 5 ++-- drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c | 3 ++- drivers/gpu/drm/msm/dp/dp_ctrl.c | 25 ++++++++++--------- drivers/gpu/drm/msm/dp/dp_display.c | 4 +-- drivers/gpu/drm/msm/dp/dp_drm.c | 5 ++-- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c index e9bbccc44dad..da9994a79ca2 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ #include +#include #include "dpu_encoder_phys.h" #include "dpu_hw_interrupts.h" #include "dpu_hw_pingpong.h" @@ -261,7 +262,7 @@ static int dpu_encoder_phys_cmd_control_vblank_irq( DRM_DEBUG_KMS("id:%u pp:%d enable=%s/%d\n", DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, - enable ? "true" : "false", refcount); + str_true_false(enable), refcount); if (enable) { if (phys_enc->vblank_refcount == 0) @@ -285,7 +286,7 @@ static int dpu_encoder_phys_cmd_control_vblank_irq( DRM_ERROR("vblank irq err id:%u pp:%d ret:%d, enable %s/%d\n", DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, ret, - enable ? "true" : "false", refcount); + str_true_false(enable), refcount); } return ret; diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c index 666de99a46a5..fc183fe37f56 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_ctl.c @@ -3,6 +3,7 @@ * Copyright (c) 2014-2015 The Linux Foundation. All rights reserved. */ +#include #include "mdp5_kms.h" #include "mdp5_ctl.h" @@ -233,7 +234,7 @@ int mdp5_ctl_set_encoder_state(struct mdp5_ctl *ctl, return -EINVAL; ctl->encoder_enabled = enabled; - DBG("intf_%d: %s", intf->num, enabled ? "on" : "off"); + DBG("intf_%d: %s", intf->num, str_on_off(enabled)); if (start_signal_needed(ctl, pipeline)) { send_start_signal(ctl); diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index 9c463ae2f8fa..d8633a596f8d 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -1366,9 +1367,9 @@ int msm_dp_ctrl_core_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl) drm_dbg_dp(ctrl->drm_dev, "enable core clocks \n"); drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n", - ctrl->stream_clks_on ? "on" : "off", - ctrl->link_clks_on ? "on" : "off", - ctrl->core_clks_on ? "on" : "off"); + str_on_off(ctrl->stream_clks_on), + str_on_off(ctrl->link_clks_on), + str_on_off(ctrl->core_clks_on)); return 0; } @@ -1385,9 +1386,9 @@ void msm_dp_ctrl_core_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl) drm_dbg_dp(ctrl->drm_dev, "disable core clocks \n"); drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n", - ctrl->stream_clks_on ? "on" : "off", - ctrl->link_clks_on ? "on" : "off", - ctrl->core_clks_on ? "on" : "off"); + str_on_off(ctrl->stream_clks_on), + str_on_off(ctrl->link_clks_on), + str_on_off(ctrl->core_clks_on)); } static int msm_dp_ctrl_link_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl) @@ -1416,9 +1417,9 @@ static int msm_dp_ctrl_link_clk_enable(struct msm_dp_ctrl *msm_dp_ctrl) drm_dbg_dp(ctrl->drm_dev, "enable link clocks\n"); drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n", - ctrl->stream_clks_on ? "on" : "off", - ctrl->link_clks_on ? "on" : "off", - ctrl->core_clks_on ? "on" : "off"); + str_on_off(ctrl->stream_clks_on), + str_on_off(ctrl->link_clks_on), + str_on_off(ctrl->core_clks_on)); return 0; } @@ -1435,9 +1436,9 @@ static void msm_dp_ctrl_link_clk_disable(struct msm_dp_ctrl *msm_dp_ctrl) drm_dbg_dp(ctrl->drm_dev, "disabled link clocks\n"); drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n", - ctrl->stream_clks_on ? "on" : "off", - ctrl->link_clks_on ? "on" : "off", - ctrl->core_clks_on ? "on" : "off"); + str_on_off(ctrl->stream_clks_on), + str_on_off(ctrl->link_clks_on), + str_on_off(ctrl->core_clks_on)); } static int msm_dp_ctrl_enable_mainlink_clocks(struct msm_dp_ctrl_private *ctrl) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 9aa8bbd73d86..bbc47d86ae9e 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -343,8 +344,7 @@ static int msm_dp_display_send_hpd_notification(struct msm_dp_display_private *d { if ((hpd && dp->msm_dp_display.link_ready) || (!hpd && !dp->msm_dp_display.link_ready)) { - drm_dbg_dp(dp->drm_dev, "HPD already %s\n", - (hpd ? "on" : "off")); + drm_dbg_dp(dp->drm_dev, "HPD already %s\n", str_on_off(hpd)); return 0; } diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index 022b3e815cf3..cca57e56c906 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -3,6 +3,7 @@ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ +#include #include #include #include @@ -25,7 +26,7 @@ static enum drm_connector_status msm_dp_bridge_detect(struct drm_bridge *bridge) dp = to_dp_bridge(bridge)->msm_dp_display; drm_dbg_dp(dp->drm_dev, "link_ready = %s\n", - (dp->link_ready) ? "true" : "false"); + str_true_false(dp->link_ready)); return (dp->link_ready) ? connector_status_connected : connector_status_disconnected; @@ -41,7 +42,7 @@ static int msm_dp_bridge_atomic_check(struct drm_bridge *bridge, dp = to_dp_bridge(bridge)->msm_dp_display; drm_dbg_dp(dp->drm_dev, "link_ready = %s\n", - (dp->link_ready) ? "true" : "false"); + str_true_false(dp->link_ready)); /* * There is no protection in the DRM framework to check if the display From b6090ffb30f3301d3831774f9c3e2f1b1141a399 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 22 Jan 2025 17:23:44 +0100 Subject: [PATCH 04/86] drm/msm/dpu: Fall back to a single DSC encoder (1:1:1) on small SoCs Some SoCs such as SC7280 (used in the Fairphone 5) have only a single DSC "hard slice" encoder. The current hardcoded use of 2:2:1 topology (2 LM and 2 DSC for a single interface) make it impossible to use Display Stream Compression panels with mainline, which is exactly what's installed on the Fairphone 5. By loosening the hardcoded `num_dsc = 2` to fall back to `num_dsc = 1` when the catalog only contains one entry, we can trivially support this phone and unblock further panel enablement on mainline. A few more supporting changes in this patch ensure hardcoded constants of 2 DSC encoders are replaced to count or read back the actual number of DSC hardware blocks that are enabled for the given virtual encoder. Likewise DSC_MODE_SPLIT_PANEL can no longer be unconditionally enabled. Cc: Luca Weiss Signed-off-by: Marijn Suijten Reviewed-by: Dmitry Baryshkov Tested-by: Luca Weiss Reviewed-by: Jessica Zhang Tested-by: Danila Tikhonov Patchwork: https://patchwork.freedesktop.org/patch/633318/ Link: https://lore.kernel.org/r/20250122-dpu-111-topology-v2-1-505e95964af9@somainline.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 47 +++++++++++---------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 503dfd79b8f2..e3cbd65d2b13 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -622,9 +622,9 @@ bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc) if (dpu_enc->phys_encs[i]) intf_count++; - /* See dpu_encoder_get_topology, we only support 2:2:1 topology */ - if (dpu_enc->dsc) - num_dsc = 2; + for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) + if (dpu_enc->hw_dsc[i]) + num_dsc++; return (num_dsc > 0) && (num_dsc > intf_count); } @@ -686,13 +686,19 @@ static struct msm_display_topology dpu_encoder_get_topology( if (dsc) { /* - * In case of Display Stream Compression (DSC), we would use - * 2 DSC encoders, 2 layer mixers and 1 interface - * this is power optimal and can drive up to (including) 4k - * screens + * Use 2 DSC encoders and 2 layer mixers per single interface + * when Display Stream Compression (DSC) is enabled, + * and when enough DSC blocks are available. + * This is power-optimal and can drive up to (including) 4k + * screens. */ - topology.num_dsc = 2; - topology.num_lm = 2; + if (dpu_kms->catalog->dsc_count >= 2) { + topology.num_dsc = 2; + topology.num_lm = 2; + } else { + topology.num_dsc = 1; + topology.num_lm = 1; + } topology.num_intf = 1; } @@ -2020,7 +2026,6 @@ static void dpu_encoder_dsc_pipe_cfg(struct dpu_hw_ctl *ctl, static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc, struct drm_dsc_config *dsc) { - /* coding only for 2LM, 2enc, 1 dsc config */ struct dpu_encoder_phys *enc_master = dpu_enc->cur_master; struct dpu_hw_ctl *ctl = enc_master->hw_ctl; struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; @@ -2030,22 +2035,24 @@ static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc, int dsc_common_mode; int pic_width; u32 initial_lines; + int num_dsc = 0; int i; for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { hw_pp[i] = dpu_enc->hw_pp[i]; hw_dsc[i] = dpu_enc->hw_dsc[i]; - if (!hw_pp[i] || !hw_dsc[i]) { - DPU_ERROR_ENC(dpu_enc, "invalid params for DSC\n"); - return; - } + if (!hw_pp[i] || !hw_dsc[i]) + break; + + num_dsc++; } - dsc_common_mode = 0; pic_width = dsc->pic_width; - dsc_common_mode = DSC_MODE_SPLIT_PANEL; + dsc_common_mode = 0; + if (num_dsc > 1) + dsc_common_mode |= DSC_MODE_SPLIT_PANEL; if (dpu_encoder_use_dsc_merge(enc_master->parent)) dsc_common_mode |= DSC_MODE_MULTIPLEX; if (enc_master->intf_mode == INTF_MODE_VIDEO) @@ -2054,14 +2061,10 @@ static void dpu_encoder_prep_dsc(struct dpu_encoder_virt *dpu_enc, this_frame_slices = pic_width / dsc->slice_width; intf_ip_w = this_frame_slices * dsc->slice_width; - /* - * dsc merge case: when using 2 encoders for the same stream, - * no. of slices need to be same on both the encoders. - */ - enc_ip_w = intf_ip_w / 2; + enc_ip_w = intf_ip_w / num_dsc; initial_lines = dpu_encoder_dsc_initial_line_calc(dsc, enc_ip_w); - for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) + for (i = 0; i < num_dsc; i++) dpu_encoder_dsc_pipe_cfg(ctl, hw_dsc[i], hw_pp[i], dsc, dsc_common_mode, initial_lines); } From 25b4614843bcc56ba150f7c99905125a019e656c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 23 Jan 2025 14:43:33 +0200 Subject: [PATCH 05/86] drm/msm/dpu: don't use active in atomic_check() The driver isn't supposed to consult crtc_state->active/active_check for resource allocation. Instead all resources should be allocated if crtc_state->enabled is set. Stop consulting active / active_changed in order to determine whether the hardware resources should be (re)allocated. Fixes: ccc862b957c6 ("drm/msm/dpu: Fix reservation failures in modeset") Reported-by: Simona Vetter Closes: https://lore.kernel.org/dri-devel/ZtW_S0j5AEr4g0QW@phenom.ffwll.local/ Reviewed-by: Simona Vetter Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/633393/ Link: https://lore.kernel.org/r/20250123-drm-dirty-modeset-v2-1-bbfd3a6cd1a4@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 4 ---- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index e5dcd41a361f..29485e76f531 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1262,10 +1262,6 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc, DRM_DEBUG_ATOMIC("%s: check\n", dpu_crtc->name); - /* force a full mode set if active state changed */ - if (crtc_state->active_changed) - crtc_state->mode_changed = true; - if (cstate->num_mixers) { rc = _dpu_crtc_check_and_setup_lm_bounds(crtc, crtc_state); if (rc) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index e3cbd65d2b13..aeec5a5ab8ff 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -799,12 +799,11 @@ static int dpu_encoder_virt_atomic_check( crtc_state->mode_changed = true; /* * Release and Allocate resources on every modeset - * Dont allocate when active is false. */ if (drm_atomic_crtc_needs_modeset(crtc_state)) { dpu_rm_release(global_state, drm_enc); - if (!crtc_state->active_changed || crtc_state->enable) + if (crtc_state->enable) ret = dpu_rm_reserve(&dpu_kms->rm, global_state, drm_enc, crtc_state, &topology); if (!ret) From 7d39f5bb82c0d7155037982dd0ff583a68db1c34 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 23 Jan 2025 14:43:34 +0200 Subject: [PATCH 06/86] drm/msm/dpu: move needs_cdm setting to dpu_encoder_get_topology() As a preparation for calling dpu_encoder_get_topology() from different places, move the code setting topology->needs_cdm to that function (instead of patching topology separately). Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/633395/ Link: https://lore.kernel.org/r/20250123-drm-dirty-modeset-v2-2-bbfd3a6cd1a4@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 41 +++++++++++---------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index aeec5a5ab8ff..de329761cdb6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -652,8 +652,11 @@ static struct msm_display_topology dpu_encoder_get_topology( struct dpu_kms *dpu_kms, struct drm_display_mode *mode, struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, struct drm_dsc_config *dsc) { + struct msm_drm_private *priv = dpu_enc->base.dev->dev_private; + struct msm_display_info *disp_info = &dpu_enc->disp_info; struct msm_display_topology topology = {0}; int i, intf_count = 0; @@ -702,6 +705,23 @@ static struct msm_display_topology dpu_encoder_get_topology( topology.num_intf = 1; } + /* + * Use CDM only for writeback or DP at the moment as other interfaces cannot handle it. + * If writeback itself cannot handle cdm for some reason it will fail in its atomic_check() + * earlier. + */ + if (disp_info->intf_type == INTF_WB && conn_state->writeback_job) { + struct drm_framebuffer *fb; + + fb = conn_state->writeback_job->fb; + + if (fb && MSM_FORMAT_IS_YUV(msm_framebuffer_format(fb))) + topology.needs_cdm = true; + } else if (disp_info->intf_type == INTF_DP) { + if (msm_dp_is_yuv_420_enabled(priv->dp[disp_info->h_tile_instance[0]], mode)) + topology.needs_cdm = true; + } + return topology; } @@ -749,9 +769,7 @@ static int dpu_encoder_virt_atomic_check( struct dpu_kms *dpu_kms; struct drm_display_mode *adj_mode; struct msm_display_topology topology; - struct msm_display_info *disp_info; struct dpu_global_state *global_state; - struct drm_framebuffer *fb; struct drm_dsc_config *dsc; int ret = 0; @@ -765,7 +783,6 @@ static int dpu_encoder_virt_atomic_check( DPU_DEBUG_ENC(dpu_enc, "\n"); priv = drm_enc->dev->dev_private; - disp_info = &dpu_enc->disp_info; dpu_kms = to_dpu_kms(priv->kms); adj_mode = &crtc_state->adjusted_mode; global_state = dpu_kms_get_global_state(crtc_state->state); @@ -776,22 +793,8 @@ static int dpu_encoder_virt_atomic_check( dsc = dpu_encoder_get_dsc_config(drm_enc); - topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode, crtc_state, dsc); - - /* - * Use CDM only for writeback or DP at the moment as other interfaces cannot handle it. - * If writeback itself cannot handle cdm for some reason it will fail in its atomic_check() - * earlier. - */ - if (disp_info->intf_type == INTF_WB && conn_state->writeback_job) { - fb = conn_state->writeback_job->fb; - - if (fb && MSM_FORMAT_IS_YUV(msm_framebuffer_format(fb))) - topology.needs_cdm = true; - } else if (disp_info->intf_type == INTF_DP) { - if (msm_dp_is_yuv_420_enabled(priv->dp[disp_info->h_tile_instance[0]], adj_mode)) - topology.needs_cdm = true; - } + topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode, crtc_state, conn_state, + dsc); if (topology.needs_cdm && !dpu_enc->cur_master->hw_cdm) crtc_state->mode_changed = true; From 41921f231abf9a3a95550b2b565df8e4329319cb Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 23 Jan 2025 14:43:35 +0200 Subject: [PATCH 07/86] drm/msm/dpu: simplify dpu_encoder_get_topology() interface As a preparation for calling dpu_encoder_get_topology() from different code paths, simplify its calling interface, obtaining some data pointers internally instead passing them via arguments. Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/633396/ Link: https://lore.kernel.org/r/20250123-drm-dirty-modeset-v2-3-bbfd3a6cd1a4@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index de329761cdb6..938d8b05a4cd 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -649,14 +649,14 @@ struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc) static struct msm_display_topology dpu_encoder_get_topology( struct dpu_encoder_virt *dpu_enc, - struct dpu_kms *dpu_kms, struct drm_display_mode *mode, struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state, - struct drm_dsc_config *dsc) + struct drm_connector_state *conn_state) { struct msm_drm_private *priv = dpu_enc->base.dev->dev_private; struct msm_display_info *disp_info = &dpu_enc->disp_info; + struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms); + struct drm_dsc_config *dsc = dpu_encoder_get_dsc_config(&dpu_enc->base); struct msm_display_topology topology = {0}; int i, intf_count = 0; @@ -770,7 +770,6 @@ static int dpu_encoder_virt_atomic_check( struct drm_display_mode *adj_mode; struct msm_display_topology topology; struct dpu_global_state *global_state; - struct drm_dsc_config *dsc; int ret = 0; if (!drm_enc || !crtc_state || !conn_state) { @@ -791,10 +790,7 @@ static int dpu_encoder_virt_atomic_check( trace_dpu_enc_atomic_check(DRMID(drm_enc)); - dsc = dpu_encoder_get_dsc_config(drm_enc); - - topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode, crtc_state, conn_state, - dsc); + topology = dpu_encoder_get_topology(dpu_enc, adj_mode, crtc_state, conn_state); if (topology.needs_cdm && !dpu_enc->cur_master->hw_cdm) crtc_state->mode_changed = true; From d1f28e30a525107cd3b7927b73c69fbab9e826a5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Jan 2025 14:21:04 +0100 Subject: [PATCH 08/86] dt-bindings: display/msm/dsi-phy: Add header with exposed clock IDs DSI phys, from earliest (28 nm) up to newest (3 nm) generation, provide two clocks. The respective clock ID is used by drivers and DTS, so it should be documented as explicit ABI. Signed-off-by: Krzysztof Kozlowski Acked-by: Stephen Boyd Acked-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/634146/ Link: https://lore.kernel.org/r/20250127132105.107138-1-krzysztof.kozlowski@linaro.org Signed-off-by: Dmitry Baryshkov --- .../devicetree/bindings/display/msm/dsi-phy-common.yaml | 2 ++ MAINTAINERS | 1 + include/dt-bindings/clock/qcom,dsi-phy-28nm.h | 9 +++++++++ 3 files changed, 12 insertions(+) create mode 100644 include/dt-bindings/clock/qcom,dsi-phy-28nm.h diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-common.yaml b/Documentation/devicetree/bindings/display/msm/dsi-phy-common.yaml index 6b57ce41c95f..d0ce85a08b6d 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-phy-common.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-phy-common.yaml @@ -15,6 +15,8 @@ description: properties: "#clock-cells": const: 1 + description: + See include/dt-bindings/clock/qcom,dsi-phy-28nm.h for clock IDs. "#phy-cells": const: 0 diff --git a/MAINTAINERS b/MAINTAINERS index 43b55429f0fc..9f29ec77a654 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7392,6 +7392,7 @@ T: git https://gitlab.freedesktop.org/drm/msm.git F: Documentation/devicetree/bindings/display/msm/ F: drivers/gpu/drm/ci/xfails/msm* F: drivers/gpu/drm/msm/ +F: include/dt-bindings/clock/qcom,dsi-phy-28nm.h F: include/uapi/drm/msm_drm.h DRM DRIVER FOR NOVATEK NT35510 PANELS diff --git a/include/dt-bindings/clock/qcom,dsi-phy-28nm.h b/include/dt-bindings/clock/qcom,dsi-phy-28nm.h new file mode 100644 index 000000000000..ab94d58377a1 --- /dev/null +++ b/include/dt-bindings/clock/qcom,dsi-phy-28nm.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ + +#ifndef _DT_BINDINGS_CLK_QCOM_DSI_PHY_28NM_H +#define _DT_BINDINGS_CLK_QCOM_DSI_PHY_28NM_H + +#define DSI_BYTE_PLL_CLK 0 +#define DSI_PIXEL_PLL_CLK 1 + +#endif From 5100ae76b5ab6afab33f38bc5850da2d076e5732 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Jan 2025 14:21:05 +0100 Subject: [PATCH 09/86] drm/msm/dsi/phy: Use the header with clock IDs Use the header with clock IDs to bind the interface between driver and DTS. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/634149/ Link: https://lore.kernel.org/r/20250127132105.107138-2-krzysztof.kozlowski@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/phy/dsi_phy.h | 5 ++--- drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c | 1 + drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c | 1 + drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c | 1 + drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c | 1 + drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c | 1 + 6 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h index 8985818bb2e0..1925418d9999 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h @@ -6,6 +6,7 @@ #ifndef __DSI_PHY_H__ #define __DSI_PHY_H__ +#include #include #include #include @@ -84,9 +85,7 @@ struct msm_dsi_dphy_timing { u8 hs_halfbyte_en_ckln; }; -#define DSI_BYTE_PLL_CLK 0 -#define DSI_PIXEL_PLL_CLK 1 -#define NUM_PROVIDED_CLKS 2 +#define NUM_PROVIDED_CLKS (DSI_PIXEL_PLL_CLK + 1) #define DSI_LANE_MAX 5 diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c index 677c62571811..9812b4d69197 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_10nm.c @@ -3,6 +3,7 @@ * Copyright (c) 2018, The Linux Foundation */ +#include #include #include #include diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c index 2c3cbe0f2870..3a1c8ece6657 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_14nm.c @@ -3,6 +3,7 @@ * Copyright (c) 2016, The Linux Foundation. All rights reserved. */ +#include #include #include #include diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c index 1383e3a4e050..90348a2af3e9 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c @@ -3,6 +3,7 @@ * Copyright (c) 2015, The Linux Foundation. All rights reserved. */ +#include #include #include diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c index 5311ab7f3c70..f3643320ff2f 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm_8960.c @@ -3,6 +3,7 @@ * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. */ +#include #include #include 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 798168180c1a..e496a95c34e9 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c @@ -3,6 +3,7 @@ * Copyright (c) 2018, The Linux Foundation */ +#include #include #include #include From baf49072877726616c7f5943a6b45eb86bfeca0a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 29 Jan 2025 12:55:04 +0100 Subject: [PATCH 10/86] drm/msm/dsi/phy: Program clock inverters in correct register Since SM8250 all downstream sources program clock inverters in PLL_CLOCK_INVERTERS_1 register and leave the PLL_CLOCK_INVERTERS as reset value (0x0). The most recent Hardware Programming Guide for 3 nm, 4 nm, 5 nm and 7 nm PHYs also mention PLL_CLOCK_INVERTERS_1. Signed-off-by: Krzysztof Kozlowski Fixes: 1ef7c99d145c ("drm/msm/dsi: add support for 7nm DSI PHY/PLL") Reviewed-by: Dmitry Baryshkov Reported-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/634489/ Link: https://lore.kernel.org/r/20250129115504.40080-1-krzysztof.kozlowski@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e496a95c34e9..3332399c7fd7 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c @@ -306,7 +306,7 @@ static void dsi_pll_commit(struct dsi_pll_7nm *pll, struct dsi_pll_config *confi writel(pll->phy->cphy_mode ? 0x00 : 0x10, base + REG_DSI_7nm_PHY_PLL_CMODE_1); writel(config->pll_clock_inverters, - base + REG_DSI_7nm_PHY_PLL_CLOCK_INVERTERS); + base + REG_DSI_7nm_PHY_PLL_CLOCK_INVERTERS_1); } static int dsi_pll_7nm_vco_set_rate(struct clk_hw *hw, unsigned long rate, From e05b233ae13b2ee6ea30d8c9f445dc5efbde6ce6 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 13 Feb 2025 17:27:56 +0100 Subject: [PATCH 11/86] dt-bindings: display: qcom,sm8550-mdss: explicitly document mdp0-mem and cpu-cfg interconnect paths The mdp1-mem is not supported on the SM8550 SoCs, and having maxItems=2 makes the bindings not clear if mdp0-mem/mdp1-mem or mdp0-mem/cpu-cfg is required, so explicitly document the mdp0-mem/cpu-cfg interconnect and add the cpu-cfg path in the example. Suggested-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Patchwork: https://patchwork.freedesktop.org/patch/637050/ Link: https://lore.kernel.org/r/20250213-topic-sm8x50-mdss-interconnect-bindings-fix-v4-1-3fa0bc42dd38@linaro.org Signed-off-by: Dmitry Baryshkov --- .../bindings/display/msm/qcom,sm8550-mdss.yaml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sm8550-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sm8550-mdss.yaml index 1ea50a2c7c8e..59192c59ddb9 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,sm8550-mdss.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,sm8550-mdss.yaml @@ -30,10 +30,14 @@ properties: maxItems: 1 interconnects: - maxItems: 2 + items: + - description: Interconnect path from mdp0 port to the data bus + - description: Interconnect path from CPU to the reg bus interconnect-names: - maxItems: 2 + items: + - const: mdp0-mem + - const: cpu-cfg patternProperties: "^display-controller@[0-9a-f]+$": @@ -91,9 +95,9 @@ examples: reg = <0x0ae00000 0x1000>; reg-names = "mdss"; - interconnects = <&mmss_noc MASTER_MDP 0 &gem_noc SLAVE_LLCC 0>, - <&mc_virt MASTER_LLCC 0 &mc_virt SLAVE_EBI1 0>; - interconnect-names = "mdp0-mem", "mdp1-mem"; + interconnects = <&mmss_noc MASTER_MDP 0 &mc_virt SLAVE_EBI1 0>, + <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_DISPLAY_CFG 0>; + interconnect-names = "mdp0-mem", "cpu-cfg"; resets = <&dispcc DISP_CC_MDSS_CORE_BCR>; From 162c57b8e7a1089d7db5a9ee8c1bff73edec1695 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 13 Feb 2025 17:27:57 +0100 Subject: [PATCH 12/86] dt-bindings: display: qcom,sm8650-mdss: explicitly document mdp0-mem and cpu-cfg interconnect paths The mdp1-mem is not supported on the SM8550 SoCs, and having maxItems=2 makes the bindings not clear if mdp0-mem/mdp1-mem or mdp0-mem/cpu-cfg is required, so explicitly document the mdp0-mem/cpu-cfg interconnect paths and complete the example with the missing interconnect paths. Suggested-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Signed-off-by: Neil Armstrong Patchwork: https://patchwork.freedesktop.org/patch/637051/ Link: https://lore.kernel.org/r/20250213-topic-sm8x50-mdss-interconnect-bindings-fix-v4-2-3fa0bc42dd38@linaro.org Signed-off-by: Dmitry Baryshkov --- .../bindings/display/msm/qcom,sm8650-mdss.yaml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sm8650-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sm8650-mdss.yaml index 24cece1e888b..a1c53e191033 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,sm8650-mdss.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,sm8650-mdss.yaml @@ -29,10 +29,14 @@ properties: maxItems: 1 interconnects: - maxItems: 2 + items: + - description: Interconnect path from mdp0 port to the data bus + - description: Interconnect path from CPU to the reg bus interconnect-names: - maxItems: 2 + items: + - const: mdp0-mem + - const: cpu-cfg patternProperties: "^display-controller@[0-9a-f]+$": @@ -75,12 +79,17 @@ examples: #include #include #include + #include display-subsystem@ae00000 { compatible = "qcom,sm8650-mdss"; reg = <0x0ae00000 0x1000>; reg-names = "mdss"; + interconnects = <&mmss_noc MASTER_MDP 0 &mc_virt SLAVE_EBI1 0>, + <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_DISPLAY_CFG 0>; + interconnect-names = "mdp0-mem", "cpu-cfg"; + resets = <&dispcc_core_bcr>; power-domains = <&dispcc_gdsc>; From 709cc0620107bd87e48e7d697f97ccc00c98c47d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 14 Feb 2025 14:17:44 +0100 Subject: [PATCH 13/86] drm/msm/dsi: Drop redundant NULL-ifying of clocks on error paths dsi_clk_init(), which gets the clocks, is called only through platform driver probe and its failure is a failure of the probe. Therefore NULL-ifying specific clocks is pointless and redundant - the PTR_ERR value stored there won't be used/dereferenced afterwards. What's more, variant-specific clock init calls like dsi_clk_init_6g_v2() are not doing this cleanup. Dropping redundant code allows later to make this a bit simpler. Reviewed-by: Abhinav Kumar Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/637303/ Link: https://lore.kernel.org/r/20250214-drm-msm-cleanups-v2-1-1bec50f37dc1@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 007311c21fda..397c9f1f5885 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -292,7 +292,6 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host) ret = PTR_ERR(msm_host->byte_clk); pr_err("%s: can't find dsi_byte clock. ret=%d\n", __func__, ret); - msm_host->byte_clk = NULL; goto exit; } @@ -301,7 +300,6 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host) ret = PTR_ERR(msm_host->pixel_clk); pr_err("%s: can't find dsi_pixel clock. ret=%d\n", __func__, ret); - msm_host->pixel_clk = NULL; goto exit; } @@ -310,7 +308,6 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host) ret = PTR_ERR(msm_host->esc_clk); pr_err("%s: can't find dsi_esc clock. ret=%d\n", __func__, ret); - msm_host->esc_clk = NULL; goto exit; } From d5bc3c3389d7850a3207f2a638966db6ecd30a5e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 14 Feb 2025 14:17:45 +0100 Subject: [PATCH 14/86] drm/msm/dsi: Simplify with dev_err_probe() dsi_get_config(), dsi_clk_init() and msm_dsi_host_init() are called only from platform driver probe function, so using dev_err_probe() is both appropriate and beneficial: - Properly marks device deferred probe status, - Avoids dmesg flood on probe deferrals, - Already incorporates printing ERR value, - Shows device name (in contrast to pr_err()), - Makes code smaller and simpler. Reviewed-by: Abhinav Kumar Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/637306/ Link: https://lore.kernel.org/r/20250214-drm-msm-cleanups-v2-2-1bec50f37dc1@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 100 ++++++++++++----------------- 1 file changed, 41 insertions(+), 59 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 397c9f1f5885..8fc9f5486aeb 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -200,7 +200,8 @@ static const struct msm_dsi_cfg_handler *dsi_get_config( ahb_clk = msm_clk_get(msm_host->pdev, "iface"); if (IS_ERR(ahb_clk)) { - pr_err("%s: cannot get interface clock\n", __func__); + dev_err_probe(dev, PTR_ERR(ahb_clk), "%s: cannot get interface clock\n", + __func__); goto exit; } @@ -208,13 +209,13 @@ static const struct msm_dsi_cfg_handler *dsi_get_config( ret = clk_prepare_enable(ahb_clk); if (ret) { - pr_err("%s: unable to enable ahb_clk\n", __func__); + dev_err_probe(dev, ret, "%s: unable to enable ahb_clk\n", __func__); goto runtime_put; } ret = dsi_get_version(msm_host->ctrl_base, &major, &minor); if (ret) { - pr_err("%s: Invalid version\n", __func__); + dev_err_probe(dev, ret, "%s: Invalid version\n", __func__); goto disable_clks; } @@ -281,39 +282,31 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host) msm_host->num_bus_clks = cfg->num_bus_clks; ret = devm_clk_bulk_get(&pdev->dev, msm_host->num_bus_clks, msm_host->bus_clks); - if (ret < 0) { - dev_err(&pdev->dev, "Unable to get clocks, ret = %d\n", ret); - goto exit; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Unable to get clocks\n"); /* get link and source clocks */ msm_host->byte_clk = msm_clk_get(pdev, "byte"); - if (IS_ERR(msm_host->byte_clk)) { - ret = PTR_ERR(msm_host->byte_clk); - pr_err("%s: can't find dsi_byte clock. ret=%d\n", - __func__, ret); - goto exit; - } + if (IS_ERR(msm_host->byte_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->byte_clk), + "%s: can't find dsi_byte clock\n", + __func__); msm_host->pixel_clk = msm_clk_get(pdev, "pixel"); - if (IS_ERR(msm_host->pixel_clk)) { - ret = PTR_ERR(msm_host->pixel_clk); - pr_err("%s: can't find dsi_pixel clock. ret=%d\n", - __func__, ret); - goto exit; - } + if (IS_ERR(msm_host->pixel_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->pixel_clk), + "%s: can't find dsi_pixel clock\n", + __func__); msm_host->esc_clk = msm_clk_get(pdev, "core"); - if (IS_ERR(msm_host->esc_clk)) { - ret = PTR_ERR(msm_host->esc_clk); - pr_err("%s: can't find dsi_esc clock. ret=%d\n", - __func__, ret); - goto exit; - } + if (IS_ERR(msm_host->esc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->esc_clk), + "%s: can't find dsi_esc clock\n", + __func__); if (cfg_hnd->ops->clk_init_ver) ret = cfg_hnd->ops->clk_init_ver(msm_host); -exit: + return ret; } @@ -1879,31 +1872,28 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) msm_dsi->host = &msm_host->base; ret = dsi_host_parse_dt(msm_host); - if (ret) { - pr_err("%s: failed to parse dt\n", __func__); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "%s: failed to parse dt\n", + __func__); msm_host->ctrl_base = msm_ioremap_size(pdev, "dsi_ctrl", &msm_host->ctrl_size); - if (IS_ERR(msm_host->ctrl_base)) { - pr_err("%s: unable to map Dsi ctrl base\n", __func__); - return PTR_ERR(msm_host->ctrl_base); - } + if (IS_ERR(msm_host->ctrl_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->ctrl_base), + "%s: unable to map Dsi ctrl base\n", __func__); pm_runtime_enable(&pdev->dev); msm_host->cfg_hnd = dsi_get_config(msm_host); - if (!msm_host->cfg_hnd) { - pr_err("%s: get config failed\n", __func__); - return -EINVAL; - } + if (!msm_host->cfg_hnd) + return dev_err_probe(&pdev->dev, -EINVAL, + "%s: get config failed\n", __func__); cfg = msm_host->cfg_hnd->cfg; msm_host->id = dsi_host_get_id(msm_host); - if (msm_host->id < 0) { - pr_err("%s: unable to identify DSI host index\n", __func__); - return msm_host->id; - } + if (msm_host->id < 0) + return dev_err_probe(&pdev->dev, msm_host->id, + "%s: unable to identify DSI host index\n", + __func__); /* fixup base address by io offset */ msm_host->ctrl_base += cfg->io_offset; @@ -1915,10 +1905,8 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) return ret; ret = dsi_clk_init(msm_host); - if (ret) { - pr_err("%s: unable to initialize dsi clks\n", __func__); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "%s: unable to initialize dsi clks\n", __func__); msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL); if (!msm_host->rx_buf) { @@ -1931,26 +1919,20 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) return ret; /* OPP table is optional */ ret = devm_pm_opp_of_add_table(&pdev->dev); - if (ret && ret != -ENODEV) { - dev_err(&pdev->dev, "invalid OPP table in device tree\n"); - return ret; - } + if (ret && ret != -ENODEV) + return dev_err_probe(&pdev->dev, ret, "invalid OPP table in device tree\n"); msm_host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); - if (!msm_host->irq) { - dev_err(&pdev->dev, "failed to get irq\n"); - return -EINVAL; - } + if (!msm_host->irq) + return dev_err_probe(&pdev->dev, -EINVAL, "failed to get irq\n"); /* do not autoenable, will be enabled later */ ret = devm_request_irq(&pdev->dev, msm_host->irq, dsi_host_irq, IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, "dsi_isr", msm_host); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request IRQ%u: %d\n", - msm_host->irq, ret); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to request IRQ%u\n", + msm_host->irq); init_completion(&msm_host->dma_comp); init_completion(&msm_host->video_comp); From cce156257ed3414ab104dc2360c5c4eb03ce7ed3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 14 Feb 2025 14:17:46 +0100 Subject: [PATCH 15/86] drm/msm/dsi: Minor whitespace and style cleanup Cleanup few obvious kernel coding style violations: missing or unnecessary braces in 'if-else', unnecessary break lines, incorrect breaking of long function declarations, unnecessary 'else' after a 'return'. No functional impact expected. Reviewed-by: Abhinav Kumar Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/637305/ Link: https://lore.kernel.org/r/20250214-drm-msm-cleanups-v2-3-1bec50f37dc1@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 51 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 8fc9f5486aeb..051e26ae1b7f 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -179,18 +179,18 @@ struct msm_dsi_host { int irq; }; - static inline u32 dsi_read(struct msm_dsi_host *msm_host, u32 reg) { return readl(msm_host->ctrl_base + reg); } + static inline void dsi_write(struct msm_dsi_host *msm_host, u32 reg, u32 data) { writel(data, msm_host->ctrl_base + reg); } -static const struct msm_dsi_cfg_handler *dsi_get_config( - struct msm_dsi_host *msm_host) +static const struct msm_dsi_cfg_handler * +dsi_get_config(struct msm_dsi_host *msm_host) { const struct msm_dsi_cfg_handler *cfg_hnd = NULL; struct device *dev = &msm_host->pdev->dev; @@ -370,7 +370,6 @@ int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host) return 0; } - int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host) { int ret; @@ -588,7 +587,6 @@ static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi) DBG("pclk=%lu, bclk=%lu", msm_host->pixel_clk_rate, msm_host->byte_clk_rate); - } int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi) @@ -677,8 +675,8 @@ static inline enum dsi_traffic_mode dsi_get_traffic_mode(const u32 mode_flags) return NON_BURST_SYNCH_EVENT; } -static inline enum dsi_vid_dst_format dsi_get_vid_fmt( - const enum mipi_dsi_pixel_format mipi_fmt) +static inline enum dsi_vid_dst_format +dsi_get_vid_fmt(const enum mipi_dsi_pixel_format mipi_fmt) { switch (mipi_fmt) { case MIPI_DSI_FMT_RGB888: return VID_DST_FORMAT_RGB888; @@ -689,8 +687,8 @@ static inline enum dsi_vid_dst_format dsi_get_vid_fmt( } } -static inline enum dsi_cmd_dst_format dsi_get_cmd_fmt( - const enum mipi_dsi_pixel_format mipi_fmt) +static inline enum dsi_cmd_dst_format +dsi_get_cmd_fmt(const enum mipi_dsi_pixel_format mipi_fmt) { switch (mipi_fmt) { case MIPI_DSI_FMT_RGB888: return CMD_DST_FORMAT_RGB888; @@ -1282,14 +1280,15 @@ static int dsi_cmd_dma_add(struct msm_dsi_host *msm_host, static int dsi_short_read1_resp(u8 *buf, const struct mipi_dsi_msg *msg) { u8 *data = msg->rx_buf; + if (data && (msg->rx_len >= 1)) { *data = buf[1]; /* strip out dcs type */ return 1; - } else { - pr_err("%s: read data does not match with rx_buf len %zu\n", - __func__, msg->rx_len); - return -EINVAL; } + + pr_err("%s: read data does not match with rx_buf len %zu\n", + __func__, msg->rx_len); + return -EINVAL; } /* @@ -1298,15 +1297,16 @@ static int dsi_short_read1_resp(u8 *buf, const struct mipi_dsi_msg *msg) static int dsi_short_read2_resp(u8 *buf, const struct mipi_dsi_msg *msg) { u8 *data = msg->rx_buf; + if (data && (msg->rx_len >= 2)) { data[0] = buf[1]; /* strip out dcs type */ data[1] = buf[2]; return 2; - } else { - pr_err("%s: read data does not match with rx_buf len %zu\n", - __func__, msg->rx_len); - return -EINVAL; } + + pr_err("%s: read data does not match with rx_buf len %zu\n", + __func__, msg->rx_len); + return -EINVAL; } static int dsi_long_read_resp(u8 *buf, const struct mipi_dsi_msg *msg) @@ -1366,8 +1366,9 @@ static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len) ret = -ETIMEDOUT; else ret = len; - } else + } else { ret = len; + } return ret; } @@ -1435,11 +1436,12 @@ static int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host, return len; } - /* for video mode, do not send cmds more than - * one pixel line, since it only transmit it - * during BLLP. - */ - /* TODO: if the command is sent in LP mode, the bit rate is only + /* + * for video mode, do not send cmds more than + * one pixel line, since it only transmit it + * during BLLP. + * + * TODO: if the command is sent in LP mode, the bit rate is only * half of esc clk rate. In this case, if the video is already * actively streaming, we need to check more carefully if the * command can be fit into one BLLP. @@ -1864,9 +1866,8 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) int ret; msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); - if (!msm_host) { + if (!msm_host) return -ENOMEM; - } msm_host->pdev = pdev; msm_dsi->host = &msm_host->base; From b39e7014ed3121f980d55097ec2bd9ecee3adc83 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 14 Feb 2025 14:17:47 +0100 Subject: [PATCH 16/86] drm/msm/dsi: Drop unnecessary -ENOMEM message Kernel core already prints detailed report about memory allocation failures, so drivers should not have their own error messages. Reviewed-by: Abhinav Kumar Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/637308/ Link: https://lore.kernel.org/r/20250214-drm-msm-cleanups-v2-4-1bec50f37dc1@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 051e26ae1b7f..2218d4f0c513 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1910,10 +1910,8 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) return dev_err_probe(&pdev->dev, ret, "%s: unable to initialize dsi clks\n", __func__); msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL); - if (!msm_host->rx_buf) { - pr_err("%s: alloc rx temp buf failed\n", __func__); + if (!msm_host->rx_buf) return -ENOMEM; - } ret = devm_pm_opp_set_clkname(&pdev->dev, "byte"); if (ret) From 14ad809ceb66d0874cbe4bd5ca9edf0de8d9ad96 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 17 Feb 2025 12:17:41 +0100 Subject: [PATCH 17/86] drm/msm/dsi: Use existing per-interface slice count in DSC timing When configuring the timing of DSI hosts (interfaces) in dsi_timing_setup() all values written to registers are taking bonded-mode into account by dividing the original mode width by 2 (half the data is sent over each of the two DSI hosts), but the full width instead of the interface width is passed as hdisplay parameter to dsi_update_dsc_timing(). Currently only msm_dsc_get_slices_per_intf() is called within dsi_update_dsc_timing() with the `hdisplay` argument which clearly documents that it wants the width of a single interface (which, again, in bonded DSI mode is half the total width of the mode) resulting in all subsequent values to be completely off. However, as soon as we start to pass the halved hdisplay into dsi_update_dsc_timing() we might as well discard msm_dsc_get_slices_per_intf() since the value it calculates is already available in dsc->slice_count which is per-interface by the current design of MSM DPU/DSI implementations and their use of the DRM DSC helpers. Fixes: 08802f515c3c ("drm/msm/dsi: Add support for DSC configuration") Reviewed-by: Dmitry Baryshkov Reviewed-by: Jessica Zhang Signed-off-by: Marijn Suijten Patchwork: https://patchwork.freedesktop.org/patch/637648/ Link: https://lore.kernel.org/r/20250217-drm-msm-initial-dualpipe-dsc-fixes-v3-1-913100d6103f@somainline.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 8 ++++---- drivers/gpu/drm/msm/msm_dsc_helper.h | 11 ----------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 2218d4f0c513..b6ae9717a5d3 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -834,7 +834,7 @@ static void dsi_ctrl_enable(struct msm_dsi_host *msm_host, dsi_write(msm_host, REG_DSI_CPHY_MODE_CTRL, BIT(0)); } -static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mode, u32 hdisplay) +static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mode) { struct drm_dsc_config *dsc = msm_host->dsc; u32 reg, reg_ctrl, reg_ctrl2; @@ -846,7 +846,7 @@ static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mod /* first calculate dsc parameters and then program * compress mode registers */ - slice_per_intf = msm_dsc_get_slices_per_intf(dsc, hdisplay); + slice_per_intf = dsc->slice_count; total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf; bytes_per_pkt = dsc->slice_chunk_size; /* * slice_per_pkt; */ @@ -979,7 +979,7 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) if (msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) { if (msm_host->dsc) - dsi_update_dsc_timing(msm_host, false, mode->hdisplay); + dsi_update_dsc_timing(msm_host, false); dsi_write(msm_host, REG_DSI_ACTIVE_H, DSI_ACTIVE_H_START(ha_start) | @@ -1000,7 +1000,7 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) DSI_ACTIVE_VSYNC_VPOS_END(vs_end)); } else { /* command mode */ if (msm_host->dsc) - dsi_update_dsc_timing(msm_host, true, mode->hdisplay); + dsi_update_dsc_timing(msm_host, true); /* image data and 1 byte write_memory_start cmd */ if (!msm_host->dsc) diff --git a/drivers/gpu/drm/msm/msm_dsc_helper.h b/drivers/gpu/drm/msm/msm_dsc_helper.h index b9049fe1e279..63f95523b2cb 100644 --- a/drivers/gpu/drm/msm/msm_dsc_helper.h +++ b/drivers/gpu/drm/msm/msm_dsc_helper.h @@ -12,17 +12,6 @@ #include #include -/** - * msm_dsc_get_slices_per_intf() - calculate number of slices per interface - * @dsc: Pointer to drm dsc config struct - * @intf_width: interface width in pixels - * Returns: Integer representing the number of slices for the given interface - */ -static inline u32 msm_dsc_get_slices_per_intf(const struct drm_dsc_config *dsc, u32 intf_width) -{ - return DIV_ROUND_UP(intf_width, dsc->slice_width); -} - /** * msm_dsc_get_bytes_per_line() - calculate bytes per line * @dsc: Pointer to drm dsc config struct From 660c396c98c061f9696bebacc178b74072e80054 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 17 Feb 2025 12:17:42 +0100 Subject: [PATCH 18/86] drm/msm/dsi: Set PHY usescase (and mode) before registering DSI host Ordering issues here cause an uninitialized (default STANDALONE) usecase to be programmed (which appears to be a MUX) in some cases when msm_dsi_host_register() is called, leading to the slave PLL in bonded-DSI mode to source from a clock parent (dsi1vco) that is off. This should seemingly not be a problem as the actual dispcc clocks from DSI1 that are muxed in the clock tree of DSI0 are way further down, this bit still seems to have an effect on them somehow and causes the right side of the panel controlled by DSI1 to not function. In an ideal world this code is refactored to no longer have such error-prone calls "across subsystems", and instead model the "PLL src" register field as a regular mux so that changing the clock parents programmatically or in DTS via `assigned-clock-parents` has the desired effect. But for the avid reader, the clocks that we *are* muxing into DSI0's tree are way further down, so if this bit turns out to be a simple mux between dsiXvco and out_div, that shouldn't have any effect as this whole tree is off anyway. Fixes: 57bf43389337 ("drm/msm/dsi: Pass down use case to PHY") Reviewed-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Signed-off-by: Marijn Suijten Patchwork: https://patchwork.freedesktop.org/patch/637650/ Link: https://lore.kernel.org/r/20250217-drm-msm-initial-dualpipe-dsc-fixes-v3-2-913100d6103f@somainline.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_manager.c | 32 ++++++++++++++++++--------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index a210b7c9e5ca..4fabb01345aa 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -74,17 +74,35 @@ static int dsi_mgr_setup_components(int id) int ret; if (!IS_BONDED_DSI()) { + /* + * Set the usecase before calling msm_dsi_host_register(), which would + * already program the PLL source mux based on a default usecase. + */ + msm_dsi_phy_set_usecase(msm_dsi->phy, MSM_DSI_PHY_STANDALONE); + msm_dsi_host_set_phy_mode(msm_dsi->host, msm_dsi->phy); + ret = msm_dsi_host_register(msm_dsi->host); if (ret) return ret; - - msm_dsi_phy_set_usecase(msm_dsi->phy, MSM_DSI_PHY_STANDALONE); - msm_dsi_host_set_phy_mode(msm_dsi->host, msm_dsi->phy); } else if (other_dsi) { struct msm_dsi *master_link_dsi = IS_MASTER_DSI_LINK(id) ? msm_dsi : other_dsi; struct msm_dsi *slave_link_dsi = IS_MASTER_DSI_LINK(id) ? other_dsi : msm_dsi; + + /* + * PLL0 is to drive both DSI link clocks in bonded DSI mode. + * + * Set the usecase before calling msm_dsi_host_register(), which would + * already program the PLL source mux based on a default usecase. + */ + msm_dsi_phy_set_usecase(clk_master_dsi->phy, + MSM_DSI_PHY_MASTER); + msm_dsi_phy_set_usecase(clk_slave_dsi->phy, + MSM_DSI_PHY_SLAVE); + msm_dsi_host_set_phy_mode(msm_dsi->host, msm_dsi->phy); + msm_dsi_host_set_phy_mode(other_dsi->host, other_dsi->phy); + /* Register slave host first, so that slave DSI device * has a chance to probe, and do not block the master * DSI device's probe. @@ -98,14 +116,6 @@ static int dsi_mgr_setup_components(int id) ret = msm_dsi_host_register(master_link_dsi->host); if (ret) return ret; - - /* PLL0 is to drive both 2 DSI link clocks in bonded DSI mode. */ - msm_dsi_phy_set_usecase(clk_master_dsi->phy, - MSM_DSI_PHY_MASTER); - msm_dsi_phy_set_usecase(clk_slave_dsi->phy, - MSM_DSI_PHY_SLAVE); - msm_dsi_host_set_phy_mode(msm_dsi->host, msm_dsi->phy); - msm_dsi_host_set_phy_mode(other_dsi->host, other_dsi->phy); } return 0; From d245ce568929e30f650e260631f7ad14970d7c2c Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Mon, 17 Feb 2025 12:17:43 +0100 Subject: [PATCH 19/86] drm/msm/dpu: Remove arbitrary limit of 1 interface in DSC topology When DSC is enabled the number of interfaces is forced to be 1, and documented that it is a "power-optimal" layout to use two DSC encoders together with two Layer Mixers. However, the same layout (two DSC hard-slice encoders with two LMs) is also used when the display is fed with data over two instead of one interface (common on 4k@120Hz smartphone panels with Dual-DSI). Solve this by simply removing the num_intf = 1 assignment as the count is already calculated by computing the number of physical encoders within the virtual encoder. Fixes: 7e9cc175b159 ("drm/msm/disp/dpu1: Add support for DSC in topology") Reviewed-by: Dmitry Baryshkov Reviewed-by: Jessica Zhang Signed-off-by: Marijn Suijten Patchwork: https://patchwork.freedesktop.org/patch/637649/ Link: https://lore.kernel.org/r/20250217-drm-msm-initial-dualpipe-dsc-fixes-v3-3-913100d6103f@somainline.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 938d8b05a4cd..32992e952553 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -689,20 +689,21 @@ static struct msm_display_topology dpu_encoder_get_topology( if (dsc) { /* - * Use 2 DSC encoders and 2 layer mixers per single interface + * Use 2 DSC encoders, 2 layer mixers and 1 or 2 interfaces * when Display Stream Compression (DSC) is enabled, * and when enough DSC blocks are available. * This is power-optimal and can drive up to (including) 4k * screens. */ - if (dpu_kms->catalog->dsc_count >= 2) { + WARN(topology.num_intf > 2, + "DSC topology cannot support more than 2 interfaces\n"); + if (intf_count >= 2 || dpu_kms->catalog->dsc_count >= 2) { topology.num_dsc = 2; topology.num_lm = 2; } else { topology.num_dsc = 1; topology.num_lm = 1; } - topology.num_intf = 1; } /* From b0e71c2637d1b99ff7093582cd3c9f0960619878 Mon Sep 17 00:00:00 2001 From: Eugene Lepshy Date: Tue, 18 Feb 2025 01:24:30 +0300 Subject: [PATCH 20/86] drm/msm/dsi: Allow values of 10 and 12 for bits per component The DRM DSC helper has various bits_per_component values - not just 8. But the DSC 1.1 block supports only 8, 10, and 12. Extend the guard accordingly and add a comment noting this limitation. Signed-off-by: Eugene Lepshy Co-developed-by: Danila Tikhonov Signed-off-by: Danila Tikhonov Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/637831/ Link: https://lore.kernel.org/r/20250217222431.82522-4-danila@jiaxyga.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index b6ae9717a5d3..5b245133a1d1 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1759,8 +1759,20 @@ static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc return -EINVAL; } - if (dsc->bits_per_component != 8) { - DRM_DEV_ERROR(&msm_host->pdev->dev, "DSI does not support bits_per_component != 8 yet\n"); + switch (dsc->bits_per_component) { + case 8: + case 10: + case 12: + /* + * Only 8, 10, and 12 bpc are supported for DSC 1.1 block. + * If additional bpc values need to be supported, update + * this quard with the appropriate DSC version verification. + */ + break; + default: + DRM_DEV_ERROR(&msm_host->pdev->dev, + "Unsupported bits_per_component value: %d\n", + dsc->bits_per_component); return -EOPNOTSUPP; } @@ -1771,7 +1783,7 @@ static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc drm_dsc_set_const_params(dsc); drm_dsc_set_rc_buf_thresh(dsc); - /* handle only bpp = bpc = 8, pre-SCR panels */ + /* DPU supports only pre-SCR panels */ ret = drm_dsc_setup_rc_params(dsc, DRM_DSC_1_1_PRE_SCR); if (ret) { DRM_DEV_ERROR(&msm_host->pdev->dev, "could not find DSC RC parameters\n"); From 52b3f0e118b1700e5c60ff676a1f522ce44fadc8 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 19 Feb 2025 12:07:12 +0800 Subject: [PATCH 21/86] drm/msm/dsi: Add check for devm_kstrdup() Add check for the return value of devm_kstrdup() in dsi_host_parse_dt() to catch potential exception. Fixes: 958d8d99ccb3 ("drm/msm/dsi: parse vsync source from device tree") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/638297/ Link: https://lore.kernel.org/r/20250219040712.2598161-1-haoxiang_li2024@163.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/dsi_host.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 5b245133a1d1..4d75529c0e85 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1831,8 +1831,15 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) __func__, ret); goto err; } - if (!ret) + if (!ret) { msm_dsi->te_source = devm_kstrdup(dev, te_source, GFP_KERNEL); + if (!msm_dsi->te_source) { + DRM_DEV_ERROR(dev, "%s: failed to allocate te_source\n", + __func__); + ret = -ENOMEM; + goto err; + } + } ret = 0; if (of_property_present(np, "syscon-sfpb")) { From de36ea80b303da228844cd9379232aa4e3825f58 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Feb 2025 17:23:32 +0100 Subject: [PATCH 22/86] drm/msm/dsi/phy: Use dsi_pll_cmn_clk_cfg1_update() when registering PLL Newly added dsi_pll_cmn_clk_cfg1_update() wrapper protects concurrent updates to PHY_CMN_CLK_CFG1 register between driver and Common Clock Framework. pll_7nm_register() still used in one place previous readl+writel, which can be simplified with this new wrapper. This is purely for readability and simplification and should have no functional impact, because the code touched here is before clock is registered via CCF, so there is no concurrency issue. Suggested-by: Dmitry Baryshkov Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/638323/ Link: https://lore.kernel.org/r/20250219-drm-msm-phy-pll-cfg-reg-v5-1-d28973fa513a@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c | 8 +++----- drivers/gpu/drm/msm/registers/display/dsi_phy_7nm.xml | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) 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 3332399c7fd7..098301880c26 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c @@ -737,11 +737,9 @@ static int pll_7nm_register(struct dsi_pll_7nm *pll_7nm, struct clk_hw **provide * don't register a pclk_mux clock and just use post_out_div instead */ if (pll_7nm->phy->cphy_mode) { - u32 data; - - data = readl(pll_7nm->phy->base + REG_DSI_7nm_PHY_CMN_CLK_CFG1); - writel(data | 3, pll_7nm->phy->base + REG_DSI_7nm_PHY_CMN_CLK_CFG1); - + dsi_pll_cmn_clk_cfg1_update(pll_7nm, + DSI_7nm_PHY_CMN_CLK_CFG1_DSICLK_SEL__MASK, + DSI_7nm_PHY_CMN_CLK_CFG1_DSICLK_SEL(3)); phy_pll_out_dsi_parent = pll_post_out_div; } else { snprintf(clk_name, sizeof(clk_name), "dsi%d_pclk_mux", pll_7nm->phy->id); diff --git a/drivers/gpu/drm/msm/registers/display/dsi_phy_7nm.xml b/drivers/gpu/drm/msm/registers/display/dsi_phy_7nm.xml index 35f7f40e405b..d2c8c46bb041 100644 --- a/drivers/gpu/drm/msm/registers/display/dsi_phy_7nm.xml +++ b/drivers/gpu/drm/msm/registers/display/dsi_phy_7nm.xml @@ -17,6 +17,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> + From 0699018b41d78abba8afd85b3f62348ed88080a9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 19 Feb 2025 17:23:33 +0100 Subject: [PATCH 23/86] drm/msm/dsi/phy: Define PHY_CMN_CLK_CFG[01] bitfields and simplify saving Add bitfields for PHY_CMN_CLK_CFG0 and PHY_CMN_CLK_CFG1 registers to avoid hard-coding bit masks and shifts and make the code a bit more readable. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/638324/ Link: https://lore.kernel.org/r/20250219-drm-msm-phy-pll-cfg-reg-v5-2-d28973fa513a@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 098301880c26..a92decbee5b5 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -573,11 +574,11 @@ static void dsi_7nm_pll_save_state(struct msm_dsi_phy *phy) cached->pll_out_div &= 0x3; cmn_clk_cfg0 = readl(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG0); - cached->bit_clk_div = cmn_clk_cfg0 & 0xf; - cached->pix_clk_div = (cmn_clk_cfg0 & 0xf0) >> 4; + cached->bit_clk_div = FIELD_GET(DSI_7nm_PHY_CMN_CLK_CFG0_DIV_CTRL_3_0__MASK, cmn_clk_cfg0); + cached->pix_clk_div = FIELD_GET(DSI_7nm_PHY_CMN_CLK_CFG0_DIV_CTRL_7_4__MASK, cmn_clk_cfg0); cmn_clk_cfg1 = readl(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1); - cached->pll_mux = cmn_clk_cfg1 & 0x3; + cached->pll_mux = FIELD_GET(DSI_7nm_PHY_CMN_CLK_CFG1_DSICLK_SEL__MASK, cmn_clk_cfg1); DBG("DSI PLL%d outdiv %x bit_clk_div %x pix_clk_div %x pll_mux %x", pll_7nm->phy->id, cached->pll_out_div, cached->bit_clk_div, @@ -599,7 +600,8 @@ static int dsi_7nm_pll_restore_state(struct msm_dsi_phy *phy) dsi_pll_cmn_clk_cfg0_write(pll_7nm, DSI_7nm_PHY_CMN_CLK_CFG0_DIV_CTRL_3_0(cached->bit_clk_div) | DSI_7nm_PHY_CMN_CLK_CFG0_DIV_CTRL_7_4(cached->pix_clk_div)); - dsi_pll_cmn_clk_cfg1_update(pll_7nm, 0x3, cached->pll_mux); + dsi_pll_cmn_clk_cfg1_update(pll_7nm, DSI_7nm_PHY_CMN_CLK_CFG1_DSICLK_SEL__MASK, + cached->pll_mux); ret = dsi_pll_7nm_vco_set_rate(phy->vco_hw, pll_7nm->vco_current_rate, From eabba31a839ae88f215f3048688e9691578a5594 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 19 Feb 2025 11:49:17 -0800 Subject: [PATCH 24/86] drm/msm: register a fault handler for display mmu faults In preparation to register a iommu fault handler for display related modules, register a fault handler for the backing mmu object of msm_kms. Currently, the fault handler only captures the display snapshot but we can expand this later if more information needs to be added to debug display mmu faults. Signed-off-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/638359/ Link: https://lore.kernel.org/r/20250219-abhinavk-smmu-fault-handler-v3-1-aa3f0bf4434a@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_kms.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c index 38965e12a6bf..738aaf4dac32 100644 --- a/drivers/gpu/drm/msm/msm_kms.c +++ b/drivers/gpu/drm/msm/msm_kms.c @@ -164,12 +164,23 @@ void msm_crtc_disable_vblank(struct drm_crtc *crtc) vblank_ctrl_queue_work(priv, crtc, false); } +static int msm_kms_fault_handler(void *arg, unsigned long iova, int flags, void *data) +{ + struct msm_kms *kms = arg; + + msm_disp_snapshot_state(kms->dev); + + return -ENOSYS; +} + struct msm_gem_address_space *msm_kms_init_aspace(struct drm_device *dev) { struct msm_gem_address_space *aspace; struct msm_mmu *mmu; struct device *mdp_dev = dev->dev; struct device *mdss_dev = mdp_dev->parent; + struct msm_drm_private *priv = dev->dev_private; + struct msm_kms *kms = priv->kms; struct device *iommu_dev; /* @@ -197,6 +208,8 @@ struct msm_gem_address_space *msm_kms_init_aspace(struct drm_device *dev) mmu->funcs->destroy(mmu); } + msm_mmu_set_fault_handler(aspace->mmu, kms, msm_kms_fault_handler); + return aspace; } From f66f3cf6bc42ab7cefd69c7e9d43f3a7d8f089fb Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 19 Feb 2025 11:49:18 -0800 Subject: [PATCH 25/86] drm/msm/iommu: rename msm_fault_handler to msm_gpu_fault_handler In preparation of registering a separate fault handler for display, lets rename the existing msm_fault_handler to msm_gpu_fault_handler. Signed-off-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/638358/ Link: https://lore.kernel.org/r/20250219-abhinavk-smmu-fault-handler-v3-2-aa3f0bf4434a@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_iommu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 2a94e82316f9..20518bf9898a 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -243,7 +243,7 @@ static const struct iommu_flush_ops tlb_ops = { .tlb_add_page = msm_iommu_tlb_add_page, }; -static int msm_fault_handler(struct iommu_domain *domain, struct device *dev, +static int msm_gpu_fault_handler(struct iommu_domain *domain, struct device *dev, unsigned long iova, int flags, void *arg); struct msm_mmu *msm_iommu_pagetable_create(struct msm_mmu *parent) @@ -319,7 +319,7 @@ struct msm_mmu *msm_iommu_pagetable_create(struct msm_mmu *parent) return &pagetable->base; } -static int msm_fault_handler(struct iommu_domain *domain, struct device *dev, +static int msm_gpu_fault_handler(struct iommu_domain *domain, struct device *dev, unsigned long iova, int flags, void *arg) { struct msm_iommu *iommu = arg; @@ -448,7 +448,7 @@ struct msm_mmu *msm_iommu_gpu_new(struct device *dev, struct msm_gpu *gpu, unsig return mmu; iommu = to_msm_iommu(mmu); - iommu_set_fault_handler(iommu->domain, msm_fault_handler, iommu); + iommu_set_fault_handler(iommu->domain, msm_gpu_fault_handler, iommu); /* Enable stall on iommu fault: */ if (adreno_smmu->set_stall) From c37d9f0b1a1873703afdc96d073bcf5f02742a09 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 19 Feb 2025 11:49:19 -0800 Subject: [PATCH 26/86] drm/msm/iommu: introduce msm_iommu_disp_new() for msm_kms Introduce a new API msm_iommu_disp_new() for display use-cases. Signed-off-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/638363/ Link: https://lore.kernel.org/r/20250219-abhinavk-smmu-fault-handler-v3-3-aa3f0bf4434a@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_iommu.c | 26 ++++++++++++++++++++++++++ drivers/gpu/drm/msm/msm_mmu.h | 1 + 2 files changed, 27 insertions(+) diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 20518bf9898a..b5d8503d28f9 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -343,6 +343,17 @@ static int msm_gpu_fault_handler(struct iommu_domain *domain, struct device *dev return 0; } +static int msm_disp_fault_handler(struct iommu_domain *domain, struct device *dev, + unsigned long iova, int flags, void *arg) +{ + struct msm_iommu *iommu = arg; + + if (iommu->base.handler) + return iommu->base.handler(iommu->base.arg, iova, flags, NULL); + + return -ENOSYS; +} + static void msm_iommu_resume_translation(struct msm_mmu *mmu) { struct adreno_smmu_priv *adreno_smmu = dev_get_drvdata(mmu->dev); @@ -437,6 +448,21 @@ struct msm_mmu *msm_iommu_new(struct device *dev, unsigned long quirks) return &iommu->base; } +struct msm_mmu *msm_iommu_disp_new(struct device *dev, unsigned long quirks) +{ + struct msm_iommu *iommu; + struct msm_mmu *mmu; + + mmu = msm_iommu_new(dev, quirks); + if (IS_ERR_OR_NULL(mmu)) + return mmu; + + iommu = to_msm_iommu(mmu); + iommu_set_fault_handler(iommu->domain, msm_disp_fault_handler, iommu); + + return mmu; +} + struct msm_mmu *msm_iommu_gpu_new(struct device *dev, struct msm_gpu *gpu, unsigned long quirks) { struct adreno_smmu_priv *adreno_smmu = dev_get_drvdata(dev); diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index 88af4f490881..730458d08d6b 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -42,6 +42,7 @@ static inline void msm_mmu_init(struct msm_mmu *mmu, struct device *dev, struct msm_mmu *msm_iommu_new(struct device *dev, unsigned long quirks); struct msm_mmu *msm_iommu_gpu_new(struct device *dev, struct msm_gpu *gpu, unsigned long quirks); +struct msm_mmu *msm_iommu_disp_new(struct device *dev, unsigned long quirks); static inline void msm_mmu_set_fault_handler(struct msm_mmu *mmu, void *arg, int (*handler)(void *arg, unsigned long iova, int flags, void *data)) From 85bdbd8306d8330bf039cc22d9ecf20a4ee73402 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 19 Feb 2025 11:49:20 -0800 Subject: [PATCH 27/86] drm/msm: switch msm_kms to use msm_iommu_disp_new() Switch msm_kms to use msm_iommu_disp_new() so that the newly registered fault handler will kick-in during any mmu faults. Signed-off-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/638360/ Link: https://lore.kernel.org/r/20250219-abhinavk-smmu-fault-handler-v3-4-aa3f0bf4434a@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_kms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c index 738aaf4dac32..35b835e0f23a 100644 --- a/drivers/gpu/drm/msm/msm_kms.c +++ b/drivers/gpu/drm/msm/msm_kms.c @@ -192,7 +192,7 @@ struct msm_gem_address_space *msm_kms_init_aspace(struct drm_device *dev) else iommu_dev = mdss_dev; - mmu = msm_iommu_new(iommu_dev, 0); + mmu = msm_iommu_disp_new(iommu_dev, 0); if (IS_ERR(mmu)) return ERR_CAST(mmu); From 89839e69f6154feecd79bd01171375225b0296e9 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 19 Feb 2025 11:49:21 -0800 Subject: [PATCH 28/86] drm/msm/dpu: rate limit snapshot capture for mmu faults There is no recovery mechanism in place yet to recover from mmu faults for DPU. We can only prevent the faults by making sure there is no misconfiguration. Rate-limit the snapshot capture for mmu faults to once per msm_atomic_commit_tail() as that should be sufficient to capture the snapshot for debugging otherwise there will be a lot of DPU snapshots getting captured for the same fault which is redundant and also might affect capturing even one snapshot accurately. Signed-off-by: Abhinav Kumar Signed-off-by: Jessica Zhang Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/638362/ Link: https://lore.kernel.org/r/20250219-abhinavk-smmu-fault-handler-v3-5-aa3f0bf4434a@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_atomic.c | 2 ++ drivers/gpu/drm/msm/msm_kms.c | 5 ++++- drivers/gpu/drm/msm/msm_kms.h | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index a7a2384044ff..83c4e6aca209 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -221,6 +221,8 @@ void msm_atomic_commit_tail(struct drm_atomic_state *state) kms->funcs->wait_flush(kms, crtc_mask); trace_msm_atomic_wait_flush_finish(crtc_mask); + atomic_set(&kms->fault_snapshot_capture, 0); + /* * Now that there is no in-progress flush, prepare the * current update: diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c index 35b835e0f23a..b877278888e6 100644 --- a/drivers/gpu/drm/msm/msm_kms.c +++ b/drivers/gpu/drm/msm/msm_kms.c @@ -168,7 +168,10 @@ static int msm_kms_fault_handler(void *arg, unsigned long iova, int flags, void { struct msm_kms *kms = arg; - msm_disp_snapshot_state(kms->dev); + if (atomic_read(&kms->fault_snapshot_capture) == 0) { + msm_disp_snapshot_state(kms->dev); + atomic_inc(&kms->fault_snapshot_capture); + } return -ENOSYS; } diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index e60162744c66..3e28c4e012d2 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -128,6 +128,9 @@ struct msm_kms { int irq; bool irq_requested; + /* rate limit the snapshot capture to once per attach */ + atomic_t fault_snapshot_capture; + /* mapper-id used to request GEM buffer mapped for scanout: */ struct msm_gem_address_space *aspace; From f561db72a663f8a73c2250bf3244ce1ce221bed7 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Wed, 26 Feb 2025 01:22:14 +0530 Subject: [PATCH 29/86] drm/msm/a6xx: Fix stale rpmh votes from GPU It was observed on sc7180 (A618 gpu) that GPU votes for GX rail and CNOC BCM nodes were not removed after GPU suspend. This was because we skipped sending 'prepare-slumber' request to gmu during suspend sequence in some cases. So, make sure we always call prepare-slumber hfi during suspend. Also, calling prepare-slumber without a prior oob-gpu handshake messes up gmu firmware's internal state. So, do that when required. Fixes: 4b565ca5a2cb ("drm/msm: Add A6XX device support") Cc: stable@vger.kernel.org Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/639569/ Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 72 +++++++++++++++------------ 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c index 699b0dd34b18..38c94915d4c9 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c @@ -1169,49 +1169,50 @@ static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu) struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; u32 val; + int ret; /* - * The GMU may still be in slumber unless the GPU started so check and - * skip putting it back into slumber if so + * GMU firmware's internal power state gets messed up if we send "prepare_slumber" hfi when + * oob_gpu handshake wasn't done after the last wake up. So do a dummy handshake here when + * required */ - val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE); + if (adreno_gpu->base.needs_hw_init) { + if (a6xx_gmu_set_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET)) + goto force_off; - if (val != 0xf) { - int ret = a6xx_gmu_wait_for_idle(gmu); + a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET); + } - /* If the GMU isn't responding assume it is hung */ - if (ret) { - a6xx_gmu_force_off(gmu); - return; - } + ret = a6xx_gmu_wait_for_idle(gmu); - a6xx_bus_clear_pending_transactions(adreno_gpu, a6xx_gpu->hung); + /* If the GMU isn't responding assume it is hung */ + if (ret) + goto force_off; - /* tell the GMU we want to slumber */ - ret = a6xx_gmu_notify_slumber(gmu); - if (ret) { - a6xx_gmu_force_off(gmu); - return; - } + a6xx_bus_clear_pending_transactions(adreno_gpu, a6xx_gpu->hung); - ret = gmu_poll_timeout(gmu, - REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, val, - !(val & A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS_GPUBUSYIGNAHB), - 100, 10000); + /* tell the GMU we want to slumber */ + ret = a6xx_gmu_notify_slumber(gmu); + if (ret) + goto force_off; - /* - * Let the user know we failed to slumber but don't worry too - * much because we are powering down anyway - */ + ret = gmu_poll_timeout(gmu, + REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, val, + !(val & A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS_GPUBUSYIGNAHB), + 100, 10000); - if (ret) - DRM_DEV_ERROR(gmu->dev, - "Unable to slumber GMU: status = 0%x/0%x\n", - gmu_read(gmu, - REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS), - gmu_read(gmu, - REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS2)); - } + /* + * Let the user know we failed to slumber but don't worry too + * much because we are powering down anyway + */ + + if (ret) + DRM_DEV_ERROR(gmu->dev, + "Unable to slumber GMU: status = 0%x/0%x\n", + gmu_read(gmu, + REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS), + gmu_read(gmu, + REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS2)); /* Turn off HFI */ a6xx_hfi_stop(gmu); @@ -1221,6 +1222,11 @@ static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu) /* Tell RPMh to power off the GPU */ a6xx_rpmh_stop(gmu); + + return; + +force_off: + a6xx_gmu_force_off(gmu); } From 977e4ef27591a41cab8ff43cf390d32936d4b7f5 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 25 Feb 2025 14:52:43 -0800 Subject: [PATCH 30/86] drm/msm: Expose DRIVER_SYNCOBJ_TIMELINE Initially we didn't want to expose the cap, as it would expose a bug in the vk driver (turnip) with older mesa versions. This was fixed over a year ago (and cherry-picked to stable release branches at the time), see https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25981. So let's go ahead and expose it now. Signed-off-by: Rob Clark Reviewed-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/639610/ --- drivers/gpu/drm/msm/msm_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index ff7a7a9f7b0d..c3588dc9e537 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -894,6 +894,7 @@ static const struct drm_driver msm_driver = { DRIVER_RENDER | DRIVER_ATOMIC | DRIVER_MODESET | + DRIVER_SYNCOBJ_TIMELINE | DRIVER_SYNCOBJ, .open = msm_open, .postclose = msm_postclose, From d7a5ac67d82c50c1f909c7056f78b1630a0f71cf Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 10 Dec 2024 08:51:22 -0800 Subject: [PATCH 31/86] drm/msm: Extend gpu devcore dumps with pgtbl info In the case of iova fault triggered devcore dumps, include additional debug information based on what we think is the current page tables, including the TTBR0 value (which should match what we have in adreno_smmu_fault_info unless things have gone horribly wrong), and the pagetable entries traversed in the process of resolving the faulting iova. Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/628117/ --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 10 ++++++++++ drivers/gpu/drm/msm/msm_gpu.c | 9 +++++++++ drivers/gpu/drm/msm/msm_gpu.h | 8 ++++++++ drivers/gpu/drm/msm/msm_iommu.c | 22 ++++++++++++++++++++++ drivers/gpu/drm/msm/msm_mmu.h | 3 ++- 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 1238f3265978..7156cda07b03 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -883,6 +883,16 @@ void adreno_show(struct msm_gpu *gpu, struct msm_gpu_state *state, drm_printf(p, " - dir=%s\n", info->flags & IOMMU_FAULT_WRITE ? "WRITE" : "READ"); drm_printf(p, " - type=%s\n", info->type); drm_printf(p, " - source=%s\n", info->block); + + /* Information extracted from what we think are the current + * pgtables. Hopefully the TTBR0 matches what we've extracted + * from the SMMU registers in smmu_info! + */ + drm_puts(p, "pgtable-fault-info:\n"); + drm_printf(p, " - ttbr0: %.16llx\n", (u64)info->pgtbl_ttbr0); + drm_printf(p, " - asid: %d\n", info->asid); + drm_printf(p, " - ptes: %.16llx %.16llx %.16llx %.16llx\n", + info->ptes[0], info->ptes[1], info->ptes[2], info->ptes[3]); } drm_printf(p, "rbbm-status: 0x%08x\n", state->rbbm_status); diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 8557998e0c92..c380d9d9f5af 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -281,6 +281,15 @@ static void msm_gpu_crashstate_capture(struct msm_gpu *gpu, if (submit) { int i; + if (state->fault_info.ttbr0) { + struct msm_gpu_fault_info *info = &state->fault_info; + struct msm_mmu *mmu = submit->aspace->mmu; + + msm_iommu_pagetable_params(mmu, &info->pgtbl_ttbr0, + &info->asid); + msm_iommu_pagetable_walk(mmu, info->iova, info->ptes); + } + state->bos = kcalloc(submit->nr_bos, sizeof(struct msm_gpu_state_bo), GFP_KERNEL); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 7cabc8480d7c..e25009150579 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -101,6 +101,14 @@ struct msm_gpu_fault_info { int flags; const char *type; const char *block; + + /* Information about what we think/expect is the current SMMU state, + * for example expected_ttbr0 should match smmu_info.ttbr0 which + * was read back from SMMU registers. + */ + phys_addr_t pgtbl_ttbr0; + u64 ptes[4]; + int asid; }; /** diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index b5d8503d28f9..fd73dcd3f30e 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -195,6 +195,28 @@ struct iommu_domain_geometry *msm_iommu_get_geometry(struct msm_mmu *mmu) return &iommu->domain->geometry; } +int +msm_iommu_pagetable_walk(struct msm_mmu *mmu, unsigned long iova, uint64_t ptes[4]) +{ + struct msm_iommu_pagetable *pagetable; + struct arm_lpae_io_pgtable_walk_data wd = {}; + + if (mmu->type != MSM_MMU_IOMMU_PAGETABLE) + return -EINVAL; + + pagetable = to_pagetable(mmu); + + if (!pagetable->pgtbl_ops->pgtable_walk) + return -EINVAL; + + pagetable->pgtbl_ops->pgtable_walk(pagetable->pgtbl_ops, iova, &wd); + + for (int i = 0; i < ARRAY_SIZE(wd.ptes); i++) + ptes[i] = wd.ptes[i]; + + return 0; +} + static const struct msm_mmu_funcs pagetable_funcs = { .map = msm_iommu_pagetable_map, .unmap = msm_iommu_pagetable_unmap, diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index 730458d08d6b..daf91529e02b 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -54,7 +54,8 @@ static inline void msm_mmu_set_fault_handler(struct msm_mmu *mmu, void *arg, struct msm_mmu *msm_iommu_pagetable_create(struct msm_mmu *parent); int msm_iommu_pagetable_params(struct msm_mmu *mmu, phys_addr_t *ttbr, - int *asid); + int *asid); +int msm_iommu_pagetable_walk(struct msm_mmu *mmu, unsigned long iova, uint64_t ptes[4]); struct iommu_domain_geometry *msm_iommu_get_geometry(struct msm_mmu *mmu); #endif /* __MSM_MMU_H__ */ From 0b305b7cadce835505bd93183a599acb1f800a05 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 17 Feb 2025 10:32:11 +0300 Subject: [PATCH 32/86] drm/msm/gem: Fix error code msm_parse_deps() The SUBMIT_ERROR() macro turns the error code negative. This extra '-' operation turns it back to positive EINVAL again. The error code is passed to ERR_PTR() and since positive values are not an IS_ERR() it eventually will lead to an oops. Delete the '-'. Fixes: 866e43b945bf ("drm/msm: UAPI error reporting") Signed-off-by: Dan Carpenter Patchwork: https://patchwork.freedesktop.org/patch/637625/ Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/msm_gem_submit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index dee470403036..3e9aa2cc38ef 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -509,7 +509,7 @@ static struct drm_syncobj **msm_parse_deps(struct msm_gem_submit *submit, } if (syncobj_desc.flags & ~MSM_SUBMIT_SYNCOBJ_FLAGS) { - ret = -SUBMIT_ERROR(EINVAL, submit, "invalid syncobj flags: %x", syncobj_desc.flags); + ret = SUBMIT_ERROR(EINVAL, submit, "invalid syncobj flags: %x", syncobj_desc.flags); break; } From 378a6219993e93d79ffc4b593f53370bc57f3b1f Mon Sep 17 00:00:00 2001 From: Jie Zhang Date: Fri, 28 Feb 2025 01:37:49 +0530 Subject: [PATCH 33/86] drm/msm/a6xx: Split out gpucc register block Some GPUs have different memory map for GPUCC block. So split out the gpucc range from a6xx_gmu_cx_registers to a separate block to accommodate those GPUs. Signed-off-by: Jie Zhang Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/640052/ Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 8 +++++--- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h | 5 +++++ 2 files changed, 10 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 0fcae53c0b14..81763876e402 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -1214,18 +1214,20 @@ static void a6xx_get_gmu_registers(struct msm_gpu *gpu, struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); a6xx_state->gmu_registers = state_kcalloc(a6xx_state, - 3, sizeof(*a6xx_state->gmu_registers)); + 4, sizeof(*a6xx_state->gmu_registers)); if (!a6xx_state->gmu_registers) return; - a6xx_state->nr_gmu_registers = 3; + a6xx_state->nr_gmu_registers = 4; /* Get the CX GMU registers from AHB */ _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gmu_reglist[0], &a6xx_state->gmu_registers[0], false); _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gmu_reglist[1], &a6xx_state->gmu_registers[1], true); + _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gpucc_reg, + &a6xx_state->gmu_registers[2], false); if (!a6xx_gmu_gx_is_on(&a6xx_gpu->gmu)) return; @@ -1234,7 +1236,7 @@ static void a6xx_get_gmu_registers(struct msm_gpu *gpu, gpu_write(gpu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0); _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gmu_reglist[2], - &a6xx_state->gmu_registers[2], false); + &a6xx_state->gmu_registers[3], false); } static struct msm_gpu_state_bo *a6xx_snapshot_gmu_bo( diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h index dd4c28a8d923..31c7462ab6d7 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h @@ -363,6 +363,9 @@ static const u32 a6xx_gmu_cx_registers[] = { 0x51e0, 0x51e2, 0x51f0, 0x51f0, 0x5200, 0x5201, /* GMU AO */ 0x9300, 0x9316, 0x9400, 0x9400, +}; + +static const u32 a6xx_gmu_gpucc_registers[] = { /* GPU CC */ 0x9800, 0x9812, 0x9840, 0x9852, 0x9c00, 0x9c04, 0x9c07, 0x9c0b, 0x9c15, 0x9c1c, 0x9c1e, 0x9c2d, 0x9c3c, 0x9c3d, 0x9c3f, 0x9c40, @@ -386,6 +389,8 @@ static const struct a6xx_registers a6xx_gmu_reglist[] = { REGS(a6xx_gmu_gx_registers, 0, 0), }; +static const struct a6xx_registers a6xx_gpucc_reg = REGS(a6xx_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 11cdb81b3c1b974d179e217babf4c2681c6cc9ed Mon Sep 17 00:00:00 2001 From: Jie Zhang Date: Fri, 28 Feb 2025 01:37:50 +0530 Subject: [PATCH 34/86] drm/msm/a6xx: Fix gpucc register block for A621 Adreno 621 has a different memory map for GPUCC block. So update a6xx_gpu_state code to dump the correct set of gpucc registers. Signed-off-by: Jie Zhang Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/640055/ Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 9 +++++++-- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index 81763876e402..2c10474ccc95 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -1226,8 +1226,13 @@ static void a6xx_get_gmu_registers(struct msm_gpu *gpu, &a6xx_state->gmu_registers[0], false); _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gmu_reglist[1], &a6xx_state->gmu_registers[1], true); - _a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gpucc_reg, - &a6xx_state->gmu_registers[2], false); + + if (adreno_is_a621(adreno_gpu)) + _a6xx_get_gmu_registers(gpu, a6xx_state, &a621_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); if (!a6xx_gmu_gx_is_on(&a6xx_gpu->gmu)) return; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h index 31c7462ab6d7..e545106c70be 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h @@ -376,6 +376,17 @@ static const u32 a6xx_gmu_gpucc_registers[] = { 0xbc00, 0xbc16, 0xbc20, 0xbc27, }; +static const u32 a621_gmu_gpucc_registers[] = { + /* GPU CC */ + 0x9800, 0x980e, 0x9c00, 0x9c0e, 0xb000, 0xb004, 0xb400, 0xb404, + 0xb800, 0xb804, 0xbc00, 0xbc05, 0xbc14, 0xbc1d, 0xbc2a, 0xbc30, + 0xbc32, 0xbc32, 0xbc41, 0xbc55, 0xbc66, 0xbc68, 0xbc78, 0xbc7a, + 0xbc89, 0xbc8a, 0xbc9c, 0xbc9e, 0xbca0, 0xbca3, 0xbcb3, 0xbcb5, + 0xbcc5, 0xbcc7, 0xbcd6, 0xbcd8, 0xbce8, 0xbce9, 0xbcf9, 0xbcfc, + 0xbd0b, 0xbd0c, 0xbd1c, 0xbd1e, 0xbd40, 0xbd70, 0xbe00, 0xbe16, + 0xbe20, 0xbe2d, +}; + static const u32 a6xx_gmu_cx_rscc_registers[] = { /* GPU RSCC */ 0x008c, 0x008c, 0x0101, 0x0102, 0x0340, 0x0342, 0x0344, 0x0347, @@ -390,6 +401,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 u32 a6xx_get_cp_roq_size(struct msm_gpu *gpu); static u32 a7xx_get_cp_roq_size(struct msm_gpu *gpu); From b1f07bc58d544787cc48502fa3ea6edf0bbb5522 Mon Sep 17 00:00:00 2001 From: Jie Zhang Date: Fri, 28 Feb 2025 01:37:51 +0530 Subject: [PATCH 35/86] drm/msm/a6xx: Add support for Adreno 623 Add support for Adreno 623 GPU found in QCS8300 chipsets. Signed-off-by: Jie Zhang Signed-off-by: Akhil P Oommen Patchwork: https://patchwork.freedesktop.org/patch/640056/ Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_catalog.c | 29 +++++++++++++++++++++ drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 8 ++++++ drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 2 +- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 5 ++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index edffb7737a97..53e2ff4406d8 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c @@ -879,6 +879,35 @@ static const struct adreno_info a6xx_gpus[] = { { 0, 0 }, { 137, 1 }, ), + }, { + .chip_ids = ADRENO_CHIP_IDS(0x06020300), + .family = ADRENO_6XX_GEN3, + .fw = { + [ADRENO_FW_SQE] = "a650_sqe.fw", + [ADRENO_FW_GMU] = "a623_gmu.bin", + }, + .gmem = SZ_512K, + .inactive_period = DRM_MSM_INACTIVE_PERIOD, + .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT | + ADRENO_QUIRK_HAS_HW_APRIV, + .init = a6xx_gpu_init, + .a6xx = &(const struct a6xx_info) { + .hwcg = a690_hwcg, + .protect = &a650_protect, + .gmu_cgc_mode = 0x00020200, + .prim_fifo_threshold = 0x00010000, + .bcms = (const struct a6xx_bcm[]) { + { .name = "SH0", .buswidth = 16 }, + { .name = "MC0", .buswidth = 4 }, + { + .name = "ACV", + .fixed = true, + .perfmode = BIT(3), + }, + { /* sentinel */ }, + }, + }, + .address_space_size = SZ_16G, }, { .chip_ids = ADRENO_CHIP_IDS( 0x06030001, diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index 0ae29a7c8a4d..1820c167fcee 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -616,6 +616,14 @@ static void a6xx_calc_ubwc_config(struct adreno_gpu *gpu) gpu->ubwc_config.uavflagprd_inv = 2; } + if (adreno_is_a623(gpu)) { + gpu->ubwc_config.highest_bank_bit = 16; + gpu->ubwc_config.amsbc = 1; + gpu->ubwc_config.rgb565_predicator = 1; + gpu->ubwc_config.uavflagprd_inv = 2; + gpu->ubwc_config.macrotile_mode = 1; + } + if (adreno_is_a640_family(gpu)) gpu->ubwc_config.amsbc = 1; diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index 2c10474ccc95..3222a406d089 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -1227,7 +1227,7 @@ 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)) + if (adreno_is_a621(adreno_gpu) || adreno_is_a623(adreno_gpu)) _a6xx_get_gmu_registers(gpu, a6xx_state, &a621_gpucc_reg, &a6xx_state->gmu_registers[2], false); else diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index dcf454629ce0..92caba3584da 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -442,6 +442,11 @@ static inline int adreno_is_a621(const struct adreno_gpu *gpu) return gpu->info->chip_ids[0] == 0x06020100; } +static inline int adreno_is_a623(const struct adreno_gpu *gpu) +{ + return gpu->info->chip_ids[0] == 0x06020300; +} + static inline int adreno_is_a630(const struct adreno_gpu *gpu) { return adreno_is_revn(gpu, 630); From 78e70fa099b9ed0a14e65ba80fd61347dcddabb2 Mon Sep 17 00:00:00 2001 From: Jie Zhang Date: Fri, 28 Feb 2025 01:37:52 +0530 Subject: [PATCH 36/86] dt-bindings: display/msm/gmu: Add Adreno 623 GMU Document Adreno 623 GMU in the dt-binding specification. Signed-off-by: Jie Zhang Signed-off-by: Akhil P Oommen Reviewed-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/640058/ Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/display/msm/gmu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/msm/gmu.yaml b/Documentation/devicetree/bindings/display/msm/gmu.yaml index ab884e236429..4392aa7a4ffe 100644 --- a/Documentation/devicetree/bindings/display/msm/gmu.yaml +++ b/Documentation/devicetree/bindings/display/msm/gmu.yaml @@ -123,6 +123,7 @@ allOf: compatible: contains: enum: + - qcom,adreno-gmu-623.0 - qcom,adreno-gmu-635.0 - qcom,adreno-gmu-660.1 - qcom,adreno-gmu-663.0 From 2dde2aadaed113feb724c19063ac61e2f6ba61a4 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 23 Jan 2025 14:43:36 +0200 Subject: [PATCH 37/86] drm/msm/dpu: don't set crtc_state->mode_changed from atomic_check() The MSM driver uses drm_atomic_helper_check() which mandates that none of the atomic_check() callbacks toggles crtc_state->mode_changed. Perform corresponding check before calling the drm_atomic_helper_check() function. Fixes: 8b45a26f2ba9 ("drm/msm/dpu: reserve cdm blocks for writeback in case of YUV output") Reported-by: Simona Vetter Closes: https://lore.kernel.org/dri-devel/ZtW_S0j5AEr4g0QW@phenom.ffwll.local/ Reviewed-by: Abhinav Kumar [DB: dropped the WARN_ON] Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/633400/ Link: https://lore.kernel.org/r/20250123-drm-dirty-modeset-v2-4-bbfd3a6cd1a4@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 32 ++++++++++++++++++--- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 4 +++ drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 24 ++++++++++++++++ drivers/gpu/drm/msm/msm_atomic.c | 13 ++++++++- drivers/gpu/drm/msm/msm_kms.h | 7 +++++ 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 32992e952553..1fc220a2ce0d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -760,6 +760,34 @@ static void dpu_encoder_assign_crtc_resources(struct dpu_kms *dpu_kms, cstate->num_mixers = num_lm; } +/** + * dpu_encoder_virt_check_mode_changed: check if full modeset is required + * @drm_enc: Pointer to drm encoder structure + * @crtc_state: Corresponding CRTC state to be checked + * @conn_state: Corresponding Connector's state to be checked + * + * Check if the changes in the object properties demand full mode set. + */ +int dpu_encoder_virt_check_mode_changed(struct drm_encoder *drm_enc, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); + struct msm_display_topology topology; + + DPU_DEBUG_ENC(dpu_enc, "\n"); + + /* Using mode instead of adjusted_mode as it wasn't computed yet */ + topology = dpu_encoder_get_topology(dpu_enc, &crtc_state->mode, crtc_state, conn_state); + + if (topology.needs_cdm && !dpu_enc->cur_master->hw_cdm) + crtc_state->mode_changed = true; + else if (!topology.needs_cdm && dpu_enc->cur_master->hw_cdm) + crtc_state->mode_changed = true; + + return 0; +} + static int dpu_encoder_virt_atomic_check( struct drm_encoder *drm_enc, struct drm_crtc_state *crtc_state, @@ -793,10 +821,6 @@ static int dpu_encoder_virt_atomic_check( topology = dpu_encoder_get_topology(dpu_enc, adj_mode, crtc_state, conn_state); - if (topology.needs_cdm && !dpu_enc->cur_master->hw_cdm) - crtc_state->mode_changed = true; - else if (!topology.needs_cdm && dpu_enc->cur_master->hw_cdm) - crtc_state->mode_changed = true; /* * Release and Allocate resources on every modeset */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index 92b5ee390788..da133ee4701a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -88,4 +88,8 @@ void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc); +int dpu_encoder_virt_check_mode_changed(struct drm_encoder *drm_enc, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); + #endif /* __DPU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 97e9cb8c2b09..8741dc6fc8dd 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -446,6 +446,29 @@ static void dpu_kms_disable_commit(struct msm_kms *kms) pm_runtime_put_sync(&dpu_kms->pdev->dev); } +static int dpu_kms_check_mode_changed(struct msm_kms *kms, struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state; + struct drm_connector *connector; + struct drm_connector_state *new_conn_state; + int i; + + for_each_new_connector_in_state(state, connector, new_conn_state, i) { + struct drm_encoder *encoder; + + if (!new_conn_state->crtc || !new_conn_state->best_encoder) + continue; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc); + + encoder = new_conn_state->best_encoder; + + dpu_encoder_virt_check_mode_changed(encoder, new_crtc_state, new_conn_state); + } + + return 0; +} + static void dpu_kms_flush_commit(struct msm_kms *kms, unsigned crtc_mask) { struct dpu_kms *dpu_kms = to_dpu_kms(kms); @@ -1062,6 +1085,7 @@ static const struct msm_kms_funcs kms_funcs = { .irq = dpu_core_irq, .enable_commit = dpu_kms_enable_commit, .disable_commit = dpu_kms_disable_commit, + .check_mode_changed = dpu_kms_check_mode_changed, .flush_commit = dpu_kms_flush_commit, .wait_flush = dpu_kms_wait_flush, .complete_commit = dpu_kms_complete_commit, diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 83c4e6aca209..87a91148a731 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -183,10 +183,16 @@ static unsigned get_crtc_mask(struct drm_atomic_state *state) int msm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { + struct msm_drm_private *priv = dev->dev_private; + struct msm_kms *kms = priv->kms; struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_crtc *crtc; - int i; + int i, ret = 0; + /* + * FIXME: stop setting allow_modeset and move this check to the DPU + * driver. + */ for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { if ((old_crtc_state->ctm && !new_crtc_state->ctm) || @@ -196,6 +202,11 @@ int msm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) } } + if (kms && kms->funcs && kms->funcs->check_mode_changed) + ret = kms->funcs->check_mode_changed(kms, state); + if (ret) + return ret; + return drm_atomic_helper_check(dev, state); } diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index 3e28c4e012d2..43b58d052ee6 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -59,6 +59,13 @@ struct msm_kms_funcs { void (*enable_commit)(struct msm_kms *kms); void (*disable_commit)(struct msm_kms *kms); + /** + * @check_mode_changed: + * + * Verify if the commit requires a full modeset on one of CRTCs. + */ + int (*check_mode_changed)(struct msm_kms *kms, struct drm_atomic_state *state); + /** * Prepare for atomic commit. This is called after any previous * (async or otherwise) commit has completed. From 17666e764f389282fe1e35989e210401a0851980 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 14 Feb 2025 16:14:24 -0800 Subject: [PATCH 38/86] drm/msm/dpu: fill CRTC resources in dpu_crtc.c Stop poking into CRTC state from dpu_encoder.c, fill CRTC HW resources from dpu_crtc_assign_resources(). Signed-off-by: Dmitry Baryshkov [quic_abhinavk@quicinc.com: cleaned up formatting] Signed-off-by: Abhinav Kumar Reviewed-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637485/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-1-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 63 +++++++++++++++++++++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 37 ------------ 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 29485e76f531..90fc94d1a2b3 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1230,6 +1230,63 @@ static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state return ret; } +#define MAX_CHANNELS_PER_CRTC 2 + +static int dpu_crtc_assign_resources(struct drm_crtc *crtc, + struct drm_crtc_state *crtc_state) +{ + struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_CRTC]; + struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_CRTC]; + struct dpu_hw_blk *hw_dspp[MAX_CHANNELS_PER_CRTC]; + int i, num_lm, num_ctl, num_dspp; + struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); + struct dpu_global_state *global_state; + struct dpu_crtc_state *cstate; + struct drm_encoder *drm_enc; + + /* + * For now, grab the first encoder in the crtc state as we don't + * support clone mode yet + */ + drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) + break; + + global_state = dpu_kms_get_global_state(crtc_state->state); + if (IS_ERR(global_state)) + return PTR_ERR(global_state); + + if (!crtc_state->enable) + return 0; + + cstate = to_dpu_crtc_state(crtc_state); + + num_ctl = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + drm_enc->base.id, + DPU_HW_BLK_CTL, hw_ctl, + ARRAY_SIZE(hw_ctl)); + num_lm = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + drm_enc->base.id, + DPU_HW_BLK_LM, hw_lm, + ARRAY_SIZE(hw_lm)); + num_dspp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + drm_enc->base.id, + DPU_HW_BLK_DSPP, hw_dspp, + ARRAY_SIZE(hw_dspp)); + + for (i = 0; i < num_lm; i++) { + int ctl_idx = (i < num_ctl) ? i : (num_ctl-1); + + cstate->mixers[i].hw_lm = to_dpu_hw_mixer(hw_lm[i]); + cstate->mixers[i].lm_ctl = to_dpu_hw_ctl(hw_ctl[ctl_idx]); + if (i < num_dspp) + cstate->mixers[i].hw_dspp = to_dpu_hw_dspp(hw_dspp[i]); + } + + cstate->num_mixers = num_lm; + + return 0; +} + static int dpu_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { @@ -1245,6 +1302,12 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc, bool needs_dirtyfb = dpu_crtc_needs_dirtyfb(crtc_state); + if (drm_atomic_crtc_needs_modeset(crtc_state)) { + rc = dpu_crtc_assign_resources(crtc, crtc_state); + if (rc < 0) + return rc; + } + if (dpu_use_virtual_planes && (crtc_state->planes_changed || crtc_state->zpos_changed)) { rc = dpu_crtc_reassign_planes(crtc, crtc_state); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 1fc220a2ce0d..61903f77730d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -726,40 +726,6 @@ static struct msm_display_topology dpu_encoder_get_topology( return topology; } -static void dpu_encoder_assign_crtc_resources(struct dpu_kms *dpu_kms, - struct drm_encoder *drm_enc, - struct dpu_global_state *global_state, - struct drm_crtc_state *crtc_state) -{ - struct dpu_crtc_state *cstate; - struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_ENC]; - struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_ENC]; - struct dpu_hw_blk *hw_dspp[MAX_CHANNELS_PER_ENC]; - int num_lm, num_ctl, num_dspp, i; - - cstate = to_dpu_crtc_state(crtc_state); - - memset(cstate->mixers, 0, sizeof(cstate->mixers)); - - num_ctl = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, DPU_HW_BLK_CTL, hw_ctl, ARRAY_SIZE(hw_ctl)); - num_lm = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm)); - num_dspp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, DPU_HW_BLK_DSPP, hw_dspp, - ARRAY_SIZE(hw_dspp)); - - for (i = 0; i < num_lm; i++) { - int ctl_idx = (i < num_ctl) ? i : (num_ctl-1); - - cstate->mixers[i].hw_lm = to_dpu_hw_mixer(hw_lm[i]); - cstate->mixers[i].lm_ctl = to_dpu_hw_ctl(hw_ctl[ctl_idx]); - cstate->mixers[i].hw_dspp = i < num_dspp ? to_dpu_hw_dspp(hw_dspp[i]) : NULL; - } - - cstate->num_mixers = num_lm; -} - /** * dpu_encoder_virt_check_mode_changed: check if full modeset is required * @drm_enc: Pointer to drm encoder structure @@ -830,9 +796,6 @@ static int dpu_encoder_virt_atomic_check( if (crtc_state->enable) ret = dpu_rm_reserve(&dpu_kms->rm, global_state, drm_enc, crtc_state, &topology); - if (!ret) - dpu_encoder_assign_crtc_resources(dpu_kms, drm_enc, - global_state, crtc_state); } trace_dpu_enc_atomic_check_flags(DRMID(drm_enc), adj_mode->flags); From 1ce69c265a53c61c5c29f97f542ff89af3f3d7e7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 14 Feb 2025 16:14:25 -0800 Subject: [PATCH 39/86] drm/msm/dpu: move resource allocation to CRTC All resource allocation is centered around the LMs. Then other blocks (except DSCs) are allocated basing on the LMs that was selected, and LM powers up the CRTC rather than the encoder. Moreover if at some point the driver supports encoder cloning, allocating resources from the encoder will be incorrect, as all clones will have different encoder IDs, while LMs are to be shared by these encoders. In addition, move mode_changed() to dpu_crtc as encoder no longer has access to topology information Signed-off-by: Dmitry Baryshkov [quic_abhinavk@quicinc.com: Refactored resource allocation for CDM] Signed-off-by: Abhinav Kumar [quic_jesszhan@quicinc.com: Changed to grabbing exising global state] Signed-off-by: Jessica Zhang [DB: rebased on top of msm-next] [DB: fixed resource allcoation to ignore the active_changed flag] Patchwork: https://patchwork.freedesktop.org/patch/637487/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-2-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 82 +++++++++- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h | 2 + drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 167 ++++++-------------- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 11 +- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 17 +- 5 files changed, 145 insertions(+), 134 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 90fc94d1a2b3..2fe7005b4529 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1231,6 +1231,50 @@ static int dpu_crtc_reassign_planes(struct drm_crtc *crtc, struct drm_crtc_state } #define MAX_CHANNELS_PER_CRTC 2 +#define MAX_HDISPLAY_SPLIT 1080 + +static struct msm_display_topology dpu_crtc_get_topology( + struct drm_crtc *crtc, + struct dpu_kms *dpu_kms, + struct drm_crtc_state *crtc_state) +{ + struct drm_display_mode *mode = &crtc_state->adjusted_mode; + struct msm_display_topology topology = {0}; + struct drm_encoder *drm_enc; + + drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) + dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state, + &crtc_state->adjusted_mode); + + /* + * Datapath topology selection + * + * Dual display + * 2 LM, 2 INTF ( Split display using 2 interfaces) + * + * Single display + * 1 LM, 1 INTF + * 2 LM, 1 INTF (stream merge to support high resolution interfaces) + * + * If DSC is enabled, use 2 LMs for 2:2:1 topology + * + * Add dspps to the reservation requirements if ctm is requested + */ + + if (topology.num_intf == 2) + topology.num_lm = 2; + else if (topology.num_dsc == 2) + topology.num_lm = 2; + else if (dpu_kms->catalog->caps->has_3d_merge) + topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1; + else + topology.num_lm = 1; + + if (crtc_state->ctm) + topology.num_dspp = topology.num_lm; + + return topology; +} static int dpu_crtc_assign_resources(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) @@ -1243,6 +1287,8 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc, struct dpu_global_state *global_state; struct dpu_crtc_state *cstate; struct drm_encoder *drm_enc; + struct msm_display_topology topology; + int ret; /* * For now, grab the first encoder in the crtc state as we don't @@ -1251,13 +1297,24 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc, drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) break; + /* + * Release and Allocate resources on every modeset + */ global_state = dpu_kms_get_global_state(crtc_state->state); if (IS_ERR(global_state)) return PTR_ERR(global_state); + dpu_rm_release(global_state, drm_enc); + if (!crtc_state->enable) return 0; + topology = dpu_crtc_get_topology(crtc, dpu_kms, crtc_state); + ret = dpu_rm_reserve(&dpu_kms->rm, global_state, + drm_enc, crtc_state, &topology); + if (ret) + return ret; + cstate = to_dpu_crtc_state(crtc_state); num_ctl = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, @@ -1287,6 +1344,28 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc, return 0; } +/** + * dpu_crtc_check_mode_changed: check if full modeset is required + * @crtc_state: Corresponding CRTC state to be checked + * + * Check if the changes in the object properties demand full mode set. + */ +int dpu_crtc_check_mode_changed(struct drm_crtc_state *crtc_state) +{ + struct drm_encoder *drm_enc; + struct drm_crtc *crtc = crtc_state->crtc; + + DRM_DEBUG_ATOMIC("%d\n", crtc->base.id); + + /* there might be cases where encoder needs a modeset too */ + drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) { + if (dpu_encoder_needs_modeset(drm_enc, crtc_state->state)) + crtc_state->mode_changed = true; + } + + return 0; +} + static int dpu_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { @@ -1302,7 +1381,8 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc, bool needs_dirtyfb = dpu_crtc_needs_dirtyfb(crtc_state); - if (drm_atomic_crtc_needs_modeset(crtc_state)) { + /* don't reallocate resources if only ACTIVE has beeen changed */ + if (crtc_state->mode_changed || crtc_state->connectors_changed) { rc = dpu_crtc_assign_resources(crtc, crtc_state); if (rc < 0) return rc; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h index 0b148f3ce0d7..51a3b5fc879a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h @@ -239,6 +239,8 @@ static inline int dpu_crtc_frame_pending(struct drm_crtc *crtc) return crtc ? atomic_read(&to_dpu_crtc(crtc)->frame_pending) : -EINVAL; } +int dpu_crtc_check_mode_changed(struct drm_crtc_state *crtc_state); + int dpu_crtc_vblank(struct drm_crtc *crtc, bool en); void dpu_crtc_vblank_callback(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 61903f77730d..9cba23dfd86f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -58,8 +58,6 @@ #define IDLE_SHORT_TIMEOUT 1 -#define MAX_HDISPLAY_SPLIT 1080 - /* timeout in frames waiting for frame done */ #define DPU_ENCODER_FRAME_DONE_TIMEOUT_FRAMES 5 @@ -647,46 +645,29 @@ struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc) return NULL; } -static struct msm_display_topology dpu_encoder_get_topology( - struct dpu_encoder_virt *dpu_enc, - struct drm_display_mode *mode, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +void dpu_encoder_update_topology(struct drm_encoder *drm_enc, + struct msm_display_topology *topology, + struct drm_atomic_state *state, + const struct drm_display_mode *adj_mode) { + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); struct msm_drm_private *priv = dpu_enc->base.dev->dev_private; struct msm_display_info *disp_info = &dpu_enc->disp_info; struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms); - struct drm_dsc_config *dsc = dpu_encoder_get_dsc_config(&dpu_enc->base); - struct msm_display_topology topology = {0}; - int i, intf_count = 0; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_framebuffer *fb; + struct drm_dsc_config *dsc; + + int i; for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++) if (dpu_enc->phys_encs[i]) - intf_count++; + topology->num_intf++; - /* Datapath topology selection - * - * Dual display - * 2 LM, 2 INTF ( Split display using 2 interfaces) - * - * Single display - * 1 LM, 1 INTF - * 2 LM, 1 INTF (stream merge to support high resolution interfaces) - * - * Add dspps to the reservation requirements if ctm is requested - */ - if (intf_count == 2) - topology.num_lm = 2; - else if (!dpu_kms->catalog->caps->has_3d_merge) - topology.num_lm = 1; - else - topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1; - - if (crtc_state->ctm) - topology.num_dspp = topology.num_lm; - - topology.num_intf = intf_count; + dsc = dpu_encoder_get_dsc_config(drm_enc); + /* We only support 2 DSC mode (with 2 LM and 1 INTF) */ if (dsc) { /* * Use 2 DSC encoders, 2 layer mixers and 1 or 2 interfaces @@ -695,112 +676,69 @@ static struct msm_display_topology dpu_encoder_get_topology( * This is power-optimal and can drive up to (including) 4k * screens. */ - WARN(topology.num_intf > 2, + WARN(topology->num_intf > 2, "DSC topology cannot support more than 2 interfaces\n"); - if (intf_count >= 2 || dpu_kms->catalog->dsc_count >= 2) { - topology.num_dsc = 2; - topology.num_lm = 2; - } else { - topology.num_dsc = 1; - topology.num_lm = 1; - } + if (topology->num_intf >= 2 || dpu_kms->catalog->dsc_count >= 2) + topology->num_dsc = 2; + else + topology->num_dsc = 1; } + connector = drm_atomic_get_new_connector_for_encoder(state, drm_enc); + if (!connector) + return; + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (!conn_state) + return; + /* * Use CDM only for writeback or DP at the moment as other interfaces cannot handle it. * If writeback itself cannot handle cdm for some reason it will fail in its atomic_check() * earlier. */ if (disp_info->intf_type == INTF_WB && conn_state->writeback_job) { - struct drm_framebuffer *fb; - fb = conn_state->writeback_job->fb; if (fb && MSM_FORMAT_IS_YUV(msm_framebuffer_format(fb))) - topology.needs_cdm = true; + topology->needs_cdm = true; } else if (disp_info->intf_type == INTF_DP) { - if (msm_dp_is_yuv_420_enabled(priv->dp[disp_info->h_tile_instance[0]], mode)) - topology.needs_cdm = true; + if (msm_dp_is_yuv_420_enabled(priv->dp[disp_info->h_tile_instance[0]], adj_mode)) + topology->needs_cdm = true; } - - return topology; } -/** - * dpu_encoder_virt_check_mode_changed: check if full modeset is required - * @drm_enc: Pointer to drm encoder structure - * @crtc_state: Corresponding CRTC state to be checked - * @conn_state: Corresponding Connector's state to be checked - * - * Check if the changes in the object properties demand full mode set. - */ -int dpu_encoder_virt_check_mode_changed(struct drm_encoder *drm_enc, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) +bool dpu_encoder_needs_modeset(struct drm_encoder *drm_enc, struct drm_atomic_state *state) { + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_framebuffer *fb; struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); - struct msm_display_topology topology; - - DPU_DEBUG_ENC(dpu_enc, "\n"); - - /* Using mode instead of adjusted_mode as it wasn't computed yet */ - topology = dpu_encoder_get_topology(dpu_enc, &crtc_state->mode, crtc_state, conn_state); - - if (topology.needs_cdm && !dpu_enc->cur_master->hw_cdm) - crtc_state->mode_changed = true; - else if (!topology.needs_cdm && dpu_enc->cur_master->hw_cdm) - crtc_state->mode_changed = true; - return 0; -} + if (!drm_enc || !state) + return false; -static int dpu_encoder_virt_atomic_check( - struct drm_encoder *drm_enc, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - struct dpu_encoder_virt *dpu_enc; - struct msm_drm_private *priv; - struct dpu_kms *dpu_kms; - struct drm_display_mode *adj_mode; - struct msm_display_topology topology; - struct dpu_global_state *global_state; - int ret = 0; + connector = drm_atomic_get_new_connector_for_encoder(state, drm_enc); + if (!connector) + return false; - if (!drm_enc || !crtc_state || !conn_state) { - DPU_ERROR("invalid arg(s), drm_enc %d, crtc/conn state %d/%d\n", - drm_enc != NULL, crtc_state != NULL, conn_state != NULL); - return -EINVAL; - } - - dpu_enc = to_dpu_encoder_virt(drm_enc); - DPU_DEBUG_ENC(dpu_enc, "\n"); + conn_state = drm_atomic_get_new_connector_state(state, connector); - priv = drm_enc->dev->dev_private; - dpu_kms = to_dpu_kms(priv->kms); - adj_mode = &crtc_state->adjusted_mode; - global_state = dpu_kms_get_global_state(crtc_state->state); - if (IS_ERR(global_state)) - return PTR_ERR(global_state); - - trace_dpu_enc_atomic_check(DRMID(drm_enc)); - - topology = dpu_encoder_get_topology(dpu_enc, adj_mode, crtc_state, conn_state); - - /* - * Release and Allocate resources on every modeset + /** + * These checks are duplicated from dpu_encoder_update_topology() since + * CRTC and encoder don't hold topology information */ - if (drm_atomic_crtc_needs_modeset(crtc_state)) { - dpu_rm_release(global_state, drm_enc); - - if (crtc_state->enable) - ret = dpu_rm_reserve(&dpu_kms->rm, global_state, - drm_enc, crtc_state, &topology); + if (dpu_enc->disp_info.intf_type == INTF_WB && conn_state->writeback_job) { + fb = conn_state->writeback_job->fb; + if (fb && MSM_FORMAT_IS_YUV(msm_framebuffer_format(fb))) { + if (!dpu_enc->cur_master->hw_cdm) + return true; + } else { + if (dpu_enc->cur_master->hw_cdm) + return true; + } } - trace_dpu_enc_atomic_check_flags(DRMID(drm_enc), adj_mode->flags); - - return ret; + return false; } static void _dpu_encoder_update_vsync_source(struct dpu_encoder_virt *dpu_enc, @@ -2619,7 +2557,6 @@ static const struct drm_encoder_helper_funcs dpu_encoder_helper_funcs = { .atomic_mode_set = dpu_encoder_virt_atomic_mode_set, .atomic_disable = dpu_encoder_virt_atomic_disable, .atomic_enable = dpu_encoder_virt_atomic_enable, - .atomic_check = dpu_encoder_virt_atomic_check, }; static const struct drm_encoder_funcs dpu_encoder_funcs = { diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index da133ee4701a..b0ac10ebd02c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -80,6 +80,13 @@ int dpu_encoder_get_crc(const struct drm_encoder *drm_enc, u32 *crcs, int pos); bool dpu_encoder_use_dsc_merge(struct drm_encoder *drm_enc); +void dpu_encoder_update_topology(struct drm_encoder *drm_enc, + struct msm_display_topology *topology, + struct drm_atomic_state *state, + const struct drm_display_mode *adj_mode); + +bool dpu_encoder_needs_modeset(struct drm_encoder *drm_enc, struct drm_atomic_state *state); + void dpu_encoder_prepare_wb_job(struct drm_encoder *drm_enc, struct drm_writeback_job *job); @@ -88,8 +95,4 @@ void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc); -int dpu_encoder_virt_check_mode_changed(struct drm_encoder *drm_enc, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state); - #endif /* __DPU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 8741dc6fc8dd..24c457b017e1 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -449,22 +449,11 @@ static void dpu_kms_disable_commit(struct msm_kms *kms) static int dpu_kms_check_mode_changed(struct msm_kms *kms, struct drm_atomic_state *state) { struct drm_crtc_state *new_crtc_state; - struct drm_connector *connector; - struct drm_connector_state *new_conn_state; + struct drm_crtc *crtc; int i; - for_each_new_connector_in_state(state, connector, new_conn_state, i) { - struct drm_encoder *encoder; - - if (!new_conn_state->crtc || !new_conn_state->best_encoder) - continue; - - new_crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc); - - encoder = new_conn_state->best_encoder; - - dpu_encoder_virt_check_mode_changed(encoder, new_crtc_state, new_conn_state); - } + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) + dpu_crtc_check_mode_changed(new_crtc_state); return 0; } From cae6a13a71f7edb078dc9ba71047dfd2a6422c31 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 14 Feb 2025 16:14:26 -0800 Subject: [PATCH 40/86] drm/msm/dpu: switch RM to use crtc_id rather than enc_id for allocation Up to now the driver has been using encoder to allocate hardware resources. Switch it to use CRTC id in preparation for the next step. Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Reviewed-by: Dmitry Baryshkov Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637503/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-3-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 18 +- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 10 +- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h | 12 +- drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 189 ++++++++++---------- drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h | 7 +- 5 files changed, 110 insertions(+), 126 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 2fe7005b4529..10653bd52885 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1286,17 +1286,9 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc, struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); struct dpu_global_state *global_state; struct dpu_crtc_state *cstate; - struct drm_encoder *drm_enc; struct msm_display_topology topology; int ret; - /* - * For now, grab the first encoder in the crtc state as we don't - * support clone mode yet - */ - drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) - break; - /* * Release and Allocate resources on every modeset */ @@ -1304,29 +1296,29 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc, if (IS_ERR(global_state)) return PTR_ERR(global_state); - dpu_rm_release(global_state, drm_enc); + dpu_rm_release(global_state, crtc); if (!crtc_state->enable) return 0; topology = dpu_crtc_get_topology(crtc, dpu_kms, crtc_state); ret = dpu_rm_reserve(&dpu_kms->rm, global_state, - drm_enc, crtc_state, &topology); + crtc_state->crtc, &topology); if (ret) return ret; cstate = to_dpu_crtc_state(crtc_state); num_ctl = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, + crtc_state->crtc, DPU_HW_BLK_CTL, hw_ctl, ARRAY_SIZE(hw_ctl)); num_lm = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, + crtc_state->crtc, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm)); num_dspp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, + crtc_state->crtc, DPU_HW_BLK_DSPP, hw_dspp, ARRAY_SIZE(hw_dspp)); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 9cba23dfd86f..a4091e861e31 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -1174,17 +1174,17 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, /* Query resource that have been reserved in atomic check step. */ num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, DPU_HW_BLK_PINGPONG, hw_pp, + drm_enc->crtc, DPU_HW_BLK_PINGPONG, hw_pp, ARRAY_SIZE(hw_pp)); num_ctl = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, DPU_HW_BLK_CTL, hw_ctl, ARRAY_SIZE(hw_ctl)); + drm_enc->crtc, DPU_HW_BLK_CTL, hw_ctl, ARRAY_SIZE(hw_ctl)); for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) dpu_enc->hw_pp[i] = i < num_pp ? to_dpu_hw_pingpong(hw_pp[i]) : NULL; num_dsc = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, DPU_HW_BLK_DSC, + drm_enc->crtc, DPU_HW_BLK_DSC, hw_dsc, ARRAY_SIZE(hw_dsc)); for (i = 0; i < num_dsc; i++) { dpu_enc->hw_dsc[i] = to_dpu_hw_dsc(hw_dsc[i]); @@ -1198,7 +1198,7 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, struct dpu_hw_blk *hw_cdm = NULL; dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->base.id, DPU_HW_BLK_CDM, + drm_enc->crtc, DPU_HW_BLK_CDM, &hw_cdm, 1); dpu_enc->cur_master->hw_cdm = hw_cdm ? to_dpu_hw_cdm(hw_cdm) : NULL; } @@ -2116,7 +2116,7 @@ static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc) global_state = dpu_kms_get_existing_global_state(phys_enc->dpu_kms); num_lm = dpu_rm_get_assigned_resources(&phys_enc->dpu_kms->rm, global_state, - phys_enc->parent->base.id, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm)); + phys_enc->parent->crtc, DPU_HW_BLK_LM, hw_lm, ARRAY_SIZE(hw_lm)); for (i = 0; i < num_lm; i++) { hw_mixer[i] = to_dpu_hw_mixer(hw_lm[i]); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h index 547cdb2c0c78..54ef6cfa2485 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h @@ -124,12 +124,12 @@ struct dpu_global_state { struct dpu_rm *rm; - uint32_t pingpong_to_enc_id[PINGPONG_MAX - PINGPONG_0]; - uint32_t mixer_to_enc_id[LM_MAX - LM_0]; - uint32_t ctl_to_enc_id[CTL_MAX - CTL_0]; - uint32_t dspp_to_enc_id[DSPP_MAX - DSPP_0]; - uint32_t dsc_to_enc_id[DSC_MAX - DSC_0]; - uint32_t cdm_to_enc_id; + uint32_t pingpong_to_crtc_id[PINGPONG_MAX - PINGPONG_0]; + uint32_t mixer_to_crtc_id[LM_MAX - LM_0]; + uint32_t ctl_to_crtc_id[CTL_MAX - CTL_0]; + uint32_t dspp_to_crtc_id[DSPP_MAX - DSPP_0]; + uint32_t dsc_to_crtc_id[DSC_MAX - DSC_0]; + uint32_t cdm_to_crtc_id; uint32_t sspp_to_crtc_id[SSPP_MAX - SSPP_NONE]; }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c index 5baf9df702b8..a7b4086ae990 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c @@ -22,9 +22,9 @@ static inline bool reserved_by_other(uint32_t *res_map, int idx, - uint32_t enc_id) + uint32_t crtc_id) { - return res_map[idx] && res_map[idx] != enc_id; + return res_map[idx] && res_map[idx] != crtc_id; } /** @@ -239,7 +239,7 @@ static int _dpu_rm_get_lm_peer(struct dpu_rm *rm, int primary_idx) * pingpong * @rm: dpu resource manager handle * @global_state: resources shared across multiple kms objects - * @enc_id: encoder id requesting for allocation + * @crtc_id: crtc id requesting for allocation * @lm_idx: index of proposed layer mixer in rm->mixer_blks[], function checks * if lm, and all other hardwired blocks connected to the lm (pp) is * available and appropriate @@ -252,14 +252,14 @@ static int _dpu_rm_get_lm_peer(struct dpu_rm *rm, int primary_idx) */ static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm, struct dpu_global_state *global_state, - uint32_t enc_id, int lm_idx, int *pp_idx, int *dspp_idx, + uint32_t crtc_id, int lm_idx, int *pp_idx, int *dspp_idx, struct msm_display_topology *topology) { const struct dpu_lm_cfg *lm_cfg; int idx; /* Already reserved? */ - if (reserved_by_other(global_state->mixer_to_enc_id, lm_idx, enc_id)) { + if (reserved_by_other(global_state->mixer_to_crtc_id, lm_idx, crtc_id)) { DPU_DEBUG("lm %d already reserved\n", lm_idx + LM_0); return false; } @@ -271,7 +271,7 @@ static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm, return false; } - if (reserved_by_other(global_state->pingpong_to_enc_id, idx, enc_id)) { + if (reserved_by_other(global_state->pingpong_to_crtc_id, idx, crtc_id)) { DPU_DEBUG("lm %d pp %d already reserved\n", lm_cfg->id, lm_cfg->pingpong); return false; @@ -287,7 +287,7 @@ static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm, return false; } - if (reserved_by_other(global_state->dspp_to_enc_id, idx, enc_id)) { + if (reserved_by_other(global_state->dspp_to_crtc_id, idx, crtc_id)) { DPU_DEBUG("lm %d dspp %d already reserved\n", lm_cfg->id, lm_cfg->dspp); return false; @@ -299,7 +299,7 @@ static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm, static int _dpu_rm_reserve_lms(struct dpu_rm *rm, struct dpu_global_state *global_state, - uint32_t enc_id, + uint32_t crtc_id, struct msm_display_topology *topology) { @@ -323,7 +323,7 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm, lm_idx[lm_count] = i; if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state, - enc_id, i, &pp_idx[lm_count], + crtc_id, i, &pp_idx[lm_count], &dspp_idx[lm_count], topology)) { continue; } @@ -342,7 +342,7 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm, continue; if (!_dpu_rm_check_lm_and_get_connected_blks(rm, - global_state, enc_id, j, + global_state, crtc_id, j, &pp_idx[lm_count], &dspp_idx[lm_count], topology)) { continue; @@ -359,12 +359,12 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm, } for (i = 0; i < lm_count; i++) { - global_state->mixer_to_enc_id[lm_idx[i]] = enc_id; - global_state->pingpong_to_enc_id[pp_idx[i]] = enc_id; - global_state->dspp_to_enc_id[dspp_idx[i]] = - topology->num_dspp ? enc_id : 0; + global_state->mixer_to_crtc_id[lm_idx[i]] = crtc_id; + global_state->pingpong_to_crtc_id[pp_idx[i]] = crtc_id; + global_state->dspp_to_crtc_id[dspp_idx[i]] = + topology->num_dspp ? crtc_id : 0; - trace_dpu_rm_reserve_lms(lm_idx[i] + LM_0, enc_id, + trace_dpu_rm_reserve_lms(lm_idx[i] + LM_0, crtc_id, pp_idx[i] + PINGPONG_0); } @@ -374,7 +374,7 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm, static int _dpu_rm_reserve_ctls( struct dpu_rm *rm, struct dpu_global_state *global_state, - uint32_t enc_id, + uint32_t crtc_id, const struct msm_display_topology *top) { int ctl_idx[MAX_BLOCKS]; @@ -393,7 +393,7 @@ static int _dpu_rm_reserve_ctls( if (!rm->ctl_blks[j]) continue; - if (reserved_by_other(global_state->ctl_to_enc_id, j, enc_id)) + if (reserved_by_other(global_state->ctl_to_crtc_id, j, crtc_id)) continue; ctl = to_dpu_hw_ctl(rm->ctl_blks[j]); @@ -417,8 +417,8 @@ static int _dpu_rm_reserve_ctls( return -ENAVAIL; for (i = 0; i < ARRAY_SIZE(ctl_idx) && i < num_ctls; i++) { - global_state->ctl_to_enc_id[ctl_idx[i]] = enc_id; - trace_dpu_rm_reserve_ctls(i + CTL_0, enc_id); + global_state->ctl_to_crtc_id[ctl_idx[i]] = crtc_id; + trace_dpu_rm_reserve_ctls(i + CTL_0, crtc_id); } return 0; @@ -426,12 +426,12 @@ static int _dpu_rm_reserve_ctls( static int _dpu_rm_pingpong_next_index(struct dpu_global_state *global_state, int start, - uint32_t enc_id) + uint32_t crtc_id) { int i; for (i = start; i < (PINGPONG_MAX - PINGPONG_0); i++) { - if (global_state->pingpong_to_enc_id[i] == enc_id) + if (global_state->pingpong_to_crtc_id[i] == crtc_id) return i; } @@ -452,7 +452,7 @@ static int _dpu_rm_pingpong_dsc_check(int dsc_idx, int pp_idx) static int _dpu_rm_dsc_alloc(struct dpu_rm *rm, struct dpu_global_state *global_state, - uint32_t enc_id, + uint32_t crtc_id, const struct msm_display_topology *top) { int num_dsc = 0; @@ -465,10 +465,10 @@ static int _dpu_rm_dsc_alloc(struct dpu_rm *rm, if (!rm->dsc_blks[dsc_idx]) continue; - if (reserved_by_other(global_state->dsc_to_enc_id, dsc_idx, enc_id)) + if (reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx, crtc_id)) continue; - pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, enc_id); + pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, crtc_id); if (pp_idx < 0) return -ENAVAIL; @@ -476,7 +476,7 @@ static int _dpu_rm_dsc_alloc(struct dpu_rm *rm, if (ret) return -ENAVAIL; - global_state->dsc_to_enc_id[dsc_idx] = enc_id; + global_state->dsc_to_crtc_id[dsc_idx] = crtc_id; num_dsc++; pp_idx++; } @@ -492,7 +492,7 @@ static int _dpu_rm_dsc_alloc(struct dpu_rm *rm, static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm, struct dpu_global_state *global_state, - uint32_t enc_id, + uint32_t crtc_id, const struct msm_display_topology *top) { int num_dsc = 0; @@ -507,11 +507,11 @@ static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm, continue; /* consective dsc index to be paired */ - if (reserved_by_other(global_state->dsc_to_enc_id, dsc_idx, enc_id) || - reserved_by_other(global_state->dsc_to_enc_id, dsc_idx + 1, enc_id)) + if (reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx, crtc_id) || + reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx + 1, crtc_id)) continue; - pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, enc_id); + pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, crtc_id); if (pp_idx < 0) return -ENAVAIL; @@ -521,7 +521,7 @@ static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm, continue; } - pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx + 1, enc_id); + pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx + 1, crtc_id); if (pp_idx < 0) return -ENAVAIL; @@ -531,8 +531,8 @@ static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm, continue; } - global_state->dsc_to_enc_id[dsc_idx] = enc_id; - global_state->dsc_to_enc_id[dsc_idx + 1] = enc_id; + global_state->dsc_to_crtc_id[dsc_idx] = crtc_id; + global_state->dsc_to_crtc_id[dsc_idx + 1] = crtc_id; num_dsc += 2; pp_idx++; /* start for next pair */ } @@ -548,11 +548,9 @@ static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm, static int _dpu_rm_reserve_dsc(struct dpu_rm *rm, struct dpu_global_state *global_state, - struct drm_encoder *enc, + uint32_t crtc_id, const struct msm_display_topology *top) { - uint32_t enc_id = enc->base.id; - if (!top->num_dsc || !top->num_intf) return 0; @@ -568,16 +566,16 @@ static int _dpu_rm_reserve_dsc(struct dpu_rm *rm, /* num_dsc should be either 1, 2 or 4 */ if (top->num_dsc > top->num_intf) /* merge mode */ - return _dpu_rm_dsc_alloc_pair(rm, global_state, enc_id, top); + return _dpu_rm_dsc_alloc_pair(rm, global_state, crtc_id, top); else - return _dpu_rm_dsc_alloc(rm, global_state, enc_id, top); + return _dpu_rm_dsc_alloc(rm, global_state, crtc_id, top); return 0; } static int _dpu_rm_reserve_cdm(struct dpu_rm *rm, struct dpu_global_state *global_state, - struct drm_encoder *enc) + uint32_t crtc_id) { /* try allocating only one CDM block */ if (!rm->cdm_blk) { @@ -585,12 +583,12 @@ static int _dpu_rm_reserve_cdm(struct dpu_rm *rm, return -EIO; } - if (global_state->cdm_to_enc_id) { + if (global_state->cdm_to_crtc_id) { DPU_ERROR("CDM_0 is already allocated\n"); return -EIO; } - global_state->cdm_to_enc_id = enc->base.id; + global_state->cdm_to_crtc_id = crtc_id; return 0; } @@ -598,30 +596,31 @@ static int _dpu_rm_reserve_cdm(struct dpu_rm *rm, static int _dpu_rm_make_reservation( struct dpu_rm *rm, struct dpu_global_state *global_state, - struct drm_encoder *enc, + uint32_t crtc_id, struct msm_display_topology *topology) { int ret; - ret = _dpu_rm_reserve_lms(rm, global_state, enc->base.id, topology); + ret = _dpu_rm_reserve_lms(rm, global_state, crtc_id, topology); if (ret) { DPU_ERROR("unable to find appropriate mixers\n"); return ret; } - ret = _dpu_rm_reserve_ctls(rm, global_state, enc->base.id, + + ret = _dpu_rm_reserve_ctls(rm, global_state, crtc_id, topology); if (ret) { DPU_ERROR("unable to find appropriate CTL\n"); return ret; } - ret = _dpu_rm_reserve_dsc(rm, global_state, enc, topology); + ret = _dpu_rm_reserve_dsc(rm, global_state, crtc_id, topology); if (ret) return ret; if (topology->needs_cdm) { - ret = _dpu_rm_reserve_cdm(rm, global_state, enc); + ret = _dpu_rm_reserve_cdm(rm, global_state, crtc_id); if (ret) { DPU_ERROR("unable to find CDM blk\n"); return ret; @@ -632,12 +631,12 @@ static int _dpu_rm_make_reservation( } static void _dpu_rm_clear_mapping(uint32_t *res_mapping, int cnt, - uint32_t enc_id) + uint32_t crtc_id) { int i; for (i = 0; i < cnt; i++) { - if (res_mapping[i] == enc_id) + if (res_mapping[i] == crtc_id) res_mapping[i] = 0; } } @@ -646,23 +645,25 @@ static void _dpu_rm_clear_mapping(uint32_t *res_mapping, int cnt, * dpu_rm_release - Given the encoder for the display chain, release any * HW blocks previously reserved for that use case. * @global_state: resources shared across multiple kms objects - * @enc: DRM Encoder handle + * @crtc: DRM CRTC handle * @return: 0 on Success otherwise -ERROR */ void dpu_rm_release(struct dpu_global_state *global_state, - struct drm_encoder *enc) + struct drm_crtc *crtc) { - _dpu_rm_clear_mapping(global_state->pingpong_to_enc_id, - ARRAY_SIZE(global_state->pingpong_to_enc_id), enc->base.id); - _dpu_rm_clear_mapping(global_state->mixer_to_enc_id, - ARRAY_SIZE(global_state->mixer_to_enc_id), enc->base.id); - _dpu_rm_clear_mapping(global_state->ctl_to_enc_id, - ARRAY_SIZE(global_state->ctl_to_enc_id), enc->base.id); - _dpu_rm_clear_mapping(global_state->dsc_to_enc_id, - ARRAY_SIZE(global_state->dsc_to_enc_id), enc->base.id); - _dpu_rm_clear_mapping(global_state->dspp_to_enc_id, - ARRAY_SIZE(global_state->dspp_to_enc_id), enc->base.id); - _dpu_rm_clear_mapping(&global_state->cdm_to_enc_id, 1, enc->base.id); + uint32_t crtc_id = crtc->base.id; + + _dpu_rm_clear_mapping(global_state->pingpong_to_crtc_id, + ARRAY_SIZE(global_state->pingpong_to_crtc_id), crtc_id); + _dpu_rm_clear_mapping(global_state->mixer_to_crtc_id, + ARRAY_SIZE(global_state->mixer_to_crtc_id), crtc_id); + _dpu_rm_clear_mapping(global_state->ctl_to_crtc_id, + ARRAY_SIZE(global_state->ctl_to_crtc_id), crtc_id); + _dpu_rm_clear_mapping(global_state->dsc_to_crtc_id, + ARRAY_SIZE(global_state->dsc_to_crtc_id), crtc_id); + _dpu_rm_clear_mapping(global_state->dspp_to_crtc_id, + ARRAY_SIZE(global_state->dspp_to_crtc_id), crtc_id); + _dpu_rm_clear_mapping(&global_state->cdm_to_crtc_id, 1, crtc_id); } /** @@ -674,42 +675,33 @@ void dpu_rm_release(struct dpu_global_state *global_state, * HW Reservations should be released via dpu_rm_release_hw. * @rm: DPU Resource Manager handle * @global_state: resources shared across multiple kms objects - * @enc: DRM Encoder handle - * @crtc_state: Proposed Atomic DRM CRTC State handle + * @crtc: DRM CRTC handle * @topology: Pointer to topology info for the display * @return: 0 on Success otherwise -ERROR */ int dpu_rm_reserve( struct dpu_rm *rm, struct dpu_global_state *global_state, - struct drm_encoder *enc, - struct drm_crtc_state *crtc_state, + struct drm_crtc *crtc, struct msm_display_topology *topology) { int ret; - /* Check if this is just a page-flip */ - if (!drm_atomic_crtc_needs_modeset(crtc_state)) - return 0; - if (IS_ERR(global_state)) { DPU_ERROR("failed to global state\n"); return PTR_ERR(global_state); } - DRM_DEBUG_KMS("reserving hw for enc %d crtc %d\n", - enc->base.id, crtc_state->crtc->base.id); + DRM_DEBUG_KMS("reserving hw for crtc %d\n", crtc->base.id); DRM_DEBUG_KMS("num_lm: %d num_dsc: %d num_intf: %d\n", topology->num_lm, topology->num_dsc, topology->num_intf); - ret = _dpu_rm_make_reservation(rm, global_state, enc, topology); + ret = _dpu_rm_make_reservation(rm, global_state, crtc->base.id, topology); if (ret) DPU_ERROR("failed to reserve hw resources: %d\n", ret); - - return ret; } @@ -800,48 +792,49 @@ void dpu_rm_release_all_sspp(struct dpu_global_state *global_state, * assigned to this encoder * @rm: DPU Resource Manager handle * @global_state: resources shared across multiple kms objects - * @enc_id: encoder id requesting for allocation + * @crtc: DRM CRTC handle * @type: resource type to return data for * @blks: pointer to the array to be filled by HW resources * @blks_size: size of the @blks array */ int dpu_rm_get_assigned_resources(struct dpu_rm *rm, - struct dpu_global_state *global_state, uint32_t enc_id, + struct dpu_global_state *global_state, struct drm_crtc *crtc, enum dpu_hw_blk_type type, struct dpu_hw_blk **blks, int blks_size) { + uint32_t crtc_id = crtc->base.id; struct dpu_hw_blk **hw_blks; - uint32_t *hw_to_enc_id; + uint32_t *hw_to_crtc_id; int i, num_blks, max_blks; switch (type) { case DPU_HW_BLK_PINGPONG: hw_blks = rm->pingpong_blks; - hw_to_enc_id = global_state->pingpong_to_enc_id; + hw_to_crtc_id = global_state->pingpong_to_crtc_id; max_blks = ARRAY_SIZE(rm->pingpong_blks); break; case DPU_HW_BLK_LM: hw_blks = rm->mixer_blks; - hw_to_enc_id = global_state->mixer_to_enc_id; + hw_to_crtc_id = global_state->mixer_to_crtc_id; max_blks = ARRAY_SIZE(rm->mixer_blks); break; case DPU_HW_BLK_CTL: hw_blks = rm->ctl_blks; - hw_to_enc_id = global_state->ctl_to_enc_id; + hw_to_crtc_id = global_state->ctl_to_crtc_id; max_blks = ARRAY_SIZE(rm->ctl_blks); break; case DPU_HW_BLK_DSPP: hw_blks = rm->dspp_blks; - hw_to_enc_id = global_state->dspp_to_enc_id; + hw_to_crtc_id = global_state->dspp_to_crtc_id; max_blks = ARRAY_SIZE(rm->dspp_blks); break; case DPU_HW_BLK_DSC: hw_blks = rm->dsc_blks; - hw_to_enc_id = global_state->dsc_to_enc_id; + hw_to_crtc_id = global_state->dsc_to_crtc_id; max_blks = ARRAY_SIZE(rm->dsc_blks); break; case DPU_HW_BLK_CDM: hw_blks = &rm->cdm_blk; - hw_to_enc_id = &global_state->cdm_to_enc_id; + hw_to_crtc_id = &global_state->cdm_to_crtc_id; max_blks = 1; break; default: @@ -851,17 +844,17 @@ int dpu_rm_get_assigned_resources(struct dpu_rm *rm, num_blks = 0; for (i = 0; i < max_blks; i++) { - if (hw_to_enc_id[i] != enc_id) + if (hw_to_crtc_id[i] != crtc_id) continue; if (num_blks == blks_size) { - DPU_ERROR("More than %d resources assigned to enc %d\n", - blks_size, enc_id); + DPU_ERROR("More than %d resources assigned to crtc %d\n", + blks_size, crtc_id); break; } if (!hw_blks[i]) { - DPU_ERROR("Allocated resource %d unavailable to assign to enc %d\n", - type, enc_id); + DPU_ERROR("Allocated resource %d unavailable to assign to crtc %d\n", + type, crtc_id); break; } blks[num_blks++] = hw_blks[i]; @@ -896,38 +889,38 @@ void dpu_rm_print_state(struct drm_printer *p, drm_puts(p, "resource mapping:\n"); drm_puts(p, "\tpingpong="); - for (i = 0; i < ARRAY_SIZE(global_state->pingpong_to_enc_id); i++) + for (i = 0; i < ARRAY_SIZE(global_state->pingpong_to_crtc_id); i++) dpu_rm_print_state_helper(p, rm->pingpong_blks[i], - global_state->pingpong_to_enc_id[i]); + global_state->pingpong_to_crtc_id[i]); drm_puts(p, "\n"); drm_puts(p, "\tmixer="); - for (i = 0; i < ARRAY_SIZE(global_state->mixer_to_enc_id); i++) + for (i = 0; i < ARRAY_SIZE(global_state->mixer_to_crtc_id); i++) dpu_rm_print_state_helper(p, rm->mixer_blks[i], - global_state->mixer_to_enc_id[i]); + global_state->mixer_to_crtc_id[i]); drm_puts(p, "\n"); drm_puts(p, "\tctl="); - for (i = 0; i < ARRAY_SIZE(global_state->ctl_to_enc_id); i++) + for (i = 0; i < ARRAY_SIZE(global_state->ctl_to_crtc_id); i++) dpu_rm_print_state_helper(p, rm->ctl_blks[i], - global_state->ctl_to_enc_id[i]); + global_state->ctl_to_crtc_id[i]); drm_puts(p, "\n"); drm_puts(p, "\tdspp="); - for (i = 0; i < ARRAY_SIZE(global_state->dspp_to_enc_id); i++) + for (i = 0; i < ARRAY_SIZE(global_state->dspp_to_crtc_id); i++) dpu_rm_print_state_helper(p, rm->dspp_blks[i], - global_state->dspp_to_enc_id[i]); + global_state->dspp_to_crtc_id[i]); drm_puts(p, "\n"); drm_puts(p, "\tdsc="); - for (i = 0; i < ARRAY_SIZE(global_state->dsc_to_enc_id); i++) + for (i = 0; i < ARRAY_SIZE(global_state->dsc_to_crtc_id); i++) dpu_rm_print_state_helper(p, rm->dsc_blks[i], - global_state->dsc_to_enc_id[i]); + global_state->dsc_to_crtc_id[i]); drm_puts(p, "\n"); drm_puts(p, "\tcdm="); dpu_rm_print_state_helper(p, rm->cdm_blk, - global_state->cdm_to_enc_id); + global_state->cdm_to_crtc_id); drm_puts(p, "\n"); drm_puts(p, "\tsspp="); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h index 99bd594ee0d1..463c532cdfdf 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h @@ -69,12 +69,11 @@ int dpu_rm_init(struct drm_device *dev, int dpu_rm_reserve(struct dpu_rm *rm, struct dpu_global_state *global_state, - struct drm_encoder *drm_enc, - struct drm_crtc_state *crtc_state, + struct drm_crtc *crtc, struct msm_display_topology *topology); void dpu_rm_release(struct dpu_global_state *global_state, - struct drm_encoder *enc); + struct drm_crtc *crtc); struct dpu_hw_sspp *dpu_rm_reserve_sspp(struct dpu_rm *rm, struct dpu_global_state *global_state, @@ -85,7 +84,7 @@ void dpu_rm_release_all_sspp(struct dpu_global_state *global_state, struct drm_crtc *crtc); int dpu_rm_get_assigned_resources(struct dpu_rm *rm, - struct dpu_global_state *global_state, uint32_t enc_id, + struct dpu_global_state *global_state, struct drm_crtc *crtc, enum dpu_hw_blk_type type, struct dpu_hw_blk **blks, int blks_size); void dpu_rm_print_state(struct drm_printer *p, From 2ea34682263b90566130d70a20bc742ed8de2e3f Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:27 -0800 Subject: [PATCH 41/86] drm/msm/dpu: Add CWB to msm_display_topology Currently, the topology is calculated based on the assumption that the user cannot request real-time and writeback simultaneously. For example, the number of LMs and CTLs are currently based off the number of phys encoders under the assumption there will be at least 1 LM/CTL per phys encoder. This will not hold true for concurrent writeback as both phys encoders (1 real-time and 1 writeback) must be driven by 1 LM/CTL when concurrent writeback is enabled. To account for this, add a cwb_enabled flag and only adjust the number of CTL/LMs needed by a given topology based on the number of phys encoders only if CWB is not enabled. Reviewed-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637486/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-4-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 11 ++++++++++- drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 14 ++++++++++++-- drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h | 2 ++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 10653bd52885..27078d3f90b4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1246,6 +1246,8 @@ static struct msm_display_topology dpu_crtc_get_topology( dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state, &crtc_state->adjusted_mode); + topology.cwb_enabled = drm_crtc_in_clone_mode(crtc_state); + /* * Datapath topology selection * @@ -1259,9 +1261,16 @@ static struct msm_display_topology dpu_crtc_get_topology( * If DSC is enabled, use 2 LMs for 2:2:1 topology * * Add dspps to the reservation requirements if ctm is requested + * + * Only hardcode num_lm to 2 for cases where num_intf == 2 and CWB is not + * enabled. This is because in cases where CWB is enabled, num_intf will + * count both the WB and real-time phys encoders. + * + * For non-DSC CWB usecases, have the num_lm be decided by the + * (mode->hdisplay > MAX_HDISPLAY_SPLIT) check. */ - if (topology.num_intf == 2) + if (topology.num_intf == 2 && !topology.cwb_enabled) topology.num_lm = 2; else if (topology.num_dsc == 2) topology.num_lm = 2; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c index a7b4086ae990..0fbb92021b18 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c @@ -381,8 +381,18 @@ static int _dpu_rm_reserve_ctls( int i = 0, j, num_ctls; bool needs_split_display; - /* each hw_intf needs its own hw_ctrl to program its control path */ - num_ctls = top->num_intf; + /* + * For non-CWB mode, each hw_intf needs its own hw_ctl to program its + * control path. + * + * Hardcode num_ctls to 1 if CWB is enabled because in CWB, both the + * writeback and real-time encoders must be driven by the same control + * path + */ + if (top->cwb_enabled) + num_ctls = 1; + else + num_ctls = top->num_intf; needs_split_display = _dpu_rm_needs_split_display(top); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h index 463c532cdfdf..b854e42d319d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h @@ -52,6 +52,7 @@ struct dpu_rm_sspp_requirements { * @num_dspp: number of dspp blocks used * @num_dsc: number of Display Stream Compression (DSC) blocks used * @needs_cdm: indicates whether cdm block is needed for this display topology + * @cwb_enabled: indicates whether CWB is enabled for this display topology */ struct msm_display_topology { u32 num_lm; @@ -59,6 +60,7 @@ struct msm_display_topology { u32 num_dspp; u32 num_dsc; bool needs_cdm; + bool cwb_enabled; }; int dpu_rm_init(struct drm_device *dev, From 20972609d12ca677b8ecf576c632bfc3cfa369b5 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:28 -0800 Subject: [PATCH 42/86] drm/msm/dpu: Require modeset if clone mode status changes If the clone mode enabled status is changing, a modeset needs to happen so that the resources can be reassigned Reviewed-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637483/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-5-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 17 ++++++++++++----- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h | 3 ++- drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 5 +++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 27078d3f90b4..41dcfa7de97e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1351,19 +1351,26 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc, * * Check if the changes in the object properties demand full mode set. */ -int dpu_crtc_check_mode_changed(struct drm_crtc_state *crtc_state) +int dpu_crtc_check_mode_changed(struct drm_crtc_state *old_crtc_state, + struct drm_crtc_state *new_crtc_state) { struct drm_encoder *drm_enc; - struct drm_crtc *crtc = crtc_state->crtc; + struct drm_crtc *crtc = new_crtc_state->crtc; + bool clone_mode_enabled = drm_crtc_in_clone_mode(old_crtc_state); + bool clone_mode_requested = drm_crtc_in_clone_mode(new_crtc_state); DRM_DEBUG_ATOMIC("%d\n", crtc->base.id); /* there might be cases where encoder needs a modeset too */ - drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) { - if (dpu_encoder_needs_modeset(drm_enc, crtc_state->state)) - crtc_state->mode_changed = true; + drm_for_each_encoder_mask(drm_enc, crtc->dev, new_crtc_state->encoder_mask) { + if (dpu_encoder_needs_modeset(drm_enc, new_crtc_state->state)) + new_crtc_state->mode_changed = true; } + if ((clone_mode_requested && !clone_mode_enabled) || + (!clone_mode_requested && clone_mode_enabled)) + new_crtc_state->mode_changed = true; + return 0; } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h index 51a3b5fc879a..94392b9b9245 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h @@ -239,7 +239,8 @@ static inline int dpu_crtc_frame_pending(struct drm_crtc *crtc) return crtc ? atomic_read(&to_dpu_crtc(crtc)->frame_pending) : -EINVAL; } -int dpu_crtc_check_mode_changed(struct drm_crtc_state *crtc_state); +int dpu_crtc_check_mode_changed(struct drm_crtc_state *old_crtc_state, + struct drm_crtc_state *new_crtc_state); int dpu_crtc_vblank(struct drm_crtc *crtc, bool en); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 24c457b017e1..50f628412dc5 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -449,11 +449,12 @@ static void dpu_kms_disable_commit(struct msm_kms *kms) static int dpu_kms_check_mode_changed(struct msm_kms *kms, struct drm_atomic_state *state) { struct drm_crtc_state *new_crtc_state; + struct drm_crtc_state *old_crtc_state; struct drm_crtc *crtc; int i; - for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) - dpu_crtc_check_mode_changed(new_crtc_state); + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) + dpu_crtc_check_mode_changed(old_crtc_state, new_crtc_state); return 0; } From f1f0379e9dd5ab0cd2088b717efed27ed51de631 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:29 -0800 Subject: [PATCH 43/86] drm/msm/dpu: Fail atomic_check if multiple outputs request CDM block Currently, our hardware only supports a single output using CDM block at most. Because of this, we cannot support cases where both writeback and DP output request CDM simultaneously To avoid this happening when CWB is enabled, change msm_display_topoloy.needs_cdm into a num_cdm counter to track how many outputs are requesting CDM block. Return EINVAL if multiple outputs are trying to reserve CDM. Signed-off-by: Jessica Zhang Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/637499/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-6-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 12 +++++++++--- drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h | 5 +++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index a4091e861e31..9b6ed3ab4317 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -700,10 +700,10 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc, fb = conn_state->writeback_job->fb; if (fb && MSM_FORMAT_IS_YUV(msm_framebuffer_format(fb))) - topology->needs_cdm = true; + topology->num_cdm++; } else if (disp_info->intf_type == INTF_DP) { if (msm_dp_is_yuv_420_enabled(priv->dp[disp_info->h_tile_instance[0]], adj_mode)) - topology->needs_cdm = true; + topology->num_cdm++; } } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c index 0fbb92021b18..4da2e47265d4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c @@ -585,7 +585,8 @@ static int _dpu_rm_reserve_dsc(struct dpu_rm *rm, static int _dpu_rm_reserve_cdm(struct dpu_rm *rm, struct dpu_global_state *global_state, - uint32_t crtc_id) + uint32_t crtc_id, + int num_cdm) { /* try allocating only one CDM block */ if (!rm->cdm_blk) { @@ -593,6 +594,11 @@ static int _dpu_rm_reserve_cdm(struct dpu_rm *rm, return -EIO; } + if (num_cdm > 1) { + DPU_ERROR("More than 1 INTF requesting CDM\n"); + return -EINVAL; + } + if (global_state->cdm_to_crtc_id) { DPU_ERROR("CDM_0 is already allocated\n"); return -EIO; @@ -629,8 +635,8 @@ static int _dpu_rm_make_reservation( if (ret) return ret; - if (topology->needs_cdm) { - ret = _dpu_rm_reserve_cdm(rm, global_state, crtc_id); + if (topology->num_cdm > 0) { + ret = _dpu_rm_reserve_cdm(rm, global_state, crtc_id, topology->num_cdm); if (ret) { DPU_ERROR("unable to find CDM blk\n"); return ret; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h index b854e42d319d..a19dbdb1b6f4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h @@ -51,7 +51,8 @@ struct dpu_rm_sspp_requirements { * @num_intf: number of interfaces the panel is mounted on * @num_dspp: number of dspp blocks used * @num_dsc: number of Display Stream Compression (DSC) blocks used - * @needs_cdm: indicates whether cdm block is needed for this display topology + * @num_cdm: indicates how many outputs are requesting cdm block for + * this display topology * @cwb_enabled: indicates whether CWB is enabled for this display topology */ struct msm_display_topology { @@ -59,7 +60,7 @@ struct msm_display_topology { u32 num_intf; u32 num_dspp; u32 num_dsc; - bool needs_cdm; + int num_cdm; bool cwb_enabled; }; From 5008375443ed2624f70d160d3b06f1bbb4fd9bed Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:30 -0800 Subject: [PATCH 44/86] drm/msm/dpu: Reserve resources for CWB Add support for RM to reserve dedicated CWB PINGPONGs and CWB muxes For concurrent writeback, even-indexed CWB muxes must be assigned to even-indexed LMs and odd-indexed CWB muxes for odd-indexed LMs. The same even/odd rule applies for dedicated CWB PINGPONGs. Track the CWB muxes in the global state and add a CWB-specific helper to reserve the correct CWB muxes and dedicated PINGPONGs following the even/odd rule. Signed-off-by: Jessica Zhang Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/637495/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-7-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 34 +++++++- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h | 2 + drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c | 87 +++++++++++++++++++++ 4 files changed, 120 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 9b6ed3ab4317..cf99e38f9789 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2013 Red Hat * Copyright (c) 2014-2018, 2020-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. * * Author: Rob Clark */ @@ -28,6 +28,7 @@ #include "dpu_hw_dsc.h" #include "dpu_hw_merge3d.h" #include "dpu_hw_cdm.h" +#include "dpu_hw_cwb.h" #include "dpu_formats.h" #include "dpu_encoder_phys.h" #include "dpu_crtc.h" @@ -133,6 +134,9 @@ enum dpu_enc_rc_states { * @cur_slave: As above but for the slave encoder. * @hw_pp: Handle to the pingpong blocks used for the display. No. * pingpong blocks can be different than num_phys_encs. + * @hw_cwb: Handle to the CWB muxes used for concurrent writeback + * display. Number of CWB muxes can be different than + * num_phys_encs. * @hw_dsc: Handle to the DSC blocks used for the display. * @dsc_mask: Bitmask of used DSC blocks. * @intfs_swapped: Whether or not the phys_enc interfaces have been swapped @@ -177,6 +181,7 @@ struct dpu_encoder_virt { struct dpu_encoder_phys *cur_master; struct dpu_encoder_phys *cur_slave; struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC]; + struct dpu_hw_cwb *hw_cwb[MAX_CHANNELS_PER_ENC]; struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; unsigned int dsc_mask; @@ -1149,7 +1154,10 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, struct dpu_hw_blk *hw_pp[MAX_CHANNELS_PER_ENC]; struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_ENC]; struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC]; + struct dpu_hw_blk *hw_cwb[MAX_CHANNELS_PER_ENC]; int num_ctl, num_pp, num_dsc; + int num_cwb = 0; + bool is_cwb_encoder; unsigned int dsc_mask = 0; int i; @@ -1163,6 +1171,8 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, priv = drm_enc->dev->dev_private; dpu_kms = to_dpu_kms(priv->kms); + is_cwb_encoder = drm_crtc_in_clone_mode(crtc_state) && + dpu_enc->disp_info.intf_type == INTF_WB; global_state = dpu_kms_get_existing_global_state(dpu_kms); if (IS_ERR_OR_NULL(global_state)) { @@ -1173,9 +1183,25 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, trace_dpu_enc_mode_set(DRMID(drm_enc)); /* Query resource that have been reserved in atomic check step. */ - num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, - drm_enc->crtc, DPU_HW_BLK_PINGPONG, hw_pp, - ARRAY_SIZE(hw_pp)); + if (is_cwb_encoder) { + num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + drm_enc->crtc, + DPU_HW_BLK_DCWB_PINGPONG, + hw_pp, ARRAY_SIZE(hw_pp)); + num_cwb = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + drm_enc->crtc, + DPU_HW_BLK_CWB, + hw_cwb, ARRAY_SIZE(hw_cwb)); + } else { + num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + drm_enc->crtc, + DPU_HW_BLK_PINGPONG, hw_pp, + ARRAY_SIZE(hw_pp)); + } + + for (i = 0; i < num_cwb; i++) + dpu_enc->hw_cwb[i] = to_dpu_hw_cwb(hw_cwb[i]); + num_ctl = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, drm_enc->crtc, DPU_HW_BLK_CTL, hw_ctl, ARRAY_SIZE(hw_ctl)); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h index ba7bb05efe9b..8d820cd1b554 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h @@ -77,12 +77,14 @@ enum dpu_hw_blk_type { DPU_HW_BLK_LM, DPU_HW_BLK_CTL, DPU_HW_BLK_PINGPONG, + DPU_HW_BLK_DCWB_PINGPONG, DPU_HW_BLK_INTF, DPU_HW_BLK_WB, DPU_HW_BLK_DSPP, DPU_HW_BLK_MERGE_3D, DPU_HW_BLK_DSC, DPU_HW_BLK_CDM, + DPU_HW_BLK_CWB, DPU_HW_BLK_MAX, }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h index 54ef6cfa2485..a57ec2ec1060 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h @@ -132,6 +132,7 @@ struct dpu_global_state { uint32_t cdm_to_crtc_id; uint32_t sspp_to_crtc_id[SSPP_MAX - SSPP_NONE]; + uint32_t cwb_to_crtc_id[CWB_MAX - CWB_0]; }; struct dpu_global_state diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c index 4da2e47265d4..3efbba425ca6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c @@ -233,6 +233,59 @@ static int _dpu_rm_get_lm_peer(struct dpu_rm *rm, int primary_idx) return -EINVAL; } +static int _dpu_rm_reserve_cwb_mux_and_pingpongs(struct dpu_rm *rm, + struct dpu_global_state *global_state, + uint32_t crtc_id, + struct msm_display_topology *topology) +{ + int num_cwb_mux = topology->num_lm, cwb_mux_count = 0; + int cwb_pp_start_idx = PINGPONG_CWB_0 - PINGPONG_0; + int cwb_pp_idx[MAX_BLOCKS]; + int cwb_mux_idx[MAX_BLOCKS]; + + /* + * Reserve additional dedicated CWB PINGPONG blocks and muxes for each + * mixer + * + * TODO: add support reserving resources for platforms with no + * PINGPONG_CWB + */ + for (int i = 0; i < ARRAY_SIZE(rm->mixer_blks) && + cwb_mux_count < num_cwb_mux; i++) { + for (int j = 0; j < ARRAY_SIZE(rm->cwb_blks); j++) { + /* + * Odd LMs must be assigned to odd CWB muxes and even + * LMs with even CWB muxes. + * + * Since the RM HW block array index is based on the HW + * block ids, we can also use the array index to enforce + * the odd/even rule. See dpu_rm_init() for more + * information + */ + if (reserved_by_other(global_state->cwb_to_crtc_id, j, crtc_id) || + i % 2 != j % 2) + continue; + + cwb_mux_idx[cwb_mux_count] = j; + cwb_pp_idx[cwb_mux_count] = j + cwb_pp_start_idx; + cwb_mux_count++; + break; + } + } + + if (cwb_mux_count != num_cwb_mux) { + DPU_ERROR("Unable to reserve all CWB PINGPONGs\n"); + return -ENAVAIL; + } + + for (int i = 0; i < cwb_mux_count; i++) { + global_state->pingpong_to_crtc_id[cwb_pp_idx[i]] = crtc_id; + global_state->cwb_to_crtc_id[cwb_mux_idx[i]] = crtc_id; + } + + return 0; +} + /** * _dpu_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets * proposed use case requirements, incl. hardwired dependent blocks like @@ -623,6 +676,12 @@ static int _dpu_rm_make_reservation( return ret; } + if (topology->cwb_enabled) { + ret = _dpu_rm_reserve_cwb_mux_and_pingpongs(rm, global_state, + crtc_id, topology); + if (ret) + return ret; + } ret = _dpu_rm_reserve_ctls(rm, global_state, crtc_id, topology); @@ -680,6 +739,8 @@ void dpu_rm_release(struct dpu_global_state *global_state, _dpu_rm_clear_mapping(global_state->dspp_to_crtc_id, ARRAY_SIZE(global_state->dspp_to_crtc_id), crtc_id); _dpu_rm_clear_mapping(&global_state->cdm_to_crtc_id, 1, crtc_id); + _dpu_rm_clear_mapping(global_state->cwb_to_crtc_id, + ARRAY_SIZE(global_state->cwb_to_crtc_id), crtc_id); } /** @@ -824,6 +885,7 @@ int dpu_rm_get_assigned_resources(struct dpu_rm *rm, switch (type) { case DPU_HW_BLK_PINGPONG: + case DPU_HW_BLK_DCWB_PINGPONG: hw_blks = rm->pingpong_blks; hw_to_crtc_id = global_state->pingpong_to_crtc_id; max_blks = ARRAY_SIZE(rm->pingpong_blks); @@ -853,6 +915,11 @@ int dpu_rm_get_assigned_resources(struct dpu_rm *rm, hw_to_crtc_id = &global_state->cdm_to_crtc_id; max_blks = 1; break; + case DPU_HW_BLK_CWB: + hw_blks = rm->cwb_blks; + hw_to_crtc_id = global_state->cwb_to_crtc_id; + max_blks = ARRAY_SIZE(rm->cwb_blks); + break; default: DPU_ERROR("blk type %d not managed by rm\n", type); return 0; @@ -863,6 +930,20 @@ int dpu_rm_get_assigned_resources(struct dpu_rm *rm, if (hw_to_crtc_id[i] != crtc_id) continue; + if (type == DPU_HW_BLK_PINGPONG) { + struct dpu_hw_pingpong *pp = to_dpu_hw_pingpong(hw_blks[i]); + + if (pp->idx >= PINGPONG_CWB_0) + continue; + } + + if (type == DPU_HW_BLK_DCWB_PINGPONG) { + struct dpu_hw_pingpong *pp = to_dpu_hw_pingpong(hw_blks[i]); + + if (pp->idx < PINGPONG_CWB_0) + continue; + } + if (num_blks == blks_size) { DPU_ERROR("More than %d resources assigned to crtc %d\n", blks_size, crtc_id); @@ -945,4 +1026,10 @@ void dpu_rm_print_state(struct drm_printer *p, dpu_rm_print_state_helper(p, rm->hw_sspp[i] ? &rm->hw_sspp[i]->base : NULL, global_state->sspp_to_crtc_id[i]); drm_puts(p, "\n"); + + drm_puts(p, "\tcwb="); + for (i = 0; i < ARRAY_SIZE(global_state->cwb_to_crtc_id); i++) + dpu_rm_print_state_helper(p, rm->cwb_blks[i], + global_state->cwb_to_crtc_id[i]); + drm_puts(p, "\n"); } From dd331404ac7c155b2863038864901049fcf9d3fe Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:31 -0800 Subject: [PATCH 45/86] drm/msm/dpu: Configure CWB in writeback encoder Cache the CWB block mask in the DPU virtual encoder and configure CWB according to the CWB block mask within the writeback phys encoder Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637501/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-8-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 75 ++++++++++++++++++- .../gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h | 7 +- .../drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 4 +- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index cf99e38f9789..a132fadb1fca 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -24,6 +24,7 @@ #include "dpu_hw_catalog.h" #include "dpu_hw_intf.h" #include "dpu_hw_ctl.h" +#include "dpu_hw_cwb.h" #include "dpu_hw_dspp.h" #include "dpu_hw_dsc.h" #include "dpu_hw_merge3d.h" @@ -139,6 +140,7 @@ enum dpu_enc_rc_states { * num_phys_encs. * @hw_dsc: Handle to the DSC blocks used for the display. * @dsc_mask: Bitmask of used DSC blocks. + * @cwb_mask Bitmask of used CWB muxes * @intfs_swapped: Whether or not the phys_enc interfaces have been swapped * for partial update right-only cases, such as pingpong * split where virtual pingpong does not generate IRQs @@ -185,6 +187,7 @@ struct dpu_encoder_virt { struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC]; unsigned int dsc_mask; + unsigned int cwb_mask; bool intfs_swapped; @@ -1159,6 +1162,7 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, int num_cwb = 0; bool is_cwb_encoder; unsigned int dsc_mask = 0; + unsigned int cwb_mask = 0; int i; if (!drm_enc) { @@ -1199,8 +1203,12 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc, ARRAY_SIZE(hw_pp)); } - for (i = 0; i < num_cwb; i++) + for (i = 0; i < num_cwb; i++) { dpu_enc->hw_cwb[i] = to_dpu_hw_cwb(hw_cwb[i]); + cwb_mask |= BIT(dpu_enc->hw_cwb[i]->idx - CWB_0); + } + + dpu_enc->cwb_mask = cwb_mask; num_ctl = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, drm_enc->crtc, DPU_HW_BLK_CTL, hw_ctl, ARRAY_SIZE(hw_ctl)); @@ -2237,6 +2245,9 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) if (phys_enc->hw_pp && phys_enc->hw_pp->ops.setup_dither) phys_enc->hw_pp->ops.setup_dither(phys_enc->hw_pp, NULL); + if (dpu_enc->cwb_mask) + dpu_encoder_helper_phys_setup_cwb(phys_enc, false); + /* reset the merge 3D HW block */ if (phys_enc->hw_pp && phys_enc->hw_pp->merge_3d) { phys_enc->hw_pp->merge_3d->ops.setup_3d_mode(phys_enc->hw_pp->merge_3d, @@ -2280,6 +2291,56 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) ctl->ops.clear_pending_flush(ctl); } +void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc, + bool enable) +{ + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(phys_enc->parent); + struct dpu_hw_cwb *hw_cwb; + struct dpu_hw_cwb_setup_cfg cwb_cfg; + + struct dpu_kms *dpu_kms; + struct dpu_global_state *global_state; + struct dpu_hw_blk *rt_pp_list[MAX_CHANNELS_PER_ENC]; + int num_pp; + + if (!phys_enc->hw_wb) + return; + + dpu_kms = phys_enc->dpu_kms; + global_state = dpu_kms_get_existing_global_state(dpu_kms); + num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, + phys_enc->parent->crtc, + DPU_HW_BLK_PINGPONG, rt_pp_list, + ARRAY_SIZE(rt_pp_list)); + + if (num_pp == 0 || num_pp > MAX_CHANNELS_PER_ENC) { + DPU_DEBUG_ENC(dpu_enc, "invalid num_pp %d\n", num_pp); + return; + } + + /* + * The CWB mux supports using LM or DSPP as tap points. For now, + * always use LM tap point + */ + cwb_cfg.input = INPUT_MODE_LM_OUT; + + for (int i = 0; i < MAX_CHANNELS_PER_ENC; i++) { + hw_cwb = dpu_enc->hw_cwb[i]; + if (!hw_cwb) + continue; + + if (enable) { + struct dpu_hw_pingpong *hw_pp = + to_dpu_hw_pingpong(rt_pp_list[i]); + cwb_cfg.pp_idx = hw_pp->idx; + } else { + cwb_cfg.pp_idx = PINGPONG_NONE; + } + + hw_cwb->ops.config_cwb(hw_cwb, &cwb_cfg); + } +} + /** * dpu_encoder_helper_phys_setup_cdm - setup chroma down sampling block * @phys_enc: Pointer to physical encoder @@ -2740,6 +2801,18 @@ enum dpu_intf_mode dpu_encoder_get_intf_mode(struct drm_encoder *encoder) return INTF_MODE_NONE; } +/** + * dpu_encoder_helper_get_cwb_mask - get CWB blocks mask for the DPU encoder + * @phys_enc: Pointer to physical encoder structure + */ +unsigned int dpu_encoder_helper_get_cwb_mask(struct dpu_encoder_phys *phys_enc) +{ + struct drm_encoder *encoder = phys_enc->parent; + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(encoder); + + return dpu_enc->cwb_mask; +} + /** * dpu_encoder_helper_get_dsc - get DSC blocks mask for the DPU encoder * This helper function is used by physical encoder to get DSC blocks mask diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h index 63f09857025c..61b22d949454 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2018 The Linux Foundation. All rights reserved. */ @@ -309,6 +309,8 @@ static inline enum dpu_3d_blend_mode dpu_encoder_helper_get_3d_blend_mode( return BLEND_3D_NONE; } +unsigned int dpu_encoder_helper_get_cwb_mask(struct dpu_encoder_phys *phys_enc); + unsigned int dpu_encoder_helper_get_dsc(struct dpu_encoder_phys *phys_enc); struct drm_dsc_config *dpu_encoder_get_dsc_config(struct drm_encoder *drm_enc); @@ -331,6 +333,9 @@ int dpu_encoder_helper_wait_for_irq(struct dpu_encoder_phys *phys_enc, void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc); +void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc, + bool enable); + void dpu_encoder_helper_phys_setup_cdm(struct dpu_encoder_phys *phys_enc, const struct msm_format *dpu_fmt, u32 output_type); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c index 4c006ec74575..f2cbc9335e54 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ @@ -340,6 +340,8 @@ static void dpu_encoder_phys_wb_setup( dpu_encoder_helper_phys_setup_cdm(phys_enc, format, CDM_CDWN_OUTPUT_WB); + dpu_encoder_helper_phys_setup_cwb(phys_enc, true); + dpu_encoder_phys_wb_setup_ctl(phys_enc); } From 0f3801d666fe49069abc7883af4061c761e1bb68 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:32 -0800 Subject: [PATCH 46/86] drm/msm/dpu: Support CWB in dpu_hw_ctl The CWB mux has a pending flush bit and *_active register. Add support for configuring them within the dpu_hw_ctl layer. Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637492/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-9-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 13 ++++++++ .../drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c | 30 ++++++++++++++++++- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h | 15 +++++++++- 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index a132fadb1fca..f2fc37067ec5 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -2274,6 +2274,7 @@ void dpu_encoder_helper_phys_cleanup(struct dpu_encoder_phys *phys_enc) intf_cfg.stream_sel = 0; /* Don't care value for video mode */ intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc); intf_cfg.dsc = dpu_encoder_helper_get_dsc(phys_enc); + intf_cfg.cwb = dpu_enc->cwb_mask; if (phys_enc->hw_intf) intf_cfg.intf = phys_enc->hw_intf->idx; @@ -2296,6 +2297,7 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc, { struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(phys_enc->parent); struct dpu_hw_cwb *hw_cwb; + struct dpu_hw_ctl *hw_ctl; struct dpu_hw_cwb_setup_cfg cwb_cfg; struct dpu_kms *dpu_kms; @@ -2306,6 +2308,14 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc, if (!phys_enc->hw_wb) return; + hw_ctl = phys_enc->hw_ctl; + + if (!phys_enc->hw_ctl) { + DPU_DEBUG("[wb:%d] no ctl assigned\n", + phys_enc->hw_wb->idx - WB_0); + return; + } + dpu_kms = phys_enc->dpu_kms; global_state = dpu_kms_get_existing_global_state(dpu_kms); num_pp = dpu_rm_get_assigned_resources(&dpu_kms->rm, global_state, @@ -2338,6 +2348,9 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc, } hw_cwb->ops.config_cwb(hw_cwb, &cwb_cfg); + + if (hw_ctl->ops.update_pending_flush_cwb) + hw_ctl->ops.update_pending_flush_cwb(hw_ctl, hw_cwb->idx); } } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c index f2cbc9335e54..648e6b3aab84 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c @@ -236,6 +236,7 @@ static void dpu_encoder_phys_wb_setup_ctl(struct dpu_encoder_phys *phys_enc) intf_cfg.intf = DPU_NONE; intf_cfg.wb = hw_wb->idx; + intf_cfg.cwb = dpu_encoder_helper_get_cwb_mask(phys_enc); if (mode_3d && hw_pp && hw_pp->merge_3d) intf_cfg.merge_3d = hw_pp->merge_3d->idx; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c index 4893f10d6a58..411a7cf088eb 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -31,12 +31,14 @@ #define CTL_MERGE_3D_ACTIVE 0x0E4 #define CTL_DSC_ACTIVE 0x0E8 #define CTL_WB_ACTIVE 0x0EC +#define CTL_CWB_ACTIVE 0x0F0 #define CTL_INTF_ACTIVE 0x0F4 #define CTL_CDM_ACTIVE 0x0F8 #define CTL_FETCH_PIPE_ACTIVE 0x0FC #define CTL_MERGE_3D_FLUSH 0x100 #define CTL_DSC_FLUSH 0x104 #define CTL_WB_FLUSH 0x108 +#define CTL_CWB_FLUSH 0x10C #define CTL_INTF_FLUSH 0x110 #define CTL_CDM_FLUSH 0x114 #define CTL_PERIPH_FLUSH 0x128 @@ -53,6 +55,7 @@ #define PERIPH_IDX 30 #define INTF_IDX 31 #define WB_IDX 16 +#define CWB_IDX 28 #define DSPP_IDX 29 /* From DPU hw rev 7.x.x */ #define CTL_INVALID_BIT 0xffff #define CTL_DEFAULT_GROUP_ID 0xf @@ -110,6 +113,7 @@ static inline void dpu_hw_ctl_clear_pending_flush(struct dpu_hw_ctl *ctx) ctx->pending_flush_mask = 0x0; ctx->pending_intf_flush_mask = 0; ctx->pending_wb_flush_mask = 0; + ctx->pending_cwb_flush_mask = 0; ctx->pending_merge_3d_flush_mask = 0; ctx->pending_dsc_flush_mask = 0; ctx->pending_cdm_flush_mask = 0; @@ -144,6 +148,9 @@ static inline void dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx) if (ctx->pending_flush_mask & BIT(WB_IDX)) DPU_REG_WRITE(&ctx->hw, CTL_WB_FLUSH, ctx->pending_wb_flush_mask); + if (ctx->pending_flush_mask & BIT(CWB_IDX)) + DPU_REG_WRITE(&ctx->hw, CTL_CWB_FLUSH, + ctx->pending_cwb_flush_mask); if (ctx->pending_flush_mask & BIT(DSPP_IDX)) for (dspp = DSPP_0; dspp < DSPP_MAX; dspp++) { @@ -310,6 +317,13 @@ static void dpu_hw_ctl_update_pending_flush_wb_v1(struct dpu_hw_ctl *ctx, ctx->pending_flush_mask |= BIT(WB_IDX); } +static void dpu_hw_ctl_update_pending_flush_cwb_v1(struct dpu_hw_ctl *ctx, + enum dpu_cwb cwb) +{ + ctx->pending_cwb_flush_mask |= BIT(cwb - CWB_0); + ctx->pending_flush_mask |= BIT(CWB_IDX); +} + static void dpu_hw_ctl_update_pending_flush_intf_v1(struct dpu_hw_ctl *ctx, enum dpu_intf intf) { @@ -547,6 +561,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx, u32 intf_active = 0; u32 dsc_active = 0; u32 wb_active = 0; + u32 cwb_active = 0; u32 mode_sel = 0; /* CTL_TOP[31:28] carries group_id to collate CTL paths @@ -561,6 +576,7 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx, intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE); wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE); + cwb_active = DPU_REG_READ(c, CTL_CWB_ACTIVE); dsc_active = DPU_REG_READ(c, CTL_DSC_ACTIVE); if (cfg->intf) @@ -569,12 +585,16 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx, if (cfg->wb) wb_active |= BIT(cfg->wb - WB_0); + if (cfg->cwb) + cwb_active |= cfg->cwb; + if (cfg->dsc) dsc_active |= cfg->dsc; DPU_REG_WRITE(c, CTL_TOP, mode_sel); DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active); DPU_REG_WRITE(c, CTL_WB_ACTIVE, wb_active); + DPU_REG_WRITE(c, CTL_CWB_ACTIVE, cwb_active); DPU_REG_WRITE(c, CTL_DSC_ACTIVE, dsc_active); if (cfg->merge_3d) @@ -624,6 +644,7 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct dpu_hw_ctl *ctx, struct dpu_hw_blk_reg_map *c = &ctx->hw; u32 intf_active = 0; u32 wb_active = 0; + u32 cwb_active = 0; u32 merge3d_active = 0; u32 dsc_active; u32 cdm_active; @@ -651,6 +672,12 @@ static void dpu_hw_ctl_reset_intf_cfg_v1(struct dpu_hw_ctl *ctx, DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active); } + if (cfg->cwb) { + cwb_active = DPU_REG_READ(c, CTL_CWB_ACTIVE); + cwb_active &= ~cfg->cwb; + DPU_REG_WRITE(c, CTL_CWB_ACTIVE, cwb_active); + } + if (cfg->wb) { wb_active = DPU_REG_READ(c, CTL_WB_ACTIVE); wb_active &= ~BIT(cfg->wb - WB_0); @@ -703,6 +730,7 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops, ops->update_pending_flush_merge_3d = dpu_hw_ctl_update_pending_flush_merge_3d_v1; ops->update_pending_flush_wb = dpu_hw_ctl_update_pending_flush_wb_v1; + ops->update_pending_flush_cwb = dpu_hw_ctl_update_pending_flush_cwb_v1; ops->update_pending_flush_dsc = dpu_hw_ctl_update_pending_flush_dsc_v1; ops->update_pending_flush_cdm = dpu_hw_ctl_update_pending_flush_cdm_v1; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h index 85c6c835cc87..080a9550a0cc 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _DPU_HW_CTL_H @@ -42,6 +42,7 @@ struct dpu_hw_stage_cfg { * @cdm: CDM block used * @stream_sel: Stream selection for multi-stream interfaces * @dsc: DSC BIT masks used + * @cwb: CWB BIT masks used */ struct dpu_hw_intf_cfg { enum dpu_intf intf; @@ -51,6 +52,7 @@ struct dpu_hw_intf_cfg { enum dpu_ctl_mode_sel intf_mode_sel; enum dpu_cdm cdm; int stream_sel; + unsigned int cwb; unsigned int dsc; }; @@ -114,6 +116,15 @@ struct dpu_hw_ctl_ops { void (*update_pending_flush_wb)(struct dpu_hw_ctl *ctx, enum dpu_wb blk); + /** + * OR in the given flushbits to the cached pending_(cwb_)flush_mask + * No effect on hardware + * @ctx : ctl path ctx pointer + * @blk : concurrent writeback block index + */ + void (*update_pending_flush_cwb)(struct dpu_hw_ctl *ctx, + enum dpu_cwb blk); + /** * OR in the given flushbits to the cached pending_(intf_)flush_mask * No effect on hardware @@ -258,6 +269,7 @@ struct dpu_hw_ctl_ops { * @pending_flush_mask: storage for pending ctl_flush managed via ops * @pending_intf_flush_mask: pending INTF flush * @pending_wb_flush_mask: pending WB flush + * @pending_cwb_flush_mask: pending CWB flush * @pending_dsc_flush_mask: pending DSC flush * @pending_cdm_flush_mask: pending CDM flush * @ops: operation list @@ -274,6 +286,7 @@ struct dpu_hw_ctl { u32 pending_flush_mask; u32 pending_intf_flush_mask; u32 pending_wb_flush_mask; + u32 pending_cwb_flush_mask; u32 pending_periph_flush_mask; u32 pending_merge_3d_flush_mask; u32 pending_dspp_flush_mask[DSPP_MAX - DSPP_0]; From 3371005e28e8e731e62b7a51e0f0c3f5ee584c2c Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:33 -0800 Subject: [PATCH 47/86] drm/msm/dpu: Adjust writeback phys encoder setup for CWB Adjust QoS remapper, OT limit, and CDP parameters to account for concurrent writeback Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637490/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-10-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c index 648e6b3aab84..849fea580a4c 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c @@ -68,7 +68,7 @@ static void dpu_encoder_phys_wb_set_ot_limit( ot_params.num = hw_wb->idx - WB_0; ot_params.width = phys_enc->cached_mode.hdisplay; ot_params.height = phys_enc->cached_mode.vdisplay; - ot_params.is_wfd = true; + ot_params.is_wfd = !dpu_encoder_helper_get_cwb_mask(phys_enc); ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode); ot_params.vbif_idx = hw_wb->caps->vbif_idx; ot_params.rd = false; @@ -111,7 +111,7 @@ static void dpu_encoder_phys_wb_set_qos_remap( qos_params.vbif_idx = hw_wb->caps->vbif_idx; qos_params.xin_id = hw_wb->caps->xin_id; qos_params.num = hw_wb->idx - WB_0; - qos_params.is_rt = false; + qos_params.is_rt = dpu_encoder_helper_get_cwb_mask(phys_enc); DPU_DEBUG("[qos_remap] wb:%d vbif:%d xin:%d is_rt:%d\n", qos_params.num, @@ -174,6 +174,7 @@ static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys *phys_enc, struct dpu_encoder_phys_wb *wb_enc = to_dpu_encoder_phys_wb(phys_enc); struct dpu_hw_wb *hw_wb; struct dpu_hw_wb_cfg *wb_cfg; + u32 cdp_usage; if (!phys_enc || !phys_enc->dpu_kms || !phys_enc->dpu_kms->catalog) { DPU_ERROR("invalid encoder\n"); @@ -182,6 +183,10 @@ static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys *phys_enc, hw_wb = phys_enc->hw_wb; wb_cfg = &wb_enc->wb_cfg; + if (dpu_encoder_helper_get_cwb_mask(phys_enc)) + cdp_usage = DPU_PERF_CDP_USAGE_RT; + else + cdp_usage = DPU_PERF_CDP_USAGE_NRT; wb_cfg->intf_mode = phys_enc->intf_mode; wb_cfg->roi.x1 = 0; @@ -199,7 +204,7 @@ static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys *phys_enc, const struct dpu_perf_cfg *perf = phys_enc->dpu_kms->catalog->perf; hw_wb->ops.setup_cdp(hw_wb, format, - perf->cdp_cfg[DPU_PERF_CDP_USAGE_NRT].wr_enable); + perf->cdp_cfg[cdp_usage].wr_enable); } if (hw_wb->ops.setup_outaddress) From 95bbde1d0d079f958c0f27c94ec0e2151cb1398c Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:34 -0800 Subject: [PATCH 48/86] drm/msm/dpu: Start frame done timer after encoder kickoff Starting the frame done timer before the encoder is finished kicking off can lead to unnecessary frame done timeouts when the device is experiencing heavy load (ex. when debug logs are enabled). Thus, create a separate API for starting the encoder frame done timer and call it after the encoder kickoff is finished Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637502/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-11-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 4 ++- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 27 +++++++++++++++------ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 3 ++- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 41dcfa7de97e..e2bc223acd13 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -999,8 +999,10 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) dpu_vbif_clear_errors(dpu_kms); - drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) + drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) { dpu_encoder_kickoff(encoder); + dpu_encoder_start_frame_done_timer(encoder); + } reinit_completion(&dpu_crtc->frame_done_comp); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index f2fc37067ec5..876bb16d88cd 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -2095,6 +2095,25 @@ bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc) return true; } +/** + * dpu_encoder_start_frame_done_timer - Start the encoder frame done timer + * @drm_enc: Pointer to drm encoder structure + */ +void dpu_encoder_start_frame_done_timer(struct drm_encoder *drm_enc) +{ + struct dpu_encoder_virt *dpu_enc; + unsigned long timeout_ms; + + dpu_enc = to_dpu_encoder_virt(drm_enc); + timeout_ms = DPU_ENCODER_FRAME_DONE_TIMEOUT_FRAMES * 1000 / + drm_mode_vrefresh(&drm_enc->crtc->state->adjusted_mode); + + atomic_set(&dpu_enc->frame_done_timeout_ms, timeout_ms); + mod_timer(&dpu_enc->frame_done_timer, + jiffies + msecs_to_jiffies(timeout_ms)); + +} + /** * dpu_encoder_kickoff - trigger a double buffer flip of the ctl path * (i.e. ctl flush and start) immediately. @@ -2104,7 +2123,6 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc) { struct dpu_encoder_virt *dpu_enc; struct dpu_encoder_phys *phys; - unsigned long timeout_ms; unsigned int i; DPU_ATRACE_BEGIN("encoder_kickoff"); @@ -2112,13 +2130,6 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc) trace_dpu_enc_kickoff(DRMID(drm_enc)); - timeout_ms = DPU_ENCODER_FRAME_DONE_TIMEOUT_FRAMES * 1000 / - drm_mode_vrefresh(&drm_enc->crtc->state->adjusted_mode); - - atomic_set(&dpu_enc->frame_done_timeout_ms, timeout_ms); - mod_timer(&dpu_enc->frame_done_timer, - jiffies + msecs_to_jiffies(timeout_ms)); - /* All phys encs are ready to go, trigger the kickoff */ _dpu_encoder_kickoff_phys(dpu_enc); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index b0ac10ebd02c..8503386bb503 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark @@ -95,4 +95,5 @@ void dpu_encoder_cleanup_wb_job(struct drm_encoder *drm_enc, bool dpu_encoder_is_valid_for_commit(struct drm_encoder *drm_enc); +void dpu_encoder_start_frame_done_timer(struct drm_encoder *drm_enc); #endif /* __DPU_ENCODER_H__ */ From 8144d17a81d9ea742be5a02da62f5a7b2a8f95c1 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:35 -0800 Subject: [PATCH 49/86] drm/msm/dpu: Skip trigger flush and start for CWB For concurrent writeback, the real time encoder is responsible for trigger flush and trigger start. Return early for trigger start and trigger flush for the concurrent writeback encoders. Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637505/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-12-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 876bb16d88cd..84cc3f0b9918 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -1618,6 +1618,7 @@ static void dpu_encoder_off_work(struct work_struct *work) static void _dpu_encoder_trigger_flush(struct drm_encoder *drm_enc, struct dpu_encoder_phys *phys, uint32_t extra_flush_bits) { + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc); struct dpu_hw_ctl *ctl; int pending_kickoff_cnt; u32 ret = UINT_MAX; @@ -1635,6 +1636,15 @@ static void _dpu_encoder_trigger_flush(struct drm_encoder *drm_enc, pending_kickoff_cnt = dpu_encoder_phys_inc_pending(phys); + /* Return early if encoder is writeback and in clone mode */ + if (drm_enc->encoder_type == DRM_MODE_ENCODER_VIRTUAL && + dpu_enc->cwb_mask) { + DPU_DEBUG("encoder %d skip flush for concurrent writeback encoder\n", + DRMID(drm_enc)); + return; + } + + if (extra_flush_bits && ctl->ops.update_pending_flush) ctl->ops.update_pending_flush(ctl, extra_flush_bits); @@ -1657,6 +1667,8 @@ static void _dpu_encoder_trigger_flush(struct drm_encoder *drm_enc, */ static void _dpu_encoder_trigger_start(struct dpu_encoder_phys *phys) { + struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(phys->parent); + if (!phys) { DPU_ERROR("invalid argument(s)\n"); return; @@ -1667,6 +1679,12 @@ static void _dpu_encoder_trigger_start(struct dpu_encoder_phys *phys) return; } + if (phys->parent->encoder_type == DRM_MODE_ENCODER_VIRTUAL && + dpu_enc->cwb_mask) { + DPU_DEBUG("encoder %d CWB enabled, skipping\n", DRMID(phys->parent)); + return; + } + if (phys->ops.trigger_start && phys->enable_state != DPU_ENC_DISABLED) phys->ops.trigger_start(phys); } From ad06972d5365b2b4107cfc7264b072a8091876b5 Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:36 -0800 Subject: [PATCH 50/86] drm/msm/dpu: Reorder encoder kickoff for CWB Add a helper that will handle the correct order of the encoder kickoffs for concurrent writeback. For concurrent writeback, the realtime encoder must always kickoff last as it will call the trigger flush and start. This avoids the following scenario where the writeback encoder increments the pending kickoff count after the WB_DONE interrupt is fired: If the realtime encoder is kicked off first, the encoder kickoff will flush/start the encoder and increment the pending kickoff count. The WB_DONE interrupt then fires (before the writeback encoder is kicked off). When the writeback encoder enters its kickoff, it will skip the flush/start (due to CWB being enabled) and hit a frame done timeout as the frame was kicked off (and the WB_DONE interrupt fired) without the pending kickoff count being incremented. In addition, the writeback timer should only start after the realtime encoder is kicked off to ensure that we don't get timeouts when the system has a heavy load (ex. when debug logs are enabled) Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637491/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-13-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 74 +++++++++++++++++++----- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index e2bc223acd13..b8e1a8f2aad5 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -953,6 +953,45 @@ static int _dpu_crtc_wait_for_frame_done(struct drm_crtc *crtc) return rc; } +static int dpu_crtc_kickoff_clone_mode(struct drm_crtc *crtc) +{ + struct drm_encoder *encoder; + struct drm_encoder *rt_encoder = NULL, *wb_encoder; + struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); + + /* Find encoder for real time display */ + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) { + if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) + wb_encoder = encoder; + else + rt_encoder = encoder; + } + + if (!rt_encoder || !wb_encoder) { + DRM_DEBUG_ATOMIC("real time or wb encoder not found\n"); + return -EINVAL; + } + + dpu_encoder_prepare_for_kickoff(wb_encoder); + dpu_encoder_prepare_for_kickoff(rt_encoder); + + dpu_vbif_clear_errors(dpu_kms); + + /* + * Kickoff real time encoder last as it's the encoder that + * will do the flush + */ + dpu_encoder_kickoff(wb_encoder); + dpu_encoder_kickoff(rt_encoder); + + /* Don't start frame done timers until the kickoffs have finished */ + dpu_encoder_start_frame_done_timer(wb_encoder); + dpu_encoder_start_frame_done_timer(rt_encoder); + + return 0; +} + /** * dpu_crtc_commit_kickoff - trigger kickoff of the commit for this crtc * @crtc: Pointer to drm crtc object @@ -981,13 +1020,27 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) goto end; } } - /* - * Encoder will flush/start now, unless it has a tx pending. If so, it - * may delay and flush at an irq event (e.g. ppdone) - */ - drm_for_each_encoder_mask(encoder, crtc->dev, - crtc->state->encoder_mask) - dpu_encoder_prepare_for_kickoff(encoder); + + if (drm_crtc_in_clone_mode(crtc->state)) { + if (dpu_crtc_kickoff_clone_mode(crtc)) + goto end; + } else { + /* + * Encoder will flush/start now, unless it has a tx pending. + * If so, it may delay and flush at an irq event (e.g. ppdone) + */ + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) + dpu_encoder_prepare_for_kickoff(encoder); + + dpu_vbif_clear_errors(dpu_kms); + + drm_for_each_encoder_mask(encoder, crtc->dev, + crtc->state->encoder_mask) { + dpu_encoder_kickoff(encoder); + dpu_encoder_start_frame_done_timer(encoder); + } + } if (atomic_inc_return(&dpu_crtc->frame_pending) == 1) { /* acquire bandwidth and other resources */ @@ -997,13 +1050,6 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc) dpu_crtc->play_count++; - dpu_vbif_clear_errors(dpu_kms); - - drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) { - dpu_encoder_kickoff(encoder); - dpu_encoder_start_frame_done_timer(encoder); - } - reinit_completion(&dpu_crtc->frame_done_comp); end: From e8cd8224a30798b65e05b26de284e1702b22ba5e Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Fri, 14 Feb 2025 16:14:37 -0800 Subject: [PATCH 51/86] drm/msm/dpu: Set possible clones for all encoders Set writeback encoders as possible clones for DSI encoders and vice versa. Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Signed-off-by: Jessica Zhang Patchwork: https://patchwork.freedesktop.org/patch/637498/ Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-14-a44c293cf422@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 32 +++++++++++++++++++++ drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h | 2 ++ drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 7 +++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 84cc3f0b9918..0eed93a4d056 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -2569,6 +2569,38 @@ static int dpu_encoder_virt_add_phys_encs( return 0; } +/** + * dpu_encoder_get_clones - Calculate the possible_clones for DPU encoder + * @drm_enc: DRM encoder pointer + * Returns: possible_clones mask + */ +uint32_t dpu_encoder_get_clones(struct drm_encoder *drm_enc) +{ + struct drm_encoder *curr; + int type = drm_enc->encoder_type; + uint32_t clone_mask = drm_encoder_mask(drm_enc); + + /* + * Set writeback as possible clones of real-time DSI encoders and vice + * versa + * + * Writeback encoders can't be clones of each other and DSI + * encoders can't be clones of each other. + * + * TODO: Add DP encoders as valid possible clones for writeback encoders + * (and vice versa) once concurrent writeback has been validated for DP + */ + drm_for_each_encoder(curr, drm_enc->dev) { + if ((type == DRM_MODE_ENCODER_VIRTUAL && + curr->encoder_type == DRM_MODE_ENCODER_DSI) || + (type == DRM_MODE_ENCODER_DSI && + curr->encoder_type == DRM_MODE_ENCODER_VIRTUAL)) + clone_mask |= drm_encoder_mask(curr); + } + + return clone_mask; +} + static int dpu_encoder_setup_display(struct dpu_encoder_virt *dpu_enc, struct dpu_kms *dpu_kms, struct msm_display_info *disp_info) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h index 8503386bb503..ca1ca2e51d7e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.h @@ -60,6 +60,8 @@ enum dpu_intf_mode dpu_encoder_get_intf_mode(struct drm_encoder *encoder); void dpu_encoder_virt_runtime_resume(struct drm_encoder *encoder); +uint32_t dpu_encoder_get_clones(struct drm_encoder *drm_enc); + struct drm_encoder *dpu_encoder_init(struct drm_device *dev, int drm_enc_mode, struct msm_display_info *disp_info); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index 50f628412dc5..3305ad0623ca 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2013 Red Hat * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. * * Author: Rob Clark */ @@ -824,8 +824,11 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms) return ret; num_encoders = 0; - drm_for_each_encoder(encoder, dev) + drm_for_each_encoder(encoder, dev) { num_encoders++; + if (catalog->cwb_count > 0) + encoder->possible_clones = dpu_encoder_get_clones(encoder); + } max_crtc_count = min(catalog->mixer_count, num_encoders); From 8ae7192e7a003b7db408b79283d4e48b0da465c2 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 26 Feb 2025 10:59:24 +0200 Subject: [PATCH 52/86] drm/msm/hdmi: switch to atomic bridge callbacks Change MSM HDMI bridge to use atomic_* callbacks in preparation to enablign the HDMI connector support. Acked-by: Maxime Ripard Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/639653/ Link: https://lore.kernel.org/r/20250226-bridge-hdmi-connector-v8-1-340af24b35cc@linaro.org --- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 4a5b5112227f..457364ebcad7 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -126,7 +126,8 @@ static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi) hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val); } -static void msm_hdmi_bridge_pre_enable(struct drm_bridge *bridge) +static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; @@ -152,7 +153,8 @@ static void msm_hdmi_bridge_pre_enable(struct drm_bridge *bridge) msm_hdmi_hdcp_on(hdmi->hdcp_ctrl); } -static void msm_hdmi_bridge_post_disable(struct drm_bridge *bridge) +static void msm_hdmi_bridge_atomic_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; @@ -299,8 +301,11 @@ static enum drm_mode_status msm_hdmi_bridge_mode_valid(struct drm_bridge *bridge } static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = { - .pre_enable = msm_hdmi_bridge_pre_enable, - .post_disable = msm_hdmi_bridge_post_disable, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_pre_enable = msm_hdmi_bridge_atomic_pre_enable, + .atomic_post_disable = msm_hdmi_bridge_atomic_post_disable, .mode_set = msm_hdmi_bridge_mode_set, .mode_valid = msm_hdmi_bridge_mode_valid, .edid_read = msm_hdmi_bridge_edid_read, From d309bda671726fbe4fcd5262664348b13d359364 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 26 Feb 2025 10:59:25 +0200 Subject: [PATCH 53/86] drm/msm/hdmi: program HDMI timings during atomic_pre_enable The mode_set callback is deprecated, it doesn't get the drm_bridge_state, just mode-related argumetns. Also Abhinav pointed out that HDMI timings should be programmed before setting up HDMI PHY and PLL. Rework the code to program HDMI timings at the start of atomic_pre_enable(). Reviewed-by: Maxime Ripard Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/639652/ Link: https://lore.kernel.org/r/20250226-bridge-hdmi-connector-v8-2-340af24b35cc@linaro.org --- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 457364ebcad7..64e9605f9e3b 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -126,15 +126,28 @@ static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi) hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val); } +static void msm_hdmi_set_timings(struct hdmi *hdmi, + const struct drm_display_mode *mode); + static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; struct hdmi_phy *phy = hdmi->phy; + struct drm_encoder *encoder = bridge->encoder; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; DBG("power up"); + connector = drm_atomic_get_new_connector_for_encoder(state, encoder); + conn_state = drm_atomic_get_new_connector_state(state, connector); + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + + msm_hdmi_set_timings(hdmi, &crtc_state->adjusted_mode); + if (!hdmi->power_on) { msm_hdmi_phy_resource_enable(phy); msm_hdmi_power_on(bridge); @@ -177,17 +190,12 @@ static void msm_hdmi_bridge_atomic_post_disable(struct drm_bridge *bridge, } } -static void msm_hdmi_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adjusted_mode) +static void msm_hdmi_set_timings(struct hdmi *hdmi, + const struct drm_display_mode *mode) { - struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); - struct hdmi *hdmi = hdmi_bridge->hdmi; int hstart, hend, vstart, vend; uint32_t frame_ctrl; - mode = adjusted_mode; - hdmi->pixclock = mode->clock * 1000; hstart = mode->htotal - mode->hsync_start; @@ -306,7 +314,6 @@ static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset, .atomic_pre_enable = msm_hdmi_bridge_atomic_pre_enable, .atomic_post_disable = msm_hdmi_bridge_atomic_post_disable, - .mode_set = msm_hdmi_bridge_mode_set, .mode_valid = msm_hdmi_bridge_mode_valid, .edid_read = msm_hdmi_bridge_edid_read, .detect = msm_hdmi_bridge_detect, From 384d2b03d0a11ffef66661e14b266e5793b1826a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 26 Feb 2025 10:59:26 +0200 Subject: [PATCH 54/86] drm/msm/hdmi: make use of the drm_connector_hdmi framework Setup the HDMI connector on the MSM HDMI outputs. Make use of atomic_check hook and of the provided Infoframe infrastructure. Acked-by: Maxime Ripard Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/639656/ Link: https://lore.kernel.org/r/20250226-bridge-hdmi-connector-v8-3-340af24b35cc@linaro.org --- drivers/gpu/drm/msm/Kconfig | 2 + drivers/gpu/drm/msm/hdmi/hdmi.c | 45 ++----- drivers/gpu/drm/msm/hdmi/hdmi.h | 15 +-- drivers/gpu/drm/msm/hdmi/hdmi_audio.c | 72 +++------- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 175 ++++++++++++++++++------- 5 files changed, 162 insertions(+), 147 deletions(-) diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 7ec833b6d829..974bc7c0ea76 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -170,6 +170,8 @@ config DRM_MSM_HDMI bool "Enable HDMI support in MSM DRM driver" depends on DRM_MSM default y + select DRM_DISPLAY_HDMI_HELPER + select DRM_DISPLAY_HDMI_STATE_HELPER help Compile in support for the HDMI output MSM DRM driver. It can be a primary or a secondary display on device. Note that this is used diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 37b3809c6bdd..b14205cb9e97 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -12,6 +12,7 @@ #include #include +#include #include #include "hdmi.h" @@ -165,8 +166,6 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi, hdmi->dev = dev; hdmi->encoder = encoder; - hdmi_audio_infoframe_init(&hdmi->audio.infoframe); - ret = msm_hdmi_bridge_init(hdmi); if (ret) { DRM_DEV_ERROR(dev->dev, "failed to create HDMI bridge: %d\n", ret); @@ -254,40 +253,12 @@ static int msm_hdmi_audio_hw_params(struct device *dev, void *data, struct hdmi_codec_params *params) { struct hdmi *hdmi = dev_get_drvdata(dev); - unsigned int chan; - unsigned int channel_allocation = 0; unsigned int rate; - unsigned int level_shift = 0; /* 0dB */ - bool down_mix = false; + int ret; DRM_DEV_DEBUG(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate, params->sample_width, params->cea.channels); - switch (params->cea.channels) { - case 2: - /* FR and FL speakers */ - channel_allocation = 0; - chan = MSM_HDMI_AUDIO_CHANNEL_2; - break; - case 4: - /* FC, LFE, FR and FL speakers */ - channel_allocation = 0x3; - chan = MSM_HDMI_AUDIO_CHANNEL_4; - break; - case 6: - /* RR, RL, FC, LFE, FR and FL speakers */ - channel_allocation = 0x0B; - chan = MSM_HDMI_AUDIO_CHANNEL_6; - break; - case 8: - /* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */ - channel_allocation = 0x1F; - chan = MSM_HDMI_AUDIO_CHANNEL_8; - break; - default: - return -EINVAL; - } - switch (params->sample_rate) { case 32000: rate = HDMI_SAMPLE_RATE_32KHZ; @@ -316,9 +287,12 @@ static int msm_hdmi_audio_hw_params(struct device *dev, void *data, return -EINVAL; } - msm_hdmi_audio_set_sample_rate(hdmi, rate); - msm_hdmi_audio_info_setup(hdmi, 1, chan, channel_allocation, - level_shift, down_mix); + ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(hdmi->connector, + ¶ms->cea); + if (ret) + return ret; + + msm_hdmi_audio_info_setup(hdmi, rate, params->cea.channels); return 0; } @@ -327,7 +301,8 @@ static void msm_hdmi_audio_shutdown(struct device *dev, void *data) { struct hdmi *hdmi = dev_get_drvdata(dev); - msm_hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0); + drm_atomic_helper_connector_hdmi_clear_audio_infoframe(hdmi->connector); + msm_hdmi_audio_disable(hdmi); } static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = { diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index a62d2aedfbb7..8faad8440cf7 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -24,8 +24,8 @@ struct hdmi_platform_config; struct hdmi_audio { bool enabled; - struct hdmi_audio_infoframe infoframe; int rate; + int channels; }; struct hdmi_hdcp_ctrl; @@ -207,12 +207,6 @@ static inline int msm_hdmi_pll_8998_init(struct platform_device *pdev) /* * audio: */ -/* Supported HDMI Audio channels and rates */ -#define MSM_HDMI_AUDIO_CHANNEL_2 0 -#define MSM_HDMI_AUDIO_CHANNEL_4 1 -#define MSM_HDMI_AUDIO_CHANNEL_6 2 -#define MSM_HDMI_AUDIO_CHANNEL_8 3 - #define HDMI_SAMPLE_RATE_32KHZ 0 #define HDMI_SAMPLE_RATE_44_1KHZ 1 #define HDMI_SAMPLE_RATE_48KHZ 2 @@ -222,11 +216,8 @@ static inline int msm_hdmi_pll_8998_init(struct platform_device *pdev) #define HDMI_SAMPLE_RATE_192KHZ 6 int msm_hdmi_audio_update(struct hdmi *hdmi); -int msm_hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, - uint32_t num_of_channels, uint32_t channel_allocation, - uint32_t level_shift, bool down_mix); -void msm_hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate); - +int msm_hdmi_audio_info_setup(struct hdmi *hdmi, int rate, int channels); +int msm_hdmi_audio_disable(struct hdmi *hdmi); /* * hdmi bridge: diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c index 4c2058c4adc1..0b91ed5eabd2 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c @@ -7,9 +7,6 @@ #include #include "hdmi.h" -/* maps MSM_HDMI_AUDIO_CHANNEL_n consts used by audio driver to # of channels: */ -static int nchannels[] = { 2, 4, 6, 8 }; - /* Supported HDMI Audio sample rates */ #define MSM_HDMI_SAMPLE_RATE_32KHZ 0 #define MSM_HDMI_SAMPLE_RATE_44_1KHZ 1 @@ -74,16 +71,17 @@ static const struct hdmi_msm_audio_arcs *get_arcs(unsigned long int pixclock) int msm_hdmi_audio_update(struct hdmi *hdmi) { struct hdmi_audio *audio = &hdmi->audio; - struct hdmi_audio_infoframe *info = &audio->infoframe; const struct hdmi_msm_audio_arcs *arcs = NULL; bool enabled = audio->enabled; uint32_t acr_pkt_ctrl, vbi_pkt_ctrl, aud_pkt_ctrl; - uint32_t infofrm_ctrl, audio_config; + uint32_t audio_config; + + if (!hdmi->connector->display_info.is_hdmi) + return -EINVAL; + + DBG("audio: enabled=%d, channels=%d, rate=%d", + audio->enabled, audio->channels, audio->rate); - DBG("audio: enabled=%d, channels=%d, channel_allocation=0x%x, " - "level_shift_value=%d, downmix_inhibit=%d, rate=%d", - audio->enabled, info->channels, info->channel_allocation, - info->level_shift_value, info->downmix_inhibit, audio->rate); DBG("video: power_on=%d, pixclock=%lu", hdmi->power_on, hdmi->pixclock); if (enabled && !(hdmi->power_on && hdmi->pixclock)) { @@ -104,7 +102,6 @@ int msm_hdmi_audio_update(struct hdmi *hdmi) acr_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_ACR_PKT_CTRL); vbi_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_VBI_PKT_CTRL); aud_pkt_ctrl = hdmi_read(hdmi, REG_HDMI_AUDIO_PKT_CTRL1); - infofrm_ctrl = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0); audio_config = hdmi_read(hdmi, REG_HDMI_AUDIO_CFG); /* Clear N/CTS selection bits */ @@ -113,7 +110,6 @@ int msm_hdmi_audio_update(struct hdmi *hdmi) if (enabled) { uint32_t n, cts, multiplier; enum hdmi_acr_cts select; - uint8_t buf[14]; n = arcs->lut[audio->rate].n; cts = arcs->lut[audio->rate].cts; @@ -155,20 +151,12 @@ int msm_hdmi_audio_update(struct hdmi *hdmi) HDMI_ACR_1_N(n)); hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL2, - COND(info->channels != 2, HDMI_AUDIO_PKT_CTRL2_LAYOUT) | + COND(audio->channels != 2, HDMI_AUDIO_PKT_CTRL2_LAYOUT) | HDMI_AUDIO_PKT_CTRL2_OVERRIDE); acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_CONT; acr_pkt_ctrl |= HDMI_ACR_PKT_CTRL_SEND; - /* configure infoframe: */ - hdmi_audio_infoframe_pack(info, buf, sizeof(buf)); - hdmi_write(hdmi, REG_HDMI_AUDIO_INFO0, - (buf[3] << 0) | (buf[4] << 8) | - (buf[5] << 16) | (buf[6] << 24)); - hdmi_write(hdmi, REG_HDMI_AUDIO_INFO1, - (buf[7] << 0) | (buf[8] << 8)); - hdmi_write(hdmi, REG_HDMI_GC, 0); vbi_pkt_ctrl |= HDMI_VBI_PKT_CTRL_GC_ENABLE; @@ -176,11 +164,6 @@ int msm_hdmi_audio_update(struct hdmi *hdmi) aud_pkt_ctrl |= HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND; - infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND; - infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT; - infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE; - infofrm_ctrl |= HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE; - audio_config &= ~HDMI_AUDIO_CFG_FIFO_WATERMARK__MASK; audio_config |= HDMI_AUDIO_CFG_FIFO_WATERMARK(4); audio_config |= HDMI_AUDIO_CFG_ENGINE_ENABLE; @@ -190,17 +173,12 @@ int msm_hdmi_audio_update(struct hdmi *hdmi) vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_ENABLE; vbi_pkt_ctrl &= ~HDMI_VBI_PKT_CTRL_GC_EVERY_FRAME; aud_pkt_ctrl &= ~HDMI_AUDIO_PKT_CTRL1_AUDIO_SAMPLE_SEND; - infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SEND; - infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_CONT; - infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE; - infofrm_ctrl &= ~HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE; audio_config &= ~HDMI_AUDIO_CFG_ENGINE_ENABLE; } hdmi_write(hdmi, REG_HDMI_ACR_PKT_CTRL, acr_pkt_ctrl); hdmi_write(hdmi, REG_HDMI_VBI_PKT_CTRL, vbi_pkt_ctrl); hdmi_write(hdmi, REG_HDMI_AUDIO_PKT_CTRL1, aud_pkt_ctrl); - hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, infofrm_ctrl); hdmi_write(hdmi, REG_HDMI_AUD_INT, COND(enabled, HDMI_AUD_INT_AUD_FIFO_URUN_INT) | @@ -214,41 +192,29 @@ int msm_hdmi_audio_update(struct hdmi *hdmi) return 0; } -int msm_hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, - uint32_t num_of_channels, uint32_t channel_allocation, - uint32_t level_shift, bool down_mix) +int msm_hdmi_audio_info_setup(struct hdmi *hdmi, int rate, int channels) { - struct hdmi_audio *audio; - if (!hdmi) return -ENXIO; - audio = &hdmi->audio; - - if (num_of_channels >= ARRAY_SIZE(nchannels)) + if ((rate < 0) || (rate >= MSM_HDMI_SAMPLE_RATE_MAX)) return -EINVAL; - audio->enabled = enabled; - audio->infoframe.channels = nchannels[num_of_channels]; - audio->infoframe.channel_allocation = channel_allocation; - audio->infoframe.level_shift_value = level_shift; - audio->infoframe.downmix_inhibit = down_mix; + hdmi->audio.rate = rate; + hdmi->audio.channels = channels; + hdmi->audio.enabled = true; return msm_hdmi_audio_update(hdmi); } -void msm_hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate) +int msm_hdmi_audio_disable(struct hdmi *hdmi) { - struct hdmi_audio *audio; - if (!hdmi) - return; - - audio = &hdmi->audio; + return -ENXIO; - if ((rate < 0) || (rate >= MSM_HDMI_SAMPLE_RATE_MAX)) - return; + hdmi->audio.rate = 0; + hdmi->audio.channels = 2; + hdmi->audio.enabled = false; - audio->rate = rate; - msm_hdmi_audio_update(hdmi); + return msm_hdmi_audio_update(hdmi); } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 64e9605f9e3b..f85eeeae0836 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "msm_kms.h" #include "hdmi.h" @@ -68,23 +70,17 @@ static void power_off(struct drm_bridge *bridge) #define AVI_IFRAME_LINE_NUMBER 1 -static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi) +static int msm_hdmi_config_avi_infoframe(struct hdmi *hdmi, + const u8 *buffer, size_t len) { - struct drm_crtc *crtc = hdmi->encoder->crtc; - const struct drm_display_mode *mode = &crtc->state->adjusted_mode; - union hdmi_infoframe frame; - u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; + u32 buf[4] = {}; u32 val; - int len; + int i; - drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, - hdmi->connector, mode); - - len = hdmi_infoframe_pack(&frame, buffer, sizeof(buffer)); - if (len < 0) { + if (len != HDMI_INFOFRAME_SIZE(AVI) || len - 3 > sizeof(buf)) { DRM_DEV_ERROR(&hdmi->pdev->dev, "failed to configure avi infoframe\n"); - return; + return -EINVAL; } /* @@ -93,37 +89,118 @@ static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi) * written to the LSB byte of AVI_INFO0 and the version is written to * the third byte from the LSB of AVI_INFO3 */ - hdmi_write(hdmi, REG_HDMI_AVI_INFO(0), + memcpy(buf, &buffer[3], len - 3); + + buf[3] |= buffer[1] << 24; + + 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_CTRL1); + val |= HDMI_INFOFRAME_CTRL0_AVI_SEND | + HDMI_INFOFRAME_CTRL0_AVI_CONT; + hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, val); + + 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); + + return 0; +} + +static int msm_hdmi_config_audio_infoframe(struct hdmi *hdmi, + const u8 *buffer, size_t len) +{ + u32 val; + + if (len != HDMI_INFOFRAME_SIZE(AUDIO)) { + DRM_DEV_ERROR(&hdmi->pdev->dev, + "failed to configure audio infoframe\n"); + return -EINVAL; + } + + hdmi_write(hdmi, REG_HDMI_AUDIO_INFO0, buffer[3] | buffer[4] << 8 | buffer[5] << 16 | buffer[6] << 24); - hdmi_write(hdmi, REG_HDMI_AVI_INFO(1), + hdmi_write(hdmi, REG_HDMI_AUDIO_INFO1, buffer[7] | buffer[8] << 8 | buffer[9] << 16 | buffer[10] << 24); - hdmi_write(hdmi, REG_HDMI_AVI_INFO(2), - buffer[11] | - buffer[12] << 8 | - buffer[13] << 16 | - buffer[14] << 24); + val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1); + 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); + + return 0; +} + +static int msm_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge, + enum hdmi_infoframe_type type) +{ + struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = hdmi_bridge->hdmi; + u32 val; + + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + 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_write(hdmi, REG_HDMI_AVI_INFO(3), - buffer[15] | - buffer[16] << 8 | - buffer[1] << 24); + 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_write(hdmi, REG_HDMI_INFOFRAME_CTRL0, - HDMI_INFOFRAME_CTRL0_AVI_SEND | - HDMI_INFOFRAME_CTRL0_AVI_CONT); + break; - 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); + case HDMI_INFOFRAME_TYPE_AUDIO: + 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_read(hdmi, REG_HDMI_INFOFRAME_CTRL1); + val &= ~HDMI_INFOFRAME_CTRL1_AUDIO_INFO_LINE__MASK; + hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val); + + break; + + default: + drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type); + } + + return 0; +} + +static int msm_hdmi_bridge_write_infoframe(struct drm_bridge *bridge, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len) +{ + struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = hdmi_bridge->hdmi; + + msm_hdmi_bridge_clear_infoframe(bridge, type); + + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + return msm_hdmi_config_avi_infoframe(hdmi, buffer, len); + case HDMI_INFOFRAME_TYPE_AUDIO: + return msm_hdmi_config_audio_infoframe(hdmi, buffer, len); + default: + drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type); + return 0; + } } static void msm_hdmi_set_timings(struct hdmi *hdmi, @@ -146,18 +223,20 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge, conn_state = drm_atomic_get_new_connector_state(state, connector); crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + hdmi->pixclock = conn_state->hdmi.tmds_char_rate; + msm_hdmi_set_timings(hdmi, &crtc_state->adjusted_mode); if (!hdmi->power_on) { msm_hdmi_phy_resource_enable(phy); msm_hdmi_power_on(bridge); hdmi->power_on = true; - if (hdmi->hdmi_mode) { - msm_hdmi_config_avi_infoframe(hdmi); + if (hdmi->hdmi_mode) msm_hdmi_audio_update(hdmi); - } } + drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); + msm_hdmi_phy_powerup(phy, hdmi->pixclock); msm_hdmi_set_mode(hdmi, true); @@ -196,8 +275,6 @@ static void msm_hdmi_set_timings(struct hdmi *hdmi, int hstart, hend, vstart, vend; uint32_t frame_ctrl; - hdmi->pixclock = mode->clock * 1000; - hstart = mode->htotal - mode->hsync_start; hend = mode->htotal - mode->hsync_start + mode->hdisplay; @@ -275,18 +352,16 @@ static const struct drm_edid *msm_hdmi_bridge_edid_read(struct drm_bridge *bridg return drm_edid; } -static enum drm_mode_status msm_hdmi_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_info *info, - const struct drm_display_mode *mode) +static enum drm_mode_status msm_hdmi_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge, + const struct drm_display_mode *mode, + unsigned long long tmds_rate) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; const struct hdmi_platform_config *config = hdmi->config; struct msm_drm_private *priv = bridge->dev->dev_private; struct msm_kms *kms = priv->kms; - long actual, requested; - - requested = 1000 * mode->clock; + long actual; /* for mdp5/apq8074, we manage our own pixel clk (as opposed to * mdp4/dtv stuff where pixel clk is assigned to mdp/encoder @@ -294,15 +369,16 @@ static enum drm_mode_status msm_hdmi_bridge_mode_valid(struct drm_bridge *bridge */ if (kms->funcs->round_pixclk) actual = kms->funcs->round_pixclk(kms, - requested, hdmi_bridge->hdmi->encoder); + tmds_rate, + hdmi_bridge->hdmi->encoder); else if (config->pwr_clk_cnt > 0) - actual = clk_round_rate(hdmi->pwr_clks[0], requested); + actual = clk_round_rate(hdmi->pwr_clks[0], tmds_rate); else - actual = requested; + actual = tmds_rate; - DBG("requested=%ld, actual=%ld", requested, actual); + DBG("requested=%lld, actual=%ld", tmds_rate, actual); - if (actual != requested) + if (actual != tmds_rate) return MODE_CLOCK_RANGE; return 0; @@ -314,9 +390,11 @@ static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset, .atomic_pre_enable = msm_hdmi_bridge_atomic_pre_enable, .atomic_post_disable = msm_hdmi_bridge_atomic_post_disable, - .mode_valid = msm_hdmi_bridge_mode_valid, .edid_read = msm_hdmi_bridge_edid_read, .detect = msm_hdmi_bridge_detect, + .hdmi_tmds_char_rate_valid = msm_hdmi_bridge_tmds_char_rate_valid, + .hdmi_clear_infoframe = msm_hdmi_bridge_clear_infoframe, + .hdmi_write_infoframe = msm_hdmi_bridge_write_infoframe, }; static void @@ -348,8 +426,11 @@ int msm_hdmi_bridge_init(struct hdmi *hdmi) bridge->funcs = &msm_hdmi_bridge_funcs; bridge->ddc = hdmi->i2c; bridge->type = DRM_MODE_CONNECTOR_HDMIA; + bridge->vendor = "Qualcomm"; + bridge->product = "Snapdragon"; bridge->ops = DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_DETECT | + DRM_BRIDGE_OP_HDMI | DRM_BRIDGE_OP_EDID; ret = devm_drm_bridge_add(hdmi->dev->dev, bridge); From d840a216211239b8846cb2a6cc58dac27380a41f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 26 Feb 2025 10:59:27 +0200 Subject: [PATCH 55/86] drm/msm/hdmi: get rid of hdmi_mode Use connector->display_info.is_hdmi instead of manually using drm_detect_hdmi_monitor(). Acked-by: Maxime Ripard Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/639657/ Link: https://lore.kernel.org/r/20250226-bridge-hdmi-connector-v8-4-340af24b35cc@linaro.org --- drivers/gpu/drm/msm/hdmi/hdmi.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi.h | 2 -- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 17 +++-------------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index b14205cb9e97..6b77e0fb8d5e 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -25,7 +25,7 @@ void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on) spin_lock_irqsave(&hdmi->reg_lock, flags); if (power_on) { ctrl |= HDMI_CTRL_ENABLE; - if (!hdmi->hdmi_mode) { + if (!hdmi->connector->display_info.is_hdmi) { ctrl |= HDMI_CTRL_HDMI; hdmi_write(hdmi, REG_HDMI_CTRL, ctrl); ctrl &= ~HDMI_CTRL_HDMI; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 8faad8440cf7..cdd3bd4f3783 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -67,8 +67,6 @@ struct hdmi { /* the encoder we are hooked to (outside of hdmi block) */ struct drm_encoder *encoder; - bool hdmi_mode; /* are we in hdmi mode? */ - int irq; struct workqueue_struct *workq; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index f85eeeae0836..96e860dc80fa 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -231,7 +231,7 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge, msm_hdmi_phy_resource_enable(phy); msm_hdmi_power_on(bridge); hdmi->power_on = true; - if (hdmi->hdmi_mode) + if (connector->display_info.is_hdmi) msm_hdmi_audio_update(hdmi); } @@ -263,7 +263,7 @@ static void msm_hdmi_bridge_atomic_post_disable(struct drm_bridge *bridge, if (hdmi->power_on) { power_off(bridge); hdmi->power_on = false; - if (hdmi->hdmi_mode) + if (hdmi->connector->display_info.is_hdmi) msm_hdmi_audio_update(hdmi); msm_hdmi_phy_resource_disable(phy); } @@ -319,7 +319,7 @@ static void msm_hdmi_set_timings(struct hdmi *hdmi, DBG("frame_ctrl=%08x", frame_ctrl); hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl); - if (hdmi->hdmi_mode) + if (hdmi->connector->display_info.is_hdmi) msm_hdmi_audio_update(hdmi); } @@ -338,17 +338,6 @@ static const struct drm_edid *msm_hdmi_bridge_edid_read(struct drm_bridge *bridg hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl); - if (drm_edid) { - /* - * FIXME: This should use connector->display_info.is_hdmi from a - * path that has read the EDID and called - * drm_edid_connector_update(). - */ - const struct edid *edid = drm_edid_raw(drm_edid); - - hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid); - } - return drm_edid; } From e92573638792f5d888348c042e87cdcf926eb4ac Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 26 Feb 2025 10:59:28 +0200 Subject: [PATCH 56/86] drm/msm/hdmi: update HDMI_GEN_PKT_CTRL_GENERIC0_UPDATE definition The GENERIC0_UPDATE field is a single bit. Redefine it as boolean to simplify its usage in the driver. Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/639660/ Link: https://lore.kernel.org/r/20250226-bridge-hdmi-connector-v8-5-340af24b35cc@linaro.org --- drivers/gpu/drm/msm/registers/display/hdmi.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/registers/display/hdmi.xml b/drivers/gpu/drm/msm/registers/display/hdmi.xml index 1cf1b14fbd91..0ebb96297dae 100644 --- a/drivers/gpu/drm/msm/registers/display/hdmi.xml +++ b/drivers/gpu/drm/msm/registers/display/hdmi.xml @@ -131,7 +131,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd"> --> - + From d7d57ecfcf5208747d2df39cf14e39526062cb6c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 26 Feb 2025 10:59:29 +0200 Subject: [PATCH 57/86] drm/msm/hdmi: also send the SPD and HDMI Vendor Specific InfoFrames Extend the driver to send SPD and HDMI Vendor Specific InfoFrames. While the HDMI block has special block to send HVS InfoFrame, use GENERIC0 block instead. VENSPEC_INFO registers pack frame data in a way that requires manual repacking in the driver, while GENERIC0 doesn't have such format requirements. The msm-4.4 kernel uses GENERIC0 to send HDR InfoFrame which we do not at this point anyway. Acked-by: Maxime Ripard Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/639661/ Link: https://lore.kernel.org/r/20250226-bridge-hdmi-connector-v8-6-340af24b35cc@linaro.org --- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 93 ++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 96e860dc80fa..9399613662ee 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -69,6 +69,8 @@ static void power_off(struct drm_bridge *bridge) } #define AVI_IFRAME_LINE_NUMBER 1 +#define SPD_IFRAME_LINE_NUMBER 1 +#define VENSPEC_IFRAME_LINE_NUMBER 3 static int msm_hdmi_config_avi_infoframe(struct hdmi *hdmi, const u8 *buffer, size_t len) @@ -142,6 +144,74 @@ static int msm_hdmi_config_audio_infoframe(struct hdmi *hdmi, return 0; } +static int msm_hdmi_config_spd_infoframe(struct hdmi *hdmi, + const u8 *buffer, size_t len) +{ + u32 buf[7] = {}; + u32 val; + int i; + + if (len != HDMI_INFOFRAME_SIZE(SPD) || len - 3 > sizeof(buf)) { + DRM_DEV_ERROR(&hdmi->pdev->dev, + "failed to configure SPD infoframe\n"); + return -EINVAL; + } + + /* checksum gets written together with the body of the frame */ + hdmi_write(hdmi, REG_HDMI_GENERIC1_HDR, + buffer[0] | + buffer[1] << 8 | + buffer[2] << 16); + + memcpy(buf, &buffer[3], len - 3); + + 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); + + return 0; +} + +static int msm_hdmi_config_hdmi_infoframe(struct hdmi *hdmi, + const u8 *buffer, size_t len) +{ + u32 buf[7] = {}; + u32 val; + int i; + + if (len < HDMI_INFOFRAME_HEADER_SIZE + HDMI_VENDOR_INFOFRAME_SIZE || + len - 3 > sizeof(buf)) { + DRM_DEV_ERROR(&hdmi->pdev->dev, + "failed to configure HDMI infoframe\n"); + return -EINVAL; + } + + /* checksum gets written together with the body of the frame */ + hdmi_write(hdmi, REG_HDMI_GENERIC0_HDR, + buffer[0] | + buffer[1] << 8 | + buffer[2] << 16); + + memcpy(buf, &buffer[3], len - 3); + + 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); + + return 0; +} + static int msm_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge, enum hdmi_infoframe_type type) { @@ -176,6 +246,25 @@ static int msm_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge, break; + case HDMI_INFOFRAME_TYPE_SPD: + 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); + + break; + + case HDMI_INFOFRAME_TYPE_VENDOR: + 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); + + break; + default: drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type); } @@ -197,6 +286,10 @@ static int msm_hdmi_bridge_write_infoframe(struct drm_bridge *bridge, return msm_hdmi_config_avi_infoframe(hdmi, buffer, len); case HDMI_INFOFRAME_TYPE_AUDIO: return msm_hdmi_config_audio_infoframe(hdmi, buffer, len); + case HDMI_INFOFRAME_TYPE_SPD: + return msm_hdmi_config_spd_infoframe(hdmi, buffer, len); + case HDMI_INFOFRAME_TYPE_VENDOR: + return msm_hdmi_config_hdmi_infoframe(hdmi, buffer, len); default: drm_dbg_driver(hdmi_bridge->base.dev, "Unsupported infoframe type %x\n", type); return 0; From ea54cfac0f8c3b452de67371920ba16a322a6555 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 26 Feb 2025 10:59:30 +0200 Subject: [PATCH 58/86] drm/msm/hdmi: use DRM HDMI Audio framework In order to simplify the driver even further and to remove the boilerplate code, rewrite the audio interface to use the DRM HDMI Audio framework. Audio InfoFames are controlled centrally via the DRM HDMI framework. Correct InfoFrame data is programmed at the atomic_pre_enable() time (if it was set before, drm_atomic_helper_connector_hdmi_update_infoframes() takes care of writing all InfoFrames, including the Audio one.) or during msm_hdmi_bridge_audio_prepare() when the new stream is started. All audio data frame management is deferred to msm_hdmi_bridge_audio_prepare() and msm_hdmi_bridge_audio_shutdown(). Reviewed-by: Maxime Ripard Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/639663/ Link: https://lore.kernel.org/r/20250226-bridge-hdmi-connector-v8-7-340af24b35cc@linaro.org --- drivers/gpu/drm/msm/hdmi/hdmi.c | 91 -------------------------- drivers/gpu/drm/msm/hdmi/hdmi.h | 18 +++-- drivers/gpu/drm/msm/hdmi/hdmi_audio.c | 68 ++++++++++++++++--- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 5 ++ 4 files changed, 71 insertions(+), 111 deletions(-) diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 6b77e0fb8d5e..248541ff4492 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -14,7 +14,6 @@ #include #include -#include #include "hdmi.h" void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on) @@ -245,87 +244,6 @@ static const struct hdmi_platform_config hdmi_tx_8974_config = { .hpd_freq = hpd_clk_freq_8x74, }; -/* - * HDMI audio codec callbacks - */ -static int msm_hdmi_audio_hw_params(struct device *dev, void *data, - struct hdmi_codec_daifmt *daifmt, - struct hdmi_codec_params *params) -{ - struct hdmi *hdmi = dev_get_drvdata(dev); - unsigned int rate; - int ret; - - DRM_DEV_DEBUG(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate, - params->sample_width, params->cea.channels); - - switch (params->sample_rate) { - case 32000: - rate = HDMI_SAMPLE_RATE_32KHZ; - break; - case 44100: - rate = HDMI_SAMPLE_RATE_44_1KHZ; - break; - case 48000: - rate = HDMI_SAMPLE_RATE_48KHZ; - break; - case 88200: - rate = HDMI_SAMPLE_RATE_88_2KHZ; - break; - case 96000: - rate = HDMI_SAMPLE_RATE_96KHZ; - break; - case 176400: - rate = HDMI_SAMPLE_RATE_176_4KHZ; - break; - case 192000: - rate = HDMI_SAMPLE_RATE_192KHZ; - break; - default: - DRM_DEV_ERROR(dev, "rate[%d] not supported!\n", - params->sample_rate); - return -EINVAL; - } - - ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(hdmi->connector, - ¶ms->cea); - if (ret) - return ret; - - msm_hdmi_audio_info_setup(hdmi, rate, params->cea.channels); - - return 0; -} - -static void msm_hdmi_audio_shutdown(struct device *dev, void *data) -{ - struct hdmi *hdmi = dev_get_drvdata(dev); - - drm_atomic_helper_connector_hdmi_clear_audio_infoframe(hdmi->connector); - msm_hdmi_audio_disable(hdmi); -} - -static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = { - .hw_params = msm_hdmi_audio_hw_params, - .audio_shutdown = msm_hdmi_audio_shutdown, -}; - -static struct hdmi_codec_pdata codec_data = { - .ops = &msm_hdmi_audio_codec_ops, - .max_i2s_channels = 8, - .i2s = 1, -}; - -static int msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device *dev) -{ - hdmi->audio_pdev = platform_device_register_data(dev, - HDMI_CODEC_DRV_NAME, - PLATFORM_DEVID_AUTO, - &codec_data, - sizeof(codec_data)); - return PTR_ERR_OR_ZERO(hdmi->audio_pdev); -} - static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) { struct msm_drm_private *priv = dev_get_drvdata(master); @@ -337,12 +255,6 @@ static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) return err; priv->hdmi = hdmi; - err = msm_hdmi_register_audio_driver(hdmi, dev); - if (err) { - DRM_ERROR("Failed to attach an audio codec %d\n", err); - hdmi->audio_pdev = NULL; - } - return 0; } @@ -352,9 +264,6 @@ static void msm_hdmi_unbind(struct device *dev, struct device *master, struct msm_drm_private *priv = dev_get_drvdata(master); if (priv->hdmi) { - if (priv->hdmi->audio_pdev) - platform_device_unregister(priv->hdmi->audio_pdev); - if (priv->hdmi->bridge) msm_hdmi_hpd_disable(priv->hdmi); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index cdd3bd4f3783..a5f481c39277 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -33,7 +33,6 @@ struct hdmi_hdcp_ctrl; struct hdmi { struct drm_device *dev; struct platform_device *pdev; - struct platform_device *audio_pdev; const struct hdmi_platform_config *config; @@ -205,17 +204,16 @@ static inline int msm_hdmi_pll_8998_init(struct platform_device *pdev) /* * audio: */ -#define HDMI_SAMPLE_RATE_32KHZ 0 -#define HDMI_SAMPLE_RATE_44_1KHZ 1 -#define HDMI_SAMPLE_RATE_48KHZ 2 -#define HDMI_SAMPLE_RATE_88_2KHZ 3 -#define HDMI_SAMPLE_RATE_96KHZ 4 -#define HDMI_SAMPLE_RATE_176_4KHZ 5 -#define HDMI_SAMPLE_RATE_192KHZ 6 +struct hdmi_codec_daifmt; +struct hdmi_codec_params; int msm_hdmi_audio_update(struct hdmi *hdmi); -int msm_hdmi_audio_info_setup(struct hdmi *hdmi, int rate, int channels); -int msm_hdmi_audio_disable(struct hdmi *hdmi); +int msm_hdmi_bridge_audio_prepare(struct drm_connector *connector, + struct drm_bridge *bridge, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params); +void msm_hdmi_bridge_audio_shutdown(struct drm_connector *connector, + struct drm_bridge *bridge); /* * hdmi bridge: diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c index 0b91ed5eabd2..8bb975e82c17 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c @@ -4,7 +4,12 @@ * Author: Rob Clark */ +#include + #include + +#include + #include "hdmi.h" /* Supported HDMI Audio sample rates */ @@ -192,29 +197,72 @@ int msm_hdmi_audio_update(struct hdmi *hdmi) return 0; } -int msm_hdmi_audio_info_setup(struct hdmi *hdmi, int rate, int channels) +int msm_hdmi_bridge_audio_prepare(struct drm_connector *connector, + struct drm_bridge *bridge, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) { - if (!hdmi) - return -ENXIO; - - if ((rate < 0) || (rate >= MSM_HDMI_SAMPLE_RATE_MAX)) + struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = hdmi_bridge->hdmi; + unsigned int rate; + int ret; + + drm_dbg_driver(bridge->dev, "%u Hz, %d bit, %d channels\n", + params->sample_rate, + params->sample_width, + params->cea.channels); + + switch (params->sample_rate) { + case 32000: + rate = MSM_HDMI_SAMPLE_RATE_32KHZ; + break; + case 44100: + rate = MSM_HDMI_SAMPLE_RATE_44_1KHZ; + break; + case 48000: + rate = MSM_HDMI_SAMPLE_RATE_48KHZ; + break; + case 88200: + rate = MSM_HDMI_SAMPLE_RATE_88_2KHZ; + break; + case 96000: + rate = MSM_HDMI_SAMPLE_RATE_96KHZ; + break; + case 176400: + rate = MSM_HDMI_SAMPLE_RATE_176_4KHZ; + break; + case 192000: + rate = MSM_HDMI_SAMPLE_RATE_192KHZ; + break; + default: + drm_err(bridge->dev, "rate[%d] not supported!\n", + params->sample_rate); return -EINVAL; + } + + ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector, + ¶ms->cea); + if (ret) + return ret; hdmi->audio.rate = rate; - hdmi->audio.channels = channels; + hdmi->audio.channels = params->cea.channels; hdmi->audio.enabled = true; return msm_hdmi_audio_update(hdmi); } -int msm_hdmi_audio_disable(struct hdmi *hdmi) +void msm_hdmi_bridge_audio_shutdown(struct drm_connector *connector, + struct drm_bridge *bridge) { - if (!hdmi) - return -ENXIO; + struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = hdmi_bridge->hdmi; + + drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector); hdmi->audio.rate = 0; hdmi->audio.channels = 2; hdmi->audio.enabled = false; - return msm_hdmi_audio_update(hdmi); + msm_hdmi_audio_update(hdmi); } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 9399613662ee..1456354c8af4 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -477,6 +477,8 @@ static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = { .hdmi_tmds_char_rate_valid = msm_hdmi_bridge_tmds_char_rate_valid, .hdmi_clear_infoframe = msm_hdmi_bridge_clear_infoframe, .hdmi_write_infoframe = msm_hdmi_bridge_write_infoframe, + .hdmi_audio_prepare = msm_hdmi_bridge_audio_prepare, + .hdmi_audio_shutdown = msm_hdmi_bridge_audio_shutdown, }; static void @@ -514,6 +516,9 @@ int msm_hdmi_bridge_init(struct hdmi *hdmi) DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_HDMI | DRM_BRIDGE_OP_EDID; + bridge->hdmi_audio_max_i2s_playback_channels = 8; + bridge->hdmi_audio_dev = &hdmi->pdev->dev; + bridge->hdmi_audio_dai_port = -1; ret = devm_drm_bridge_add(hdmi->dev->dev, bridge); if (ret) From f9d1b528219beea3f42cc75504541611e1b8ca83 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Feb 2025 10:30:26 +0300 Subject: [PATCH 59/86] drm/msm/dpu: fix error pointer dereference in msm_kms_init_aspace() If msm_gem_address_space_create() fails, then return right away. Otherwise it leads to a Oops when we dereference "aspace" on the next line. Fixes: eabba31a839a ("drm/msm: register a fault handler for display mmu faults") Signed-off-by: Dan Carpenter Reviewed-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/639357/ Link: https://lore.kernel.org/r/3221e88c-3351-42e6-aeb1-69f4f014b509@stanley.mountain [DB: fixed commit id] Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/msm_kms.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c index b877278888e6..35d5397e73b4 100644 --- a/drivers/gpu/drm/msm/msm_kms.c +++ b/drivers/gpu/drm/msm/msm_kms.c @@ -209,6 +209,7 @@ struct msm_gem_address_space *msm_kms_init_aspace(struct drm_device *dev) if (IS_ERR(aspace)) { dev_err(mdp_dev, "aspace create, error %pe\n", aspace); mmu->funcs->destroy(mmu); + return aspace; } msm_mmu_set_fault_handler(aspace->mmu, kms, msm_kms_fault_handler); From d3169ce5251b5325a02b8e7bedc0f9ea0515c32b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 21 Feb 2025 16:13:11 +0100 Subject: [PATCH 60/86] dt-bindings: display/msm: qcom, sa8775p-mdss: Add missing eDP phy The Qualcomm SA8775p MDSS display block comes with eDP phy, already used in DTS and already documented in phy/qcom,edp-phy.yaml binding. Add the missing device node in the binding and extend example to silence dtbs_check warnings like: sa8775p-ride.dtb: display-subsystem@ae00000: Unevaluated properties are not allowed ('phy@aec2a00', 'phy@aec5a00' were unexpected) Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring (Arm) Patchwork: https://patchwork.freedesktop.org/patch/638744/ Link: https://lore.kernel.org/r/20250221151311.138755-1-krzysztof.kozlowski@linaro.org Signed-off-by: Dmitry Baryshkov --- .../display/msm/qcom,sa8775p-mdss.yaml | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sa8775p-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sa8775p-mdss.yaml index a90a8b3f1a9e..5fac3e266703 100644 --- a/Documentation/devicetree/bindings/display/msm/qcom,sa8775p-mdss.yaml +++ b/Documentation/devicetree/bindings/display/msm/qcom,sa8775p-mdss.yaml @@ -52,6 +52,13 @@ patternProperties: items: - const: qcom,sa8775p-dp + "^phy@[0-9a-f]+$": + type: object + additionalProperties: true + properties: + compatible: + const: qcom,sa8775p-edp-phy + required: - compatible @@ -61,6 +68,7 @@ examples: - | #include #include + #include #include #include #include @@ -158,6 +166,26 @@ examples: }; }; + mdss0_dp0_phy: phy@aec2a00 { + compatible = "qcom,sa8775p-edp-phy"; + + reg = <0x0aec2a00 0x200>, + <0x0aec2200 0xd0>, + <0x0aec2600 0xd0>, + <0x0aec2000 0x1c8>; + + clocks = <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_AUX_CLK>, + <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>; + clock-names = "aux", + "cfg_ahb"; + + #clock-cells = <1>; + #phy-cells = <0>; + + vdda-phy-supply = <&vreg_l1c>; + vdda-pll-supply = <&vreg_l4a>; + }; + displayport-controller@af54000 { compatible = "qcom,sa8775p-dp"; @@ -186,9 +214,9 @@ examples: assigned-clocks = <&dispcc_mdss_dptx0_link_clk_src>, <&dispcc_mdss_dptx0_pixel0_clk_src>; - assigned-clock-parents = <&mdss0_edp_phy 0>, <&mdss0_edp_phy 1>; + assigned-clock-parents = <&mdss0_dp0_phy 0>, <&mdss0_dp0_phy 1>; - phys = <&mdss0_edp_phy>; + phys = <&mdss0_dp0_phy>; phy-names = "dp"; operating-points-v2 = <&dp_opp_table>; From 51bc064005c5a0a904270e4e8b4bfefdd19c1fef Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 20 Feb 2025 05:59:22 +0200 Subject: [PATCH 61/86] drm/msm/dpu: rename CDM block definition The CDM block is not limited to SC7280, but it is common to all platforms since DPU 5.x. Rename it from sc7280_cdm to dpu_cdm_5_x. Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/638405/ Link: https://lore.kernel.org/r/20250220-dpu-add-cdm-v2-1-77f5f0df3d9a@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h | 2 +- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h | 2 +- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h | 2 +- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h index e8916ae826a6..47e01c3c242f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_0_sm8250.h @@ -386,7 +386,7 @@ const struct dpu_mdss_cfg dpu_sm8250_cfg = { .mdss_ver = &sm8250_mdss_ver, .caps = &sm8250_dpu_caps, .mdp = &sm8250_mdp, - .cdm = &sc7280_cdm, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm8250_ctl), .ctl = sm8250_ctl, .sspp_count = ARRAY_SIZE(sm8250_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h index 2f153e0b5c6a..e9625c48c567 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_2_sc7280.h @@ -248,7 +248,7 @@ const struct dpu_mdss_cfg dpu_sc7280_cfg = { .mdss_ver = &sc7280_mdss_ver, .caps = &sc7280_dpu_caps, .mdp = &sc7280_mdp, - .cdm = &sc7280_cdm, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sc7280_ctl), .ctl = sc7280_ctl, .sspp_count = ARRAY_SIZE(sc7280_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h index 76ec72a32378..4d96ce71746f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_4_sa8775p.h @@ -458,7 +458,7 @@ const struct dpu_mdss_cfg dpu_sa8775p_cfg = { .mdss_ver = &sa8775p_mdss_ver, .caps = &sa8775p_dpu_caps, .mdp = &sa8775p_mdp, - .cdm = &sc7280_cdm, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sa8775p_ctl), .ctl = sa8775p_ctl, .sspp_count = ARRAY_SIZE(sa8775p_sspp), 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 0b342c043875..ec7f42a334fc 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -507,7 +507,7 @@ static const struct dpu_dsc_sub_blks dsc_sblk_1 = { /************************************************************* * CDM block config *************************************************************/ -static const struct dpu_cdm_cfg sc7280_cdm = { +static const struct dpu_cdm_cfg dpu_cdm_5_x = { .name = "cdm_0", .id = CDM_0, .len = 0x228, From 15f2825defebaee4490881a23981d03b51604b6a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 20 Feb 2025 05:59:23 +0200 Subject: [PATCH 62/86] drm/msm/dpu: enable CDM_0 for DPUs 5.x+ Enable the CDM_0 block on DPU generations starting from 5.x as documented in the vendor dtsi file. Signed-off-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/638407/ Link: https://lore.kernel.org/r/20250220-dpu-add-cdm-v2-2-77f5f0df3d9a@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h | 1 + 11 files changed, 11 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h index bcb39807fe61..85fde7243dd4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h @@ -452,6 +452,7 @@ const struct dpu_mdss_cfg dpu_sm8650_cfg = { .mdss_ver = &sm8650_mdss_ver, .caps = &sm8650_dpu_caps, .mdp = &sm8650_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm8650_ctl), .ctl = sm8650_ctl, .sspp_count = ARRAY_SIZE(sm8650_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h index 36cc9dbc00b5..23188290001f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h @@ -388,6 +388,7 @@ const struct dpu_mdss_cfg dpu_sm8150_cfg = { .mdss_ver = &sm8150_mdss_ver, .caps = &sm8150_dpu_caps, .mdp = &sm8150_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm8150_ctl), .ctl = sm8150_ctl, .sspp_count = ARRAY_SIZE(sm8150_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h index e8eacdb47967..de8ccf589f1f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h @@ -414,6 +414,7 @@ const struct dpu_mdss_cfg dpu_sc8180x_cfg = { .mdss_ver = &sc8180x_mdss_ver, .caps = &sc8180x_dpu_caps, .mdp = &sc8180x_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sc8180x_ctl), .ctl = sc8180x_ctl, .sspp_count = ARRAY_SIZE(sc8180x_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h index 2fe674d1e059..b2ebf76e3867 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h @@ -309,6 +309,7 @@ const struct dpu_mdss_cfg dpu_sm7150_cfg = { .mdss_ver = &sm7150_mdss_ver, .caps = &sm7150_dpu_caps, .mdp = &sm7150_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm7150_ctl), .ctl = sm7150_ctl, .sspp_count = ARRAY_SIZE(sm7150_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h index 744012892b7c..2b4aea177bca 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h @@ -248,6 +248,7 @@ const struct dpu_mdss_cfg dpu_sm6150_cfg = { .mdss_ver = &sm6150_mdss_ver, .caps = &sm6150_dpu_caps, .mdp = &sm6150_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm6150_ctl), .ctl = sm6150_ctl, .sspp_count = ARRAY_SIZE(sm6150_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h index 76f60a2df7a8..cc2951112bda 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h @@ -216,6 +216,7 @@ const struct dpu_mdss_cfg dpu_sm6125_cfg = { .mdss_ver = &sm6125_mdss_ver, .caps = &sm6125_dpu_caps, .mdp = &sm6125_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm6125_ctl), .ctl = sm6125_ctl, .sspp_count = ARRAY_SIZE(sm6125_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h index 7382ebb6e5b2..42a00550eefb 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h @@ -204,6 +204,7 @@ const struct dpu_mdss_cfg dpu_sc7180_cfg = { .mdss_ver = &sc7180_mdss_ver, .caps = &sc7180_dpu_caps, .mdp = &sc7180_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sc7180_ctl), .ctl = sc7180_ctl, .sspp_count = ARRAY_SIZE(sc7180_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h index 0502cee2f116..828a02429405 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h @@ -222,6 +222,7 @@ const struct dpu_mdss_cfg dpu_sm6350_cfg = { .mdss_ver = &sm6350_mdss_ver, .caps = &sm6350_dpu_caps, .mdp = &sm6350_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm6350_ctl), .ctl = sm6350_ctl, .sspp_count = ARRAY_SIZE(sm6350_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h index f7c08e89c882..795e9ebf8c11 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h @@ -396,6 +396,7 @@ const struct dpu_mdss_cfg dpu_sm8350_cfg = { .mdss_ver = &sm8350_mdss_ver, .caps = &sm8350_dpu_caps, .mdp = &sm8350_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm8350_ctl), .ctl = sm8350_ctl, .sspp_count = ARRAY_SIZE(sm8350_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h index 08742472f9cc..048dfb9dbb60 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h @@ -412,6 +412,7 @@ const struct dpu_mdss_cfg dpu_sm8450_cfg = { .mdss_ver = &sm8450_mdss_ver, .caps = &sm8450_dpu_caps, .mdp = &sm8450_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm8450_ctl), .ctl = sm8450_ctl, .sspp_count = ARRAY_SIZE(sm8450_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h index 4d3787fceb72..a5b90e5e3120 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h @@ -407,6 +407,7 @@ const struct dpu_mdss_cfg dpu_sm8550_cfg = { .mdss_ver = &sm8550_mdss_ver, .caps = &sm8550_dpu_caps, .mdp = &sm8550_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sm8550_ctl), .ctl = sm8550_ctl, .sspp_count = ARRAY_SIZE(sm8550_sspp), From d5c1e00ea2de178d0b0f57ff19758335caa20904 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 20 Feb 2025 05:59:24 +0200 Subject: [PATCH 63/86] drm/msm/dpu: enable CDM_0 for DPUs 1.x - 4.x Enable the CDM_0 block on DPU versions 1.x - 4.x as documented in the vendor dtsi file. Signed-off-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/638408/ Link: https://lore.kernel.org/r/20250220-dpu-add-cdm-v2-3-77f5f0df3d9a@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h | 1 + drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h | 1 + drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 7 +++++++ 10 files changed, 16 insertions(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h index ab3dfb0b374e..1f32807bb5e5 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_14_msm8937.h @@ -190,6 +190,7 @@ const struct dpu_mdss_cfg dpu_msm8937_cfg = { .mdss_ver = &msm8937_mdss_ver, .caps = &msm8937_dpu_caps, .mdp = msm8937_mdp, + .cdm = &dpu_cdm_1_x_4_x, .ctl_count = ARRAY_SIZE(msm8937_ctl), .ctl = msm8937_ctl, .sspp_count = ARRAY_SIZE(msm8937_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h index 6bdaecca6761..42131959ff22 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_15_msm8917.h @@ -167,6 +167,7 @@ const struct dpu_mdss_cfg dpu_msm8917_cfg = { .mdss_ver = &msm8917_mdss_ver, .caps = &msm8917_dpu_caps, .mdp = msm8917_mdp, + .cdm = &dpu_cdm_1_x_4_x, .ctl_count = ARRAY_SIZE(msm8917_ctl), .ctl = msm8917_ctl, .sspp_count = ARRAY_SIZE(msm8917_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h index 14f36ea6ad0e..2b4723a5c676 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_16_msm8953.h @@ -198,6 +198,7 @@ const struct dpu_mdss_cfg dpu_msm8953_cfg = { .mdss_ver = &msm8953_mdss_ver, .caps = &msm8953_dpu_caps, .mdp = msm8953_mdp, + .cdm = &dpu_cdm_1_x_4_x, .ctl_count = ARRAY_SIZE(msm8953_ctl), .ctl = msm8953_ctl, .sspp_count = ARRAY_SIZE(msm8953_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h index 491f6f5827d1..5cf19de71f06 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_1_7_msm8996.h @@ -316,6 +316,7 @@ const struct dpu_mdss_cfg dpu_msm8996_cfg = { .mdss_ver = &msm8996_mdss_ver, .caps = &msm8996_dpu_caps, .mdp = msm8996_mdp, + .cdm = &dpu_cdm_1_x_4_x, .ctl_count = ARRAY_SIZE(msm8996_ctl), .ctl = msm8996_ctl, .sspp_count = ARRAY_SIZE(msm8996_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h index 64c94e919a69..746474679ef5 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_0_msm8998.h @@ -302,6 +302,7 @@ const struct dpu_mdss_cfg dpu_msm8998_cfg = { .mdss_ver = &msm8998_mdss_ver, .caps = &msm8998_dpu_caps, .mdp = &msm8998_mdp, + .cdm = &dpu_cdm_1_x_4_x, .ctl_count = ARRAY_SIZE(msm8998_ctl), .ctl = msm8998_ctl, .sspp_count = ARRAY_SIZE(msm8998_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h index 424815e7fb7d..4f2f68b07f20 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_2_sdm660.h @@ -269,6 +269,7 @@ const struct dpu_mdss_cfg dpu_sdm660_cfg = { .mdss_ver = &sdm660_mdss_ver, .caps = &sdm660_dpu_caps, .mdp = &sdm660_mdp, + .cdm = &dpu_cdm_1_x_4_x, .ctl_count = ARRAY_SIZE(sdm660_ctl), .ctl = sdm660_ctl, .sspp_count = ARRAY_SIZE(sdm660_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h index df01227fc364..c70bef025ac4 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_3_3_sdm630.h @@ -205,6 +205,7 @@ const struct dpu_mdss_cfg dpu_sdm630_cfg = { .mdss_ver = &sdm630_mdss_ver, .caps = &sdm630_dpu_caps, .mdp = &sdm630_mdp, + .cdm = &dpu_cdm_1_x_4_x, .ctl_count = ARRAY_SIZE(sdm630_ctl), .ctl = sdm630_ctl, .sspp_count = ARRAY_SIZE(sdm630_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h index 72bd4f7e9e50..ab7b4822ca63 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_0_sdm845.h @@ -319,6 +319,7 @@ const struct dpu_mdss_cfg dpu_sdm845_cfg = { .mdss_ver = &sdm845_mdss_ver, .caps = &sdm845_dpu_caps, .mdp = &sdm845_mdp, + .cdm = &dpu_cdm_1_x_4_x, .ctl_count = ARRAY_SIZE(sdm845_ctl), .ctl = sdm845_ctl, .sspp_count = ARRAY_SIZE(sdm845_sspp), diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h index daef07924886..c2fde980fb52 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_4_1_sdm670.h @@ -132,6 +132,7 @@ const struct dpu_mdss_cfg dpu_sdm670_cfg = { .mdss_ver = &sdm670_mdss_ver, .caps = &sdm845_dpu_caps, .mdp = &sdm670_mdp, + .cdm = &dpu_cdm_1_x_4_x, .ctl_count = ARRAY_SIZE(sdm845_ctl), .ctl = sdm845_ctl, .sspp_count = ARRAY_SIZE(sdm670_sspp), 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 ec7f42a334fc..a6bb46b201e9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -507,6 +507,13 @@ static const struct dpu_dsc_sub_blks dsc_sblk_1 = { /************************************************************* * CDM block config *************************************************************/ +static const struct dpu_cdm_cfg dpu_cdm_1_x_4_x = { + .name = "cdm_0", + .id = CDM_0, + .len = 0x224, + .base = 0x79200, +}; + static const struct dpu_cdm_cfg dpu_cdm_5_x = { .name = "cdm_0", .id = CDM_0, From b5bbf4fab0a9830a42153cb635410036c8655577 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 20 Feb 2025 05:59:25 +0200 Subject: [PATCH 64/86] drm/msm/dpu: enable CDM_0 for SC8280XP platform Enable CDM on the SC8280XP platform, allowing RGB to YUV conversion for the output. Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/638411/ Link: https://lore.kernel.org/r/20250220-dpu-add-cdm-v2-4-77f5f0df3d9a@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h index 0d143e390eca..fcee1c3665f8 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_0_sc8280xp.h @@ -435,6 +435,7 @@ const struct dpu_mdss_cfg dpu_sc8280xp_cfg = { .mdss_ver = &sc8280xp_mdss_ver, .caps = &sc8280xp_dpu_caps, .mdp = &sc8280xp_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(sc8280xp_ctl), .ctl = sc8280xp_ctl, .sspp_count = ARRAY_SIZE(sc8280xp_sspp), From 989a24211eafde7b5e1e20d6f3fad10b96f9cf0b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 20 Feb 2025 05:59:26 +0200 Subject: [PATCH 65/86] drm/msm/dpu: enable CDM_0 for X Elite platform Enable CDM on the X Elite platform, allowing RGB to YUV conversion for the output. Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/638412/ Link: https://lore.kernel.org/r/20250220-dpu-add-cdm-v2-5-77f5f0df3d9a@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h index 6b112e3d17da..8977fa48926b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h @@ -453,6 +453,7 @@ const struct dpu_mdss_cfg dpu_x1e80100_cfg = { .mdss_ver = &x1e80100_mdss_ver, .caps = &x1e80100_dpu_caps, .mdp = &x1e80100_mdp, + .cdm = &dpu_cdm_5_x, .ctl_count = ARRAY_SIZE(x1e80100_ctl), .ctl = x1e80100_ctl, .sspp_count = ARRAY_SIZE(x1e80100_sspp), From b9aedd32a81792f5b4b7716815aba375ebaff526 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Feb 2025 05:21:11 +0200 Subject: [PATCH 66/86] drm/msm/dpu: extract bandwidth aggregation function In preparation to refactoring the dpu_core_perf debugfs interface, extract the bandwidth aggregation function from _dpu_core_perf_crtc_update_bus(). Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/636058/ Link: https://lore.kernel.org/r/20250209-dpu-perf-rework-v5-1-87e936cf3004@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) 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 6f0a37f954fe..c7ac1140e79d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -210,36 +210,41 @@ int dpu_core_perf_crtc_check(struct drm_crtc *crtc, return 0; } -static int _dpu_core_perf_crtc_update_bus(struct dpu_kms *kms, - struct drm_crtc *crtc) +static void dpu_core_perf_aggregate(struct drm_device *ddev, + enum dpu_crtc_client_type curr_client_type, + struct dpu_core_perf_params *perf) { - struct dpu_core_perf_params perf = { 0 }; - enum dpu_crtc_client_type curr_client_type - = dpu_crtc_get_client_type(crtc); - struct drm_crtc *tmp_crtc; struct dpu_crtc_state *dpu_cstate; - int i, ret = 0; - u64 avg_bw; - - if (!kms->num_paths) - return 0; + struct drm_crtc *tmp_crtc; - drm_for_each_crtc(tmp_crtc, crtc->dev) { + drm_for_each_crtc(tmp_crtc, ddev) { if (tmp_crtc->enabled && - curr_client_type == - dpu_crtc_get_client_type(tmp_crtc)) { + curr_client_type == dpu_crtc_get_client_type(tmp_crtc)) { dpu_cstate = to_dpu_crtc_state(tmp_crtc->state); - perf.max_per_pipe_ib = max(perf.max_per_pipe_ib, - dpu_cstate->new_perf.max_per_pipe_ib); + perf->max_per_pipe_ib = max(perf->max_per_pipe_ib, + dpu_cstate->new_perf.max_per_pipe_ib); - perf.bw_ctl += dpu_cstate->new_perf.bw_ctl; + perf->bw_ctl += dpu_cstate->new_perf.bw_ctl; - DRM_DEBUG_ATOMIC("crtc=%d bw=%llu paths:%d\n", - tmp_crtc->base.id, - dpu_cstate->new_perf.bw_ctl, kms->num_paths); + DRM_DEBUG_ATOMIC("crtc=%d bw=%llu\n", + tmp_crtc->base.id, + dpu_cstate->new_perf.bw_ctl); } } +} + +static int _dpu_core_perf_crtc_update_bus(struct dpu_kms *kms, + struct drm_crtc *crtc) +{ + struct dpu_core_perf_params perf = { 0 }; + int i, ret = 0; + u64 avg_bw; + + if (!kms->num_paths) + return 0; + + dpu_core_perf_aggregate(crtc->dev, dpu_crtc_get_client_type(crtc), &perf); avg_bw = perf.bw_ctl; do_div(avg_bw, (kms->num_paths * 1000)); /*Bps_to_icc*/ From 795aef6f365394c6a505bd88cdc92804dae5402b Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Feb 2025 05:21:12 +0200 Subject: [PATCH 67/86] drm/msm/dpu: remove duplicate code calculating sum of bandwidths The code in dpu_core_perf_crtc_check() mostly duplicates code in dpu_core_perf_aggregate(). Remove the duplication by reusing the latter function. Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/636059/ Link: https://lore.kernel.org/r/20250209-dpu-perf-rework-v5-2-87e936cf3004@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 94 ++++++++----------- 1 file changed, 38 insertions(+), 56 deletions(-) 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 c7ac1140e79d..f0d490afb53b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -140,6 +140,30 @@ static void _dpu_core_perf_calc_crtc(const struct dpu_core_perf *core_perf, perf->max_per_pipe_ib, perf->bw_ctl); } +static void dpu_core_perf_aggregate(struct drm_device *ddev, + enum dpu_crtc_client_type curr_client_type, + struct dpu_core_perf_params *perf) +{ + struct dpu_crtc_state *dpu_cstate; + struct drm_crtc *tmp_crtc; + + drm_for_each_crtc(tmp_crtc, ddev) { + if (tmp_crtc->enabled && + curr_client_type == dpu_crtc_get_client_type(tmp_crtc)) { + dpu_cstate = to_dpu_crtc_state(tmp_crtc->state); + + perf->max_per_pipe_ib = max(perf->max_per_pipe_ib, + dpu_cstate->new_perf.max_per_pipe_ib); + + perf->bw_ctl += dpu_cstate->new_perf.bw_ctl; + + DRM_DEBUG_ATOMIC("crtc=%d bw=%llu\n", + tmp_crtc->base.id, + dpu_cstate->new_perf.bw_ctl); + } + } +} + /** * dpu_core_perf_crtc_check - validate performance of the given crtc state * @crtc: Pointer to crtc @@ -150,11 +174,9 @@ int dpu_core_perf_crtc_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { u32 bw, threshold; - u64 bw_sum_of_intfs = 0; - enum dpu_crtc_client_type curr_client_type; struct dpu_crtc_state *dpu_cstate; - struct drm_crtc *tmp_crtc; struct dpu_kms *kms; + struct dpu_core_perf_params perf; if (!crtc || !state) { DPU_ERROR("invalid crtc\n"); @@ -172,68 +194,28 @@ int dpu_core_perf_crtc_check(struct drm_crtc *crtc, /* obtain new values */ _dpu_core_perf_calc_crtc(&kms->perf, crtc, state, &dpu_cstate->new_perf); - bw_sum_of_intfs = dpu_cstate->new_perf.bw_ctl; - curr_client_type = dpu_crtc_get_client_type(crtc); - - drm_for_each_crtc(tmp_crtc, crtc->dev) { - if (tmp_crtc->enabled && - dpu_crtc_get_client_type(tmp_crtc) == curr_client_type && - tmp_crtc != crtc) { - struct dpu_crtc_state *tmp_cstate = - to_dpu_crtc_state(tmp_crtc->state); - - DRM_DEBUG_ATOMIC("crtc:%d bw:%llu ctrl:%d\n", - tmp_crtc->base.id, tmp_cstate->new_perf.bw_ctl, - tmp_cstate->bw_control); - - bw_sum_of_intfs += tmp_cstate->new_perf.bw_ctl; - } + dpu_core_perf_aggregate(crtc->dev, dpu_crtc_get_client_type(crtc), &perf); - /* convert bandwidth to kb */ - bw = DIV_ROUND_UP_ULL(bw_sum_of_intfs, 1000); - DRM_DEBUG_ATOMIC("calculated bandwidth=%uk\n", bw); + /* convert bandwidth to kb */ + bw = DIV_ROUND_UP_ULL(perf.bw_ctl, 1000); + DRM_DEBUG_ATOMIC("calculated bandwidth=%uk\n", bw); - threshold = kms->perf.perf_cfg->max_bw_high; + threshold = kms->perf.perf_cfg->max_bw_high; - DRM_DEBUG_ATOMIC("final threshold bw limit = %d\n", threshold); + DRM_DEBUG_ATOMIC("final threshold bw limit = %d\n", threshold); - if (!threshold) { - DPU_ERROR("no bandwidth limits specified\n"); - return -E2BIG; - } else if (bw > threshold) { - DPU_ERROR("exceeds bandwidth: %ukb > %ukb\n", bw, - threshold); - return -E2BIG; - } + if (!threshold) { + DPU_ERROR("no bandwidth limits specified\n"); + return -E2BIG; + } else if (bw > threshold) { + DPU_ERROR("exceeds bandwidth: %ukb > %ukb\n", bw, + threshold); + return -E2BIG; } return 0; } -static void dpu_core_perf_aggregate(struct drm_device *ddev, - enum dpu_crtc_client_type curr_client_type, - struct dpu_core_perf_params *perf) -{ - struct dpu_crtc_state *dpu_cstate; - struct drm_crtc *tmp_crtc; - - drm_for_each_crtc(tmp_crtc, ddev) { - if (tmp_crtc->enabled && - curr_client_type == dpu_crtc_get_client_type(tmp_crtc)) { - dpu_cstate = to_dpu_crtc_state(tmp_crtc->state); - - perf->max_per_pipe_ib = max(perf->max_per_pipe_ib, - dpu_cstate->new_perf.max_per_pipe_ib); - - perf->bw_ctl += dpu_cstate->new_perf.bw_ctl; - - DRM_DEBUG_ATOMIC("crtc=%d bw=%llu\n", - tmp_crtc->base.id, - dpu_cstate->new_perf.bw_ctl); - } - } -} - static int _dpu_core_perf_crtc_update_bus(struct dpu_kms *kms, struct drm_crtc *crtc) { From c54b61102f468cef440423a57341a95763a287a3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Feb 2025 05:21:13 +0200 Subject: [PATCH 68/86] drm/msm/dpu: change ib values to u32 The IB values in core_perf calculations (max_per_pipe_ib, fix_core_ib_vote) are expressed in KBps and are passed to icc_set_bw without additional division. Change type of those values to u32. Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/636061/ Link: https://lore.kernel.org/r/20250209-dpu-perf-rework-v5-3-87e936cf3004@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h | 6 +++--- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) 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 f0d490afb53b..7263ab63a692 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -135,7 +135,7 @@ static void _dpu_core_perf_calc_crtc(const struct dpu_core_perf *core_perf, } DRM_DEBUG_ATOMIC( - "crtc=%d clk_rate=%llu core_ib=%llu core_ab=%llu\n", + "crtc=%d clk_rate=%llu core_ib=%u core_ab=%llu\n", crtc->base.id, perf->core_clk_rate, perf->max_per_pipe_ib, perf->bw_ctl); } @@ -477,7 +477,7 @@ int dpu_core_perf_debugfs_init(struct dpu_kms *dpu_kms, struct dentry *parent) (u32 *)perf, &dpu_core_perf_mode_fops); debugfs_create_u64("fix_core_clk_rate", 0600, entry, &perf->fix_core_clk_rate); - debugfs_create_u64("fix_core_ib_vote", 0600, entry, + debugfs_create_u32("fix_core_ib_vote", 0600, entry, &perf->fix_core_ib_vote); debugfs_create_u64("fix_core_ab_vote", 0600, entry, &perf->fix_core_ab_vote); diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h index 451bf8021114..e2ab7b3a8246 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h @@ -19,7 +19,7 @@ * @core_clk_rate: core clock rate request */ struct dpu_core_perf_params { - u64 max_per_pipe_ib; + u32 max_per_pipe_ib; u64 bw_ctl; u64 core_clk_rate; }; @@ -40,7 +40,7 @@ struct dpu_core_perf_tune { * @perf_tune: debug control for performance tuning * @enable_bw_release: debug control for bandwidth release * @fix_core_clk_rate: fixed core clock request in Hz used in mode 2 - * @fix_core_ib_vote: fixed core ib vote in bps used in mode 2 + * @fix_core_ib_vote: fixed core ib vote in KBps used in mode 2 * @fix_core_ab_vote: fixed core ab vote in bps used in mode 2 */ struct dpu_core_perf { @@ -50,7 +50,7 @@ struct dpu_core_perf { struct dpu_core_perf_tune perf_tune; u32 enable_bw_release; u64 fix_core_clk_rate; - u64 fix_core_ib_vote; + u32 fix_core_ib_vote; u64 fix_core_ab_vote; }; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index b8e1a8f2aad5..6e3e9162222a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1680,7 +1680,7 @@ static int dpu_crtc_debugfs_state_show(struct seq_file *s, void *v) seq_printf(s, "core_clk_rate: %llu\n", dpu_crtc->cur_perf.core_clk_rate); seq_printf(s, "bw_ctl: %llu\n", dpu_crtc->cur_perf.bw_ctl); - seq_printf(s, "max_per_pipe_ib: %llu\n", + seq_printf(s, "max_per_pipe_ib: %u\n", dpu_crtc->cur_perf.max_per_pipe_ib); return 0; From 879a7f8c8c2d727bc4c8ae3238f0dbf52eada6db Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Feb 2025 05:21:14 +0200 Subject: [PATCH 69/86] drm/msm/dpu: make fix_core_ab_vote consistent with fix_core_ib_vote The fix_core_ab_vote is an average bandwidth value, used for bandwidth overrides in several cases. However there is an internal inconsistency: fix_core_ib_vote is defined in KBps, while fix_core_ab_vote is defined in Bps. Fix that by changing the type of the variable to u32 and using * 1000ULL multiplier when setting up the dpu_core_perf_params::bw_ctl value. Signed-off-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/636064/ Link: https://lore.kernel.org/r/20250209-dpu-perf-rework-v5-4-87e936cf3004@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 7263ab63a692..7cabc8f26908 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -125,7 +125,7 @@ static void _dpu_core_perf_calc_crtc(const struct dpu_core_perf *core_perf, perf->max_per_pipe_ib = 0; perf->core_clk_rate = 0; } else if (core_perf->perf_tune.mode == DPU_PERF_MODE_FIXED) { - perf->bw_ctl = core_perf->fix_core_ab_vote; + perf->bw_ctl = core_perf->fix_core_ab_vote * 1000ULL; perf->max_per_pipe_ib = core_perf->fix_core_ib_vote; perf->core_clk_rate = core_perf->fix_core_clk_rate; } else { @@ -479,7 +479,7 @@ int dpu_core_perf_debugfs_init(struct dpu_kms *dpu_kms, struct dentry *parent) &perf->fix_core_clk_rate); debugfs_create_u32("fix_core_ib_vote", 0600, entry, &perf->fix_core_ib_vote); - debugfs_create_u64("fix_core_ab_vote", 0600, entry, + debugfs_create_u32("fix_core_ab_vote", 0600, entry, &perf->fix_core_ab_vote); return 0; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h index e2ab7b3a8246..d2f21d34e501 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.h @@ -41,7 +41,7 @@ struct dpu_core_perf_tune { * @enable_bw_release: debug control for bandwidth release * @fix_core_clk_rate: fixed core clock request in Hz used in mode 2 * @fix_core_ib_vote: fixed core ib vote in KBps used in mode 2 - * @fix_core_ab_vote: fixed core ab vote in bps used in mode 2 + * @fix_core_ab_vote: fixed core ab vote in KBps used in mode 2 */ struct dpu_core_perf { const struct dpu_perf_cfg *perf_cfg; @@ -51,7 +51,7 @@ struct dpu_core_perf { u32 enable_bw_release; u64 fix_core_clk_rate; u32 fix_core_ib_vote; - u64 fix_core_ab_vote; + u32 fix_core_ab_vote; }; int dpu_core_perf_crtc_check(struct drm_crtc *crtc, From f6c782ea7de5a12549e55e0fc1cc452486189252 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Feb 2025 05:21:15 +0200 Subject: [PATCH 70/86] drm/msm/dpu: also use KBps for bw_ctl output Change debugfs and log entries to use KBps / u32 for bw_ctl and similar data. Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/636066/ Link: https://lore.kernel.org/r/20250209-dpu-perf-rework-v5-5-87e936cf3004@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 5 +++-- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) 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 7cabc8f26908..b93f7556f187 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -135,9 +135,10 @@ static void _dpu_core_perf_calc_crtc(const struct dpu_core_perf *core_perf, } DRM_DEBUG_ATOMIC( - "crtc=%d clk_rate=%llu core_ib=%u core_ab=%llu\n", + "crtc=%d clk_rate=%llu core_ib=%u core_ab=%u\n", crtc->base.id, perf->core_clk_rate, - perf->max_per_pipe_ib, perf->bw_ctl); + perf->max_per_pipe_ib, + (u32)DIV_ROUND_UP_ULL(perf->bw_ctl, 1000)); } static void dpu_core_perf_aggregate(struct drm_device *ddev, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 6e3e9162222a..b0a062d6fa3b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1679,7 +1679,8 @@ static int dpu_crtc_debugfs_state_show(struct seq_file *s, void *v) seq_printf(s, "intf_mode: %d\n", dpu_crtc_get_intf_mode(crtc)); seq_printf(s, "core_clk_rate: %llu\n", dpu_crtc->cur_perf.core_clk_rate); - seq_printf(s, "bw_ctl: %llu\n", dpu_crtc->cur_perf.bw_ctl); + seq_printf(s, "bw_ctl: %uk\n", + (u32)DIV_ROUND_UP_ULL(dpu_crtc->cur_perf.bw_ctl, 1000)); seq_printf(s, "max_per_pipe_ib: %u\n", dpu_crtc->cur_perf.max_per_pipe_ib); From 40fc01273f07f4939473bd8f45b2aeb2dd4e21d6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Feb 2025 05:21:16 +0200 Subject: [PATCH 71/86] drm/msm/dpu: rename average bandwidth-related debugfs files Rename the debugfs files to match their purpose and the patter provided by other bandwidth and clock-related files: threshold_high -> max_core_ab threshold_low -> low_core_ab Reviewed-by: Abhinav Kumar Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/636069/ Link: https://lore.kernel.org/r/20250209-dpu-perf-rework-v5-6-87e936cf3004@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b93f7556f187..70f43e8359ca 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -464,9 +464,9 @@ int dpu_core_perf_debugfs_init(struct dpu_kms *dpu_kms, struct dentry *parent) &perf->core_clk_rate); debugfs_create_u32("enable_bw_release", 0600, entry, (u32 *)&perf->enable_bw_release); - debugfs_create_u32("threshold_low", 0400, entry, + debugfs_create_u32("low_core_ab", 0400, entry, (u32 *)&perf->perf_cfg->max_bw_low); - debugfs_create_u32("threshold_high", 0400, entry, + debugfs_create_u32("max_core_ab", 0400, entry, (u32 *)&perf->perf_cfg->max_bw_high); debugfs_create_u32("min_core_ib", 0400, entry, (u32 *)&perf->perf_cfg->min_core_ib); From e7e2495595e01839b117e9c08500ff80cd218f6a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Feb 2025 05:21:17 +0200 Subject: [PATCH 72/86] drm/msm/dpu: drop core_clk_rate overrides from _dpu_core_perf_calc_crtc core_clk_rate override is handled in _dpu_core_perf_get_core_clk_rate(). Drop imperfect duplicating code from _dpu_core_perf_calc_crtc(). Signed-off-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/636070/ Link: https://lore.kernel.org/r/20250209-dpu-perf-rework-v5-7-87e936cf3004@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 70f43e8359ca..c8e20be08aa8 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -123,16 +123,14 @@ static void _dpu_core_perf_calc_crtc(const struct dpu_core_perf *core_perf, if (core_perf->perf_tune.mode == DPU_PERF_MODE_MINIMUM) { perf->bw_ctl = 0; perf->max_per_pipe_ib = 0; - perf->core_clk_rate = 0; } else if (core_perf->perf_tune.mode == DPU_PERF_MODE_FIXED) { perf->bw_ctl = core_perf->fix_core_ab_vote * 1000ULL; perf->max_per_pipe_ib = core_perf->fix_core_ib_vote; - perf->core_clk_rate = core_perf->fix_core_clk_rate; } else { perf->bw_ctl = _dpu_core_perf_calc_bw(perf_cfg, crtc); perf->max_per_pipe_ib = perf_cfg->min_dram_ib; - perf->core_clk_rate = _dpu_core_perf_calc_clk(perf_cfg, crtc, state); } + perf->core_clk_rate = _dpu_core_perf_calc_clk(perf_cfg, crtc, state); DRM_DEBUG_ATOMIC( "crtc=%d clk_rate=%llu core_ib=%u core_ab=%u\n", From 6d3175a72cc07e90f81fb35841048a8a9b5134cb Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 9 Feb 2025 05:21:18 +0200 Subject: [PATCH 73/86] drm/msm/dpu: handle perf mode in _dpu_core_perf_crtc_update_bus() Move perf mode handling for the bandwidth to _dpu_core_perf_crtc_update_bus() rather than overriding per-CRTC data and then aggregating known values. Note, this changes the fix_core_ab_vote. Previously it would be multiplied per the CRTC number, now it will be used directly for interconnect voting. This better reflects user requirements in the case of different resolutions being set on different CRTCs: instead of using the same bandwidth for each CRTC (which is incorrect) user can now calculate overall bandwidth required by all outputs and use that value. Note #2: this also disables threshold checks for user-entered bandwidth values. First of all, it doesn't make sense to fail atomic commits because of the debugfs input. Compositors have no way to correlate failing commits with debugfs settings. Second, it makes sense to allow users to go beyond these values and check whether this makes any difference or fixes the issue. Signed-off-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/636072/ Link: https://lore.kernel.org/r/20250209-dpu-perf-rework-v5-8-87e936cf3004@linaro.org --- drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) 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 c8e20be08aa8..7ff3405c6867 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -118,20 +118,9 @@ static void _dpu_core_perf_calc_crtc(const struct dpu_core_perf *core_perf, return; } - memset(perf, 0, sizeof(struct dpu_core_perf_params)); - - if (core_perf->perf_tune.mode == DPU_PERF_MODE_MINIMUM) { - perf->bw_ctl = 0; - perf->max_per_pipe_ib = 0; - } else if (core_perf->perf_tune.mode == DPU_PERF_MODE_FIXED) { - perf->bw_ctl = core_perf->fix_core_ab_vote * 1000ULL; - perf->max_per_pipe_ib = core_perf->fix_core_ib_vote; - } else { - perf->bw_ctl = _dpu_core_perf_calc_bw(perf_cfg, crtc); - perf->max_per_pipe_ib = perf_cfg->min_dram_ib; - } + perf->bw_ctl = _dpu_core_perf_calc_bw(perf_cfg, crtc); + perf->max_per_pipe_ib = perf_cfg->min_dram_ib; perf->core_clk_rate = _dpu_core_perf_calc_clk(perf_cfg, crtc, state); - DRM_DEBUG_ATOMIC( "crtc=%d clk_rate=%llu core_ib=%u core_ab=%u\n", crtc->base.id, perf->core_clk_rate, @@ -220,18 +209,29 @@ static int _dpu_core_perf_crtc_update_bus(struct dpu_kms *kms, { struct dpu_core_perf_params perf = { 0 }; int i, ret = 0; - u64 avg_bw; + u32 avg_bw; + u32 peak_bw; if (!kms->num_paths) return 0; - dpu_core_perf_aggregate(crtc->dev, dpu_crtc_get_client_type(crtc), &perf); + if (kms->perf.perf_tune.mode == DPU_PERF_MODE_MINIMUM) { + avg_bw = 0; + peak_bw = 0; + } else if (kms->perf.perf_tune.mode == DPU_PERF_MODE_FIXED) { + avg_bw = kms->perf.fix_core_ab_vote; + peak_bw = kms->perf.fix_core_ib_vote; + } else { + dpu_core_perf_aggregate(crtc->dev, dpu_crtc_get_client_type(crtc), &perf); + + avg_bw = div_u64(perf.bw_ctl, 1000); /*Bps_to_icc*/ + peak_bw = perf.max_per_pipe_ib; + } - avg_bw = perf.bw_ctl; - do_div(avg_bw, (kms->num_paths * 1000)); /*Bps_to_icc*/ + avg_bw /= kms->num_paths; for (i = 0; i < kms->num_paths; i++) - icc_set_bw(kms->path[i], avg_bw, perf.max_per_pipe_ib); + icc_set_bw(kms->path[i], avg_bw, peak_bw); return ret; } From 3045b3b0bb645a4db929ec72b96343f1b0728723 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 6 Mar 2025 00:44:36 +0200 Subject: [PATCH 74/86] MAINTAINERS: use kernel.org alias My Linaro email will stop working soon. Use @kernel.org email instead. Signed-off-by: Dmitry Baryshkov Acked-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/641152/ Signed-off-by: Rob Clark --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 9f29ec77a654..b056f518e533 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7380,7 +7380,7 @@ F: include/uapi/drm/msm_drm.h DRM DRIVER for Qualcomm display hardware M: Rob Clark M: Abhinav Kumar -M: Dmitry Baryshkov +M: Dmitry Baryshkov R: Sean Paul R: Marijn Suijten L: linux-arm-msm@vger.kernel.org From 60065f260a2289816e5c6e3e8b9b4e3d5fce11cf Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 6 Mar 2025 00:44:37 +0200 Subject: [PATCH 75/86] mailmap: remap all addresses to kernel.org alias Remap all historical and non-historical entries to my kernel.org email. Signed-off-by: Dmitry Baryshkov Acked-by: Abhinav Kumar Patchwork: https://patchwork.freedesktop.org/patch/641154/ Signed-off-by: Rob Clark --- .mailmap | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.mailmap b/.mailmap index ffb3a7e5e088..e37d457b338c 100644 --- a/.mailmap +++ b/.mailmap @@ -200,10 +200,11 @@ Dengcheng Zhu Dengcheng Zhu Dikshita Agarwal -Dmitry Baryshkov -Dmitry Baryshkov <[dbaryshkov@gmail.com]> -Dmitry Baryshkov -Dmitry Baryshkov +Dmitry Baryshkov +Dmitry Baryshkov <[dbaryshkov@gmail.com]> +Dmitry Baryshkov +Dmitry Baryshkov +Dmitry Baryshkov Dmitry Safonov <0x7f454c46@gmail.com> Dmitry Safonov <0x7f454c46@gmail.com> Dmitry Safonov <0x7f454c46@gmail.com> From 06dd5d86c6aef1c7609ca3a5ffa4097e475e2213 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 28 Feb 2025 13:31:24 -0800 Subject: [PATCH 76/86] drm/msm/a6xx: Fix a6xx indexed-regs in devcoreduump Somehow, possibly as a result of rebase gone badly, setting nr_indexed_regs for pre-a650 a6xx devices lost the setting of nr_indexed_regs, resulting in values getting snapshot, but omitted from the devcoredump. Fixes: e997ae5f45ca ("drm/msm/a6xx: Mostly implement A7xx gpu_state") Signed-off-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/640289/ --- drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c index 3222a406d089..341a72a67401 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c @@ -1514,6 +1514,8 @@ static void a6xx_get_indexed_registers(struct msm_gpu *gpu, /* Restore the size in the hardware */ gpu_write(gpu, REG_A6XX_CP_MEM_POOL_SIZE, mempool_size); + + a6xx_state->nr_indexed_regs = count; } static void a7xx_get_indexed_registers(struct msm_gpu *gpu, From e79751f69b3c1ab42c6ae8c3b9e3bf05d1bdb49b Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Wed, 5 Mar 2025 19:46:44 -0800 Subject: [PATCH 77/86] drm/msm/dpu: Clear perf params before calculating bw To prevent incorrect BW calculation, zero out dpu_core_perf_params before it is passed into dpu_core_perf_aggregate(). Signed-off-by: Jessica Zhang Fixes: 795aef6f3653 ("drm/msm/dpu: remove duplicate code calculating sum of bandwidths") Reviewed-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/641278/ Signed-off-by: Rob Clark --- 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 7ff3405c6867..0fb5789c60d0 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_core_perf.c @@ -164,7 +164,7 @@ int dpu_core_perf_crtc_check(struct drm_crtc *crtc, u32 bw, threshold; struct dpu_crtc_state *dpu_cstate; struct dpu_kms *kms; - struct dpu_core_perf_params perf; + struct dpu_core_perf_params perf = { 0 }; if (!crtc || !state) { DPU_ERROR("invalid crtc\n"); From 69d02730431e4fa214354225b3c88d5b19ef8efa Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Wed, 5 Mar 2025 19:15:05 -0800 Subject: [PATCH 78/86] drm/msm/dpu: Support YUV formats on writeback for DPU 5.x+ Now that CDM_0 has been enabled for DPU 5.x+, add support for YUV formats on writeback Signed-off-by: Jessica Zhang Reviewed-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/641270/ Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h | 4 ++-- drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h index 85fde7243dd4..6ac97c378056 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_10_0_sm8650.h @@ -343,8 +343,8 @@ static const struct dpu_wb_cfg sm8650_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, .vbif_idx = VBIF_RT, .maxlinewidth = 4096, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h index 23188290001f..979527d98fbc 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h @@ -298,8 +298,8 @@ static const struct dpu_wb_cfg sm8150_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, .vbif_idx = VBIF_RT, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h index de8ccf589f1f..d76b8992a6c1 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h @@ -305,8 +305,8 @@ static const struct dpu_wb_cfg sc8180x_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, .vbif_idx = VBIF_RT, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h index b2ebf76e3867..83db11339b29 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_2_sm7150.h @@ -261,8 +261,8 @@ static const struct dpu_wb_cfg sm7150_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, .vbif_idx = VBIF_RT, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h index 2b4aea177bca..da11830d4407 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_3_sm6150.h @@ -168,8 +168,8 @@ static const struct dpu_wb_cfg sm6150_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, .vbif_idx = VBIF_RT, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h index cc2951112bda..d3d3a34d0b45 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_4_sm6125.h @@ -145,8 +145,8 @@ static const struct dpu_wb_cfg sm6125_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, .vbif_idx = VBIF_RT, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h index 42a00550eefb..040c94c0bb66 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_2_sc7180.h @@ -157,8 +157,8 @@ static const struct dpu_wb_cfg sc7180_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, .vbif_idx = VBIF_RT, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h index 828a02429405..397278ba999b 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_6_4_sm6350.h @@ -151,8 +151,8 @@ static const struct dpu_wb_cfg sm6350_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, .vbif_idx = VBIF_RT, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h index 795e9ebf8c11..0c860e804cab 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_7_0_sm8350.h @@ -305,8 +305,8 @@ static const struct dpu_wb_cfg sm8350_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, .vbif_idx = VBIF_RT, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h index 048dfb9dbb60..19b2ee8bbd5f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h @@ -321,8 +321,8 @@ static const struct dpu_wb_cfg sm8450_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .clk_ctrl = DPU_CLK_CTRL_WB2, .xin_id = 6, .vbif_idx = VBIF_RT, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h index a5b90e5e3120..24f988465bf6 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_0_sm8550.h @@ -317,8 +317,8 @@ static const struct dpu_wb_cfg sm8550_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, .vbif_idx = VBIF_RT, .maxlinewidth = 4096, diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h index 8977fa48926b..6417baa84f82 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h +++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_9_2_x1e80100.h @@ -317,8 +317,8 @@ static const struct dpu_wb_cfg x1e80100_wb[] = { .name = "wb_2", .id = WB_2, .base = 0x65000, .len = 0x2c8, .features = WB_SM8250_MASK, - .format_list = wb2_formats_rgb, - .num_formats = ARRAY_SIZE(wb2_formats_rgb), + .format_list = wb2_formats_rgb_yuv, + .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv), .xin_id = 6, .vbif_idx = VBIF_RT, .maxlinewidth = 4096, From 1cf5cd92a20b4b47354092b869b7056edd83b8ff Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Wed, 5 Mar 2025 19:16:51 -0800 Subject: [PATCH 79/86] drm/msm/dpu: Adjust CDM_MUX to support CWB PINGPONG Similar to WB_MUX, CDM_MUX also needs to be adjusted to support dedicated CWB PINGPONGs Signed-off-by: Jessica Zhang Reviewed-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/641272/ Link: https://lore.kernel.org/r/20250305-cdm-cwb-mux-fix-v1-1-16148ca6e4d2@quicinc.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cdm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cdm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cdm.c index ae1534c49ae0..3f88c3641d4a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cdm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_cdm.c @@ -214,7 +214,9 @@ static void dpu_hw_cdm_bind_pingpong_blk(struct dpu_hw_cdm *ctx, const enum dpu_ mux_cfg = DPU_REG_READ(c, CDM_MUX); mux_cfg &= ~0xf; - if (pp) + if (pp >= PINGPONG_CWB_0) + mux_cfg |= 0xd; + else if (pp) mux_cfg |= (pp - PINGPONG_0) & 0x7; else mux_cfg |= 0xf; From 2060a2db1253b6f841fe91a0c49cb7765197f3c6 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Fri, 7 Mar 2025 09:50:30 +0800 Subject: [PATCH 80/86] drm/msm/dpu: Remove duplicate dpu_hw_cwb.h header ./drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c: dpu_hw_cwb.h is included more than once. Reported-by: Abaci Robot Closes: https://bugzilla.openanolis.cn/show_bug.cgi?id=19239 Signed-off-by: Jiapeng Chong Reported-by: kernel test robot Reviewed-by: Dmitry Baryshkov Fixes: dd331404ac7c ("drm/msm/dpu: Configure CWB in writeback encoder") Patchwork: https://patchwork.freedesktop.org/patch/641543/ Link: https://lore.kernel.org/r/20250307015030.86282-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 0eed93a4d056..4d2589140426 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -29,7 +29,6 @@ #include "dpu_hw_dsc.h" #include "dpu_hw_merge3d.h" #include "dpu_hw_cdm.h" -#include "dpu_hw_cwb.h" #include "dpu_formats.h" #include "dpu_encoder_phys.h" #include "dpu_crtc.h" From 096775c3dcf3051414f0965e9ab34572f1fc2aef Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 6 Mar 2025 10:22:29 +0200 Subject: [PATCH 81/86] drm/msm/dpu: correct dpu_crtc_check_mode_changed docs Correct commit 20972609d12c ("drm/msm/dpu: Require modeset if clone mode status changes") and describe old_crtc_state and new_crtc_state params instead of the single previously used parameter crtc_state. Fixes: 20972609d12c ("drm/msm/dpu: Require modeset if clone mode status changes") Signed-off-by: Dmitry Baryshkov Reviewed-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/641313/ Link: https://lore.kernel.org/r/20250306-dpu-fix-docs-v1-1-e51b71e8ad84@kernel.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index b0a062d6fa3b..536d15818ba2 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1395,7 +1395,8 @@ static int dpu_crtc_assign_resources(struct drm_crtc *crtc, /** * dpu_crtc_check_mode_changed: check if full modeset is required - * @crtc_state: Corresponding CRTC state to be checked + * @old_crtc_state: Previous CRTC state + * @new_crtc_state: Corresponding CRTC state to be checked * * Check if the changes in the object properties demand full mode set. */ From 2ee664833000609213114c4bd7efba489cef5f71 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 6 Mar 2025 10:22:30 +0200 Subject: [PATCH 82/86] drm/msm/dpu: correct struct dpu_encoder_virt docs Fix a typo in struct dpu_encoder_virt kerneldoc, which made it ignore description of the cwb_mask field. Fixes: dd331404ac7c ("drm/msm/dpu: Configure CWB in writeback encoder") Signed-off-by: Dmitry Baryshkov Reviewed-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/641315/ Link: https://lore.kernel.org/r/20250306-dpu-fix-docs-v1-2-e51b71e8ad84@kernel.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c index 4d2589140426..284e69bb47c1 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c @@ -139,7 +139,7 @@ enum dpu_enc_rc_states { * num_phys_encs. * @hw_dsc: Handle to the DSC blocks used for the display. * @dsc_mask: Bitmask of used DSC blocks. - * @cwb_mask Bitmask of used CWB muxes + * @cwb_mask: Bitmask of used CWB muxes * @intfs_swapped: Whether or not the phys_enc interfaces have been swapped * for partial update right-only cases, such as pingpong * split where virtual pingpong does not generate IRQs From 1b9d131a098ebbf8dc73573cfaa39fb122b7a6a5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 7 Mar 2025 12:29:39 +0300 Subject: [PATCH 83/86] drm/msm/dpu: Fix uninitialized variable in dpu_crtc_kickoff_clone_mode() After the loop there is a check for whether "wb_encoder" has been set to non-NULL, however it was never set to NULL. Initialize it to NULL. Fixes: ad06972d5365 ("drm/msm/dpu: Reorder encoder kickoff for CWB") Signed-off-by: Dan Carpenter Reviewed-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/641631/ Link: https://lore.kernel.org/r/f8ba03dc-0f90-4781-8d54-c16b3251ecb1@stanley.mountain Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index 536d15818ba2..0714936d8835 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -956,7 +956,7 @@ static int _dpu_crtc_wait_for_frame_done(struct drm_crtc *crtc) static int dpu_crtc_kickoff_clone_mode(struct drm_crtc *crtc) { struct drm_encoder *encoder; - struct drm_encoder *rt_encoder = NULL, *wb_encoder; + struct drm_encoder *rt_encoder = NULL, *wb_encoder = NULL; struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc); /* Find encoder for real time display */ From 375f1dec6957c6b967fbe9912fb3e0e1f60f6c9f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 8 Mar 2025 02:02:21 +0200 Subject: [PATCH 84/86] drm/msm/dpu: drop wb2_formats_rgb After enabling YUV support for writeback on a variety of DPU hardware, the wb2_formats_rgb is now unused. Drop it following the report of LKP. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202503071857.oZbQsPaE-lkp@intel.com/ Reviewed-by: Rob Clark # on IRC Patchwork: https://patchwork.freedesktop.org/patch/641848/ Link: https://lore.kernel.org/r/20250308-dpu-drop-wb2-rgb-v1-1-f5503fcd1bc2@linaro.org Signed-off-by: Dmitry Baryshkov --- .../gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c | 31 ------------------- 1 file changed, 31 deletions(-) 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 a6bb46b201e9..64265ca4656a 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -232,37 +232,6 @@ static const u32 rotation_v2_formats[] = { /* TODO add formats after validation */ }; -static const u32 wb2_formats_rgb[] = { - DRM_FORMAT_RGB565, - DRM_FORMAT_BGR565, - DRM_FORMAT_RGB888, - DRM_FORMAT_ARGB8888, - DRM_FORMAT_RGBA8888, - DRM_FORMAT_ABGR8888, - DRM_FORMAT_XRGB8888, - DRM_FORMAT_RGBX8888, - DRM_FORMAT_XBGR8888, - DRM_FORMAT_ARGB1555, - DRM_FORMAT_RGBA5551, - DRM_FORMAT_XRGB1555, - DRM_FORMAT_RGBX5551, - DRM_FORMAT_ARGB4444, - DRM_FORMAT_RGBA4444, - DRM_FORMAT_RGBX4444, - DRM_FORMAT_XRGB4444, - DRM_FORMAT_BGR888, - DRM_FORMAT_BGRA8888, - DRM_FORMAT_BGRX8888, - DRM_FORMAT_ABGR1555, - DRM_FORMAT_BGRA5551, - DRM_FORMAT_XBGR1555, - DRM_FORMAT_BGRX5551, - DRM_FORMAT_ABGR4444, - DRM_FORMAT_BGRA4444, - DRM_FORMAT_BGRX4444, - DRM_FORMAT_XBGR4444, -}; - static const u32 wb2_formats_rgb_yuv[] = { DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, From 0edf7b1f31901d915b5a91e9f52e887f14804e8b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 21 Feb 2025 16:24:11 +0100 Subject: [PATCH 85/86] dt-bindings: display/msm: dsi-controller-main: Combine if:then: entries Several devices have the same clock inputs, thus they can be in the same if:then: clause, making everything smaller. No functional impact. Reviewed-by: Rob Herring (Arm) Signed-off-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/638749/ Link: https://lore.kernel.org/r/20250221-b4-sm8750-display-v3-1-3ea95b1630ea@linaro.org Signed-off-by: Dmitry Baryshkov --- .../display/msm/dsi-controller-main.yaml | 64 ++----------------- 1 file changed, 5 insertions(+), 59 deletions(-) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml index ffbd1dc9470e..e496e5430918 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml @@ -248,24 +248,6 @@ allOf: contains: enum: - qcom,msm8916-dsi-ctrl - then: - properties: - clocks: - maxItems: 6 - clock-names: - items: - - const: mdp_core - - const: iface - - const: bus - - const: byte - - const: pixel - - const: core - - - if: - properties: - compatible: - contains: - enum: - qcom,msm8953-dsi-ctrl - qcom,msm8976-dsi-ctrl then: @@ -328,28 +310,13 @@ allOf: contains: enum: - qcom,msm8998-dsi-ctrl - - qcom,sm6125-dsi-ctrl - - qcom,sm6350-dsi-ctrl - then: - properties: - clocks: - maxItems: 6 - clock-names: - items: - - const: byte - - const: byte_intf - - const: pixel - - const: core - - const: iface - - const: bus - - - if: - properties: - compatible: - contains: - enum: - qcom,sc7180-dsi-ctrl - qcom,sc7280-dsi-ctrl + - qcom,sdm845-dsi-ctrl + - qcom,sm6115-dsi-ctrl + - qcom,sm6125-dsi-ctrl + - qcom,sm6350-dsi-ctrl + - qcom,sm6375-dsi-ctrl - qcom,sm6150-dsi-ctrl - qcom,sm7150-dsi-ctrl - qcom,sm8150-dsi-ctrl @@ -393,27 +360,6 @@ allOf: - const: pixel - const: core - - if: - properties: - compatible: - contains: - enum: - - qcom,sdm845-dsi-ctrl - - qcom,sm6115-dsi-ctrl - - qcom,sm6375-dsi-ctrl - then: - properties: - clocks: - maxItems: 6 - clock-names: - items: - - const: byte - - const: byte_intf - - const: pixel - - const: core - - const: iface - - const: bus - unevaluatedProperties: false examples: From 83ee6d2ec52f1378f3473ee8657d559bebdbda44 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 21 Feb 2025 16:24:12 +0100 Subject: [PATCH 86/86] dt-bindings: display/msm: dsi-controller-main: Add missing minItems Specific constrain in if:then: blocks for variable lists, like clocks and clock-names, should have a fixed upper and lower size. Older dtschema implied minItems, but that's not true since 2024 and missing minItems means that lower bound is not set. Reviewed-by: Rob Herring (Arm) Signed-off-by: Krzysztof Kozlowski Patchwork: https://patchwork.freedesktop.org/patch/638751/ Link: https://lore.kernel.org/r/20250221-b4-sm8750-display-v3-2-3ea95b1630ea@linaro.org Signed-off-by: Dmitry Baryshkov --- .../bindings/display/msm/dsi-controller-main.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml index e496e5430918..2aab33cd0017 100644 --- a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml +++ b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml @@ -231,6 +231,7 @@ allOf: then: properties: clocks: + minItems: 7 maxItems: 7 clock-names: items: @@ -253,6 +254,7 @@ allOf: then: properties: clocks: + minItems: 6 maxItems: 6 clock-names: items: @@ -273,6 +275,7 @@ allOf: then: properties: clocks: + minItems: 7 maxItems: 7 clock-names: items: @@ -293,6 +296,7 @@ allOf: then: properties: clocks: + minItems: 7 maxItems: 7 clock-names: items: @@ -328,6 +332,7 @@ allOf: then: properties: clocks: + minItems: 6 maxItems: 6 clock-names: items: @@ -347,6 +352,7 @@ allOf: then: properties: clocks: + minItems: 9 maxItems: 9 clock-names: items: