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>
This commit is contained in:
Adrian Salido-Moreno 2012-05-29 19:00:54 -07:00 committed by Stephen Boyd
parent 99969c305e
commit 0b73752fe7
4 changed files with 337 additions and 0 deletions

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -0,0 +1,314 @@
/* Copyright (c) 2012, Code Aurora Forum. 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;
}