diff options
| -rw-r--r-- | drivers/video/fbdev/msm/mdss.h | 15 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.c | 5 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.h | 6 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_ctl.c | 5 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c | 4 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_overlay.c | 9 | ||||
| -rw-r--r-- | 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) |
