]> 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: Hack in interpreting the YUV 422 data.
[android-sdk/kernel-video.git] / drivers / media / platform / ti-vpe / vip.c
index 2cd0f87da8e29785ad38a87b05b219893024e720..bd141b3b9dde990a1e77f5cfb65d40ea75dd22c1 100644 (file)
@@ -162,7 +162,8 @@ 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],
+               /* bus order is reversed so flip Y and UV bytes */
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422],
                                  },
        },
        {
@@ -171,7 +172,8 @@ static struct vip_fmt vip_formats[] = {
                .code           = V4L2_MBUS_FMT_YUYV8_2X8,
                .colorspace     = V4L2_COLORSPACE_SMPTE170M,
                .coplanar       = 0,
-               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YC422],
+               /* bus order is reversed so flip Y and UV bytes */
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YCB422],
                                  },
        },
        {
@@ -180,7 +182,18 @@ static struct vip_fmt vip_formats[] = {
                .code           = V4L2_MBUS_FMT_VYUY8_2X8,
                .colorspace     = V4L2_COLORSPACE_SMPTE170M,
                .coplanar       = 0,
-               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_YC422],
+               /* bus order is reversed so flip Y and UV bytes */
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422],
+                                 },
+       },
+       {
+               .name           = "YVYU 422 packed",
+               .fourcc         = V4L2_PIX_FMT_YVYU,
+               .code           = V4L2_MBUS_FMT_YVYU8_2X8,
+               .colorspace     = V4L2_COLORSPACE_SMPTE170M,
+               .coplanar       = 0,
+               /* bus order is reversed so flip Y and UV bytes */
+               .vpdma_fmt      = { &vpdma_yuv_fmts[VPDMA_DATA_FMT_CBY422],
                                  },
        },
        {
@@ -304,6 +317,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 +538,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 +1038,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 +1070,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 +1081,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 +1108,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 +1120,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 +1144,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)
@@ -1153,13 +1175,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 +1193,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 +1208,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 +1249,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,20 +1265,35 @@ 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);
+
+                       stream = vpdma_hwlist_get_priv(vpdma, list_num);
+                       if (!stream || stream->list_num != list_num) {
+                               vip_err(dev, "IRQ occured for unused list");
+                               continue;
+                       }
+
+                       vpdma_clear_list_stat(vpdma, irq_num, list_num);
+
+                       if (dev->num_skip_irq)
+                               dev->num_skip_irq--;
+                       else
+                               vip_process_buffer_complete(stream);
+
+                       vip_schedule_next_buffer(stream);
+                       irqst &= ~((1 << list_num * 2));
+               }
+       }
 
        return IRQ_HANDLED;
 }
@@ -1595,6 +1634,8 @@ static int vip_g_fmt_vid_cap(struct file *file, void *priv,
                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, "g_fmt vpdma data type: 0x%02X\n",
+               port->fmt->vpdma_fmt[0]->data_type);
 
        return 0;
 }
@@ -1605,6 +1646,7 @@ static int vip_g_fmt_vid_cap(struct file *file, void *priv,
 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;
@@ -1617,6 +1659,15 @@ static void set_fmt_params(struct vip_stream *stream)
                         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);
+       }
 }
 
 int vip_s_fmt_vid_cap(struct file *file, void *priv,
@@ -1700,6 +1751,8 @@ int vip_s_fmt_vid_cap(struct file *file, void *priv,
        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;
 }
@@ -1916,26 +1969,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;
 }
 
@@ -1956,12 +2011,14 @@ static int vip_stop_streaming(struct vb2_queue *vq)
                        vip_dbg(1, dev, "stream on failed in subdev\n");
        }
 
-       disable_irqs(dev, dev->slice_id);
-       clear_irqs(dev, dev->slice_id);
+       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);
@@ -1977,8 +2034,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;
 }
@@ -2014,33 +2071,59 @@ 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;
 
-       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;
+
+       port->fmt = port->dev->active_fmt[0];
+       port->src_colorspace = port->fmt->colorspace;
+       port->c_rect.left = 0;
+       port->c_rect.top = 0;
+
+done:
+       port->num_streams++;
+       return 0;
+}
+
+static int vip_init_stream(struct vip_stream *stream)
+{
+       int ret;
+
+       ret = vip_init_port(stream->port);
+       if (ret != 0)
+               return ret;
+
+       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_unmap_desc_buf(dev->shared->vpdma, &dev->desc_list.buf);
-       vpdma_free_desc_buf(&dev->desc_list.buf);
-       vpdma_free_desc_list(&dev->desc_list);
-
        /*
         * On last close, disable clocks to conserve power
         */
@@ -2054,9 +2137,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);
 
@@ -2077,7 +2161,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;
                        }
                }
 
@@ -2099,7 +2184,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,
@@ -2110,11 +2194,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);
@@ -2125,54 +2204,41 @@ 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)
@@ -2199,7 +2265,7 @@ int vip_open(struct file *file)
         * Then initialize hw module.
         */
        if (v4l2_fh_is_singular_file(file)) {
-               if (vip_init_port(port)) {
+               if (vip_init_stream(stream)) {
                        goto free_fh;
                        ret = -ENODEV;
                }
@@ -2249,7 +2315,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);
@@ -2293,6 +2362,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
@@ -2380,6 +2458,7 @@ 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);
 }
 
@@ -2396,9 +2475,17 @@ 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);
 
+       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 0;
 }
 
@@ -2484,6 +2571,10 @@ static int get_subdev_active_format(struct vip_dev *dev,
                }
        }
 
+       if (find_active_format_by_pix(dev, V4L2_PIX_FMT_YUYV))
+               /* When YUYV format is supported, NV12 can also be supported */
+               dev->active_fmt[dev->num_active_fmt++] = &vip_formats[2];
+
        if (dev->num_active_fmt == 0) {
 
                vip_err(dev, "No suitable format reported by subdev %s\n",
@@ -2511,20 +2602,21 @@ static int vip_async_bound(struct v4l2_async_notifier *notifier,
        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);
+       vip_info(dev, "Using sensor %s for capture\n", subdev->name);
+       alloc_port(dev, 0);
 
        return 0;
 }
@@ -2792,12 +2884,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);
@@ -2810,6 +2896,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");
@@ -2817,7 +2907,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;
                }
@@ -2825,12 +2915,6 @@ 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;
@@ -2862,6 +2946,12 @@ static int vip_probe(struct platform_device *pdev)
                vip_set_slice_path(dev, VIP_MULTI_CHANNEL_DATA_SELECT);
        }
 
+       shared->vpdma = vpdma_create(pdev, vip_vpdma_fw_cb);
+       if (!shared->vpdma) {
+               dev_err(&pdev->dev, "Creating VPDMA failed");
+               goto dev_unreg;
+       }
+
        return 0;
 
 dev_unreg:
@@ -2917,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