diff --git a/Documentation/devicetree/bindings/fb/mdss-mdp.txt b/Documentation/devicetree/bindings/fb/mdss-mdp.txt index a616427f21ea..6303168bb966 100644 --- a/Documentation/devicetree/bindings/fb/mdss-mdp.txt +++ b/Documentation/devicetree/bindings/fb/mdss-mdp.txt @@ -449,6 +449,11 @@ Fudge Factors: Fudge factors are used to boost demand for - qcom,max-pipe-width: This value specifies the maximum MDP SSPP width the device supports. If not specified, a default value of 2048 will be applied. +- qcom,mdss-slave-pingpong-off: Offset address for the extra TE block which needs + to be programmed when pingpong split feature is enabled. + Offset is calculated from the "mdp_phys" + register value. Mandatory when qcom,mdss-has-dst-split + is enabled. Optional subnodes: - mdss_fb: Child nodes representing the frame buffer virtual devices. @@ -666,6 +671,7 @@ Example: 0x00021500 0x00021700>; qcom,mdss-cdm-off = <0x0007A200>; qcom,mdss-ppb-off = <0x0000420>; + qcom,mdss-slave-pingpong-off = <0x00073000> /* buffer parameters to calculate prefill bandwidth */ qcom,mdss-prefill-outstanding-buffer-bytes = <1024>; diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 1c62a75ba796..b20f0df1c343 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -314,8 +314,10 @@ struct mdss_data_type { u32 max_target_zorder; u8 ncursor_pipes; u32 max_cursor_size; + u32 nppb; struct mdss_mdp_ppb *ppb; + char __iomem *slave_pingpong_base; struct mdss_mdp_mixer *mixer_intf; struct mdss_mdp_mixer *mixer_wb; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index be77adf49660..fe4f89f31756 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -2695,7 +2695,7 @@ static void mdss_mdp_parse_vbif_qos(struct platform_device *pdev) static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) { struct mdss_data_type *mdata = platform_get_drvdata(pdev); - u32 data; + u32 data, slave_pingpong_off; const char *wfd_data; int rc; struct property *prop = NULL; @@ -2763,6 +2763,18 @@ static int mdss_mdp_parse_dt_misc(struct platform_device *pdev) mdata->has_pingpong_split = of_property_read_bool(pdev->dev.of_node, "qcom,mdss-has-dst-split"); + if (mdata->has_pingpong_split) { + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,mdss-slave-pingpong-off", + &slave_pingpong_off); + if (rc) { + pr_err("Error in device tree: slave pingpong offset\n"); + return rc; + } + mdata->slave_pingpong_base = mdata->mdss_io.base + + slave_pingpong_off; + } + /* * 2x factor on AB because bus driver will divide by 2 * due to 2x ports to BIMC diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index fa04ed447766..49c6625fcc30 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -80,6 +80,9 @@ #define CURSOR_PIPE_LEFT 0 #define CURSOR_PIPE_RIGHT 1 +#define MASTER_CTX 0 +#define SLAVE_CTX 1 + /* * Recommendation is to have different ot depending on the fps * and resolution, but since current SW doesn't support different @@ -300,6 +303,7 @@ struct mdss_mdp_ctl { struct blocking_notifier_head notifier_head; void *priv_data; + void *intf_ctx[2]; u32 wb_type; bool prg_fet; diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 32cbc3dbf1a3..9d13151d01a2 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -2399,6 +2399,12 @@ static void mdss_mdp_ctl_split_display_enable(int enable, lower |= BIT(4); else lower |= BIT(8); + /* + * Enable SMART_PANEL_FREE_RUN if ping pong split + * is enabled. + */ + if (is_pingpong_split(main_ctl->mfd)) + lower |= BIT(2); upper = lower; } else { /* interface controlling sw trigger (video mode) */ diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index fafe032e5c62..e2ab75167023 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -61,6 +61,7 @@ struct mdss_mdp_cmd_ctx { struct mdss_intf_recovery intf_recovery; struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */ u32 pp_timeout_report_cnt; + int pingpong_split_slave; }; struct mdss_mdp_cmd_ctx mdss_mdp_cmd_ctx_list[MAX_SESSIONS]; @@ -122,14 +123,15 @@ exit: } -static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_ctl *ctl, - struct mdss_mdp_mixer *mixer, bool enable) +static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_mixer *mixer, + struct mdss_mdp_cmd_ctx *ctx, bool enable) { struct mdss_mdp_pp_tear_check *te = NULL; struct mdss_panel_info *pinfo; u32 vsync_clk_speed_hz, total_lines, vclks_line, cfg = 0; char __iomem *pingpong_base; - struct mdss_mdp_cmd_ctx *ctx = ctl->priv_data; + struct mdss_mdp_ctl *ctl = ctx->ctl; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); if (IS_ERR_OR_NULL(ctl->panel_data)) { pr_err("no panel data\n"); @@ -174,9 +176,9 @@ static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_ctl *ctl, } pingpong_base = mixer->pingpong_base; - /* for dst split pp_num is cmd session (0 and 1) */ - if (is_pingpong_split(ctl->mfd)) - pingpong_base += ctx->pp_num * SPLIT_MIXER_OFFSET; + + if (ctx->pingpong_split_slave) + pingpong_base = mdata->slave_pingpong_base; mdss_mdp_pingpong_write(pingpong_base, MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC, cfg); @@ -203,14 +205,16 @@ static int mdss_mdp_cmd_tearcheck_cfg(struct mdss_mdp_ctl *ctl, return 0; } -static int mdss_mdp_cmd_tearcheck_setup(struct mdss_mdp_ctl *ctl, bool enable) +static int mdss_mdp_cmd_tearcheck_setup(struct mdss_mdp_cmd_ctx *ctx, + bool enable) { int rc = 0; struct mdss_mdp_mixer *mixer; + struct mdss_mdp_ctl *ctl = ctx->ctl; mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT); if (mixer) { - rc = mdss_mdp_cmd_tearcheck_cfg(ctl, mixer, enable); + rc = mdss_mdp_cmd_tearcheck_cfg(mixer, ctx, enable); if (rc) goto err; } @@ -218,9 +222,9 @@ static int mdss_mdp_cmd_tearcheck_setup(struct mdss_mdp_ctl *ctl, bool enable) if (!(ctl->opmode & MDSS_MDP_CTL_OP_PACK_3D_ENABLE)) { mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_RIGHT); if (mixer) - rc = mdss_mdp_cmd_tearcheck_cfg(ctl, mixer, enable); + rc = mdss_mdp_cmd_tearcheck_cfg(mixer, ctx, enable); } - err: +err: return rc; } @@ -296,7 +300,7 @@ static inline void mdss_mdp_cmd_clk_off(struct mdss_mdp_cmd_ctx *ctx) static void mdss_mdp_cmd_readptr_done(void *arg) { struct mdss_mdp_ctl *ctl = arg; - struct mdss_mdp_cmd_ctx *ctx = ctl->priv_data; + struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; struct mdss_mdp_vsync_handler *tmp; ktime_t vsync_time; @@ -382,7 +386,7 @@ static void mdss_mdp_cmd_intf_recovery(void *data, int event) static void mdss_mdp_cmd_pingpong_done(void *arg) { struct mdss_mdp_ctl *ctl = arg; - struct mdss_mdp_cmd_ctx *ctx = ctl->priv_data; + struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; struct mdss_mdp_vsync_handler *tmp; ktime_t vsync_time; u32 status; @@ -463,7 +467,8 @@ static void clk_ctrl_work(struct work_struct *work) sctl = mdss_mdp_get_split_ctl(ctl); if (sctl) { - sctx = (struct mdss_mdp_cmd_ctx *) sctl->priv_data; + sctx = + (struct mdss_mdp_cmd_ctx *)sctl->intf_ctx[MASTER_CTX]; } else { /* slave ctl, let master ctl do clk control */ mutex_unlock(&cmd_clk_mtx); @@ -488,7 +493,7 @@ static int mdss_mdp_cmd_add_vsync_handler(struct mdss_mdp_ctl *ctl, unsigned long flags; bool enable_rdptr = false; - ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("%s: invalid ctx\n", __func__); return -ENODEV; @@ -498,7 +503,7 @@ static int mdss_mdp_cmd_add_vsync_handler(struct mdss_mdp_ctl *ctl, ctx->rdptr_enabled); sctl = mdss_mdp_get_split_ctl(ctl); if (sctl) - sctx = (struct mdss_mdp_cmd_ctx *) sctl->priv_data; + sctx = (struct mdss_mdp_cmd_ctx *) sctl->intf_ctx[MASTER_CTX]; spin_lock_irqsave(&ctx->clk_lock, flags); if (!handle->enabled) { @@ -536,7 +541,7 @@ static int mdss_mdp_cmd_remove_vsync_handler(struct mdss_mdp_ctl *ctl, struct mdss_mdp_cmd_ctx *ctx, *sctx = NULL; unsigned long flags; - ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("%s: invalid ctx\n", __func__); return -ENODEV; @@ -546,7 +551,7 @@ static int mdss_mdp_cmd_remove_vsync_handler(struct mdss_mdp_ctl *ctl, ctx->rdptr_enabled, 0x88888); sctl = mdss_mdp_get_split_ctl(ctl); if (sctl) - sctx = (struct mdss_mdp_cmd_ctx *) sctl->priv_data; + sctx = (struct mdss_mdp_cmd_ctx *) sctl->intf_ctx[MASTER_CTX]; spin_lock_irqsave(&ctx->clk_lock, flags); if (handle->enabled) { @@ -593,7 +598,7 @@ static int mdss_mdp_cmd_wait4pingpong(struct mdss_mdp_ctl *ctl, void *arg) unsigned long flags; int rc = 0; - ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; @@ -682,14 +687,14 @@ static void mdss_mdp_cmd_set_sync_ctx( { struct mdss_mdp_cmd_ctx *ctx, *sctx; - ctx = (struct mdss_mdp_cmd_ctx *)ctl->priv_data; + ctx = (struct mdss_mdp_cmd_ctx *)ctl->intf_ctx[MASTER_CTX]; if (!sctl) { ctx->sync_ctx = NULL; return; } - sctx = (struct mdss_mdp_cmd_ctx *)sctl->priv_data; + sctx = (struct mdss_mdp_cmd_ctx *)sctl->intf_ctx[MASTER_CTX]; if (!sctl->roi.w && !sctl->roi.h) { /* left only */ @@ -734,14 +739,14 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl, struct mdss_mdp_cmd_ctx *ctx, *sctx = NULL; int rc = 0; - ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; } if (sctl) - sctx = (struct mdss_mdp_cmd_ctx *) sctl->priv_data; + sctx = (struct mdss_mdp_cmd_ctx *) sctl->intf_ctx[MASTER_CTX]; if (!__mdss_mdp_cmd_is_panel_power_on_interactive(ctx)) { rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_LINK_READY, NULL); @@ -779,7 +784,7 @@ static int __mdss_mdp_cmd_configure_autorefresh(struct mdss_mdp_ctl *ctl, int pr_err("invalid ctl structure\n"); return -ENODEV; } - ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; @@ -876,7 +881,7 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) struct mdss_mdp_ctl *sctl = NULL; struct mdss_mdp_cmd_ctx *ctx, *sctx = NULL; - ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; @@ -900,7 +905,7 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) PERF_HW_MDP_STATE, PERF_STATUS_BUSY); if (sctl) { - sctx = (struct mdss_mdp_cmd_ctx *) sctl->priv_data; + sctx = (struct mdss_mdp_cmd_ctx *) sctl->intf_ctx[MASTER_CTX]; mdss_mdp_ctl_perf_set_transaction_status(sctl, PERF_HW_MDP_STATE, PERF_STATUS_BUSY); } @@ -977,44 +982,26 @@ int mdss_mdp_cmd_restore(struct mdss_mdp_ctl *ctl) { pr_debug("%s: called for ctl%d\n", __func__, ctl->num); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); - if (mdss_mdp_cmd_tearcheck_setup(ctl, true)) + if (mdss_mdp_cmd_tearcheck_setup(ctl->intf_ctx[MASTER_CTX], true)) pr_warn("%s: tearcheck setup failed\n", __func__); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); return 0; } -int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, - int panel_power_state) +int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_cmd_ctx *ctx, int panel_power_state) { - struct mdss_mdp_ctl *sctl = NULL; struct mdss_mdp_cmd_ctx *sctx = NULL; - struct mdss_mdp_cmd_ctx *ctx; + struct mdss_mdp_ctl *sctl = NULL; unsigned long flags; unsigned long sflags; int need_wait = 0; - int ret = 0; int hz; - if (session >= MAX_SESSIONS) - return 0; - - if (is_pingpong_split(ctl->mfd)) { - ret = mdss_mdp_cmd_intfs_stop(ctl, (session + 1), - panel_power_state); - if (IS_ERR_VALUE(ret)) - return ret; - } - sctl = mdss_mdp_get_split_ctl(ctl); if (sctl) - sctx = (struct mdss_mdp_cmd_ctx *) sctl->priv_data; - - ctx = &mdss_mdp_cmd_ctx_list[session]; - if (!ctx->ref_cnt) { - pr_err("invalid ctx session: %d\n", session); - return -ENODEV; - } + sctx = (struct mdss_mdp_cmd_ctx *) sctl->intf_ctx[MASTER_CTX]; /* intf stopped, no more kickoff */ ctx->intf_stopped = 1; @@ -1057,11 +1044,11 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, mdss_mdp_cmd_clk_off(ctx); flush_work(&ctx->pp_done_work); - mdss_mdp_cmd_tearcheck_setup(ctl, false); + mdss_mdp_cmd_tearcheck_setup(ctx, false); if (mdss_panel_is_power_on(panel_power_state)) { pr_debug("%s: intf stopped with panel on\n", __func__); - goto end; + return 0; } mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, @@ -1071,9 +1058,39 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, memset(ctx, 0, sizeof(*ctx)); -end: - pr_debug("%s:-\n", __func__); + return 0; +} +int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, + int panel_power_state) +{ + struct mdss_mdp_cmd_ctx *ctx; + + if (session >= MAX_SESSIONS) + return 0; + + ctx = ctl->intf_ctx[MASTER_CTX]; + if (!ctx->ref_cnt) { + pr_err("invalid ctx session: %d\n", session); + return -ENODEV; + } + + mdss_mdp_cmd_ctx_stop(ctl, ctx, panel_power_state); + + if (is_pingpong_split(ctl->mfd)) { + session += 1; + + if (session >= MAX_SESSIONS) + return 0; + + ctx = ctl->intf_ctx[SLAVE_CTX]; + if (!ctx->ref_cnt) { + pr_err("invalid ctx session: %d\n", session); + return -ENODEV; + } + mdss_mdp_cmd_ctx_stop(ctl, ctx, panel_power_state); + } + pr_debug("%s:-\n", __func__); return 0; } @@ -1084,7 +1101,7 @@ static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl, struct mdss_mdp_vsync_handler *tmp, *handle; int session; - ctx = (struct mdss_mdp_cmd_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_cmd_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; @@ -1102,7 +1119,7 @@ static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl, int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) { - struct mdss_mdp_cmd_ctx *ctx = ctl->priv_data; + struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); bool panel_off = false; bool turn_off_clocks = false; @@ -1215,7 +1232,8 @@ panel_events: } pr_debug("%s: turn off panel\n", __func__); - ctl->priv_data = NULL; + ctl->intf_ctx[MASTER_CTX] = NULL; + ctl->intf_ctx[SLAVE_CTX] = NULL; ctl->ops.stop_fnc = NULL; ctl->ops.display_fnc = NULL; ctl->ops.wait_pingpong = NULL; @@ -1233,6 +1251,51 @@ end: return ret; } +static int mdss_mdp_cmd_ctx_setup(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_cmd_ctx *ctx, int pp_num, + int pingpong_split_slave) { + int ret = 0; + + ctx->ctl = ctl; + ctx->pp_num = pp_num; + ctx->pingpong_split_slave = pingpong_split_slave; + ctx->pp_timeout_report_cnt = 0; + init_waitqueue_head(&ctx->pp_waitq); + init_completion(&ctx->stop_comp); + init_completion(&ctx->readptr_done); + spin_lock_init(&ctx->clk_lock); + spin_lock_init(&ctx->koff_lock); + mutex_init(&ctx->clk_mtx); + mutex_init(&ctx->autorefresh_mtx); + INIT_WORK(&ctx->clk_work, clk_ctrl_work); + INIT_WORK(&ctx->pp_done_work, pingpong_done_work); + atomic_set(&ctx->pp_done_cnt, 0); + ctx->autorefresh_off_pending = false; + ctx->autorefresh_init = false; + INIT_LIST_HEAD(&ctx->vsync_handlers); + + ctx->intf_recovery.fxn = mdss_mdp_cmd_intf_recovery; + ctx->intf_recovery.data = ctx; + + ctx->intf_stopped = 0; + + pr_debug("%s: ctx=%p num=%d\n", __func__, ctx, ctx->pp_num); + MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled, + ctx->rdptr_enabled); + + mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, + ctx->pp_num, mdss_mdp_cmd_readptr_done, ctl); + + mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num, + mdss_mdp_cmd_pingpong_done, ctl); + + ret = mdss_mdp_cmd_tearcheck_setup(ctx, true); + if (ret) + pr_err("tearcheck setup failed\n"); + + return ret; +} + static int mdss_mdp_cmd_intfs_setup(struct mdss_mdp_ctl *ctl, int session) { @@ -1244,12 +1307,6 @@ static int mdss_mdp_cmd_intfs_setup(struct mdss_mdp_ctl *ctl, if (session >= MAX_SESSIONS) return 0; - if (is_pingpong_split(ctl->mfd)) { - ret = mdss_mdp_cmd_intfs_setup(ctl, (session + 1)); - if (IS_ERR_VALUE(ret)) - return ret; - } - sctl = mdss_mdp_get_split_ctl(ctl); ctx = &mdss_mdp_cmd_ctx_list[session]; if (ctx->ref_cnt) { @@ -1281,51 +1338,43 @@ static int mdss_mdp_cmd_intfs_setup(struct mdss_mdp_ctl *ctl, return -ENODEV; } - ctl->priv_data = ctx; - if (!ctx) { - pr_err("invalid ctx\n"); + ctl->intf_ctx[MASTER_CTX] = ctx; + + ret = mdss_mdp_cmd_ctx_setup(ctl, ctx, session, false); + if (ret) { + pr_err("mdss_mdp_cmd_ctx_setup failed for ping ping: %d\n", + mixer->num); + ctx->ref_cnt--; return -ENODEV; } - ctx->ctl = ctl; - ctx->pp_num = (is_pingpong_split(ctl->mfd) ? session : mixer->num); - ctx->pp_timeout_report_cnt = 0; - init_waitqueue_head(&ctx->pp_waitq); - init_completion(&ctx->stop_comp); - init_completion(&ctx->readptr_done); - spin_lock_init(&ctx->clk_lock); - spin_lock_init(&ctx->koff_lock); - mutex_init(&ctx->clk_mtx); - mutex_init(&ctx->autorefresh_mtx); - INIT_WORK(&ctx->clk_work, clk_ctrl_work); - INIT_WORK(&ctx->pp_done_work, pingpong_done_work); - atomic_set(&ctx->pp_done_cnt, 0); - ctx->autorefresh_off_pending = false; - ctx->autorefresh_init = false; - INIT_LIST_HEAD(&ctx->vsync_handlers); + if (is_pingpong_split(ctl->mfd)) { + session += 1; + if (session >= MAX_SESSIONS) + return 0; + ctx = &mdss_mdp_cmd_ctx_list[session]; + if (ctx->ref_cnt) { + if (mdss_panel_is_power_on(ctx->panel_power_state)) { + pr_debug("%s: cmd_start with panel always on\n", + __func__); + mdss_mdp_cmd_restore(ctl); + return mdss_mdp_cmd_panel_on(ctl, sctl); + } else { + pr_err("Intf %d already in use\n", session); + return -EBUSY; + } + } + ctx->ref_cnt++; - ctx->intf_recovery.fxn = mdss_mdp_cmd_intf_recovery; - ctx->intf_recovery.data = ctx; + ctl->intf_ctx[SLAVE_CTX] = ctx; - ctx->intf_stopped = 0; - - pr_debug("%s: ctx=%p num=%d mixer=%d\n", __func__, - ctx, ctx->pp_num, mixer->num); - MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled, - ctx->rdptr_enabled); - - mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, - ctx->pp_num, mdss_mdp_cmd_readptr_done, ctl); - - mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num, - mdss_mdp_cmd_pingpong_done, ctl); - - ret = mdss_mdp_cmd_tearcheck_setup(ctl, true); - if (ret) { - pr_err("tearcheck setup failed\n"); - return ret; + ret = mdss_mdp_cmd_ctx_setup(ctl, ctx, session, true); + if (ret) { + pr_err("mdss_mdp_cmd_ctx_setup failed for slave ping pong block"); + ctx->ref_cnt--; + return -EPERM; + } } - return 0; } int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index d3e0c0d48a67..59ce6624fd39 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -91,9 +91,9 @@ static inline u32 mdss_mdp_video_line_count(struct mdss_mdp_ctl *ctl) { struct mdss_mdp_video_ctx *ctx; u32 line_cnt = 0; - if (!ctl || !ctl->priv_data) + if (!ctl || !ctl->intf_ctx[MASTER_CTX]) goto line_count_exit; - ctx = ctl->priv_data; + ctx = ctl->intf_ctx[MASTER_CTX]; line_cnt = mdp_video_read(ctx, MDSS_MDP_REG_INTF_LINE_COUNT); line_count_exit: return line_cnt; @@ -149,7 +149,7 @@ static void mdss_mdp_video_intf_recovery(void *data, int event) return; } - ctx = ctl->priv_data; + ctx = ctl->intf_ctx[MASTER_CTX]; pr_debug("%s: ctl num = %d, event = %d\n", __func__, ctl->num, event); @@ -214,17 +214,16 @@ static void mdss_mdp_video_intf_recovery(void *data, int event) } static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl, - struct intf_timing_params *p) + struct intf_timing_params *p, + struct mdss_mdp_video_ctx *ctx) { u32 hsync_period, vsync_period; u32 hsync_start_x, hsync_end_x, display_v_start, display_v_end; u32 active_h_start, active_h_end, active_v_start, active_v_end; u32 den_polarity, hsync_polarity, vsync_polarity; u32 display_hctl, active_hctl, hsync_ctl, polarity_ctl; - struct mdss_mdp_video_ctx *ctx; struct mdss_data_type *mdata; - ctx = ctl->priv_data; mdata = ctl->mdata; hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width + p->h_front_porch; @@ -332,7 +331,7 @@ static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl, static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl, bool clear) { - struct mdss_mdp_video_ctx *ctx = ctl->priv_data; + struct mdss_mdp_video_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; mutex_lock(&ctx->vsync_mtx); if (atomic_inc_return(&ctx->vsync_ref) == 1) @@ -345,7 +344,7 @@ static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl, bool clear) static inline void video_vsync_irq_disable(struct mdss_mdp_ctl *ctl) { - struct mdss_mdp_video_ctx *ctx = ctl->priv_data; + struct mdss_mdp_video_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; mutex_lock(&ctx->vsync_mtx); if (atomic_dec_return(&ctx->vsync_ref) == 0) @@ -366,7 +365,7 @@ static int mdss_mdp_video_add_vsync_handler(struct mdss_mdp_ctl *ctl, goto exit; } - ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx for ctl=%d\n", ctl->num); ret = -ENODEV; @@ -395,7 +394,7 @@ static int mdss_mdp_video_remove_vsync_handler(struct mdss_mdp_ctl *ctl, unsigned long flags; bool irq_dis = false; - ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx for ctl=%d\n", ctl->num); return -ENODEV; @@ -415,42 +414,12 @@ static int mdss_mdp_video_remove_vsync_handler(struct mdss_mdp_ctl *ctl, return 0; } -static int mdss_mdp_video_intfs_stop(struct mdss_mdp_ctl *ctl, - struct mdss_panel_data *pdata, int inum) +static int mdss_mdp_video_ctx_stop(struct mdss_mdp_ctl *ctl, + struct mdss_panel_info *pinfo, struct mdss_mdp_video_ctx *ctx) { - struct mdss_data_type *mdata; - struct mdss_panel_info *pinfo; - struct mdss_mdp_video_ctx *ctx; - struct mdss_mdp_vsync_handler *tmp, *handle; struct mdss_mdp_ctl *sctl; int rc = 0; u32 frame_rate = 0; - int ret = 0; - - if (pdata == NULL) - return 0; - - if (is_pingpong_split(ctl->mfd)) { - ret = mdss_mdp_video_intfs_stop(ctl, pdata->next, (inum + 1)); - if (IS_ERR_VALUE(ret)) - return ret; - } - - mdata = ctl->mdata; - pinfo = &pdata->panel_info; - - if (inum < mdata->nintf) { - ctx = ((struct mdss_mdp_video_ctx *) mdata->video_intf) + inum; - if (!ctx->ref_cnt) { - pr_err("Intf %d not in use\n", (inum + MDSS_MDP_INTF0)); - return -ENODEV; - } - pr_debug("stop ctl=%d video Intf #%d base=%p", ctl->num, - ctx->intf_num, ctx->base); - } else { - pr_err("Invalid intf number: %d\n", (inum + MDSS_MDP_INTF0)); - return -EINVAL; - } mutex_lock(&ctl->offlock); if (ctx->timegen_en) { @@ -464,8 +433,7 @@ static int mdss_mdp_video_intfs_stop(struct mdss_mdp_ctl *ctl, mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0); /* wait for at least one VSYNC for proper TG OFF */ - frame_rate = mdss_panel_get_framerate - (&(ctl->panel_data->panel_info)); + frame_rate = mdss_panel_get_framerate(pinfo); if (!(frame_rate >= 24 && frame_rate <= 240)) frame_rate = 24; msleep((1000/frame_rate) + 1); @@ -487,13 +455,10 @@ static int mdss_mdp_video_intfs_stop(struct mdss_mdp_ctl *ctl, mdss_bus_bandwidth_ctrl(false); } - list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list) - mdss_mdp_video_remove_vsync_handler(ctl, handle); - mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, - (inum + MDSS_MDP_INTF0), NULL, NULL); + ctx->intf_num, NULL, NULL); mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_UNDER_RUN, - (inum + MDSS_MDP_INTF0), NULL, NULL); + ctx->intf_num, NULL, NULL); ctx->ref_cnt--; end: @@ -501,6 +466,61 @@ end: return rc; } +static int mdss_mdp_video_intfs_stop(struct mdss_mdp_ctl *ctl, + struct mdss_panel_data *pdata, int inum) +{ + struct mdss_data_type *mdata; + struct mdss_panel_info *pinfo; + struct mdss_mdp_video_ctx *ctx; + struct mdss_mdp_vsync_handler *tmp, *handle; + int ret = 0; + + if (pdata == NULL) + return 0; + + mdata = ctl->mdata; + pinfo = &pdata->panel_info; + + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; + if (!ctx->ref_cnt) { + pr_err("Intf %d not in use\n", (inum + MDSS_MDP_INTF0)); + return -ENODEV; + } + pr_debug("stop ctl=%d video Intf #%d base=%p", ctl->num, ctx->intf_num, + ctx->base); + + ret = mdss_mdp_video_ctx_stop(ctl, pinfo, ctx); + if (ret) { + pr_err("mdss_mdp_video_ctx_stop failed for intf: %d", + ctx->intf_num); + return -EPERM; + } + + if (is_pingpong_split(ctl->mfd)) { + pinfo = &pdata->next->panel_info; + + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX]; + if (!ctx->ref_cnt) { + pr_err("Intf %d not in use\n", (inum + MDSS_MDP_INTF0)); + return -ENODEV; + } + pr_debug("stop ctl=%d video Intf #%d base=%p", ctl->num, + ctx->intf_num, ctx->base); + + ret = mdss_mdp_video_ctx_stop(ctl, pinfo, ctx); + if (ret) { + pr_err("mdss_mdp_video_ctx_stop failed for intf: %d", + ctx->intf_num); + return -EPERM; + } + } + + list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list) + mdss_mdp_video_remove_vsync_handler(ctl, handle); + + return 0; +} + static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) { @@ -516,7 +536,7 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) MDSS_XLOG(ctl->num, ctl->vsync_cnt); mdss_mdp_ctl_reset(ctl); - ctl->priv_data = NULL; + ctl->intf_ctx[MASTER_CTX] = NULL; if (ctl->cdm) { mdss_mdp_cdm_destroy(ctl->cdm); @@ -528,7 +548,7 @@ static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) static void mdss_mdp_video_vsync_intr_done(void *arg) { struct mdss_mdp_ctl *ctl = arg; - struct mdss_mdp_video_ctx *ctx = ctl->priv_data; + struct mdss_mdp_video_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; struct mdss_mdp_vsync_handler *tmp; ktime_t vsync_time; @@ -556,7 +576,7 @@ static void mdss_mdp_video_vsync_intr_done(void *arg) static int mdss_mdp_video_pollwait(struct mdss_mdp_ctl *ctl) { - struct mdss_mdp_video_ctx *ctx = ctl->priv_data; + struct mdss_mdp_video_ctx *ctx = ctl->intf_ctx[MASTER_CTX]; u32 mask, status; int rc; @@ -598,7 +618,7 @@ static int mdss_mdp_video_wait4comp(struct mdss_mdp_ctl *ctl, void *arg) struct mdss_mdp_video_ctx *ctx; int rc; - ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; @@ -774,7 +794,7 @@ static int mdss_mdp_video_dfps_wait4vsync(struct mdss_mdp_ctl *ctl) int rc = 0; struct mdss_mdp_video_ctx *ctx; - ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; @@ -840,9 +860,8 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, int rc = 0; u32 hsync_period, vsync_period; struct mdss_data_type *mdata; - u32 inum = ctl->intf_num - MDSS_MDP_INTF0; - ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; @@ -850,28 +869,15 @@ static int mdss_mdp_video_config_fps(struct mdss_mdp_ctl *ctl, mdata = ctl->mdata; if (sctl) { - sctx = (struct mdss_mdp_video_ctx *) sctl->priv_data; + sctx = (struct mdss_mdp_video_ctx *) sctl->intf_ctx[MASTER_CTX]; if (!sctx) { pr_err("invalid ctx\n"); return -ENODEV; } } else if (is_pingpong_split(ctl->mfd)) { - /* - * On targets when destination split is enabled, mixer swap - * is not valid. So we can safely assume that ctl->intf_num - * is INTF1. For this case, increment inum to retrieve sctx. - */ - inum += 1; - if (inum < mdata->nintf) { - sctx = ((struct mdss_mdp_video_ctx *) - mdata->video_intf) + inum; - if (!sctx) { - pr_err("invalid sctx\n"); - return -ENODEV; - } - } else { - pr_err("Invalid intf number: %d\n", - (inum + MDSS_MDP_INTF0)); + sctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[SLAVE_CTX]; + if (!sctx) { + pr_err("invalid sctx\n"); return -ENODEV; } } @@ -992,7 +998,7 @@ static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) pr_debug("kickoff ctl=%d\n", ctl->num); - ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; @@ -1071,7 +1077,7 @@ int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl, struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); off = 0; - ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + ctx = (struct mdss_mdp_video_ctx *) ctl->intf_ctx[MASTER_CTX]; if (!ctx) { pr_err("invalid ctx for ctl=%d\n", ctl->num); return -ENODEV; @@ -1218,43 +1224,14 @@ static int mdss_mdp_video_cdm_setup(struct mdss_mdp_cdm *cdm, setup.output_height = pinfo->yres + pinfo->lcdc.yres_pad; return mdss_mdp_cdm_setup(cdm, &setup); } -static int mdss_mdp_video_intfs_setup(struct mdss_mdp_ctl *ctl, - struct mdss_panel_data *pdata, int inum) + +static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_video_ctx *ctx, struct mdss_panel_info *pinfo) { - struct mdss_data_type *mdata; - struct mdss_panel_info *pinfo; - struct mdss_mdp_video_ctx *ctx; struct intf_timing_params itp = {0}; u32 dst_bpp; - int ret = 0; + struct mdss_data_type *mdata = ctl->mdata; - if (pdata == NULL) - return 0; - - if (is_pingpong_split(ctl->mfd)) { - ret = mdss_mdp_video_intfs_setup(ctl, pdata->next, (inum + 1)); - if (IS_ERR_VALUE(ret)) - return ret; - } - - mdata = ctl->mdata; - pinfo = &pdata->panel_info; - - if (inum < mdata->nintf) { - ctx = ((struct mdss_mdp_video_ctx *) mdata->video_intf) + inum; - if (ctx->ref_cnt) { - pr_err("Intf %d already in use\n", - (inum + MDSS_MDP_INTF0)); - return -EBUSY; - } - pr_debug("video Intf #%d base=%p", ctx->intf_num, ctx->base); - ctx->ref_cnt++; - } else { - pr_err("Invalid intf number: %d\n", (inum + MDSS_MDP_INTF0)); - return -EINVAL; - } - - ctl->priv_data = ctx; ctx->intf_type = ctl->intf_type; init_completion(&ctx->vsync_comp); spin_lock_init(&ctx->vsync_lock); @@ -1295,10 +1272,10 @@ static int mdss_mdp_video_intfs_setup(struct mdss_mdp_ctl *ctl, } mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, - (inum + MDSS_MDP_INTF0), - mdss_mdp_video_vsync_intr_done, ctl); + ctx->intf_num, mdss_mdp_video_vsync_intr_done, + ctl); mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_UNDER_RUN, - (inum + MDSS_MDP_INTF0), + ctx->intf_num, mdss_mdp_video_underrun_intr_done, ctl); dst_bpp = pinfo->fbc.enabled ? (pinfo->fbc.target_bpp) : (pinfo->bpp); @@ -1337,16 +1314,83 @@ static int mdss_mdp_video_intfs_setup(struct mdss_mdp_ctl *ctl, itp.hsync_pulse_width >>= 1; } if (!ctl->panel_data->panel_info.cont_splash_enabled) { - if (mdss_mdp_video_timegen_setup(ctl, &itp)) { + if (mdss_mdp_video_timegen_setup(ctl, &itp, ctx)) { pr_err("unable to set timing parameters intfs: %d\n", - (inum + MDSS_MDP_INTF0)); + ctx->intf_num); return -EINVAL; } mdss_mdp_fetch_start_config(ctx, ctl); } mdp_video_write(ctx, MDSS_MDP_REG_INTF_PANEL_FORMAT, ctl->dst_format); + return 0; +} + +static int mdss_mdp_video_intfs_setup(struct mdss_mdp_ctl *ctl, + struct mdss_panel_data *pdata, int inum) +{ + struct mdss_data_type *mdata; + struct mdss_panel_info *pinfo; + struct mdss_mdp_video_ctx *ctx; + int ret = 0; + + if (pdata == NULL) + return 0; + + mdata = ctl->mdata; + pinfo = &pdata->panel_info; + + if (inum < mdata->nintf) { + ctx = ((struct mdss_mdp_video_ctx *) mdata->video_intf) + inum; + if (ctx->ref_cnt) { + pr_err("Intf %d already in use\n", + (inum + MDSS_MDP_INTF0)); + return -EBUSY; + } + pr_debug("video Intf #%d base=%p", ctx->intf_num, ctx->base); + ctx->ref_cnt++; + } else { + pr_err("Invalid intf number: %d\n", (inum + MDSS_MDP_INTF0)); + return -EINVAL; + } + + ctl->intf_ctx[MASTER_CTX] = ctx; + ret = mdss_mdp_video_ctx_setup(ctl, ctx, pinfo); + if (ret) { + pr_err("Video context setup failed for interface: %d\n", + ctx->intf_num); + ctx->ref_cnt--; + return -EPERM; + } + + if (is_pingpong_split(ctl->mfd)) { + if ((inum + 1) >= mdata->nintf) { + pr_err("Intf not available for ping pong split: (%d)\n", + (inum + 1 + MDSS_MDP_INTF0)); + return -EINVAL; + } + + ctx = ((struct mdss_mdp_video_ctx *) mdata->video_intf) + + inum + 1; + if (ctx->ref_cnt) { + pr_err("Intf %d already in use\n", + (inum + MDSS_MDP_INTF0)); + return -EBUSY; + } + pr_debug("video Intf #%d base=%p", ctx->intf_num, ctx->base); + ctx->ref_cnt++; + + ctl->intf_ctx[SLAVE_CTX] = ctx; + pinfo = &pdata->next->panel_info; + ret = mdss_mdp_video_ctx_setup(ctl, ctx, pinfo); + if (ret) { + pr_err("Video context setup failed for interface: %d\n", + ctx->intf_num); + ctx->ref_cnt--; + return -EPERM; + } + } return 0; }