M7350/kernel/drivers/video/msm/mdss/mdss_mdp_intf_video.c
2024-09-09 08:52:07 +00:00

674 lines
18 KiB
C

/* Copyright (c) 2012-2013, 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 <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include "mdss_fb.h"
#include "mdss_mdp.h"
/* wait for at least 2 vsyncs for lowest refresh rate (24hz) */
#define VSYNC_TIMEOUT_US 100000
#define MDP_INTR_MASK_INTF_VSYNC(intf_num) \
(1 << (2 * (intf_num - MDSS_MDP_INTF0) + MDSS_MDP_IRQ_INTF_VSYNC))
/* 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;
};
struct mdss_mdp_video_ctx {
u32 intf_num;
char __iomem *base;
u32 intf_type;
u8 ref_cnt;
u8 timegen_en;
bool polling_en;
u32 poll_cnt;
struct completion vsync_comp;
int wait_pending;
atomic_t vsync_ref;
spinlock_t vsync_lock;
struct list_head vsync_handlers;
};
static inline void mdp_video_write(struct mdss_mdp_video_ctx *ctx,
u32 reg, u32 val)
{
writel_relaxed(val, ctx->base + reg);
}
static inline u32 mdp_video_read(struct mdss_mdp_video_ctx *ctx,
u32 reg)
{
return readl_relaxed(ctx->base + reg);
}
static inline u32 mdss_mdp_video_line_count(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
u32 line_cnt = 0;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
line_cnt = mdp_video_read(ctx, MDSS_MDP_REG_INTF_LINE_COUNT);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
return line_cnt;
}
int mdss_mdp_video_addr_setup(struct mdss_data_type *mdata,
u32 *offsets, u32 count)
{
struct mdss_mdp_video_ctx *head;
u32 i;
head = devm_kzalloc(&mdata->pdev->dev,
sizeof(struct mdss_mdp_video_ctx) * count, GFP_KERNEL);
if (!head)
return -ENOMEM;
for (i = 0; i < count; i++) {
head[i].base = mdata->mdp_base + offsets[i];
pr_debug("adding Video Intf #%d offset=0x%x virt=%p\n", i,
offsets[i], head[i].base);
head[i].ref_cnt = 0;
head[i].intf_num = i + MDSS_MDP_INTF0;
INIT_LIST_HEAD(&head[i].vsync_handlers);
}
mdata->video_intf = head;
mdata->nintf = count;
return 0;
}
static int mdss_mdp_video_timegen_setup(struct mdss_mdp_video_ctx *ctx,
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 den_polarity, hsync_polarity, vsync_polarity;
u32 display_hctl, active_hctl, hsync_ctl, polarity_ctl;
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 (ctx->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;
den_polarity = 0;
if (MDSS_INTF_HDMI == ctx->intf_type) {
hsync_polarity = p->yres >= 720 ? 0 : 1;
vsync_polarity = p->yres >= 720 ? 0 : 1;
} else {
hsync_polarity = 0;
vsync_polarity = 0;
}
polarity_ctl = (den_polarity << 2) | /* DEN Polarity */
(vsync_polarity << 1) | /* VSYNC Polarity */
(hsync_polarity << 0); /* HSYNC Polarity */
mdp_video_write(ctx, MDSS_MDP_REG_INTF_HSYNC_CTL, hsync_ctl);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0,
vsync_period * hsync_period);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_VSYNC_PULSE_WIDTH_F0,
p->vsync_pulse_width * hsync_period);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_DISPLAY_HCTL, display_hctl);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_DISPLAY_V_START_F0,
display_v_start);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_DISPLAY_V_END_F0, display_v_end);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_ACTIVE_HCTL, active_hctl);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_ACTIVE_V_START_F0,
active_v_start);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_ACTIVE_V_END_F0, active_v_end);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_BORDER_COLOR, p->border_clr);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_UNDERFLOW_COLOR,
p->underflow_clr);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_HSYNC_SKEW, p->hsync_skew);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_POLARITY_CTL, polarity_ctl);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN, 0x3);
return 0;
}
static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl, bool clear)
{
struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
if (atomic_inc_return(&ctx->vsync_ref) == 1)
mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
else if (clear)
mdss_mdp_irq_clear(ctl->mdata, MDSS_MDP_IRQ_INTF_VSYNC,
ctl->intf_num);
}
static inline void video_vsync_irq_disable(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
if (atomic_dec_return(&ctx->vsync_ref) == 0)
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
}
static int mdss_mdp_video_add_vsync_handler(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_vsync_handler *handle)
{
struct mdss_mdp_video_ctx *ctx;
unsigned long flags;
int ret = 0;
bool irq_en = false;
if (!handle || !(handle->vsync_handler)) {
ret = -EINVAL;
goto exit;
}
ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
if (!ctx) {
pr_err("invalid ctx for ctl=%d\n", ctl->num);
ret = -ENODEV;
goto exit;
}
spin_lock_irqsave(&ctx->vsync_lock, flags);
if (!handle->enabled) {
handle->enabled = true;
list_add(&handle->list, &ctx->vsync_handlers);
irq_en = true;
}
spin_unlock_irqrestore(&ctx->vsync_lock, flags);
if (irq_en)
video_vsync_irq_enable(ctl, false);
exit:
return ret;
}
static int mdss_mdp_video_remove_vsync_handler(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_vsync_handler *handle)
{
struct mdss_mdp_video_ctx *ctx;
unsigned long flags;
bool irq_dis = false;
ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
if (!ctx) {
pr_err("invalid ctx for ctl=%d\n", ctl->num);
return -ENODEV;
}
spin_lock_irqsave(&ctx->vsync_lock, flags);
if (handle->enabled) {
handle->enabled = false;
list_del_init(&handle->list);
irq_dis = true;
}
spin_unlock_irqrestore(&ctx->vsync_lock, flags);
if (irq_dis)
video_vsync_irq_disable(ctl);
return 0;
}
static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_video_ctx *ctx;
struct mdss_mdp_vsync_handler *tmp, *handle;
int rc;
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) {
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_BLANK, NULL);
if (rc == -EBUSY) {
pr_debug("intf #%d busy don't turn off\n",
ctl->intf_num);
return rc;
}
WARN(rc, "intf %d blank error (%d)\n", ctl->intf_num, rc);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
ctx->timegen_en = false;
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_OFF, NULL);
WARN(rc, "intf %d timegen off error (%d)\n", ctl->intf_num, rc);
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
ctl->intf_num);
}
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, ctl->intf_num,
NULL, NULL);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_UNDER_RUN, ctl->intf_num,
NULL, NULL);
ctx->ref_cnt--;
ctl->priv_data = NULL;
return 0;
}
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_vsync_handler *tmp;
ktime_t vsync_time;
if (!ctx) {
pr_err("invalid ctx\n");
return;
}
vsync_time = ktime_get();
ctl->vsync_cnt++;
pr_debug("intr ctl=%d vsync cnt=%u vsync_time=%d\n",
ctl->num, ctl->vsync_cnt, (int)ktime_to_ms(vsync_time));
ctx->polling_en = false;
complete_all(&ctx->vsync_comp);
spin_lock(&ctx->vsync_lock);
list_for_each_entry(tmp, &ctx->vsync_handlers, list) {
tmp->vsync_handler(ctl, vsync_time);
}
spin_unlock(&ctx->vsync_lock);
}
static int mdss_mdp_video_pollwait(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
u32 mask, status;
int rc;
mask = MDP_INTR_MASK_INTF_VSYNC(ctl->intf_num);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
rc = readl_poll_timeout(ctl->mdata->mdp_base + MDSS_MDP_REG_INTR_STATUS,
status,
(status & mask) || try_wait_for_completion(&ctx->vsync_comp),
1000,
VSYNC_TIMEOUT_US);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
if (rc == 0) {
pr_debug("vsync poll successful! rc=%d status=0x%x\n",
rc, status);
ctx->poll_cnt++;
if (status) {
struct mdss_mdp_vsync_handler *tmp;
unsigned long flags;
ktime_t vsync_time = ktime_get();
spin_lock_irqsave(&ctx->vsync_lock, flags);
list_for_each_entry(tmp, &ctx->vsync_handlers, list)
tmp->vsync_handler(ctl, vsync_time);
spin_unlock_irqrestore(&ctx->vsync_lock, flags);
}
} else {
pr_warn("vsync poll timed out! rc=%d status=0x%x mask=0x%x\n",
rc, status, mask);
}
return rc;
}
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;
if (!ctx) {
pr_err("invalid ctx\n");
return -ENODEV;
}
WARN(!ctx->wait_pending, "waiting without commit! ctl=%d", ctl->num);
if (ctx->polling_en) {
rc = mdss_mdp_video_pollwait(ctl);
} else {
rc = wait_for_completion_interruptible_timeout(&ctx->vsync_comp,
usecs_to_jiffies(VSYNC_TIMEOUT_US));
if (rc < 0) {
pr_warn("vsync wait interrupted ctl=%d\n", ctl->num);
} else if (rc == 0) {
pr_warn("vsync wait timeout %d, fallback to poll mode\n",
ctl->num);
ctx->polling_en++;
rc = mdss_mdp_video_pollwait(ctl);
} else {
rc = 0;
}
}
if (ctx->wait_pending) {
ctx->wait_pending = 0;
video_vsync_irq_disable(ctl);
}
return rc;
}
static void mdss_mdp_video_underrun_intr_done(void *arg)
{
struct mdss_mdp_ctl *ctl = arg;
if (unlikely(!ctl))
return;
ctl->underrun_cnt++;
pr_debug("display underrun detected for ctl=%d count=%d\n", ctl->num,
ctl->underrun_cnt);
}
static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_video_ctx *ctx;
int rc;
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;
}
if (!ctx->wait_pending) {
ctx->wait_pending++;
video_vsync_irq_enable(ctl, true);
INIT_COMPLETION(ctx->vsync_comp);
} else {
WARN(1, "commit without wait! ctl=%d", ctl->num);
}
if (!ctx->timegen_en) {
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL);
if (rc) {
pr_warn("intf #%d unblank error (%d)\n",
ctl->intf_num, rc);
video_vsync_irq_disable(ctl);
ctx->wait_pending = 0;
return rc;
}
pr_debug("enabling timing gen for intf=%d\n", ctl->intf_num);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN, ctl->intf_num);
mdp_video_write(ctx, MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
wmb();
rc = wait_for_completion_interruptible_timeout(&ctx->vsync_comp,
usecs_to_jiffies(VSYNC_TIMEOUT_US));
WARN(rc <= 0, "timeout (%d) enabling timegen on ctl=%d\n",
rc, ctl->num);
ctx->timegen_en = true;
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_ON, NULL);
WARN(rc, "intf %d panel on error (%d)\n", ctl->intf_num, rc);
}
return 0;
}
int mdss_mdp_video_copy_splash_screen(struct mdss_panel_data *pdata)
{
void *virt = NULL;
unsigned long bl_fb_addr = 0;
unsigned long *bl_fb_addr_va;
unsigned long pipe_addr, pipe_src_size;
u32 height, width, rgb_size, bpp;
size_t size;
static struct ion_handle *ihdl;
struct ion_client *iclient = mdss_get_ionclient();
static ion_phys_addr_t phys;
pipe_addr = MDSS_MDP_REG_SSPP_OFFSET(3) +
MDSS_MDP_REG_SSPP_SRC0_ADDR;
pipe_src_size =
MDSS_MDP_REG_SSPP_OFFSET(3) + MDSS_MDP_REG_SSPP_SRC_SIZE;
bpp = 3;
rgb_size = MDSS_MDP_REG_READ(pipe_src_size);
bl_fb_addr = MDSS_MDP_REG_READ(pipe_addr);
height = (rgb_size >> 16) & 0xffff;
width = rgb_size & 0xffff;
size = PAGE_ALIGN(height * width * bpp);
pr_debug("%s:%d splash_height=%d splash_width=%d Buffer size=%d\n",
__func__, __LINE__, height, width, size);
ihdl = ion_alloc(iclient, size, SZ_1M,
ION_HEAP(ION_QSECOM_HEAP_ID), 0);
if (IS_ERR_OR_NULL(ihdl)) {
pr_err("unable to alloc fbmem from ion (%p)\n", ihdl);
return -ENOMEM;
}
pdata->panel_info.splash_ihdl = ihdl;
virt = ion_map_kernel(iclient, ihdl);
ion_phys(iclient, ihdl, &phys, &size);
pr_debug("%s %d Allocating %u bytes at 0x%lx (%pa phys)\n",
__func__, __LINE__, size,
(unsigned long int)virt, &phys);
bl_fb_addr_va = (unsigned long *)ioremap(bl_fb_addr, size);
memcpy(virt, bl_fb_addr_va, size);
MDSS_MDP_REG_WRITE(pipe_addr, phys);
MDSS_MDP_REG_WRITE(MDSS_MDP_REG_CTL_FLUSH + MDSS_MDP_REG_CTL_OFFSET(0),
0x48);
return 0;
}
int mdss_mdp_video_reconfigure_splash_done(struct mdss_mdp_ctl *ctl)
{
struct ion_client *iclient = mdss_get_ionclient();
struct mdss_panel_data *pdata;
int ret = 0, off;
int mdss_mdp_rev = MDSS_MDP_REG_READ(MDSS_MDP_REG_HW_VERSION);
int mdss_v2_intf_off = 0;
off = 0;
pdata = ctl->panel_data;
pdata->panel_info.cont_splash_enabled = 0;
ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_BEGIN,
NULL);
if (ret) {
pr_err("%s: Failed to handle 'CONT_SPLASH_BEGIN' event\n",
__func__);
return ret;
}
mdss_mdp_ctl_write(ctl, 0, MDSS_MDP_LM_BORDER_COLOR);
off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
if (mdss_mdp_rev == MDSS_MDP_HW_REV_102)
mdss_v2_intf_off = 0xEC00;
MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN -
mdss_v2_intf_off, 0);
/* wait for 1 VSYNC for the pipe to be unstaged */
msleep(20);
ion_free(iclient, pdata->panel_info.splash_ihdl);
ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_FINISH,
NULL);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
return ret;
}
int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
{
struct mdss_data_type *mdata;
struct mdss_panel_info *pinfo;
struct mdss_mdp_video_ctx *ctx;
struct mdss_mdp_mixer *mixer;
struct intf_timing_params itp = {0};
u32 dst_bpp;
int i;
mdata = ctl->mdata;
pinfo = &ctl->panel_data->panel_info;
mixer = mdss_mdp_mixer_get(ctl, MDSS_MDP_MIXER_MUX_LEFT);
if (!mixer) {
pr_err("mixer not setup correctly\n");
return -ENODEV;
}
i = ctl->intf_num - MDSS_MDP_INTF0;
if (i < mdata->nintf) {
ctx = ((struct mdss_mdp_video_ctx *) mdata->video_intf) + i;
if (ctx->ref_cnt) {
pr_err("Intf %d already in use\n", ctl->intf_num);
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", ctl->intf_num);
return -EINVAL;
}
pr_debug("start ctl=%u\n", ctl->num);
ctl->priv_data = ctx;
ctx->intf_type = ctl->intf_type;
init_completion(&ctx->vsync_comp);
spin_lock_init(&ctx->vsync_lock);
atomic_set(&ctx->vsync_ref, 0);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
mdss_mdp_video_vsync_intr_done, ctl);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_UNDER_RUN, ctl->intf_num,
mdss_mdp_video_underrun_intr_done, ctl);
dst_bpp = pinfo->fbc.enabled ? (pinfo->fbc.target_bpp) : (pinfo->bpp);
itp.width = mult_frac((pinfo->xres + pinfo->lcdc.xres_pad),
dst_bpp, pinfo->bpp);
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 = mult_frac(pinfo->xres, dst_bpp, pinfo->bpp);
itp.yres = pinfo->yres;
itp.h_back_porch = mult_frac(pinfo->lcdc.h_back_porch, dst_bpp,
pinfo->bpp);
itp.h_front_porch = mult_frac(pinfo->lcdc.h_front_porch, dst_bpp,
pinfo->bpp);
itp.v_back_porch = mult_frac(pinfo->lcdc.v_back_porch, dst_bpp,
pinfo->bpp);
itp.v_front_porch = mult_frac(pinfo->lcdc.v_front_porch, dst_bpp,
pinfo->bpp);
itp.hsync_pulse_width = mult_frac(pinfo->lcdc.h_pulse_width, dst_bpp,
pinfo->bpp);
itp.vsync_pulse_width = pinfo->lcdc.v_pulse_width;
if (mdss_mdp_video_timegen_setup(ctx, &itp)) {
pr_err("unable to get timing parameters\n");
return -EINVAL;
}
mdp_video_write(ctx, MDSS_MDP_REG_INTF_PANEL_FORMAT, ctl->dst_format);
ctl->stop_fnc = mdss_mdp_video_stop;
ctl->display_fnc = mdss_mdp_video_display;
ctl->wait_fnc = mdss_mdp_video_wait4comp;
ctl->read_line_cnt_fnc = mdss_mdp_video_line_count;
ctl->add_vsync_handler = mdss_mdp_video_add_vsync_handler;
ctl->remove_vsync_handler = mdss_mdp_video_remove_vsync_handler;
return 0;
}