diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt index 4c46e485f7bb..a1f0fc53f250 100644 --- a/Documentation/devicetree/bindings/display/msm/sde.txt +++ b/Documentation/devicetree/bindings/display/msm/sde.txt @@ -57,6 +57,8 @@ Required properties - qcom,sde-pp-off: Array of offset addresses for the available pingpong blocks. These offsets are calculated from register "mdp_phys" defined in reg property. +- qcom,sde-pp-slave: Array of flags indicating whether each ping pong + block may be configured as a pp slave. - qcom,sde-intf-off: Array of offset addresses for the available SDE interface blocks that can drive data to a panel controller. The offsets are calculated @@ -286,6 +288,7 @@ Example: qcom,sde-intf-type = "none", "dsi", "dsi", "hdmi"; qcom,sde-pp-off = <0x00071000 0x00071800 0x00072000 0x00072800>; + qcom,sde-pp-slave = <0x0 0x0 0x0 0x0>; qcom,sde-cdm-off = <0x0007a200>; qcom,sde-dsc-off = <0x00081000 0x00081400>; qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>; diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index 875c3c99f337..535a033435d2 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -290,8 +290,11 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc) /* stage config flush mask */ ctl->ops.update_pending_flush(ctl, mixer[i].flush_mask); - SDE_DEBUG("lm %d ctl %d add mask 0x%x to pending flush\n", - mixer[i].hw_lm->idx, ctl->idx, mixer[i].flush_mask); + SDE_DEBUG("lm %d, op_mode 0x%X, ctl %d, flush mask 0x%x\n", + mixer[i].hw_lm->idx - LM_0, + mixer[i].mixer_op_mode, + ctl->idx - CTL_0, + mixer[i].flush_mask); ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx, &sde_crtc->stage_cfg, i); @@ -492,7 +495,7 @@ static void _sde_crtc_setup_mixer_for_encoder( /* CTL may be <= LMs, if <, multiple LMs controlled by 1 CTL */ if (!sde_rm_get_hw(rm, &ctl_iter)) { SDE_DEBUG("no ctl assigned to lm %d, using previous\n", - mixer->hw_lm->idx); + mixer->hw_lm->idx - LM_0); mixer->hw_ctl = last_valid_ctl; } else { mixer->hw_ctl = (struct sde_hw_ctl *)ctl_iter.hw; @@ -502,7 +505,7 @@ static void _sde_crtc_setup_mixer_for_encoder( /* Shouldn't happen, mixers are always >= ctls */ if (!mixer->hw_ctl) { SDE_ERROR("no valid ctls found for lm %d\n", - mixer->hw_lm->idx); + mixer->hw_lm->idx - LM_0); return; } diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 7481c2a63d7d..545dfa7c4f16 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -244,6 +244,57 @@ void sde_encoder_destroy(struct drm_encoder *drm_enc) kfree(sde_enc); } +void sde_encoder_helper_split_config( + struct sde_encoder_phys *phys_enc, + enum sde_intf interface) +{ + struct sde_encoder_virt *sde_enc; + struct split_pipe_cfg cfg = { 0 }; + struct sde_hw_mdp *hw_mdptop; + enum sde_rm_topology_name topology; + + if (!phys_enc || !phys_enc->hw_mdptop || !phys_enc->parent) { + SDE_ERROR("invalid arg(s), encoder %d\n", phys_enc != 0); + return; + } + + sde_enc = to_sde_encoder_virt(phys_enc->parent); + hw_mdptop = phys_enc->hw_mdptop; + cfg.en = phys_enc->split_role != ENC_ROLE_SOLO; + cfg.mode = phys_enc->intf_mode; + cfg.intf = interface; + + if (cfg.en && phys_enc->ops.needs_single_flush && + phys_enc->ops.needs_single_flush(phys_enc)) + cfg.split_flush_en = true; + + topology = sde_connector_get_topology_name(phys_enc->connector); + if (topology == SDE_RM_TOPOLOGY_PPSPLIT) + cfg.pp_split_slave = cfg.intf; + else + cfg.pp_split_slave = INTF_MAX; + + if (phys_enc->split_role != ENC_ROLE_SLAVE) { + /* master/solo encoder */ + SDE_DEBUG_ENC(sde_enc, "enable %d\n", cfg.en); + + if (hw_mdptop->ops.setup_split_pipe) + hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg); + } else { + /* + * slave encoder + * - determine split index from master index, + * assume master is first pp + */ + cfg.pp_split_index = sde_enc->hw_pp[0]->idx - PINGPONG_0; + SDE_DEBUG_ENC(sde_enc, "master using pp%d\n", + cfg.pp_split_index); + + if (hw_mdptop->ops.setup_pp_split) + hw_mdptop->ops.setup_pp_split(hw_mdptop, &cfg); + } +} + static int sde_encoder_virt_atomic_check( struct drm_encoder *drm_enc, struct drm_crtc_state *crtc_state, @@ -581,6 +632,7 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc, struct sde_encoder_phys *phys, uint32_t extra_flush_bits) { struct sde_hw_ctl *ctl; + int pending_kickoff_cnt; if (!drm_enc || !phys) { SDE_ERROR("invalid argument(s), drm_enc %d, phys_enc %d\n", @@ -594,6 +646,10 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc, return; } + pending_kickoff_cnt = sde_encoder_phys_inc_pending(phys); + SDE_EVT32(DRMID(&to_sde_encoder_virt(drm_enc)->base), + phys->intf_idx, pending_kickoff_cnt); + if (extra_flush_bits && ctl->ops.update_pending_flush) ctl->ops.update_pending_flush(ctl, extra_flush_bits); @@ -674,7 +730,6 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) struct sde_hw_ctl *ctl; uint32_t i, pending_flush; unsigned long lock_flags; - int pending_kickoff_cnt; if (!sde_enc) { SDE_ERROR("invalid encoder\n"); @@ -689,18 +744,16 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc) /* don't perform flush/start operations for slave encoders */ for (i = 0; i < sde_enc->num_phys_encs; i++) { struct sde_encoder_phys *phys = sde_enc->phys_encs[i]; + if (!phys || phys->enable_state == SDE_ENC_DISABLED) continue; - pending_kickoff_cnt = sde_encoder_phys_inc_pending(phys); - SDE_EVT32(DRMID(&sde_enc->base), i, pending_kickoff_cnt); - ctl = phys->hw_ctl; if (!ctl) continue; - if (!phys->ops.needs_split_flush || - !phys->ops.needs_split_flush(phys)) + if (!phys->ops.needs_single_flush || + !phys->ops.needs_single_flush(phys)) _sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0); else if (ctl->ops.get_pending_flush) pending_flush |= ctl->ops.get_pending_flush(ctl); diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h index 21a52acdbde7..355bd7e7fc11 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h @@ -87,7 +87,7 @@ struct sde_encoder_virt_ops { * For CMD encoder, may wait for previous tx done * @handle_post_kickoff: Do any work necessary post-kickoff work * @trigger_start: Process start event on physical encoder - * @needs_split_flush: Whether encoder type needs split flush + * @needs_single_flush: Whether encoder slaves need to be flushed * @setup_misr: Sets up MISR, enable and disables based on sysfs * @collect_misr: Collects MISR data on frame update */ @@ -114,7 +114,7 @@ struct sde_encoder_phys_ops { void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc); void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc); void (*trigger_start)(struct sde_encoder_phys *phys_enc); - bool (*needs_split_flush)(struct sde_encoder_phys *phys_enc); + bool (*needs_single_flush)(struct sde_encoder_phys *phys_enc); void (*setup_misr)(struct sde_encoder_phys *phys_encs, struct sde_misr_params *misr_map); @@ -388,4 +388,15 @@ static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode( return BLEND_3D_NONE; } +/** + * sde_encoder_helper_split_config - split display configuration helper function + * This helper function may be used by physical encoders to configure + * the split display related registers. + * @phys_enc: Pointer to physical encoder structure + * @interface: enum sde_intf setting + */ +void sde_encoder_helper_split_config( + struct sde_encoder_phys *phys_enc, + enum sde_intf interface); + #endif /* __sde_encoder_phys_H__ */ diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c index bd18281c73ae..17ed73dc5155 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c @@ -80,9 +80,8 @@ static void sde_encoder_phys_cmd_mode_set( /* Retrieve previously allocated HW Resources. Shouldn't fail */ sde_rm_init_hw_iter(&iter, phys_enc->parent->base.id, SDE_HW_BLK_CTL); for (i = 0; i <= instance; i++) { - sde_rm_get_hw(rm, &iter); - if (i == instance) - phys_enc->hw_ctl = (struct sde_hw_ctl *) iter.hw; + if (sde_rm_get_hw(rm, &iter)) + phys_enc->hw_ctl = (struct sde_hw_ctl *)iter.hw; } if (IS_ERR_OR_NULL(phys_enc->hw_ctl)) { @@ -129,6 +128,22 @@ static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx) phys_enc); } +static bool _sde_encoder_phys_is_ppsplit_slave( + struct sde_encoder_phys *phys_enc) +{ + enum sde_rm_topology_name topology; + + if (!phys_enc) + return false; + + topology = sde_connector_get_topology_name(phys_enc->connector); + if (topology == SDE_RM_TOPOLOGY_PPSPLIT && + phys_enc->split_role == ENC_ROLE_SLAVE) + return true; + + return false; +} + static int _sde_encoder_phys_cmd_wait_for_idle( struct sde_encoder_phys *phys_enc) { @@ -137,6 +152,15 @@ static int _sde_encoder_phys_cmd_wait_for_idle( u32 irq_status; int ret; + if (!phys_enc) { + SDE_ERROR("invalid encoder\n"); + return -EINVAL; + } + + /* slave encoder doesn't enable for ppsplit */ + if (_sde_encoder_phys_is_ppsplit_slave(phys_enc)) + return 0; + /* return EWOULDBLOCK since we know the wait isn't necessary */ if (phys_enc->enable_state == SDE_ENC_DISABLED) { SDE_ERROR_CMDENC(cmd_enc, "encoder is disabled\n"); @@ -374,34 +398,16 @@ static void sde_encoder_phys_cmd_pingpong_config( sde_encoder_phys_cmd_tearcheck_config(phys_enc); } -static bool sde_encoder_phys_cmd_needs_split_flush( +static bool sde_encoder_phys_cmd_needs_single_flush( struct sde_encoder_phys *phys_enc) { - return false; -} + enum sde_rm_topology_name topology; -static void sde_encoder_phys_cmd_split_config( - struct sde_encoder_phys *phys_enc, bool enable) -{ - struct sde_encoder_phys_cmd *cmd_enc = - to_sde_encoder_phys_cmd(phys_enc); - struct sde_hw_mdp *hw_mdptop = phys_enc->hw_mdptop; - struct split_pipe_cfg cfg = { 0 }; + if (!phys_enc) + return false; - if (!phys_enc) { - SDE_ERROR("invalid encoder\n"); - return; - } - SDE_DEBUG_CMDENC(cmd_enc, "enable %d\n", enable); - - cfg.en = enable; - cfg.mode = INTF_MODE_CMD; - cfg.intf = cmd_enc->intf_idx; - cfg.split_flush_en = enable && - sde_encoder_phys_cmd_needs_split_flush(phys_enc); - - if (hw_mdptop && hw_mdptop->ops.setup_split_pipe) - hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg); + topology = sde_connector_get_topology_name(phys_enc->connector); + return topology == SDE_RM_TOPOLOGY_PPSPLIT; } static int sde_encoder_phys_cmd_control_vblank_irq( @@ -453,28 +459,26 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc) to_sde_encoder_phys_cmd(phys_enc); struct sde_hw_ctl *ctl; u32 flush_mask; - int ret = 0; + int ret; - if (!phys_enc) { - SDE_ERROR("invalid encoder\n"); + if (!phys_enc || !phys_enc->hw_ctl) { + SDE_ERROR("invalid arg(s), encoder %d\n", phys_enc != 0); return; } SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0); - if (WARN_ON(phys_enc->enable_state == SDE_ENC_ENABLED)) + if (phys_enc->enable_state == SDE_ENC_ENABLED) { + SDE_ERROR("already enabled\n"); return; + } - /* - * Only master configures master/slave configuration, so no slave check - * In solo configuration, solo encoder needs to program no-split - */ - if (phys_enc->split_role == ENC_ROLE_MASTER) - sde_encoder_phys_cmd_split_config(phys_enc, true); - else if (phys_enc->split_role == ENC_ROLE_SOLO) - sde_encoder_phys_cmd_split_config(phys_enc, false); + sde_encoder_helper_split_config(phys_enc, cmd_enc->intf_idx); sde_encoder_phys_cmd_pingpong_config(phys_enc); + if (_sde_encoder_phys_is_ppsplit_slave(phys_enc)) + goto update_flush; + /* Both master and slave need to register for pp_tx_done */ ret = sde_encoder_phys_cmd_register_irq(phys_enc, SDE_IRQ_TYPE_PING_PONG_COMP, @@ -503,6 +507,7 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc) return; } +update_flush: ctl = phys_enc->hw_ctl; ctl->ops.get_bitmask_intf(ctl, &flush_mask, cmd_enc->intf_idx); ctl->ops.update_pending_flush(ctl, flush_mask); @@ -524,24 +529,30 @@ static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc) } SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0); - if (WARN_ON(phys_enc->enable_state == SDE_ENC_DISABLED)) + if (phys_enc->enable_state == SDE_ENC_DISABLED) { + SDE_ERROR_CMDENC(cmd_enc, "already disabled\n"); return; + } SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0); - ret = _sde_encoder_phys_cmd_wait_for_idle(phys_enc); - if (ret) { - atomic_set(&phys_enc->pending_kickoff_cnt, 0); - SDE_ERROR_CMDENC(cmd_enc, - "pp %d failed wait for idle at disable: %d\n", - phys_enc->hw_pp->idx - PINGPONG_0, ret); - SDE_EVT32(DRMID(phys_enc->parent), - phys_enc->hw_pp->idx - PINGPONG_0, ret); - } + if (!_sde_encoder_phys_is_ppsplit_slave(phys_enc)) { + ret = _sde_encoder_phys_cmd_wait_for_idle(phys_enc); + if (ret) { + atomic_set(&phys_enc->pending_kickoff_cnt, 0); + SDE_ERROR_CMDENC(cmd_enc, + "pp %d failed wait for idle, %d\n", + phys_enc->hw_pp->idx - PINGPONG_0, ret); + SDE_EVT32(DRMID(phys_enc->parent), + phys_enc->hw_pp->idx - PINGPONG_0, ret); + } - sde_encoder_phys_cmd_unregister_irq(phys_enc, INTR_IDX_UNDERRUN); - sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false); - sde_encoder_phys_cmd_unregister_irq(phys_enc, INTR_IDX_PINGPONG); + sde_encoder_phys_cmd_unregister_irq( + phys_enc, INTR_IDX_UNDERRUN); + sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false); + sde_encoder_phys_cmd_unregister_irq( + phys_enc, INTR_IDX_PINGPONG); + } phys_enc->enable_state = SDE_ENC_DISABLED; @@ -634,7 +645,7 @@ static void sde_encoder_phys_cmd_init_ops( ops->wait_for_commit_done = sde_encoder_phys_cmd_wait_for_commit_done; ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff; ops->trigger_start = sde_encoder_helper_trigger_start; - ops->needs_split_flush = sde_encoder_phys_cmd_needs_split_flush; + ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush; } struct sde_encoder_phys *sde_encoder_phys_cmd_init( diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c index 683868eaec1e..eddb28a86b52 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c @@ -319,38 +319,12 @@ static void sde_encoder_phys_vid_underrun_irq(void *arg, int irq_idx) phys_enc); } -static bool sde_encoder_phys_vid_needs_split_flush( +static bool sde_encoder_phys_vid_needs_single_flush( struct sde_encoder_phys *phys_enc) { return phys_enc && phys_enc->split_role != ENC_ROLE_SOLO; } -static void _sde_encoder_phys_vid_split_config( - struct sde_encoder_phys *phys_enc, bool enable) -{ - struct sde_encoder_phys_vid *vid_enc = - to_sde_encoder_phys_vid(phys_enc); - struct sde_hw_mdp *hw_mdptop = phys_enc->hw_mdptop; - struct split_pipe_cfg cfg = { 0 }; - - SDE_DEBUG_VIDENC(vid_enc, "enable %d\n", enable); - - cfg.en = enable; - cfg.mode = INTF_MODE_VIDEO; - cfg.intf = vid_enc->hw_intf->idx; - cfg.split_flush_en = enable && - sde_encoder_phys_vid_needs_split_flush(phys_enc); - - /* Configure split pipe control to handle master/slave triggering */ - if (hw_mdptop && hw_mdptop->ops.setup_split_pipe) { - unsigned long lock_flags; - - spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); - hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg); - spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); - } -} - static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc, enum sde_intr_type intr_type, int idx, void (*irq_func)(void *, int), const char *irq_name) @@ -429,16 +403,17 @@ static void sde_encoder_phys_vid_mode_set( struct drm_display_mode *mode, struct drm_display_mode *adj_mode) { - struct sde_rm *rm = &phys_enc->sde_kms->rm; + struct sde_rm *rm; struct sde_rm_hw_iter iter; int i, instance; struct sde_encoder_phys_vid *vid_enc; - if (!phys_enc) { + if (!phys_enc || !phys_enc->sde_kms) { SDE_ERROR("invalid encoder\n"); return; } + rm = &phys_enc->sde_kms->rm; vid_enc = to_sde_encoder_phys_vid(phys_enc); phys_enc->cached_mode = *adj_mode; SDE_DEBUG_VIDENC(vid_enc, "caching mode:\n"); @@ -449,11 +424,9 @@ static void sde_encoder_phys_vid_mode_set( /* Retrieve previously allocated HW Resources. Shouldn't fail */ sde_rm_init_hw_iter(&iter, phys_enc->parent->base.id, SDE_HW_BLK_CTL); for (i = 0; i <= instance; i++) { - sde_rm_get_hw(rm, &iter); - if (i == instance) - phys_enc->hw_ctl = (struct sde_hw_ctl *) iter.hw; + if (sde_rm_get_hw(rm, &iter)) + phys_enc->hw_ctl = (struct sde_hw_ctl *)iter.hw; } - if (IS_ERR_OR_NULL(phys_enc->hw_ctl)) { SDE_ERROR_VIDENC(vid_enc, "failed to init ctl, %ld\n", PTR_ERR(phys_enc->hw_ctl)); @@ -531,10 +504,7 @@ static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc) if (WARN_ON(!vid_enc->hw_intf->ops.enable_timing)) return; - if (phys_enc->split_role == ENC_ROLE_MASTER) - _sde_encoder_phys_vid_split_config(phys_enc, true); - else if (phys_enc->split_role == ENC_ROLE_SOLO) - _sde_encoder_phys_vid_split_config(phys_enc, false); + sde_encoder_helper_split_config(phys_enc, vid_enc->hw_intf->idx); sde_encoder_phys_vid_setup_timing_engine(phys_enc); ret = sde_encoder_phys_vid_control_vblank_irq(phys_enc, true); @@ -772,7 +742,7 @@ static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops) ops->control_vblank_irq = sde_encoder_phys_vid_control_vblank_irq; ops->wait_for_commit_done = sde_encoder_phys_vid_wait_for_commit_done; ops->handle_post_kickoff = sde_encoder_phys_vid_handle_post_kickoff; - ops->needs_split_flush = sde_encoder_phys_vid_needs_split_flush; + ops->needs_single_flush = sde_encoder_phys_vid_needs_single_flush; ops->setup_misr = sde_encoder_phys_vid_setup_misr; ops->collect_misr = sde_encoder_phys_vid_collect_misr; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 6783c232e868..72c0b5cfd094 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -155,6 +155,7 @@ enum { TE2_LEN, DSC_OFF, DSC_LEN, + PP_SLAVE, PP_PROP_MAX, }; @@ -347,6 +348,7 @@ static struct sde_prop_type pp_prop[] = { {TE2_LEN, "qcom,sde-te2-size", false, PROP_TYPE_U32}, {DSC_OFF, "qcom,sde-dsc-off", false, PROP_TYPE_U32_ARRAY}, {DSC_LEN, "qcom,sde-dsc-size", false, PROP_TYPE_U32}, + {PP_SLAVE, "qcom,sde-pp-slave", false, PROP_TYPE_U32_ARRAY}, }; static struct sde_prop_type cdm_prop[] = { @@ -1723,6 +1725,9 @@ static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) set_bit(SDE_PINGPONG_SPLIT, &pp->features); } + if (PROP_VALUE_ACCESS(prop_value, PP_SLAVE, i)) + set_bit(SDE_PINGPONG_SLAVE, &pp->features); + sblk->dsc.base = PROP_VALUE_ACCESS(prop_value, DSC_OFF, i); if (sblk->dsc.base) { sblk->dsc.id = SDE_PINGPONG_DSC; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h index 26bb832b5fa3..b9a1bd97c015 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h @@ -159,6 +159,7 @@ enum { * @SDE_PINGPONG_TE Tear check block * @SDE_PINGPONG_TE2 Additional tear check block for split pipes * @SDE_PINGPONG_SPLIT PP block supports split fifo + * @SDE_PINGPONG_SLAVE PP block is a suitable slave for split fifo * @SDE_PINGPONG_DSC, Display stream compression blocks * @SDE_PINGPONG_MAX */ @@ -166,6 +167,7 @@ enum { SDE_PINGPONG_TE = 0x1, SDE_PINGPONG_TE2, SDE_PINGPONG_SPLIT, + SDE_PINGPONG_SLAVE, SDE_PINGPONG_DSC, SDE_PINGPONG_MAX }; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c index 10d39177345d..045d4c24bb29 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_top.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c @@ -29,13 +29,16 @@ #define TRAFFIC_SHAPER_WR_CLIENT(num) (0x060 + (num * 4)) #define TRAFFIC_SHAPER_FIXPOINT_FACTOR 4 -static void sde_hw_setup_split_pipe_control(struct sde_hw_mdp *mdp, +static void sde_hw_setup_split_pipe(struct sde_hw_mdp *mdp, struct split_pipe_cfg *cfg) { struct sde_hw_blk_reg_map *c = &mdp->hw; u32 upper_pipe = 0; u32 lower_pipe = 0; + if (!mdp || !cfg) + return; + if (cfg->en) { if (cfg->mode == INTF_MODE_CMD) { lower_pipe = FLD_SPLIT_DISPLAY_CMD; @@ -46,7 +49,7 @@ static void sde_hw_setup_split_pipe_control(struct sde_hw_mdp *mdp, lower_pipe |= FLD_INTF_2_SW_TRG_MUX; /* free run */ - if (cfg->pp_split) + if (cfg->pp_split_slave != INTF_MAX) lower_pipe = FLD_SMART_PANEL_FREE_RUN; upper_pipe = lower_pipe; @@ -61,12 +64,39 @@ static void sde_hw_setup_split_pipe_control(struct sde_hw_mdp *mdp, } } - SDE_REG_WRITE(c, SSPP_SPARE, (cfg->split_flush_en) ? 0x1 : 0x0); + SDE_REG_WRITE(c, SSPP_SPARE, cfg->split_flush_en ? 0x1 : 0x0); SDE_REG_WRITE(c, SPLIT_DISPLAY_LOWER_PIPE_CTRL, lower_pipe); SDE_REG_WRITE(c, SPLIT_DISPLAY_UPPER_PIPE_CTRL, upper_pipe); SDE_REG_WRITE(c, SPLIT_DISPLAY_EN, cfg->en & 0x1); } +static void sde_hw_setup_pp_split(struct sde_hw_mdp *mdp, + struct split_pipe_cfg *cfg) +{ + u32 ppb_config = 0x0; + u32 ppb_control = 0x0; + + if (!mdp || !cfg) + return; + + if (cfg->en && cfg->pp_split_slave != INTF_MAX) { + ppb_config |= (cfg->pp_split_slave - INTF_0 + 1) << 20; + ppb_config |= BIT(16); /* split enable */ + ppb_control = BIT(5); /* horz split*/ + } + if (cfg->pp_split_index) { + SDE_REG_WRITE(&mdp->hw, PPB0_CONFIG, 0x0); + SDE_REG_WRITE(&mdp->hw, PPB0_CNTL, 0x0); + SDE_REG_WRITE(&mdp->hw, PPB1_CONFIG, ppb_config); + SDE_REG_WRITE(&mdp->hw, PPB1_CNTL, ppb_control); + } else { + SDE_REG_WRITE(&mdp->hw, PPB0_CONFIG, ppb_config); + SDE_REG_WRITE(&mdp->hw, PPB0_CNTL, ppb_control); + SDE_REG_WRITE(&mdp->hw, PPB1_CONFIG, 0x0); + SDE_REG_WRITE(&mdp->hw, PPB1_CNTL, 0x0); + } +} + static void sde_hw_setup_cdm_output(struct sde_hw_mdp *mdp, struct cdm_output_cfg *cfg) { @@ -112,7 +142,8 @@ static bool sde_hw_setup_clk_force_ctrl(struct sde_hw_mdp *mdp, static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops, unsigned long cap) { - ops->setup_split_pipe = sde_hw_setup_split_pipe_control; + ops->setup_split_pipe = sde_hw_setup_split_pipe; + ops->setup_pp_split = sde_hw_setup_pp_split; ops->setup_cdm_output = sde_hw_setup_cdm_output; ops->setup_clk_force_ctrl = sde_hw_setup_clk_force_ctrl; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.h b/drivers/gpu/drm/msm/sde/sde_hw_top.h index 7359a77bc954..eb8ff8685908 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_top.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_top.h @@ -40,7 +40,8 @@ struct traffic_shaper_cfg { * @en : Enable/disable dual pipe confguration * @mode : Panel interface mode * @intf : Interface id for main control path - * @pp_split : Ping pong split is enabled or disabled + * @pp_split_slave: Slave interface for ping pong split, INTF_MAX to disable + * @pp_split_idx: Ping pong index for ping pong split * @split_flush_en: Allows both the paths to be flushed when master path is * flushed */ @@ -48,7 +49,8 @@ struct split_pipe_cfg { bool en; enum sde_intf_mode mode; enum sde_intf intf; - bool pp_split; + enum sde_intf pp_split_slave; + u32 pp_split_index; bool split_flush_en; }; @@ -66,6 +68,7 @@ struct cdm_output_cfg { * struct sde_hw_mdp_ops - interface to the MDP TOP Hw driver functions * Assumption is these functions will be called after clocks are enabled. * @setup_split_pipe : Programs the pipe control registers + * @setup_pp_split : Programs the pp split control registers * @setup_cdm_output : programs cdm control * @setup_traffic_shaper : programs traffic shaper control */ @@ -78,6 +81,13 @@ struct sde_hw_mdp_ops { void (*setup_split_pipe)(struct sde_hw_mdp *mdp, struct split_pipe_cfg *p); + /** setup_pp_split() : Configure pp split related registers + * @mdp : mdp top context driver + * @cfg : upper and lower part of pipe configuration + */ + void (*setup_pp_split)(struct sde_hw_mdp *mdp, + struct split_pipe_cfg *cfg); + /** * setup_cdm_output() : Setup selection control of the cdm data path * @mdp : mdp top context driver diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c index 075e9d69af8f..1d27b27d265c 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.c +++ b/drivers/gpu/drm/msm/sde/sde_rm.c @@ -592,7 +592,7 @@ static int _sde_rm_reserve_lms( struct sde_rm_hw_blk *pp[MAX_BLOCKS]; struct sde_rm_hw_iter iter_i, iter_j; int lm_count = 0; - int i; + int i, rc = 0; if (!reqs->num_lm) { SDE_ERROR("invalid number of lm: %d\n", reqs->num_lm); @@ -651,7 +651,27 @@ static int _sde_rm_reserve_lms( dspp[i] ? dspp[i]->id : 0); } - return 0; + if (reqs->top_name == SDE_RM_TOPOLOGY_PPSPLIT) { + /* reserve a free PINGPONG_SLAVE block */ + rc = -ENAVAIL; + sde_rm_init_hw_iter(&iter_i, 0, SDE_HW_BLK_PINGPONG); + while (sde_rm_get_hw(rm, &iter_i)) { + struct sde_pingpong_cfg *pp_cfg = + (struct sde_pingpong_cfg *) + (iter_i.blk->catalog); + + if (!(test_bit(SDE_PINGPONG_SLAVE, &pp_cfg->features))) + continue; + if (RESERVED_BY_OTHER(iter_i.blk, rsvp)) + continue; + + iter_i.blk->rsvp_nxt = rsvp; + rc = 0; + break; + } + } + + return rc; } static int _sde_rm_reserve_ctls(