From 85236a55a7ae729a0e56696bba9dc8c4745f32d2 Mon Sep 17 00:00:00 2001 From: Carl Vanderlip Date: Wed, 30 Oct 2013 19:09:14 -0700 Subject: msm: mdss: Control histogram intr and clk in suspend cases Histogram interrupts, and clocks used to handle interrupts, need to be enabled in sync with histogram requests. To ensure that these interrupts and clocks are disabled when screen is suspended or not updating, a control structure is introduced that allows for these resources to be gated by specifying transitions from when they should and should not be active. CRs-Fixed: 568049 Change-Id: I77a50085ee38de597092c273c7ce9974c7bb29c3 Signed-off-by: Carl Vanderlip --- drivers/video/fbdev/msm/mdss.h | 15 +++ drivers/video/fbdev/msm/mdss_mdp.c | 5 + drivers/video/fbdev/msm/mdss_mdp.h | 6 +- drivers/video/fbdev/msm/mdss_mdp_ctl.c | 5 + drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c | 4 + drivers/video/fbdev/msm/mdss_mdp_overlay.c | 9 +- drivers/video/fbdev/msm/mdss_mdp_pp.c | 156 +++++++++++++++++++++++++++- 7 files changed, 189 insertions(+), 11 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 5044c7332283..4219395883b6 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -71,6 +71,19 @@ struct mdss_fudge_factor { u32 denom; }; +#define MDSS_IRQ_SUSPEND -1 +#define MDSS_IRQ_RESUME 1 +#define MDSS_IRQ_REQ 0 + +struct mdss_intr { + /* requested intr */ + u32 req; + /* currently enabled intr */ + u32 curr; + int state; + spinlock_t lock; +}; + struct mdss_data_type { u32 mdp_rev; struct clk *mdp_clk[MDSS_MAX_CLK]; @@ -149,6 +162,8 @@ struct mdss_data_type { u32 nad_cfgs; struct workqueue_struct *ad_calc_wq; + struct mdss_intr hist_intr; + struct ion_client *iclient; int iommu_attached; struct mdss_iommu_map_type *iommu_map; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index be587ef4e9e5..c31f176af475 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1010,6 +1010,11 @@ static u32 mdss_mdp_res_init(struct mdss_data_type *mdata) if (rc) return rc; + mdata->hist_intr.req = 0; + mdata->hist_intr.curr = 0; + mdata->hist_intr.state = 0; + spin_lock_init(&mdata->hist_intr.lock); + mdata->iclient = msm_ion_client_create(-1, mdata->pdev->name); if (IS_ERR_OR_NULL(mdata->iclient)) { pr_err("msm_ion_client_create() return error (%p)\n", diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index e06e712860d3..1afc7d894eb6 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -582,8 +582,10 @@ int mdss_mdp_hist_lut_config(struct mdp_hist_lut_data *config, u32 *copyback); int mdss_mdp_dither_config(struct mdp_dither_cfg_data *config, u32 *copyback); int mdss_mdp_gamut_config(struct mdp_gamut_cfg_data *config, u32 *copyback); -int mdss_mdp_histogram_start(struct mdp_histogram_start_req *req); -int mdss_mdp_histogram_stop(u32 block); +int mdss_mdp_hist_intr_req(struct mdss_intr *intr, u32 bits, bool en); +int mdss_mdp_hist_intr_setup(struct mdss_intr *intr, int state); +int mdss_mdp_hist_start(struct mdp_histogram_start_req *req); +int mdss_mdp_hist_stop(u32 block); int mdss_mdp_hist_collect(struct mdp_histogram_data *hist); void mdss_mdp_hist_intr_done(u32 isr); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index f45324c7ddf7..4701e7fc5989 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -1128,6 +1128,7 @@ static int mdss_mdp_ctl_start_sub(struct mdss_mdp_ctl *ctl) int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl) { struct mdss_mdp_ctl *sctl; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); int ret = 0; if (ctl->power_on) { @@ -1174,6 +1175,7 @@ int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl) mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_PACK_3D, 0); } } + mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_RESUME); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); error: @@ -1186,6 +1188,7 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl) { struct mdss_mdp_ctl *sctl; int ret = 0; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); u32 off; if (!ctl->power_on) { @@ -1201,6 +1204,8 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl) mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_SUSPEND); + if (ctl->stop_fnc) ret = ctl->stop_fnc(ctl); else diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index c490862df453..76a64090db19 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -176,12 +176,14 @@ static int mdss_mdp_cmd_tearcheck_setup(struct mdss_mdp_ctl *ctl) static inline void mdss_mdp_cmd_clk_on(struct mdss_mdp_cmd_ctx *ctx) { unsigned long flags; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); mutex_lock(&ctx->clk_mtx); if (!ctx->clk_enabled) { ctx->clk_enabled = 1; mdss_mdp_ctl_intf_event (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_RESUME); } spin_lock_irqsave(&ctx->clk_lock, flags); if (!ctx->rdptr_enabled) @@ -194,6 +196,7 @@ static inline void mdss_mdp_cmd_clk_on(struct mdss_mdp_cmd_ctx *ctx) static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx) { unsigned long flags; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); int set_clk_off = 0; mutex_lock(&ctx->clk_mtx); @@ -204,6 +207,7 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx) if (ctx->clk_enabled && set_clk_off) { ctx->clk_enabled = 0; + mdss_mdp_hist_intr_setup(&mdata->hist_intr, MDSS_IRQ_SUSPEND); mdss_mdp_ctl_intf_event (ctx->ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)0); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 4b9030774b22..43a0be5ad662 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -546,11 +546,10 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, pipe->pp_cfg.hist_cfg.frame_cnt; hist.bit_mask = pipe->pp_cfg.hist_cfg.bit_mask; hist.num_bins = pipe->pp_cfg.hist_cfg.num_bins; - mdss_mdp_histogram_start(&hist); + mdss_mdp_hist_start(&hist); } else if (pipe->pp_cfg.hist_cfg.ops & MDP_PP_OPS_DISABLE) { - mdss_mdp_histogram_stop( - pipe->pp_cfg.hist_cfg.block); + mdss_mdp_hist_stop(pipe->pp_cfg.hist_cfg.block); } } len = pipe->pp_cfg.hist_lut_cfg.len; @@ -2065,7 +2064,7 @@ static int mdss_mdp_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd, if (ret) return ret; - ret = mdss_mdp_histogram_start(&hist_req); + ret = mdss_mdp_hist_start(&hist_req); break; case MSMFB_HISTOGRAM_STOP: @@ -2073,7 +2072,7 @@ static int mdss_mdp_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd, if (ret) return ret; - ret = mdss_mdp_histogram_stop(block); + ret = mdss_mdp_hist_stop(block); if (ret) return ret; diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index e7f209e64448..c7cc825070cc 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -2758,6 +2758,8 @@ static int pp_histogram_enable(struct pp_hist_col_info *hist_info, { unsigned long flag; int ret = 0; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + mutex_lock(&hist_info->hist_mutex); /* check if it is idle */ if (hist_info->col_en) { @@ -2777,7 +2779,7 @@ static int pp_histogram_enable(struct pp_hist_col_info *hist_info, hist_info->col_en = true; spin_unlock_irqrestore(&hist_info->hist_lock, flag); hist_info->is_kick_ready = true; - mdss_mdp_hist_irq_enable(3 << shift_bit); + mdss_mdp_hist_intr_req(&mdata->hist_intr, 3 << shift_bit, true); writel_relaxed(req->frame_cnt, ctl_base + 8); /* Kick out reset start */ writel_relaxed(1, ctl_base + 4); @@ -2786,7 +2788,7 @@ exit: return ret; } -int mdss_mdp_histogram_start(struct mdp_histogram_start_req *req) +int mdss_mdp_hist_start(struct mdp_histogram_start_req *req) { u32 done_shift_bit; char __iomem *ctl_base; @@ -2869,6 +2871,8 @@ static int pp_histogram_disable(struct pp_hist_col_info *hist_info, { int ret = 0; unsigned long flag; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + mutex_lock(&hist_info->hist_mutex); if (hist_info->col_en == false) { pr_debug("Histogram already disabled (%d)", (u32) ctl_base); @@ -2881,7 +2885,7 @@ static int pp_histogram_disable(struct pp_hist_col_info *hist_info, hist_info->col_state = HIST_UNKNOWN; spin_unlock_irqrestore(&hist_info->hist_lock, flag); hist_info->is_kick_ready = false; - mdss_mdp_hist_irq_disable(done_bit); + mdss_mdp_hist_intr_req(&mdata->hist_intr, done_bit, false); writel_relaxed(BIT(1), ctl_base);/* cancel */ ret = 0; exit: @@ -2889,7 +2893,7 @@ exit: return ret; } -int mdss_mdp_histogram_stop(u32 block) +int mdss_mdp_hist_stop(u32 block) { int i, ret = 0; char __iomem *ctl_base; @@ -2966,6 +2970,150 @@ hist_stop_exit: return ret; } +/** + * mdss_mdp_hist_intr_req() - Request changes the histogram interupts + * @intr: structure containting state of interrupt register + * @bits: the bits on interrupt register that should be changed + * @en: true if bits should be set, false if bits should be cleared + * + * Adds or removes the bits from the interrupt request. + * + * Does not store reference count for each bit. I.e. a bit with multiple + * enable requests can be disabled with a single disable request. + * + * Return: 0 if uneventful, errno on invalid input + */ +int mdss_mdp_hist_intr_req(struct mdss_intr *intr, u32 bits, bool en) +{ + unsigned long flag; + int ret = 0; + if (!intr) { + pr_err("NULL addr passed, %p", intr); + return -EINVAL; + } + + spin_lock_irqsave(&intr->lock, flag); + if (en) + intr->req |= bits; + else + intr->req &= ~bits; + spin_unlock_irqrestore(&intr->lock, flag); + + mdss_mdp_hist_intr_setup(intr, MDSS_IRQ_REQ); + + return ret; +} + + +#define MDSS_INTR_STATE_ACTIVE 1 +#define MDSS_INTR_STATE_NULL 0 +#define MDSS_INTR_STATE_SUSPEND -1 + +/** + * mdss_mdp_hist_intr_setup() - Manage intr and clk depending on requests. + * @intr: structure containting state of intr reg + * @state: MDSS_IRQ_SUSPEND if suspend is needed, + * MDSS_IRQ_RESUME if resume is needed, + * MDSS_IRQ_REQ if neither (i.e. requesting an interrupt) + * + * This function acts as a gatekeeper for the interrupt, making sure that the + * MDP clocks are enabled while the interrupts are enabled to prevent + * unclocked accesses. + * + * To reduce code repetition, 4 state transitions have been encoded here. Each + * transition updates the interrupt's state structure (mdss_intr) to reflect + * the which bits have been requested (intr->req), are currently enabled + * (intr->curr), as well as defines which interrupt bits need to be enabled or + * disabled ('en' and 'dis' respectively). The 4th state is not explicity + * coded in the if/else chain, but is for MDSS_IRQ_REQ's when the interrupt + * is in suspend, in which case, the only change required (intr->req being + * updated) has already occured in the calling function. + * + * To control the clock, which can't be requested while holding the spinlock, + * the inital state is compared with the exit state to detect when the + * interrupt needs a clock. + * + * The clock requests surrounding the majority of this function serve to + * enable the register writes to change the interrupt register, as well as to + * prevent a race condition that could keep the clocks on (due to mdp_clk_cnt + * never being decremented below 0) when a enable/disable occurs but the + * disable requests the clocks disabled before the enable is able to request + * the clocks enabled. + * + * Return: 0 if uneventful, errno on repeated action or invalid input + */ +int mdss_mdp_hist_intr_setup(struct mdss_intr *intr, int type) +{ + unsigned long flag; + int ret = 0, req_clk = 0; + u32 en = 0, dis = 0; + u32 diff, init_curr; + int init_state; + if (!intr) { + WARN(1, "NULL intr pointer"); + return -EINVAL; + } + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + spin_lock_irqsave(&intr->lock, flag); + + init_state = intr->state; + init_curr = intr->curr; + + if (type == MDSS_IRQ_RESUME) { + /* resume intrs */ + if (intr->state == MDSS_INTR_STATE_ACTIVE) { + ret = -EPERM; + goto exit; + } + en = intr->req; + dis = 0; + intr->curr = intr->req; + intr->state = intr->curr ? + MDSS_INTR_STATE_ACTIVE : MDSS_INTR_STATE_NULL; + } else if (type == MDSS_IRQ_SUSPEND) { + /* suspend intrs */ + if (intr->state == MDSS_INTR_STATE_SUSPEND) { + ret = -EPERM; + goto exit; + } + en = 0; + dis = intr->curr; + intr->curr = 0; + intr->state = MDSS_INTR_STATE_SUSPEND; + } else if (intr->state != MDSS_IRQ_SUSPEND) { + /* Not resuming/suspending or in suspend state */ + diff = intr->req ^ intr->curr; + en = diff & ~intr->curr; + dis = diff & ~intr->req; + intr->curr = intr->req; + intr->state = intr->curr ? + MDSS_INTR_STATE_ACTIVE : MDSS_INTR_STATE_NULL; + } + + if (en) + mdss_mdp_hist_irq_enable(en); + if (dis) + mdss_mdp_hist_irq_disable(dis); + + if ((init_state != MDSS_INTR_STATE_ACTIVE) && + (intr->state == MDSS_INTR_STATE_ACTIVE)) + req_clk = 1; + else if ((init_state == MDSS_INTR_STATE_ACTIVE) && + (intr->state != MDSS_INTR_STATE_ACTIVE)) + req_clk = -1; + +exit: + spin_unlock_irqrestore(&intr->lock, flag); + if (req_clk < 0) + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + else if (req_clk > 0) + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + return ret; +} + static int pp_hist_collect(struct mdp_histogram_data *hist, struct pp_hist_col_info *hist_info, char __iomem *ctl_base) -- cgit v1.2.3