From 06af1c706e41c15424c67b81359a5f28de4c1dab Mon Sep 17 00:00:00 2001 From: Guchun Chen Date: Wed, 25 Apr 2018 22:13:43 +0800 Subject: [PATCH 1/3] drm: msm: hw ctl needs to care reserved pipe When kernel operates hw ctls, early RVC in bootloader can also co-exist to update hardware registers. So it's needed to care the reserved pipe by early RVC before kernel starts to handle ctl setup. CRs-Fixed: 2225630 Change-Id: I2df06350a44bd128dfb89cc0668c41d2edfb26a6 Signed-off-by: Guchun Chen --- drivers/gpu/drm/msm/sde/sde_crtc.c | 21 ++++++++- drivers/gpu/drm/msm/sde/sde_hw_ctl.c | 66 ++++++++++++++++++++++++++-- drivers/gpu/drm/msm/sde/sde_hw_ctl.h | 14 ++++-- 3 files changed, 92 insertions(+), 9 deletions(-) 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..9762e69d87e0 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; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h index 74dbde92639a..c0e34bf027f6 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,25 @@ 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); }; /** From 6fb8f903dfe7abb298f38ff301e361ab9acd3fd7 Mon Sep 17 00:00:00 2001 From: Guchun Chen Date: Fri, 20 Apr 2018 19:10:04 +0800 Subject: [PATCH 2/3] drm: msm: read SDE registers for splash case Read sde registers like layer mixer and ctrl information from bootloader when early splash is enabled in bootloader. These information will be updated to kernel resource manager to let bootloader and kernel use the same hardware setting. CRs-Fixed: 2225630 Change-Id: I0e971de1992b380e30933f476b1a7e185ce0ad96 Signed-off-by: Guchun Chen Signed-off-by: Camus Wong --- drivers/gpu/drm/msm/sde/sde_hw_ctl.c | 34 ++++ drivers/gpu/drm/msm/sde/sde_hw_ctl.h | 15 ++ drivers/gpu/drm/msm/sde/sde_rm.c | 248 ++++++++++++++++++++++++++- drivers/gpu/drm/msm/sde/sde_rm.h | 12 +- drivers/gpu/drm/msm/sde/sde_splash.c | 12 +- drivers/gpu/drm/msm/sde/sde_splash.h | 28 +++ 6 files changed, 336 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c index 9762e69d87e0..341738f624db 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c @@ -516,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) { @@ -536,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 c0e34bf027f6..a008ecf4a11d 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h @@ -165,6 +165,21 @@ struct sde_hw_ctl_ops { void (*setup_blendstage)(struct sde_hw_ctl *ctx, 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_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..71ebb4b37d6b 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" @@ -359,6 +360,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) { @@ -377,17 +379,21 @@ __ref int sde_splash_init(struct sde_power_handle *phandle, struct msm_kms *kms) 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, diff --git a/drivers/gpu/drm/msm/sde/sde_splash.h b/drivers/gpu/drm/msm/sde/sde_splash.h index 9eddd87e5e26..0608b2db878d 100644 --- a/drivers/gpu/drm/msm/sde/sde_splash.h +++ b/drivers/gpu/drm/msm/sde/sde_splash.h @@ -15,16 +15,44 @@ #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; + /* current hw configuration */ + struct sde_res_data res; + /* flag of display scratch registers */ bool program_scratch_regs; From c96659a88a12f1363d2cebbc9bd73eb3cafda1ea Mon Sep 17 00:00:00 2001 From: Guchun Chen Date: Fri, 13 Apr 2018 17:29:40 +0800 Subject: [PATCH 3/3] drm: msm: improve early splash mechanism Early RVC will keep running even if all display's splash in bootloader are stopped. So kernel splash handoff mechanism needs to be modified to match the scenario. CRs-Fixed: 2225630 Change-Id: I0e00403f6cae0401ea23465f78cf092abfa2a611 Signed-off-by: Guchun Chen --- drivers/gpu/drm/msm/sde/sde_connector.c | 13 +- drivers/gpu/drm/msm/sde/sde_kms.c | 5 +- drivers/gpu/drm/msm/sde/sde_splash.c | 204 +++++++++--------------- drivers/gpu/drm/msm/sde/sde_splash.h | 19 ++- 4 files changed, 91 insertions(+), 150 deletions(-) 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_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_splash.c b/drivers/gpu/drm/msm/sde/sde_splash.c index 71ebb4b37d6b..f124bd7d5904 100644 --- a/drivers/gpu/drm/msm/sde/sde_splash.c +++ b/drivers/gpu/drm/msm/sde/sde_splash.c @@ -36,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) @@ -190,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, @@ -327,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; } @@ -374,6 +357,7 @@ __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); @@ -578,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; } @@ -709,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"); @@ -745,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; @@ -842,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); @@ -864,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 0608b2db878d..2fd8ba03112f 100644 --- a/drivers/gpu/drm/msm/sde/sde_splash.h +++ b/drivers/gpu/drm/msm/sde/sde_splash.h @@ -53,8 +53,8 @@ struct sde_splash_info { /* current hw configuration */ struct sde_res_data res; - /* flag of display scratch registers */ - bool program_scratch_regs; + /* flag of display splash status */ + bool display_splash_enabled; /* to indicate LK is totally exited */ bool lk_is_exited; @@ -119,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. @@ -180,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