#define VPE_CHROMA     1
 
 /* per m2m context info */
+#define VPE_MAX_SRC_BUFS       3       /* need 3 src fields to de-interlace */
+
 #define VPE_DEF_BUFS_PER_JOB   1       /* default one buffer per batch job */
 
 /*
                0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8,
                0x00C8, 0x0348, 0x0018, 0x3FD8, 0x3FB8, 0x0378, 0x00E8, 0x3FE8,
        },
+       {
+               /* Coefficients for Top Field Interlaced input */
+               0x0051, 0x03D5, 0x3FE3, 0x3FF7, 0x3FB5, 0x02E9, 0x018F, 0x3FD3,
+               /* Coefficients for Bottom Field Interlaced input */
+               0x016B, 0x0247, 0x00B1, 0x3F9D, 0x3FCF, 0x03DB, 0x005D, 0x3FF9,
+       },
+};
+
+/*
+ * the following registers are for configuring some of the parameters of the
+ * motion and edge detection blocks inside DEI, these generally remain the same,
+ * these could be passed later via userspace if some one needs to tweak these.
+ */
+struct vpe_dei_regs {
+       unsigned long mdt_spacial_freq_thr_reg;         /* VPE_DEI_REG2 */
+       unsigned long edi_config_reg;                   /* VPE_DEI_REG3 */
+       unsigned long edi_lut_reg0;                     /* VPE_DEI_REG4 */
+       unsigned long edi_lut_reg1;                     /* VPE_DEI_REG5 */
+       unsigned long edi_lut_reg2;                     /* VPE_DEI_REG6 */
+       unsigned long edi_lut_reg3;                     /* VPE_DEI_REG7 */
+};
+
+/*
+ * default expert DEI register values, unlikely to be modified.
+ */
+static const struct vpe_dei_regs dei_regs = {
+       0x020C0804u,
+       0x0118100Fu,
+       0x08040200u,
+       0x1010100Cu,
+       0x10101010u,
+       0x10101010u,
 };
 
 /*
  */
 struct vpe_port_data {
        enum vpdma_channel channel;     /* VPDMA channel */
+       u8      vb_index;               /* input frame f, f-1, f-2 index */
        u8      vb_part;                /* plane index for co-panar formats */
 };
 
  */
 #define VPE_PORT_LUMA1_IN      0
 #define VPE_PORT_CHROMA1_IN    1
+#define VPE_PORT_LUMA2_IN      2
+#define VPE_PORT_CHROMA2_IN    3
+#define VPE_PORT_LUMA3_IN      4
+#define VPE_PORT_CHROMA3_IN    5
+#define VPE_PORT_MV_IN         6
+#define VPE_PORT_MV_OUT                7
 #define VPE_PORT_LUMA_OUT      8
 #define VPE_PORT_CHROMA_OUT    9
 #define VPE_PORT_RGB_OUT       10
 static const struct vpe_port_data port_data[11] = {
        [VPE_PORT_LUMA1_IN] = {
                .channel        = VPE_CHAN_LUMA1_IN,
+               .vb_index       = 0,
                .vb_part        = VPE_LUMA,
        },
        [VPE_PORT_CHROMA1_IN] = {
                .channel        = VPE_CHAN_CHROMA1_IN,
+               .vb_index       = 0,
+               .vb_part        = VPE_CHROMA,
+       },
+       [VPE_PORT_LUMA2_IN] = {
+               .channel        = VPE_CHAN_LUMA2_IN,
+               .vb_index       = 1,
+               .vb_part        = VPE_LUMA,
+       },
+       [VPE_PORT_CHROMA2_IN] = {
+               .channel        = VPE_CHAN_CHROMA2_IN,
+               .vb_index       = 1,
+               .vb_part        = VPE_CHROMA,
+       },
+       [VPE_PORT_LUMA3_IN] = {
+               .channel        = VPE_CHAN_LUMA3_IN,
+               .vb_index       = 2,
+               .vb_part        = VPE_LUMA,
+       },
+       [VPE_PORT_CHROMA3_IN] = {
+               .channel        = VPE_CHAN_CHROMA3_IN,
+               .vb_index       = 2,
                .vb_part        = VPE_CHROMA,
        },
+       [VPE_PORT_MV_IN] = {
+               .channel        = VPE_CHAN_MV_IN,
+       },
+       [VPE_PORT_MV_OUT] = {
+               .channel        = VPE_CHAN_MV_OUT,
+       },
        [VPE_PORT_LUMA_OUT] = {
                .channel        = VPE_CHAN_LUMA_OUT,
                .vb_part        = VPE_LUMA,
        unsigned int            height;                         /* frame height */
        unsigned int            bytesperline[VPE_MAX_PLANES];   /* bytes per line in memory */
        enum v4l2_colorspace    colorspace;
+       enum v4l2_field         field;                          /* supported field value */
        unsigned int            flags;
        unsigned int            sizeimage[VPE_MAX_PLANES];      /* image size in memory */
        struct v4l2_rect        c_rect;                         /* crop/compose rectangle */
 /* vpe_q_data flag bits */
 #define        Q_DATA_FRAME_1D         (1 << 0)
 #define        Q_DATA_MODE_TILED       (1 << 1)
+#define        Q_DATA_INTERLACED       (1 << 2)
 
 enum {
        Q_DATA_SRC = 0,
        struct v4l2_m2m_ctx     *m2m_ctx;
        struct v4l2_ctrl_handler hdl;
 
+       unsigned int            field;                  /* current field */
        unsigned int            sequence;               /* current frame/field seq */
        unsigned int            aborting;               /* abort after next irq */
 
        unsigned int            bufs_completed;         /* bufs done in this batch */
 
        struct vpe_q_data       q_data[2];              /* src & dst queue data */
-       struct vb2_buffer       *src_vb;
+       struct vb2_buffer       *src_vbs[VPE_MAX_SRC_BUFS];
        struct vb2_buffer       *dst_vb;
 
+       dma_addr_t              mv_buf_dma[2];          /* dma addrs of motion vector in/out bufs */
+       void                    *mv_buf[2];             /* virtual addrs of motion vector bufs */
+       size_t                  mv_buf_size;            /* current motion vector buffer size */
        struct vpdma_buf        mmr_adb;                /* shadow reg addr/data block */
        struct vpdma_desc_list  desc_list;              /* DMA descriptor list */
 
+       bool                    deinterlacing;          /* using de-interlacer */
        bool                    load_mmrs;              /* have new shadow reg values */
+
+       unsigned int            src_mv_buf_selector;
 };
 
 
        struct vpdma_adb_hdr    us3_hdr;
        u32                     us3_regs[8];
        struct vpdma_adb_hdr    dei_hdr;
-       u32                     dei_regs[1];
-       u32                     dei_pad[3];
+       u32                     dei_regs[8];
        struct vpdma_adb_hdr    sc_hdr;
        u32                     sc_regs[1];
        u32                     sc_pad[3];
        VPE_SET_MMR_ADB_HDR(ctx, csc_hdr, csc_regs, VPE_CSC_CSC00);
 };
 
+/*
+ * Allocate or re-allocate the motion vector DMA buffers
+ * There are two buffers, one for input and one for output.
+ * However, the roles are reversed after each field is processed.
+ * In other words, after each field is processed, the previous
+ * output (dst) MV buffer becomes the new input (src) MV buffer.
+ */
+static int realloc_mv_buffers(struct vpe_ctx *ctx, size_t size)
+{
+       struct device *dev = ctx->dev->v4l2_dev.dev;
+
+       if (ctx->mv_buf_size == size)
+               return 0;
+
+       if (ctx->mv_buf[0])
+               dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[0],
+                       ctx->mv_buf_dma[0]);
+
+       if (ctx->mv_buf[1])
+               dma_free_coherent(dev, ctx->mv_buf_size, ctx->mv_buf[1],
+                       ctx->mv_buf_dma[1]);
+
+       if (size == 0)
+               return 0;
+
+       ctx->mv_buf[0] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[0],
+                               GFP_KERNEL);
+       if (!ctx->mv_buf[0]) {
+               vpe_err(ctx->dev, "failed to allocate motion vector buffer\n");
+               return -ENOMEM;
+       }
+
+       ctx->mv_buf[1] = dma_alloc_coherent(dev, size, &ctx->mv_buf_dma[1],
+                               GFP_KERNEL);
+       if (!ctx->mv_buf[1]) {
+               vpe_err(ctx->dev, "failed to allocate motion vector buffer\n");
+               dma_free_coherent(dev, size, ctx->mv_buf[0],
+                       ctx->mv_buf_dma[0]);
+
+               return -ENOMEM;
+       }
+
+       ctx->mv_buf_size = size;
+       ctx->src_mv_buf_selector = 0;
+
+       return 0;
+}
+
+static void free_mv_buffers(struct vpe_ctx *ctx)
+{
+       realloc_mv_buffers(ctx, 0);
+}
+
+/*
+ * While de-interlacing, we keep the two most recent input buffers
+ * around.  This function frees those two buffers when we have
+ * finished processing the current stream.
+ */
+static void free_vbs(struct vpe_ctx *ctx)
+{
+       struct vpe_dev *dev = ctx->dev;
+       unsigned long flags;
+
+       if (ctx->src_vbs[2] == NULL)
+               return;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (ctx->src_vbs[2]) {
+               v4l2_m2m_buf_done(ctx->src_vbs[2], VB2_BUF_STATE_DONE);
+               v4l2_m2m_buf_done(ctx->src_vbs[1], VB2_BUF_STATE_DONE);
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+}
+
 /*
  * Enable or disable the VPE clocks
  */
 static void set_us_coefficients(struct vpe_ctx *ctx)
 {
        struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+       struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
        u32 *us1_reg = &mmr_adb->us1_regs[0];
        u32 *us2_reg = &mmr_adb->us2_regs[0];
        u32 *us3_reg = &mmr_adb->us3_regs[0];
 
        cp = &us_coeffs[0].anchor_fid0_c0;
 
+       if (s_q_data->flags & Q_DATA_INTERLACED)        /* interlaced */
+               cp += sizeof(us_coeffs[0]) / sizeof(*cp);
+
        end_cp = cp + sizeof(us_coeffs[0]) / sizeof(*cp);
 
        while (cp < end_cp) {
 
        /* regs for now */
        vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA1_IN);
+       vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA2_IN);
+       vpdma_set_line_mode(ctx->dev->vpdma, line_mode, VPE_CHAN_CHROMA3_IN);
 
        /* frame start for input luma */
        vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
                VPE_CHAN_LUMA1_IN);
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_LUMA2_IN);
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_LUMA3_IN);
 
        /* frame start for input chroma */
        vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
                VPE_CHAN_CHROMA1_IN);
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_CHROMA2_IN);
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_CHROMA3_IN);
+
+       /* frame start for MV in client */
+       vpdma_set_frame_start_event(ctx->dev->vpdma, VPDMA_FSEVENT_CHANNEL_ACTIVE,
+               VPE_CHAN_MV_IN);
 
        ctx->load_mmrs = true;
 }
 /*
  * Set the de-interlacer shadow register values
  */
-static void set_dei_regs_bypass(struct vpe_ctx *ctx)
+static void set_dei_regs(struct vpe_ctx *ctx)
 {
        struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
        struct vpe_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
        unsigned int src_h = s_q_data->c_rect.height;
        unsigned int src_w = s_q_data->c_rect.width;
        u32 *dei_mmr0 = &mmr_adb->dei_regs[0];
+       bool deinterlace = true;
        u32 val = 0;
 
        /*
         * for both progressive and interlace content in interlace bypass mode.
         * It has been recommended not to use progressive bypass mode.
         */
-       val = VPE_DEI_INTERLACE_BYPASS;
+       if ((!ctx->deinterlacing && (s_q_data->flags & Q_DATA_INTERLACED)) ||
+                       !(s_q_data->flags & Q_DATA_INTERLACED)) {
+               deinterlace = false;
+               val = VPE_DEI_INTERLACE_BYPASS;
+       }
+
+       src_h = deinterlace ? src_h * 2 : src_h;
 
        val |= (src_h << VPE_DEI_HEIGHT_SHIFT) |
                (src_w << VPE_DEI_WIDTH_SHIFT) |
        ctx->load_mmrs = true;
 }
 
+static void set_dei_shadow_registers(struct vpe_ctx *ctx)
+{
+       struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
+       u32 *dei_mmr = &mmr_adb->dei_regs[0];
+       const struct vpe_dei_regs *cur = &dei_regs;
+
+       dei_mmr[2]  = cur->mdt_spacial_freq_thr_reg;
+       dei_mmr[3]  = cur->edi_config_reg;
+       dei_mmr[4]  = cur->edi_lut_reg0;
+       dei_mmr[5]  = cur->edi_lut_reg1;
+       dei_mmr[6]  = cur->edi_lut_reg2;
+       dei_mmr[7]  = cur->edi_lut_reg3;
+
+       ctx->load_mmrs = true;
+}
+
 static void set_csc_coeff_bypass(struct vpe_ctx *ctx)
 {
        struct vpe_mmr_adb *mmr_adb = ctx->mmr_adb.addr;
  */
 static int set_srcdst_params(struct vpe_ctx *ctx)
 {
+       struct vpe_q_data *s_q_data =  &ctx->q_data[Q_DATA_SRC];
+       struct vpe_q_data *d_q_data =  &ctx->q_data[Q_DATA_DST];
+       size_t mv_buf_size;
+       int ret;
+
        ctx->sequence = 0;
+       ctx->field = V4L2_FIELD_TOP;
+
+       if ((s_q_data->flags & Q_DATA_INTERLACED) &&
+                       !(d_q_data->flags & Q_DATA_INTERLACED)) {
+               const struct vpdma_data_format *mv =
+                       &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
+
+               ctx->deinterlacing = 1;
+               mv_buf_size =
+                       (s_q_data->width * s_q_data->height * mv->depth) >> 3;
+       } else {
+               ctx->deinterlacing = 0;
+               mv_buf_size = 0;
+       }
+
+       free_vbs(ctx);
+
+       ret = realloc_mv_buffers(ctx, mv_buf_size);
+       if (ret)
+               return ret;
 
        set_cfg_and_line_modes(ctx);
-       set_dei_regs_bypass(ctx);
+       set_dei_regs(ctx);
        set_csc_coeff_bypass(ctx);
        set_sc_regs_bypass(ctx);
 
        struct vpe_ctx *ctx = priv;
        int needed = ctx->bufs_per_job;
 
+       if (ctx->deinterlacing && ctx->src_vbs[2] == NULL)
+               needed += 2;    /* need additional two most recent fields */
+
        if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed)
                return 0;
 
        struct v4l2_rect *c_rect = &q_data->c_rect;
        struct vpe_fmt *fmt = q_data->fmt;
        const struct vpdma_data_format *vpdma_fmt;
-       int plane = fmt->coplanar ? p_data->vb_part : 0;
+       int mv_buf_selector = !ctx->src_mv_buf_selector;
        dma_addr_t dma_addr;
        u32 flags = 0;
 
-       vpdma_fmt = fmt->vpdma_fmt[plane];
-       dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
-       if (!dma_addr) {
-               vpe_err(ctx->dev,
-                       "acquiring output buffer(%d) dma_addr failed\n",
-                       port);
-               return;
+       if (port == VPE_PORT_MV_OUT) {
+               vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
+               dma_addr = ctx->mv_buf_dma[mv_buf_selector];
+       } else {
+               /* to incorporate interleaved formats */
+               int plane = fmt->coplanar ? p_data->vb_part : 0;
+
+               vpdma_fmt = fmt->vpdma_fmt[plane];
+               dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
+               if (!dma_addr) {
+                       vpe_err(ctx->dev,
+                               "acquiring output buffer(%d) dma_addr failed\n",
+                               port);
+                       return;
+               }
        }
 
        if (q_data->flags & Q_DATA_FRAME_1D)
 {
        struct vpe_q_data *q_data = &ctx->q_data[Q_DATA_SRC];
        const struct vpe_port_data *p_data = &port_data[port];
-       struct vb2_buffer *vb = ctx->src_vb;
+       struct vb2_buffer *vb = ctx->src_vbs[p_data->vb_index];
        struct v4l2_rect *c_rect = &q_data->c_rect;
        struct vpe_fmt *fmt = q_data->fmt;
        const struct vpdma_data_format *vpdma_fmt;
-       int plane = fmt->coplanar ? p_data->vb_part : 0;
-       int field = 0;
+       int mv_buf_selector = ctx->src_mv_buf_selector;
+       int field = vb->v4l2_buf.field == V4L2_FIELD_BOTTOM;
        dma_addr_t dma_addr;
        u32 flags = 0;
 
-       vpdma_fmt = fmt->vpdma_fmt[plane];
+       if (port == VPE_PORT_MV_IN) {
+               vpdma_fmt = &vpdma_misc_fmts[VPDMA_DATA_FMT_MV];
+               dma_addr = ctx->mv_buf_dma[mv_buf_selector];
+       } else {
+               /* to incorporate interleaved formats */
+               int plane = fmt->coplanar ? p_data->vb_part : 0;
 
-       dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
-       if (!dma_addr) {
-               vpe_err(ctx->dev,
-                       "acquiring input buffer(%d) dma_addr failed\n",
-                       port);
-               return;
+               vpdma_fmt = fmt->vpdma_fmt[plane];
+
+               dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane);
+               if (!dma_addr) {
+                       vpe_err(ctx->dev,
+                               "acquiring input buffer(%d) dma_addr failed\n",
+                               port);
+                       return;
+               }
        }
 
        if (q_data->flags & Q_DATA_FRAME_1D)
 static void enable_irqs(struct vpe_ctx *ctx)
 {
        write_reg(ctx->dev, VPE_INT0_ENABLE0_SET, VPE_INT0_LIST0_COMPLETE);
-       write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DS1_UV_ERROR_INT);
+       write_reg(ctx->dev, VPE_INT0_ENABLE1_SET, VPE_DEI_ERROR_INT |
+                               VPE_DS1_UV_ERROR_INT);
 
        vpdma_enable_list_complete_irq(ctx->dev->vpdma, 0, true);
 }
        struct vpe_ctx *ctx = priv;
        struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
 
-       ctx->src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
-       WARN_ON(ctx->src_vb == NULL);
+       if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) {
+               ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+               WARN_ON(ctx->src_vbs[2] == NULL);
+               ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+               WARN_ON(ctx->src_vbs[1] == NULL);
+       }
+
+       ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+       WARN_ON(ctx->src_vbs[0] == NULL);
        ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
        WARN_ON(ctx->dst_vb == NULL);
 
                ctx->load_mmrs = false;
        }
 
+       /* output data descriptors */
+       if (ctx->deinterlacing)
+               add_out_dtd(ctx, VPE_PORT_MV_OUT);
+
        add_out_dtd(ctx, VPE_PORT_LUMA_OUT);
        if (d_q_data->fmt->coplanar)
                add_out_dtd(ctx, VPE_PORT_CHROMA_OUT);
 
+       /* input data descriptors */
+       if (ctx->deinterlacing) {
+               add_in_dtd(ctx, VPE_PORT_LUMA3_IN);
+               add_in_dtd(ctx, VPE_PORT_CHROMA3_IN);
+
+               add_in_dtd(ctx, VPE_PORT_LUMA2_IN);
+               add_in_dtd(ctx, VPE_PORT_CHROMA2_IN);
+       }
+
        add_in_dtd(ctx, VPE_PORT_LUMA1_IN);
        add_in_dtd(ctx, VPE_PORT_CHROMA1_IN);
 
+       if (ctx->deinterlacing)
+               add_in_dtd(ctx, VPE_PORT_MV_IN);
+
        /* sync on channel control descriptors for input ports */
        vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA1_IN);
        vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA1_IN);
 
+       if (ctx->deinterlacing) {
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+                       VPE_CHAN_LUMA2_IN);
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+                       VPE_CHAN_CHROMA2_IN);
+
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+                       VPE_CHAN_LUMA3_IN);
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list,
+                       VPE_CHAN_CHROMA3_IN);
+
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_IN);
+       }
+
        /* sync on channel control descriptors for output ports */
        vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_LUMA_OUT);
        if (d_q_data->fmt->coplanar)
                vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_CHROMA_OUT);
 
+       if (ctx->deinterlacing)
+               vpdma_add_sync_on_channel_ctd(&ctx->desc_list, VPE_CHAN_MV_OUT);
+
        enable_irqs(ctx);
 
        vpdma_map_desc_buf(ctx->dev->vpdma, &ctx->desc_list.buf);
        vpdma_submit_descs(ctx->dev->vpdma, &ctx->desc_list);
 }
 
+static void dei_error(struct vpe_ctx *ctx)
+{
+       dev_warn(ctx->dev->v4l2_dev.dev,
+               "received DEI error interrupt\n");
+}
+
 static void ds1_uv_error(struct vpe_ctx *ctx)
 {
        dev_warn(ctx->dev->v4l2_dev.dev,
 {
        struct vpe_dev *dev = (struct vpe_dev *)data;
        struct vpe_ctx *ctx;
+       struct vpe_q_data *d_q_data;
        struct vb2_buffer *s_vb, *d_vb;
        struct v4l2_buffer *s_buf, *d_buf;
        unsigned long flags;
                goto handled;
        }
 
-       if (irqst1 & VPE_DS1_UV_ERROR_INT) {
-               irqst1 &= ~VPE_DS1_UV_ERROR_INT;
-               ds1_uv_error(ctx);
+       if (irqst1) {
+               if (irqst1 & VPE_DEI_ERROR_INT) {
+                       irqst1 &= ~VPE_DEI_ERROR_INT;
+                       dei_error(ctx);
+               }
+               if (irqst1 & VPE_DS1_UV_ERROR_INT) {
+                       irqst1 &= ~VPE_DS1_UV_ERROR_INT;
+                       ds1_uv_error(ctx);
+               }
        }
 
        if (irqst0) {
 
        vpdma_reset_desc_list(&ctx->desc_list);
 
+        /* the previous dst mv buffer becomes the next src mv buffer */
+       ctx->src_mv_buf_selector = !ctx->src_mv_buf_selector;
+
        if (ctx->aborting)
                goto finished;
 
-       s_vb = ctx->src_vb;
+       s_vb = ctx->src_vbs[0];
        d_vb = ctx->dst_vb;
        s_buf = &s_vb->v4l2_buf;
        d_buf = &d_vb->v4l2_buf;
                d_buf->flags |= V4L2_BUF_FLAG_TIMECODE;
                d_buf->timecode = s_buf->timecode;
        }
-
        d_buf->sequence = ctx->sequence;
+       d_buf->field = ctx->field;
+
+       d_q_data = &ctx->q_data[Q_DATA_DST];
+       if (d_q_data->flags & Q_DATA_INTERLACED) {
+               if (ctx->field == V4L2_FIELD_BOTTOM) {
+                       ctx->sequence++;
+                       ctx->field = V4L2_FIELD_TOP;
+               } else {
+                       WARN_ON(ctx->field != V4L2_FIELD_TOP);
+                       ctx->field = V4L2_FIELD_BOTTOM;
+               }
+       } else {
+               ctx->sequence++;
+       }
 
-       ctx->sequence++;
+       if (ctx->deinterlacing)
+               s_vb = ctx->src_vbs[2];
 
        spin_lock_irqsave(&dev->lock, flags);
        v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE);
        v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE);
        spin_unlock_irqrestore(&dev->lock, flags);
 
+       if (ctx->deinterlacing) {
+               ctx->src_vbs[2] = ctx->src_vbs[1];
+               ctx->src_vbs[1] = ctx->src_vbs[0];
+       }
+
        ctx->bufs_completed++;
        if (ctx->bufs_completed < ctx->bufs_per_job) {
                device_run(ctx);
        pix->width = q_data->width;
        pix->height = q_data->height;
        pix->pixelformat = q_data->fmt->fourcc;
+       pix->field = q_data->field;
 
        if (V4L2_TYPE_IS_OUTPUT(f->type)) {
                pix->colorspace = q_data->colorspace;
                return -EINVAL;
        }
 
-       pix->field = V4L2_FIELD_NONE;
+       if (pix->field != V4L2_FIELD_NONE && pix->field != V4L2_FIELD_ALTERNATE)
+               pix->field = V4L2_FIELD_NONE;
 
        v4l_bound_align_image(&pix->width, MIN_W, MAX_W, W_ALIGN,
                              &pix->height, MIN_H, MAX_H, H_ALIGN,
        q_data->width           = pix->width;
        q_data->height          = pix->height;
        q_data->colorspace      = pix->colorspace;
+       q_data->field           = pix->field;
 
        for (i = 0; i < pix->num_planes; i++) {
                plane_fmt = &pix->plane_fmt[i];
        q_data->c_rect.width    = q_data->width;
        q_data->c_rect.height   = q_data->height;
 
+       if (q_data->field == V4L2_FIELD_ALTERNATE)
+               q_data->flags |= Q_DATA_INTERLACED;
+       else
+               q_data->flags &= ~Q_DATA_INTERLACED;
+
        vpe_dbg(ctx->dev, "Setting format for type %d, wxh: %dx%d, fmt: %d bpl_y %d",
                f->type, q_data->width, q_data->height, q_data->fmt->fourcc,
                q_data->bytesperline[VPE_LUMA]);
        s_q_data->sizeimage[VPE_LUMA] = (s_q_data->width * s_q_data->height *
                        s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3;
        s_q_data->colorspace = V4L2_COLORSPACE_SMPTE240M;
+       s_q_data->field = V4L2_FIELD_NONE;
        s_q_data->c_rect.left = 0;
        s_q_data->c_rect.top = 0;
        s_q_data->c_rect.width = s_q_data->width;
 
        ctx->q_data[Q_DATA_DST] = *s_q_data;
 
+       set_dei_shadow_registers(ctx);
        set_src_registers(ctx);
        set_dst_registers(ctx);
        ret = set_srcdst_params(ctx);
        vpe_dbg(dev, "releasing instance %p\n", ctx);
 
        mutex_lock(&dev->dev_mutex);
+       free_vbs(ctx);
+       free_mv_buffers(ctx);
        vpdma_free_desc_list(&ctx->desc_list);
        vpdma_free_desc_buf(&ctx->mmr_adb);