#include <media/videobuf2-vmalloc.h>
 
 #include "coda.h"
+#include "imx-vdoa.h"
 #define CREATE_TRACE_POINTS
 #include "trace.h"
 
        u32 val;
        int ret;
 
+       v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+                "Video Data Order Adapter: %s\n",
+                ctx->use_vdoa ? "Enabled" : "Disabled");
+
        /* Start decoding */
        q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
        q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
        if (dst_fourcc == V4L2_PIX_FMT_NV12)
                ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE;
        if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP)
-               ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR;
+               ctx->frame_mem_ctrl |= (0x3 << 9) |
+                       ((ctx->use_vdoa) ? 0 : CODA9_FRAME_TILED2LINEAR);
        coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL);
 
        ctx->display_idx = -1;
                 __func__, ctx->idx, width, height);
 
        ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED);
+       /*
+        * If the VDOA is used, the decoder needs one additional frame,
+        * because the frames are freed when the next frame is decoded.
+        * Otherwise there are visible errors in the decoded frames (green
+        * regions in displayed frames) and a broken order of frames (earlier
+        * frames are sporadically displayed after later frames).
+        */
+       if (ctx->use_vdoa)
+               ctx->num_internal_frames += 1;
        if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) {
                v4l2_err(&dev->v4l2_dev,
                         "not enough framebuffers to decode (%d < %d)\n",
        struct coda_q_data *q_data_dst;
        struct coda_buffer_meta *meta;
        unsigned long flags;
+       u32 rot_mode = 0;
        u32 reg_addr, reg_stride;
 
        dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
        if (dev->devtype->product == CODA_960)
                coda_set_gdi_regs(ctx);
 
-       if (dev->devtype->product == CODA_960) {
-               /*
-                * The CODA960 seems to have an internal list of buffers with
-                * 64 entries that includes the registered frame buffers as
-                * well as the rotator buffer output.
-                * ROT_INDEX needs to be < 0x40, but > ctx->num_internal_frames.
-                */
-               coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->vb2_buf.index,
-                               CODA9_CMD_DEC_PIC_ROT_INDEX);
-
-               reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y;
-               reg_stride = CODA9_CMD_DEC_PIC_ROT_STRIDE;
+       if (ctx->use_vdoa &&
+           ctx->display_idx >= 0 &&
+           ctx->display_idx < ctx->num_internal_frames) {
+               vdoa_device_run(ctx->vdoa,
+                               vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0),
+                               ctx->internal_frames[ctx->display_idx].paddr);
        } else {
-               reg_addr = CODA_CMD_DEC_PIC_ROT_ADDR_Y;
-               reg_stride = CODA_CMD_DEC_PIC_ROT_STRIDE;
+               if (dev->devtype->product == CODA_960) {
+                       /*
+                        * The CODA960 seems to have an internal list of
+                        * buffers with 64 entries that includes the
+                        * registered frame buffers as well as the rotator
+                        * buffer output.
+                        *
+                        * ROT_INDEX needs to be < 0x40, but >
+                        * ctx->num_internal_frames.
+                        */
+                       coda_write(dev,
+                                  CODA_MAX_FRAMEBUFFERS + dst_buf->vb2_buf.index,
+                                  CODA9_CMD_DEC_PIC_ROT_INDEX);
+
+                       reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y;
+                       reg_stride = CODA9_CMD_DEC_PIC_ROT_STRIDE;
+               } else {
+                       reg_addr = CODA_CMD_DEC_PIC_ROT_ADDR_Y;
+                       reg_stride = CODA_CMD_DEC_PIC_ROT_STRIDE;
+               }
+               coda_write_base(ctx, q_data_dst, dst_buf, reg_addr);
+               coda_write(dev, q_data_dst->bytesperline, reg_stride);
+
+               rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode;
        }
-       coda_write_base(ctx, q_data_dst, dst_buf, reg_addr);
-       coda_write(dev, q_data_dst->bytesperline, reg_stride);
 
-       coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
-                       CODA_CMD_DEC_PIC_ROT_MODE);
+       coda_write(dev, rot_mode, CODA_CMD_DEC_PIC_ROT_MODE);
 
        switch (dev->devtype->product) {
        case CODA_DX6:
        u32 src_fourcc;
        int success;
        u32 err_mb;
+       int err_vdoa = 0;
        u32 val;
 
        /* Update kfifo out pointer from coda bitstream read pointer */
                }
        }
 
+       /* Wait until the VDOA finished writing the previous display frame */
+       if (ctx->use_vdoa &&
+           ctx->display_idx >= 0 &&
+           ctx->display_idx < ctx->num_internal_frames) {
+               err_vdoa = vdoa_wait_for_completion(ctx->vdoa);
+       }
+
        ctx->frm_dis_flg = coda_read(dev,
                                     CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
 
-       /*
-        * The previous display frame was copied out by the rotator,
-        * now it can be overwritten again
-        */
+       /* The previous display frame was copied out and can be overwritten */
        if (ctx->display_idx >= 0 &&
            ctx->display_idx < ctx->num_internal_frames) {
                ctx->frm_dis_flg &= ~(1 << ctx->display_idx);
                }
                vb2_set_plane_payload(&dst_buf->vb2_buf, 0, payload);
 
-               coda_m2m_buf_done(ctx, dst_buf, ctx->frame_errors[ctx->display_idx] ?
-                                 VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+               if (ctx->frame_errors[ctx->display_idx] || err_vdoa)
+                       coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_ERROR);
+               else
+                       coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE);
 
                v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
                        "job finished: decoding frame (%d) (%s)\n",
 
 #include <media/videobuf2-vmalloc.h>
 
 #include "coda.h"
+#include "imx-vdoa.h"
 
 #define CODA_NAME              "coda"
 
 module_param(disable_tiling, int, 0644);
 MODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers");
 
+static int disable_vdoa;
+module_param(disable_vdoa, int, 0644);
+MODULE_PARM_DESC(disable_vdoa, "Disable Video Data Order Adapter tiled to raster-scan conversion");
+
 void coda_write(struct coda_dev *dev, u32 data, u32 reg)
 {
        v4l2_dbg(2, coda_debug, &dev->v4l2_dev,
        }
 }
 
+static struct vdoa_data *coda_get_vdoa_data(void)
+{
+       struct device_node *vdoa_node;
+       struct platform_device *vdoa_pdev;
+       struct vdoa_data *vdoa_data = NULL;
+
+       vdoa_node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-vdoa");
+       if (!vdoa_node)
+               return NULL;
+
+       vdoa_pdev = of_find_device_by_node(vdoa_node);
+       if (!vdoa_pdev)
+               goto out;
+
+       vdoa_data = platform_get_drvdata(vdoa_pdev);
+       if (!vdoa_data)
+               vdoa_data = ERR_PTR(-EPROBE_DEFER);
+
+out:
+       if (vdoa_node)
+               of_node_put(vdoa_node);
+
+       return vdoa_data;
+}
+
 /*
  * V4L2 ioctl() operations.
  */
        return 0;
 }
 
+static int coda_try_fmt_vdoa(struct coda_ctx *ctx, struct v4l2_format *f,
+                            bool *use_vdoa)
+{
+       int err;
+
+       if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       if (!use_vdoa)
+               return -EINVAL;
+
+       if (!ctx->vdoa) {
+               *use_vdoa = false;
+               return 0;
+       }
+
+       err = vdoa_context_configure(NULL, f->fmt.pix.width, f->fmt.pix.height,
+                                    f->fmt.pix.pixelformat);
+       if (err) {
+               *use_vdoa = false;
+               return 0;
+       }
+
+       *use_vdoa = true;
+       return 0;
+}
+
 static unsigned int coda_estimate_sizeimage(struct coda_ctx *ctx, u32 sizeimage,
                                            u32 width, u32 height)
 {
        const struct coda_codec *codec;
        struct vb2_queue *src_vq;
        int ret;
+       bool use_vdoa;
 
        ret = coda_try_pixelformat(ctx, f);
        if (ret < 0)
                f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
                f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
                                       f->fmt.pix.height * 3 / 2;
+
+               ret = coda_try_fmt_vdoa(ctx, f, &use_vdoa);
+               if (ret < 0)
+                       return ret;
        }
 
        return 0;
 
        switch (f->fmt.pix.pixelformat) {
        case V4L2_PIX_FMT_NV12:
-               if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
-                       ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
-                       if (!disable_tiling)
-                               break;
-               }
+               ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
+               if (!disable_tiling)
+                       break;
                /* else fall through */
        case V4L2_PIX_FMT_YUV420:
        case V4L2_PIX_FMT_YVU420:
                break;
        }
 
+       if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP &&
+           !coda_try_fmt_vdoa(ctx, f, &ctx->use_vdoa) &&
+           ctx->use_vdoa)
+               vdoa_context_configure(ctx->vdoa, f->fmt.pix.width,
+                                      f->fmt.pix.height,
+                                      f->fmt.pix.pixelformat);
+       else
+               ctx->use_vdoa = false;
+
        v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
                "Setting format for type %d, wxh: %dx%d, fmt: %4.4s %c\n",
                f->type, q_data->width, q_data->height,
                bool stream_end = ctx->bit_stream_param &
                                  CODA_BIT_STREAM_END_FLAG;
                int num_metas = ctx->num_metas;
+               unsigned int count;
+
+               count = hweight32(ctx->frm_dis_flg);
+               if (ctx->use_vdoa && count >= (ctx->num_internal_frames - 1)) {
+                       v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+                                "%d: not ready: all internal buffers in use: %d/%d (0x%x)",
+                                ctx->idx, count, ctx->num_internal_frames,
+                                ctx->frm_dis_flg);
+                       return 0;
+               }
 
                if (ctx->hold && !src_bufs) {
                        v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
        default:
                ctx->reg_idx = idx;
        }
+       if (ctx->dev->vdoa && !disable_vdoa) {
+               ctx->vdoa = vdoa_context_create(dev->vdoa);
+               if (!ctx->vdoa)
+                       v4l2_warn(&dev->v4l2_dev,
+                                 "Failed to create vdoa context: not using vdoa");
+       }
+       ctx->use_vdoa = false;
 
        /* Power up and upload firmware if necessary */
        ret = pm_runtime_get_sync(&dev->plat_dev->dev);
        /* If this instance is running, call .job_abort and wait for it to end */
        v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
 
+       if (ctx->vdoa)
+               vdoa_context_destroy(ctx->vdoa);
+
        /* In case the instance was not running, we still need to call SEQ_END */
        if (ctx->ops->seq_end_work) {
                queue_work(dev->workqueue, &ctx->seq_end_work);
        }
        dev->iram_pool = pool;
 
+       /* Get vdoa_data if supported by the platform */
+       dev->vdoa = coda_get_vdoa_data();
+       if (PTR_ERR(dev->vdoa) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
        ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
        if (ret)
                return ret;