]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/kernel-video.git/blobdiff - drivers/media/platform/ti-vpe/vip.c
media: ti-vpe: vip: fix try_format for interlaced frames usecase
[android-sdk/kernel-video.git] / drivers / media / platform / ti-vpe / vip.c
index ede2aab2a54f0030f978037f3aa0e98151d81368..ab0b961dd1dcaec00ab06e6a76e10dac9949df43 100644 (file)
@@ -127,9 +127,9 @@ static struct vip_srce_info srce_info[5] = {
 
 static struct vip_fmt vip_formats[] = {
        {
-               .name           = "YUV 444 co-planar",
+               .name           = "NV24 YUV 444 co-planar",
                .fourcc         = V4L2_PIX_FMT_NV24,
-               .code           = V4L2_MBUS_FMT_YDYUYDYV8_1X16,
+               .code           = V4L2_MBUS_FMT_UYVY8_2X8,
                .colorspace     = V4L2_COLORSPACE_SMPTE170M,
                .coplanar       = 1,
                .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y444],
@@ -137,9 +137,9 @@ static struct vip_fmt vip_formats[] = {
                                  },
        },
        {
-               .name           = "YUV 422 co-planar",
+               .name           = "NV16 YUV 422 co-planar",
                .fourcc         = V4L2_PIX_FMT_NV16,
-               .code           = V4L2_MBUS_FMT_YDYUYDYV8_1X16,
+               .code           = V4L2_MBUS_FMT_UYVY8_2X8,
                .colorspace     = V4L2_COLORSPACE_SMPTE170M,
                .coplanar       = 1,
                .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y422],
@@ -147,9 +147,9 @@ static struct vip_fmt vip_formats[] = {
                                  },
        },
        {
-               .name           = "YUV 420 co-planar",
+               .name           = "NV12 YUV 420 co-planar",
                .fourcc         = V4L2_PIX_FMT_NV12,
-               .code           = V4L2_MBUS_FMT_YDYUYDYV8_1X16,
+               .code           = V4L2_MBUS_FMT_UYVY8_2X8,
                .colorspace     = V4L2_COLORSPACE_SMPTE170M,
                .coplanar       = 1,
                .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_Y420],
@@ -162,25 +162,34 @@ static struct vip_fmt vip_formats[] = {
                .code           = V4L2_MBUS_FMT_UYVY8_2X8,
                .colorspace     = V4L2_COLORSPACE_SMPTE170M,
                .coplanar       = 0,
-               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CY422],
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422],
                                  },
        },
        {
                .name           = "YUYV 422 packed",
                .fourcc         = V4L2_PIX_FMT_YUYV,
-               .code           = V4L2_MBUS_FMT_YUYV8_2X8,
+               .code           = V4L2_MBUS_FMT_UYVY8_2X8,
                .colorspace     = V4L2_COLORSPACE_SMPTE170M,
                .coplanar       = 0,
-               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YC422],
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422],
                                  },
        },
        {
                .name           = "VYUY 422 packed",
                .fourcc         = V4L2_PIX_FMT_VYUY,
-               .code           = V4L2_MBUS_FMT_VYUY8_2X8,
+               .code           = V4L2_MBUS_FMT_UYVY8_2X8,
                .colorspace     = V4L2_COLORSPACE_SMPTE170M,
                .coplanar       = 0,
-               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YC422],
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CRY422],
+                                 },
+       },
+       {
+               .name           = "YVYU 422 packed",
+               .fourcc         = V4L2_PIX_FMT_YVYU,
+               .code           = V4L2_MBUS_FMT_UYVY8_2X8,
+               .colorspace     = V4L2_COLORSPACE_SMPTE170M,
+               .coplanar       = 0,
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCR422],
                                  },
        },
        {
@@ -219,39 +228,11 @@ static char *fourcc_to_str(u32 fmt)
 /*
  * Find our format description corresponding to the passed v4l2_format
  */
-#ifdef DISABLED_FOR_NOW
-static struct vip_fmt *find_format_by_pix(u32 pixelformat)
-{
-       struct vip_fmt *fmt;
-       unsigned int k;
-
-       for (k = 0; k < ARRAY_SIZE(vip_formats); k++) {
-               fmt = &vip_formats[k];
-               if (fmt->fourcc == pixelformat)
-                       return fmt;
-       }
-
-       return NULL;
-}
-#endif
-
-static struct vip_fmt *find_format_by_code(u32 code)
-{
-       struct vip_fmt *fmt;
-       unsigned int k;
-
-       for (k = 0; k < ARRAY_SIZE(vip_formats); k++) {
-               fmt = &vip_formats[k];
-               if (fmt->code == code)
-                       return fmt;
-       }
-
-       return NULL;
-}
 
-static struct vip_fmt *find_active_format_by_pix(struct vip_dev *dev,
-                                                u32 pixelformat)
+static struct vip_fmt *find_port_format_by_pix(struct vip_port *port,
+                                              u32 pixelformat)
 {
+       struct vip_dev *dev = port->dev;
        struct vip_fmt *fmt;
        unsigned int k;
 
@@ -264,21 +245,6 @@ static struct vip_fmt *find_active_format_by_pix(struct vip_dev *dev,
        return NULL;
 }
 
-static struct vip_fmt *find_active_format_by_code(struct vip_dev *dev,
-                                                u32 code)
-{
-       struct vip_fmt *fmt;
-       unsigned int k;
-
-       for (k = 0; k < dev->num_active_fmt; k++) {
-               fmt = dev->active_fmt[k];
-               if (fmt->code == code)
-                       return fmt;
-       }
-
-       return NULL;
-}
-
 static LIST_HEAD(vip_shared_list);
 
 static inline struct vip_dev *notifier_to_vip_dev(struct v4l2_async_notifier *n)
@@ -304,6 +270,7 @@ static inline struct vip_dev *notifier_to_vip_dev(struct v4l2_async_notifier *n)
 static int alloc_port(struct vip_dev *, int);
 static void free_port(struct vip_port *);
 static int vip_setup_parser(struct vip_port *port);
+static void stop_dma(struct vip_stream *stream);
 
 static inline u32 read_sreg(struct vip_shared *shared, int offset)
 {
@@ -524,7 +491,7 @@ static void vip_set_pclk_polarity(struct vip_port *port, int polarity)
 {
        u32 val, ret, offset;
 
-       if (polarity == 0 && port->dev->syscon) {
+       if (polarity == 1 && port->dev->syscon) {
 
                /*
                 * When the VIP parser is configured to so that the pixel clock
@@ -1024,9 +991,9 @@ static int add_out_dtd(struct vip_stream *stream, int srce_type)
         * This will make sure that an abort descriptor for this channel
         * would be submitted to VPDMA causing any ongoing  transaction to be
         * aborted and cleanup the VPDMA FSM for this channel */
-       dev->vpdma_channels[channel] = 1;
+       stream->vpdma_channels[channel] = 1;
 
-       vpdma_rawchan_add_out_dtd(&dev->desc_list, c_rect->width, c_rect,
+       vpdma_rawchan_add_out_dtd(&stream->desc_list, c_rect->width, c_rect,
                fmt->vpdma_fmt[plane], dma_addr, max_width, max_height,
                channel, flags);
 
@@ -1056,11 +1023,10 @@ static void add_stream_dtds(struct vip_stream *stream)
                add_out_dtd(stream, VIP_SRCE_CHROMA);
 }
 
-static void enable_irqs(struct vip_dev *dev, int irq_num)
+static void enable_irqs(struct vip_dev *dev, int irq_num, int list_num)
 {
        u32 reg_addr = VIP_INT0_ENABLE0_SET +
                        VIP_INTC_INTX_OFFSET * irq_num;
-       int list_num = dev->slice_id;
 
        write_sreg(dev->shared, reg_addr, 1 << (list_num * 2));
 
@@ -1068,24 +1034,23 @@ static void enable_irqs(struct vip_dev *dev, int irq_num)
                irq_num, list_num, true);
 }
 
-static void disable_irqs(struct vip_dev *dev, int irq_num)
+static void disable_irqs(struct vip_dev *dev, int irq_num, int list_num)
 {
        u32 reg_addr = VIP_INT0_ENABLE0_CLR +
                        VIP_INTC_INTX_OFFSET * irq_num;
-       int list_num = dev->slice_id;
 
-       write_sreg(dev->shared, reg_addr, 0xffffffff);
+       write_sreg(dev->shared, reg_addr, 1 << (list_num * 2));
 
        vpdma_enable_list_complete_irq(dev->shared->vpdma,
                irq_num, list_num, false);
 }
 
-static void clear_irqs(struct vip_dev *dev, int irq_num)
+static void clear_irqs(struct vip_dev *dev, int irq_num, int list_num)
 {
        u32 reg_addr = VIP_INT0_STATUS0_CLR +
                        VIP_INTC_INTX_OFFSET * irq_num;
 
-       write_sreg(dev->shared, reg_addr, 0xffffffff);
+       write_sreg(dev->shared, reg_addr, 1 << (list_num * 2));
 
        vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id);
 }
@@ -1096,11 +1061,11 @@ static void populate_desc_list(struct vip_stream *stream)
        struct vip_dev *dev = port->dev;
        unsigned int list_length;
 
-       dev->desc_next = dev->desc_list.buf.addr;
+       stream->desc_next = stream->desc_list.buf.addr;
        add_stream_dtds(stream);
 
-       list_length = dev->desc_next - dev->desc_list.buf.addr;
-       vpdma_map_desc_buf(dev->shared->vpdma, &dev->desc_list.buf);
+       list_length = stream->desc_next - stream->desc_list.buf.addr;
+       vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
 }
 
 /*
@@ -1108,13 +1073,15 @@ static void populate_desc_list(struct vip_stream *stream)
  * Should be called after a new vb is queued and on a vpdma list
  * completion interrupt.
  */
-static void start_dma(struct vip_dev *dev, struct vip_buffer *buf)
+static void start_dma(struct vip_stream *stream, struct vip_buffer *buf)
 {
+       struct vip_dev *dev = stream->port->dev;
        struct vpdma_data *vpdma = dev->shared->vpdma;
+       int list_num = stream->list_num;
        dma_addr_t dma_addr;
        int drop_data;
 
-       if (vpdma_list_busy(vpdma, dev->slice_id)) {
+       if (vpdma_list_busy(vpdma, list_num)) {
                vip_err(dev, "vpdma list busy, cannot post");
                return;                         /* nothing to do */
        }
@@ -1130,9 +1097,17 @@ static void start_dma(struct vip_dev *dev, struct vip_buffer *buf)
                vip_dbg(4, dev, "start_dma: dropped\n");
        }
 
-       vpdma_update_dma_addr(dev->shared->vpdma, &dev->desc_list,
-                             dma_addr, dev->write_desc, drop_data);
-       vpdma_submit_descs(dev->shared->vpdma, &dev->desc_list, dev->slice_id);
+       vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list,
+                             dma_addr, stream->write_desc, drop_data, 0);
+
+       if (stream->port->fmt->coplanar) {
+               dma_addr += stream->width * stream->height;
+               vpdma_update_dma_addr(dev->shared->vpdma, &stream->desc_list,
+                             dma_addr, stream->write_desc + 1, drop_data, 1);
+       }
+
+       vpdma_submit_descs(dev->shared->vpdma,
+                       &stream->desc_list, stream->list_num);
 }
 
 static void vip_schedule_next_buffer(struct vip_stream *stream)
@@ -1143,7 +1118,7 @@ static void vip_schedule_next_buffer(struct vip_stream *stream)
 
        spin_lock_irqsave(&dev->slock, flags);
        if (list_empty(&stream->vidq)) {
-               vip_dbg(3, dev, "Dropping frame\n");
+               vip_dbg(4, dev, "Dropping frame\n");
                if (list_empty(&stream->dropq)) {
                        vip_err(dev, "No dropq buffer left!");
                        spin_unlock_irqrestore(&dev->slock, flags);
@@ -1153,13 +1128,13 @@ static void vip_schedule_next_buffer(struct vip_stream *stream)
                                 struct vip_buffer, list);
 
                buf->drop = true;
-               list_move_tail(&buf->list, &dev->vip_bufs);
+               list_move_tail(&buf->list, &stream->post_bufs);
                buf = NULL;
        } else if (vb2_is_streaming(&stream->vb_vidq)) {
                buf = list_entry(stream->vidq.next,
                                struct vip_buffer, list);
                buf->drop = false;
-               list_move_tail(&buf->list, &dev->vip_bufs);
+               list_move_tail(&buf->list, &stream->post_bufs);
                vip_dbg(4, dev, "added next buffer\n");
        } else {
                vip_err(dev, "IRQ occurred when not streaming\n");
@@ -1171,12 +1146,12 @@ static void vip_schedule_next_buffer(struct vip_stream *stream)
                buf = list_entry(stream->dropq.next,
                                 struct vip_buffer, list);
                buf->drop = true;
-               list_move_tail(&buf->list, &dev->vip_bufs);
+               list_move_tail(&buf->list, &stream->post_bufs);
                buf = NULL;
        }
 
        spin_unlock_irqrestore(&dev->slock, flags);
-       start_dma(dev, buf);
+       start_dma(stream, buf);
 }
 
 static void vip_process_buffer_complete(struct vip_stream *stream)
@@ -1186,15 +1161,16 @@ static void vip_process_buffer_complete(struct vip_stream *stream)
        struct vb2_buffer *vb = NULL;
        unsigned long flags, fld;
 
-       buf = list_first_entry(&dev->vip_bufs, struct vip_buffer, list);
+       buf = list_first_entry(&stream->post_bufs, struct vip_buffer, list);
 
        if (stream->port->flags & FLAG_INTERLACED) {
-               vpdma_unmap_desc_buf(dev->shared->vpdma, &dev->desc_list.buf);
+               vpdma_unmap_desc_buf(dev->shared->vpdma,
+                                       &stream->desc_list.buf);
 
-               fld = dtd_get_field(dev->write_desc);
+               fld = dtd_get_field(stream->write_desc);
                stream->field = fld ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
 
-               vpdma_map_desc_buf(dev->shared->vpdma, &dev->desc_list.buf);
+               vpdma_map_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
        }
 
        if (buf) {
@@ -1226,8 +1202,9 @@ static void vip_process_buffer_complete(struct vip_stream *stream)
 static irqreturn_t vip_irq(int irq_vip, void *data)
 {
        struct vip_dev *dev = (struct vip_dev *)data;
+       struct vpdma_data *vpdma = dev->shared->vpdma;
        struct vip_stream *stream;
-       int list_num = dev->slice_id;
+       int list_num;
        int irq_num = dev->slice_id;
        u32 irqst, reg_addr;
 
@@ -1241,51 +1218,42 @@ static irqreturn_t vip_irq(int irq_vip, void *data)
        vip_dbg(8, dev, "IRQ %d VIP_INT%d_STATUS0 0x%x\n",
                irq_vip, irq_num, irqst);
        if (irqst) {
-               vpdma_clear_list_stat(dev->shared->vpdma, irq_num, list_num);
-
                reg_addr = VIP_INT0_STATUS0_CLR +
                        VIP_INTC_INTX_OFFSET * irq_num;
                write_sreg(dev->shared, reg_addr, irqst);
-       }
 
-       stream = dev->ports[0]->cap_streams[0];
-       if (dev->num_skip_irq)
-               dev->num_skip_irq--;
-       else
-               vip_process_buffer_complete(stream);
+               for (list_num = 0; list_num < 8;  list_num++) {
+                       /* Check for LIST_COMPLETE IRQ */
+                       if (!(irqst & (1 << list_num * 2)))
+                               continue;
 
-       vip_schedule_next_buffer(stream);
+                       vip_dbg(8, dev, "IRQ %d: handling LIST%d_COMPLETE\n",
+                               irq_num, list_num);
 
-       return IRQ_HANDLED;
-}
+                       stream = vpdma_hwlist_get_priv(vpdma, list_num);
+                       if (!stream || stream->list_num != list_num) {
+                               vip_err(dev, "IRQ occured for unused list");
+                               continue;
+                       }
 
-/*
- * video ioctls
- */
-static struct v4l2_mbus_framefmt *
-vip_video_pix_to_mbus(const struct v4l2_pix_format *pix,
-                     struct v4l2_mbus_framefmt *mbus)
-{
-       unsigned int i;
+                       vpdma_clear_list_stat(vpdma, irq_num, list_num);
 
-       memset(mbus, 0, sizeof(*mbus));
-       mbus->width = pix->width;
-       mbus->height = pix->height;
+                       if (dev->num_skip_irq)
+                               dev->num_skip_irq--;
+                       else
+                               vip_process_buffer_complete(stream);
 
-       mbus->code = V4L2_MBUS_FMT_YUYV8_2X8;
-       for (i = 0; i < ARRAY_SIZE(vip_formats) - 1; ++i) {
-               if (vip_formats[i].fourcc == pix->pixelformat) {
-                       mbus->code = vip_formats[i].code;
-                       break;
+                       vip_schedule_next_buffer(stream);
+                       irqst &= ~((1 << list_num * 2));
                }
        }
 
-       mbus->colorspace = pix->colorspace;
-       mbus->field = pix->field;
-
-       return mbus;
+       return IRQ_HANDLED;
 }
 
+/*
+ * video ioctls
+ */
 static int vip_querycap(struct file *file, void *priv,
                        struct v4l2_capability *cap)
 {
@@ -1293,7 +1261,8 @@ static int vip_querycap(struct file *file, void *priv,
        strncpy(cap->card, VIP_MODULE_NAME, sizeof(cap->card) - 1);
        snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
                VIP_MODULE_NAME);
-       cap->device_caps  = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
+       cap->device_caps  = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
+                           V4L2_CAP_READWRITE;
        cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
        return 0;
 }
@@ -1307,6 +1276,7 @@ static int vip_enuminput(struct file *file, void *priv,
                return -EINVAL;
 
        inp->type = V4L2_INPUT_TYPE_CAMERA;
+       inp->std = stream->vfd->tvnorms;
        sprintf(inp->name, "camera %u", stream->vfd->num);
 
        return 0;
@@ -1330,7 +1300,9 @@ static int vip_querystd(struct file *file, void *fh, v4l2_std_id *std)
        struct vip_stream *stream = file2stream(file);
        struct vip_dev *dev = stream->port->dev;
 
+       *std = stream->vfd->tvnorms;
        v4l2_subdev_call(dev->sensor, video, querystd, std);
+       vip_dbg(1, dev, "querystd: 0x%lx\n", (unsigned long)*std);
        return 0;
 }
 
@@ -1339,8 +1311,10 @@ static int vip_g_std(struct file *file, void *fh, v4l2_std_id *std)
        struct vip_stream *stream = file2stream(file);
        struct vip_dev *dev = stream->port->dev;
 
-       *std = 0;
+       *std = stream->vfd->tvnorms;
        v4l2_subdev_call(dev->sensor, video, g_std_output, std);
+       vip_dbg(1, dev, "g_std: 0x%lx\n", (unsigned long)*std);
+
        return 0;
 }
 
@@ -1349,22 +1323,15 @@ static int vip_s_std(struct file *file, void *fh, v4l2_std_id std)
        struct vip_stream *stream = file2stream(file);
        struct vip_dev *dev = stream->port->dev;
 
-       v4l2_subdev_call(dev->sensor, video, s_std_output, std);
-       return 0;
-}
-
-static int vip_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a)
-{
-       return -EINVAL;
-}
+       vip_dbg(1, dev, "s_std: 0x%lx\n", (unsigned long)std);
 
-static int vip_g_ctrl(struct file *file, void *fh, struct v4l2_control *a)
-{
-       return 0;
-}
+       if (!(std & stream->vfd->tvnorms)) {
+               vip_dbg(1, dev, "s_std after check: 0x%lx\n",
+                       (unsigned long)std);
+               return -EINVAL;
+       }
 
-static int vip_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
-{
+       v4l2_subdev_call(dev->sensor, video, s_std_output, std);
        return 0;
 }
 
@@ -1373,9 +1340,10 @@ static int vip_enum_fmt_vid_cap(struct file *file, void *priv,
 {
        struct vip_stream *stream = file2stream(file);
        struct vip_dev *dev = stream->port->dev;
-       struct vip_fmt *fmt;
+       struct vip_fmt *fmt = NULL;
 
        vip_dbg(3, dev, "enum_fmt index:%d\n", f->index);
+
        if (f->index >= dev->num_active_fmt)
                return -EINVAL;
 
@@ -1400,18 +1368,14 @@ static int vip_enum_framesizes(struct file *file, void *priv,
 {
        struct vip_stream *stream = file2stream(file);
        struct vip_dev *dev = stream->port->dev;
+       struct vip_port *port = stream->port;
        struct vip_fmt *fmt;
-       int ret;
 
-       fmt = find_active_format_by_pix(dev, f->pixel_format);
+       fmt = find_port_format_by_pix(port, f->pixel_format);
        if (!fmt)
                return -EINVAL;
 
-       ret = v4l2_subdev_call(dev->sensor, video, enum_framesizes, f);
-       if (ret)
-               vip_dbg(1, dev, "enum_framesizes failed in subdev\n");
-
-       return ret;
+       return v4l2_subdev_call(dev->sensor, video, enum_framesizes, f);
 }
 
 static int vip_enum_frameintervals(struct file *file, void *priv,
@@ -1419,6 +1383,7 @@ static int vip_enum_frameintervals(struct file *file, void *priv,
 {
        struct vip_stream *stream = file2stream(file);
        struct vip_dev *dev = stream->port->dev;
+       struct vip_port *port = stream->port;
        struct v4l2_frmsizeenum fsize;
        struct vip_fmt *fmt;
        int ret;
@@ -1426,7 +1391,7 @@ static int vip_enum_frameintervals(struct file *file, void *priv,
        if (f->index)
                return -EINVAL;
 
-       fmt = find_active_format_by_pix(dev, f->pixel_format);
+       fmt = find_port_format_by_pix(port, f->pixel_format);
        if (!fmt)
                return -EINVAL;
 
@@ -1436,8 +1401,6 @@ static int vip_enum_frameintervals(struct file *file, void *priv,
                ret = v4l2_subdev_call(dev->sensor, video,
                                        enum_framesizes, &fsize);
                if (ret) {
-                       if (fsize.index == 0)
-                               vip_dbg(1, dev, "enum_frameinterval failed on the first enum_framesize\n");
                        return -EINVAL;
                }
 
@@ -1452,8 +1415,9 @@ static int vip_enum_frameintervals(struct file *file, void *priv,
                            (f->height >= fsize.stepwise.min_height) &&
                            (f->height <= fsize.stepwise.max_height))
                                break;
-               } else
+               } else {
                        return -EINVAL;
+               }
        }
 
        f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
@@ -1463,6 +1427,19 @@ static int vip_enum_frameintervals(struct file *file, void *priv,
        return 0;
 }
 
+static int vip_g_parm(struct file *file, void *priv,
+                     struct v4l2_streamparm *parm)
+{
+       if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       parm->parm.capture.capability   = V4L2_CAP_TIMEPERFRAME;
+       parm->parm.capture.timeperframe.numerator = 1;
+       parm->parm.capture.timeperframe.denominator = 30;
+       parm->parm.capture.readbuffers  = 4;
+       return 0;
+}
+
 static int vip_s_parm(struct file *file, void *priv,
                        struct v4l2_streamparm *parm)
 {
@@ -1471,65 +1448,123 @@ static int vip_s_parm(struct file *file, void *priv,
 
        parm->parm.capture.timeperframe.numerator = 1;
        parm->parm.capture.timeperframe.denominator = 30;
+       parm->parm.capture.readbuffers  = 4;
 
        return 0;
 }
 
-static int vip_try_fmt_vid_cap(struct file *file, void *priv,
-                              struct v4l2_format *f)
+static int vip_calc_format_size(struct vip_port *port,
+                               struct vip_fmt *fmt,
+                               struct v4l2_format *f)
 {
-       struct vip_stream *stream = file2stream(file);
-       struct vip_dev *dev = stream->port->dev;
-       struct vip_fmt *fmt = find_active_format_by_pix(dev,
-                                                       f->fmt.pix.pixelformat);
-       enum v4l2_field field;
-       int depth;
+       struct vip_dev *dev = port->dev;
+       enum v4l2_field *field;
 
        if (!fmt) {
-               vip_err(dev,
-                       "Fourcc format (0x%08x) invalid.\n",
-                       f->fmt.pix.pixelformat);
+               vip_dbg(2, dev,
+                       "no vip_fmt format provided!\n");
                return -EINVAL;
        }
 
-       field = f->fmt.pix.field;
-
-       if (field == V4L2_FIELD_ANY)
-               field = V4L2_FIELD_NONE;
-       else if (V4L2_FIELD_NONE != field && V4L2_FIELD_ALTERNATE != field)
+       field = &f->fmt.pix.field;
+       if (*field == V4L2_FIELD_ANY)
+               *field = V4L2_FIELD_NONE;
+       else if (V4L2_FIELD_NONE != *field && V4L2_FIELD_ALTERNATE != *field)
                return -EINVAL;
 
-       f->fmt.pix.field = field;
-
        v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, W_ALIGN,
                              &f->fmt.pix.height, MIN_H, MAX_H, H_ALIGN,
                              S_ALIGN);
 
-       if (fmt->coplanar)
-               depth = 8;
-
-       f->fmt.pix.bytesperline = round_up((f->fmt.pix.width *
-                                           fmt->vpdma_fmt[0]->depth) >> 3,
-                                          1 << L_ALIGN);
+       f->fmt.pix.bytesperline = f->fmt.pix.width *
+                                 (fmt->vpdma_fmt[0]->depth >> 3);
+       f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline,
+                                       VPDMA_STRIDE_ALIGN);
        f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width *
                (fmt->vpdma_fmt[0]->depth +
                 (fmt->coplanar ? fmt->vpdma_fmt[1]->depth : 0)) >> 3;
        f->fmt.pix.colorspace = fmt->colorspace;
        f->fmt.pix.priv = 0;
 
+       vip_dbg(3, dev, "calc_format_size: fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+               fourcc_to_str(f->fmt.pix.pixelformat),
+               f->fmt.pix.width, f->fmt.pix.height,
+               f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
        return 0;
 }
 
+static int vip_try_fmt_vid_cap(struct file *file, void *priv,
+                              struct v4l2_format *f)
+{
+       struct vip_stream *stream = file2stream(file);
+       struct vip_port *port = stream->port;
+       struct vip_dev *dev = port->dev;
+       struct v4l2_frmsizeenum fsize;
+       struct vip_fmt *fmt;
+       int ret, found;
+
+       vip_dbg(3, dev, "try_fmt fourcc:%s size: %dx%d\n",
+               fourcc_to_str(f->fmt.pix.pixelformat),
+               f->fmt.pix.width, f->fmt.pix.height);
+
+       fmt = find_port_format_by_pix(port, f->fmt.pix.pixelformat);
+       if (!fmt) {
+               vip_dbg(2, dev,
+                       "Fourcc format (0x%08x) not found.\n",
+                       f->fmt.pix.pixelformat);
+
+               /* Just get the first one enumerated */
+               fmt = dev->active_fmt[0];
+               f->fmt.pix.pixelformat = fmt->fourcc;
+       }
+
+       /* check for/find a valid width/height */
+       ret = 0;
+       found = false;
+       for (fsize.index = 0; ; fsize.index++) {
+               ret = v4l2_subdev_call(dev->sensor, video,
+                                       enum_framesizes, &fsize);
+               if (ret)
+                       break;
+
+               if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+                       if ((f->fmt.pix.width == fsize.discrete.width) &&
+                           (((f->fmt.pix.field == V4L2_FIELD_ALTERNATE) ?
+                           f->fmt.pix.height * 2 : f->fmt.pix.height) ==
+                           fsize.discrete.height)) {
+                               found = true;
+                               break;
+                       }
+               } else if ((fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) ||
+                          (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE)) {
+                       if ((f->fmt.pix.width >= fsize.stepwise.min_width) &&
+                           (f->fmt.pix.width <= fsize.stepwise.max_width) &&
+                           (f->fmt.pix.height >= fsize.stepwise.min_height) &&
+                           (f->fmt.pix.height <= fsize.stepwise.max_height)) {
+                               found = true;
+                               break;
+                       }
+               }
+       }
+
+       if (!found) {
+               /* use existing values as default */
+               f->fmt.pix.width = port->mbus_framefmt.width;
+               f->fmt.pix.height =  port->mbus_framefmt.height;
+       }
+
+       /* That we have a fmt calculate imagesize and bytesperline */
+       return vip_calc_format_size(port, fmt, f);
+}
+
 static int vip_g_fmt_vid_cap(struct file *file, void *priv,
                             struct v4l2_format *f)
 {
        struct vip_stream *stream = file2stream(file);
        struct vip_port *port = stream->port;
        struct vip_dev *dev = stream->port->dev;
-       struct v4l2_mbus_framefmt mbus_fmt;
-       struct vip_fmt *fmt;
-       struct v4l2_format try_f;
-       int ret;
+       struct vip_fmt *fmt = port->fmt;
 
        /* Use last known values or defaults */
        f->fmt.pix.width        = stream->width;
@@ -1540,74 +1575,19 @@ static int vip_g_fmt_vid_cap(struct file *file, void *priv,
        f->fmt.pix.bytesperline = stream->bytesperline;
        f->fmt.pix.sizeimage    = stream->sizeimage;
 
-       /* Check with the subdevice */
-       ret = v4l2_subdev_call(dev->sensor, video, g_mbus_fmt, &mbus_fmt);
-       if (ret)
-               vip_dbg(1, dev, "g_mbus_fmt failed in subdev\n");
-
-       fmt = find_active_format_by_code(dev, mbus_fmt.code);
-       if (!fmt) {
-               vip_err(dev,
-                       "mbus_code (0x%08x) invalid.\n",
-                       mbus_fmt.code);
-               return -EINVAL;
-       }
-
-       /*
-        * Run a try_fmt call to properly calculate
-        * the sizeimage and bytesperline values
-        * in case the defaults were not accurate.
-        */
-       try_f = *f;
-       try_f.fmt.pix.pixelformat = fmt->fourcc;
-       try_f.fmt.pix.width = mbus_fmt.width;
-       try_f.fmt.pix.height = mbus_fmt.height;
-       try_f.fmt.pix.field = mbus_fmt.field;
-       try_f.fmt.pix.colorspace = mbus_fmt.colorspace;
-
-       ret = vip_try_fmt_vid_cap(file, priv, &try_f);
-       if (ret)
-               return ret;
-
-       /*
-        * Since everything looks correct update
-        * the local copy as well to make sure we are consistent
-        */
-       *f = try_f;
-       stream->width = f->fmt.pix.width;
-       stream->height = f->fmt.pix.height;
-       stream->sup_field = f->fmt.pix.field;
-       stream->bytesperline = f->fmt.pix.bytesperline;
-       stream->sizeimage = f->fmt.pix.sizeimage;
-
-       vip_dbg(3, dev, "g_fmt fourcc:%s size: %dx%d\n",
+       vip_dbg(3, dev,
+               "g_fmt fourcc:%s code: %04x size: %dx%d bpl:%d img_size:%d\n",
                fourcc_to_str(f->fmt.pix.pixelformat),
-               f->fmt.pix.width, f->fmt.pix.height);
+               fmt->code,
+               f->fmt.pix.width, f->fmt.pix.height,
+               f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+       vip_dbg(3, dev, "g_fmt vpdma data type: 0x%02X\n",
+               port->fmt->vpdma_fmt[0]->data_type);
 
        return 0;
 }
 
-/*
- * Set the registers that are modified when the video format changes.
- */
-static void set_fmt_params(struct vip_stream *stream)
-{
-       struct vip_dev *dev = stream->port->dev;
-
-       stream->sequence = 0;
-       stream->field = V4L2_FIELD_TOP;
-
-       if (stream->port->fmt->colorspace == V4L2_COLORSPACE_SRGB) {
-               vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT);
-               /* Set alpha component in background color */
-               vpdma_set_bg_color(dev->shared->vpdma,
-                       (struct vpdma_data_format *)
-                        stream->port->fmt->vpdma_fmt[0],
-                       0xff);
-       }
-}
-
-int vip_s_fmt_vid_cap(struct file *file, void *priv,
+static int vip_s_fmt_vid_cap(struct file *file, void *priv,
                             struct v4l2_format *f)
 {
        struct vip_stream *stream = file2stream(file);
@@ -1617,17 +1597,25 @@ int vip_s_fmt_vid_cap(struct file *file, void *priv,
        struct v4l2_mbus_framefmt *mf;
        int ret;
 
+       vip_dbg(3, dev, "s_fmt input fourcc:%s size: %dx%d\n",
+               fourcc_to_str(f->fmt.pix.pixelformat),
+               f->fmt.pix.width, f->fmt.pix.height);
+
        ret = vip_try_fmt_vid_cap(file, priv, f);
        if (ret)
                return ret;
 
+       vip_dbg(3, dev, "s_fmt try_fmt fourcc:%s size: %dx%d\n",
+               fourcc_to_str(f->fmt.pix.pixelformat),
+               f->fmt.pix.width, f->fmt.pix.height);
+
        if (vb2_is_busy(&stream->vb_vidq)) {
                vip_err(dev, "%s queue busy\n", __func__);
                return -EBUSY;
        }
 
-       port->fmt               = find_active_format_by_pix(dev,
-                                       f->fmt.pix.pixelformat);
+       port->fmt = find_port_format_by_pix(port,
+                                           f->fmt.pix.pixelformat);
        stream->width           = f->fmt.pix.width;
        stream->height          = f->fmt.pix.height;
        stream->bytesperline    = f->fmt.pix.bytesperline;
@@ -1644,30 +1632,72 @@ int vip_s_fmt_vid_cap(struct file *file, void *priv,
        else
                port->flags &= ~FLAG_INTERLACED;
 
-       vip_dbg(1, dev,
-               "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
-               f->type, stream->width, stream->height, port->fmt->fourcc);
+       vip_dbg(3, dev, "s_fmt fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+               fourcc_to_str(f->fmt.pix.pixelformat),
+               f->fmt.pix.width, f->fmt.pix.height,
+               f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
 
-       set_fmt_params(stream);
+       mf = &sfmt.format;
+       v4l2_fill_mbus_format(mf, &f->fmt.pix, port->fmt->code);
 
-       mf = vip_video_pix_to_mbus(&f->fmt.pix, &sfmt.format);
+       /* Save it */
+       port->mbus_framefmt = *mf;
+
+       vip_dbg(3, dev, "s_fmt pix_to_mbus mbus_code: %04X size: %dx%d\n",
+               mf->code,
+               mf->width, mf->height);
 
        ret = v4l2_subdev_call(dev->sensor, video, try_mbus_fmt, mf);
        if (ret) {
                vip_dbg(1, dev, "try_mbus_fmt failed in subdev\n");
                return ret;
        }
+       vip_dbg(3, dev, "s_fmt subdev try_fmt mbus_code: %04X size: %dx%d\n",
+               mf->code,
+               mf->width, mf->height);
        ret = v4l2_subdev_call(dev->sensor, video, s_mbus_fmt, mf);
        if (ret) {
                vip_dbg(1, dev, "s_mbus_fmt failed in subdev\n");
                return ret;
        }
-
-       vip_setup_parser(dev->ports[0]);
+       vip_dbg(3, dev, "s_fmt subdev s_fmt mbus_code: %04X size: %dx%d\n",
+               mf->code,
+               mf->width, mf->height);
+       vip_dbg(3, dev, "s_fmt vpdma data type: 0x%02X\n",
+               port->fmt->vpdma_fmt[0]->data_type);
 
        return 0;
 }
-EXPORT_SYMBOL(vip_s_fmt_vid_cap);
+
+/*
+ * Set the registers that are modified when the video format changes.
+ */
+static void set_fmt_params(struct vip_stream *stream)
+{
+       struct vip_dev *dev = stream->port->dev;
+       int data_path_reg;
+
+       stream->sequence = 0;
+       stream->field = V4L2_FIELD_TOP;
+
+       if (stream->port->fmt->colorspace == V4L2_COLORSPACE_SRGB) {
+               vip_set_slice_path(dev, VIP_RGB_OUT_LO_DATA_SELECT);
+               /* Set alpha component in background color */
+               vpdma_set_bg_color(dev->shared->vpdma,
+                                  (struct vpdma_data_format *)
+                                  stream->port->fmt->vpdma_fmt[0],
+                                  0xff);
+       }
+
+       data_path_reg = VIP_VIP1_DATA_PATH_SELECT + 4 * dev->slice_id;
+       if (stream->port->fmt->coplanar) {
+               stream->port->flags &= ~FLAG_MULT_PORT;
+               write_vreg(dev, data_path_reg, 0x600);
+       } else {
+               stream->port->flags |= FLAG_MULT_PORT;
+               write_vreg(dev, data_path_reg, 0x8000);
+       }
+}
 
 static int vip_g_selection(struct file *file, void *fh,
                           struct v4l2_selection *s)
@@ -1763,10 +1793,6 @@ static const struct v4l2_ioctl_ops vip_ioctl_ops = {
        .vidioc_g_std           = vip_g_std,
        .vidioc_s_std           = vip_s_std,
 
-       .vidioc_queryctrl       = vip_queryctrl,
-       .vidioc_g_ctrl          = vip_g_ctrl,
-       .vidioc_s_ctrl          = vip_s_ctrl,
-
        .vidioc_enum_fmt_vid_cap = vip_enum_fmt_vid_cap,
        .vidioc_g_fmt_vid_cap   = vip_g_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap = vip_try_fmt_vid_cap,
@@ -1775,7 +1801,7 @@ static const struct v4l2_ioctl_ops vip_ioctl_ops = {
        .vidioc_enum_frameintervals     = vip_enum_frameintervals,
        .vidioc_enum_framesizes         = vip_enum_framesizes,
        .vidioc_s_parm                  = vip_s_parm,
-
+       .vidioc_g_parm                  = vip_g_parm,
        .vidioc_g_selection     = vip_g_selection,
        .vidioc_s_selection     = vip_s_selection,
        .vidioc_reqbufs         = vb2_ioctl_reqbufs,
@@ -1850,6 +1876,10 @@ static int vip_start_streaming(struct vb2_queue *vq, unsigned int count)
        struct vip_dev *dev = port->dev;
        struct vip_buffer *buf;
        unsigned long flags;
+       int ret;
+
+       set_fmt_params(stream);
+       vip_setup_parser(dev->ports[0]);
 
        buf = list_entry(stream->vidq.next,
                         struct vip_buffer, list);
@@ -1860,6 +1890,14 @@ static int vip_start_streaming(struct vb2_queue *vq, unsigned int count)
        stream->sequence = 0;
        stream->field = V4L2_FIELD_TOP;
 
+       if (dev->sensor) {
+               ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1);
+               if (ret) {
+                       vip_dbg(1, dev, "stream on failed in subdev\n");
+                       return ret;
+               }
+       }
+
        populate_desc_list(stream);
 
        /* The first few VPDMA ListComplete interrupts fire pretty quiclky
@@ -1872,26 +1910,28 @@ static int vip_start_streaming(struct vb2_queue *vq, unsigned int count)
        dev->num_skip_irq = VIP_VPDMA_FIFO_SIZE;
 
        spin_lock_irqsave(&dev->slock, flags);
-       if (vpdma_list_busy(dev->shared->vpdma, dev->slice_id)) {
+       if (vpdma_list_busy(dev->shared->vpdma, stream->list_num)) {
                spin_unlock_irqrestore(&dev->slock, flags);
-               vpdma_unmap_desc_buf(dev->shared->vpdma, &dev->desc_list.buf);
-               vpdma_reset_desc_list(&dev->desc_list);
+               vpdma_unmap_desc_buf(dev->shared->vpdma,
+                               &stream->desc_list.buf);
+               vpdma_reset_desc_list(&stream->desc_list);
                return -EBUSY;
        }
 
-       list_move_tail(&buf->list, &dev->vip_bufs);
+       list_move_tail(&buf->list, &stream->post_bufs);
        spin_unlock_irqrestore(&dev->slock, flags);
 
        vip_dbg(2, dev, "start_streaming: start_dma buf 0x%x\n",
                (unsigned int)buf);
-       start_dma(dev, buf);
+       start_dma(stream, buf);
 
        /* We enable the irq after posting the vpdma descriptor
         * to prevent sprurious interrupt coming in before the
         * vb2 layer is completely ready to handle them
         * otherwise the vb2_streaming test would fail early on
          */
-       enable_irqs(dev, dev->slice_id);
+       enable_irqs(dev, dev->slice_id, stream->list_num);
+
        return 0;
 }
 
@@ -1904,13 +1944,22 @@ static int vip_stop_streaming(struct vb2_queue *vq)
        struct vip_port *port = stream->port;
        struct vip_dev *dev = port->dev;
        struct vip_buffer *buf;
+       int ret;
 
-       disable_irqs(dev, dev->slice_id);
-       clear_irqs(dev, dev->slice_id);
+       if (dev->sensor) {
+               ret = v4l2_subdev_call(dev->sensor, video, s_stream, 0);
+               if (ret)
+                       vip_dbg(1, dev, "stream on failed in subdev\n");
+       }
+
+       disable_irqs(dev, dev->slice_id, stream->list_num);
+       clear_irqs(dev, dev->slice_id, stream->list_num);
+       stop_dma(stream);
 
        /* release all active buffers */
-       while (!list_empty(&dev->vip_bufs)) {
-               buf = list_entry(dev->vip_bufs.next, struct vip_buffer, list);
+       while (!list_empty(&stream->post_bufs)) {
+               buf = list_entry(stream->post_bufs.next,
+                                       struct vip_buffer, list);
                list_del(&buf->list);
                if (buf->drop == 1)
                        list_add_tail(&buf->list, &stream->dropq);
@@ -1926,8 +1975,8 @@ static int vip_stop_streaming(struct vb2_queue *vq)
        if (!vb2_is_streaming(vq))
                return 0;
 
-       vpdma_unmap_desc_buf(dev->shared->vpdma, &dev->desc_list.buf);
-       vpdma_reset_desc_list(&dev->desc_list);
+       vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+       vpdma_reset_desc_list(&stream->desc_list);
 
        return 0;
 }
@@ -1963,32 +2012,123 @@ static struct vb2_ops vip_video_qops = {
  */
 
 static int vip_init_dev(struct vip_dev *dev)
+{
+       if (dev->num_ports != 0)
+               goto done;
+
+       vip_set_clock_enable(dev, 1);
+done:
+       dev->num_ports++;
+
+       return 0;
+}
+
+static int vip_init_port(struct vip_port *port)
 {
        int ret;
+       struct vip_dev *dev = port->dev;
+       struct vip_fmt *fmt;
+       struct v4l2_mbus_framefmt *mbus_fmt = &port->mbus_framefmt;
 
-       if (dev->num_ports != 0)
+       if (port->num_streams != 0)
                goto done;
 
-       ret = vpdma_create_desc_list(&dev->desc_list, VIP_DESC_LIST_SIZE,
+       ret = vip_init_dev(port->dev);
+       if (ret)
+               goto done;
+
+       /* Get subdevice current frame format */
+       ret = v4l2_subdev_call(dev->sensor, video, g_mbus_fmt, mbus_fmt);
+       if (ret)
+               vip_dbg(1, dev, "init_port g_mbus_fmt failed in subdev\n");
+
+       /* try to find one that matches */
+       fmt = find_port_format_by_pix(port, mbus_fmt->code);
+       if (!fmt) {
+               vip_dbg(1, dev, "subdev default mbus_fmt %04x is not matched.\n",
+                       mbus_fmt->code);
+               /* if all else fails just pick the first one */
+               fmt = dev->active_fmt[0];
+
+               mbus_fmt->code = fmt->code;
+               ret = v4l2_subdev_call(dev->sensor, video,
+                                      s_mbus_fmt, mbus_fmt);
+               if (ret)
+                       vip_dbg(1, dev, "init_port s_mbus_fmt failed in subdev\n");
+       }
+
+       /* Assign current format */
+       port->fmt = fmt;
+
+       vip_dbg(3, dev, "vip_init_port: g_mbus_fmt subdev mbus_code: %04X fourcc:%s size: %dx%d\n",
+               fmt->code,
+               fourcc_to_str(fmt->fourcc),
+               mbus_fmt->width, mbus_fmt->height);
+
+       if (mbus_fmt->field == V4L2_FIELD_ALTERNATE)
+               port->flags |= FLAG_INTERLACED;
+       else
+               port->flags &= ~FLAG_INTERLACED;
+
+       port->c_rect.left       = 0;
+       port->c_rect.top        = 0;
+       port->c_rect.width      = mbus_fmt->width;
+       port->c_rect.height     = mbus_fmt->height;
+
+done:
+       port->num_streams++;
+       return 0;
+}
+
+static int vip_init_stream(struct vip_stream *stream)
+{
+       struct vip_port *port = stream->port;
+       struct vip_dev *dev = port->dev;
+       struct vip_fmt *fmt;
+       struct v4l2_mbus_framefmt *mbus_fmt;
+       struct v4l2_format f;
+       int ret;
+
+       ret = vip_init_port(port);
+       if (ret != 0)
+               return ret;
+
+       fmt = port->fmt;
+       mbus_fmt = &port->mbus_framefmt;
+
+       /* Properly calculate the sizeimage and bytesperline values. */
+       v4l2_fill_pix_format(&f.fmt.pix, mbus_fmt);
+       f.fmt.pix.pixelformat = fmt->fourcc;
+       ret = vip_calc_format_size(port, fmt, &f);
+       if (ret)
+               return ret;
+
+       stream->width = f.fmt.pix.width;
+       stream->height = f.fmt.pix.height;
+       stream->sup_field = f.fmt.pix.field;
+       stream->bytesperline = f.fmt.pix.bytesperline;
+       stream->sizeimage = f.fmt.pix.sizeimage;
+
+       vip_dbg(3, dev, "init_stream fourcc:%s size: %dx%d bpl:%d img_size:%d\n",
+               fourcc_to_str(f.fmt.pix.pixelformat),
+               f.fmt.pix.width, f.fmt.pix.height,
+               f.fmt.pix.bytesperline, f.fmt.pix.sizeimage);
+       vip_dbg(3, dev, "init_stream vpdma data type: 0x%02X\n",
+               port->fmt->vpdma_fmt[0]->data_type);
+
+       ret = vpdma_create_desc_list(&stream->desc_list, VIP_DESC_LIST_SIZE,
                        VPDMA_LIST_TYPE_NORMAL);
 
        if (ret != 0)
                return ret;
 
-       dev->write_desc = (struct vpdma_dtd *)dev->desc_list.buf.addr
+       stream->write_desc = (struct vpdma_dtd *)stream->desc_list.buf.addr
                                + 15;
-       vip_set_clock_enable(dev, 1);
-done:
-       dev->num_ports++;
-
        return 0;
 }
 
 static void vip_release_dev(struct vip_dev *dev)
 {
-       vpdma_free_desc_buf(&dev->desc_list.buf);
-       vpdma_free_desc_list(&dev->desc_list);
-
        /*
         * On last close, disable clocks to conserve power
         */
@@ -2002,9 +2142,10 @@ static int vip_setup_parser(struct vip_port *port)
        struct vip_dev *dev = port->dev;
        struct v4l2_of_endpoint *endpoint = dev->endpoint;
        int iface = DUAL_8B_INTERFACE;
-       int sync_type;
+       int sync_type, pclk_type;
        unsigned int flags;
 
+       flags = endpoint->bus.parallel.flags;
        vip_reset_port(port);
        vip_set_port_enable(port, 1);
 
@@ -2025,7 +2166,8 @@ static int vip_setup_parser(struct vip_port *port)
                        break;
                        case 1:
                        default:
-                               sync_type = EMBEDDED_SYNC_SINGLE_YUV422;
+                               sync_type =
+                               EMBEDDED_SYNC_LINE_MULTIPLEXED_YUV422;
                        }
                }
 
@@ -2047,7 +2189,6 @@ static int vip_setup_parser(struct vip_port *port)
                else
                        sync_type = DISCRETE_SYNC_SINGLE_YUV422;
 
-               flags = endpoint->bus.parallel.flags;
                if (flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH |
                        V4L2_MBUS_HSYNC_ACTIVE_LOW))
                        vip_set_vsync_polarity(port,
@@ -2058,11 +2199,6 @@ static int vip_setup_parser(struct vip_port *port)
                        vip_set_hsync_polarity(port,
                                flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH ? 1 : 0);
 
-               if (flags & (V4L2_MBUS_PCLK_SAMPLE_RISING |
-                       V4L2_MBUS_PCLK_SAMPLE_FALLING))
-                       vip_set_pclk_polarity(port,
-                               flags & V4L2_MBUS_PCLK_SAMPLE_RISING ? 1 : 0);
-
                vip_xtra_set_repack_sel(port, 0);
                vip_set_actvid_hsync_n(port, 0);
                vip_set_actvid_polarity(port, 1);
@@ -2073,57 +2209,44 @@ static int vip_setup_parser(struct vip_port *port)
                return -EINVAL;
        }
 
+       pclk_type = flags & V4L2_MBUS_PCLK_SAMPLE_RISING ? 0 : 1;
+       vip_set_pclk_polarity(port, pclk_type);
        vip_set_data_interface(port, iface);
        vip_sync_type(port, sync_type);
        return 0;
 }
 
-static int vip_init_port(struct vip_port *port)
+static void vip_release_stream(struct vip_stream *stream)
 {
-       int ret;
-
-       if (port->num_streams != 0)
-               goto done;
-
-       ret = vip_init_dev(port->dev);
-       if (ret)
-               goto done;
-
-       port->fmt = port->dev->active_fmt[0];
-       port->src_colorspace = port->fmt->colorspace;
-       port->c_rect.left = 0;
-       port->c_rect.top = 0;
+       struct vip_dev *dev = stream->port->dev;
 
-done:
-       port->num_streams++;
-       return 0;
+       vpdma_unmap_desc_buf(dev->shared->vpdma, &stream->desc_list.buf);
+       vpdma_free_desc_buf(&stream->desc_list.buf);
+       vpdma_free_desc_list(&stream->desc_list);
 }
 
-static void vip_release_port(struct vip_port *port)
+static void stop_dma(struct vip_stream *stream)
 {
-       struct vip_dev *dev = port->dev;
+       struct vip_dev *dev = stream->port->dev;
        int ch, size = 0;
 
        /* Create a list of channels to be cleared */
        for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++) {
-               if (dev->vpdma_channels[ch] == 1) {
-                       dev->vpdma_channels[size++] = ch;
+               if (stream->vpdma_channels[ch] == 1) {
+                       stream->vpdma_channels[size++] = ch;
                        vip_dbg(2, dev, "Clear channel no: %d\n", ch);
                }
        }
 
        /* Clear all the used channels for the list */
-       vpdma_list_cleanup(dev->shared->vpdma, dev->slice_id,
-               dev->vpdma_channels, size);
+       vpdma_list_cleanup(dev->shared->vpdma, stream->list_num,
+               stream->vpdma_channels, size);
 
        for (ch = 0; ch < VPDMA_MAX_CHANNELS; ch++)
-               dev->vpdma_channels[ch] = 0;
-
-       if (--port->num_streams == 0)
-               vip_release_dev(port->dev);
+               stream->vpdma_channels[ch] = 0;
 }
 
-int vip_open(struct file *file)
+static int vip_open(struct file *file)
 {
        struct vip_stream *stream = video_drvdata(file);
        struct vip_port *port = stream->port;
@@ -2147,22 +2270,10 @@ int vip_open(struct file *file)
         * Then initialize hw module.
         */
        if (v4l2_fh_is_singular_file(file)) {
-               if (vip_init_port(port)) {
-                       goto free_fh;
+               if (vip_init_stream(stream)) {
                        ret = -ENODEV;
+                       goto free_fh;
                }
-               stream->width = 1280;
-               stream->height = 720;
-               stream->sizeimage = stream->width * stream->height *
-                       (port->fmt->vpdma_fmt[0]->depth +
-                       (port->fmt->coplanar ?
-                               port->fmt->vpdma_fmt[1]->depth : 0)) >> 3;
-               stream->bytesperline = round_up((stream->width *
-                                       port->fmt->vpdma_fmt[0]->depth) >> 3,
-                                       1 << L_ALIGN);
-               stream->sup_field = V4L2_FIELD_NONE;
-               port->c_rect.width = stream->width;
-               port->c_rect.height = stream->height;
                vip_dbg(1, dev, "Created stream instance %p\n", stream);
        }
 
@@ -2178,9 +2289,8 @@ free_fh:
        }
        return ret;
 }
-EXPORT_SYMBOL(vip_open);
 
-int vip_release(struct file *file)
+static int vip_release(struct file *file)
 {
        struct vip_stream *stream = video_drvdata(file);
        struct vip_port *port = stream->port;
@@ -2197,7 +2307,10 @@ int vip_release(struct file *file)
                mutex_lock(&dev->mutex);
 
                vip_stop_streaming(q);
-               vip_release_port(stream->port);
+               vip_release_stream(stream);
+
+               if (--port->num_streams == 0)
+                       vip_release_dev(port->dev);
 
                mutex_unlock(&dev->mutex);
                vip_dbg(1, dev, "Releasing stream instance %p\n", stream);
@@ -2205,12 +2318,12 @@ int vip_release(struct file *file)
 
        return vb2_fop_release(file);
 }
-EXPORT_SYMBOL(vip_release);
 
 static const struct v4l2_file_operations vip_fops = {
        .owner          = THIS_MODULE,
        .open           = vip_open,
        .release        = vip_release,
+       .read           = vb2_fop_read,
        .poll           = vb2_fop_poll,
        .unlocked_ioctl = video_ioctl2,
        .mmap           = vb2_fop_mmap,
@@ -2222,6 +2335,7 @@ static struct video_device vip_videodev = {
        .ioctl_ops      = &vip_ioctl_ops,
        .minor          = -1,
        .release        = video_device_release,
+       .tvnorms        = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
 };
 
 static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type)
@@ -2241,6 +2355,15 @@ static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type)
        stream->stream_id = stream_id;
        stream->vfl_type = vfl_type;
 
+       stream->list_num = vpdma_hwlist_alloc(dev->shared->vpdma, stream);
+       if (stream->list_num < 0) {
+               vip_err(dev, "Could not get VPDMA hwlist");
+               ret = -ENODEV;
+               goto do_free_stream;
+       }
+
+       INIT_LIST_HEAD(&stream->post_bufs);
+
        if (vfl_type == VFL_TYPE_GRABBER)
                port->cap_streams[stream_id] = stream;
        else
@@ -2251,7 +2374,7 @@ static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type)
         */
        q = &stream->vb_vidq;
        q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-       q->io_modes = VB2_MMAP | VB2_DMABUF;
+       q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
        q->drv_priv = stream;
        q->buf_struct_size = sizeof(struct vip_buffer);
        q->ops = &vip_video_qops;
@@ -2328,9 +2451,53 @@ static void free_stream(struct vip_stream *stream)
 
        video_unregister_device(stream->vfd);
        video_device_release(stream->vfd);
+       vpdma_hwlist_release(dev->shared->vpdma, stream->list_num);
        kfree(stream);
 }
 
+static int get_subdev_active_format(struct vip_port *port,
+                                   struct v4l2_subdev *subdev)
+{
+       struct vip_dev *dev = port->dev;
+       struct vip_fmt *fmt;
+       enum v4l2_mbus_pixelcode code;
+       int ret = 0;
+       unsigned int k, i, j;
+
+       /* Enumerate sub device formats and enable all matching local formats */
+       dev->num_active_fmt = 0;
+       for (k = 0, i = 0;
+            (ret != -EINVAL);
+            k++) {
+               ret = v4l2_subdev_call(subdev, video, enum_mbus_fmt, k, &code);
+               if (ret == 0) {
+                       vip_dbg(2, dev,
+                               "subdev %s: code: %04x idx: %d\n",
+                               subdev->name, code, k);
+
+                       for (j = 0; j < ARRAY_SIZE(vip_formats); j++) {
+                               fmt = &vip_formats[j];
+                               if (code == fmt->code) {
+                                       dev->active_fmt[i] = fmt;
+                                       dev->num_active_fmt = i++;
+                                       vip_dbg(2, dev,
+                                               "matched fourcc: %s: code: %04x idx: %d\n",
+                                               fourcc_to_str(fmt->fourcc),
+                                               fmt->code,
+                                               dev->num_active_fmt);
+                               }
+                       }
+               }
+       }
+
+       if (i == 0) {
+               vip_err(dev, "No suitable format reported by subdev %s\n",
+                       subdev->name);
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int alloc_port(struct vip_dev *dev, int id)
 {
        struct vip_port *port;
@@ -2344,10 +2511,18 @@ static int alloc_port(struct vip_dev *dev, int id)
        port->dev = dev;
        port->port_id = id;
        port->num_streams = 0;
+       port->flags |= FLAG_MULT_PORT;
 
        ret = alloc_stream(port, 0, VFL_TYPE_GRABBER);
 
-       return 0;
+       if (dev->endpoint->bus_type == V4L2_MBUS_BT656) {
+               /* Allocate streams for 4 channels */
+               ret = alloc_stream(port, 2, VFL_TYPE_GRABBER);
+               ret = alloc_stream(port, 4, VFL_TYPE_GRABBER);
+               ret = alloc_stream(port, 6, VFL_TYPE_GRABBER);
+       }
+
+       return get_subdev_active_format(port, dev->sensor);
 }
 
 static void free_port(struct vip_port *port)
@@ -2409,38 +2584,6 @@ static int vip_runtime_get(struct platform_device *pdev)
        return r < 0 ? r : 0;
 }
 
-static int get_subdev_active_format(struct vip_dev *dev,
-                                   struct v4l2_subdev *subdev)
-{
-       struct vip_fmt *fmt;
-       enum v4l2_mbus_pixelcode code;
-       int ret = 0;
-       unsigned int k;
-
-       /* first find how many formats to allocate the correct size */
-       dev->num_active_fmt = 0;
-       for (k = 0;
-            (ret != -EINVAL) && (dev->num_active_fmt < VIP_MAX_ACTIVE_FMT);
-            k++) {
-               ret = v4l2_subdev_call(subdev, video, enum_mbus_fmt, k, &code);
-               if (ret == 0) {
-                       fmt = find_format_by_code(code);
-                       if (fmt) {
-                               dev->active_fmt[dev->num_active_fmt] = fmt;
-                               dev->num_active_fmt++;
-                       }
-               }
-       }
-
-       if (dev->num_active_fmt == 0) {
-
-               vip_err(dev, "No suitable format reported by subdev %s\n",
-                       subdev->name);
-               return -EINVAL;
-       }
-
-       return 0;
-}
 
 static int vip_async_bound(struct v4l2_async_notifier *notifier,
                        struct v4l2_subdev *subdev,
@@ -2448,33 +2591,33 @@ static int vip_async_bound(struct v4l2_async_notifier *notifier,
 {
        struct vip_dev *dev = notifier_to_vip_dev(notifier);
        unsigned int idx = asd - &dev->config->asd[0];
+       int ret;
 
        vip_dbg(1, dev, "vip_async_bound\n");
        if (idx > dev->config->asd_sizes)
                return -EINVAL;
 
-       if (get_subdev_active_format(dev, subdev))
-               return 0;
-
        if (dev->sensor) {
                if (asd < dev->sensor->asd) {
                        /* Notified of a subdev earlier in the array */
+                       dev->sensor = subdev;
+                       dev->endpoint = &dev->config->endpoints[idx];
                        vip_info(dev, "Switching to subdev %s (High priority)",
                                 subdev->name);
-               } else {
+
+               } else
                        vip_info(dev, "Rejecting subdev %s (Low priority)",
                                 subdev->name);
-                       return 0;
-               }
-       } else
-               alloc_port(dev, 0);
+               return 0;
+       }
 
        dev->sensor = subdev;
        dev->endpoint = &dev->config->endpoints[idx];
-       vip_info(dev, "Using sensor %s for capture\n",
-                subdev->name);
+       ret = alloc_port(dev, 0);
+       if (!ret)
+               vip_info(dev, "Using sensor %s for capture\n", subdev->name);
 
-       return 0;
+       return ret;
 }
 
 static int vip_async_complete(struct v4l2_async_notifier *notifier)
@@ -2681,6 +2824,7 @@ static int vip_probe(struct platform_device *pdev)
        struct pinctrl *pinctrl;
        int ret, slice = VIP_SLICE1;
        u32 tmp, pid;
+       struct v4l2_ctrl_handler *hdl;
 
        pm_runtime_enable(&pdev->dev);
 
@@ -2707,8 +2851,7 @@ static int vip_probe(struct platform_device *pdev)
 
        pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
        if (IS_ERR(pinctrl)) {
-               ret = PTR_ERR(pinctrl);
-               goto free_shared;
+               dev_warn(&pdev->dev, "No explicit pinctrl resources data.\n");
        }
 
        if (devm_request_mem_region(&pdev->dev, shared->res->start,
@@ -2740,12 +2883,6 @@ static int vip_probe(struct platform_device *pdev)
        vip_shared_set_clock_enable(shared, 1);
        vip_top_vpdma_reset(shared);
 
-       shared->vpdma = vpdma_create(pdev, vip_vpdma_fw_cb);
-       if (!shared->vpdma) {
-               dev_err(&pdev->dev, "Creating VPDMA failed");
-               goto do_iounmap;
-       }
-
        list_add_tail(&shared->list, &vip_shared_list);
        platform_set_drvdata(pdev, shared);
        atomic_set(&shared->devs_allocated, 0);
@@ -2758,6 +2895,10 @@ static int vip_probe(struct platform_device *pdev)
                if (!dev)
                        return -ENOMEM;
 
+               dev->instance_id = (int)of_dev_id->data;
+               snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
+                       "%s%d-s%d", VIP_MODULE_NAME, dev->instance_id, slice);
+
                dev->irq = platform_get_irq(pdev, slice);
                if (!dev->irq) {
                        dev_err(&pdev->dev, "Could not get IRQ");
@@ -2765,7 +2906,7 @@ static int vip_probe(struct platform_device *pdev)
                }
 
                if (devm_request_irq(&pdev->dev, dev->irq, vip_irq,
-                                    0, VIP_MODULE_NAME, dev) < 0) {
+                                    0, dev->v4l2_dev.name, dev) < 0) {
                        ret = -ENOMEM;
                        goto dev_unreg;
                }
@@ -2773,18 +2914,16 @@ static int vip_probe(struct platform_device *pdev)
                spin_lock_init(&dev->slock);
                spin_lock_init(&dev->lock);
 
-               INIT_LIST_HEAD(&dev->vip_bufs);
-
-               dev->instance_id = (int)of_dev_id->data;
-
-               snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
-                       "%s%d-%d", VIP_MODULE_NAME, dev->instance_id, slice);
                ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
                if (ret)
                        goto err_runtime_get;
 
                mutex_init(&dev->mutex);
 
+               hdl = &dev->ctrl_handler;
+               v4l2_ctrl_handler_init(hdl, 11);
+               dev->v4l2_dev.ctrl_handler = hdl;
+
                dev->slice_id = slice;
                dev->pdev = pdev;
                dev->res = shared->res;
@@ -2806,6 +2945,13 @@ static int vip_probe(struct platform_device *pdev)
                vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT);
        }
 
+       shared->vpdma = &shared->vpdma_data;
+       ret = vpdma_create(pdev, shared->vpdma, vip_vpdma_fw_cb);
+       if (ret) {
+               dev_err(&pdev->dev, "Creating VPDMA failed");
+               goto dev_unreg;
+       }
+
        return 0;
 
 dev_unreg:
@@ -2861,6 +3007,7 @@ static const struct of_device_id vip_of_match[] = {
        },
        {},
 };
+MODULE_DEVICE_TABLE(of, vip_of_match);
 #else
 #define vip_of_match NULL
 #endif