diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index e8dfd1f08236..f74a682c5f04 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -572,8 +572,6 @@ void sde_connector_complete_commit(struct drm_connector *connector) { struct drm_device *dev; struct msm_drm_private *priv; - struct sde_connector *c_conn; - struct sde_kms *sde_kms; if (!connector) { SDE_ERROR("invalid connector\n"); @@ -582,7 +580,6 @@ void sde_connector_complete_commit(struct drm_connector *connector) dev = connector->dev; priv = dev->dev_private; - sde_kms = to_sde_kms(priv->kms); /* signal connector's retire fence */ sde_fence_signal(&to_sde_connector(connector)->retire_fence, 0); @@ -590,14 +587,8 @@ void sde_connector_complete_commit(struct drm_connector *connector) /* after first vsync comes, * early splash resource should start to be released. */ - if (sde_splash_get_lk_complete_status(&sde_kms->splash_info)) { - c_conn = to_sde_connector(connector); - - sde_splash_clean_up_free_resource(priv->kms, - &priv->phandle, - c_conn->connector_type, - c_conn->display); - } + if (sde_splash_get_lk_complete_status(priv->kms)) + sde_splash_free_resource(priv->kms, &priv->phandle); } diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index cd00ab4b4a81..6ad1ce16c20a 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -304,6 +304,8 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc) struct sde_crtc_mixer *mixer = sde_crtc->mixers; struct sde_hw_ctl *ctl; struct sde_hw_mixer *lm; + struct sde_splash_info *sinfo; + struct sde_kms *sde_kms = _sde_crtc_get_kms(crtc); int i; @@ -314,6 +316,17 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc) return; } + if (!sde_kms) { + SDE_ERROR("invalid sde_kms\n"); + return; + } + + sinfo = &sde_kms->splash_info; + if (!sinfo) { + SDE_ERROR("invalid splash info\n"); + return; + } + for (i = 0; i < sde_crtc->num_mixers; i++) { if (!mixer[i].hw_lm || !mixer[i].hw_ctl) { SDE_ERROR("invalid lm or ctl assigned to mixer\n"); @@ -323,7 +336,10 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc) mixer[i].flush_mask = 0; if (mixer[i].hw_ctl->ops.clear_all_blendstages) mixer[i].hw_ctl->ops.clear_all_blendstages( - mixer[i].hw_ctl); + mixer[i].hw_ctl, + sinfo->handoff, + sinfo->reserved_pipe_info, + MAX_BLOCKS); } /* initialize stage cfg */ @@ -350,7 +366,8 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc) mixer[i].flush_mask); ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx, - &sde_crtc->stage_cfg, i); + &sde_crtc->stage_cfg, i, + sinfo->handoff, sinfo->reserved_pipe_info, MAX_BLOCKS); } } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c index 46e2a13cecc4..341738f624db 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -258,6 +258,35 @@ static inline int sde_hw_ctl_get_bitmask_cdm(struct sde_hw_ctl *ctx, return 0; } +static inline int sde_hw_ctl_get_splash_mixercfg(const u32 *resv_pipes, + u32 length) +{ + int i = 0; + u32 mixercfg = 0; + + for (i = 0; i < length; i++) { + /* LK's splash VIG layer always stays on top */ + switch (resv_pipes[i]) { + case SSPP_VIG0: + mixercfg |= 0x7 << 0; + break; + case SSPP_VIG1: + mixercfg |= 0x7 << 3; + break; + case SSPP_VIG2: + mixercfg |= 0x7 << 6; + break; + case SSPP_VIG3: + mixercfg |= 0x7 << 26; + break; + default: + break; + } + } + + return mixercfg; +} + static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 count) { struct sde_hw_blk_reg_map *c = &ctx->hw; @@ -312,15 +341,29 @@ static int sde_hw_ctl_wait_reset_status(struct sde_hw_ctl *ctx) return 0; } -static void sde_hw_ctl_clear_all_blendstages(struct sde_hw_ctl *ctx) +static void sde_hw_ctl_clear_all_blendstages(struct sde_hw_ctl *ctx, + bool handoff, const u32 *resv_pipes, u32 resv_pipes_length) { struct sde_hw_blk_reg_map *c = &ctx->hw; int i; for (i = 0; i < ctx->mixer_count; i++) { int mixer_id = ctx->mixer_hw_caps[i].id; + u32 mixercfg = 0; - SDE_REG_WRITE(c, CTL_LAYER(mixer_id), 0); + /* + * if bootloaer still has early RVC running, mixer status + * can't be direcly cleared. + */ + if (handoff) { + mixercfg = + sde_hw_ctl_get_splash_mixercfg(resv_pipes, + resv_pipes_length); + + mixercfg &= SDE_REG_READ(c, CTL_LAYER(mixer_id)); + } + + SDE_REG_WRITE(c, CTL_LAYER(mixer_id), mixercfg); SDE_REG_WRITE(c, CTL_LAYER_EXT(mixer_id), 0); SDE_REG_WRITE(c, CTL_LAYER_EXT2(mixer_id), 0); SDE_REG_WRITE(c, CTL_LAYER_EXT3(mixer_id), 0); @@ -328,7 +371,8 @@ static void sde_hw_ctl_clear_all_blendstages(struct sde_hw_ctl *ctx) } static void sde_hw_ctl_setup_blendstage(struct sde_hw_ctl *ctx, - enum sde_lm lm, struct sde_hw_stage_cfg *stage_cfg, u32 index) + enum sde_lm lm, struct sde_hw_stage_cfg *stage_cfg, u32 index, + bool handoff, const u32 *resv_pipes, u32 resv_pipes_length) { struct sde_hw_blk_reg_map *c = &ctx->hw; u32 mixercfg, mixercfg_ext, mix, ext, mixercfg_ext2; @@ -353,6 +397,20 @@ static void sde_hw_ctl_setup_blendstage(struct sde_hw_ctl *ctx, mixercfg_ext = 0; mixercfg_ext2 = 0; + /* + * if bootloader still have RVC running, its mixer stauts + * should be updated to kernel's mixer setup. + */ + if (handoff) { + mixercfg = + sde_hw_ctl_get_splash_mixercfg(resv_pipes, + resv_pipes_length); + + mixercfg &= SDE_REG_READ(c, CTL_LAYER(lm)); + mixercfg |= BIT(24); + stages--; + } + for (i = 0; i <= stages; i++) { /* overflow to ext register if 'i + 1 > 7' */ mix = (i + 1) & 0x7; @@ -458,6 +516,38 @@ static void sde_hw_ctl_intf_cfg(struct sde_hw_ctl *ctx, SDE_REG_WRITE(c, CTL_TOP, intf_cfg); } +static inline u32 sde_hw_ctl_read_ctl_top_for_splash(struct sde_hw_ctl *ctx) +{ + struct sde_hw_blk_reg_map *c; + u32 ctl_top; + + if (!ctx) { + pr_err("Invalid ctx\n"); + return 0; + } + + c = &ctx->hw; + ctl_top = SDE_REG_READ(c, CTL_TOP); + return ctl_top; +} + +static inline u32 sde_hw_ctl_read_ctl_layers_for_splash(struct sde_hw_ctl *ctx, + int index) +{ + struct sde_hw_blk_reg_map *c; + u32 ctl_top; + + if (!ctx) { + pr_err("Invalid ctx\n"); + return 0; + } + + c = &ctx->hw; + ctl_top = SDE_REG_READ(c, CTL_LAYER(index)); + + return ctl_top; +} + static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops, unsigned long cap) { @@ -478,6 +568,8 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops, ops->get_bitmask_intf = sde_hw_ctl_get_bitmask_intf; ops->get_bitmask_cdm = sde_hw_ctl_get_bitmask_cdm; ops->get_bitmask_wb = sde_hw_ctl_get_bitmask_wb; + ops->read_ctl_top_for_splash = sde_hw_ctl_read_ctl_top_for_splash; + ops->read_ctl_layers_for_splash = sde_hw_ctl_read_ctl_layers_for_splash; }; struct sde_hw_ctl *sde_hw_ctl_init(enum sde_ctl idx, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h index 74dbde92639a..a008ecf4a11d 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -146,17 +146,40 @@ struct sde_hw_ctl_ops { /** * Set all blend stages to disabled * @ctx : ctl path ctx pointer + * @handoff : handoff flag + * @resv_pipes : reserved pipes in DT + * @resv_pipes_length: array size of array reserved_pipes */ - void (*clear_all_blendstages)(struct sde_hw_ctl *ctx); + void (*clear_all_blendstages)(struct sde_hw_ctl *ctx, + bool handoff, const u32 *resv_pipes, u32 resv_pipes_length); /** * Configure layer mixer to pipe configuration * @ctx : ctl path ctx pointer * @lm : layer mixer enumeration * @cfg : blend stage configuration + * @handoff : handoff flag + * @resv_pipes : reserved pipes in DT + * @resv_pipes_length: array size of array reserved_pipes */ void (*setup_blendstage)(struct sde_hw_ctl *ctx, - enum sde_lm lm, struct sde_hw_stage_cfg *cfg, u32 index); + enum sde_lm lm, struct sde_hw_stage_cfg *cfg, u32 index, + bool handoff, const u32 *resv_pipes, u32 resv_pipes_length); + + /** + * read CTL_TOP register value for splash case + * @ctx : ctl path ctx pointer + * @Return : CTL top register value + */ + u32 (*read_ctl_top_for_splash)(struct sde_hw_ctl *ctx); + + /** + * read CTL layers register value for splash case + * @ctx : ctl path ctx pointer + * @index : layer index for this ctl path + * @Return : CTL layers register value + */ + u32 (*read_ctl_layers_for_splash)(struct sde_hw_ctl *ctx, int index); }; /** diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index b95157b28855..1da8b5b4ff10 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -343,8 +343,9 @@ static void sde_kms_prepare_commit(struct msm_kms *kms, struct drm_device *dev = sde_kms->dev; struct msm_drm_private *priv = dev->dev_private; - if (sde_kms->splash_info.handoff) - sde_splash_clean_up_exit_lk(kms); + if (sde_kms->splash_info.handoff && + sde_kms->splash_info.display_splash_enabled) + sde_splash_lk_stop_splash(kms); else sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true); diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c index de0551b22d2e..6055dc861c72 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.c +++ b/drivers/gpu/drm/msm/sde/sde_rm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,6 +24,7 @@ #include "sde_encoder.h" #include "sde_connector.h" #include "sde_hw_sspp.h" +#include "sde_splash.h" #define RESERVED_BY_OTHER(h, r) \ ((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) @@ -417,6 +418,8 @@ int sde_rm_init(struct sde_rm *rm, mutex_init(&rm->rm_lock); + rm->dev = dev; + INIT_LIST_HEAD(&rm->rsvps); for (type = 0; type < SDE_HW_BLK_MAX; type++) INIT_LIST_HEAD(&rm->hw_blks[type]); @@ -652,7 +655,8 @@ static bool _sde_rm_check_lm_and_get_connected_blks( static int _sde_rm_reserve_lms( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, - struct sde_rm_requirements *reqs) + struct sde_rm_requirements *reqs, + uint32_t prefer_lm_id) { struct sde_rm_hw_blk *lm[MAX_BLOCKS]; @@ -678,6 +682,10 @@ static int _sde_rm_reserve_lms( lm_count = 0; lm[lm_count] = iter_i.blk; + /* find the matched lm id */ + if ((prefer_lm_id > 0) && (iter_i.blk->id != prefer_lm_id)) + continue; + if (!_sde_rm_check_lm_and_get_connected_blks(rm, rsvp, reqs, lm[lm_count], &dspp[lm_count], &pp[lm_count], NULL)) @@ -699,6 +707,7 @@ static int _sde_rm_reserve_lms( continue; lm[lm_count] = iter_j.blk; + ++lm_count; } } @@ -747,7 +756,8 @@ static int _sde_rm_reserve_lms( static int _sde_rm_reserve_ctls( struct sde_rm *rm, struct sde_rm_rsvp *rsvp, - struct sde_rm_requirements *reqs) + struct sde_rm_requirements *reqs, + uint32_t prefer_ctl_id) { struct sde_rm_hw_blk *ctls[MAX_BLOCKS]; struct sde_rm_hw_iter iter; @@ -769,6 +779,14 @@ static int _sde_rm_reserve_ctls( SDE_DEBUG("ctl %d caps 0x%lX\n", iter.blk->id, caps); + /* early return when finding the matched ctl id */ + if ((prefer_ctl_id > 0) && (iter.blk->id == prefer_ctl_id)) { + ctls[i] = iter.blk; + + if (++i == reqs->num_ctl) + break; + } + if (reqs->needs_split_display != has_split_display) continue; @@ -928,10 +946,10 @@ static int _sde_rm_make_next_rsvp( * - Check mixers without DSPPs * - Only then allow to grab from mixers with DSPP capability */ - ret = _sde_rm_reserve_lms(rm, rsvp, reqs); + ret = _sde_rm_reserve_lms(rm, rsvp, reqs, 0); if (ret && !RM_RQ_DSPP(reqs)) { reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_DSPP); - ret = _sde_rm_reserve_lms(rm, rsvp, reqs); + ret = _sde_rm_reserve_lms(rm, rsvp, reqs, 0); } if (ret) { @@ -944,10 +962,10 @@ static int _sde_rm_make_next_rsvp( * - Check mixers without Split Display * - Only then allow to grab from CTLs with split display capability */ - _sde_rm_reserve_ctls(rm, rsvp, reqs); + _sde_rm_reserve_ctls(rm, rsvp, reqs, 0); if (ret && !reqs->needs_split_display) { reqs->needs_split_display = true; - _sde_rm_reserve_ctls(rm, rsvp, reqs); + _sde_rm_reserve_ctls(rm, rsvp, reqs, 0); } if (ret) { SDE_ERROR("unable to find appropriate CTL\n"); @@ -962,6 +980,109 @@ static int _sde_rm_make_next_rsvp( return ret; } +static int _sde_rm_make_next_rsvp_for_splash( + struct sde_rm *rm, + struct drm_encoder *enc, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + struct sde_rm_rsvp *rsvp, + struct sde_rm_requirements *reqs) +{ + int ret; + struct msm_drm_private *priv; + struct sde_kms *sde_kms; + struct sde_splash_info *sinfo; + int i; + int intf_id = INTF_0; + u32 prefer_lm_id = 0; + u32 prefer_ctl_id = 0; + + if (!enc->dev || !enc->dev->dev_private) { + SDE_ERROR("drm device invalid\n"); + return -EINVAL; + } + + priv = enc->dev->dev_private; + if (!priv->kms) { + SDE_ERROR("invalid kms\n"); + return -EINVAL; + } + + sde_kms = to_sde_kms(priv->kms); + sinfo = &sde_kms->splash_info; + + /* Get the intf id first, and reserve the same lk and ctl + * in bootloader for kernel resource manager + */ + for (i = 0; i < ARRAY_SIZE(reqs->hw_res.intfs); i++) { + if (reqs->hw_res.intfs[i] == INTF_MODE_NONE) + continue; + intf_id = i + INTF_0; + break; + } + + /* get preferred lm id and ctl id */ + for (i = 0; i < CTL_MAX - 1; i++) { + if (sinfo->res.top[i].intf_sel != intf_id) + continue; + + prefer_lm_id = sinfo->res.top[i].lm[0].lm_id; + prefer_ctl_id = sinfo->res.top[i].lm[0].ctl_id; + break; + } + + SDE_DEBUG("intf_id %d, prefer lm_id %d, ctl_id %d\n", + intf_id, prefer_lm_id, prefer_ctl_id); + + /* Create reservation info, tag reserved blocks with it as we go */ + rsvp->seq = ++rm->rsvp_next_seq; + rsvp->enc_id = enc->base.id; + rsvp->topology = reqs->top_name; + list_add_tail(&rsvp->list, &rm->rsvps); + + /* + * Assign LMs and blocks whose usage is tied to them: DSPP & Pingpong. + * Do assignment preferring to give away low-resource mixers first: + * - Check mixers without DSPPs + * - Only then allow to grab from mixers with DSPP capability + */ + ret = _sde_rm_reserve_lms(rm, rsvp, reqs, prefer_lm_id); + if (ret && !RM_RQ_DSPP(reqs)) { + reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_DSPP); + ret = _sde_rm_reserve_lms(rm, rsvp, reqs, prefer_lm_id); + } + + if (ret) { + SDE_ERROR("unable to find appropriate mixers\n"); + return ret; + } + + /* + * Do assignment preferring to give away low-resource CTLs first: + * - Check mixers without Split Display + * - Only then allow to grab from CTLs with split display capability + */ + for (i = 0; i < sinfo->res.ctl_top_cnt; i++) + SDE_DEBUG("splash_info ctl_ids[%d] = %d\n", + i, sinfo->res.ctl_ids[i]); + + ret = _sde_rm_reserve_ctls(rm, rsvp, reqs, prefer_ctl_id); + if (ret && !reqs->needs_split_display) { + reqs->needs_split_display = true; + _sde_rm_reserve_ctls(rm, rsvp, reqs, prefer_ctl_id); + } + + if (ret) { + SDE_ERROR("unable to find appropriate CTL\n"); + return ret; + } + + /* Assign INTFs, WBs, and blks whose usage is tied to them: CTL & CDM */ + ret = _sde_rm_reserve_intf_related_hw(rm, rsvp, &reqs->hw_res); + + return ret; +} + static int _sde_rm_populate_requirements( struct sde_rm *rm, struct drm_encoder *enc, @@ -1253,6 +1374,8 @@ int sde_rm_reserve( { struct sde_rm_rsvp *rsvp_cur, *rsvp_nxt; struct sde_rm_requirements reqs; + struct msm_drm_private *priv; + struct sde_kms *sde_kms; int ret; if (!rm || !enc || !crtc_state || !conn_state) { @@ -1260,6 +1383,19 @@ int sde_rm_reserve( return -EINVAL; } + if (!enc->dev || !enc->dev->dev_private) { + SDE_ERROR("invalid drm device\n"); + return -EINVAL; + } + + priv = enc->dev->dev_private; + if (!priv->kms) { + SDE_ERROR("invald kms\n"); + return -EINVAL; + } + + sde_kms = to_sde_kms(priv->kms); + /* Check if this is just a page-flip */ if (!drm_atomic_crtc_needs_modeset(crtc_state)) return 0; @@ -1318,8 +1454,13 @@ int sde_rm_reserve( } /* Check the proposed reservation, store it in hw's "next" field */ - ret = _sde_rm_make_next_rsvp(rm, enc, crtc_state, conn_state, - rsvp_nxt, &reqs); + if (sde_kms->splash_info.handoff) { + SDE_DEBUG("Reserve resource for splash\n"); + ret = _sde_rm_make_next_rsvp_for_splash + (rm, enc, crtc_state, conn_state, rsvp_nxt, &reqs); + } else + ret = _sde_rm_make_next_rsvp(rm, enc, crtc_state, conn_state, + rsvp_nxt, &reqs); _sde_rm_print_rsvps(rm, SDE_RM_STAGE_AFTER_RSVPNEXT); @@ -1352,3 +1493,92 @@ end: return ret; } + +static int _sde_rm_get_ctl_lm_for_splash(struct sde_hw_ctl *ctl, + int max_lm_cnt, u8 lm_cnt, u8 *lm_ids, + struct splash_ctl_top *top, int index) +{ + int j; + struct splash_lm_hw *lm; + + if (!ctl || !top) { + SDE_ERROR("invalid parameters\n"); + return 0; + } + + lm = top->lm; + for (j = 0; j < max_lm_cnt; j++) { + lm[top->ctl_lm_cnt].lm_reg_value = + ctl->ops.read_ctl_layers_for_splash(ctl, j + LM_0); + + if (lm[top->ctl_lm_cnt].lm_reg_value) { + lm[top->ctl_lm_cnt].ctl_id = index + CTL_0; + lm_ids[lm_cnt++] = j + LM_0; + lm[top->ctl_lm_cnt].lm_id = j + LM_0; + top->ctl_lm_cnt++; + } + } + + return top->ctl_lm_cnt; +} + +static void _sde_rm_get_ctl_top_for_splash(struct sde_hw_ctl *ctl, + struct splash_ctl_top *top) +{ + if (!ctl || !top) { + SDE_ERROR("invalid ctl or top\n"); + return; + } + + if (!ctl->ops.read_ctl_top_for_splash) { + SDE_ERROR("read_ctl_top not initialized\n"); + return; + } + + top->value = ctl->ops.read_ctl_top_for_splash(ctl); + top->intf_sel = (top->value >> 4) & 0xf; +} + +int sde_rm_read_resource_for_splash(struct sde_rm *rm, + void *splash_info, + struct sde_mdss_cfg *cat) +{ + struct sde_rm_hw_iter ctl_iter; + int index = 0; + struct sde_splash_info *sinfo; + struct sde_hw_ctl *ctl; + + if (!rm || !splash_info || !cat) + return -EINVAL; + + sinfo = (struct sde_splash_info *)splash_info; + + sde_rm_init_hw_iter(&ctl_iter, 0, SDE_HW_BLK_CTL); + + while (_sde_rm_get_hw_locked(rm, &ctl_iter)) { + ctl = (struct sde_hw_ctl *)ctl_iter.hw; + + _sde_rm_get_ctl_top_for_splash(ctl, + &sinfo->res.top[index]); + + if (sinfo->res.top[index].intf_sel) { + sinfo->res.lm_cnt += + _sde_rm_get_ctl_lm_for_splash(ctl, + cat->mixer_count, + sinfo->res.lm_cnt, + sinfo->res.lm_ids, + &sinfo->res.top[index], index); + + sinfo->res.ctl_ids[sinfo->res.ctl_top_cnt] = + index + CTL_0; + + sinfo->res.ctl_top_cnt++; + } + index++; + } + + SDE_DEBUG("%s: ctl_top_cnt=%d, lm_cnt=%d\n", __func__, + sinfo->res.ctl_top_cnt, sinfo->res.lm_cnt); + + return 0; +} diff --git a/drivers/gpu/drm/msm/sde/sde_rm.h b/drivers/gpu/drm/msm/sde/sde_rm.h index 87e95bfebe98..bec398a3b996 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.h +++ b/drivers/gpu/drm/msm/sde/sde_rm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -212,4 +212,14 @@ int sde_rm_check_property_topctl(uint64_t val); */ int sde_rm_check_property_topctl(uint64_t val); +/** + * sde_rm_read_resource_for_splash - read splash resource used in bootloader + * @rm: SDE Resource Manager handle + * @sinfo: handle for splash info + * @cat: Pointer to hardware catalog + */ +int sde_rm_read_resource_for_splash(struct sde_rm *rm, + void *sinfo, + struct sde_mdss_cfg *cat); + #endif /* __SDE_RM_H__ */ diff --git a/drivers/gpu/drm/msm/sde/sde_splash.c b/drivers/gpu/drm/msm/sde/sde_splash.c index f6bd7b040dcb..f124bd7d5904 100644 --- a/drivers/gpu/drm/msm/sde/sde_splash.c +++ b/drivers/gpu/drm/msm/sde/sde_splash.c @@ -22,6 +22,7 @@ #include "sde_hw_util.h" #include "sde_hw_intf.h" #include "sde_hw_catalog.h" +#include "sde_rm.h" #include "dsi_display.h" #include "sde_hdmi.h" @@ -35,11 +36,9 @@ #define SCRATCH_REGISTER_2 0x01C #define SDE_LK_RUNNING_VALUE 0xC001CAFE -#define SDE_LK_SHUT_DOWN_VALUE 0xDEADDEAD +#define SDE_LK_STOP_SPLASH_VALUE 0xDEADDEAD #define SDE_LK_EXIT_VALUE 0xDEADBEEF -#define SDE_LK_EXIT_MAX_LOOP 20 - #define INTF_HDMI_SEL (BIT(25) | BIT(24)) #define INTF_DSI0_SEL BIT(8) #define INTF_DSI1_SEL BIT(16) @@ -189,26 +188,14 @@ static bool _sde_splash_lk_check(struct sde_hw_intr *intr) } /** - * _sde_splash_notify_lk_to_exit. + * _sde_splash_notify_lk_stop_splash. * - * Function to monitor LK's status and tell it to exit. + * Function to stop early splash in LK. */ -static void _sde_splash_notify_lk_exit(struct sde_hw_intr *intr) +static inline void _sde_splash_notify_lk_stop_splash(struct sde_hw_intr *intr) { - int i = 0; - - /* first is to write exit signal to scratch register*/ - SDE_REG_WRITE(&intr->hw, SCRATCH_REGISTER_1, SDE_LK_SHUT_DOWN_VALUE); - - while ((SDE_LK_EXIT_VALUE != - SDE_REG_READ(&intr->hw, SCRATCH_REGISTER_1)) && - (++i < SDE_LK_EXIT_MAX_LOOP)) { - DRM_INFO("wait for LK's exit"); - msleep(20); - } - - if (i == SDE_LK_EXIT_MAX_LOOP) - SDE_ERROR("Loop LK's exit failed\n"); + /* write splash stop signal to scratch register*/ + SDE_REG_WRITE(&intr->hw, SCRATCH_REGISTER_1, SDE_LK_STOP_SPLASH_VALUE); } static int _sde_splash_gem_new(struct drm_device *dev, @@ -326,31 +313,28 @@ static void _sde_splash_sent_pipe_update_uevent(struct sde_kms *sde_kms) kfree(event_string); } -static void _sde_splash_get_connector_ref_cnt(struct sde_splash_info *sinfo, - u32 *hdmi_cnt, u32 *dsi_cnt) +static int _sde_splash_free_module_resource(struct msm_mmu *mmu, + struct sde_splash_info *sinfo) { - mutex_lock(&sde_splash_lock); - *hdmi_cnt = sinfo->hdmi_connector_cnt; - *dsi_cnt = sinfo->dsi_connector_cnt; - mutex_unlock(&sde_splash_lock); -} + int i = 0; + struct msm_gem_object *msm_obj; -static int _sde_splash_free_resource(struct msm_mmu *mmu, - struct sde_splash_info *sinfo, enum splash_connector_type conn) -{ - struct msm_gem_object *msm_obj = to_msm_bo(sinfo->obj[conn]); + for (i = 0; i < sinfo->splash_mem_num; i++) { + msm_obj = to_msm_bo(sinfo->obj[i]); - if (!msm_obj) - return -EINVAL; + if (!msm_obj) + return -EINVAL; - if (mmu->funcs && mmu->funcs->unmap) - mmu->funcs->early_splash_unmap(mmu, - sinfo->splash_mem_paddr[conn], msm_obj->sgt); + if (mmu->funcs && mmu->funcs->unmap) + mmu->funcs->early_splash_unmap(mmu, + sinfo->splash_mem_paddr[i], msm_obj->sgt); - _sde_splash_free_bootup_memory_to_system(sinfo->splash_mem_paddr[conn], - sinfo->splash_mem_size[conn]); + _sde_splash_free_bootup_memory_to_system( + sinfo->splash_mem_paddr[i], + sinfo->splash_mem_size[i]); - _sde_splash_destroy_gem_object(msm_obj); + _sde_splash_destroy_gem_object(msm_obj); + } return 0; } @@ -359,6 +343,7 @@ __ref int sde_splash_init(struct sde_power_handle *phandle, struct msm_kms *kms) { struct sde_kms *sde_kms; struct sde_splash_info *sinfo; + int ret = 0; int i = 0; if (!phandle || !kms) { @@ -372,22 +357,27 @@ __ref int sde_splash_init(struct sde_power_handle *phandle, struct msm_kms *kms) sinfo->dsi_connector_cnt = 0; sinfo->hdmi_connector_cnt = 0; + /* Vote data bus after splash is enabled in bootloader */ sde_power_data_bus_bandwidth_ctrl(phandle, sde_kms->core_client, true); for (i = 0; i < sinfo->splash_mem_num; i++) { if (!memblock_is_reserved(sinfo->splash_mem_paddr[i])) { - SDE_ERROR("failed to reserve memory\n"); + SDE_ERROR("LK's splash memory is not reserved\n"); /* withdraw the vote when failed. */ sde_power_data_bus_bandwidth_ctrl(phandle, sde_kms->core_client, false); - return -EINVAL; + ret = -EINVAL; + break; } } - return 0; + ret = sde_rm_read_resource_for_splash(&sde_kms->rm, + (void *)sinfo, sde_kms->catalog); + + return ret; } void sde_splash_destroy(struct sde_splash_info *sinfo, @@ -572,12 +562,12 @@ int sde_splash_get_handoff_status(struct msm_kms *kms) if (num_of_display_on) { sinfo->handoff = true; - sinfo->program_scratch_regs = true; + sinfo->display_splash_enabled = true; sinfo->lk_is_exited = false; sinfo->intf_sel_status = intf_sel; } else { sinfo->handoff = false; - sinfo->program_scratch_regs = false; + sinfo->display_splash_enabled = false; sinfo->lk_is_exited = true; } @@ -703,29 +693,34 @@ void sde_splash_setup_connector_count(struct sde_splash_info *sinfo, } } -bool sde_splash_get_lk_complete_status(struct sde_splash_info *sinfo) +bool sde_splash_get_lk_complete_status(struct msm_kms *kms) { - bool ret = 0; + struct sde_kms *sde_kms = to_sde_kms(kms); + struct sde_hw_intr *intr; - mutex_lock(&sde_splash_lock); - ret = !sinfo->handoff && !sinfo->lk_is_exited; - mutex_unlock(&sde_splash_lock); + if (!sde_kms || !sde_kms->hw_intr) { + SDE_ERROR("invalid kms\n"); + return false; + } - return ret; + intr = sde_kms->hw_intr; + + if (sde_kms->splash_info.handoff && + SDE_LK_EXIT_VALUE == SDE_REG_READ(&intr->hw, + SCRATCH_REGISTER_1)) { + SDE_DEBUG("LK totoally exits\n"); + return true; + } + + return false; } -int sde_splash_clean_up_free_resource(struct msm_kms *kms, - struct sde_power_handle *phandle, - int connector_type, void *display) +int sde_splash_free_resource(struct msm_kms *kms, + struct sde_power_handle *phandle) { struct sde_kms *sde_kms; struct sde_splash_info *sinfo; struct msm_mmu *mmu; - struct dsi_display *dsi_display = display; - int ret = 0; - int hdmi_conn_count = 0; - int dsi_conn_count = 0; - static const char *last_commit_display_type = "unknown"; if (!phandle || !kms) { SDE_ERROR("invalid phandle/kms.\n"); @@ -739,88 +734,49 @@ int sde_splash_clean_up_free_resource(struct msm_kms *kms, return -EINVAL; } - _sde_splash_get_connector_ref_cnt(sinfo, &hdmi_conn_count, - &dsi_conn_count); - mutex_lock(&sde_splash_lock); - if (hdmi_conn_count == 0 && dsi_conn_count == 0 && - !sinfo->lk_is_exited) { - /* When both hdmi's and dsi's handoff are finished, - * 1. Destroy splash node objects. - * 2. Release the memory which LK's stack is running on. - * 3. Withdraw AHB data bus bandwidth voting. - */ - DRM_INFO("HDMI and DSI resource handoff is completed\n"); - - sinfo->lk_is_exited = true; - - _sde_splash_destroy_splash_node(sinfo); - - _sde_splash_free_bootup_memory_to_system(sinfo->lk_pool_paddr, - sinfo->lk_pool_size); - - sde_power_data_bus_bandwidth_ctrl(phandle, - sde_kms->core_client, false); - - _sde_splash_sent_pipe_update_uevent(sde_kms); - + if (!sinfo->handoff) { mutex_unlock(&sde_splash_lock); return 0; } mmu = sde_kms->aspace[0]->mmu; - - switch (connector_type) { - case DRM_MODE_CONNECTOR_HDMIA: - if (sinfo->hdmi_connector_cnt == 1) { - sinfo->hdmi_connector_cnt--; - - ret = _sde_splash_free_resource(mmu, - sinfo, SPLASH_HDMI); - } - break; - case DRM_MODE_CONNECTOR_DSI: - /* - * Basically, we have commits coming on two DSI connectors. - * So when releasing DSI resource, it's ensured that the - * coming commits should happen on different DSIs, to promise - * the handoff has finished on the two DSIs, then it's safe - * to release DSI resource, otherwise, problem happens when - * freeing memory, while DSI0 or DSI1 is still visiting - * the memory. - */ - if (strcmp(dsi_display->display_type, "unknown") && - strcmp(last_commit_display_type, - dsi_display->display_type)) { - if (sinfo->dsi_connector_cnt > 1) - sinfo->dsi_connector_cnt--; - else if (sinfo->dsi_connector_cnt == 1) { - ret = _sde_splash_free_resource(mmu, - sinfo, SPLASH_DSI); - - sinfo->dsi_connector_cnt--; - } - - last_commit_display_type = dsi_display->display_type; - } - break; - default: - ret = -EINVAL; - SDE_ERROR("%s: invalid connector_type %d\n", - __func__, connector_type); + if (!mmu) { + mutex_unlock(&sde_splash_lock); + return -EINVAL; } - mutex_unlock(&sde_splash_lock); + /* free HDMI's, DSI's and early camera's reserved memory */ + _sde_splash_free_module_resource(mmu, sinfo); - return ret; + _sde_splash_destroy_splash_node(sinfo); + + /* free lk_pool heap memory */ + _sde_splash_free_bootup_memory_to_system(sinfo->lk_pool_paddr, + sinfo->lk_pool_size); + + /* withdraw data bus vote */ + sde_power_data_bus_bandwidth_ctrl(phandle, + sde_kms->core_client, false); + + /* send uevent to notify user to recycle resource */ + _sde_splash_sent_pipe_update_uevent(sde_kms); + + /* Finally mark handoff flag to false to say handoff is complete */ + sinfo->handoff = false; + + DRM_INFO("HDMI and DSI resource handoff is completed\n"); + + mutex_unlock(&sde_splash_lock); + return 0; } /* * In below function, it will - * 1. Notify LK to exit and wait for exiting is done. + * 1. Notify LK to stop display splash. * 2. Set DOMAIN_ATTR_EARLY_MAP to 1 to enable stage 1 translation in iommu. */ -int sde_splash_clean_up_exit_lk(struct msm_kms *kms) +int sde_splash_lk_stop_splash(struct msm_kms *kms) { struct sde_splash_info *sinfo; struct msm_mmu *mmu; @@ -836,12 +792,11 @@ int sde_splash_clean_up_exit_lk(struct msm_kms *kms) /* Monitor LK's status and tell it to exit. */ mutex_lock(&sde_splash_lock); - if (sinfo->program_scratch_regs) { + if (sinfo->display_splash_enabled) { if (_sde_splash_lk_check(sde_kms->hw_intr)) - _sde_splash_notify_lk_exit(sde_kms->hw_intr); + _sde_splash_notify_lk_stop_splash(sde_kms->hw_intr); - sinfo->handoff = false; - sinfo->program_scratch_regs = false; + sinfo->display_splash_enabled = false; } mutex_unlock(&sde_splash_lock); @@ -858,7 +813,8 @@ int sde_splash_clean_up_exit_lk(struct msm_kms *kms) */ if (mmu->funcs && mmu->funcs->set_property) { ret = mmu->funcs->set_property(mmu, - DOMAIN_ATTR_EARLY_MAP, &sinfo->handoff); + DOMAIN_ATTR_EARLY_MAP, + &sinfo->display_splash_enabled); if (ret) SDE_ERROR("set_property failed\n"); diff --git a/drivers/gpu/drm/msm/sde/sde_splash.h b/drivers/gpu/drm/msm/sde/sde_splash.h index 9eddd87e5e26..2fd8ba03112f 100644 --- a/drivers/gpu/drm/msm/sde/sde_splash.h +++ b/drivers/gpu/drm/msm/sde/sde_splash.h @@ -15,18 +15,46 @@ #include "msm_kms.h" #include "msm_mmu.h" +#include "sde_hw_mdss.h" + +#define SPLASH_CTL_MAX 5 +#define SPLASH_LM_MAX 7 enum splash_connector_type { SPLASH_DSI = 0, SPLASH_HDMI, }; +struct splash_lm_hw { + u8 lm_id; + u8 ctl_id; + u32 lm_reg_value; +}; + +struct splash_ctl_top { + u32 value; + u8 intf_sel; + u8 ctl_lm_cnt; + struct splash_lm_hw lm[SPLASH_LM_MAX]; +}; + +struct sde_res_data { + struct splash_ctl_top top[SPLASH_CTL_MAX]; + u8 ctl_ids[SPLASH_CTL_MAX]; + u8 lm_ids[SPLASH_LM_MAX]; + u8 ctl_top_cnt; + u8 lm_cnt; +}; + struct sde_splash_info { /* handoff flag */ bool handoff; - /* flag of display scratch registers */ - bool program_scratch_regs; + /* current hw configuration */ + struct sde_res_data res; + + /* flag of display splash status */ + bool display_splash_enabled; /* to indicate LK is totally exited */ bool lk_is_exited; @@ -91,21 +119,20 @@ void sde_splash_setup_connector_count(struct sde_splash_info *sinfo, int connector_type); /** - * sde_splash_clean_up_exit_lk. + * sde_splash_lk_stop_splash. * - * Tell LK to exit, and clean up the resource. + * Tell LK to stop display splash. */ -int sde_splash_clean_up_exit_lk(struct msm_kms *kms); +int sde_splash_lk_stop_splash(struct msm_kms *kms); /** - * sde_splash_clean_up_free_resource. + * sde_splash_free_resource. * * According to input connector_type, free * HDMI's and DSI's resource respectively. */ -int sde_splash_clean_up_free_resource(struct msm_kms *kms, - struct sde_power_handle *phandle, - int connector_type, void *display); +int sde_splash_free_resource(struct msm_kms *kms, + struct sde_power_handle *phandle); /** * sde_splash_parse_memory_dt. @@ -152,7 +179,7 @@ void sde_splash_destroy(struct sde_splash_info *sinfo, * * Get LK's status to check if it has been stopped. */ -bool sde_splash_get_lk_complete_status(struct sde_splash_info *sinfo); +bool sde_splash_get_lk_complete_status(struct msm_kms *kms); /** * sde_splash_setup_display_resource