[PATCH 110/133] [Jaunty SRU] ARM.imx51 Freescale:ENGR00110765 Support of 720p in IPU/TVE
Brad Figg
brad.figg at canonical.com
Thu Jul 9 16:49:40 UTC 2009
From: Arik Gubeskys <arik.gubeskys at freescale.com>
1. IC bypass implementation. The IC unit is bypassed
when no resizing or rotation are needed.
2. Adding 720p support to the IPU and TVE drivers.
The following use cases supported:
a. 720p content => 720p display
b. 720p content => NTSC/PAL display (downsizing)
3. Cable detection in TVE ported to TVEv2
Signed-off-by: Arik Gubeskys <rm05686 at freescale.com>
Signed-off-by: Rob Herring <r.herring at freescale.com>
Signed-off-by: Brad Figg <brad.figg at canonical.com>
---
arch/arm/mach-mx51/mx51_3stack.c | 12 +
drivers/media/video/mxc/output/mxc_v4l2_output.c | 309 +++++++++++++---------
drivers/media/video/mxc/output/mxc_v4l2_output.h | 3 +-
drivers/mxc/ipu3/ipu_disp.c | 44 +++-
drivers/video/mxc/mxc_ipuv3_fb.c | 4 +-
drivers/video/mxc/tve.c | 93 ++++++-
6 files changed, 319 insertions(+), 146 deletions(-)
diff --git a/arch/arm/mach-mx51/mx51_3stack.c b/arch/arm/mach-mx51/mx51_3stack.c
index 481afa9..fa31c6b 100644
--- a/arch/arm/mach-mx51/mx51_3stack.c
+++ b/arch/arm/mach-mx51/mx51_3stack.c
@@ -236,6 +236,16 @@ static inline void mxc_init_nand_mtd(void)
#if defined(CONFIG_FB_MXC_SYNC_PANEL) || \
defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
+
+static struct mxc_fb_platform_data fb_data[] = {
+ {
+ .interface_pix_fmt = IPU_PIX_FMT_RGB666,
+ },
+ {
+ .interface_pix_fmt = IPU_PIX_FMT_YUV444,
+ },
+};
+
static struct platform_device mxc_fb_device[] = {
{
.name = "mxc_sdc_fb",
@@ -243,6 +253,7 @@ static struct platform_device mxc_fb_device[] = {
.dev = {
.release = mxc_nop_release,
.coherent_dma_mask = 0xFFFFFFFF,
+ .platform_data = &fb_data[0],
},
},
{
@@ -251,6 +262,7 @@ static struct platform_device mxc_fb_device[] = {
.dev = {
.release = mxc_nop_release,
.coherent_dma_mask = 0xFFFFFFFF,
+ .platform_data = &fb_data[1],
},
},
{
diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.c b/drivers/media/video/mxc/output/mxc_v4l2_output.c
index 046b1e7..51b35cd 100644
--- a/drivers/media/video/mxc/output/mxc_v4l2_output.c
+++ b/drivers/media/video/mxc/output/mxc_v4l2_output.c
@@ -58,6 +58,7 @@ struct v4l2_output mxc_outputs[2] = {
};
static int video_nr = 16;
+static int pending_buffer;
static spinlock_t g_lock = SPIN_LOCK_UNLOCKED;
/* debug counters */
@@ -202,29 +203,12 @@ static u32 fmt_to_bpp(u32 pixelformat)
{
u32 bpp;
- switch (pixelformat) {
- case V4L2_PIX_FMT_RGB565:
- bpp = 16;
- break;
- case V4L2_PIX_FMT_BGR24:
- case V4L2_PIX_FMT_RGB24:
- bpp = 24;
- break;
- case V4L2_PIX_FMT_BGR32:
- case V4L2_PIX_FMT_RGB32:
- bpp = 32;
- break;
- default:
- bpp = 8;
- break;
- }
+ bpp = 8*bytes_per_pixel(pixelformat);
return bpp;
}
static bool format_is_yuv(u32 pixelformat)
{
- u32 bpp;
-
switch (pixelformat) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_UYVY:
@@ -254,20 +238,86 @@ static u32 bpp_to_fmt(struct fb_info *fbi)
static irqreturn_t mxc_v4l2out_disp_refresh_irq_handler(int irq, void *dev_id)
{
- struct completion *comp = dev_id;
+ vout_data *vout = dev_id;
+ int index, last_buf, ret;
+ unsigned long timeout;
+ unsigned long lock_flags = 0;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ g_irq_cnt++;
+
+ if (vout->ic_bypass && (pending_buffer || vout->frame_count < 3)) {
+ last_buf = vout->ipu_buf[vout->next_done_ipu_buf];
+ if (last_buf != -1) {
+ g_buf_output_cnt++;
+ vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE;
+ queue_buf(&vout->done_q, last_buf);
+ vout->ipu_buf[vout->next_done_ipu_buf] = -1;
+ wake_up_interruptible(&vout->v4l_bufq);
+ vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+ }
+ }
+
+ if (pending_buffer) {
+ if (vout->ic_bypass) {
+ ret = ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf);
+ } else {
+ ret = ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf);
+ }
+ if (ret < 0) {
+ dev_err(&vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ }
+ vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+
+ pending_buffer = 0;
+
+ /* Setup timer for next buffer */
+ index = peek_next_buf(&vout->ready_q);
+ if (index != -1) {
+ /* if timestamp is 0, then default to 30fps */
+ if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
+ && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)
+ && vout->start_jiffies)
+ timeout =
+ vout->start_jiffies + vout->frame_count * HZ / 30;
+ else
+ timeout =
+ get_jiffies(&vout->v4l2_bufs[index].timestamp);
+
+ if (jiffies >= timeout) {
+ dev_dbg(&vout->video_dev->dev,
+ "warning: timer timeout already expired.\n");
+ }
+ if (mod_timer(&vout->output_timer, timeout))
+ dev_dbg(&vout->video_dev->dev,
+ "warning: timer was already set\n");
+
+ dev_dbg(&vout->video_dev->dev,
+ "timer handler next schedule: %lu\n", timeout);
+ } else {
+ vout->state = STATE_STREAM_PAUSED;
+ }
+ }
+
+ if (vout->state == STATE_STREAM_STOPPING) {
+ if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) {
+ vout->state = STATE_STREAM_OFF;
+ }
+ }
+
+ spin_unlock_irqrestore(&g_lock, lock_flags);
- complete(comp);
return IRQ_HANDLED;
}
-static void timer_work_func(struct work_struct *work)
+static int get_display_irq(vout_data *vout)
{
- int index, ret, disp_irq = 0;
- unsigned long timeout;
- unsigned long lock_flags = 0;
- vout_data *vout =
- container_of(work, vout_data, timer_work);
- DECLARE_COMPLETION_ONSTACK(disp_comp);
+
+ int disp_irq = 0;
switch (vout->display_ch) {
case MEM_FG_SYNC:
@@ -282,58 +332,12 @@ static void timer_work_func(struct work_struct *work)
"not support display channel\n");
}
- ipu_clear_irq(disp_irq);
- ret = ipu_request_irq(disp_irq, mxc_v4l2out_disp_refresh_irq_handler, 0, NULL, &disp_comp);
- if (ret < 0) {
- dev_err(&vout->video_dev->dev,
- "Disp irq %d in use\n", disp_irq);
- return;
- }
- wait_for_completion_timeout(&disp_comp, msecs_to_jiffies(40));
- ipu_free_irq(disp_irq, &disp_comp);
-
- spin_lock_irqsave(&g_lock, lock_flags);
-
- if (ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
- vout->next_rdy_ipu_buf) < 0) {
- dev_err(&vout->video_dev->dev,
- "unable to set IPU buffer ready\n");
- }
- vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
-
- /* Setup timer for next buffer */
- index = peek_next_buf(&vout->ready_q);
- if (index != -1) {
- /* if timestamp is 0, then default to 30fps */
- if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0)
- && (vout->v4l2_bufs[index].timestamp.tv_usec == 0)
- && vout->start_jiffies)
- timeout =
- vout->start_jiffies + vout->frame_count * HZ / 30;
- else
- timeout =
- get_jiffies(&vout->v4l2_bufs[index].timestamp);
-
- if (jiffies >= timeout) {
- dev_dbg(&vout->video_dev->dev,
- "warning: timer timeout already expired.\n");
- }
- if (mod_timer(&vout->output_timer, timeout))
- dev_dbg(&vout->video_dev->dev,
- "warning: timer was already set\n");
-
- dev_dbg(&vout->video_dev->dev,
- "timer handler next schedule: %lu\n", timeout);
- } else {
- vout->state = STATE_STREAM_PAUSED;
- }
-
- spin_unlock_irqrestore(&g_lock, lock_flags);
+ return disp_irq;
}
static void mxc_v4l2out_timer_handler(unsigned long arg)
{
- int index;
+ int index, ret;
unsigned long lock_flags = 0;
vout_data *vout = (vout_data *) arg;
@@ -365,18 +369,28 @@ static void mxc_v4l2out_timer_handler(unsigned long arg)
g_buf_dq_cnt++;
vout->frame_count++;
- vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
- if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ if (vout->ic_bypass) {
+ vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+ ret = ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index].m.offset);
+ } else {
+ vout->ipu_buf[vout->next_rdy_ipu_buf] = index;
+ ret = ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
vout->next_rdy_ipu_buf,
- vout->v4l2_bufs[index].m.offset) < 0) {
+ vout->v4l2_bufs[index].m.offset);
+ }
+ if (ret < 0) {
dev_err(&vout->video_dev->dev,
"unable to update buffer %d address\n",
vout->next_rdy_ipu_buf);
goto exit0;
}
+ pending_buffer = 1;
+
spin_unlock_irqrestore(&g_lock, lock_flags);
- schedule_work(&vout->timer_work);
+
return;
exit0:
@@ -403,7 +417,6 @@ static irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id)
queue_buf(&vout->done_q, last_buf);
vout->ipu_buf[vout->next_done_ipu_buf] = -1;
wake_up_interruptible(&vout->v4l_bufq);
- /* printk("pp_irq: buf %d done\n", vout->next_done_ipu_buf); */
vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
}
@@ -457,9 +470,9 @@ static int mxc_v4l2out_streamon(vout_data * vout)
struct fb_var_screeninfo fbvar;
struct fb_info *fbi =
registered_fb[vout->output_fb_num[vout->cur_disp_output]];
- int pp_in_buf[2];
u16 out_width;
u16 out_height;
+ int disp_irq = 0;
ipu_channel_t display_input_ch = MEM_PP_MEM;
bool use_direct_adc = false;
mm_segment_t old_fs;
@@ -475,18 +488,20 @@ static int mxc_v4l2out_streamon(vout_data * vout)
return -EINVAL;
}
+ pending_buffer = 0;
+
out_width = vout->crop_current.width;
out_height = vout->crop_current.height;
vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0;
- vout->ipu_buf[0] = pp_in_buf[0] = dequeue_buf(&vout->ready_q);
- vout->ipu_buf[1] = pp_in_buf[1] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf[0] = dequeue_buf(&vout->ready_q);
+ vout->ipu_buf[1] = dequeue_buf(&vout->ready_q);
vout->frame_count = 2;
- ipu_enable_irq(IPU_IRQ_PP_IN_EOF);
-
/* Init Display Channel */
#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+ ipu_enable_irq(IPU_IRQ_PP_IN_EOF);
+
if (vout->cur_disp_output < DISP3) {
mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, 0);
fbi = NULL;
@@ -529,12 +544,8 @@ static int mxc_v4l2out_streamon(vout_data * vout)
mem_pp_adc.
in_pixel_fmt),
vout->rotate,
- vout->
- v4l2_bufs[pp_in_buf[0]].m.
- offset,
- vout->
- v4l2_bufs[pp_in_buf[1]].m.
- offset,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+ vout->v4l2_bufs[vout->ipu_buf[1]].m.offset,
vout->offset.u_offset,
vout->offset.v_offset) !=
0) {
@@ -602,6 +613,20 @@ static int mxc_v4l2out_streamon(vout_data * vout)
{ /* Use SDC */
dev_dbg(dev, "Using SDC channel\n");
+ /* Bypass IC if resizing and rotation not needed
+ Always do CSC in DP
+ Meanwhile, apply IC bypass to SDC only
+ */
+ if (out_width == vout->v2f.fmt.pix.width &&
+ out_height == vout->v2f.fmt.pix.height &&
+ ipu_can_rotate_in_place(vout->rotate)) {
+ pr_debug("Bypassing IC\n");
+ vout->ic_bypass = 1;
+ ipu_disable_irq(IPU_IRQ_PP_IN_EOF);
+ } else {
+ vout->ic_bypass = 0;
+ }
+
fbvar = fbi->var;
if (vout->cur_disp_output == 3) {
@@ -645,12 +670,21 @@ static int mxc_v4l2out_streamon(vout_data * vout)
(fbi->fix.line_length * fbi->var.yres);
vout->display_buf_size = vout->crop_current.width *
vout->crop_current.height * fbi->var.bits_per_pixel / 8;
-
- vout->post_proc_ch = MEM_PP_MEM;
}
/* Init PP */
- if (use_direct_adc == false) {
+ if (use_direct_adc == false && !vout->ic_bypass) {
+ vout->post_proc_ch = MEM_PP_MEM;
+ ipu_enable_irq(IPU_IRQ_PP_IN_EOF);
+
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ out_width = vout->crop_current.height;
+ out_height = vout->crop_current.width;
+ }
+ memset(¶ms, 0, sizeof(params));
+ params.mem_pp_mem.in_width = vout->v2f.fmt.pix.width;
+ params.mem_pp_mem.in_height = vout->v2f.fmt.pix.height;
+
if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
out_width = vout->crop_current.height;
out_height = vout->crop_current.width;
@@ -671,19 +705,19 @@ static int mxc_v4l2out_streamon(vout_data * vout)
}
if (ipu_init_channel_buffer(vout->post_proc_ch,
- IPU_INPUT_BUFFER,
- params.mem_pp_mem.in_pixel_fmt,
- params.mem_pp_mem.in_width,
- params.mem_pp_mem.in_height,
- vout->v2f.fmt.pix.bytesperline /
- bytes_per_pixel(params.mem_pp_mem.
- in_pixel_fmt),
- IPU_ROTATE_NONE,
- vout->v4l2_bufs[pp_in_buf[0]].m.
- offset,
- vout->v4l2_bufs[pp_in_buf[1]].m.
- offset, vout->offset.u_offset,
- vout->offset.v_offset) != 0) {
+ IPU_INPUT_BUFFER,
+ params.mem_pp_mem.in_pixel_fmt,
+ params.mem_pp_mem.in_width,
+ params.mem_pp_mem.in_height,
+ vout->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(params.mem_pp_mem.
+ in_pixel_fmt),
+ IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.
+ offset,
+ vout->v4l2_bufs[vout->ipu_buf[1]].m.
+ offset, vout->offset.u_offset,
+ vout->offset.v_offset) != 0) {
dev_err(dev, "Error initializing PP input buffer\n");
return -EINVAL;
}
@@ -787,27 +821,47 @@ static int mxc_v4l2out_streamon(vout_data * vout)
vout->state = STATE_STREAM_PAUSED;
- ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
- ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1);
-
if (use_direct_adc == false) {
- ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0);
- ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1);
-
- ipu_enable_channel(vout->post_proc_ch);
-
- if (fbi) {
- acquire_console_sem();
- fb_blank(fbi, FB_BLANK_UNBLANK);
- release_console_sem();
+ ipu_enable_channel(vout->display_ch);
+ if (!vout->ic_bypass) {
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1);
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1);
+
+ ipu_enable_channel(vout->post_proc_ch);
} else {
+ ipu_disable_channel(vout->display_ch, true);
+ ipu_init_channel_buffer(vout->display_ch,
+ IPU_INPUT_BUFFER,
+ vout->v2f.fmt.pix.pixelformat,
+ vout->v2f.fmt.pix.width,
+ vout->v2f.fmt.pix.height,
+ vout->v2f.fmt.pix.bytesperline /
+ bytes_per_pixel(vout->v2f.fmt.pix.pixelformat),
+ IPU_ROTATE_NONE,
+ vout->v4l2_bufs[vout->ipu_buf[0]].m.offset,
+ vout->v4l2_bufs[vout->ipu_buf[1]].m.offset,
+ vout->offset.u_offset,
+ vout->offset.v_offset);
ipu_enable_channel(vout->display_ch);
+
+ ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0);
+ ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1);
}
+ disp_irq = get_display_irq(vout);
+ ipu_request_irq(disp_irq, mxc_v4l2out_disp_refresh_irq_handler,
+ 0, NULL, vout);
} else {
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0);
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1);
ipu_enable_channel(vout->post_proc_ch);
}
vout->start_jiffies = jiffies;
+
+ msleep(1);
+
dev_dbg(dev,
"streamon: start time = %lu jiffies\n", vout->start_jiffies);
@@ -825,7 +879,7 @@ static int mxc_v4l2out_streamoff(vout_data * vout)
{
struct fb_info *fbi =
registered_fb[vout->output_fb_num[vout->cur_disp_output]];
- int i, retval = 0;
+ int i, retval = 0, disp_irq = 0;
unsigned long lockflag = 0;
if (!vout)
@@ -835,8 +889,6 @@ static int mxc_v4l2out_streamoff(vout_data * vout)
return 0;
}
- cancel_work_sync(&vout->timer_work);
-
spin_lock_irqsave(&g_lock, lockflag);
del_timer(&vout->output_timer);
@@ -845,10 +897,15 @@ static int mxc_v4l2out_streamoff(vout_data * vout)
vout->state = STATE_STREAM_STOPPING;
}
- ipu_disable_irq(IPU_IRQ_PP_IN_EOF);
+ if (!vout->ic_bypass)
+ ipu_disable_irq(IPU_IRQ_PP_IN_EOF);
spin_unlock_irqrestore(&g_lock, lockflag);
+ pending_buffer = 0;
+ disp_irq = get_display_irq(vout);
+ ipu_free_irq(disp_irq, vout);
+
if (vout->display_ch == MEM_FG_SYNC) {
struct mxcfb_pos fb_pos;
mm_segment_t old_fs;
@@ -880,7 +937,8 @@ static int mxc_v4l2out_streamoff(vout_data * vout)
}
ipu_disable_channel(MEM_PP_MEM, true);
- if (vout->display_ch == ADC_SYS2) {
+ if (vout->display_ch == ADC_SYS2 ||
+ vout->display_ch == MEM_FG_SYNC) {
ipu_disable_channel(vout->display_ch, true);
ipu_uninit_channel(vout->display_ch);
} else {
@@ -1135,8 +1193,6 @@ static int mxc_v4l2out_open(struct inode *inode, struct file *file)
vout->rotate = IPU_ROTATE_NONE;
g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0;
- INIT_WORK(&vout->timer_work, timer_work_func);
-
}
file->private_data = dev;
@@ -1181,7 +1237,6 @@ static int mxc_v4l2out_close(struct inode *inode, struct file *file)
/* capture off */
wake_up_interruptible(&vout->v4l_bufq);
- flush_scheduled_work();
}
return 0;
diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.h b/drivers/media/video/mxc/output/mxc_v4l2_output.h
index 27e5a0a..9f5001f 100644
--- a/drivers/media/video/mxc/output/mxc_v4l2_output.h
+++ b/drivers/media/video/mxc/output/mxc_v4l2_output.h
@@ -92,6 +92,7 @@ typedef struct _vout_data {
int output_fb_num[MXC_V4L2_OUT_NUM_OUTPUTS];
int output_enabled[MXC_V4L2_OUT_NUM_OUTPUTS];
struct v4l2_framebuffer v4l2_fb;
+ int ic_bypass;
ipu_channel_t display_ch;
ipu_channel_t post_proc_ch;
@@ -124,8 +125,6 @@ typedef struct _vout_data {
/* crop */
struct v4l2_rect crop_bounds[MXC_V4L2_OUT_NUM_OUTPUTS];
struct v4l2_rect crop_current;
-
- struct work_struct timer_work;
} vout_data;
#endif
diff --git a/drivers/mxc/ipu3/ipu_disp.c b/drivers/mxc/ipu3/ipu_disp.c
index 19d190b..b8a28c9 100644
--- a/drivers/mxc/ipu3/ipu_disp.c
+++ b/drivers/mxc/ipu3/ipu_disp.c
@@ -64,11 +64,11 @@ void _ipu_dmfc_init(void)
/* disable DMFC-IC channel*/
__raw_writel(0x2, DMFC_IC_CTRL);
/* 1 - segment 0 and 1; 2, 1C and 2C unused */
- __raw_writel(0x00000090, DMFC_WR_CHAN);
+ __raw_writel(0x00000088, DMFC_WR_CHAN);
__raw_writel(0x20202000, DMFC_WR_CHAN_DEF);
/* 5B - segment 2 and 3; 5F - segment 4 and 5; */
/* 6B - segment 6; 6F - segment 7 */
- __raw_writel(0x1F1E9492, DMFC_DP_CHAN);
+ __raw_writel(0x1F1E9694, DMFC_DP_CHAN);
}
void _ipu_dmfc_set_wait4eot(int dma_chan, int width)
@@ -397,9 +397,15 @@ void _ipu_dc_init(int dc_chan, int di, bool interlaced)
_ipu_dc_link_event(dc_chan, DC_EVT_EOL, 0, 2);
_ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 0, 1);
} else {
- _ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3);
- _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2);
- _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 4, 1);
+ if (di) {
+ _ipu_dc_link_event(dc_chan, DC_EVT_NL, 2, 3);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 3, 2);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 4, 1);
+ } else {
+ _ipu_dc_link_event(dc_chan, DC_EVT_NL, 5, 3);
+ _ipu_dc_link_event(dc_chan, DC_EVT_EOL, 6, 2);
+ _ipu_dc_link_event(dc_chan, DC_EVT_NEW_DATA, 7, 1);
+ }
}
_ipu_dc_link_event(dc_chan, DC_EVT_NF, 0, 0);
_ipu_dc_link_event(dc_chan, DC_EVT_NFIELD, 0, 0);
@@ -1087,10 +1093,32 @@ int32_t ipu_init_sync_panel(int disp, uint32_t pixel_clk,
width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0,
0);
+ /* reset all unused counters */
+ __raw_writel(0, DI_SW_GEN0(disp, 6));
+ __raw_writel(0, DI_SW_GEN1(disp, 6));
+ __raw_writel(0, DI_SW_GEN0(disp, 7));
+ __raw_writel(0, DI_SW_GEN1(disp, 7));
+ __raw_writel(0, DI_SW_GEN0(disp, 8));
+ __raw_writel(0, DI_SW_GEN1(disp, 8));
+ __raw_writel(0, DI_SW_GEN0(disp, 9));
+ __raw_writel(0, DI_SW_GEN1(disp, 9));
+
+ reg = __raw_readl(DI_STP_REP(disp, 6));
+ reg &= 0x0000FFFF;
+ __raw_writel(reg, DI_STP_REP(disp, 6));
+ __raw_writel(0, DI_STP_REP(disp, 7));
+ __raw_writel(0, DI_STP_REP(disp, 9));
+
/* Init template microcode */
- _ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5);
- _ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5);
- _ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ if (disp) {
+ _ipu_dc_write_tmpl(2, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ _ipu_dc_write_tmpl(3, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ _ipu_dc_write_tmpl(4, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ } else {
+ _ipu_dc_write_tmpl(5, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ _ipu_dc_write_tmpl(6, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ _ipu_dc_write_tmpl(7, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ }
if (sig.Hsync_pol)
di_gen |= DI_GEN_POLARITY_2;
diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c
index 2b9d43d..e1a1707 100644
--- a/drivers/video/mxc/mxc_ipuv3_fb.c
+++ b/drivers/video/mxc/mxc_ipuv3_fb.c
@@ -141,6 +141,7 @@ static int mxcfb_set_par(struct fb_info *fbi)
ipu_di_signal_cfg_t sig_cfg;
ipu_channel_params_t params;
struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par;
+ uint32_t v_to_h_sync;
dev_dbg(fbi->device, "Reconfiguring framebuffer\n");
@@ -215,6 +216,7 @@ static int mxcfb_set_par(struct fb_info *fbi)
fbi->var.vsync_len,
fbi->var.lower_margin, sig_cfg) != 0) {
#else
+ v_to_h_sync = (mxc_fbi->ipu_di) ? 0 : 480;
if (ipu_init_sync_panel(mxc_fbi->ipu_di,
(PICOS2KHZ(fbi->var.pixclock)) * 1000UL,
fbi->var.xres, fbi->var.yres,
@@ -225,7 +227,7 @@ static int mxcfb_set_par(struct fb_info *fbi)
fbi->var.upper_margin,
fbi->var.vsync_len,
fbi->var.lower_margin,
- 480, sig_cfg) != 0) {
++ v_to_h_sync, sig_cfg) != 0) {
#endif
dev_err(fbi->device,
"mxcfb: Error initializing panel.\n");
diff --git a/drivers/video/mxc/tve.c b/drivers/video/mxc/tve.c
index 10436d4..46bf5e7 100644
--- a/drivers/video/mxc/tve.c
+++ b/drivers/video/mxc/tve.c
@@ -56,10 +56,12 @@
#define TVE_STAND_MASK (0x0F<<8)
#define TVE_NTSC_STAND (0UL<<8)
#define TVE_PAL_STAND (3UL<<8)
+#define TVE_HD720P60_STAND (4UL<<8)
#define TVOUT_FMT_OFF 0
#define TVOUT_FMT_NTSC 1
#define TVOUT_FMT_PAL 2
+#define TVOUT_FMT_720P60 3
static int enabled; /* enable power on or not */
@@ -67,6 +69,7 @@ static struct fb_info *tve_fbi;
struct tve_data {
struct platform_device *pdev;
+ int revision;
int cur_mode;
int output_mode;
int detect;
@@ -140,6 +143,16 @@ static struct fb_videomode video_modes[] = {
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_EXT,
FB_VMODE_INTERLACED | FB_VMODE_ODD_FLD_FIRST,
0,},
+ {
+ /* 720p60 TV output */
+ "720P60", 60, 1280, 720, 13468,
+ 260, 109,
+ 25, 4,
+ 1, 1,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT |
+ FB_SYNC_EXT,
+ FB_VMODE_NONINTERLACED,
+ 0,},
};
enum tvout_mode {
@@ -180,6 +193,20 @@ static void tve_set_tvout_mode(int mode)
__raw_writel(conf_reg, tve.base + tve_regs->tve_com_conf_reg);
}
+static int _is_tvout_mode_hd_compatible(void)
+{
+ u32 conf_reg, mode;
+
+ conf_reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+ mode = (conf_reg >> tve_reg_fields->tvout_mode_offset) & 7;
+ if (mode == YPBPR || mode == RGB) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
/**
* tve_setup
* initial the CH7024 chipset by setting register
@@ -192,14 +219,32 @@ static void tve_set_tvout_mode(int mode)
static int tve_setup(int mode)
{
u32 reg;
+ struct clk *pll3_clk;
+ unsigned long pll3_clock_rate = 216000000;
if (tve.cur_mode == mode)
return 0;
tve.cur_mode = mode;
- if (!enabled)
- clk_enable(tve.clk);
+ switch (mode) {
+ case TVOUT_FMT_PAL:
+ case TVOUT_FMT_NTSC:
+ pll3_clock_rate = 216000000;
+ break;
+ case TVOUT_FMT_720P60:
+ pll3_clock_rate = 297000000;
+ break;
+ }
+ if (enabled)
+ clk_disable(tve.clk);
+
+ pll3_clk = clk_get(NULL, "pll3");
+ clk_disable(pll3_clk);
+ clk_set_rate(pll3_clk, pll3_clock_rate);
+ clk_enable(pll3_clk);
+
+ clk_enable(tve.clk);
/* select output video format */
if (mode == TVOUT_FMT_PAL) {
@@ -212,6 +257,15 @@ static int tve_setup(int mode)
reg = (reg & ~TVE_STAND_MASK) | TVE_NTSC_STAND;
__raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
pr_debug("TVE: change to NTSC video\n");
+ } else if (mode == TVOUT_FMT_720P60) {
+ if (!_is_tvout_mode_hd_compatible()) {
+ tve_set_tvout_mode(YPBPR);
+ pr_debug("The TV out mode is HD incompatible. Setting to YPBPR.");
+ }
+ reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
+ reg = (reg & ~TVE_STAND_MASK) | TVE_HD720P60_STAND;
+ __raw_writel(reg, tve.base + tve_regs->tve_com_conf_reg);
+ pr_debug("TVE: change to 720P60 video\n");
} else if (mode == TVOUT_FMT_OFF) {
__raw_writel(0x0, tve.base + tve_regs->tve_com_conf_reg);
pr_debug("TVE: change to OFF video\n");
@@ -265,7 +319,6 @@ static void tve_disable(void)
reg = __raw_readl(tve.base + tve_regs->tve_com_conf_reg);
__raw_writel(reg & ~TVE_ENABLE & ~TVE_IPU_CLK_ENABLE,
tve.base + tve_regs->tve_com_conf_reg);
- tve_set_tvout_mode(TV_OFF);
clk_disable(tve.clk);
pr_debug("TVE power off.\n");
}
@@ -419,6 +472,7 @@ int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v)
tve_fbi = fbi;
fb_add_videomode(&video_modes[0], &tve_fbi->modelist);
fb_add_videomode(&video_modes[1], &tve_fbi->modelist);
+ fb_add_videomode(&video_modes[2], &tve_fbi->modelist);
break;
case FB_EVENT_MODE_CHANGE:
if (tve_fbi != fbi)
@@ -441,6 +495,9 @@ int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v)
} else if (fb_mode_is_equal(fbi->mode, &video_modes[1])) {
tve_setup(TVOUT_FMT_PAL);
tve_enable();
+ } else if (fb_mode_is_equal(fbi->mode, &video_modes[2])) {
+ tve_setup(TVOUT_FMT_720P60);
+ tve_enable();
} else {
tve_setup(TVOUT_FMT_OFF);
}
@@ -463,6 +520,13 @@ int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v)
tve_setup(TVOUT_FMT_PAL);
}
tve_enable();
+ } else if (fb_mode_is_equal(fbi->mode,
+ &video_modes[2])) {
+ if (tve.cur_mode != TVOUT_FMT_720P60) {
+ tve_disable();
+ tve_setup(TVOUT_FMT_720P60);
+ }
+ tve_enable();
} else {
tve_setup(TVOUT_FMT_OFF);
}
@@ -544,7 +608,6 @@ static int tve_probe(struct platform_device *pdev)
goto err0;
}
- INIT_DELAYED_WORK(&tve.cd_work, cd_work_func);
ret = request_irq(tve.irq, tve_detect_handler, 0, pdev->name, pdev);
if (ret < 0)
goto err0;
@@ -575,6 +638,7 @@ static int tve_probe(struct platform_device *pdev)
if (tve_fbi != NULL) {
fb_add_videomode(&video_modes[0], &tve_fbi->modelist);
fb_add_videomode(&video_modes[1], &tve_fbi->modelist);
+ fb_add_videomode(&video_modes[2], &tve_fbi->modelist);
}
tve.dac_reg = regulator_get(&pdev->dev, plat_data->dac_reg);
@@ -593,7 +657,8 @@ static int tve_probe(struct platform_device *pdev)
clk_set_rate(tve.clk, 216000000);
clk_enable(tve.clk);
- if (_tve_get_revision() == 1) {
+ tve.revision = _tve_get_revision();
+ if (tve.revision == 1) {
tve_regs = &tve_regs_v1;
tve_reg_fields = &tve_reg_fields_v1;
} else {
@@ -602,8 +667,11 @@ static int tve_probe(struct platform_device *pdev)
}
/* Setup cable detect, for YPrPb mode, default use channel#0 for Y */
- __raw_writel(0x01067701, tve.base + tve_regs->tve_cd_cont_reg);
- /* tve_man_detect(); not working */
+ INIT_DELAYED_WORK(&tve.cd_work, cd_work_func);
+ if (tve.revision == 1)
+ __raw_writel(0x01067701, tve.base + tve_regs->tve_cd_cont_reg);
+ else
+ __raw_writel(0x00770601, tve.base + tve_regs->tve_cd_cont_reg);
conf_reg = 0;
__raw_writel(conf_reg, tve.base + tve_regs->tve_com_conf_reg);
@@ -663,7 +731,12 @@ static int tve_resume(struct platform_device *pdev)
clk_enable(tve.clk);
/* Setup cable detect */
- __raw_writel(0x01067701, tve.base + tve_regs->tve_cd_cont_reg);
+ if (tve.revision == 1)
+ __raw_writel(0x01067701,
+ tve.base + tve_regs->tve_cd_cont_reg);
+ else
+ __raw_writel(0x00770601,
+ tve.base + tve_regs->tve_cd_cont_reg);
if (tve.cur_mode == TVOUT_FMT_NTSC) {
tve_disable();
@@ -673,6 +746,10 @@ static int tve_resume(struct platform_device *pdev)
tve_disable();
tve.cur_mode = TVOUT_FMT_OFF;
tve_setup(TVOUT_FMT_PAL);
+ } else if (tve.cur_mode == TVOUT_FMT_720P60) {
+ tve_disable();
+ tve.cur_mode = TVOUT_FMT_OFF;
+ tve_setup(TVOUT_FMT_720P60);
}
tve_enable();
}
--
1.6.0.4
More information about the kernel-team
mailing list