diff mbox series

[FFmpeg-devel,v2] avcodec/v4l2_m2m_dec: export v4l2 buffer dma-buf

Message ID 20211119091106.23046-1-ming.qian@nxp.com
State New
Headers show
Series [FFmpeg-devel,v2] avcodec/v4l2_m2m_dec: export v4l2 buffer dma-buf
Related show

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished

Commit Message

Ming Qian Nov. 19, 2021, 9:11 a.m. UTC
if the v4l2 buffer is supported to export dma-buf,
then we can report it to AV_PIX_FMT_DRM_PRIME,
so the caller can pass it to other hardware device,
such as display it directly without copy frame data.

Signed-off-by: Ming Qian <ming.qian@nxp.com>
---
 libavcodec/v4l2_buffers.c | 115 +++++++++++++++++++++++++++++++++++++-
 libavcodec/v4l2_buffers.h |   2 +
 libavcodec/v4l2_context.c |  53 ++++++++++++++++++
 libavcodec/v4l2_context.h |  17 ++++++
 libavcodec/v4l2_fmt.c     |  96 ++++++++++++++++++-------------
 libavcodec/v4l2_fmt.h     |   1 +
 libavcodec/v4l2_m2m_dec.c |  20 +++++++
 7 files changed, 263 insertions(+), 41 deletions(-)
diff mbox series

Patch

diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c
index 4b2679eb3814..2f695fa269df 100644
--- a/libavcodec/v4l2_buffers.c
+++ b/libavcodec/v4l2_buffers.c
@@ -21,18 +21,24 @@ 
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
 #include <linux/videodev2.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <poll.h>
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_drm.h"
 #include "libavcodec/avcodec.h"
 #include "libavcodec/internal.h"
 #include "libavutil/pixdesc.h"
 #include "v4l2_context.h"
 #include "v4l2_buffers.h"
 #include "v4l2_m2m.h"
+#include "v4l2_fmt.h"
 
 #define USEC_PER_SEC 1000000
 static AVRational v4l2_timebase = { 1, USEC_PER_SEC };
@@ -210,7 +216,7 @@  static enum AVColorTransferCharacteristic v4l2_get_color_trc(V4L2Buffer *buf)
     return AVCOL_TRC_UNSPECIFIED;
 }
 
-static void v4l2_free_buffer(void *opaque, uint8_t *unused)
+static void v4l2_free_buffer(void *opaque, uint8_t *data)
 {
     V4L2Buffer* avbuf = opaque;
     V4L2m2mContext *s = buf_to_m2mctx(avbuf);
@@ -230,6 +236,12 @@  static void v4l2_free_buffer(void *opaque, uint8_t *unused)
                 ff_v4l2_buffer_enqueue(avbuf);
         }
 
+        if (avbuf->hwctx_ref) {
+            AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)data;
+
+            av_buffer_unref(&avbuf->hwctx_ref);
+            av_free(desc);
+        }
         av_buffer_unref(&avbuf->context_ref);
     }
 }
@@ -338,6 +350,90 @@  static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf)
     return 0;
 }
 
+static int v4l2_buffer_buf_to_hwframe(AVFrame *frame, V4L2Buffer *avbuf)
+{
+    V4L2Context *ctx = avbuf->context;
+    AVDRMFrameDescriptor *desc = NULL;
+    AVDRMLayerDescriptor *layer = NULL;
+    int i;
+    int ret;
+
+    if (!ctx->hwframes)
+        return AVERROR(EINVAL);
+
+    for (i = 0; i < avbuf->num_planes; i++) {
+        if (avbuf->plane_info[i].dmafd < 0)
+            return AVERROR(EINVAL);
+    }
+
+    desc = av_mallocz(sizeof(AVDRMFrameDescriptor));
+    if (!desc)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < avbuf->num_planes; i++) {
+        desc->objects[i].fd = avbuf->plane_info[i].dmafd;
+        desc->objects[i].size = avbuf->plane_info[i].length;
+    }
+    desc->nb_objects = avbuf->num_planes;
+
+    desc->nb_layers = 1;
+    layer = &desc->layers[0];
+    layer->format = ff_v4l2_format_avfmt_to_drm(avbuf->context->av_pix_fmt);
+    layer->nb_planes = avbuf->num_planes;
+    for (i = 0; i < avbuf->num_planes; i++) {
+        layer->planes[i].object_index = i;
+        layer->planes[i].offset = 0;
+        layer->planes[i].pitch = avbuf->plane_info[i].bytesperline;
+    }
+
+    /* fixup special cases */
+    switch (avbuf->context->av_pix_fmt) {
+    case AV_PIX_FMT_NV12:
+    case AV_PIX_FMT_NV21:
+        if (avbuf->num_planes > 1)
+            break;
+        layer->nb_planes = 2;
+        layer->planes[1].object_index = 0;
+        layer->planes[1].offset = avbuf->plane_info[0].bytesperline * avbuf->context->format.fmt.pix_mp.height;
+        layer->planes[1].pitch = avbuf->plane_info[0].bytesperline;
+        break;
+
+    case AV_PIX_FMT_YUV420P:
+        if (avbuf->num_planes > 1)
+            break;
+        layer->nb_planes = 3;
+        layer->planes[1].object_index = 0;
+        layer->planes[2].object_index = 0;
+        layer->planes[1].offset = avbuf->plane_info[0].bytesperline * avbuf->context->format.fmt.pix_mp.height;
+        layer->planes[2].offset = layer->planes[1].offset + ((avbuf->plane_info[0].bytesperline * avbuf->context->format.fmt.pix_mp.height) >> 2);
+        layer->planes[1].pitch = avbuf->plane_info[0].bytesperline >> 1;
+        layer->planes[2].pitch = avbuf->plane_info[0].bytesperline >> 1;
+        break;
+
+    default:
+        break;
+    }
+
+    avbuf->hwctx_ref = av_buffer_ref(ctx->hwdevice);
+    frame->buf[0] = av_buffer_create((uint8_t *)desc, sizeof(*desc), v4l2_free_buffer, avbuf, 0);
+    if (!frame->buf[0]) {
+        av_free(desc);
+        av_buffer_unref(&avbuf->hwctx_ref);
+        return AVERROR(ENOMEM);
+    }
+    frame->data[0] = (uint8_t *)desc;
+    frame->format = AV_PIX_FMT_DRM_PRIME;
+    frame->hw_frames_ctx = av_buffer_ref(ctx->hwframes);
+
+    ret = v4l2_buf_increase_ref(avbuf);
+    if (ret) {
+        av_buffer_unref(&frame->buf[0]);
+        return ret;
+    }
+
+    return 0;
+}
+
 static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out)
 {
     int i, ret;
@@ -418,12 +514,17 @@  int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out)
 
 int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf)
 {
+    V4L2m2mContext *s = buf_to_m2mctx(avbuf);
     int ret;
 
     av_frame_unref(frame);
 
     /* 1. get references to the actual data */
-    ret = v4l2_buffer_buf_to_swframe(frame, avbuf);
+    if (s->avctx->pix_fmt == AV_PIX_FMT_DRM_PRIME) {
+        ret = v4l2_buffer_buf_to_hwframe(frame, avbuf);
+    } else {
+        ret = v4l2_buffer_buf_to_swframe(frame, avbuf);
+    }
     if (ret)
         return ret;
 
@@ -494,6 +595,7 @@  int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out)
 int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index)
 {
     V4L2Context *ctx = avbuf->context;
+    struct v4l2_exportbuffer exp;
     int ret, i;
 
     avbuf->buf.memory = V4L2_MEMORY_MMAP;
@@ -539,6 +641,15 @@  int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index)
 
         if (avbuf->plane_info[i].mm_addr == MAP_FAILED)
             return AVERROR(ENOMEM);
+
+        exp.type = ctx->type;
+        exp.index = avbuf->buf.index;
+        exp.plane = i;
+        exp.fd = -1;
+        exp.flags = O_CLOEXEC | O_RDWR;
+        if (ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_EXPBUF, &exp))
+            avbuf->context->support_dma_buf = 0;
+        avbuf->plane_info[i].dmafd = exp.fd;
     }
 
     avbuf->status = V4L2BUF_AVAILABLE;
diff --git a/libavcodec/v4l2_buffers.h b/libavcodec/v4l2_buffers.h
index 3d2ff1b9a5d7..04250cda175e 100644
--- a/libavcodec/v4l2_buffers.h
+++ b/libavcodec/v4l2_buffers.h
@@ -55,6 +55,7 @@  typedef struct V4L2Buffer {
         int bytesperline;
         void * mm_addr;
         size_t length;
+        int dmafd;
     } plane_info[VIDEO_MAX_PLANES];
 
     int num_planes;
@@ -66,6 +67,7 @@  typedef struct V4L2Buffer {
     int flags;
     enum V4L2Buffer_status status;
 
+    AVBufferRef *hwctx_ref;
 } V4L2Buffer;
 
 /**
diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c
index b08f0015c2e5..9b578fdfe120 100644
--- a/libavcodec/v4l2_context.c
+++ b/libavcodec/v4l2_context.c
@@ -453,6 +453,10 @@  static int v4l2_release_buffers(V4L2Context* ctx)
             if (p->mm_addr && p->length)
                 if (munmap(p->mm_addr, p->length) < 0)
                     av_log(logger(ctx), AV_LOG_ERROR, "%s unmap plane (%s))\n", ctx->name, av_err2str(AVERROR(errno)));
+            if (p->dmafd >= 0) {
+                close(p->dmafd);
+                p->dmafd = -1;
+            }
         }
     }
 
@@ -694,6 +698,53 @@  int ff_v4l2_context_set_format(V4L2Context* ctx)
     return ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_S_FMT, &ctx->format);
 }
 
+
+int ff_v4l2_context_init_hw_ctx(V4L2Context *ctx)
+{
+    AVHWFramesContext *hwframes;
+    int ret;
+
+    if (!ctx->support_dma_buf)
+        return AVERROR(EINVAL);
+
+    ctx->hwdevice = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM);
+    if (!ctx->hwdevice) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    ret = av_hwdevice_ctx_init(ctx->hwdevice);
+    if (ret < 0)
+        goto  fail;
+
+    ctx->hwframes = av_hwframe_ctx_alloc(ctx->hwdevice);
+    if (!ctx->hwframes) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    hwframes = (AVHWFramesContext*)ctx->hwframes->data;
+    hwframes->format = AV_PIX_FMT_DRM_PRIME;
+    hwframes->sw_format = ctx->av_pix_fmt;
+    hwframes->width = ctx->width;
+    hwframes->height = ctx->height;
+    ret = av_hwframe_ctx_init(ctx->hwframes);
+    if (ret < 0)
+        goto fail;
+
+    return 0;
+fail:
+    ff_v4l2_context_uninit_hw_ctx(ctx);
+    ctx->support_dma_buf = 0;
+    return ret;
+}
+
+void ff_v4l2_context_uninit_hw_ctx(V4L2Context *ctx)
+{
+    av_buffer_unref(&ctx->hwframes);
+    av_buffer_unref(&ctx->hwdevice);
+}
+
 void ff_v4l2_context_release(V4L2Context* ctx)
 {
     int ret;
@@ -706,6 +757,7 @@  void ff_v4l2_context_release(V4L2Context* ctx)
         av_log(logger(ctx), AV_LOG_WARNING, "V4L2 failed to unmap the %s buffers\n", ctx->name);
 
     av_freep(&ctx->buffers);
+    ff_v4l2_context_uninit_hw_ctx(ctx);
 }
 
 int ff_v4l2_context_init(V4L2Context* ctx)
@@ -740,6 +792,7 @@  int ff_v4l2_context_init(V4L2Context* ctx)
         return AVERROR(ENOMEM);
     }
 
+    ctx->support_dma_buf = 1;
     for (i = 0; i < req.count; i++) {
         ctx->buffers[i].context = ctx;
         ret = ff_v4l2_buffer_initialize(&ctx->buffers[i], i);
diff --git a/libavcodec/v4l2_context.h b/libavcodec/v4l2_context.h
index 6f7460c89a9d..723d622e38c3 100644
--- a/libavcodec/v4l2_context.h
+++ b/libavcodec/v4l2_context.h
@@ -93,6 +93,9 @@  typedef struct V4L2Context {
      */
     int done;
 
+    int support_dma_buf;
+    AVBufferRef *hwdevice;
+    AVBufferRef *hwframes;
 } V4L2Context;
 
 /**
@@ -184,4 +187,18 @@  int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt);
  */
 int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* f);
 
+/**
+ * Initializes the hw context of V4L2Context.
+ *
+ * @param[in] ctx A pointer to a V4L2Context. See V4L2Context description for required variables.
+ * @return 0 in case of success, a negative value representing the error otherwise.
+ */
+int ff_v4l2_context_init_hw_ctx(V4L2Context *ctx);
+
+/**
+ * Releases the hw context of V4L2Context.
+ *
+ * @param[in] ctx A pointer to a V4L2Context.
+ */
+void ff_v4l2_context_uninit_hw_ctx(V4L2Context *ctx);
 #endif // AVCODEC_V4L2_CONTEXT_H
diff --git a/libavcodec/v4l2_fmt.c b/libavcodec/v4l2_fmt.c
index 6df47e3f5a3c..a64b6d530283 100644
--- a/libavcodec/v4l2_fmt.c
+++ b/libavcodec/v4l2_fmt.c
@@ -29,83 +29,91 @@ 
 #define AV_CODEC(x) AV_CODEC_ID_##x
 #define AV_FMT(x)   AV_PIX_FMT_##x
 
+#if CONFIG_LIBDRM
+#include <drm_fourcc.h>
+#define DRM_FMT(x)  DRM_FORMAT_##x
+#else
+#define DRM_FMT(x)  0
+#endif
+
 static const struct fmt_conversion {
     enum AVPixelFormat avfmt;
     enum AVCodecID avcodec;
     uint32_t v4l2_fmt;
+    uint32_t drm_fmt;
 } fmt_map[] = {
-    { AV_FMT(RGB555LE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB555) },
-    { AV_FMT(RGB555BE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB555X) },
-    { AV_FMT(RGB565LE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB565) },
-    { AV_FMT(RGB565BE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB565X) },
-    { AV_FMT(BGR24),       AV_CODEC(RAWVIDEO),    V4L2_FMT(BGR24) },
-    { AV_FMT(RGB24),       AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB24) },
-    { AV_FMT(BGR0),        AV_CODEC(RAWVIDEO),    V4L2_FMT(BGR32) },
-    { AV_FMT(0RGB),        AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB32) },
-    { AV_FMT(GRAY8),       AV_CODEC(RAWVIDEO),    V4L2_FMT(GREY) },
-    { AV_FMT(YUV420P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV420) },
-    { AV_FMT(YUYV422),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUYV) },
-    { AV_FMT(UYVY422),     AV_CODEC(RAWVIDEO),    V4L2_FMT(UYVY) },
-    { AV_FMT(YUV422P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV422P) },
-    { AV_FMT(YUV411P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV411P) },
-    { AV_FMT(YUV410P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV410) },
-    { AV_FMT(YUV410P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YVU410) },
-    { AV_FMT(NV12),        AV_CODEC(RAWVIDEO),    V4L2_FMT(NV12) },
-    { AV_FMT(NONE),        AV_CODEC(MJPEG),       V4L2_FMT(MJPEG) },
-    { AV_FMT(NONE),        AV_CODEC(MJPEG),       V4L2_FMT(JPEG) },
+    { AV_FMT(RGB555LE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB555),  DRM_FMT(XRGB1555) },
+    { AV_FMT(RGB555BE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB555X), DRM_FMT(XRGB1555) | DRM_FMT(BIG_ENDIAN) },
+    { AV_FMT(RGB565LE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB565),  DRM_FMT(RGB565) },
+    { AV_FMT(RGB565BE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB565X), DRM_FMT(RGB565) | DRM_FMT(BIG_ENDIAN) },
+    { AV_FMT(BGR24),       AV_CODEC(RAWVIDEO),    V4L2_FMT(BGR24),   DRM_FMT(BGR888) },
+    { AV_FMT(RGB24),       AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB24),   DRM_FMT(RGB888) },
+    { AV_FMT(BGR0),        AV_CODEC(RAWVIDEO),    V4L2_FMT(BGR32),   DRM_FMT(XRGB8888) },
+    { AV_FMT(0RGB),        AV_CODEC(RAWVIDEO),    V4L2_FMT(RGB32),   DRM_FMT(BGRX8888) },
+    { AV_FMT(GRAY8),       AV_CODEC(RAWVIDEO),    V4L2_FMT(GREY),    DRM_FMT(R8) },
+    { AV_FMT(YUV420P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV420),  DRM_FMT(YUV420) },
+    { AV_FMT(YUYV422),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUYV),    DRM_FMT(YUYV) },
+    { AV_FMT(UYVY422),     AV_CODEC(RAWVIDEO),    V4L2_FMT(UYVY),    DRM_FMT(UYVY) },
+    { AV_FMT(YUV422P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV422P), DRM_FMT(YUV422) },
+    { AV_FMT(YUV411P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV411P), DRM_FMT(YUV411) },
+    { AV_FMT(YUV410P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV410),  DRM_FMT(YUV410) },
+    { AV_FMT(YUV410P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YVU410),  DRM_FMT(YVU410) },
+    { AV_FMT(NV12),        AV_CODEC(RAWVIDEO),    V4L2_FMT(NV12),    DRM_FMT(NV12) },
+    { AV_FMT(NONE),        AV_CODEC(MJPEG),       V4L2_FMT(MJPEG),   DRM_FMT(INVALID) },
+    { AV_FMT(NONE),        AV_CODEC(MJPEG),       V4L2_FMT(JPEG),    DRM_FMT(INVALID) },
 #ifdef V4L2_PIX_FMT_SRGGB8
-    { AV_FMT(BAYER_BGGR8), AV_CODEC(RAWVIDEO),    V4L2_FMT(SBGGR8) },
-    { AV_FMT(BAYER_GBRG8), AV_CODEC(RAWVIDEO),    V4L2_FMT(SGBRG8) },
-    { AV_FMT(BAYER_GRBG8), AV_CODEC(RAWVIDEO),    V4L2_FMT(SGRBG8) },
-    { AV_FMT(BAYER_RGGB8), AV_CODEC(RAWVIDEO),    V4L2_FMT(SRGGB8) },
+    { AV_FMT(BAYER_BGGR8), AV_CODEC(RAWVIDEO),    V4L2_FMT(SBGGR8),  DRM_FMT(INVALID) },
+    { AV_FMT(BAYER_GBRG8), AV_CODEC(RAWVIDEO),    V4L2_FMT(SGBRG8),  DRM_FMT(INVALID) },
+    { AV_FMT(BAYER_GRBG8), AV_CODEC(RAWVIDEO),    V4L2_FMT(SGRBG8),  DRM_FMT(INVALID) },
+    { AV_FMT(BAYER_RGGB8), AV_CODEC(RAWVIDEO),    V4L2_FMT(SRGGB8),  DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_Y16
-    { AV_FMT(GRAY16LE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(Y16) },
+    { AV_FMT(GRAY16LE),    AV_CODEC(RAWVIDEO),    V4L2_FMT(Y16),     DRM_FMT(R16) },
 #endif
 #ifdef V4L2_PIX_FMT_NV12M
-    { AV_FMT(NV12),        AV_CODEC(RAWVIDEO),    V4L2_FMT(NV12M) },
+    { AV_FMT(NV12),        AV_CODEC(RAWVIDEO),    V4L2_FMT(NV12M),   DRM_FMT(NV12) },
 #endif
 #ifdef V4L2_PIX_FMT_NV21M
-    { AV_FMT(NV21),        AV_CODEC(RAWVIDEO),    V4L2_FMT(NV21M) },
+    { AV_FMT(NV21),        AV_CODEC(RAWVIDEO),    V4L2_FMT(NV21M),   DRM_FMT(NV21) },
 #endif
 #ifdef V4L2_PIX_FMT_YUV420M
-    { AV_FMT(YUV420P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV420M) },
+    { AV_FMT(YUV420P),     AV_CODEC(RAWVIDEO),    V4L2_FMT(YUV420M), DRM_FMT(YUV420) },
 #endif
 #ifdef V4L2_PIX_FMT_NV16M
-    { AV_FMT(NV16),        AV_CODEC(RAWVIDEO),    V4L2_FMT(NV16M) },
+    { AV_FMT(NV16),        AV_CODEC(RAWVIDEO),    V4L2_FMT(NV16M),   DRM_FMT(NV16) },
 #endif
 #ifdef V4L2_PIX_FMT_H263
-    { AV_FMT(NONE),        AV_CODEC(H263),        V4L2_FMT(H263) },
+    { AV_FMT(NONE),        AV_CODEC(H263),        V4L2_FMT(H263),    DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_H264
-    { AV_FMT(NONE),        AV_CODEC(H264),        V4L2_FMT(H264) },
+    { AV_FMT(NONE),        AV_CODEC(H264),        V4L2_FMT(H264),    DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_MPEG4
-    { AV_FMT(NONE),        AV_CODEC(MPEG4),       V4L2_FMT(MPEG4) },
+    { AV_FMT(NONE),        AV_CODEC(MPEG4),       V4L2_FMT(MPEG4),   DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_CPIA1
-    { AV_FMT(NONE),        AV_CODEC(CPIA),        V4L2_FMT(CPIA1) },
+    { AV_FMT(NONE),        AV_CODEC(CPIA),        V4L2_FMT(CPIA1),   DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_DV
-    { AV_FMT(NONE),        AV_CODEC(DVVIDEO),     V4L2_FMT(DV) },
+    { AV_FMT(NONE),        AV_CODEC(DVVIDEO),     V4L2_FMT(DV),      DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_MPEG1
-    { AV_FMT(NONE),        AV_CODEC(MPEG1VIDEO),  V4L2_FMT(MPEG1) },
+    { AV_FMT(NONE),        AV_CODEC(MPEG1VIDEO),  V4L2_FMT(MPEG1),   DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_MPEG2
-    { AV_FMT(NONE),        AV_CODEC(MPEG2VIDEO),  V4L2_FMT(MPEG2) },
+    { AV_FMT(NONE),        AV_CODEC(MPEG2VIDEO),  V4L2_FMT(MPEG2),   DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_VP8
-    { AV_FMT(NONE),        AV_CODEC(VP8),         V4L2_FMT(VP8) },
+    { AV_FMT(NONE),        AV_CODEC(VP8),         V4L2_FMT(VP8),     DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_VP9
-    { AV_FMT(NONE),        AV_CODEC(VP9),         V4L2_FMT(VP9) },
+    { AV_FMT(NONE),        AV_CODEC(VP9),         V4L2_FMT(VP9),     DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_HEVC
-    { AV_FMT(NONE),        AV_CODEC(HEVC),        V4L2_FMT(HEVC) },
+    { AV_FMT(NONE),        AV_CODEC(HEVC),        V4L2_FMT(HEVC),    DRM_FMT(INVALID) },
 #endif
 #ifdef V4L2_PIX_FMT_VC1_ANNEX_G
-    { AV_FMT(NONE),        AV_CODEC(VC1),         V4L2_FMT(VC1_ANNEX_G) },
+    { AV_FMT(NONE),        AV_CODEC(VC1),         V4L2_FMT(VC1_ANNEX_G), DRM_FMT(INVALID) },
 #endif
 };
 
@@ -139,3 +147,13 @@  enum AVPixelFormat ff_v4l2_format_v4l2_to_avfmt(uint32_t v4l2_fmt, enum AVCodecI
     }
     return AV_PIX_FMT_NONE;
 }
+
+uint32_t ff_v4l2_format_avfmt_to_drm(enum AVPixelFormat avfmt)
+{
+    int i;
+    for (i = 0; i < FF_ARRAY_ELEMS(fmt_map); i++) {
+        if (fmt_map[i].avfmt == avfmt)
+            return fmt_map[i].drm_fmt;
+    }
+    return DRM_FMT(INVALID);
+}
diff --git a/libavcodec/v4l2_fmt.h b/libavcodec/v4l2_fmt.h
index 577e03a7a76c..f705fe9c0b30 100644
--- a/libavcodec/v4l2_fmt.h
+++ b/libavcodec/v4l2_fmt.h
@@ -31,5 +31,6 @@ 
 enum AVPixelFormat ff_v4l2_format_v4l2_to_avfmt(uint32_t v4l2_fmt, enum AVCodecID avcodec);
 uint32_t ff_v4l2_format_avcodec_to_v4l2(enum AVCodecID avcodec);
 uint32_t ff_v4l2_format_avfmt_to_v4l2(enum AVPixelFormat avfmt);
+uint32_t ff_v4l2_format_avfmt_to_drm(enum AVPixelFormat avfmt);
 
 #endif /* AVCODEC_V4L2_FMT_H*/
diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c
index 6b936b6df2a9..fe362e4598f4 100644
--- a/libavcodec/v4l2_m2m_dec.c
+++ b/libavcodec/v4l2_m2m_dec.c
@@ -29,6 +29,9 @@ 
 #include "libavcodec/avcodec.h"
 #include "libavcodec/decode.h"
 #include "libavcodec/internal.h"
+#include "hwconfig.h"
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_drm.h"
 
 #include "v4l2_context.h"
 #include "v4l2_m2m.h"
@@ -99,6 +102,17 @@  static int v4l2_try_start(AVCodecContext *avctx)
         return ret;
     }
 
+    if (capture->support_dma_buf) {
+        ff_v4l2_context_uninit_hw_ctx(capture);
+        ret = ff_v4l2_context_init_hw_ctx(capture);
+        if (!ret) {
+            enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_DRM_PRIME,
+                                                capture->av_pix_fmt,
+                                                AV_PIX_FMT_NONE };
+            avctx->pix_fmt = ff_get_format(s->avctx, pix_fmts);
+        }
+    }
+
     return 0;
 }
 
@@ -224,6 +238,11 @@  static const AVOption options[] = {
     { NULL},
 };
 
+static const AVCodecHWConfigInternal *const v4l2_m2m_dec_hw_configs[] = {
+    HW_CONFIG_INTERNAL(DRM_PRIME),
+    NULL
+};
+
 #define M2MDEC_CLASS(NAME) \
     static const AVClass v4l2_m2m_ ## NAME ## _dec_class = { \
         .class_name = #NAME "_v4l2m2m_decoder", \
@@ -248,6 +267,7 @@  static const AVOption options[] = {
         .capabilities   = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \
         .caps_internal  = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, \
         .wrapper_name   = "v4l2m2m", \
+        .hw_configs     = v4l2_m2m_dec_hw_configs, \
     }
 
 M2MDEC(h264,  "H.264", AV_CODEC_ID_H264,       "h264_mp4toannexb");