diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 89d82be9d6..ee9b92062a 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -1184,6 +1184,24 @@ int sde_connector_state_get_mode_info(struct drm_connector_state *conn_state, int sde_connector_get_lm_cnt_from_topology(struct drm_connector *conn, const struct drm_display_mode *drm_mode); +/** + * sde_conn_get_max_mode_width - retrieves the maximum width from all modes + * conn: Pointer to DRM connector object + */ +static inline u32 sde_conn_get_max_mode_width(struct drm_connector *conn) +{ + u32 maxw = 0; + struct drm_display_mode *mode; + + if (!conn) + return maxw; + + list_for_each_entry(mode, &conn->modes, head) + maxw = maxw > mode->hdisplay ? maxw : mode->hdisplay; + + return maxw; +} + /** * sde_connector_state_get_topology - get topology from given connector state * conn_state: Pointer to the DRM connector state object diff --git a/msm/sde/sde_encoder.c b/msm/sde/sde_encoder.c index d52ced8648..9f1c79aaf2 100644 --- a/msm/sde/sde_encoder.c +++ b/msm/sde/sde_encoder.c @@ -1431,6 +1431,85 @@ static int _sde_encoder_update_roi(struct drm_encoder *drm_enc) return 0; } +static void _sde_encoder_update_ppb_size(struct drm_encoder *drm_enc) +{ + struct sde_kms *sde_kms; + struct sde_hw_mdp *hw_mdp; + struct drm_display_mode *mode; + struct sde_encoder_virt *sde_enc; + u32 maxw, pixels_per_pp, num_lm_or_pp, latency_lines; + int i; + + if (!drm_enc) { + SDE_ERROR("invalid encoder parameter\n"); + return; + } + + sde_enc = to_sde_encoder_virt(drm_enc); + if (!sde_enc->cur_master || !sde_enc->cur_master->connector) { + SDE_ERROR_ENC(sde_enc, "invalid master or conn\n"); + return; + } + + /* program only for realtime displays */ + if (sde_enc->disp_info.intf_type == DRM_MODE_CONNECTOR_VIRTUAL) + return; + + sde_kms = sde_encoder_get_kms(&sde_enc->base); + if (!sde_kms) { + SDE_ERROR_ENC(sde_enc, "invalid sde_kms\n"); + return; + } + + /* check if hw support is available, early return if not available */ + if (sde_kms->catalog->ppb_sz_program == SDE_PPB_SIZE_THRU_NONE) + return; + + hw_mdp = sde_kms->hw_mdp; + if (!hw_mdp) { + SDE_ERROR_ENC(sde_enc, "invalid mdp top\n"); + return; + } + + mode = &drm_enc->crtc->state->adjusted_mode; + num_lm_or_pp = sde_enc->cur_channel_cnt; + latency_lines = sde_kms->catalog->ppb_buf_max_lines; + + for (i = 0; i < num_lm_or_pp; i++) { + struct sde_hw_pingpong *hw_pp = sde_enc->hw_pp[i]; + if (!hw_pp) { + SDE_ERROR_ENC(sde_enc, "invalid hw_pp i:%d pp_cnt:%d\n", i, num_lm_or_pp); + return; + } + + if (hw_pp->ops.set_ppb_fifo_size) { + pixels_per_pp = mult_frac(mode->hdisplay, latency_lines, num_lm_or_pp); + hw_pp->ops.set_ppb_fifo_size(hw_pp, pixels_per_pp); + + SDE_EVT32(DRMID(drm_enc), i, hw_pp->idx, mode->hdisplay, pixels_per_pp, + sde_kms->catalog->ppb_sz_program, SDE_EVTLOG_FUNC_CASE1); + SDE_DEBUG_ENC(sde_enc, "hw-pp i:%d pp_cnt:%d pixels_per_pp:%d\n", + i, num_lm_or_pp, pixels_per_pp); + } else if (hw_mdp->ops.set_ppb_fifo_size) { + maxw = sde_conn_get_max_mode_width(sde_enc->cur_master->connector); + if (!maxw) { + SDE_ERROR_ENC(sde_enc, "failed to get max horizantal resolution\n"); + return; + } + + pixels_per_pp = mult_frac(maxw, latency_lines, num_lm_or_pp); + hw_mdp->ops.set_ppb_fifo_size(hw_mdp, hw_pp->idx, pixels_per_pp); + + SDE_EVT32(DRMID(drm_enc), i, hw_pp->idx, maxw, pixels_per_pp, + sde_kms->catalog->ppb_sz_program, SDE_EVTLOG_FUNC_CASE2); + SDE_DEBUG_ENC(sde_enc, "hw-pp i:%d pp_cnt:%d pixels_per_pp:%d\n", + i, num_lm_or_pp, pixels_per_pp); + } else { + SDE_ERROR_ENC(sde_enc, "invalid - ppb fifo size support is partial\n"); + } + } +} + void sde_encoder_helper_vsync_config(struct sde_encoder_phys *phys_enc, u32 vsync_source) { struct sde_vsync_source_cfg vsync_cfg = { 0 }; @@ -2549,12 +2628,14 @@ static void _sde_encoder_virt_populate_hw_res(struct drm_encoder *drm_enc) struct sde_rm_hw_request request_hw; int i, j; + sde_enc->cur_channel_cnt = 0; sde_rm_init_hw_iter(&pp_iter, drm_enc->base.id, SDE_HW_BLK_PINGPONG); for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { sde_enc->hw_pp[i] = NULL; if (!sde_rm_get_hw(&sde_kms->rm, &pp_iter)) break; sde_enc->hw_pp[i] = to_sde_hw_pingpong(pp_iter.hw); + sde_enc->cur_channel_cnt++; } for (i = 0; i < sde_enc->num_phys_encs; i++) { @@ -3000,6 +3081,9 @@ static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc) _sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info); + if (!sde_encoder_in_cont_splash(drm_enc)) + _sde_encoder_update_ppb_size(drm_enc); + memset(&sde_enc->prv_conn_roi, 0, sizeof(sde_enc->prv_conn_roi)); memset(&sde_enc->cur_conn_roi, 0, sizeof(sde_enc->cur_conn_roi)); _sde_encoder_control_fal10_veto(drm_enc, true); diff --git a/msm/sde/sde_encoder.h b/msm/sde/sde_encoder.h index e9c7991110..a1513035eb 100644 --- a/msm/sde/sde_encoder.h +++ b/msm/sde/sde_encoder.h @@ -158,6 +158,7 @@ enum sde_sim_qsync_event { * pingpong blocks can be different than num_phys_encs. * @hw_dsc: Array of DSC block handles used for the display. * @hw_vdc: Array of VDC block handles used for the display. + * @cur_channel_cnt Number of data channels currently used for the display * @dirty_dsc_ids: Cached dsc indexes for dirty DSC blocks needing flush * @intfs_swapped Whether or not the phys_enc interfaces have been swapped * for partial update right-only cases, such as pingpong @@ -243,6 +244,7 @@ struct sde_encoder_virt { struct sde_hw_pingpong *hw_dsc_pp[MAX_CHANNELS_PER_ENC]; enum sde_dsc dirty_dsc_ids[MAX_CHANNELS_PER_ENC]; enum sde_vdc dirty_vdc_ids[MAX_CHANNELS_PER_ENC]; + u32 cur_channel_cnt; bool intfs_swapped; bool qdss_status; diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index 7d3d2af3a0..3f6bb8361d 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -147,6 +147,7 @@ #define DEFAULT_AXI_BUS_WIDTH 32 #define DEFAULT_CPU_MASK 0 #define DEFAULT_CPU_DMA_LATENCY PM_QOS_DEFAULT_VALUE +#define DEFAULT_PPB_BUF_MAX_LINES 4 /* Uidle values */ #define SDE_UIDLE_FAL10_EXIT_CNT 128 @@ -4050,6 +4051,9 @@ static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) } } + if (sde_cfg->ppb_sz_program == SDE_PPB_SIZE_THRU_PINGPONG) + set_bit(SDE_PINGPONG_SET_SIZE, &pp->features); + sblk->dither.base = PROP_VALUE_ACCESS(prop_value, DITHER_OFF, i); if (sblk->dither.base) { @@ -4243,6 +4247,9 @@ static int sde_top_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) else if (major_version < SDE_HW_MAJOR(SDE_HW_VER_810)) set_bit(SDE_MDP_WD_TIMER, &cfg->mdp[0].features); + if (cfg->ppb_sz_program == SDE_PPB_SIZE_THRU_TOP) + set_bit(SDE_MDP_TOP_PPB_SET_SIZE, &cfg->mdp[0].features); + rc = _add_to_irq_offset_list(cfg, SDE_INTR_HWBLK_TOP, SDE_INTR_TOP_INTR, cfg->mdp[0].base); if (rc) @@ -5017,6 +5024,9 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) set_bit(SDE_FEATURE_HDR, sde_cfg->features); sde_cfg->mdss_hw_block_size = DEFAULT_MDSS_HW_BLOCK_SIZE; + /* Set target specific value based on sytems recommendation if not same as default value */ + sde_cfg->ppb_buf_max_lines = DEFAULT_PPB_BUF_MAX_LINES; + for (i = 0; i < SSPP_MAX; i++) { sde_cfg->demura_supported[i][0] = ~0x0; sde_cfg->demura_supported[i][1] = ~0x0; @@ -5399,6 +5409,8 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) set_bit(SDE_FEATURE_EPT_FPS, sde_cfg->features); sde_cfg->allowed_dsc_reservation_switch = SDE_DP_DSC_RESERVATION_SWITCH; sde_cfg->autorefresh_disable_seq = AUTOREFRESH_DISABLE_SEQ2; + /* if pingpong block supports it this should not be set on top block */ + sde_cfg->ppb_sz_program = SDE_PPB_SIZE_THRU_TOP; sde_cfg->perf.min_prefill_lines = 40; sde_cfg->vbif_qos_nlvl = 8; sde_cfg->qos_target_time_ns = 11160; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 142c0cbb70..fba2af696b 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -151,6 +151,8 @@ #define DNSC_BLUR_MAX_RATIO_COUNT 7 +#define MDP_PPB_FIFO_ENTRY_SIZE 4 + /* * UIDLE supported versions */ @@ -267,8 +269,8 @@ struct sde_intr_irq_offsets { * @SDE_MDP_DHDR_MEMPOOL Dynamic HDR Metadata mempool present * @SDE_MDP_DHDR_MEMPOOL_4K Dynamic HDR mempool is 4k aligned * @SDE_MDP_PERIPH_TOP_REMOVED Indicates if periph top0 block is removed - * @SDE_MDP_MAX Maximum value - + * @SDE_MDP_TOP_PPB_SET_SIZE Indicates if top block supports ppb size setting + * @SDE_MDP_MAX Maximum value */ enum { SDE_MDP_PANIC_PER_PIPE = 0x1, @@ -281,6 +283,7 @@ enum { SDE_MDP_DHDR_MEMPOOL, SDE_MDP_DHDR_MEMPOOL_4K, SDE_MDP_PERIPH_TOP_0_REMOVED, + SDE_MDP_TOP_PPB_SET_SIZE, SDE_MDP_MAX }; @@ -512,6 +515,7 @@ enum { * @SDE_PINGPONG_MERGE_3D, Separate MERGE_3D block exists * @SDE_PINGPONG_CWB, PP block supports CWB * @SDE_PINGPONG_CWB_DITHER, PP block supports CWB dither + * @SDE_PINGPONG_SET_SIZE, PP block supports setting latency buffer size * @SDE_PINGPONG_MAX */ enum { @@ -525,6 +529,7 @@ enum { SDE_PINGPONG_MERGE_3D, SDE_PINGPONG_CWB, SDE_PINGPONG_CWB_DITHER, + SDE_PINGPONG_SET_SIZE, SDE_PINGPONG_MAX }; @@ -731,6 +736,18 @@ enum { SDE_UIDLE_MAX }; +/** + * sde_ppb_size_option PPB size limit programming choice + * @SDE_PPB_SIZE_THRU_NONE ppb size programming not available + * @SDE_PPB_SIZE_THRU_TOP ppb size programming supported in top block + * @SDE_PPB_SIZE_THRU_PINGPONG ppb size programming supported in pingpong block + */ +enum sde_ppb_size_option { + SDE_PPB_SIZE_THRU_NONE, + SDE_PPB_SIZE_THRU_TOP, + SDE_PPB_SIZE_THRU_PINGPONG, +}; + /** * MDSS features - For enabling target specific functionality in @sde_mdss_cfg "features" bitmap * @SDE_FEATURE_CDP Client driven prefetch supported @@ -1941,6 +1958,8 @@ struct sde_perf_cfg { * @dnsc_blur_filter_count supported filter count for downscale blur * @ipcc_protocol_id ipcc protocol id for the hw * @ipcc_client_phys_id dpu ipcc client id for the hw, physical client id if supported + * @ppb_sz_program enum value for pingpong buffer size programming choice by hw + * @ppb_buf_max_lines maximum lines needed for pingpong latency buffer size */ struct sde_mdss_cfg { /* Block Revisions */ @@ -2062,6 +2081,9 @@ struct sde_mdss_cfg { u32 ipcc_protocol_id; u32 ipcc_client_phys_id; + + enum sde_ppb_size_option ppb_sz_program; + u32 ppb_buf_max_lines; }; struct sde_mdss_hw_cfg_handler { diff --git a/msm/sde/sde_hw_pingpong.c b/msm/sde/sde_hw_pingpong.c index 2250950de1..2652050246 100644 --- a/msm/sde/sde_hw_pingpong.c +++ b/msm/sde/sde_hw_pingpong.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ @@ -42,6 +42,9 @@ #define MERGE_3D_MODE 0x004 #define MERGE_3D_MUX 0x000 +#define PPB_FIFO_SIZE_CFG 0x01C +#define PPB_FIFO_SIZE_MASK 0x0FFF + static struct sde_merge_3d_cfg *_merge_3d_offset(enum sde_merge_3d idx, struct sde_mdss_cfg *m, void __iomem *addr, @@ -439,6 +442,22 @@ line_count_exit: return line; } +static void sde_hw_pp_set_ppb_fifo_size(struct sde_hw_pingpong *pp, u32 pixels) +{ + struct sde_hw_blk_reg_map *c; + u32 val; + + if (!pp) + return; + + c = &pp->hw; + + /* covert to fifo units, 4 pixels can be stored per fifo */ + val = (pixels / MDP_PPB_FIFO_ENTRY_SIZE) & 0x0FFF; + + SDE_REG_WRITE(c, PPB_FIFO_SIZE_CFG, val); +} + static void sde_hw_pp_setup_3d_merge_mode(struct sde_hw_pingpong *pp, enum sde_3d_blend_mode cfg) { @@ -473,7 +492,11 @@ static void _setup_pingpong_ops(struct sde_hw_pingpong_ops *ops, ops->get_autorefresh = sde_hw_pp_get_autorefresh_config; ops->poll_timeout_wr_ptr = sde_hw_pp_poll_timeout_wr_ptr; ops->get_line_count = sde_hw_pp_get_line_count; + } else if (hw_cap->features & BIT(SDE_PINGPONG_SET_SIZE)) { + /* PPB_FIFO_CFG offset conflicts with legacy PP Tear registers */ + ops->set_ppb_fifo_size = sde_hw_pp_set_ppb_fifo_size; } + if (hw_cap->features & BIT(SDE_PINGPONG_DSC)) { ops->setup_dsc = sde_hw_pp_setup_dsc; ops->enable_dsc = sde_hw_pp_dsc_enable; diff --git a/msm/sde/sde_hw_pingpong.h b/msm/sde/sde_hw_pingpong.h index ea86eefa07..3c286cc5e5 100644 --- a/msm/sde/sde_hw_pingpong.h +++ b/msm/sde/sde_hw_pingpong.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ @@ -131,6 +131,11 @@ struct sde_hw_pingpong_ops { * get PP features supported by this instance */ unsigned long (*get_hw_caps)(struct sde_hw_pingpong *pp); + + /** + * set_ppb_fifo_size - set ppb latency buffer size to a fixed value + */ + void (*set_ppb_fifo_size)(struct sde_hw_pingpong *pp, u32 pixels); }; struct sde_hw_merge_3d_ops { diff --git a/msm/sde/sde_hw_top.c b/msm/sde/sde_hw_top.c index bd6ad6d13c..a2657b11c5 100644 --- a/msm/sde/sde_hw_top.c +++ b/msm/sde/sde_hw_top.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ @@ -118,6 +118,8 @@ #define HW_FENCE_IPCC_FENCE_PROTOCOL_ID 4 #define HW_FENCE_DPU_FENCE_PROTOCOL_ID 3 +static int ppb_offset_map[PINGPONG_MAX] = {1, 0, 3, 2, 5, 4, 7, 7, 6, 6, -1, -1}; + static void sde_hw_setup_split_pipe(struct sde_hw_mdp *mdp, struct split_pipe_cfg *cfg) { @@ -767,6 +769,38 @@ static void sde_hw_setup_hw_fences_config(struct sde_hw_mdp *mdp, u32 protocol_i SDE_REG_WRITE(&c, offset, val); } +void sde_hw_top_set_ppb_fifo_size(struct sde_hw_mdp *mdp, u32 pp, u32 sz) +{ + struct sde_hw_blk_reg_map c; + u32 offset, val, pp_index; + + if (!mdp) { + SDE_ERROR("invalid mdp instance\n"); + return; + } + + if (pp >= PINGPONG_MAX || ppb_offset_map[pp - PINGPONG_0] < 0) { + SDE_ERROR("invalid pingpong index:%d max:%d\n", pp, PINGPONG_MAX); + return; + } + + pp_index = pp - PINGPONG_0; + + c = mdp->hw; + offset = PPB_FIFO_SIZE + ((ppb_offset_map[pp_index] / 2) * 0x4); + + spin_lock(&mdp->slock); + /* read, modify & update *respective 16 bit fields */ + val = SDE_REG_READ(&c, offset); + + /* divide by 4 as each fifo entry can store 4 pixels */ + sz = (sz / MDP_PPB_FIFO_ENTRY_SIZE) & 0xFFFF; + sz = ppb_offset_map[pp_index] % 2 ? (sz << 16) : sz; + val = (ppb_offset_map[pp_index] % 2) ? (val & 0xFFFF) : (val & 0xFFFF0000); + SDE_REG_WRITE(&c, offset, val | sz); + spin_unlock(&mdp->slock); +} + static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops, unsigned long cap, u32 hw_fence_rev) { ops->setup_split_pipe = sde_hw_setup_split_pipe; @@ -793,6 +827,9 @@ static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops, unsigned long cap, u32 hw ops->hw_fence_input_timestamp_ctrl = sde_hw_hw_fence_timestamp_ctrl; ops->hw_fence_input_status = sde_hw_input_hw_fence_status; } + + if (cap & BIT(SDE_MDP_TOP_PPB_SET_SIZE)) + ops->set_ppb_fifo_size = sde_hw_top_set_ppb_fifo_size; } static const struct sde_mdp_cfg *_top_offset(enum sde_mdp mdp, @@ -839,6 +876,8 @@ struct sde_hw_mdp *sde_hw_mdptop_init(enum sde_mdp idx, return ERR_PTR(-EINVAL); } + spin_lock_init(&mdp->slock); + /* * Assign ops */ diff --git a/msm/sde/sde_hw_top.h b/msm/sde/sde_hw_top.h index 694a56ece3..c605a4ee58 100644 --- a/msm/sde/sde_hw_top.h +++ b/msm/sde/sde_hw_top.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-2023 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. */ @@ -230,6 +230,14 @@ struct sde_hw_mdp_ops { * @enable: indicates if timestamps should be cleared */ void (*hw_fence_input_timestamp_ctrl)(struct sde_hw_mdp *mdp, bool enable, bool clear); + + /** + * set_ppb_fifo_size - set ppb latency buffer size to a fixed value + * @mdp: mdp top context driver + * @pp: indicates pingpong block id + * @sz: indicates size of the ppb in terms of pixels + */ + void (*set_ppb_fifo_size)(struct sde_hw_mdp *mdp, u32 pp, u32 sz); }; struct sde_hw_mdp { @@ -239,6 +247,8 @@ struct sde_hw_mdp { enum sde_mdp idx; const struct sde_mdp_cfg *caps; + spinlock_t slock; + /* ops */ struct sde_hw_mdp_ops ops; }; diff --git a/msm/sde/sde_hwio.h b/msm/sde/sde_hwio.h index 170f36c354..35cad0138a 100644 --- a/msm/sde/sde_hwio.h +++ b/msm/sde/sde_hwio.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _SDE_HWIO_H @@ -33,6 +34,7 @@ #define PPB0_CONFIG 0x334 #define PPB1_CNTL 0x338 #define PPB1_CONFIG 0x33C +#define PPB_FIFO_SIZE 0x350 #define PPB2_CNTL 0x370 #define PPB3_CNTL 0x374 #define HW_EVENTS_CTL 0x37C