Loading drivers/gpu/drm/msm/sde/sde_crtc.c +87 −113 Original line number Diff line number Diff line Loading @@ -42,8 +42,7 @@ static struct sde_kms *get_kms(struct drm_crtc *crtc) return to_sde_kms(priv->kms); } static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, struct drm_encoder *encoder) static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct sde_kms *sde_kms = get_kms(crtc); Loading Loading @@ -71,14 +70,14 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, } /* query encoder resources */ sde_encoder_get_hw_resources(sde_crtc->encoder, &enc_hw_res); sde_encoder_get_hw_resources(sde_crtc->mixers[0].encoder, &enc_hw_res); /* parse encoder hw resources, find CTL paths */ for (i = CTL_0; i <= sde_kms->catalog->ctl_count; i++) { WARN_ON(sde_crtc->num_ctls > CRTC_DUAL_MIXERS); if (enc_hw_res.ctls[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_ctls]; &sde_crtc->mixers[sde_crtc->num_ctls]; mixer->hw_ctl = sde_rm_get_ctl_path(sde_kms, i); if (IS_ERR_OR_NULL(mixer->hw_ctl)) { DRM_ERROR("Invalid ctl_path\n"); Loading @@ -99,7 +98,7 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, for (i = INTF_0; i <= sde_kms->catalog->intf_count; i++) { if (enc_hw_res.intfs[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_mixers]; &sde_crtc->mixers[sde_crtc->num_mixers]; plat_hw_res_map = sde_rm_get_res_map(sde_kms, i, SDE_NONE); Loading @@ -107,15 +106,12 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, if (!lm_idx && unused_lm_count) lm_idx = unused_lm_id[--unused_lm_count]; DBG("Acquiring LM %d", lm_idx); DBG("intf %d acquiring lm %d", i, lm_idx); mixer->hw_lm = sde_rm_acquire_mixer(sde_kms, lm_idx); if (IS_ERR_OR_NULL(mixer->hw_lm)) { DRM_ERROR("Invalid mixer\n"); return -EACCES; } /* interface info */ mixer->intf_idx = i; mixer->mode = enc_hw_res.intfs[i]; sde_crtc->num_mixers++; } } Loading @@ -127,7 +123,7 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, for (i = WB_0; i < WB_MAX; i++) { if (enc_hw_res.wbs[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_mixers]; &sde_crtc->mixers[sde_crtc->num_mixers]; plat_hw_res_map = sde_rm_get_res_map(sde_kms, SDE_NONE, i); Loading @@ -135,27 +131,24 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, if (!lm_idx && unused_lm_count) lm_idx = unused_lm_id[--unused_lm_count]; DBG("Acquiring LM %d", lm_idx); DBG("wb %d acquiring lm %d", i, lm_idx); mixer->hw_lm = sde_rm_acquire_mixer(sde_kms, lm_idx); if (IS_ERR_OR_NULL(mixer->hw_lm)) { DRM_ERROR("Invalid mixer\n"); return -EACCES; } /* interface info */ mixer->wb_idx = i; mixer->mode = enc_hw_res.wbs[i]; sde_crtc->num_mixers++; } } DBG("control paths %d, num_mixers %d, lm[0] %d, ctl[0] %d ", sde_crtc->num_ctls, sde_crtc->num_mixers, sde_crtc->mixer[0].hw_lm->idx, sde_crtc->mixer[0].hw_ctl->idx); sde_crtc->mixers[0].hw_lm->idx, sde_crtc->mixers[0].hw_ctl->idx); if (sde_crtc->num_mixers > 1) DBG("lm[1] %d, ctl[1], %d", sde_crtc->mixer[1].hw_lm->idx, sde_crtc->mixer[1].hw_ctl->idx); sde_crtc->mixers[1].hw_lm->idx, sde_crtc->mixers[1].hw_ctl->idx); return 0; } Loading Loading @@ -271,7 +264,7 @@ static void sde_crtc_get_blend_cfg(struct sde_hw_blend_cfg *cfg, static void blend_setup(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct sde_crtc_mixer *mixer = sde_crtc->mixer; struct sde_crtc_mixer *mixer = sde_crtc->mixers; struct drm_plane *plane; struct sde_plane_state *pstate; struct sde_hw_blend_cfg blend; Loading Loading @@ -306,7 +299,7 @@ static void blend_setup(struct drm_crtc *crtc) sde_plane_pipe(plane); DBG("crtc_id %d - mixer %d pipe %d at stage %d", i, sde_crtc->id, crtc->base.id, sde_plane_pipe(plane), pstate->stage); plane_cnt++; Loading Loading @@ -370,7 +363,7 @@ void sde_crtc_prepare_fence(struct drm_crtc *crtc) sde_crtc = to_sde_crtc(crtc); MSM_EVT(crtc->dev, sde_crtc->id, crtc->enabled); MSM_EVT(crtc->dev, crtc->base.id, 0); sde_fence_prepare(&sde_crtc->output_fence); } Loading @@ -392,8 +385,9 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) */ if (!file || (event->base.file_priv == file)) { sde_crtc->event = NULL; DBG("%s: send event: %pK", sde_crtc->name, event); drm_send_vblank_event(dev, sde_crtc->id, event); SDE_DEBUG("%s: send event: %pK", sde_crtc->name, event); drm_send_vblank_event(dev, sde_crtc->drm_crtc_id, event); } } spin_unlock_irqrestore(&dev->event_lock, flags); Loading @@ -415,10 +409,10 @@ static void sde_crtc_vblank_cb(void *data) drm_crtc_vblank_put(crtc); } if (sde_crtc->drm_requested_vblank) { drm_handle_vblank(dev, sde_crtc->id); if (atomic_read(&sde_crtc->drm_requested_vblank)) { drm_handle_vblank(dev, sde_crtc->drm_crtc_id); DBG_IRQ(""); MSM_EVT(crtc->dev, sde_crtc->id, 0); MSM_EVT(crtc->dev, crtc->base.id, 0); } } Loading @@ -434,30 +428,13 @@ static u32 _sde_crtc_update_ctl_flush_mask(struct drm_crtc *crtc) return -EINVAL; } MSM_EVT(crtc->dev, sde_crtc->id, 0); MSM_EVT(crtc->dev, crtc->base.id, 0); DBG(""); for (i = 0; i < sde_crtc->num_ctls; i++) { mixer = &sde_crtc->mixer[i]; mixer = &sde_crtc->mixers[i]; ctl = mixer->hw_ctl; switch (mixer->mode) { case INTF_MODE_CMD: case INTF_MODE_VIDEO: ctl->ops.get_bitmask_intf(ctl, &mixer->flush_mask, mixer->intf_idx); break; case INTF_MODE_WB_LINE: ctl->ops.get_bitmask_wb(ctl, &mixer->flush_mask, mixer->wb_idx); break; default: DBG("Invalid ctl %d interface mode %d", ctl->idx, mixer->mode); return -EINVAL; } ctl->ops.update_pending_flush(ctl, mixer->flush_mask); DBG("added CTL_ID %d mask 0x%x to pending flush", ctl->idx, mixer->flush_mask); Loading Loading @@ -494,55 +471,37 @@ static void _sde_crtc_trigger_kickoff(void *data) { struct drm_crtc *crtc = (struct drm_crtc *)data; struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct sde_crtc_mixer *mixer; struct sde_hw_ctl *ctl; u32 i; int i; if (!data) { DRM_ERROR("invalid argument\n"); return; } MSM_EVT(crtc->dev, sde_crtc->id, 0); MSM_EVT(crtc->dev, crtc->base.id, 0); /* Commit all pending flush masks to hardware */ for (i = 0; i < sde_crtc->num_ctls; i++) { ctl = sde_crtc->mixer[i].hw_ctl; for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) { ctl = sde_crtc->mixers[i].hw_ctl; if (ctl) { ctl->ops.trigger_flush(ctl); MSM_EVT(crtc->dev, crtc->base.id, ctl->idx); } } /* Signal start to any interface types that require it */ for (i = 0; i < sde_crtc->num_ctls; i++) { ctl = sde_crtc->mixer[i].hw_ctl; if (sde_crtc->mixer[i].mode != INTF_MODE_VIDEO) { for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) { mixer = &sde_crtc->mixers[i]; ctl = mixer->hw_ctl; if (ctl && sde_encoder_needs_ctl_start(mixer->encoder)) { ctl->ops.trigger_start(ctl); DBG("trigger start on ctl %d", ctl->idx); MSM_EVT(crtc->dev, crtc->base.id, ctl->idx); } } } void sde_crtc_wait_for_commit_done(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); int ret; /* ref count the vblank event and interrupts while we wait for it */ if (drm_crtc_vblank_get(crtc)) return; /* * Wait post-flush if necessary to delay before plane_cleanup * For example, wait for vsync in case of video mode panels * This should be a no-op for command mode panels */ MSM_EVT(crtc->dev, sde_crtc->id, 0); ret = sde_encoder_wait_for_commit_done(sde_crtc->encoder); if (ret) DBG("sde_encoder_wait_post_flush returned %d", ret); /* release vblank event ref count */ drm_crtc_vblank_put(crtc); } /** * _sde_crtc_set_input_fence_timeout - update ns version of in fence timeout * @cstate: Pointer to sde crtc state Loading Loading @@ -630,9 +589,9 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, /* Reset flush mask from previous commit */ for (i = 0; i < sde_crtc->num_ctls; i++) { struct sde_hw_ctl *ctl = sde_crtc->mixer[i].hw_ctl; struct sde_hw_ctl *ctl = sde_crtc->mixers[i].hw_ctl; sde_crtc->mixer[i].flush_mask = 0; sde_crtc->mixers[i].flush_mask = 0; ctl->ops.clear_pending_flush(ctl); } Loading Loading @@ -751,19 +710,26 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc, void sde_crtc_commit_kickoff(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct drm_encoder *encoder; struct drm_device *dev; if (!crtc) { DRM_ERROR("invalid argument\n"); return; } dev = crtc->dev; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->crtc != crtc) continue; /* * Encoder will flush/start now, unless it has a tx pending * in which case it may delay and flush at an irq event (e.g. ppdone) * Encoder will flush/start now, unless it has a tx pending. * If so, it may delay and flush at an irq event (e.g. ppdone) */ sde_encoder_schedule_kickoff(sde_crtc->encoder, _sde_crtc_trigger_kickoff, crtc); sde_encoder_schedule_kickoff(encoder, _sde_crtc_trigger_kickoff, crtc); } } /** Loading Loading @@ -876,7 +842,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc) DBG(""); sde_crtc = to_sde_crtc(crtc); mixer = sde_crtc->mixer; mixer = sde_crtc->mixers; if (WARN_ON(!crtc->state)) return; Loading @@ -892,7 +858,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc) */ if ((sde_crtc->num_ctls == 0) || (sde_crtc->num_mixers == 0)) { rc = sde_crtc_reserve_hw_resources(crtc, sde_crtc->encoder); rc = sde_crtc_reserve_hw_resources(crtc); if (rc) { DRM_ERROR("error reserving HW resource for CRTC\n"); return; Loading Loading @@ -999,25 +965,30 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, int sde_crtc_vblank(struct drm_crtc *crtc, bool en) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct drm_encoder *encoder; struct drm_device *dev = crtc->dev; DBG("%d", en); MSM_EVT(crtc->dev, en, 0); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->crtc != crtc) continue; /* * Mark that framework requested vblank, * as opposed to enabling vblank only for our internal purposes * Currently this variable isn't required, but may be useful for future * features * Currently this variable isn't required, but may be useful for * future features */ sde_crtc->drm_requested_vblank = en; atomic_set(&sde_crtc->drm_requested_vblank, en); MSM_EVT(crtc->dev, crtc->base.id, en); if (en) sde_encoder_register_vblank_callback(sde_crtc->encoder, sde_encoder_register_vblank_callback(encoder, sde_crtc_vblank_cb, (void *)crtc); else sde_encoder_register_vblank_callback(sde_crtc->encoder, NULL, NULL); sde_encoder_register_vblank_callback(encoder, NULL, NULL); } return 0; } Loading Loading @@ -1153,17 +1124,15 @@ static int _sde_debugfs_mixer_read(struct seq_file *s, void *data) sde_crtc = s->private; for (i = 0; i < sde_crtc->num_mixers; ++i) { m = &sde_crtc->mixer[i]; m = &sde_crtc->mixers[i]; if (!m->hw_lm) { seq_printf(s, "Mixer[%d] has no LM\n", i); } else if (!m->hw_ctl) { seq_printf(s, "Mixer[%d] has no CTL\n", i); } else { seq_printf(s, "LM_%d/CTL_%d -> INTF_%d, WB_%d\n", seq_printf(s, "LM_%d/CTL_%d\n", m->hw_lm->idx - LM_0, m->hw_ctl->idx - CTL_0, m->intf_idx - INTF_0, m->wb_idx - WB_0); m->hw_ctl->idx - CTL_0); } } seq_printf(s, "Border: %d\n", sde_crtc->stage_cfg.border_enable); Loading Loading @@ -1232,13 +1201,14 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc, /* initialize crtc */ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_encoder *encoder, struct drm_plane *plane, int id) struct drm_plane *plane, int drm_crtc_id) { struct drm_crtc *crtc = NULL; struct sde_crtc *sde_crtc = NULL; struct msm_drm_private *priv = NULL; struct sde_kms *kms = NULL; int rc; int i, rc; priv = dev->dev_private; kms = to_sde_kms(priv->kms); Loading @@ -1249,8 +1219,9 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, crtc = &sde_crtc->base; sde_crtc->id = id; sde_crtc->encoder = encoder; sde_crtc->drm_crtc_id = drm_crtc_id; atomic_set(&sde_crtc->drm_requested_vblank, 0); spin_lock_init(&sde_crtc->lm_lock); drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs); Loading @@ -1258,7 +1229,10 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, drm_crtc_helper_add(crtc, &sde_crtc_helper_funcs); plane->crtc = crtc; rc = sde_crtc_reserve_hw_resources(crtc, encoder); for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) sde_crtc->mixers[i].encoder = encoder; rc = sde_crtc_reserve_hw_resources(crtc); if (rc) { DRM_ERROR(" error reserving HW resource for this CRTC\n"); return ERR_PTR(-EINVAL); Loading drivers/gpu/drm/msm/sde/sde_crtc.h +12 −18 Original line number Diff line number Diff line Loading @@ -26,31 +26,26 @@ #define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages) /** * struct sde_crtc_mixer - stores the map for each virtual pipeline in the CRTC * @hw_dspp : DSPP HW Driver context * struct sde_crtc_mixer: stores the map for each virtual pipeline in the CRTC * @hw_lm: LM HW Driver context * @hw_ctl: CTL Path HW driver context * @intf_idx : Interface idx * @wb_idx : Writeback idx * @mode : Interface mode Active/CMD * @flush_mask: Flush mask value for this commit * @encoder: Encoder attached to this lm & ctl */ struct sde_crtc_mixer { struct sde_hw_dspp *hw_dspp; struct sde_hw_mixer *hw_lm; struct sde_hw_ctl *hw_ctl; enum sde_intf intf_idx; enum sde_wb wb_idx; enum sde_intf_mode mode; u32 flush_mask; struct drm_encoder *encoder; }; /** * struct sde_crtc - virtualized CRTC data structure * @base : Base drm crtc structure * @name : ASCII description of this crtc * @encoder : Associated drm encoder object * @id : Unique crtc identifier * @drm_crtc_id : Id for reporting vblank. Id is relative init order into * mode_config.crtc_list and used by user space to identify * specific crtc in apis such as drm_wait_vblank * @lm_lock : LM register access spinlock * @num_ctls : Number of ctl paths in use * @num_mixers : Number of mixers in use Loading @@ -67,21 +62,20 @@ struct sde_crtc_mixer { struct sde_crtc { struct drm_crtc base; char name[SDE_CRTC_NAME_SIZE]; struct drm_encoder *encoder; int id; int drm_crtc_id; spinlock_t lm_lock; /* protect registers */ /* HW Resources reserved for the crtc */ u32 num_ctls; u32 num_mixers; struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS]; struct sde_crtc_mixer mixers[CRTC_DUAL_MIXERS]; /*if there is a pending flip, these will be non-null */ struct drm_pending_vblank_event *event; atomic_t pending; u32 vsync_count; bool drm_requested_vblank; atomic_t drm_requested_vblank; struct msm_property_info property_info; struct msm_property_data property_data[CRTC_PROP_COUNT]; Loading drivers/gpu/drm/msm/sde/sde_encoder.c +18 −0 Original line number Diff line number Diff line Loading @@ -179,6 +179,24 @@ void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc, } } bool sde_encoder_needs_ctl_start(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; struct sde_encoder_phys *phys; if (!drm_enc) { DRM_ERROR("Invalid pointer"); return false; } sde_enc = to_sde_encoder_virt(drm_enc); phys = sde_enc->cur_master; if (phys && phys->ops.needs_ctl_start) return phys->ops.needs_ctl_start(phys); return false; } static void sde_encoder_destroy(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; Loading drivers/gpu/drm/msm/sde/sde_encoder_phys.h +2 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ struct sde_encoder_virt_ops { * triggering the next kickoff * (ie for previous tx to complete) * @handle_post_kickoff: Do any work necessary post-kickoff work * @needs_ctl_start: Whether encoder type needs ctl_start */ struct sde_encoder_phys_ops { Loading @@ -102,6 +103,7 @@ struct sde_encoder_phys_ops { void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc, bool *wait_until_ready); void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc); bool (*needs_ctl_start)(struct sde_encoder_phys *phys_enc); }; /** Loading drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +7 −0 Original line number Diff line number Diff line Loading @@ -423,6 +423,12 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff( MSM_EVT(DEV(phys_enc), cmd_enc->hw_pp->idx, new_pending_cnt); } static bool sde_encoder_phys_cmd_needs_ctl_start( struct sde_encoder_phys *phys_enc) { return true; } static void sde_encoder_phys_cmd_init_ops( struct sde_encoder_phys_ops *ops) { Loading @@ -436,6 +442,7 @@ static void sde_encoder_phys_cmd_init_ops( ops->control_vblank_irq = sde_encoder_phys_cmd_control_vblank_irq; 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->needs_ctl_start = sde_encoder_phys_cmd_needs_ctl_start; } struct sde_encoder_phys *sde_encoder_phys_cmd_init( Loading Loading
drivers/gpu/drm/msm/sde/sde_crtc.c +87 −113 Original line number Diff line number Diff line Loading @@ -42,8 +42,7 @@ static struct sde_kms *get_kms(struct drm_crtc *crtc) return to_sde_kms(priv->kms); } static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, struct drm_encoder *encoder) static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct sde_kms *sde_kms = get_kms(crtc); Loading Loading @@ -71,14 +70,14 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, } /* query encoder resources */ sde_encoder_get_hw_resources(sde_crtc->encoder, &enc_hw_res); sde_encoder_get_hw_resources(sde_crtc->mixers[0].encoder, &enc_hw_res); /* parse encoder hw resources, find CTL paths */ for (i = CTL_0; i <= sde_kms->catalog->ctl_count; i++) { WARN_ON(sde_crtc->num_ctls > CRTC_DUAL_MIXERS); if (enc_hw_res.ctls[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_ctls]; &sde_crtc->mixers[sde_crtc->num_ctls]; mixer->hw_ctl = sde_rm_get_ctl_path(sde_kms, i); if (IS_ERR_OR_NULL(mixer->hw_ctl)) { DRM_ERROR("Invalid ctl_path\n"); Loading @@ -99,7 +98,7 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, for (i = INTF_0; i <= sde_kms->catalog->intf_count; i++) { if (enc_hw_res.intfs[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_mixers]; &sde_crtc->mixers[sde_crtc->num_mixers]; plat_hw_res_map = sde_rm_get_res_map(sde_kms, i, SDE_NONE); Loading @@ -107,15 +106,12 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, if (!lm_idx && unused_lm_count) lm_idx = unused_lm_id[--unused_lm_count]; DBG("Acquiring LM %d", lm_idx); DBG("intf %d acquiring lm %d", i, lm_idx); mixer->hw_lm = sde_rm_acquire_mixer(sde_kms, lm_idx); if (IS_ERR_OR_NULL(mixer->hw_lm)) { DRM_ERROR("Invalid mixer\n"); return -EACCES; } /* interface info */ mixer->intf_idx = i; mixer->mode = enc_hw_res.intfs[i]; sde_crtc->num_mixers++; } } Loading @@ -127,7 +123,7 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, for (i = WB_0; i < WB_MAX; i++) { if (enc_hw_res.wbs[i]) { struct sde_crtc_mixer *mixer = &sde_crtc->mixer[sde_crtc->num_mixers]; &sde_crtc->mixers[sde_crtc->num_mixers]; plat_hw_res_map = sde_rm_get_res_map(sde_kms, SDE_NONE, i); Loading @@ -135,27 +131,24 @@ static int sde_crtc_reserve_hw_resources(struct drm_crtc *crtc, if (!lm_idx && unused_lm_count) lm_idx = unused_lm_id[--unused_lm_count]; DBG("Acquiring LM %d", lm_idx); DBG("wb %d acquiring lm %d", i, lm_idx); mixer->hw_lm = sde_rm_acquire_mixer(sde_kms, lm_idx); if (IS_ERR_OR_NULL(mixer->hw_lm)) { DRM_ERROR("Invalid mixer\n"); return -EACCES; } /* interface info */ mixer->wb_idx = i; mixer->mode = enc_hw_res.wbs[i]; sde_crtc->num_mixers++; } } DBG("control paths %d, num_mixers %d, lm[0] %d, ctl[0] %d ", sde_crtc->num_ctls, sde_crtc->num_mixers, sde_crtc->mixer[0].hw_lm->idx, sde_crtc->mixer[0].hw_ctl->idx); sde_crtc->mixers[0].hw_lm->idx, sde_crtc->mixers[0].hw_ctl->idx); if (sde_crtc->num_mixers > 1) DBG("lm[1] %d, ctl[1], %d", sde_crtc->mixer[1].hw_lm->idx, sde_crtc->mixer[1].hw_ctl->idx); sde_crtc->mixers[1].hw_lm->idx, sde_crtc->mixers[1].hw_ctl->idx); return 0; } Loading Loading @@ -271,7 +264,7 @@ static void sde_crtc_get_blend_cfg(struct sde_hw_blend_cfg *cfg, static void blend_setup(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct sde_crtc_mixer *mixer = sde_crtc->mixer; struct sde_crtc_mixer *mixer = sde_crtc->mixers; struct drm_plane *plane; struct sde_plane_state *pstate; struct sde_hw_blend_cfg blend; Loading Loading @@ -306,7 +299,7 @@ static void blend_setup(struct drm_crtc *crtc) sde_plane_pipe(plane); DBG("crtc_id %d - mixer %d pipe %d at stage %d", i, sde_crtc->id, crtc->base.id, sde_plane_pipe(plane), pstate->stage); plane_cnt++; Loading Loading @@ -370,7 +363,7 @@ void sde_crtc_prepare_fence(struct drm_crtc *crtc) sde_crtc = to_sde_crtc(crtc); MSM_EVT(crtc->dev, sde_crtc->id, crtc->enabled); MSM_EVT(crtc->dev, crtc->base.id, 0); sde_fence_prepare(&sde_crtc->output_fence); } Loading @@ -392,8 +385,9 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) */ if (!file || (event->base.file_priv == file)) { sde_crtc->event = NULL; DBG("%s: send event: %pK", sde_crtc->name, event); drm_send_vblank_event(dev, sde_crtc->id, event); SDE_DEBUG("%s: send event: %pK", sde_crtc->name, event); drm_send_vblank_event(dev, sde_crtc->drm_crtc_id, event); } } spin_unlock_irqrestore(&dev->event_lock, flags); Loading @@ -415,10 +409,10 @@ static void sde_crtc_vblank_cb(void *data) drm_crtc_vblank_put(crtc); } if (sde_crtc->drm_requested_vblank) { drm_handle_vblank(dev, sde_crtc->id); if (atomic_read(&sde_crtc->drm_requested_vblank)) { drm_handle_vblank(dev, sde_crtc->drm_crtc_id); DBG_IRQ(""); MSM_EVT(crtc->dev, sde_crtc->id, 0); MSM_EVT(crtc->dev, crtc->base.id, 0); } } Loading @@ -434,30 +428,13 @@ static u32 _sde_crtc_update_ctl_flush_mask(struct drm_crtc *crtc) return -EINVAL; } MSM_EVT(crtc->dev, sde_crtc->id, 0); MSM_EVT(crtc->dev, crtc->base.id, 0); DBG(""); for (i = 0; i < sde_crtc->num_ctls; i++) { mixer = &sde_crtc->mixer[i]; mixer = &sde_crtc->mixers[i]; ctl = mixer->hw_ctl; switch (mixer->mode) { case INTF_MODE_CMD: case INTF_MODE_VIDEO: ctl->ops.get_bitmask_intf(ctl, &mixer->flush_mask, mixer->intf_idx); break; case INTF_MODE_WB_LINE: ctl->ops.get_bitmask_wb(ctl, &mixer->flush_mask, mixer->wb_idx); break; default: DBG("Invalid ctl %d interface mode %d", ctl->idx, mixer->mode); return -EINVAL; } ctl->ops.update_pending_flush(ctl, mixer->flush_mask); DBG("added CTL_ID %d mask 0x%x to pending flush", ctl->idx, mixer->flush_mask); Loading Loading @@ -494,55 +471,37 @@ static void _sde_crtc_trigger_kickoff(void *data) { struct drm_crtc *crtc = (struct drm_crtc *)data; struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct sde_crtc_mixer *mixer; struct sde_hw_ctl *ctl; u32 i; int i; if (!data) { DRM_ERROR("invalid argument\n"); return; } MSM_EVT(crtc->dev, sde_crtc->id, 0); MSM_EVT(crtc->dev, crtc->base.id, 0); /* Commit all pending flush masks to hardware */ for (i = 0; i < sde_crtc->num_ctls; i++) { ctl = sde_crtc->mixer[i].hw_ctl; for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) { ctl = sde_crtc->mixers[i].hw_ctl; if (ctl) { ctl->ops.trigger_flush(ctl); MSM_EVT(crtc->dev, crtc->base.id, ctl->idx); } } /* Signal start to any interface types that require it */ for (i = 0; i < sde_crtc->num_ctls; i++) { ctl = sde_crtc->mixer[i].hw_ctl; if (sde_crtc->mixer[i].mode != INTF_MODE_VIDEO) { for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) { mixer = &sde_crtc->mixers[i]; ctl = mixer->hw_ctl; if (ctl && sde_encoder_needs_ctl_start(mixer->encoder)) { ctl->ops.trigger_start(ctl); DBG("trigger start on ctl %d", ctl->idx); MSM_EVT(crtc->dev, crtc->base.id, ctl->idx); } } } void sde_crtc_wait_for_commit_done(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); int ret; /* ref count the vblank event and interrupts while we wait for it */ if (drm_crtc_vblank_get(crtc)) return; /* * Wait post-flush if necessary to delay before plane_cleanup * For example, wait for vsync in case of video mode panels * This should be a no-op for command mode panels */ MSM_EVT(crtc->dev, sde_crtc->id, 0); ret = sde_encoder_wait_for_commit_done(sde_crtc->encoder); if (ret) DBG("sde_encoder_wait_post_flush returned %d", ret); /* release vblank event ref count */ drm_crtc_vblank_put(crtc); } /** * _sde_crtc_set_input_fence_timeout - update ns version of in fence timeout * @cstate: Pointer to sde crtc state Loading Loading @@ -630,9 +589,9 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, /* Reset flush mask from previous commit */ for (i = 0; i < sde_crtc->num_ctls; i++) { struct sde_hw_ctl *ctl = sde_crtc->mixer[i].hw_ctl; struct sde_hw_ctl *ctl = sde_crtc->mixers[i].hw_ctl; sde_crtc->mixer[i].flush_mask = 0; sde_crtc->mixers[i].flush_mask = 0; ctl->ops.clear_pending_flush(ctl); } Loading Loading @@ -751,19 +710,26 @@ static void sde_crtc_destroy_state(struct drm_crtc *crtc, void sde_crtc_commit_kickoff(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct drm_encoder *encoder; struct drm_device *dev; if (!crtc) { DRM_ERROR("invalid argument\n"); return; } dev = crtc->dev; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->crtc != crtc) continue; /* * Encoder will flush/start now, unless it has a tx pending * in which case it may delay and flush at an irq event (e.g. ppdone) * Encoder will flush/start now, unless it has a tx pending. * If so, it may delay and flush at an irq event (e.g. ppdone) */ sde_encoder_schedule_kickoff(sde_crtc->encoder, _sde_crtc_trigger_kickoff, crtc); sde_encoder_schedule_kickoff(encoder, _sde_crtc_trigger_kickoff, crtc); } } /** Loading Loading @@ -876,7 +842,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc) DBG(""); sde_crtc = to_sde_crtc(crtc); mixer = sde_crtc->mixer; mixer = sde_crtc->mixers; if (WARN_ON(!crtc->state)) return; Loading @@ -892,7 +858,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc) */ if ((sde_crtc->num_ctls == 0) || (sde_crtc->num_mixers == 0)) { rc = sde_crtc_reserve_hw_resources(crtc, sde_crtc->encoder); rc = sde_crtc_reserve_hw_resources(crtc); if (rc) { DRM_ERROR("error reserving HW resource for CRTC\n"); return; Loading Loading @@ -999,25 +965,30 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, int sde_crtc_vblank(struct drm_crtc *crtc, bool en) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); struct drm_encoder *encoder; struct drm_device *dev = crtc->dev; DBG("%d", en); MSM_EVT(crtc->dev, en, 0); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->crtc != crtc) continue; /* * Mark that framework requested vblank, * as opposed to enabling vblank only for our internal purposes * Currently this variable isn't required, but may be useful for future * features * Currently this variable isn't required, but may be useful for * future features */ sde_crtc->drm_requested_vblank = en; atomic_set(&sde_crtc->drm_requested_vblank, en); MSM_EVT(crtc->dev, crtc->base.id, en); if (en) sde_encoder_register_vblank_callback(sde_crtc->encoder, sde_encoder_register_vblank_callback(encoder, sde_crtc_vblank_cb, (void *)crtc); else sde_encoder_register_vblank_callback(sde_crtc->encoder, NULL, NULL); sde_encoder_register_vblank_callback(encoder, NULL, NULL); } return 0; } Loading Loading @@ -1153,17 +1124,15 @@ static int _sde_debugfs_mixer_read(struct seq_file *s, void *data) sde_crtc = s->private; for (i = 0; i < sde_crtc->num_mixers; ++i) { m = &sde_crtc->mixer[i]; m = &sde_crtc->mixers[i]; if (!m->hw_lm) { seq_printf(s, "Mixer[%d] has no LM\n", i); } else if (!m->hw_ctl) { seq_printf(s, "Mixer[%d] has no CTL\n", i); } else { seq_printf(s, "LM_%d/CTL_%d -> INTF_%d, WB_%d\n", seq_printf(s, "LM_%d/CTL_%d\n", m->hw_lm->idx - LM_0, m->hw_ctl->idx - CTL_0, m->intf_idx - INTF_0, m->wb_idx - WB_0); m->hw_ctl->idx - CTL_0); } } seq_printf(s, "Border: %d\n", sde_crtc->stage_cfg.border_enable); Loading Loading @@ -1232,13 +1201,14 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc, /* initialize crtc */ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_encoder *encoder, struct drm_plane *plane, int id) struct drm_plane *plane, int drm_crtc_id) { struct drm_crtc *crtc = NULL; struct sde_crtc *sde_crtc = NULL; struct msm_drm_private *priv = NULL; struct sde_kms *kms = NULL; int rc; int i, rc; priv = dev->dev_private; kms = to_sde_kms(priv->kms); Loading @@ -1249,8 +1219,9 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, crtc = &sde_crtc->base; sde_crtc->id = id; sde_crtc->encoder = encoder; sde_crtc->drm_crtc_id = drm_crtc_id; atomic_set(&sde_crtc->drm_requested_vblank, 0); spin_lock_init(&sde_crtc->lm_lock); drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs); Loading @@ -1258,7 +1229,10 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, drm_crtc_helper_add(crtc, &sde_crtc_helper_funcs); plane->crtc = crtc; rc = sde_crtc_reserve_hw_resources(crtc, encoder); for (i = 0; i < ARRAY_SIZE(sde_crtc->mixers); i++) sde_crtc->mixers[i].encoder = encoder; rc = sde_crtc_reserve_hw_resources(crtc); if (rc) { DRM_ERROR(" error reserving HW resource for this CRTC\n"); return ERR_PTR(-EINVAL); Loading
drivers/gpu/drm/msm/sde/sde_crtc.h +12 −18 Original line number Diff line number Diff line Loading @@ -26,31 +26,26 @@ #define CRTC_HW_MIXER_MAXSTAGES(c, idx) ((c)->mixer[idx].sblk->maxblendstages) /** * struct sde_crtc_mixer - stores the map for each virtual pipeline in the CRTC * @hw_dspp : DSPP HW Driver context * struct sde_crtc_mixer: stores the map for each virtual pipeline in the CRTC * @hw_lm: LM HW Driver context * @hw_ctl: CTL Path HW driver context * @intf_idx : Interface idx * @wb_idx : Writeback idx * @mode : Interface mode Active/CMD * @flush_mask: Flush mask value for this commit * @encoder: Encoder attached to this lm & ctl */ struct sde_crtc_mixer { struct sde_hw_dspp *hw_dspp; struct sde_hw_mixer *hw_lm; struct sde_hw_ctl *hw_ctl; enum sde_intf intf_idx; enum sde_wb wb_idx; enum sde_intf_mode mode; u32 flush_mask; struct drm_encoder *encoder; }; /** * struct sde_crtc - virtualized CRTC data structure * @base : Base drm crtc structure * @name : ASCII description of this crtc * @encoder : Associated drm encoder object * @id : Unique crtc identifier * @drm_crtc_id : Id for reporting vblank. Id is relative init order into * mode_config.crtc_list and used by user space to identify * specific crtc in apis such as drm_wait_vblank * @lm_lock : LM register access spinlock * @num_ctls : Number of ctl paths in use * @num_mixers : Number of mixers in use Loading @@ -67,21 +62,20 @@ struct sde_crtc_mixer { struct sde_crtc { struct drm_crtc base; char name[SDE_CRTC_NAME_SIZE]; struct drm_encoder *encoder; int id; int drm_crtc_id; spinlock_t lm_lock; /* protect registers */ /* HW Resources reserved for the crtc */ u32 num_ctls; u32 num_mixers; struct sde_crtc_mixer mixer[CRTC_DUAL_MIXERS]; struct sde_crtc_mixer mixers[CRTC_DUAL_MIXERS]; /*if there is a pending flip, these will be non-null */ struct drm_pending_vblank_event *event; atomic_t pending; u32 vsync_count; bool drm_requested_vblank; atomic_t drm_requested_vblank; struct msm_property_info property_info; struct msm_property_data property_data[CRTC_PROP_COUNT]; Loading
drivers/gpu/drm/msm/sde/sde_encoder.c +18 −0 Original line number Diff line number Diff line Loading @@ -179,6 +179,24 @@ void sde_encoder_get_hw_resources(struct drm_encoder *drm_enc, } } bool sde_encoder_needs_ctl_start(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; struct sde_encoder_phys *phys; if (!drm_enc) { DRM_ERROR("Invalid pointer"); return false; } sde_enc = to_sde_encoder_virt(drm_enc); phys = sde_enc->cur_master; if (phys && phys->ops.needs_ctl_start) return phys->ops.needs_ctl_start(phys); return false; } static void sde_encoder_destroy(struct drm_encoder *drm_enc) { struct sde_encoder_virt *sde_enc = NULL; Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys.h +2 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ struct sde_encoder_virt_ops { * triggering the next kickoff * (ie for previous tx to complete) * @handle_post_kickoff: Do any work necessary post-kickoff work * @needs_ctl_start: Whether encoder type needs ctl_start */ struct sde_encoder_phys_ops { Loading @@ -102,6 +103,7 @@ struct sde_encoder_phys_ops { void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc, bool *wait_until_ready); void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc); bool (*needs_ctl_start)(struct sde_encoder_phys *phys_enc); }; /** Loading
drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c +7 −0 Original line number Diff line number Diff line Loading @@ -423,6 +423,12 @@ static void sde_encoder_phys_cmd_prepare_for_kickoff( MSM_EVT(DEV(phys_enc), cmd_enc->hw_pp->idx, new_pending_cnt); } static bool sde_encoder_phys_cmd_needs_ctl_start( struct sde_encoder_phys *phys_enc) { return true; } static void sde_encoder_phys_cmd_init_ops( struct sde_encoder_phys_ops *ops) { Loading @@ -436,6 +442,7 @@ static void sde_encoder_phys_cmd_init_ops( ops->control_vblank_irq = sde_encoder_phys_cmd_control_vblank_irq; 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->needs_ctl_start = sde_encoder_phys_cmd_needs_ctl_start; } struct sde_encoder_phys *sde_encoder_phys_cmd_init( Loading