Loading Documentation/devicetree/bindings/display/msm/sde.txt +3 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,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 Loading Loading @@ -297,6 +299,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>; Loading drivers/gpu/drm/msm/sde/sde_crtc.c +7 −4 Original line number Diff line number Diff line Loading @@ -291,8 +291,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); Loading Loading @@ -493,7 +496,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; Loading @@ -503,7 +506,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; } Loading drivers/gpu/drm/msm/sde/sde_encoder.c +59 −6 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -579,6 +630,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", Loading @@ -592,6 +644,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); Loading Loading @@ -672,7 +728,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"); Loading @@ -687,18 +742,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); Loading drivers/gpu/drm/msm/sde/sde_encoder_phys.h +13 −2 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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); Loading Loading @@ -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__ */ drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +64 −53 Original line number Diff line number Diff line Loading @@ -80,8 +80,7 @@ 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) if (sde_rm_get_hw(rm, &iter)) phys_enc->hw_ctl = (struct sde_hw_ctl *)iter.hw; } Loading Loading @@ -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) { Loading @@ -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"); Loading Loading @@ -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; } 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) { SDE_ERROR("invalid encoder\n"); return; } SDE_DEBUG_CMDENC(cmd_enc, "enable %d\n", enable); enum sde_rm_topology_name topology; 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 (!phys_enc) return false; 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( Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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); 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 at disable: %d\n", "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_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_PINGPONG); } phys_enc->enable_state = SDE_ENC_DISABLED; Loading Loading @@ -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( Loading Loading
Documentation/devicetree/bindings/display/msm/sde.txt +3 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,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 Loading Loading @@ -297,6 +299,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>; Loading
drivers/gpu/drm/msm/sde/sde_crtc.c +7 −4 Original line number Diff line number Diff line Loading @@ -291,8 +291,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); Loading Loading @@ -493,7 +496,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; Loading @@ -503,7 +506,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; } Loading
drivers/gpu/drm/msm/sde/sde_encoder.c +59 −6 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -579,6 +630,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", Loading @@ -592,6 +644,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); Loading Loading @@ -672,7 +728,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"); Loading @@ -687,18 +742,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); Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys.h +13 −2 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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); Loading Loading @@ -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__ */
drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +64 −53 Original line number Diff line number Diff line Loading @@ -80,8 +80,7 @@ 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) if (sde_rm_get_hw(rm, &iter)) phys_enc->hw_ctl = (struct sde_hw_ctl *)iter.hw; } Loading Loading @@ -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) { Loading @@ -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"); Loading Loading @@ -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; } 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) { SDE_ERROR("invalid encoder\n"); return; } SDE_DEBUG_CMDENC(cmd_enc, "enable %d\n", enable); enum sde_rm_topology_name topology; 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 (!phys_enc) return false; 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( Loading Loading @@ -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, Loading Loading @@ -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); Loading @@ -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); 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 at disable: %d\n", "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_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_PINGPONG); } phys_enc->enable_state = SDE_ENC_DISABLED; Loading Loading @@ -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( Loading