Loading drivers/gpu/drm/msm/sde/sde_crtc.c +17 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,16 @@ static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc) return to_sde_kms(priv->kms); } static inline struct drm_encoder *_sde_crtc_get_encoder(struct drm_crtc *crtc) { struct drm_encoder *enc; drm_for_each_encoder_mask(enc, crtc->dev, crtc->state->encoder_mask) return enc; return NULL; } static inline int _sde_crtc_power_enable(struct sde_crtc *sde_crtc, bool enable) { struct drm_crtc *crtc; Loading Loading @@ -3107,6 +3117,8 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc, { struct sde_crtc *sde_crtc; struct sde_crtc_state *cstate; struct drm_encoder *enc; struct sde_kms *sde_kms; if (!crtc || !state) { SDE_ERROR("invalid argument(s)\n"); Loading @@ -3115,9 +3127,14 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc, sde_crtc = to_sde_crtc(crtc); cstate = to_sde_crtc_state(state); enc = _sde_crtc_get_encoder(crtc); sde_kms = _sde_crtc_get_kms(crtc); SDE_DEBUG("crtc%d\n", crtc->base.id); if (sde_kms && enc) sde_rm_release(&sde_kms->rm, enc, true); __drm_atomic_helper_crtc_destroy_state(state); /* destroy value helper */ Loading drivers/gpu/drm/msm/sde/sde_encoder.c +1 −1 Original line number Diff line number Diff line Loading @@ -3287,7 +3287,7 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) SDE_DEBUG_ENC(sde_enc, "encoder disabled\n"); sde_rm_release(&sde_kms->rm, drm_enc); sde_rm_release(&sde_kms->rm, drm_enc, false); } void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc, Loading drivers/gpu/drm/msm/sde/sde_rm.c +289 −171 Original line number Diff line number Diff line Loading @@ -18,7 +18,8 @@ #include "sde_hw_dsc.h" #define RESERVED_BY_OTHER(h, r) \ ((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) (((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) ||\ ((h)->rsvp_nxt && ((h)->rsvp_nxt->enc_id != (r)->enc_id))) #define RM_RQ_LOCK(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_LOCK)) #define RM_RQ_CLEAR(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_CLEAR)) Loading Loading @@ -390,72 +391,11 @@ static int _sde_rm_hw_blk_create( return 0; } int sde_rm_init(struct sde_rm *rm, static int _sde_rm_hw_blk_create_new(struct sde_rm *rm, struct sde_mdss_cfg *cat, void __iomem *mmio, struct drm_device *dev) void __iomem *mmio) { int i, rc = 0; enum sde_hw_blk_type type; if (!rm || !cat || !mmio || !dev) { SDE_ERROR("invalid kms\n"); return -EINVAL; } /* Clear, setup lists */ memset(rm, 0, sizeof(*rm)); mutex_init(&rm->rm_lock); INIT_LIST_HEAD(&rm->rsvps); for (type = 0; type < SDE_HW_BLK_MAX; type++) INIT_LIST_HEAD(&rm->hw_blks[type]); rm->dev = dev; if (IS_SDE_CTL_REV_100(cat->ctl_rev)) rm->topology_tbl = g_ctl_ver_1_top_table; else rm->topology_tbl = g_top_table; /* Some of the sub-blocks require an mdptop to be created */ rm->hw_mdp = sde_hw_mdptop_init(MDP_TOP, mmio, cat); if (IS_ERR_OR_NULL(rm->hw_mdp)) { rc = PTR_ERR(rm->hw_mdp); rm->hw_mdp = NULL; SDE_ERROR("failed: mdp hw not available\n"); goto fail; } /* Interrogate HW catalog and create tracking items for hw blocks */ for (i = 0; i < cat->mixer_count; i++) { struct sde_lm_cfg *lm = &cat->mixer[i]; if (lm->pingpong == PINGPONG_MAX) { SDE_ERROR("mixer %d without pingpong\n", lm->id); goto fail; } rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_LM, cat->mixer[i].id, &cat->mixer[i]); if (rc) { SDE_ERROR("failed: lm hw not available\n"); goto fail; } if (!rm->lm_max_width) { rm->lm_max_width = lm->sblk->maxwidth; } else if (rm->lm_max_width != lm->sblk->maxwidth) { /* * Don't expect to have hw where lm max widths differ. * If found, take the min. */ SDE_ERROR("unsupported: lm maxwidth differs\n"); if (rm->lm_max_width > lm->sblk->maxwidth) rm->lm_max_width = lm->sblk->maxwidth; } } for (i = 0; i < cat->dspp_count; i++) { rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_DSPP, Loading Loading @@ -536,6 +476,79 @@ int sde_rm_init(struct sde_rm *rm, } } fail: return rc; } int sde_rm_init(struct sde_rm *rm, struct sde_mdss_cfg *cat, void __iomem *mmio, struct drm_device *dev) { int i, rc = 0; enum sde_hw_blk_type type; if (!rm || !cat || !mmio || !dev) { SDE_ERROR("invalid input params\n"); return -EINVAL; } /* Clear, setup lists */ memset(rm, 0, sizeof(*rm)); mutex_init(&rm->rm_lock); INIT_LIST_HEAD(&rm->rsvps); for (type = 0; type < SDE_HW_BLK_MAX; type++) INIT_LIST_HEAD(&rm->hw_blks[type]); rm->dev = dev; if (IS_SDE_CTL_REV_100(cat->ctl_rev)) rm->topology_tbl = g_ctl_ver_1_top_table; else rm->topology_tbl = g_top_table; /* Some of the sub-blocks require an mdptop to be created */ rm->hw_mdp = sde_hw_mdptop_init(MDP_TOP, mmio, cat); if (IS_ERR_OR_NULL(rm->hw_mdp)) { rc = PTR_ERR(rm->hw_mdp); rm->hw_mdp = NULL; SDE_ERROR("failed: mdp hw not available\n"); goto fail; } /* Interrogate HW catalog and create tracking items for hw blocks */ for (i = 0; i < cat->mixer_count; i++) { struct sde_lm_cfg *lm = &cat->mixer[i]; if (lm->pingpong == PINGPONG_MAX) { SDE_ERROR("mixer %d without pingpong\n", lm->id); goto fail; } rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_LM, cat->mixer[i].id, &cat->mixer[i]); if (rc) { SDE_ERROR("failed: lm hw not available\n"); goto fail; } if (!rm->lm_max_width) { rm->lm_max_width = lm->sblk->maxwidth; } else if (rm->lm_max_width != lm->sblk->maxwidth) { /* * Don't expect to have hw where lm max widths differ. * If found, take the min. */ SDE_ERROR("unsupported: lm maxwidth differs\n"); if (rm->lm_max_width > lm->sblk->maxwidth) rm->lm_max_width = lm->sblk->maxwidth; } } rc = _sde_rm_hw_blk_create_new(rm, cat, mmio); if (!rc) return 0; fail: Loading @@ -544,63 +557,18 @@ int sde_rm_init(struct sde_rm *rm, return rc; } /** * _sde_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets * proposed use case requirements, incl. hardwired dependent blocks like * pingpong, and dspp. * @rm: sde resource manager handle * @rsvp: reservation currently being created * @reqs: proposed use case requirements * @lm: proposed layer mixer, function checks if lm, and all other hardwired * blocks connected to the lm (pp, dspp) are available and appropriate * @dspp: output parameter, dspp block attached to the layer mixer. * NULL if dspp was not available, or not matching requirements. * @pp: output parameter, pingpong block attached to the layer mixer. * NULL if dspp was not available, or not matching requirements. * @primary_lm: if non-null, this function check if lm is compatible primary_lm * as well as satisfying all other requirements * @Return: true if lm matches all requirements, false otherwise */ static bool _sde_rm_check_lm_and_get_connected_blks( static bool _sde_rm_check_lm( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, struct sde_rm_requirements *reqs, const struct sde_lm_cfg *lm_cfg, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **dspp, struct sde_rm_hw_blk **ds, struct sde_rm_hw_blk **pp, struct sde_rm_hw_blk *primary_lm) struct sde_rm_hw_blk **pp) { const struct sde_lm_cfg *lm_cfg = to_sde_hw_mixer(lm->hw)->cap; const struct sde_pingpong_cfg *pp_cfg; struct sde_rm_hw_iter iter; bool is_valid_dspp, is_valid_ds, ret; u32 display_pref, cwb_pref; *dspp = NULL; *ds = NULL; *pp = NULL; display_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF); cwb_pref = lm_cfg->features & BIT(SDE_DISP_CWB_PREF); SDE_DEBUG("check lm %d: dspp %d ds %d pp %d disp_pref: %d cwb_pref%d\n", lm_cfg->id, lm_cfg->dspp, lm_cfg->ds, lm_cfg->pingpong, display_pref, cwb_pref); /* Check if this layer mixer is a peer of the proposed primary LM */ if (primary_lm) { const struct sde_lm_cfg *prim_lm_cfg = to_sde_hw_mixer(primary_lm->hw)->cap; if (!test_bit(lm_cfg->id, &prim_lm_cfg->lm_pair_mask)) { SDE_DEBUG("lm %d not peer of lm %d\n", lm_cfg->id, prim_lm_cfg->id); return false; } } /* bypass rest of the checks if LM for primary display is found */ if (!display_pref) { is_valid_dspp = (lm_cfg->dspp != DSPP_MAX) ? true : false; is_valid_ds = (lm_cfg->ds != DS_MAX) ? true : false; Loading @@ -625,30 +593,20 @@ static bool _sde_rm_check_lm_and_get_connected_blks( lm_cfg->id, (bool)(RM_RQ_DSPP(reqs)), lm_cfg->dspp, (bool)(RM_RQ_DS(reqs)), lm_cfg->ds); return ret; } /** * If CWB is enabled and LM is not CWB supported * then return false. */ if (RM_RQ_CWB(reqs) && !cwb_pref) { SDE_DEBUG("fail: cwb supported lm not allocated\n"); return false; return ret; } } else if (!(reqs->hw_res.is_primary && display_pref)) { SDE_DEBUG( "display preference is not met. is_primary: %d display_pref: %d\n", (int)reqs->hw_res.is_primary, (int)display_pref); return false; return true; } /* Already reserved? */ if (RESERVED_BY_OTHER(lm, rsvp)) { SDE_DEBUG("lm %d already reserved\n", lm_cfg->id); return false; } static bool _sde_rm_reserve_dspp( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, const struct sde_lm_cfg *lm_cfg, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **dspp) { struct sde_rm_hw_iter iter; if (lm_cfg->dspp != DSPP_MAX) { sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_DSPP); Loading @@ -672,6 +630,19 @@ static bool _sde_rm_check_lm_and_get_connected_blks( } } return true; } static bool _sde_rm_reserve_ds( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, const struct sde_lm_cfg *lm_cfg, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **ds) { struct sde_rm_hw_iter iter; if (lm_cfg->ds != DS_MAX) { sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_DS); while (_sde_rm_get_hw_locked(rm, &iter)) { Loading @@ -694,6 +665,22 @@ static bool _sde_rm_check_lm_and_get_connected_blks( } } return true; } static bool _sde_rm_reserve_pp( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, struct sde_rm_requirements *reqs, const struct sde_lm_cfg *lm_cfg, const struct sde_pingpong_cfg *pp_cfg, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **dspp, struct sde_rm_hw_blk **ds, struct sde_rm_hw_blk **pp) { struct sde_rm_hw_iter iter; sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_PINGPONG); while (_sde_rm_get_hw_locked(rm, &iter)) { if (iter.blk->id == lm_cfg->pingpong) { Loading Loading @@ -723,6 +710,107 @@ static bool _sde_rm_check_lm_and_get_connected_blks( *ds = NULL; return false; } return true; } /** * _sde_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets * proposed use case requirements, incl. hardwired dependent blocks like * pingpong, and dspp. * @rm: sde resource manager handle * @rsvp: reservation currently being created * @reqs: proposed use case requirements * @lm: proposed layer mixer, function checks if lm, and all other hardwired * blocks connected to the lm (pp, dspp) are available and appropriate * @dspp: output parameter, dspp block attached to the layer mixer. * NULL if dspp was not available, or not matching requirements. * @pp: output parameter, pingpong block attached to the layer mixer. * NULL if dspp was not available, or not matching requirements. * @primary_lm: if non-null, this function check if lm is compatible primary_lm * as well as satisfying all other requirements * @Return: true if lm matches all requirements, false otherwise */ static bool _sde_rm_check_lm_and_get_connected_blks( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, struct sde_rm_requirements *reqs, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **dspp, struct sde_rm_hw_blk **ds, struct sde_rm_hw_blk **pp, struct sde_rm_hw_blk *primary_lm) { const struct sde_lm_cfg *lm_cfg = to_sde_hw_mixer(lm->hw)->cap; const struct sde_pingpong_cfg *pp_cfg; bool ret; u32 display_pref, cwb_pref; *dspp = NULL; *ds = NULL; *pp = NULL; display_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF); cwb_pref = lm_cfg->features & BIT(SDE_DISP_CWB_PREF); SDE_DEBUG("check lm %d: dspp %d ds %d pp %d disp_pref: %d cwb_pref%d\n", lm_cfg->id, lm_cfg->dspp, lm_cfg->ds, lm_cfg->pingpong, display_pref, cwb_pref); /* Check if this layer mixer is a peer of the proposed primary LM */ if (primary_lm) { const struct sde_lm_cfg *prim_lm_cfg = to_sde_hw_mixer(primary_lm->hw)->cap; if (!test_bit(lm_cfg->id, &prim_lm_cfg->lm_pair_mask)) { SDE_DEBUG("lm %d not peer of lm %d\n", lm_cfg->id, prim_lm_cfg->id); return false; } } /* bypass rest of the checks if LM for primary display is found */ if (!display_pref) { /* Check lm for valid requirements */ ret = _sde_rm_check_lm(rm, rsvp, reqs, lm_cfg, lm, dspp, ds, pp); if (!ret) return ret; /** * If CWB is enabled and LM is not CWB supported * then return false. */ if (RM_RQ_CWB(reqs) && !cwb_pref) { SDE_DEBUG("fail: cwb supported lm not allocated\n"); return false; } } else if (!(reqs->hw_res.is_primary && display_pref)) { SDE_DEBUG( "display preference is not met. is_primary: %d display_pref: %d\n", (int)reqs->hw_res.is_primary, (int)display_pref); return false; } /* Already reserved? */ if (RESERVED_BY_OTHER(lm, rsvp)) { SDE_DEBUG("lm %d already reserved\n", lm_cfg->id); return false; } /* Reserve dspp */ ret = _sde_rm_reserve_dspp(rm, rsvp, lm_cfg, lm, dspp); if (!ret) return ret; /* Reserve ds */ ret = _sde_rm_reserve_ds(rm, rsvp, lm_cfg, lm, ds); if (!ret) return ret; /* Reserve pp */ ret = _sde_rm_reserve_pp(rm, rsvp, reqs, lm_cfg, pp_cfg, lm, dspp, ds, pp); if (!ret) return ret; return true; } Loading Loading @@ -1452,6 +1540,26 @@ static struct sde_rm_rsvp *_sde_rm_get_rsvp( return NULL; } static struct sde_rm_rsvp *_sde_rm_get_rsvp_nxt( struct sde_rm *rm, struct drm_encoder *enc) { struct sde_rm_rsvp *i; if (list_empty(&rm->rsvps)) return NULL; list_for_each_entry(i, &rm->rsvps, list) if (i->enc_id == enc->base.id) break; list_for_each_entry_continue(i, &rm->rsvps, list) if (i->enc_id == enc->base.id) return i; return NULL; } static struct drm_connector *_sde_rm_get_connector( struct drm_encoder *enc) { Loading Loading @@ -1539,7 +1647,7 @@ static void _sde_rm_release_rsvp( kfree(rsvp); } void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc) void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc, bool nxt) { struct sde_rm_rsvp *rsvp; struct drm_connector *conn; Loading @@ -1552,15 +1660,20 @@ void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc) mutex_lock(&rm->rm_lock); if (nxt) rsvp = _sde_rm_get_rsvp_nxt(rm, enc); else rsvp = _sde_rm_get_rsvp(rm, enc); if (!rsvp) { SDE_ERROR("failed to find rsvp for enc %d\n", enc->base.id); SDE_DEBUG("failed to find rsvp for enc %d, nxt %d", enc->base.id, nxt); goto end; } conn = _sde_rm_get_connector(enc); if (!conn) { SDE_ERROR("failed to get connector for enc %d\n", enc->base.id); SDE_ERROR("failed to get connector for enc %d, nxt %d", enc->base.id, nxt); goto end; } Loading Loading @@ -1651,6 +1764,12 @@ int sde_rm_reserve( _sde_rm_print_rsvps(rm, SDE_RM_STAGE_BEGIN); rsvp_cur = _sde_rm_get_rsvp(rm, enc); rsvp_nxt = _sde_rm_get_rsvp_nxt(rm, enc); if (!test_only && rsvp_nxt) goto commit_rsvp; ret = _sde_rm_populate_requirements(rm, enc, crtc_state, conn_state, &reqs); if (ret) { Loading @@ -1675,8 +1794,6 @@ int sde_rm_reserve( goto end; } rsvp_cur = _sde_rm_get_rsvp(rm, enc); /* * User can request that we clear out any reservation during the * atomic_check phase by using this CLEAR bit Loading @@ -1696,30 +1813,31 @@ int sde_rm_reserve( _sde_rm_print_rsvps(rm, SDE_RM_STAGE_AFTER_RSVPNEXT); if (ret) { SDE_ERROR("failed to reserve hw resources: %d\n", ret); SDE_ERROR("failed to reserve hw resources: %d, test_only %d\n", ret, test_only); _sde_rm_release_rsvp(rm, rsvp_nxt, conn_state->connector); goto end; } else if (test_only && !RM_RQ_LOCK(&reqs)) { /* * Normally, if test_only, test the reservation and then undo * However, if the user requests LOCK, then keep the reservation * made during the atomic_check phase. */ SDE_DEBUG("test_only: discard test rsvp[s%de%d]\n", SDE_DEBUG("test_only: rsvp[s%de%d]\n", rsvp_nxt->seq, rsvp_nxt->enc_id); _sde_rm_release_rsvp(rm, rsvp_nxt, conn_state->connector); goto end; } else { if (test_only && RM_RQ_LOCK(&reqs)) SDE_DEBUG("test_only & LOCK: lock rsvp[s%de%d]\n", rsvp_nxt->seq, rsvp_nxt->enc_id); } commit_rsvp: _sde_rm_release_rsvp(rm, rsvp_cur, conn_state->connector); ret = _sde_rm_commit_rsvp(rm, rsvp_nxt, conn_state); } _sde_rm_print_rsvps(rm, SDE_RM_STAGE_FINAL); end: _sde_rm_print_rsvps(rm, SDE_RM_STAGE_FINAL); mutex_unlock(&rm->rm_lock); return ret; Loading drivers/gpu/drm/msm/sde/sde_rm.h +3 −2 Original line number Diff line number Diff line Loading @@ -184,13 +184,14 @@ int sde_rm_reserve(struct sde_rm *rm, bool test_only); /** * sde_rm_reserve - Given the encoder for the display chain, release any * sde_rm_release - Given the encoder for the display chain, release any * HW blocks previously reserved for that use case. * @rm: SDE Resource Manager handle * @enc: DRM Encoder handle * @nxt: Choose option to release rsvp_nxt * @Return: 0 on Success otherwise -ERROR */ void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc); void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc, bool nxt); /** * sde_rm_get_mdp - Retrieve HW block for MDP TOP. Loading Loading
drivers/gpu/drm/msm/sde/sde_crtc.c +17 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,16 @@ static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc) return to_sde_kms(priv->kms); } static inline struct drm_encoder *_sde_crtc_get_encoder(struct drm_crtc *crtc) { struct drm_encoder *enc; drm_for_each_encoder_mask(enc, crtc->dev, crtc->state->encoder_mask) return enc; return NULL; } static inline int _sde_crtc_power_enable(struct sde_crtc *sde_crtc, bool enable) { struct drm_crtc *crtc; Loading Loading @@ -3107,6 +3117,8 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc, { struct sde_crtc *sde_crtc; struct sde_crtc_state *cstate; struct drm_encoder *enc; struct sde_kms *sde_kms; if (!crtc || !state) { SDE_ERROR("invalid argument(s)\n"); Loading @@ -3115,9 +3127,14 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc, sde_crtc = to_sde_crtc(crtc); cstate = to_sde_crtc_state(state); enc = _sde_crtc_get_encoder(crtc); sde_kms = _sde_crtc_get_kms(crtc); SDE_DEBUG("crtc%d\n", crtc->base.id); if (sde_kms && enc) sde_rm_release(&sde_kms->rm, enc, true); __drm_atomic_helper_crtc_destroy_state(state); /* destroy value helper */ Loading
drivers/gpu/drm/msm/sde/sde_encoder.c +1 −1 Original line number Diff line number Diff line Loading @@ -3287,7 +3287,7 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc) SDE_DEBUG_ENC(sde_enc, "encoder disabled\n"); sde_rm_release(&sde_kms->rm, drm_enc); sde_rm_release(&sde_kms->rm, drm_enc, false); } void sde_encoder_helper_phys_disable(struct sde_encoder_phys *phys_enc, Loading
drivers/gpu/drm/msm/sde/sde_rm.c +289 −171 Original line number Diff line number Diff line Loading @@ -18,7 +18,8 @@ #include "sde_hw_dsc.h" #define RESERVED_BY_OTHER(h, r) \ ((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) (((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) ||\ ((h)->rsvp_nxt && ((h)->rsvp_nxt->enc_id != (r)->enc_id))) #define RM_RQ_LOCK(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_LOCK)) #define RM_RQ_CLEAR(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_CLEAR)) Loading Loading @@ -390,72 +391,11 @@ static int _sde_rm_hw_blk_create( return 0; } int sde_rm_init(struct sde_rm *rm, static int _sde_rm_hw_blk_create_new(struct sde_rm *rm, struct sde_mdss_cfg *cat, void __iomem *mmio, struct drm_device *dev) void __iomem *mmio) { int i, rc = 0; enum sde_hw_blk_type type; if (!rm || !cat || !mmio || !dev) { SDE_ERROR("invalid kms\n"); return -EINVAL; } /* Clear, setup lists */ memset(rm, 0, sizeof(*rm)); mutex_init(&rm->rm_lock); INIT_LIST_HEAD(&rm->rsvps); for (type = 0; type < SDE_HW_BLK_MAX; type++) INIT_LIST_HEAD(&rm->hw_blks[type]); rm->dev = dev; if (IS_SDE_CTL_REV_100(cat->ctl_rev)) rm->topology_tbl = g_ctl_ver_1_top_table; else rm->topology_tbl = g_top_table; /* Some of the sub-blocks require an mdptop to be created */ rm->hw_mdp = sde_hw_mdptop_init(MDP_TOP, mmio, cat); if (IS_ERR_OR_NULL(rm->hw_mdp)) { rc = PTR_ERR(rm->hw_mdp); rm->hw_mdp = NULL; SDE_ERROR("failed: mdp hw not available\n"); goto fail; } /* Interrogate HW catalog and create tracking items for hw blocks */ for (i = 0; i < cat->mixer_count; i++) { struct sde_lm_cfg *lm = &cat->mixer[i]; if (lm->pingpong == PINGPONG_MAX) { SDE_ERROR("mixer %d without pingpong\n", lm->id); goto fail; } rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_LM, cat->mixer[i].id, &cat->mixer[i]); if (rc) { SDE_ERROR("failed: lm hw not available\n"); goto fail; } if (!rm->lm_max_width) { rm->lm_max_width = lm->sblk->maxwidth; } else if (rm->lm_max_width != lm->sblk->maxwidth) { /* * Don't expect to have hw where lm max widths differ. * If found, take the min. */ SDE_ERROR("unsupported: lm maxwidth differs\n"); if (rm->lm_max_width > lm->sblk->maxwidth) rm->lm_max_width = lm->sblk->maxwidth; } } for (i = 0; i < cat->dspp_count; i++) { rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_DSPP, Loading Loading @@ -536,6 +476,79 @@ int sde_rm_init(struct sde_rm *rm, } } fail: return rc; } int sde_rm_init(struct sde_rm *rm, struct sde_mdss_cfg *cat, void __iomem *mmio, struct drm_device *dev) { int i, rc = 0; enum sde_hw_blk_type type; if (!rm || !cat || !mmio || !dev) { SDE_ERROR("invalid input params\n"); return -EINVAL; } /* Clear, setup lists */ memset(rm, 0, sizeof(*rm)); mutex_init(&rm->rm_lock); INIT_LIST_HEAD(&rm->rsvps); for (type = 0; type < SDE_HW_BLK_MAX; type++) INIT_LIST_HEAD(&rm->hw_blks[type]); rm->dev = dev; if (IS_SDE_CTL_REV_100(cat->ctl_rev)) rm->topology_tbl = g_ctl_ver_1_top_table; else rm->topology_tbl = g_top_table; /* Some of the sub-blocks require an mdptop to be created */ rm->hw_mdp = sde_hw_mdptop_init(MDP_TOP, mmio, cat); if (IS_ERR_OR_NULL(rm->hw_mdp)) { rc = PTR_ERR(rm->hw_mdp); rm->hw_mdp = NULL; SDE_ERROR("failed: mdp hw not available\n"); goto fail; } /* Interrogate HW catalog and create tracking items for hw blocks */ for (i = 0; i < cat->mixer_count; i++) { struct sde_lm_cfg *lm = &cat->mixer[i]; if (lm->pingpong == PINGPONG_MAX) { SDE_ERROR("mixer %d without pingpong\n", lm->id); goto fail; } rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_LM, cat->mixer[i].id, &cat->mixer[i]); if (rc) { SDE_ERROR("failed: lm hw not available\n"); goto fail; } if (!rm->lm_max_width) { rm->lm_max_width = lm->sblk->maxwidth; } else if (rm->lm_max_width != lm->sblk->maxwidth) { /* * Don't expect to have hw where lm max widths differ. * If found, take the min. */ SDE_ERROR("unsupported: lm maxwidth differs\n"); if (rm->lm_max_width > lm->sblk->maxwidth) rm->lm_max_width = lm->sblk->maxwidth; } } rc = _sde_rm_hw_blk_create_new(rm, cat, mmio); if (!rc) return 0; fail: Loading @@ -544,63 +557,18 @@ int sde_rm_init(struct sde_rm *rm, return rc; } /** * _sde_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets * proposed use case requirements, incl. hardwired dependent blocks like * pingpong, and dspp. * @rm: sde resource manager handle * @rsvp: reservation currently being created * @reqs: proposed use case requirements * @lm: proposed layer mixer, function checks if lm, and all other hardwired * blocks connected to the lm (pp, dspp) are available and appropriate * @dspp: output parameter, dspp block attached to the layer mixer. * NULL if dspp was not available, or not matching requirements. * @pp: output parameter, pingpong block attached to the layer mixer. * NULL if dspp was not available, or not matching requirements. * @primary_lm: if non-null, this function check if lm is compatible primary_lm * as well as satisfying all other requirements * @Return: true if lm matches all requirements, false otherwise */ static bool _sde_rm_check_lm_and_get_connected_blks( static bool _sde_rm_check_lm( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, struct sde_rm_requirements *reqs, const struct sde_lm_cfg *lm_cfg, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **dspp, struct sde_rm_hw_blk **ds, struct sde_rm_hw_blk **pp, struct sde_rm_hw_blk *primary_lm) struct sde_rm_hw_blk **pp) { const struct sde_lm_cfg *lm_cfg = to_sde_hw_mixer(lm->hw)->cap; const struct sde_pingpong_cfg *pp_cfg; struct sde_rm_hw_iter iter; bool is_valid_dspp, is_valid_ds, ret; u32 display_pref, cwb_pref; *dspp = NULL; *ds = NULL; *pp = NULL; display_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF); cwb_pref = lm_cfg->features & BIT(SDE_DISP_CWB_PREF); SDE_DEBUG("check lm %d: dspp %d ds %d pp %d disp_pref: %d cwb_pref%d\n", lm_cfg->id, lm_cfg->dspp, lm_cfg->ds, lm_cfg->pingpong, display_pref, cwb_pref); /* Check if this layer mixer is a peer of the proposed primary LM */ if (primary_lm) { const struct sde_lm_cfg *prim_lm_cfg = to_sde_hw_mixer(primary_lm->hw)->cap; if (!test_bit(lm_cfg->id, &prim_lm_cfg->lm_pair_mask)) { SDE_DEBUG("lm %d not peer of lm %d\n", lm_cfg->id, prim_lm_cfg->id); return false; } } /* bypass rest of the checks if LM for primary display is found */ if (!display_pref) { is_valid_dspp = (lm_cfg->dspp != DSPP_MAX) ? true : false; is_valid_ds = (lm_cfg->ds != DS_MAX) ? true : false; Loading @@ -625,30 +593,20 @@ static bool _sde_rm_check_lm_and_get_connected_blks( lm_cfg->id, (bool)(RM_RQ_DSPP(reqs)), lm_cfg->dspp, (bool)(RM_RQ_DS(reqs)), lm_cfg->ds); return ret; } /** * If CWB is enabled and LM is not CWB supported * then return false. */ if (RM_RQ_CWB(reqs) && !cwb_pref) { SDE_DEBUG("fail: cwb supported lm not allocated\n"); return false; return ret; } } else if (!(reqs->hw_res.is_primary && display_pref)) { SDE_DEBUG( "display preference is not met. is_primary: %d display_pref: %d\n", (int)reqs->hw_res.is_primary, (int)display_pref); return false; return true; } /* Already reserved? */ if (RESERVED_BY_OTHER(lm, rsvp)) { SDE_DEBUG("lm %d already reserved\n", lm_cfg->id); return false; } static bool _sde_rm_reserve_dspp( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, const struct sde_lm_cfg *lm_cfg, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **dspp) { struct sde_rm_hw_iter iter; if (lm_cfg->dspp != DSPP_MAX) { sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_DSPP); Loading @@ -672,6 +630,19 @@ static bool _sde_rm_check_lm_and_get_connected_blks( } } return true; } static bool _sde_rm_reserve_ds( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, const struct sde_lm_cfg *lm_cfg, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **ds) { struct sde_rm_hw_iter iter; if (lm_cfg->ds != DS_MAX) { sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_DS); while (_sde_rm_get_hw_locked(rm, &iter)) { Loading @@ -694,6 +665,22 @@ static bool _sde_rm_check_lm_and_get_connected_blks( } } return true; } static bool _sde_rm_reserve_pp( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, struct sde_rm_requirements *reqs, const struct sde_lm_cfg *lm_cfg, const struct sde_pingpong_cfg *pp_cfg, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **dspp, struct sde_rm_hw_blk **ds, struct sde_rm_hw_blk **pp) { struct sde_rm_hw_iter iter; sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_PINGPONG); while (_sde_rm_get_hw_locked(rm, &iter)) { if (iter.blk->id == lm_cfg->pingpong) { Loading Loading @@ -723,6 +710,107 @@ static bool _sde_rm_check_lm_and_get_connected_blks( *ds = NULL; return false; } return true; } /** * _sde_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets * proposed use case requirements, incl. hardwired dependent blocks like * pingpong, and dspp. * @rm: sde resource manager handle * @rsvp: reservation currently being created * @reqs: proposed use case requirements * @lm: proposed layer mixer, function checks if lm, and all other hardwired * blocks connected to the lm (pp, dspp) are available and appropriate * @dspp: output parameter, dspp block attached to the layer mixer. * NULL if dspp was not available, or not matching requirements. * @pp: output parameter, pingpong block attached to the layer mixer. * NULL if dspp was not available, or not matching requirements. * @primary_lm: if non-null, this function check if lm is compatible primary_lm * as well as satisfying all other requirements * @Return: true if lm matches all requirements, false otherwise */ static bool _sde_rm_check_lm_and_get_connected_blks( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, struct sde_rm_requirements *reqs, struct sde_rm_hw_blk *lm, struct sde_rm_hw_blk **dspp, struct sde_rm_hw_blk **ds, struct sde_rm_hw_blk **pp, struct sde_rm_hw_blk *primary_lm) { const struct sde_lm_cfg *lm_cfg = to_sde_hw_mixer(lm->hw)->cap; const struct sde_pingpong_cfg *pp_cfg; bool ret; u32 display_pref, cwb_pref; *dspp = NULL; *ds = NULL; *pp = NULL; display_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF); cwb_pref = lm_cfg->features & BIT(SDE_DISP_CWB_PREF); SDE_DEBUG("check lm %d: dspp %d ds %d pp %d disp_pref: %d cwb_pref%d\n", lm_cfg->id, lm_cfg->dspp, lm_cfg->ds, lm_cfg->pingpong, display_pref, cwb_pref); /* Check if this layer mixer is a peer of the proposed primary LM */ if (primary_lm) { const struct sde_lm_cfg *prim_lm_cfg = to_sde_hw_mixer(primary_lm->hw)->cap; if (!test_bit(lm_cfg->id, &prim_lm_cfg->lm_pair_mask)) { SDE_DEBUG("lm %d not peer of lm %d\n", lm_cfg->id, prim_lm_cfg->id); return false; } } /* bypass rest of the checks if LM for primary display is found */ if (!display_pref) { /* Check lm for valid requirements */ ret = _sde_rm_check_lm(rm, rsvp, reqs, lm_cfg, lm, dspp, ds, pp); if (!ret) return ret; /** * If CWB is enabled and LM is not CWB supported * then return false. */ if (RM_RQ_CWB(reqs) && !cwb_pref) { SDE_DEBUG("fail: cwb supported lm not allocated\n"); return false; } } else if (!(reqs->hw_res.is_primary && display_pref)) { SDE_DEBUG( "display preference is not met. is_primary: %d display_pref: %d\n", (int)reqs->hw_res.is_primary, (int)display_pref); return false; } /* Already reserved? */ if (RESERVED_BY_OTHER(lm, rsvp)) { SDE_DEBUG("lm %d already reserved\n", lm_cfg->id); return false; } /* Reserve dspp */ ret = _sde_rm_reserve_dspp(rm, rsvp, lm_cfg, lm, dspp); if (!ret) return ret; /* Reserve ds */ ret = _sde_rm_reserve_ds(rm, rsvp, lm_cfg, lm, ds); if (!ret) return ret; /* Reserve pp */ ret = _sde_rm_reserve_pp(rm, rsvp, reqs, lm_cfg, pp_cfg, lm, dspp, ds, pp); if (!ret) return ret; return true; } Loading Loading @@ -1452,6 +1540,26 @@ static struct sde_rm_rsvp *_sde_rm_get_rsvp( return NULL; } static struct sde_rm_rsvp *_sde_rm_get_rsvp_nxt( struct sde_rm *rm, struct drm_encoder *enc) { struct sde_rm_rsvp *i; if (list_empty(&rm->rsvps)) return NULL; list_for_each_entry(i, &rm->rsvps, list) if (i->enc_id == enc->base.id) break; list_for_each_entry_continue(i, &rm->rsvps, list) if (i->enc_id == enc->base.id) return i; return NULL; } static struct drm_connector *_sde_rm_get_connector( struct drm_encoder *enc) { Loading Loading @@ -1539,7 +1647,7 @@ static void _sde_rm_release_rsvp( kfree(rsvp); } void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc) void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc, bool nxt) { struct sde_rm_rsvp *rsvp; struct drm_connector *conn; Loading @@ -1552,15 +1660,20 @@ void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc) mutex_lock(&rm->rm_lock); if (nxt) rsvp = _sde_rm_get_rsvp_nxt(rm, enc); else rsvp = _sde_rm_get_rsvp(rm, enc); if (!rsvp) { SDE_ERROR("failed to find rsvp for enc %d\n", enc->base.id); SDE_DEBUG("failed to find rsvp for enc %d, nxt %d", enc->base.id, nxt); goto end; } conn = _sde_rm_get_connector(enc); if (!conn) { SDE_ERROR("failed to get connector for enc %d\n", enc->base.id); SDE_ERROR("failed to get connector for enc %d, nxt %d", enc->base.id, nxt); goto end; } Loading Loading @@ -1651,6 +1764,12 @@ int sde_rm_reserve( _sde_rm_print_rsvps(rm, SDE_RM_STAGE_BEGIN); rsvp_cur = _sde_rm_get_rsvp(rm, enc); rsvp_nxt = _sde_rm_get_rsvp_nxt(rm, enc); if (!test_only && rsvp_nxt) goto commit_rsvp; ret = _sde_rm_populate_requirements(rm, enc, crtc_state, conn_state, &reqs); if (ret) { Loading @@ -1675,8 +1794,6 @@ int sde_rm_reserve( goto end; } rsvp_cur = _sde_rm_get_rsvp(rm, enc); /* * User can request that we clear out any reservation during the * atomic_check phase by using this CLEAR bit Loading @@ -1696,30 +1813,31 @@ int sde_rm_reserve( _sde_rm_print_rsvps(rm, SDE_RM_STAGE_AFTER_RSVPNEXT); if (ret) { SDE_ERROR("failed to reserve hw resources: %d\n", ret); SDE_ERROR("failed to reserve hw resources: %d, test_only %d\n", ret, test_only); _sde_rm_release_rsvp(rm, rsvp_nxt, conn_state->connector); goto end; } else if (test_only && !RM_RQ_LOCK(&reqs)) { /* * Normally, if test_only, test the reservation and then undo * However, if the user requests LOCK, then keep the reservation * made during the atomic_check phase. */ SDE_DEBUG("test_only: discard test rsvp[s%de%d]\n", SDE_DEBUG("test_only: rsvp[s%de%d]\n", rsvp_nxt->seq, rsvp_nxt->enc_id); _sde_rm_release_rsvp(rm, rsvp_nxt, conn_state->connector); goto end; } else { if (test_only && RM_RQ_LOCK(&reqs)) SDE_DEBUG("test_only & LOCK: lock rsvp[s%de%d]\n", rsvp_nxt->seq, rsvp_nxt->enc_id); } commit_rsvp: _sde_rm_release_rsvp(rm, rsvp_cur, conn_state->connector); ret = _sde_rm_commit_rsvp(rm, rsvp_nxt, conn_state); } _sde_rm_print_rsvps(rm, SDE_RM_STAGE_FINAL); end: _sde_rm_print_rsvps(rm, SDE_RM_STAGE_FINAL); mutex_unlock(&rm->rm_lock); return ret; Loading
drivers/gpu/drm/msm/sde/sde_rm.h +3 −2 Original line number Diff line number Diff line Loading @@ -184,13 +184,14 @@ int sde_rm_reserve(struct sde_rm *rm, bool test_only); /** * sde_rm_reserve - Given the encoder for the display chain, release any * sde_rm_release - Given the encoder for the display chain, release any * HW blocks previously reserved for that use case. * @rm: SDE Resource Manager handle * @enc: DRM Encoder handle * @nxt: Choose option to release rsvp_nxt * @Return: 0 on Success otherwise -ERROR */ void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc); void sde_rm_release(struct sde_rm *rm, struct drm_encoder *enc, bool nxt); /** * sde_rm_get_mdp - Retrieve HW block for MDP TOP. Loading