diff options
author | Ran Ferderber <Ran.Ferderber@freescale.com> | 2009-08-11 10:45:22 +0300 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2009-10-13 11:05:09 -0400 |
commit | 1852787b446a0b8279dfd3efd9d47403c056ff31 (patch) | |
tree | 9a08a43108e63952cb3900cb665fda7e3c38f544 | |
parent | a9067a441adcab98b64e720c024698b2727c6476 (diff) |
ENGR00114284: MX51: Add IPU interlace support in 2 more motion algorithms
Add algorithms for medium and low motion streams
Signed-off-by: Ran Ferderber Ran.Ferderber@freescale.com
-rw-r--r-- | drivers/media/video/mxc/output/mxc_v4l2_output.c | 365 | ||||
-rw-r--r-- | drivers/media/video/mxc/output/mxc_v4l2_output.h | 5 | ||||
-rw-r--r-- | drivers/mxc/ipu3/ipu_common.c | 40 | ||||
-rw-r--r-- | drivers/mxc/ipu3/ipu_ic.c | 82 | ||||
-rw-r--r-- | drivers/mxc/ipu3/ipu_prv.h | 3 | ||||
-rw-r--r-- | drivers/mxc/ipu3/ipu_regs.h | 6 | ||||
-rw-r--r-- | include/linux/ipu.h | 17 | ||||
-rw-r--r-- | include/linux/mxc_v4l2.h | 1 |
8 files changed, 404 insertions, 115 deletions
diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.c b/drivers/media/video/mxc/output/mxc_v4l2_output.c index ac289e85ee5f..d38afbb17b46 100644 --- a/drivers/media/video/mxc/output/mxc_v4l2_output.c +++ b/drivers/media/video/mxc/output/mxc_v4l2_output.c @@ -36,6 +36,12 @@ #include "mxc_v4l2_output.h" vout_data *g_vout; +#define INTERLACED_CONTENT(vout) ((cpu_is_mx51_rev(CHIP_REV_2_0) >= 1) && \ + (((vout)->field_fmt == V4L2_FIELD_INTERLACED_TB) || \ + ((vout)->field_fmt == V4L2_FIELD_INTERLACED_BT))) +#define LOAD_3FIELDS(vout) ((INTERLACED_CONTENT(vout)) && \ + ((vout)->motion_sel != HIGH_MOTION)) + #define SDC_FG_FB_FORMAT IPU_PIX_FMT_RGB565 struct v4l2_output mxc_outputs[2] = { @@ -59,7 +65,10 @@ struct v4l2_output mxc_outputs[2] = { static int video_nr = 16; static int pending_buffer; +static int pp_eof; static spinlock_t g_lock = SPIN_LOCK_UNLOCKED; +static int last_index_n; +static int last_index_c; /* debug counters */ uint32_t g_irq_cnt; @@ -259,13 +268,18 @@ static irqreturn_t mxc_v4l2out_disp_refresh_irq_handler(int irq, void *dev_id) } } - if (pending_buffer) { + if ((pending_buffer) && (pp_eof || vout->ic_bypass)) { + pp_eof = 0; 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 (LOAD_3FIELDS(vout)) { + ret = ipu_select_multi_vdi_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, @@ -341,7 +355,6 @@ static void mxc_v4l2out_timer_handler(unsigned long arg) unsigned long lock_flags = 0; vout_data *vout = (vout_data *) arg; - spin_lock_irqsave(&g_lock, lock_flags); if ((vout->state == STATE_STREAM_STOPPING) @@ -359,25 +372,71 @@ static void mxc_v4l2out_timer_handler(unsigned long arg) } /* Dequeue buffer and pass to IPU */ - index = dequeue_buf(&vout->ready_q); - if (index == -1) { /* no buffers ready, should never occur */ - dev_err(&vout->video_dev->dev, - "mxc_v4l2out: timer - no queued buffers ready\n"); - goto exit0; + unsigned int aid_field_offset, current_field_offset; + if (INTERLACED_CONTENT(vout)) { + if (((LOAD_3FIELDS(vout)) && (vout->next_rdy_ipu_buf)) || + ((!LOAD_3FIELDS(vout)) && !(vout->next_rdy_ipu_buf))) { + aid_field_offset = vout->bytesperline; + current_field_offset = 0; + index = last_index_n; + } else { + aid_field_offset = 0; + current_field_offset = vout->bytesperline; + index = dequeue_buf(&vout->ready_q); + if (index == -1) { /* no buffers ready, should never occur */ + dev_err(&vout->video_dev->dev, + "mxc_v4l2out: timer - no queued buffers ready\n"); + goto exit0; + } + g_buf_dq_cnt++; + vout->frame_count++; + last_index_n = index; + } + } else { + current_field_offset = 0; + index = dequeue_buf(&vout->ready_q); + if (index == -1) { /* no buffers ready, should never occur */ + dev_err(&vout->video_dev->dev, + "mxc_v4l2out: timer - no queued buffers ready\n"); + goto exit0; + } + g_buf_dq_cnt++; + vout->frame_count++; } - g_buf_dq_cnt++; - vout->frame_count++; 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); + if (LOAD_3FIELDS(vout)) { + int index_n = index; + index = last_index_n; + int index_p = last_index_c; + vout->ipu_buf_p[vout->next_rdy_ipu_buf] = index_p; + vout->ipu_buf[vout->next_rdy_ipu_buf] = last_index_c = index; + vout->ipu_buf_n[vout->next_rdy_ipu_buf] = last_index_n = index_n; + last_index_n = vout->ipu_buf_n[vout->next_rdy_ipu_buf]; + last_index_c = vout->ipu_buf[vout->next_rdy_ipu_buf]; + ret = ipu_update_channel_buffer(vout->post_proc_ch, + IPU_INPUT_BUFFER, + vout->next_rdy_ipu_buf, + vout->v4l2_bufs[index].m.offset+current_field_offset); + ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_P, + IPU_INPUT_BUFFER, + vout->next_rdy_ipu_buf, + vout->v4l2_bufs[index_p].m.offset+aid_field_offset); + ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_N, + IPU_INPUT_BUFFER, + vout->next_rdy_ipu_buf, + vout->v4l2_bufs[index_n].m.offset+aid_field_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+current_field_offset); + } } if (ret < 0) { dev_err(&vout->video_dev->dev, @@ -409,15 +468,26 @@ static irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id) g_irq_cnt++; /* Process previous buffer */ - last_buf = vout->ipu_buf[vout->next_done_ipu_buf]; + if (LOAD_3FIELDS(vout)) + last_buf = vout->ipu_buf_p[vout->next_done_ipu_buf]; + else + 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); + if ((!INTERLACED_CONTENT(vout)) || (vout->next_done_ipu_buf)) { + g_buf_output_cnt++; + vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE; + queue_buf(&vout->done_q, last_buf); + wake_up_interruptible(&vout->v4l_bufq); + } vout->ipu_buf[vout->next_done_ipu_buf] = -1; - wake_up_interruptible(&vout->v4l_bufq); + if (LOAD_3FIELDS(vout)) { + vout->ipu_buf_p[vout->next_done_ipu_buf] = -1; + vout->ipu_buf_n[vout->next_done_ipu_buf] = -1; + } vout->next_done_ipu_buf = !vout->next_done_ipu_buf; } + pp_eof = 1; if (vout->state == STATE_STREAM_STOPPING) { if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) { @@ -454,12 +524,88 @@ static irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_MXC_IPU_V3EX /*! - * Start the output stream + * Initialize VDI channels + * + * @param vout structure vout_data * + * + * @return status 0 Success + */ +static int init_VDI_channel(vout_data *vout, ipu_channel_params_t params) +{ + struct device *dev = &vout->video_dev->dev; + + if (ipu_init_channel(MEM_VDI_PRP_VF_MEM, ¶ms) != 0) { + dev_dbg(dev, "Error initializing VDI current channel\n"); + return -EINVAL; + } + if (LOAD_3FIELDS(vout)) { + if (ipu_init_channel(MEM_VDI_PRP_VF_MEM_P, ¶ms) != 0) { + dev_err(dev, "Error initializing VDI previous channel\n"); + return -EINVAL; + } + if (ipu_init_channel(MEM_VDI_PRP_VF_MEM_N, ¶ms) != 0) { + dev_err(dev, "Error initializing VDI next channel\n"); + return -EINVAL; + } + } + return 0; +} + +/*! + * Initialize VDI channel buffers + * + * @param vout structure vout_data * + * + * @return status 0 Success + */ +static int init_VDI_in_channel_buffer(vout_data *vout, uint32_t in_pixel_fmt, + uint16_t in_width, uint16_t in_height, + uint32_t stride, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, + uint32_t u_offset, uint32_t v_offset) +{ + struct device *dev = &vout->video_dev->dev; + + if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER, + in_pixel_fmt, in_width, in_height, stride, + IPU_ROTATE_NONE, + vout->v4l2_bufs[vout->ipu_buf[0]].m.offset+vout->bytesperline, + vout->v4l2_bufs[vout->ipu_buf[0]].m.offset, + u_offset, v_offset) != 0) { + dev_err(dev, "Error initializing VDI current input buffer\n"); + return -EINVAL; + } + if (LOAD_3FIELDS(vout)) { + if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM_P, + IPU_INPUT_BUFFER, + in_pixel_fmt, in_width, in_height, + stride, IPU_ROTATE_NONE, + vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset, + vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline, + u_offset, v_offset) != 0) { + dev_err(dev, "Error initializing VDI previous input buffer\n"); + return -EINVAL; + } + if (ipu_init_channel_buffer(MEM_VDI_PRP_VF_MEM_N, + IPU_INPUT_BUFFER, + in_pixel_fmt, in_width, in_height, + stride, IPU_ROTATE_NONE, + vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset, + vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline, + u_offset, v_offset) != 0) { + dev_err(dev, "Error initializing VDI next input buffer\n"); + return -EINVAL; + } + } + return 0; +} + +/*! + * Initialize VDI path * * @param vout structure vout_data * - * out_width + * * @return status 0 Success */ static int init_VDI(ipu_channel_params_t params, vout_data *vout, @@ -469,6 +615,8 @@ static int init_VDI(ipu_channel_params_t params, vout_data *vout, { params.mem_prp_vf_mem.in_width = vout->v2f.fmt.pix.width; params.mem_prp_vf_mem.in_height = vout->v2f.fmt.pix.height; + params.mem_prp_vf_mem.motion_sel = vout->motion_sel; + params.mem_prp_vf_mem.field_fmt = vout->field_fmt; params.mem_prp_vf_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat; params.mem_prp_vf_mem.out_width = out_width; params.mem_prp_vf_mem.out_height = out_height; @@ -476,25 +624,23 @@ static int init_VDI(ipu_channel_params_t params, vout_data *vout, params.mem_prp_vf_mem.out_pixel_fmt = SDC_FG_FB_FORMAT; else params.mem_prp_vf_mem.out_pixel_fmt = bpp_to_fmt(fbi); - if (ipu_init_channel(vout->post_proc_ch, ¶ms) != 0) { - dev_err(dev, "Error initializing PRP channel\n"); + + if (init_VDI_channel(vout, params) != 0) { + dev_err(dev, "Error init_VDI_channel channel\n"); return -EINVAL; } - if (ipu_init_channel_buffer(vout->post_proc_ch, - IPU_INPUT_BUFFER, - params.mem_prp_vf_mem.in_pixel_fmt, - params.mem_prp_vf_mem.in_width, - params.mem_prp_vf_mem.in_height, - vout->v2f.fmt.pix.bytesperline / - bytes_per_pixel(params.mem_prp_vf_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 PRP input buffer\n"); + + if (init_VDI_in_channel_buffer(vout, + params.mem_prp_vf_mem.in_pixel_fmt, + params.mem_prp_vf_mem.in_width, + params.mem_prp_vf_mem.in_height, + bytes_per_pixel(params.mem_prp_vf_mem. + in_pixel_fmt), + 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) { return -EINVAL; } @@ -583,10 +729,11 @@ static int init_VDI(ipu_channel_params_t params, vout_data *vout, } return 0; } -#endif /*! - * Start the output stream + * Initialize PP path + * + * @param params structure ipu_channel_params_t * * @param vout structure vout_data * * @@ -734,17 +881,14 @@ static int mxc_v4l2out_streamon(vout_data * vout) bool use_direct_adc = false; mm_segment_t old_fs; -#ifdef CONFIG_MXC_IPU_V3EX dev_dbg(dev, "mxc_v4l2out_streamon: field format=%d\n", vout->field_fmt); - if (vout->field_fmt == V4L2_FIELD_ALTERNATE) { + if (INTERLACED_CONTENT(vout)) { ipu_request_irq(IPU_IRQ_PRP_VF_OUT_EOF, mxc_v4l2out_pp_in_irq_handler, 0, &vout->video_dev->name, vout); display_input_ch = MEM_VDI_PRP_VF_MEM; - } else -#endif - { + } else { ipu_request_irq(IPU_IRQ_PP_IN_EOF, mxc_v4l2out_pp_in_irq_handler, 0, &vout->video_dev->name, vout); @@ -762,26 +906,46 @@ static int mxc_v4l2out_streamon(vout_data * vout) return -EINVAL; } + if ((vout->field_fmt == V4L2_FIELD_BOTTOM) || (vout->field_fmt == V4L2_FIELD_TOP)) { + dev_err(dev, "4 queued buffers need, not supported yet!\n"); + 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] = dequeue_buf(&vout->ready_q); - vout->ipu_buf[1] = dequeue_buf(&vout->ready_q); - vout->frame_count = 2; + vout->next_done_ipu_buf = 0; + vout->next_rdy_ipu_buf = 1; + + if (!INTERLACED_CONTENT(vout)) { + vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0; + vout->ipu_buf[0] = dequeue_buf(&vout->ready_q); + vout->ipu_buf[1] = dequeue_buf(&vout->ready_q); + vout->frame_count = 2; + } else if (!LOAD_3FIELDS(vout)) { + vout->ipu_buf[0] = dequeue_buf(&vout->ready_q); + vout->ipu_buf[1] = -1; + vout->frame_count = 1; + last_index_n = vout->ipu_buf[0]; + } else { + vout->ipu_buf_p[0] = dequeue_buf(&vout->ready_q); + vout->ipu_buf[0] = vout->ipu_buf_p[0]; + vout->ipu_buf_n[0] = dequeue_buf(&vout->ready_q); + vout->ipu_buf_p[1] = -1; + vout->ipu_buf[1] = -1; + vout->ipu_buf_n[1] = -1; + last_index_c = vout->ipu_buf[0]; + last_index_n = vout->ipu_buf_n[0]; + vout->frame_count = 2; + } /* Init Display Channel */ #ifdef CONFIG_FB_MXC_ASYNC_PANEL -#ifdef CONFIG_MXC_IPU_V3EX - if (vout->field_fmt == V4L2_FIELD_ALTERNATE) { + if (INTERLACED_CONTENT(vout)) ipu_enable_irq(IPU_IRQ_PRP_VF_OUT_EOF); - } else -#endif - { + else ipu_enable_irq(IPU_IRQ_PP_IN_EOF); - } if (vout->cur_disp_output < DISP3) { mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, 0); @@ -963,25 +1127,18 @@ 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; -#ifdef CONFIG_MXC_IPU_V3EX - if (vout->field_fmt == V4L2_FIELD_ALTERNATE) { + if (INTERLACED_CONTENT(vout)) vout->post_proc_ch = MEM_VDI_PRP_VF_MEM; - } else -#endif - { + else vout->post_proc_ch = MEM_PP_MEM; - } } /* Init PP */ if (use_direct_adc == false && !vout->ic_bypass) { -#ifdef CONFIG_MXC_IPU_V3EX - if (vout->field_fmt == V4L2_FIELD_ALTERNATE) { + if (INTERLACED_CONTENT(vout)) { vout->post_proc_ch = MEM_VDI_PRP_VF_MEM; ipu_enable_irq(IPU_IRQ_PRP_VF_OUT_EOF); - } else -#endif - { + } else { vout->post_proc_ch = MEM_PP_MEM; ipu_enable_irq(IPU_IRQ_PP_IN_EOF); } @@ -992,13 +1149,10 @@ static int mxc_v4l2out_streamon(vout_data * vout) } memset(¶ms, 0, sizeof(params)); int rc; -#ifdef CONFIG_MXC_IPU_V3EX - if (vout->field_fmt == V4L2_FIELD_ALTERNATE) { + if (INTERLACED_CONTENT(vout)) { rc = init_VDI(params, vout, dev, fbi, &display_input_ch, out_width, out_height); - } else -#endif - { + } else { rc = init_PP(params, vout, dev, fbi, &display_input_ch, out_width, out_height); } @@ -1015,12 +1169,19 @@ static int mxc_v4l2out_streamon(vout_data * vout) if (use_direct_adc == false) { 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_enable_channel(vout->post_proc_ch); 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 (LOAD_3FIELDS(vout)) { + ipu_enable_channel(MEM_VDI_PRP_VF_MEM_P); + ipu_enable_channel(MEM_VDI_PRP_VF_MEM_N); + ipu_select_multi_vdi_buffer(0); + } else if (INTERLACED_CONTENT(vout)) { + ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0); + } else { + ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0); + ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1); + } } else { ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER, @@ -1028,7 +1189,6 @@ static int mxc_v4l2out_streamon(vout_data * vout) ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1, vout->v4l2_bufs[vout->ipu_buf[1]].m.offset); - ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0); ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1); } @@ -1040,7 +1200,6 @@ static int mxc_v4l2out_streamon(vout_data * vout) ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1); ipu_enable_channel(vout->post_proc_ch); } - vout->start_jiffies = jiffies; msleep(1); @@ -1072,14 +1231,10 @@ static int mxc_v4l2out_streamoff(vout_data * vout) return 0; } -#ifdef CONFIG_MXC_IPU_V3EX - if (vout->field_fmt == V4L2_FIELD_ALTERNATE) { + if (INTERLACED_CONTENT(vout)) ipu_free_irq(IPU_IRQ_PRP_VF_OUT_EOF, vout); - } else -#endif - { + else ipu_free_irq(IPU_IRQ_PP_IN_EOF, vout); - } spin_lock_irqsave(&g_lock, lockflag); @@ -1090,14 +1245,10 @@ static int mxc_v4l2out_streamoff(vout_data * vout) } if (!vout->ic_bypass) { -#ifdef CONFIG_MXC_IPU_V3EX - if (vout->field_fmt == V4L2_FIELD_ALTERNATE) { + if (INTERLACED_CONTENT(vout)) ipu_disable_irq(IPU_IRQ_PRP_VF_OUT_EOF); - } else -#endif - { + else ipu_disable_irq(IPU_IRQ_PP_IN_EOF); - } } spin_unlock_irqrestore(&g_lock, lockflag); @@ -1136,10 +1287,8 @@ static int mxc_v4l2out_streamoff(vout_data * vout) vout->display_buf_size); } } else { - ipu_unlink_channels(MEM_PP_MEM, - vout->display_ch); + ipu_unlink_channels(MEM_PP_MEM, vout->display_ch); } - ipu_disable_channel(MEM_PP_MEM, true); if (vout->display_ch == ADC_SYS2 || @@ -1163,9 +1312,7 @@ static int mxc_v4l2out_streamoff(vout_data * vout) ipu_uninit_channel(MEM_PP_MEM); if (!ipu_can_rotate_in_place(vout->rotate)) ipu_uninit_channel(MEM_ROT_PP_MEM); - } -#ifdef CONFIG_MXC_IPU_V3EX - else if (vout->post_proc_ch == MEM_VDI_PRP_VF_MEM) { + } else if (INTERLACED_CONTENT(vout) && (vout->post_proc_ch == MEM_VDI_PRP_VF_MEM)) { if (!ipu_can_rotate_in_place(vout->rotate)) { ipu_unlink_channels(MEM_VDI_PRP_VF_MEM, MEM_ROT_VF_MEM); @@ -1206,9 +1353,7 @@ static int mxc_v4l2out_streamoff(vout_data * vout) ipu_uninit_channel(MEM_VDI_PRP_VF_MEM); if (!ipu_can_rotate_in_place(vout->rotate)) ipu_uninit_channel(MEM_ROT_VF_MEM); - } -#endif - else { /* ADC Direct */ + } else { /* ADC Direct */ ipu_disable_channel(MEM_PP_ADC, true); ipu_uninit_channel(MEM_PP_ADC); } @@ -1309,21 +1454,26 @@ static int mxc_v4l2out_s_fmt(vout_data * vout, struct v4l2_format *f) } else { bytesperline = f->fmt.pix.bytesperline; } + vout->bytesperline = bytesperline; /* Based on http://v4l2spec.bytesex.org/spec/x6386.htm#V4L2-FIELD */ - switch (f->fmt.pix.field) { + vout->field_fmt = f->fmt.pix.field; + switch (vout->field_fmt) { /* Images are in progressive format, not interlaced */ case V4L2_FIELD_NONE: - vout->field_fmt = V4L2_FIELD_NONE; break; /* The two fields of a frame are passed in separate buffers, in temporal order, i. e. the older one first. */ case V4L2_FIELD_ALTERNATE: + dev_err(&vout->video_dev->dev, + "V4L2_FIELD_ALTERNATE field format not supported yet!\n"); + break; case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: if (cpu_is_mx51()) - vout->field_fmt = V4L2_FIELD_ALTERNATE; - break; + break; + case V4L2_FIELD_INTERLACED_BT: + dev_err(&vout->video_dev->dev, + "V4L2_FIELD_INTERLACED_BT field format not supported yet!\n"); default: vout->field_fmt = V4L2_FIELD_NONE; break; @@ -1415,6 +1565,9 @@ static int mxc_set_v42lout_control(vout_data * vout, struct v4l2_control *c) case V4L2_CID_MXC_ROT: vout->rotate = c->value; break; + case V4L2_CID_MXC_MOTION: + vout->motion_sel = c->value; + break; default: return -EINVAL; } @@ -1669,7 +1822,7 @@ mxc_v4l2out_do_ioctl(struct inode *inode, struct file *file, break; } - dev_dbg(&vdev->dev, "VIDIOC_QBUF: %d\n", buf->index); + dev_dbg(&vdev->dev, "VIDIOC_QBUF: %d field = %d\n", buf->index, buf->field); /* mmapped buffers are L1 WB cached, * so we need to clean them */ diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.h b/drivers/media/video/mxc/output/mxc_v4l2_output.h index 6d4084053e29..069edde1e850 100644 --- a/drivers/media/video/mxc/output/mxc_v4l2_output.h +++ b/drivers/media/video/mxc/output/mxc_v4l2_output.h @@ -41,6 +41,7 @@ #define MXC_V4L2_OUT_2_SDC 0 #define MXC_V4L2_OUT_2_ADC 1 + typedef struct { int list[MAX_FRAME_NUM + 1]; int head; @@ -87,6 +88,8 @@ typedef struct _vout_data { s8 next_rdy_ipu_buf; s8 next_done_ipu_buf; s8 ipu_buf[2]; + s8 ipu_buf_p[2]; + s8 ipu_buf_n[2]; volatile v4lout_state state; int cur_disp_output; @@ -126,7 +129,9 @@ typedef struct _vout_data { /* crop */ struct v4l2_rect crop_bounds[MXC_V4L2_OUT_NUM_OUTPUTS]; struct v4l2_rect crop_current; + u32 bytesperline; enum v4l2_field field_fmt; + ipu_motion_sel motion_sel; } vout_data; #endif diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c index 7fb6b983a58f..c81e3a22ce32 100644 --- a/drivers/mxc/ipu3/ipu_common.c +++ b/drivers/mxc/ipu3/ipu_common.c @@ -484,7 +484,13 @@ int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t *params) if (params->mem_prp_vf_mem.graphics_combine_en) g_sec_chan_en[IPU_CHAN_ID(channel)] = true; _ipu_ic_init_prpvf(params, false); - _ipu_vdi_init(params); + _ipu_vdi_init(channel, params); + break; + case MEM_VDI_PRP_VF_MEM_P: + _ipu_vdi_init(channel, params); + break; + case MEM_VDI_PRP_VF_MEM_N: + _ipu_vdi_init(channel, params); break; case MEM_ROT_VF_MEM: ipu_ic_use_count++; @@ -1036,10 +1042,41 @@ int32_t ipu_select_buffer(ipu_channel_t channel, ipu_buffer_t type, /*Mark buffer 1 as ready. */ __raw_writel(idma_mask(dma_chan), IPU_CHA_BUF1_RDY(dma_chan)); } + if (channel == MEM_VDI_PRP_VF_MEM) + _ipu_vdi_toggle_top_field_man(); return 0; } EXPORT_SYMBOL(ipu_select_buffer); +/*! + * This function is called to set a channel's buffer as ready. + * + * @param bufNum Input parameter for which buffer number set to + * ready state. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_select_multi_vdi_buffer(uint32_t bufNum) +{ + + uint32_t dma_chan = channel_2_dma(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER); + uint32_t mask_bit = + idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_P, IPU_INPUT_BUFFER))| + idma_mask(dma_chan)| + idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_N, IPU_INPUT_BUFFER)); + + if (bufNum == 0) { + /*Mark buffer 0 as ready. */ + __raw_writel(mask_bit, IPU_CHA_BUF0_RDY(dma_chan)); + } else { + /*Mark buffer 1 as ready. */ + __raw_writel(mask_bit, IPU_CHA_BUF1_RDY(dma_chan)); + } + _ipu_vdi_toggle_top_field_man(); + return 0; +} +EXPORT_SYMBOL(ipu_select_multi_vdi_buffer); + #define NA -1 static int proc_dest_sel[] = { 0, 1, 1, 3, 5, 5, 4, 7, 8, 9, 10, 11, 12, 14, 15, 16, @@ -1654,7 +1691,6 @@ static irqreturn_t ipu_irq_handler(int irq, void *desc) dev_err(g_ipu_dev, "IPU Error - IPU_INT_STAT_%d = 0x%08X\n", err_reg[i], int_stat); - /* Disable interrupts so we only get error once */ int_stat = __raw_readl(IPU_INT_CTRL(err_reg[i])) & ~int_stat; diff --git a/drivers/mxc/ipu3/ipu_ic.c b/drivers/mxc/ipu3/ipu_ic.c index 437df0fa409e..2df9894dbd4e 100644 --- a/drivers/mxc/ipu3/ipu_ic.c +++ b/drivers/mxc/ipu3/ipu_ic.c @@ -42,6 +42,34 @@ static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize, uint32_t *resizeCoeff, uint32_t *downsizeCoeff); +void _ipu_vdi_set_top_field_man(bool top_field_0) +{ + uint32_t reg; + + reg = __raw_readl(VDI_C); + if (top_field_0) + reg &= ~VDI_C_TOP_FIELD_MAN_1; + else + reg |= VDI_C_TOP_FIELD_MAN_1; + __raw_writel(reg, VDI_C); +} + +void _ipu_vdi_set_motion(ipu_motion_sel motion_sel) +{ + uint32_t reg; + + reg = __raw_readl(VDI_C); + reg &= ~(VDI_C_MOT_SEL_FULL | VDI_C_MOT_SEL_MED | VDI_C_MOT_SEL_LOW); + if (motion_sel == HIGH_MOTION) + reg |= VDI_C_MOT_SEL_FULL; + else if (motion_sel == MED_MOTION) + reg |= VDI_C_MOT_SEL_MED; + else + reg |= VDI_C_MOT_SEL_LOW; + + __raw_writel(reg, VDI_C); +} + void ic_dump_register(void) { printk(KERN_DEBUG "IC_CONF = \t0x%08X\n", __raw_readl(IC_CONF)); @@ -125,7 +153,7 @@ void _ipu_ic_disable_task(ipu_channel_t channel) __raw_writel(ic_conf, IC_CONF); } -void _ipu_vdi_init(ipu_channel_params_t *params) +void _ipu_vdi_init(ipu_channel_t channel, ipu_channel_params_t *params) { uint32_t reg; uint32_t pixel_fmt; @@ -140,17 +168,47 @@ void _ipu_vdi_init(ipu_channel_params_t *params) (params->mem_prp_vf_mem.in_pixel_fmt == V4L2_PIX_FMT_YUV422P) ? VDI_C_CH_422 : VDI_C_CH_420; - reg = pixel_fmt | VDI_C_MOT_SEL_FULL | VDI_C_BURST_SIZE2_4; + reg = __raw_readl(VDI_C); + reg |= pixel_fmt; + switch (channel) { + case MEM_VDI_PRP_VF_MEM: + reg |= VDI_C_BURST_SIZE2_4; + break; + case MEM_VDI_PRP_VF_MEM_P: + reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_SET_1 | VDI_C_VWM1_CLR_2; + break; + case MEM_VDI_PRP_VF_MEM_N: + reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_SET_1 | VDI_C_VWM3_CLR_2; + break; + default: + break; + } __raw_writel(reg, VDI_C); + bool top_field_0; + /* MED_MOTION and LOW_MOTION algorithm that are using 3 fields + * should start presenting using the 2nd field. + */ + if (((params->mem_prp_vf_mem.field_fmt == V4L2_FIELD_INTERLACED_TB) && + (params->mem_prp_vf_mem.motion_sel != HIGH_MOTION)) || + ((params->mem_prp_vf_mem.field_fmt == V4L2_FIELD_INTERLACED_BT) && + (params->mem_prp_vf_mem.motion_sel == HIGH_MOTION))) + top_field_0 = false; + else + top_field_0 = true; + + /* Buffer selection toggle the value therefore init val is inverted. */ + _ipu_vdi_set_top_field_man(!top_field_0); + + _ipu_vdi_set_motion(params->mem_prp_vf_mem.motion_sel); + reg = __raw_readl(IC_CONF); reg &= ~IC_CONF_RWS_EN; __raw_writel(reg, IC_CONF); } -_ipu_vdi_uninit(void) +void _ipu_vdi_uninit(void) { - uint32_t reg; __raw_writel(0, VDI_FSIZE); __raw_writel(0, VDI_C); } @@ -746,3 +804,19 @@ static bool _calc_resize_coeffs(uint32_t inSize, uint32_t outSize, return true; } + +void _ipu_vdi_toggle_top_field_man() +{ + uint32_t reg; + uint32_t mask_reg; + + reg = __raw_readl(VDI_C); + mask_reg = reg & VDI_C_TOP_FIELD_MAN_1; + if (mask_reg == VDI_C_TOP_FIELD_MAN_1) + reg &= ~VDI_C_TOP_FIELD_MAN_1; + else + reg |= VDI_C_TOP_FIELD_MAN_1; + + __raw_writel(reg, VDI_C); +} + diff --git a/drivers/mxc/ipu3/ipu_prv.h b/drivers/mxc/ipu3/ipu_prv.h index 7fdd27230c68..c1a7ccadfc2d 100644 --- a/drivers/mxc/ipu3/ipu_prv.h +++ b/drivers/mxc/ipu3/ipu_prv.h @@ -61,7 +61,7 @@ int _ipu_chan_is_interlaced(ipu_channel_t channel); void _ipu_ic_enable_task(ipu_channel_t channel); void _ipu_ic_disable_task(ipu_channel_t channel); void _ipu_ic_init_prpvf(ipu_channel_params_t *params, bool src_is_csi); -void _ipu_vdi_init(ipu_channel_params_t *params); +void _ipu_vdi_init(ipu_channel_t channel, ipu_channel_params_t *params); void _ipu_vdi_uninit(void); void _ipu_ic_uninit_prpvf(void); void _ipu_ic_init_rotate_vf(ipu_channel_params_t *params); @@ -78,6 +78,7 @@ void _ipu_ic_init_rotate_pp(ipu_channel_params_t *params); void _ipu_ic_uninit_rotate_pp(void); int _ipu_ic_idma_init(int dma_chan, uint16_t width, uint16_t height, int burst_size, ipu_rotate_mode_t rot); +void _ipu_vdi_toggle_top_field_man(); int _ipu_csi_init(ipu_channel_t channel, uint32_t csi); void ipu_csi_set_test_generator(bool active, uint32_t r_value, uint32_t g_value, uint32_t b_value, diff --git a/drivers/mxc/ipu3/ipu_regs.h b/drivers/mxc/ipu3/ipu_regs.h index 7769c10e737b..9ddcd351d6b8 100644 --- a/drivers/mxc/ipu3/ipu_regs.h +++ b/drivers/mxc/ipu3/ipu_regs.h @@ -608,15 +608,17 @@ enum { VDI_C_CH_420 = 0x00000000, VDI_C_CH_422 = 0x00000002, VDI_C_MOT_SEL_FULL = 0x00000008, - VDI_C_MOT_SEL_HIGH = 0x00000004, + VDI_C_MOT_SEL_LOW = 0x00000004, VDI_C_MOT_SEL_MED = 0x00000000, VDI_C_BURST_SIZE1_4 = 0x00000030, VDI_C_BURST_SIZE2_4 = 0x00000300, VDI_C_BURST_SIZE3_4 = 0x00003000, VDI_C_VWM1_SET_1 = 0x00000000, - VDI_C_VWM1_CLR_2 = 0x00010000, + VDI_C_VWM1_CLR_2 = 0x00080000, VDI_C_VWM3_SET_1 = 0x00000000, VDI_C_VWM3_CLR_2 = 0x02000000, + VDI_C_TOP_FIELD_MAN_1 = 0x40000000, + VDI_C_TOP_FIELD_AUTO_1 = 0x80000000, }; enum di_pins { diff --git a/include/linux/ipu.h b/include/linux/ipu.h index 78195375d994..b1d27fb64af2 100644 --- a/include/linux/ipu.h +++ b/include/linux/ipu.h @@ -26,6 +26,7 @@ #define __ASM_ARCH_IPU_H__ #include <linux/types.h> +#include <linux/videodev2.h> #ifdef __KERNEL__ #include <linux/interrupt.h> #else @@ -72,6 +73,15 @@ typedef enum { IPU_PANEL_TFT, } ipu_panel_t; +/*! + * Enumeration of VDI MOTION select + */ +typedef enum { + MED_MOTION = 0, + LOW_MOTION = 1, + HIGH_MOTION = 2, +} ipu_motion_sel; + /* IPU Pixel format definitions */ /* Four-character-code (FOURCC) */ #define fourcc(a, b, c, d)\ @@ -193,6 +203,9 @@ typedef enum { MEM_DC_SYNC = CHAN_NONE, DIRECT_ASYNC0 = CHAN_NONE, DIRECT_ASYNC1 = CHAN_NONE, + MEM_VDI_PRP_VF_MEM_P = CHAN_NONE, + MEM_VDI_PRP_VF_MEM = CHAN_NONE, + MEM_VDI_PRP_VF_MEM_N = CHAN_NONE, #else MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48), MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49), @@ -357,6 +370,8 @@ typedef union { uint8_t alpha; uint32_t key_color; bool alpha_chan_en; + ipu_motion_sel motion_sel; + enum v4l2_field field_fmt; } mem_prp_vf_mem; struct { uint32_t temp; @@ -812,6 +827,7 @@ enum { STOP, }; + /*Define template constants*/ #define ATM_ADDR_RANGE 0x20 /*offset address of DISP */ #define TEMPLATE_BUF_SIZE 0x20 /*size of template */ @@ -860,6 +876,7 @@ int32_t ipu_update_channel_buffer(ipu_channel_t channel, ipu_buffer_t type, int32_t ipu_select_buffer(ipu_channel_t channel, ipu_buffer_t type, uint32_t bufNum); +int32_t ipu_select_multi_vdi_buffer(uint32_t bufNum); int32_t ipu_link_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch); int32_t ipu_unlink_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch); diff --git a/include/linux/mxc_v4l2.h b/include/linux/mxc_v4l2.h index a94074eb9610..d474aa2f4c86 100644 --- a/include/linux/mxc_v4l2.h +++ b/include/linux/mxc_v4l2.h @@ -29,6 +29,7 @@ #define V4L2_CID_MXC_ROT (V4L2_CID_PRIVATE_BASE + 0) #define V4L2_CID_MXC_FLASH (V4L2_CID_PRIVATE_BASE + 1) #define V4L2_CID_MXC_VF_ROT (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_MXC_MOTION (V4L2_CID_PRIVATE_BASE + 3) #define V4L2_MXC_ROTATE_NONE 0 #define V4L2_MXC_ROTATE_VERT_FLIP 1 |