diff options
| author | Adrian Salido-Moreno <adrianm@codeaurora.org> | 2012-05-29 19:00:54 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:11:43 -0700 |
| commit | 111b1b4e614a204c0ccdaaa3e96e0244896624c9 (patch) | |
| tree | 3347041662bbf6252f413134655e93a83548d857 /drivers | |
| parent | 92c603dc9a5333dfa0818c7c9a371f68441b6d86 (diff) | |
mdss: display: add video display interface support
Video mode display interface is used by DSI, HDMI and EDP controllers.
Implement MDP support in order to setup data path to these controllers.
Change-Id: Ibc766a1d10a55bcf64a3ab693db034a378afe4ee
Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
[cip@codeaurora.org: Moved mdss_mdp_intf_video.c file location]
Signed-off-by: Clarence Ip <cip@codeaurora.org>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/video/fbdev/msm/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.h | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_ctl.c | 21 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_intf_video.c | 314 |
4 files changed, 337 insertions, 0 deletions
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index b6867b7d7f4c..780e0c64009b 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -1,5 +1,6 @@ mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o mdss-mdp-objs += mdss_mdp_pp.o +mdss-mdp-objs += mdss_mdp_intf_video.o mdss-mdp-objs += mdss_mdp_intf_writeback.o mdss-mdp-objs += mdss_mdp_rotator.o mdss-mdp-objs += mdss_mdp_overlay.o diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index cf82c2528e6c..28bc55442837 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -275,6 +275,7 @@ void mdss_mdp_clk_ctrl(int enable, int isr); void mdss_mdp_footswitch_ctrl(int on); int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd); +int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl); int mdss_mdp_writeback_start(struct mdss_mdp_ctl *ctl); int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 8d500e0b93f2..68ae81a2f19a 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -348,6 +348,27 @@ static int mdss_mdp_ctl_init(struct msm_fb_data_type *mfd) } switch (mfd->panel_info.type) { + case EDP_PANEL: + ctl->intf_num = MDSS_MDP_INTF0; + ctl->intf_type = MDSS_INTF_EDP; + ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE; + ctl->start_fnc = mdss_mdp_video_start; + break; + case MIPI_VIDEO_PANEL: + if (mfd->panel_info.pdest == DISPLAY_1) + ctl->intf_num = MDSS_MDP_INTF1; + else + ctl->intf_num = MDSS_MDP_INTF2; + ctl->intf_type = MDSS_INTF_DSI; + ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE; + ctl->start_fnc = mdss_mdp_video_start; + break; + case DTV_PANEL: + ctl->intf_num = MDSS_MDP_INTF3; + ctl->intf_type = MDSS_INTF_HDMI; + ctl->opmode = MDSS_MDP_CTL_OP_VIDEO_MODE; + ctl->start_fnc = mdss_mdp_video_start; + break; case WRITEBACK_PANEL: ctl->intf_num = MDSS_MDP_NO_INTF; ctl->opmode = MDSS_MDP_CTL_OP_WFD_MODE; diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c new file mode 100644 index 000000000000..72cf6ccbcea6 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -0,0 +1,314 @@ +/* Copyright (c) 2012, 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 + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include "mdss_fb.h" +#include "mdss_mdp.h" + +/* intf timing settings */ +struct intf_timing_params { + u32 width; + u32 height; + u32 xres; + u32 yres; + + u32 h_back_porch; + u32 h_front_porch; + u32 v_back_porch; + u32 v_front_porch; + u32 hsync_pulse_width; + u32 vsync_pulse_width; + + u32 border_clr; + u32 underflow_clr; + u32 hsync_skew; +}; + +#define MAX_SESSIONS 3 +struct mdss_mdp_video_ctx { + u32 ctl_num; + u32 pp_num; + u8 ref_cnt; + + u8 timegen_en; + struct completion pp_comp; + struct completion vsync_comp; +}; + +struct mdss_mdp_video_ctx mdss_mdp_video_ctx_list[MAX_SESSIONS]; + +static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl, + struct intf_timing_params *p) +{ + 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 display_hctl, active_hctl, hsync_ctl, polarity_ctl; + int off; + + off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num); + + hsync_period = p->hsync_pulse_width + p->h_back_porch + + p->width + p->h_front_porch; + vsync_period = p->vsync_pulse_width + p->v_back_porch + + p->height + p->v_front_porch; + + display_v_start = ((p->vsync_pulse_width + p->v_back_porch) * + hsync_period) + p->hsync_skew; + display_v_end = ((vsync_period - p->v_front_porch) * hsync_period) + + p->hsync_skew - 1; + + if (ctl->intf_type == MDSS_INTF_EDP) { + display_v_start += p->hsync_pulse_width + p->h_back_porch; + display_v_end -= p->h_front_porch; + } + + hsync_start_x = p->h_back_porch + p->hsync_pulse_width; + hsync_end_x = hsync_period - p->h_front_porch - 1; + + if (p->width != p->xres) { + active_h_start = hsync_start_x; + active_h_end = active_h_start + p->xres - 1; + } else { + active_h_start = 0; + active_h_end = 0; + } + + if (p->height != p->yres) { + active_v_start = display_v_start; + active_v_end = active_v_start + (p->yres * hsync_period) - 1; + } else { + active_v_start = 0; + active_v_end = 0; + } + + + if (active_h_end) { + active_hctl = (active_h_end << 16) | active_h_start; + active_hctl |= BIT(31); /* ACTIVE_H_ENABLE */ + } else { + active_hctl = 0; + } + + if (active_v_end) + active_v_start |= BIT(31); /* ACTIVE_V_ENABLE */ + + hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width; + display_hctl = (hsync_end_x << 16) | hsync_start_x; + polarity_ctl = (0 << 2) | /* DEN Polarity */ + (0 << 1) | /* VSYNC Polarity */ + (0); /* HSYNC Polarity */ + + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_HSYNC_CTL, hsync_ctl); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0, + vsync_period * hsync_period); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_VSYNC_PULSE_WIDTH_F0, + p->vsync_pulse_width * hsync_period); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_DISPLAY_HCTL, + display_hctl); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_DISPLAY_V_START_F0, + display_v_start); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_DISPLAY_V_END_F0, + display_v_end); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_ACTIVE_HCTL, active_hctl); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_ACTIVE_V_START_F0, + active_v_start); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_ACTIVE_V_END_F0, + active_v_end); + + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_BORDER_COLOR, + p->border_clr); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_UNDERFLOW_COLOR, + p->underflow_clr); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_HSYNC_SKEW, + p->hsync_skew); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_POLARITY_CTL, + polarity_ctl); + + return 0; +} + +static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl) +{ + struct mdss_mdp_video_ctx *ctx; + int off; + + pr_debug("stop ctl=%d\n", ctl->num); + + ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + if (!ctx) { + pr_err("invalid ctx for ctl=%d\n", ctl->num); + return -ENODEV; + } + + if (ctx->timegen_en) { + off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + } + + memset(ctx, 0, sizeof(*ctx)); + + return 0; +} + +static void mdss_mdp_video_pp_intr_done(void *arg) +{ + struct mdss_mdp_video_ctx *ctx; + + ctx = (struct mdss_mdp_video_ctx *) arg; + if (!ctx) { + pr_err("invalid ctx\n"); + return; + } + + pr_debug("intr mixer=%d\n", ctx->pp_num); + + complete(&ctx->pp_comp); +} + +static void mdss_mdp_video_vsync_intr_done(void *arg) +{ + struct mdss_mdp_video_ctx *ctx; + + ctx = (struct mdss_mdp_video_ctx *) arg; + if (!ctx) { + pr_err("invalid ctx\n"); + return; + } + + pr_debug("intr ctl=%d\n", ctx->ctl_num); + + complete(&ctx->vsync_comp); +} + +static int mdss_mdp_video_prepare(struct mdss_mdp_ctl *ctl, void *arg) +{ + struct mdss_mdp_video_ctx *ctx; + u32 intr_type = MDSS_MDP_IRQ_PING_PONG_COMP; + + if (ctl->play_cnt == 0) + return 0; + + pr_debug("setup ctl=%d\n", ctl->num); + + ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + if (!ctx) { + pr_err("invalid ctx\n"); + return -ENODEV; + } + mdss_mdp_set_intr_callback(intr_type, ctx->pp_num, + mdss_mdp_video_pp_intr_done, ctx); + mdss_mdp_irq_enable(intr_type, ctx->pp_num); + + wait_for_completion_interruptible(&ctx->pp_comp); + mdss_mdp_irq_disable(intr_type, ctx->pp_num); + + return 0; +} + +static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg) +{ + struct mdss_mdp_video_ctx *ctx; + u32 intr_type = MDSS_MDP_IRQ_INTF_VSYNC; + + pr_debug("kickoff ctl=%d\n", ctl->num); + + ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data; + if (!ctx) { + pr_err("invalid ctx\n"); + return -ENODEV; + } + mdss_mdp_set_intr_callback(intr_type, ctl->intf_num, + mdss_mdp_video_vsync_intr_done, ctx); + mdss_mdp_irq_enable(intr_type, ctl->intf_num); + + if (!ctx->timegen_en) { + int off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1); + ctx->timegen_en = 1; + } + + wait_for_completion_interruptible(&ctx->vsync_comp); + mdss_mdp_irq_disable(intr_type, ctl->intf_num); + + return 0; +} + +int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl) +{ + struct msm_fb_data_type *mfd; + struct mdss_panel_info *pinfo; + struct mdss_mdp_video_ctx *ctx; + struct mdss_mdp_mixer *mixer; + struct intf_timing_params itp = {0}; + struct fb_info *fbi; + int i; + + mfd = ctl->mfd; + fbi = mfd->fbi; + pinfo = &mfd->panel_info; + mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT); + + if (!mixer) { + pr_err("mixer not setup correctly\n"); + return -ENODEV; + } + + pr_debug("start ctl=%u\n", ctl->num); + + for (i = 0; i < MAX_SESSIONS; i++) { + ctx = &mdss_mdp_video_ctx_list[i]; + if (ctx->ref_cnt == 0) { + ctx->ref_cnt++; + break; + } + } + if (i == MAX_SESSIONS) { + pr_err("too many sessions\n"); + return -ENOMEM; + } + ctl->priv_data = ctx; + ctx->ctl_num = ctl->num; + ctx->pp_num = mixer->num; + init_completion(&ctx->pp_comp); + init_completion(&ctx->vsync_comp); + + itp.width = pinfo->xres + pinfo->lcdc.xres_pad; + itp.height = pinfo->yres + pinfo->lcdc.yres_pad; + itp.border_clr = pinfo->lcdc.border_clr; + itp.underflow_clr = pinfo->lcdc.underflow_clr; + itp.hsync_skew = pinfo->lcdc.hsync_skew; + + itp.xres = fbi->var.xres; + itp.yres = fbi->var.yres; + itp.h_back_porch = fbi->var.left_margin; + itp.h_front_porch = fbi->var.right_margin; + itp.v_back_porch = fbi->var.upper_margin; + itp.v_front_porch = fbi->var.lower_margin; + itp.hsync_pulse_width = fbi->var.hsync_len; + itp.vsync_pulse_width = fbi->var.vsync_len; + + if (mdss_mdp_video_timegen_setup(ctl, &itp)) { + pr_err("unable to get timing parameters\n"); + return -EINVAL; + } + + ctl->stop_fnc = mdss_mdp_video_stop; + ctl->prepare_fnc = mdss_mdp_video_prepare; + ctl->display_fnc = mdss_mdp_video_display; + + return 0; +} |
