Loading drivers/gpu/drm/msm/sde/sde_encoder.c +6 −0 Original line number Diff line number Diff line Loading @@ -1847,6 +1847,12 @@ static void sde_encoder_frame_done_callback( struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); unsigned int i; if (!sde_enc->frame_busy_mask[0]) { /* suppress frame_done without waiter, likely autorefresh */ SDE_EVT32(DRMID(drm_enc), event, ready_phys->intf_idx); return; } /* One of the physical encoders has become idle */ for (i = 0; i < sde_enc->num_phys_encs; i++) if (sde_enc->phys_encs[i] == ready_phys) { Loading drivers/gpu/drm/msm/sde/sde_encoder_phys.h +17 −0 Original line number Diff line number Diff line Loading @@ -172,6 +172,8 @@ struct sde_encoder_phys_ops { * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel * @INTR_IDX_RDPTR: Readpointer done unterrupt for cmd mode panel * @INTR_IDX_AUTOREFRESH_DONE: Autorefresh done for cmd mode panel meaning * autorefresh has triggered a double buffer flip */ enum sde_intr_idx { INTR_IDX_VSYNC, Loading @@ -179,6 +181,7 @@ enum sde_intr_idx { INTR_IDX_UNDERRUN, INTR_IDX_CTL_START, INTR_IDX_RDPTR, INTR_IDX_AUTOREFRESH_DONE, INTR_IDX_MAX, }; Loading Loading @@ -283,6 +286,18 @@ struct sde_encoder_phys_vid { u64 rot_prefill_line; }; /** * struct sde_encoder_phys_cmd_autorefresh - autorefresh state tracking * @cfg: current active autorefresh configuration * @kickoff_cnt: atomic count tracking autorefresh done irq kickoffs pending * @kickoff_wq: wait queue for waiting on autorefresh done irq */ struct sde_encoder_phys_cmd_autorefresh { struct sde_hw_autorefresh cfg; atomic_t kickoff_cnt; wait_queue_head_t kickoff_wq; }; /** * struct sde_encoder_phys_cmd - sub-class of sde_encoder_phys to handle command * mode specific operations Loading @@ -292,12 +307,14 @@ struct sde_encoder_phys_vid { * @serialize_wait4pp: serialize wait4pp feature waits for pp_done interrupt * after ctl_start instead of before next frame kickoff * @pp_timeout_report_cnt: number of pingpong done irq timeout errors * @autorefresh: autorefresh feature state */ struct sde_encoder_phys_cmd { struct sde_encoder_phys base; int stream_sel; bool serialize_wait4pp; int pp_timeout_report_cnt; struct sde_encoder_phys_cmd_autorefresh autorefresh; }; /** Loading drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +358 −6 Original line number Diff line number Diff line Loading @@ -44,6 +44,16 @@ #define DEFAULT_TEARCHECK_SYNC_THRESH_START 4 #define DEFAULT_TEARCHECK_SYNC_THRESH_CONTINUE 4 #define SDE_ENC_WR_PTR_START_TIMEOUT_US 20000 static inline int _sde_encoder_phys_cmd_get_idle_timeout( struct sde_encoder_phys_cmd *cmd_enc) { return cmd_enc->autorefresh.cfg.frame_count ? cmd_enc->autorefresh.cfg.frame_count * KICKOFF_TIMEOUT_MS : KICKOFF_TIMEOUT_MS; } static inline bool sde_encoder_phys_cmd_is_master( struct sde_encoder_phys *phys_enc) { Loading @@ -60,6 +70,52 @@ static bool sde_encoder_phys_cmd_mode_fixup( return true; } static uint64_t _sde_encoder_phys_cmd_get_autorefresh_property( struct sde_encoder_phys *phys_enc) { struct drm_connector *conn = phys_enc->connector; if (!conn || !conn->state) return 0; return sde_connector_get_property(conn->state, CONNECTOR_PROP_AUTOREFRESH); } static void _sde_encoder_phys_cmd_config_autorefresh( struct sde_encoder_phys *phys_enc, u32 new_frame_count) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); struct sde_hw_pingpong *hw_pp = phys_enc->hw_pp; struct drm_connector *conn = phys_enc->connector; struct sde_hw_autorefresh *cfg_cur, cfg_nxt; if (!conn || !conn->state || !hw_pp) return; cfg_cur = &cmd_enc->autorefresh.cfg; /* autorefresh property value should be validated already */ memset(&cfg_nxt, 0, sizeof(cfg_nxt)); cfg_nxt.frame_count = new_frame_count; cfg_nxt.enable = (cfg_nxt.frame_count != 0); SDE_DEBUG_CMDENC(cmd_enc, "autorefresh state %d->%d framecount %d\n", cfg_cur->enable, cfg_nxt.enable, cfg_nxt.frame_count); SDE_EVT32(DRMID(phys_enc->parent), hw_pp->idx, cfg_cur->enable, cfg_nxt.enable, cfg_nxt.frame_count); /* only proceed on state changes */ if (cfg_nxt.enable == cfg_cur->enable) return; memcpy(cfg_cur, &cfg_nxt, sizeof(*cfg_cur)); if (hw_pp->ops.setup_autorefresh) hw_pp->ops.setup_autorefresh(hw_pp, cfg_cur); } static void _sde_encoder_phys_cmd_update_flush_mask( struct sde_encoder_phys *phys_enc) { Loading Loading @@ -124,6 +180,29 @@ static void sde_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx) wake_up_all(&phys_enc->pending_kickoff_wq); } static void sde_encoder_phys_cmd_autorefresh_done_irq(void *arg, int irq_idx) { struct sde_encoder_phys *phys_enc = arg; struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); unsigned long lock_flags; int new_cnt; if (!cmd_enc) return; phys_enc = &cmd_enc->base; spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); new_cnt = atomic_add_unless(&cmd_enc->autorefresh.kickoff_cnt, -1, 0); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); SDE_EVT32_IRQ(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, new_cnt); /* Signal any waiting atomic commit thread */ wake_up_all(&cmd_enc->autorefresh.kickoff_wq); } static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx) { struct sde_encoder_phys *phys_enc = arg; Loading Loading @@ -190,6 +269,10 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx( irq = &phys_enc->irq[INTR_IDX_UNDERRUN]; irq->hw_idx = phys_enc->intf_idx; irq->irq_idx = -EINVAL; irq = &phys_enc->irq[INTR_IDX_AUTOREFRESH_DONE]; irq->hw_idx = phys_enc->hw_pp->idx; irq->irq_idx = -EINVAL; } static void sde_encoder_phys_cmd_mode_set( Loading Loading @@ -302,6 +385,74 @@ static bool _sde_encoder_phys_is_ppsplit_slave( phys_enc->split_role == ENC_ROLE_SLAVE; } static int _sde_encoder_phys_cmd_poll_write_pointer_started( struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); struct sde_hw_pingpong *hw_pp = phys_enc->hw_pp; struct sde_hw_pp_vsync_info info; u32 timeout_us = SDE_ENC_WR_PTR_START_TIMEOUT_US; int ret; if (!hw_pp || !hw_pp->ops.get_vsync_info || !hw_pp->ops.poll_timeout_wr_ptr) return 0; ret = hw_pp->ops.get_vsync_info(hw_pp, &info); if (ret) return ret; SDE_DEBUG_CMDENC(cmd_enc, "pp:%d rd_ptr %d wr_ptr %d\n", phys_enc->hw_pp->idx - PINGPONG_0, info.rd_ptr_line_count, info.wr_ptr_line_count); SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, info.wr_ptr_line_count); ret = hw_pp->ops.poll_timeout_wr_ptr(hw_pp, timeout_us); if (ret) { SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, timeout_us, ret); SDE_DBG_DUMP("sde", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl", "dsi1_phy", "vbif_rt", "dbg_bus", "vbif_dbg_bus", "panic"); } return ret; } static bool _sde_encoder_phys_cmd_is_ongoing_pptx( struct sde_encoder_phys *phys_enc) { struct sde_hw_pingpong *hw_pp; struct sde_hw_pp_vsync_info info; if (!phys_enc) return false; hw_pp = phys_enc->hw_pp; if (!hw_pp || !hw_pp->ops.get_vsync_info) return false; hw_pp->ops.get_vsync_info(hw_pp, &info); SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, atomic_read(&phys_enc->pending_kickoff_cnt), info.wr_ptr_line_count, phys_enc->cached_mode.vdisplay); if (info.wr_ptr_line_count > 0 && info.wr_ptr_line_count < phys_enc->cached_mode.vdisplay) return true; return false; } static int _sde_encoder_phys_cmd_wait_for_idle( struct sde_encoder_phys *phys_enc) { Loading Loading @@ -333,6 +484,42 @@ static int _sde_encoder_phys_cmd_wait_for_idle( return ret; } static int _sde_encoder_phys_cmd_wait_for_autorefresh_done( struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); struct sde_encoder_wait_info wait_info; int ret = 0; if (!phys_enc) { SDE_ERROR("invalid encoder\n"); return -EINVAL; } /* only master deals with autorefresh */ if (!sde_encoder_phys_cmd_is_master(phys_enc)) return 0; wait_info.wq = &cmd_enc->autorefresh.kickoff_wq; wait_info.atomic_cnt = &cmd_enc->autorefresh.kickoff_cnt; wait_info.timeout_ms = _sde_encoder_phys_cmd_get_idle_timeout(cmd_enc); /* wait for autorefresh kickoff to start */ ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_AUTOREFRESH_DONE, &wait_info); /* double check that kickoff has started by reading write ptr reg */ if (!ret) ret = _sde_encoder_phys_cmd_poll_write_pointer_started( phys_enc); else sde_encoder_helper_report_irq_timeout(phys_enc, INTR_IDX_AUTOREFRESH_DONE); return ret; } static int sde_encoder_phys_cmd_control_vblank_irq( struct sde_encoder_phys *phys_enc, bool enable) Loading Loading @@ -387,14 +574,20 @@ void sde_encoder_phys_cmd_irq_control(struct sde_encoder_phys *phys_enc, sde_encoder_helper_register_irq(phys_enc, INTR_IDX_UNDERRUN); sde_encoder_phys_cmd_control_vblank_irq(phys_enc, true); if (sde_encoder_phys_cmd_is_master(phys_enc)) if (sde_encoder_phys_cmd_is_master(phys_enc)) { sde_encoder_helper_register_irq(phys_enc, INTR_IDX_CTL_START); } else { sde_encoder_helper_register_irq(phys_enc, INTR_IDX_AUTOREFRESH_DONE); } if (sde_encoder_phys_cmd_is_master(phys_enc)) } else { if (sde_encoder_phys_cmd_is_master(phys_enc)) { sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_CTL_START); sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_AUTOREFRESH_DONE); } sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_UNDERRUN); sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false); Loading Loading @@ -445,7 +638,9 @@ static void sde_encoder_phys_cmd_tearcheck_config( } tc_cfg.vsync_count = vsync_hz / (mode->vtotal * mode->vrefresh); tc_cfg.hw_vsync_mode = 1; /* enable external TE after kickoff to avoid premature autorefresh */ tc_cfg.hw_vsync_mode = 0; /* * By setting sync_cfg_height to near max register value, we essentially Loading Loading @@ -561,6 +756,41 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc) phys_enc->enable_state = SDE_ENC_ENABLED; } static bool _sde_encoder_phys_cmd_is_autorefresh_enabled( struct sde_encoder_phys *phys_enc) { struct sde_hw_pingpong *hw_pp; struct sde_hw_autorefresh cfg; int ret; if (!phys_enc || !phys_enc->hw_pp) return 0; if (!sde_encoder_phys_cmd_is_master(phys_enc)) return 0; hw_pp = phys_enc->hw_pp; if (!hw_pp->ops.get_autorefresh) return 0; ret = hw_pp->ops.get_autorefresh(hw_pp, &cfg); if (ret) return 0; return cfg.enable; } static void _sde_encoder_phys_cmd_connect_te( struct sde_encoder_phys *phys_enc, bool enable) { if (!phys_enc || !phys_enc->hw_pp || !phys_enc->hw_pp->ops.connect_external_te) return; SDE_EVT32(DRMID(phys_enc->parent), enable); phys_enc->hw_pp->ops.connect_external_te(phys_enc->hw_pp, enable); } static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = Loading Loading @@ -638,7 +868,10 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff( return; } SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0); SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0); SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, atomic_read(&phys_enc->pending_kickoff_cnt), atomic_read(&cmd_enc->autorefresh.kickoff_cnt)); /* * Mark kickoff request as outstanding. If there are more than one, Loading @@ -652,6 +885,10 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff( phys_enc->hw_pp->idx - PINGPONG_0); SDE_ERROR("failed wait_for_idle: %d\n", ret); } SDE_DEBUG_CMDENC(cmd_enc, "pp:%d pending_cnt %d\n", phys_enc->hw_pp->idx - PINGPONG_0, atomic_read(&phys_enc->pending_kickoff_cnt)); } static int _sde_encoder_phys_cmd_wait_for_ctl_start( Loading Loading @@ -722,6 +959,10 @@ static int sde_encoder_phys_cmd_wait_for_commit_done( if (sde_encoder_phys_cmd_is_master(phys_enc)) rc = _sde_encoder_phys_cmd_wait_for_ctl_start(phys_enc); if (!rc && sde_encoder_phys_cmd_is_master(phys_enc) && cmd_enc->autorefresh.cfg.enable) rc = _sde_encoder_phys_cmd_wait_for_autorefresh_done(phys_enc); /* required for both controllers */ if (!rc && cmd_enc->serialize_wait4pp) sde_encoder_phys_cmd_prepare_for_kickoff(phys_enc, NULL); Loading Loading @@ -765,6 +1006,108 @@ static void sde_encoder_phys_cmd_update_split_role( static void sde_encoder_phys_cmd_prepare_commit( struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); if (!phys_enc) return; if (sde_encoder_phys_cmd_is_master(phys_enc)) { unsigned long lock_flags; SDE_EVT32(DRMID(phys_enc->parent), phys_enc->intf_idx - INTF_0, cmd_enc->autorefresh.cfg.enable); if (!_sde_encoder_phys_cmd_is_autorefresh_enabled(phys_enc)) return; /** * Autorefresh must be disabled carefully: * - Must disable while there is no ongoing transmission * - Receiving a TE will trigger the next Autorefresh TX * - Only safe to disable Autorefresh between PPDone and TE * - However, that is a small time window * - Disabling External TE gives large safe window, assuming * internally generated TE is set to a large counter value * * If Autorefresh is active: * 1. Disable external TE * - TE will run on an SDE counter set to large value (~200ms) * * 2. Check for ongoing TX * - If ongoing TX, set pending_kickoff_cnt if not set already * - We don't want to wait for a ppdone that will never * arrive, so verify ongoing TX * * 3. Wait for TX to Complete * - Wait for PPDone pending count to reach 0 * * 4. Leave Autorefresh Disabled * - Assume disable of Autorefresh since it is now safe * - Can now safely Disable Encoder, do debug printing, etc. * without worrying that Autorefresh will kickoff */ spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); /* disable external TE to prevent next autorefresh */ _sde_encoder_phys_cmd_connect_te(phys_enc, false); /* verify that we disabled TE during outstanding TX */ if (_sde_encoder_phys_cmd_is_ongoing_pptx(phys_enc)) atomic_add_unless(&phys_enc->pending_kickoff_cnt, 1, 1); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); /* wait for ppdone if necessary due to catching ongoing TX */ if (_sde_encoder_phys_cmd_wait_for_idle(phys_enc)) SDE_ERROR_CMDENC(cmd_enc, "pp:%d kickoff timed out\n", phys_enc->hw_pp->idx - PINGPONG_0); /* * not strictly necessary for kickoff, but simplifies disable * callflow since our disable is split across multiple phys_encs */ _sde_encoder_phys_cmd_config_autorefresh(phys_enc, 0); SDE_DEBUG_CMDENC(cmd_enc, "disabled autorefresh & ext TE\n"); } } static void sde_encoder_phys_cmd_handle_post_kickoff( struct sde_encoder_phys *phys_enc) { if (!phys_enc) return; /** * re-enable external TE, either for the first time after enabling * or if disabled for Autorefresh */ _sde_encoder_phys_cmd_connect_te(phys_enc, true); } static void sde_encoder_phys_cmd_trigger_start( struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); u32 frame_cnt; if (!phys_enc) return; /* we don't issue CTL_START when using autorefresh */ frame_cnt = _sde_encoder_phys_cmd_get_autorefresh_property(phys_enc); if (frame_cnt) { _sde_encoder_phys_cmd_config_autorefresh(phys_enc, frame_cnt); atomic_inc(&cmd_enc->autorefresh.kickoff_cnt); } else { sde_encoder_helper_trigger_start(phys_enc); } } static void sde_encoder_phys_cmd_init_ops( Loading @@ -782,7 +1125,8 @@ 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->wait_for_tx_complete = sde_encoder_phys_cmd_wait_for_tx_complete; ops->trigger_start = sde_encoder_helper_trigger_start; ops->handle_post_kickoff = sde_encoder_phys_cmd_handle_post_kickoff; ops->trigger_start = sde_encoder_phys_cmd_trigger_start; ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush; ops->hw_reset = sde_encoder_helper_hw_reset; ops->irq_control = sde_encoder_phys_cmd_irq_control; Loading Loading @@ -860,10 +1204,18 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init( irq->intr_idx = INTR_IDX_UNDERRUN; irq->cb.func = sde_encoder_phys_cmd_underrun_irq; irq = &phys_enc->irq[INTR_IDX_AUTOREFRESH_DONE]; irq->name = "autorefresh_done"; irq->intr_type = SDE_IRQ_TYPE_PING_PONG_AUTO_REF; irq->intr_idx = INTR_IDX_AUTOREFRESH_DONE; irq->cb.func = sde_encoder_phys_cmd_autorefresh_done_irq; atomic_set(&phys_enc->vblank_refcount, 0); atomic_set(&phys_enc->pending_kickoff_cnt, 0); atomic_set(&phys_enc->pending_ctlstart_cnt, 0); init_waitqueue_head(&phys_enc->pending_kickoff_wq); atomic_set(&cmd_enc->autorefresh.kickoff_cnt, 0); init_waitqueue_head(&cmd_enc->autorefresh.kickoff_wq); SDE_DEBUG_CMDENC(cmd_enc, "created\n"); Loading Loading
drivers/gpu/drm/msm/sde/sde_encoder.c +6 −0 Original line number Diff line number Diff line Loading @@ -1847,6 +1847,12 @@ static void sde_encoder_frame_done_callback( struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc); unsigned int i; if (!sde_enc->frame_busy_mask[0]) { /* suppress frame_done without waiter, likely autorefresh */ SDE_EVT32(DRMID(drm_enc), event, ready_phys->intf_idx); return; } /* One of the physical encoders has become idle */ for (i = 0; i < sde_enc->num_phys_encs; i++) if (sde_enc->phys_encs[i] == ready_phys) { Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys.h +17 −0 Original line number Diff line number Diff line Loading @@ -172,6 +172,8 @@ struct sde_encoder_phys_ops { * @INTR_IDX_PINGPONG: Pingpong done unterrupt for cmd mode panel * @INTR_IDX_UNDERRUN: Underrun unterrupt for video and cmd mode panel * @INTR_IDX_RDPTR: Readpointer done unterrupt for cmd mode panel * @INTR_IDX_AUTOREFRESH_DONE: Autorefresh done for cmd mode panel meaning * autorefresh has triggered a double buffer flip */ enum sde_intr_idx { INTR_IDX_VSYNC, Loading @@ -179,6 +181,7 @@ enum sde_intr_idx { INTR_IDX_UNDERRUN, INTR_IDX_CTL_START, INTR_IDX_RDPTR, INTR_IDX_AUTOREFRESH_DONE, INTR_IDX_MAX, }; Loading Loading @@ -283,6 +286,18 @@ struct sde_encoder_phys_vid { u64 rot_prefill_line; }; /** * struct sde_encoder_phys_cmd_autorefresh - autorefresh state tracking * @cfg: current active autorefresh configuration * @kickoff_cnt: atomic count tracking autorefresh done irq kickoffs pending * @kickoff_wq: wait queue for waiting on autorefresh done irq */ struct sde_encoder_phys_cmd_autorefresh { struct sde_hw_autorefresh cfg; atomic_t kickoff_cnt; wait_queue_head_t kickoff_wq; }; /** * struct sde_encoder_phys_cmd - sub-class of sde_encoder_phys to handle command * mode specific operations Loading @@ -292,12 +307,14 @@ struct sde_encoder_phys_vid { * @serialize_wait4pp: serialize wait4pp feature waits for pp_done interrupt * after ctl_start instead of before next frame kickoff * @pp_timeout_report_cnt: number of pingpong done irq timeout errors * @autorefresh: autorefresh feature state */ struct sde_encoder_phys_cmd { struct sde_encoder_phys base; int stream_sel; bool serialize_wait4pp; int pp_timeout_report_cnt; struct sde_encoder_phys_cmd_autorefresh autorefresh; }; /** Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +358 −6 Original line number Diff line number Diff line Loading @@ -44,6 +44,16 @@ #define DEFAULT_TEARCHECK_SYNC_THRESH_START 4 #define DEFAULT_TEARCHECK_SYNC_THRESH_CONTINUE 4 #define SDE_ENC_WR_PTR_START_TIMEOUT_US 20000 static inline int _sde_encoder_phys_cmd_get_idle_timeout( struct sde_encoder_phys_cmd *cmd_enc) { return cmd_enc->autorefresh.cfg.frame_count ? cmd_enc->autorefresh.cfg.frame_count * KICKOFF_TIMEOUT_MS : KICKOFF_TIMEOUT_MS; } static inline bool sde_encoder_phys_cmd_is_master( struct sde_encoder_phys *phys_enc) { Loading @@ -60,6 +70,52 @@ static bool sde_encoder_phys_cmd_mode_fixup( return true; } static uint64_t _sde_encoder_phys_cmd_get_autorefresh_property( struct sde_encoder_phys *phys_enc) { struct drm_connector *conn = phys_enc->connector; if (!conn || !conn->state) return 0; return sde_connector_get_property(conn->state, CONNECTOR_PROP_AUTOREFRESH); } static void _sde_encoder_phys_cmd_config_autorefresh( struct sde_encoder_phys *phys_enc, u32 new_frame_count) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); struct sde_hw_pingpong *hw_pp = phys_enc->hw_pp; struct drm_connector *conn = phys_enc->connector; struct sde_hw_autorefresh *cfg_cur, cfg_nxt; if (!conn || !conn->state || !hw_pp) return; cfg_cur = &cmd_enc->autorefresh.cfg; /* autorefresh property value should be validated already */ memset(&cfg_nxt, 0, sizeof(cfg_nxt)); cfg_nxt.frame_count = new_frame_count; cfg_nxt.enable = (cfg_nxt.frame_count != 0); SDE_DEBUG_CMDENC(cmd_enc, "autorefresh state %d->%d framecount %d\n", cfg_cur->enable, cfg_nxt.enable, cfg_nxt.frame_count); SDE_EVT32(DRMID(phys_enc->parent), hw_pp->idx, cfg_cur->enable, cfg_nxt.enable, cfg_nxt.frame_count); /* only proceed on state changes */ if (cfg_nxt.enable == cfg_cur->enable) return; memcpy(cfg_cur, &cfg_nxt, sizeof(*cfg_cur)); if (hw_pp->ops.setup_autorefresh) hw_pp->ops.setup_autorefresh(hw_pp, cfg_cur); } static void _sde_encoder_phys_cmd_update_flush_mask( struct sde_encoder_phys *phys_enc) { Loading Loading @@ -124,6 +180,29 @@ static void sde_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx) wake_up_all(&phys_enc->pending_kickoff_wq); } static void sde_encoder_phys_cmd_autorefresh_done_irq(void *arg, int irq_idx) { struct sde_encoder_phys *phys_enc = arg; struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); unsigned long lock_flags; int new_cnt; if (!cmd_enc) return; phys_enc = &cmd_enc->base; spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); new_cnt = atomic_add_unless(&cmd_enc->autorefresh.kickoff_cnt, -1, 0); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); SDE_EVT32_IRQ(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, new_cnt); /* Signal any waiting atomic commit thread */ wake_up_all(&cmd_enc->autorefresh.kickoff_wq); } static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx) { struct sde_encoder_phys *phys_enc = arg; Loading Loading @@ -190,6 +269,10 @@ static void _sde_encoder_phys_cmd_setup_irq_hw_idx( irq = &phys_enc->irq[INTR_IDX_UNDERRUN]; irq->hw_idx = phys_enc->intf_idx; irq->irq_idx = -EINVAL; irq = &phys_enc->irq[INTR_IDX_AUTOREFRESH_DONE]; irq->hw_idx = phys_enc->hw_pp->idx; irq->irq_idx = -EINVAL; } static void sde_encoder_phys_cmd_mode_set( Loading Loading @@ -302,6 +385,74 @@ static bool _sde_encoder_phys_is_ppsplit_slave( phys_enc->split_role == ENC_ROLE_SLAVE; } static int _sde_encoder_phys_cmd_poll_write_pointer_started( struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); struct sde_hw_pingpong *hw_pp = phys_enc->hw_pp; struct sde_hw_pp_vsync_info info; u32 timeout_us = SDE_ENC_WR_PTR_START_TIMEOUT_US; int ret; if (!hw_pp || !hw_pp->ops.get_vsync_info || !hw_pp->ops.poll_timeout_wr_ptr) return 0; ret = hw_pp->ops.get_vsync_info(hw_pp, &info); if (ret) return ret; SDE_DEBUG_CMDENC(cmd_enc, "pp:%d rd_ptr %d wr_ptr %d\n", phys_enc->hw_pp->idx - PINGPONG_0, info.rd_ptr_line_count, info.wr_ptr_line_count); SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, info.wr_ptr_line_count); ret = hw_pp->ops.poll_timeout_wr_ptr(hw_pp, timeout_us); if (ret) { SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, timeout_us, ret); SDE_DBG_DUMP("sde", "dsi0_ctrl", "dsi0_phy", "dsi1_ctrl", "dsi1_phy", "vbif_rt", "dbg_bus", "vbif_dbg_bus", "panic"); } return ret; } static bool _sde_encoder_phys_cmd_is_ongoing_pptx( struct sde_encoder_phys *phys_enc) { struct sde_hw_pingpong *hw_pp; struct sde_hw_pp_vsync_info info; if (!phys_enc) return false; hw_pp = phys_enc->hw_pp; if (!hw_pp || !hw_pp->ops.get_vsync_info) return false; hw_pp->ops.get_vsync_info(hw_pp, &info); SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, atomic_read(&phys_enc->pending_kickoff_cnt), info.wr_ptr_line_count, phys_enc->cached_mode.vdisplay); if (info.wr_ptr_line_count > 0 && info.wr_ptr_line_count < phys_enc->cached_mode.vdisplay) return true; return false; } static int _sde_encoder_phys_cmd_wait_for_idle( struct sde_encoder_phys *phys_enc) { Loading Loading @@ -333,6 +484,42 @@ static int _sde_encoder_phys_cmd_wait_for_idle( return ret; } static int _sde_encoder_phys_cmd_wait_for_autorefresh_done( struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); struct sde_encoder_wait_info wait_info; int ret = 0; if (!phys_enc) { SDE_ERROR("invalid encoder\n"); return -EINVAL; } /* only master deals with autorefresh */ if (!sde_encoder_phys_cmd_is_master(phys_enc)) return 0; wait_info.wq = &cmd_enc->autorefresh.kickoff_wq; wait_info.atomic_cnt = &cmd_enc->autorefresh.kickoff_cnt; wait_info.timeout_ms = _sde_encoder_phys_cmd_get_idle_timeout(cmd_enc); /* wait for autorefresh kickoff to start */ ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_AUTOREFRESH_DONE, &wait_info); /* double check that kickoff has started by reading write ptr reg */ if (!ret) ret = _sde_encoder_phys_cmd_poll_write_pointer_started( phys_enc); else sde_encoder_helper_report_irq_timeout(phys_enc, INTR_IDX_AUTOREFRESH_DONE); return ret; } static int sde_encoder_phys_cmd_control_vblank_irq( struct sde_encoder_phys *phys_enc, bool enable) Loading Loading @@ -387,14 +574,20 @@ void sde_encoder_phys_cmd_irq_control(struct sde_encoder_phys *phys_enc, sde_encoder_helper_register_irq(phys_enc, INTR_IDX_UNDERRUN); sde_encoder_phys_cmd_control_vblank_irq(phys_enc, true); if (sde_encoder_phys_cmd_is_master(phys_enc)) if (sde_encoder_phys_cmd_is_master(phys_enc)) { sde_encoder_helper_register_irq(phys_enc, INTR_IDX_CTL_START); } else { sde_encoder_helper_register_irq(phys_enc, INTR_IDX_AUTOREFRESH_DONE); } if (sde_encoder_phys_cmd_is_master(phys_enc)) } else { if (sde_encoder_phys_cmd_is_master(phys_enc)) { sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_CTL_START); sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_AUTOREFRESH_DONE); } sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_UNDERRUN); sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false); Loading Loading @@ -445,7 +638,9 @@ static void sde_encoder_phys_cmd_tearcheck_config( } tc_cfg.vsync_count = vsync_hz / (mode->vtotal * mode->vrefresh); tc_cfg.hw_vsync_mode = 1; /* enable external TE after kickoff to avoid premature autorefresh */ tc_cfg.hw_vsync_mode = 0; /* * By setting sync_cfg_height to near max register value, we essentially Loading Loading @@ -561,6 +756,41 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc) phys_enc->enable_state = SDE_ENC_ENABLED; } static bool _sde_encoder_phys_cmd_is_autorefresh_enabled( struct sde_encoder_phys *phys_enc) { struct sde_hw_pingpong *hw_pp; struct sde_hw_autorefresh cfg; int ret; if (!phys_enc || !phys_enc->hw_pp) return 0; if (!sde_encoder_phys_cmd_is_master(phys_enc)) return 0; hw_pp = phys_enc->hw_pp; if (!hw_pp->ops.get_autorefresh) return 0; ret = hw_pp->ops.get_autorefresh(hw_pp, &cfg); if (ret) return 0; return cfg.enable; } static void _sde_encoder_phys_cmd_connect_te( struct sde_encoder_phys *phys_enc, bool enable) { if (!phys_enc || !phys_enc->hw_pp || !phys_enc->hw_pp->ops.connect_external_te) return; SDE_EVT32(DRMID(phys_enc->parent), enable); phys_enc->hw_pp->ops.connect_external_te(phys_enc->hw_pp, enable); } static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = Loading Loading @@ -638,7 +868,10 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff( return; } SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0); SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0); SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0, atomic_read(&phys_enc->pending_kickoff_cnt), atomic_read(&cmd_enc->autorefresh.kickoff_cnt)); /* * Mark kickoff request as outstanding. If there are more than one, Loading @@ -652,6 +885,10 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff( phys_enc->hw_pp->idx - PINGPONG_0); SDE_ERROR("failed wait_for_idle: %d\n", ret); } SDE_DEBUG_CMDENC(cmd_enc, "pp:%d pending_cnt %d\n", phys_enc->hw_pp->idx - PINGPONG_0, atomic_read(&phys_enc->pending_kickoff_cnt)); } static int _sde_encoder_phys_cmd_wait_for_ctl_start( Loading Loading @@ -722,6 +959,10 @@ static int sde_encoder_phys_cmd_wait_for_commit_done( if (sde_encoder_phys_cmd_is_master(phys_enc)) rc = _sde_encoder_phys_cmd_wait_for_ctl_start(phys_enc); if (!rc && sde_encoder_phys_cmd_is_master(phys_enc) && cmd_enc->autorefresh.cfg.enable) rc = _sde_encoder_phys_cmd_wait_for_autorefresh_done(phys_enc); /* required for both controllers */ if (!rc && cmd_enc->serialize_wait4pp) sde_encoder_phys_cmd_prepare_for_kickoff(phys_enc, NULL); Loading Loading @@ -765,6 +1006,108 @@ static void sde_encoder_phys_cmd_update_split_role( static void sde_encoder_phys_cmd_prepare_commit( struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); if (!phys_enc) return; if (sde_encoder_phys_cmd_is_master(phys_enc)) { unsigned long lock_flags; SDE_EVT32(DRMID(phys_enc->parent), phys_enc->intf_idx - INTF_0, cmd_enc->autorefresh.cfg.enable); if (!_sde_encoder_phys_cmd_is_autorefresh_enabled(phys_enc)) return; /** * Autorefresh must be disabled carefully: * - Must disable while there is no ongoing transmission * - Receiving a TE will trigger the next Autorefresh TX * - Only safe to disable Autorefresh between PPDone and TE * - However, that is a small time window * - Disabling External TE gives large safe window, assuming * internally generated TE is set to a large counter value * * If Autorefresh is active: * 1. Disable external TE * - TE will run on an SDE counter set to large value (~200ms) * * 2. Check for ongoing TX * - If ongoing TX, set pending_kickoff_cnt if not set already * - We don't want to wait for a ppdone that will never * arrive, so verify ongoing TX * * 3. Wait for TX to Complete * - Wait for PPDone pending count to reach 0 * * 4. Leave Autorefresh Disabled * - Assume disable of Autorefresh since it is now safe * - Can now safely Disable Encoder, do debug printing, etc. * without worrying that Autorefresh will kickoff */ spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); /* disable external TE to prevent next autorefresh */ _sde_encoder_phys_cmd_connect_te(phys_enc, false); /* verify that we disabled TE during outstanding TX */ if (_sde_encoder_phys_cmd_is_ongoing_pptx(phys_enc)) atomic_add_unless(&phys_enc->pending_kickoff_cnt, 1, 1); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); /* wait for ppdone if necessary due to catching ongoing TX */ if (_sde_encoder_phys_cmd_wait_for_idle(phys_enc)) SDE_ERROR_CMDENC(cmd_enc, "pp:%d kickoff timed out\n", phys_enc->hw_pp->idx - PINGPONG_0); /* * not strictly necessary for kickoff, but simplifies disable * callflow since our disable is split across multiple phys_encs */ _sde_encoder_phys_cmd_config_autorefresh(phys_enc, 0); SDE_DEBUG_CMDENC(cmd_enc, "disabled autorefresh & ext TE\n"); } } static void sde_encoder_phys_cmd_handle_post_kickoff( struct sde_encoder_phys *phys_enc) { if (!phys_enc) return; /** * re-enable external TE, either for the first time after enabling * or if disabled for Autorefresh */ _sde_encoder_phys_cmd_connect_te(phys_enc, true); } static void sde_encoder_phys_cmd_trigger_start( struct sde_encoder_phys *phys_enc) { struct sde_encoder_phys_cmd *cmd_enc = to_sde_encoder_phys_cmd(phys_enc); u32 frame_cnt; if (!phys_enc) return; /* we don't issue CTL_START when using autorefresh */ frame_cnt = _sde_encoder_phys_cmd_get_autorefresh_property(phys_enc); if (frame_cnt) { _sde_encoder_phys_cmd_config_autorefresh(phys_enc, frame_cnt); atomic_inc(&cmd_enc->autorefresh.kickoff_cnt); } else { sde_encoder_helper_trigger_start(phys_enc); } } static void sde_encoder_phys_cmd_init_ops( Loading @@ -782,7 +1125,8 @@ 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->wait_for_tx_complete = sde_encoder_phys_cmd_wait_for_tx_complete; ops->trigger_start = sde_encoder_helper_trigger_start; ops->handle_post_kickoff = sde_encoder_phys_cmd_handle_post_kickoff; ops->trigger_start = sde_encoder_phys_cmd_trigger_start; ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush; ops->hw_reset = sde_encoder_helper_hw_reset; ops->irq_control = sde_encoder_phys_cmd_irq_control; Loading Loading @@ -860,10 +1204,18 @@ struct sde_encoder_phys *sde_encoder_phys_cmd_init( irq->intr_idx = INTR_IDX_UNDERRUN; irq->cb.func = sde_encoder_phys_cmd_underrun_irq; irq = &phys_enc->irq[INTR_IDX_AUTOREFRESH_DONE]; irq->name = "autorefresh_done"; irq->intr_type = SDE_IRQ_TYPE_PING_PONG_AUTO_REF; irq->intr_idx = INTR_IDX_AUTOREFRESH_DONE; irq->cb.func = sde_encoder_phys_cmd_autorefresh_done_irq; atomic_set(&phys_enc->vblank_refcount, 0); atomic_set(&phys_enc->pending_kickoff_cnt, 0); atomic_set(&phys_enc->pending_ctlstart_cnt, 0); init_waitqueue_head(&phys_enc->pending_kickoff_wq); atomic_set(&cmd_enc->autorefresh.kickoff_cnt, 0); init_waitqueue_head(&cmd_enc->autorefresh.kickoff_wq); SDE_DEBUG_CMDENC(cmd_enc, "created\n"); Loading