[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 17:49:40 BST 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(&params, 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