mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
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:
parent
99969c305e
commit
0b73752fe7
4 changed files with 337 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
314
drivers/video/msm/mdss/mdss_mdp_intf_video.c
Normal file
314
drivers/video/msm/mdss/mdss_mdp_intf_video.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue