The ITU-R BT.656 standard data format provides interlaced video data.

This patch adds to the ISP CCDC driver the ability to deinterlace the
video data and send progressive frames to user-space applications.

The changes are:
    - Maintain two buffers (struct isp_buffer), current and last.
    - Decouple next buffer obtaining from last buffer releasing.
    - Move most of the logic to the VD1 interrupt handler since the
      ISP is not busy there.

Signed-off-by: Javier Martinez Canillas <martinez.jav...@gmail.com>
---
 drivers/media/video/omap3isp/ispccdc.c |  195 ++++++++++++++++++++++----------
 drivers/media/video/omap3isp/ispccdc.h |    6 +
 include/media/omap3isp.h               |    3 +
 3 files changed, 146 insertions(+), 58 deletions(-)

diff --git a/drivers/media/video/omap3isp/ispccdc.c 
b/drivers/media/video/omap3isp/ispccdc.c
index c25db54..fff1ae1 100644
--- a/drivers/media/video/omap3isp/ispccdc.c
+++ b/drivers/media/video/omap3isp/ispccdc.c
@@ -40,6 +40,7 @@
 static struct v4l2_mbus_framefmt *
 __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
                  unsigned int pad, enum v4l2_subdev_format_whence which);
+static bool ccdc_input_is_bt656(struct isp_ccdc_device *ccdc);
 
 static const unsigned int ccdc_fmts[] = {
        V4L2_MBUS_FMT_Y8_1X8,
@@ -893,7 +894,7 @@ static void ccdc_config_outlineoffset(struct 
isp_ccdc_device *ccdc,
                    ISPCCDC_SDOFST_FINV);
 
        isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
-                   ISPCCDC_SDOFST_FOFST_4L);
+                   ISPCCDC_SDOFST_FOFST_1L);
 
        switch (oddeven) {
        case EVENEVEN:
@@ -1010,6 +1011,9 @@ static void ccdc_config_sync_if(struct isp_ccdc_device 
*ccdc,
        if (pdata && pdata->vs_pol)
                syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
 
+       if (pdata && pdata->fldmode)
+               syn_mode |= ISPCCDC_SYN_MODE_FLDMODE;
+
        isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
 
        if (format->code == V4L2_MBUS_FMT_UYVY8_2X8)
@@ -1115,6 +1119,10 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
        unsigned int shift;
        u32 syn_mode;
        u32 ccdc_pattern;
+       u32 nph;
+       u32 nlv;
+       u32 vd0;
+       u32 vd1;
 
        pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
        sensor = media_entity_to_v4l2_subdev(pad->entity);
@@ -1185,26 +1193,44 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
        }
        ccdc_config_imgattr(ccdc, ccdc_pattern);
 
+       if (pdata->bt656) {
+               vd0 = nlv = format->height / 2 - 1;
+               vd1 = format->height / 3 - 1;
+               nph = format->width * 2 - 1;
+       } else {
+               vd0 = nlv = format->height - 2;
+               vd1 = format->height * 2 / 3;
+               nph = format->width - 1;
+       }
+
        /* Generate VD0 on the last line of the image and VD1 on the
         * 2/3 height line.
         */
-       isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) |
-                      ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT),
+       isp_reg_writel(isp, (vd0 << ISPCCDC_VDINT_0_SHIFT) |
+                      (vd1 << ISPCCDC_VDINT_1_SHIFT),
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
 
        /* CCDC_PAD_SOURCE_OF */
        format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
 
        isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
-                      ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
+                      (nph << ISPCCDC_HORZ_INFO_NPH_SHIFT),
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO);
+       isp_reg_writel(isp, nlv << ISPCCDC_VERT_LINES_NLV_SHIFT,
+                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
        isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT,
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
-       isp_reg_writel(isp, (format->height - 1)
-                       << ISPCCDC_VERT_LINES_NLV_SHIFT,
-                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
+       isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV1_SHIFT,
+                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
 
-       ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0);
+
+       if (pdata->bt656) {
+               ccdc_config_outlineoffset(ccdc, nph, EVENEVEN, 1);
+               ccdc_config_outlineoffset(ccdc, nph, EVENODD, 1);
+               ccdc_config_outlineoffset(ccdc, nph, ODDEVEN, 1);
+               ccdc_config_outlineoffset(ccdc, nph, ODDODD, 1);
+       } else
+               ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 
0);
 
        /* CCDC_PAD_SOURCE_VP */
        format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
@@ -1212,13 +1238,12 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
        isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) |
                       (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT),
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ);
-       isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
-                      ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
-                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
-
        isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) |
                       (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
                       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
+       isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
+                      ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
+                      OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
 
        /* Use PACK8 mode for 1byte per pixel formats. */
        if (omap3isp_video_format_info(format->code)->width <= 8)
@@ -1464,6 +1489,19 @@ done:
        spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
 }
 
+static void ccdc_release_buffer(struct isp_buffer *buffer, int error)
+{
+       buffer->buffer.state = error ? ISP_BUF_STATE_ERROR : ISP_BUF_STATE_DONE;
+       wake_up(&buffer->buffer.wait);
+}
+
+static void ccdc_release_last_buffer(struct isp_ccdc_device *ccdc)
+{
+       struct isp_buffer *buffer = ccdc->last_buffer;
+       ccdc->last_buffer = NULL;
+       ccdc_release_buffer(buffer, ccdc->error);
+}
+
 static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
 {
        struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
@@ -1495,11 +1533,19 @@ static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
                goto done;
        }
 
-       buffer = omap3isp_video_buffer_next(&ccdc->video_out, ccdc->error);
-       if (buffer != NULL) {
-               ccdc_set_outaddr(ccdc, buffer->isp_addr);
+       if (!ccdc_input_is_bt656(ccdc)) {
+               ccdc->last_buffer = ccdc->current_buffer;
+               buffer = omap3isp_video_buffer_next(&ccdc->video_out,
+                                                   ccdc->error);
+               if (buffer != NULL) {
+                       ccdc_set_outaddr(ccdc, buffer->isp_addr);
+                       restart = 1;
+               }
+               ccdc->current_buffer = buffer;
+               if (ccdc->last_buffer)
+                       ccdc_release_last_buffer(ccdc);
+       } else
                restart = 1;
-       }
 
        pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
 
@@ -1541,6 +1587,16 @@ static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc)
                ccdc_enable(ccdc);
 }
 
+static inline struct isp_buffer *ccdc_getbuffer(struct isp_ccdc_device *ccdc)
+{
+       return omap3isp_video_buffer_next(&ccdc->video_out, ccdc->error);
+}
+
+static inline void ccdc_set_next_buffer(struct isp_ccdc_device *ccdc)
+{
+       ccdc_set_outaddr(ccdc, ccdc->current_buffer->isp_addr);
+}
+
 /*
  * ccdc_vd1_isr - Handle VD1 event
  * @ccdc: Pointer to ISP CCDC device.
@@ -1549,57 +1605,77 @@ static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+       if (ccdc_input_is_bt656(ccdc)) {
+               if (ccdc->interlaced_cnt) {
+                       ccdc->interlaced_cnt = 0;
+                       ccdc->last_buffer = ccdc->current_buffer;
+                       if (!list_empty(&ccdc->video_out.dmaqueue)) {
+                               ccdc->current_buffer =
+                                       ccdc_getbuffer(ccdc);
+                               if (ccdc->current_buffer != NULL)
+                                       ccdc_set_next_buffer(ccdc);
+                       } else
+                               ccdc->current_buffer = NULL;
+               } else {
+                       ccdc->interlaced_cnt = 1;
+                       if (ccdc->last_buffer)
+                               ccdc_release_last_buffer(ccdc);
+               }
+       } else {
 
-       /*
-        * Depending on the CCDC pipeline state, CCDC stopping should be
-        * handled differently. In SINGLESHOT we emulate an internal CCDC
-        * stopping because the CCDC hw works only in continuous mode.
-        * When CONTINUOUS pipeline state is used and the CCDC writes it's
-        * data to memory the CCDC and LSC are stopped immediately but
-        * without change the CCDC stopping state machine. The CCDC
-        * stopping state machine should be used only when user request
-        * for stopping is received (SINGLESHOT is an exeption).
-        */
-       switch (ccdc->state) {
-       case ISP_PIPELINE_STREAM_SINGLESHOT:
-               ccdc->stopping = CCDC_STOP_REQUEST;
-               break;
+               spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
 
-       case ISP_PIPELINE_STREAM_CONTINUOUS:
-               if (ccdc->output & CCDC_OUTPUT_MEMORY) {
-                       if (ccdc->lsc.state != LSC_STATE_STOPPED)
-                               __ccdc_lsc_enable(ccdc, 0);
-                       __ccdc_enable(ccdc, 0);
-               }
-               break;
+               /*
+                * Depending on the CCDC pipeline state, CCDC stopping should be
+                * handled differently. In SINGLESHOT we emulate an internal 
CCDC
+                * stopping because the CCDC hw works only in continuous mode.
+                * When CONTINUOUS pipeline state is used and the CCDC writes 
it's
+                * data to memory the CCDC and LSC are stopped immediately but
+                * without change the CCDC stopping state machine. The CCDC
+                * stopping state machine should be used only when user request
+                * for stopping is received (SINGLESHOT is an exeption).
+                */
+               switch (ccdc->state) {
+               case ISP_PIPELINE_STREAM_SINGLESHOT:
+                       ccdc->stopping = CCDC_STOP_REQUEST;
+                       break;
 
-       case ISP_PIPELINE_STREAM_STOPPED:
-               break;
-       }
+               case ISP_PIPELINE_STREAM_CONTINUOUS:
+                       if (ccdc->output & CCDC_OUTPUT_MEMORY) {
+                               if (ccdc->lsc.state != LSC_STATE_STOPPED)
+                                       __ccdc_lsc_enable(ccdc, 0);
+                               __ccdc_enable(ccdc, 0);
+                       }
+                       break;
 
-       if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
-               goto done;
+               case ISP_PIPELINE_STREAM_STOPPED:
+                       break;
+               }
 
-       if (ccdc->lsc.request == NULL)
-               goto done;
+               if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
+                       goto done;
 
-       /*
-        * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ
-        * do the appropriate changes in registers
-        */
-       if (ccdc->lsc.state == LSC_STATE_RUNNING) {
-               __ccdc_lsc_enable(ccdc, 0);
-               ccdc->lsc.state = LSC_STATE_RECONFIG;
-               goto done;
-       }
+               if (ccdc->lsc.request == NULL)
+                       goto done;
 
-       /* LSC has been in STOPPED state, enable it */
-       if (ccdc->lsc.state == LSC_STATE_STOPPED)
-               ccdc_lsc_enable(ccdc);
+               /*
+                * LSC need to be reconfigured. Stop it here and on next
+                * LSC_DONE IRQ do the appropriate changes in registers
+                */
+               if (ccdc->lsc.state == LSC_STATE_RUNNING) {
+                       __ccdc_lsc_enable(ccdc, 0);
+                       ccdc->lsc.state = LSC_STATE_RECONFIG;
+                       goto done;
+               }
+
+               /* LSC has been in STOPPED state, enable it */
+               if (ccdc->lsc.state == LSC_STATE_STOPPED)
+                       ccdc_lsc_enable(ccdc);
 
 done:
-       spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+               spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+
+       }
 }
 
 /*
@@ -1638,6 +1714,7 @@ static int ccdc_video_queue(struct isp_video *video, 
struct isp_buffer *buffer)
                return -ENODEV;
 
        ccdc_set_outaddr(ccdc, buffer->isp_addr);
+       ccdc->current_buffer = buffer;
 
        /* We now have a buffer queued on the output, restart the pipeline
         * on the next CCDC interrupt if running in continuous mode (or when
@@ -1764,6 +1841,8 @@ static int ccdc_set_stream(struct v4l2_subdev *sd, int 
enable)
                        omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
                omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC);
                ccdc->underrun = 0;
+               ccdc->current_buffer = NULL;
+               ccdc->interlaced_cnt = 0;
                break;
        }
 
diff --git a/drivers/media/video/omap3isp/ispccdc.h 
b/drivers/media/video/omap3isp/ispccdc.h
index 54811ce..dad021c 100644
--- a/drivers/media/video/omap3isp/ispccdc.h
+++ b/drivers/media/video/omap3isp/ispccdc.h
@@ -134,6 +134,9 @@ struct ispccdc_lsc {
  * @wait: Wait queue used to stop the module
  * @stopping: Stopping state
  * @ioctl_lock: Serializes ioctl calls and LSC requests freeing
+ * @current_buffer: Buffer for current frame
+ * @last_buffer: Buffer used for the last frame
+ * @interlaced_cnt: Sub-frame count for an interlaced video frame
  */
 struct isp_ccdc_device {
        struct v4l2_subdev subdev;
@@ -164,6 +167,9 @@ struct isp_ccdc_device {
        wait_queue_head_t wait;
        unsigned int stopping;
        struct mutex ioctl_lock;
+       struct isp_buffer *current_buffer;
+       struct isp_buffer *last_buffer;
+       unsigned int interlaced_cnt;
 };
 
 struct isp_device;
diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h
index 3b2d2b7..0f215de 100644
--- a/include/media/omap3isp.h
+++ b/include/media/omap3isp.h
@@ -61,6 +61,8 @@ enum {
  *             0 - Normal, 1 - One's complement
  * @bt656: ITU-R BT656 embedded synchronization
  *             0 - HS/VS sync, 1 - BT656 sync
+ * @fldmode: Field mode
+ *             0 - progressive, 1 - Interlaced
  */
 struct isp_parallel_platform_data {
        unsigned int data_lane_shift:2;
@@ -69,6 +71,7 @@ struct isp_parallel_platform_data {
        unsigned int vs_pol:1;
        unsigned int data_pol:1;
        unsigned int bt656:1;
+       unsigned int fldmode:1;
 };
 
 enum {
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to