2020-02-06 08:45:08

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 0/5] media: meson: vdec: Add VP9 decoding support

Hello,

This patchset aims to bring VP9 decoding support to Amlogic GXL, G12A & SM1
platforms for the amlogic stateful video decoder driver.

With this, it passes v4l2-compliance with streaming on Amlogic G12A and
Amlogic SM1 SoCs successfully using the stream at [1] with a fixed
pyv4l2compliance script for VP9 at [2].

The original script kept the IVF headers in the stream, confusing the
decoder. The fixed script only extracts the payload from the IVF container.

The decoder has been tested using the Google CTS TestVectorsIttiam VP9 yuv420
samples and the VP9 Decoder Performance streams at [5], decoding all streams from
Profile 0 and 2 up to Level 4.1, with 10bit downsampling to 8bit.

This patchset depends on :
- H.264 and compliance at [4]

Changes since v3 at [7]:
- fixes necessary spare ref buffer handling in parser
- added a comment to indicate how it's handled
- fix VP9 on SM1, was working with G12A firmware, but needed some changed with SM1 specific firmware
- pushed (gxl) and switched to missing (sm1) vp9 firmwares to linux-firmware repo

Changes since v2 at [6]:
- Rebased on H.264 and compliance at [4]

Changes since v1 at [3]:
- Fixed compliance for delta frame resize, but proper ref keeping is broken
- Added warns for delta frame resize, to be fixed in a following patchset
- Added VP9 probabilities parsing and transformation support to decore the VP9 performance streams
- Fixed refs keeping, avoid deleting necessary refs for next frame
- Properly used the kernel clamp_val() macros
- Zeroed the workspace to avoid refs handling glitches
- Add lock around the flush & stop to avoid race between IRQ and drain/stop

[1] https://github.com/superna9999/pyv4l2compliance/raw/tests/output/Jellyfish_1080_10s_5MB.vp9.hdr
[2] https://github.com/superna9999/pyv4l2compliance
[3] https://lore.kernel.org/linux-media/
[4] https://lore.kernel.org/linux-media/[email protected]
[5] https://www.webmproject.org/vp9/levels/
[6] https://lore.kernel.org/linux-media/[email protected]
[7] https://lore.kernel.org/linux-media/[email protected]

The compliance log is:
# v4l2-compliance --stream-from-hdr Jellyfish_1080_10s_5MB.vp9.hdr -s 200
v4l2-compliance SHA: 7ead0e1856b89f2e19369af452bb03fd0cd16793, 64 bits

Compliance test for meson-vdec device /dev/video0:

Driver Info:
Driver name : meson-vdec
Card type : Amlogic Video Decoder
Bus info : platform:meson-vdec
Driver version : 5.5.0
Capabilities : 0x84204000
Video Memory-to-Memory Multiplanar
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04204000
Video Memory-to-Memory Multiplanar
Streaming
Extended Pix Format
Detected Stateful Decoder

Required ioctls:
test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
test second /dev/video0 open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK

Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 0 Audio Inputs: 0 Tuners: 0

Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)

Control ioctls:
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK
test VIDIOC_QUERYCTRL: OK
test VIDIOC_G/S_CTRL: OK
test VIDIOC_G/S/TRY_EXT_CTRLS: OK
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 2 Private Controls: 0

Format ioctls:
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
test VIDIOC_G/S_PARM: OK (Not Supported)
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK
test VIDIOC_TRY_FMT: OK
test VIDIOC_S_FMT: OK
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)

Codec ioctls:
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK

Buffer ioctls:
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
test VIDIOC_EXPBUF: OK
test Requests: OK (Not Supported)

Test input 0:

Streaming ioctls:
test read/write: OK (Not Supported)
test blocking wait: OK
Video Capture Multiplanar: Captured 198 buffers
test MMAP (select): OK
Video Capture Multiplanar: Captured 198 buffers
test MMAP (epoll): OK
test USERPTR (select): OK (Not Supported)
test DMABUF: Cannot test, specify --expbuf-device

Total for meson-vdec device /dev/video0: 49, Succeeded: 49, Failed: 0, Warnings: 0

Maxime Jourdan (4):
media: meson: vdec: add helpers for lossless framebuffer compression
buffers
media: meson: vdec: add common HEVC decoder support
media: meson: vdec: add VP9 input support
media: meson: vdec: add VP9 decoder support

Neil Armstrong (1):
media: meson: vdec: align stride on 32 bytes

drivers/staging/media/meson/vdec/Makefile | 4 +-
.../media/meson/vdec/codec_hevc_common.c | 286 +++
.../media/meson/vdec/codec_hevc_common.h | 77 +
drivers/staging/media/meson/vdec/codec_vp9.c | 2138 +++++++++++++++++
drivers/staging/media/meson/vdec/codec_vp9.h | 13 +
drivers/staging/media/meson/vdec/esparser.c | 150 +-
drivers/staging/media/meson/vdec/hevc_regs.h | 218 ++
drivers/staging/media/meson/vdec/vdec.c | 15 +-
.../staging/media/meson/vdec/vdec_helpers.c | 35 +-
.../staging/media/meson/vdec/vdec_helpers.h | 4 +
drivers/staging/media/meson/vdec/vdec_hevc.c | 231 ++
drivers/staging/media/meson/vdec/vdec_hevc.h | 13 +
.../staging/media/meson/vdec/vdec_platform.c | 38 +
13 files changed, 3209 insertions(+), 13 deletions(-)
create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.c
create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.h
create mode 100644 drivers/staging/media/meson/vdec/codec_vp9.c
create mode 100644 drivers/staging/media/meson/vdec/codec_vp9.h
create mode 100644 drivers/staging/media/meson/vdec/hevc_regs.h
create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.c
create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.h

--
2.22.0


2020-02-06 09:14:15

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 2/5] media: meson: vdec: add helpers for lossless framebuffer compression buffers

From: Maxime Jourdan <[email protected]>

Add helpers to support the lossless framebuffer compression format that
will be used in HEVC & VP9 decoders when decoding 10bit content for
downsampling to 8bit NV12 and later proper compressed buffer support.

Signed-off-by: Maxime Jourdan <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
.../staging/media/meson/vdec/vdec_helpers.c | 27 +++++++++++++++++++
.../staging/media/meson/vdec/vdec_helpers.h | 4 +++
2 files changed, 31 insertions(+)

diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c
index 3f7929c54dc6..caec0fb60338 100644
--- a/drivers/staging/media/meson/vdec/vdec_helpers.c
+++ b/drivers/staging/media/meson/vdec/vdec_helpers.c
@@ -50,6 +50,33 @@ void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val)
}
EXPORT_SYMBOL_GPL(amvdec_write_parser);

+/* 4 KiB per 64x32 block */
+u32 amvdec_am21c_body_size(u32 width, u32 height)
+{
+ u32 width_64 = ALIGN(width, 64) / 64;
+ u32 height_32 = ALIGN(height, 32) / 32;
+
+ return SZ_4K * width_64 * height_32;
+}
+EXPORT_SYMBOL_GPL(amvdec_am21c_body_size);
+
+/* 32 bytes per 128x64 block */
+u32 amvdec_am21c_head_size(u32 width, u32 height)
+{
+ u32 width_128 = ALIGN(width, 128) / 128;
+ u32 height_64 = ALIGN(height, 64) / 64;
+
+ return 32 * width_128 * height_64;
+}
+EXPORT_SYMBOL_GPL(amvdec_am21c_head_size);
+
+u32 amvdec_am21c_size(u32 width, u32 height)
+{
+ return ALIGN(amvdec_am21c_body_size(width, height) +
+ amvdec_am21c_head_size(width, height), SZ_64K);
+}
+EXPORT_SYMBOL_GPL(amvdec_am21c_size);
+
static int canvas_alloc(struct amvdec_session *sess, u8 *canvas_id)
{
int ret;
diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.h b/drivers/staging/media/meson/vdec/vdec_helpers.h
index 165e6293ffba..cfaed52ab526 100644
--- a/drivers/staging/media/meson/vdec/vdec_helpers.h
+++ b/drivers/staging/media/meson/vdec/vdec_helpers.h
@@ -27,6 +27,10 @@ void amvdec_clear_dos_bits(struct amvdec_core *core, u32 reg, u32 val);
u32 amvdec_read_parser(struct amvdec_core *core, u32 reg);
void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val);

+u32 amvdec_am21c_body_size(u32 width, u32 height);
+u32 amvdec_am21c_head_size(u32 width, u32 height);
+u32 amvdec_am21c_size(u32 width, u32 height);
+
/**
* amvdec_dst_buf_done_idx() - Signal that a buffer is done decoding
*
--
2.22.0

2020-02-06 09:14:58

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 3/5] media: meson: vdec: add common HEVC decoder support

From: Maxime Jourdan <[email protected]>

Add support for the HEVC & VP9 common decoder support, handling
Amlogic GXBB, GXL, G12A and SM1 platforms.

This handles the "HEVC" hw decoder used for HEVC and VP9, and will be
using in the new H264 multi-instance decoder for G12A & SM1 platforms.

Signed-off-by: Maxime Jourdan <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/staging/media/meson/vdec/Makefile | 4 +-
.../media/meson/vdec/codec_hevc_common.c | 286 ++++++++++++++++++
.../media/meson/vdec/codec_hevc_common.h | 77 +++++
drivers/staging/media/meson/vdec/hevc_regs.h | 211 +++++++++++++
drivers/staging/media/meson/vdec/vdec_hevc.c | 231 ++++++++++++++
drivers/staging/media/meson/vdec/vdec_hevc.h | 13 +
6 files changed, 820 insertions(+), 2 deletions(-)
create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.c
create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.h
create mode 100644 drivers/staging/media/meson/vdec/hevc_regs.h
create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.c
create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.h

diff --git a/drivers/staging/media/meson/vdec/Makefile b/drivers/staging/media/meson/vdec/Makefile
index 711d990c760e..f55b6e625034 100644
--- a/drivers/staging/media/meson/vdec/Makefile
+++ b/drivers/staging/media/meson/vdec/Makefile
@@ -2,7 +2,7 @@
# Makefile for Amlogic meson video decoder driver

meson-vdec-objs = esparser.o vdec.o vdec_helpers.o vdec_platform.o
-meson-vdec-objs += vdec_1.o
-meson-vdec-objs += codec_mpeg12.o codec_h264.o
+meson-vdec-objs += vdec_1.o vdec_hevc.o
+meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_hevc_common.o

obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o
diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.c b/drivers/staging/media/meson/vdec/codec_hevc_common.c
new file mode 100644
index 000000000000..335bcba062ac
--- /dev/null
+++ b/drivers/staging/media/meson/vdec/codec_hevc_common.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Maxime Jourdan <[email protected]>
+ */
+
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "codec_hevc_common.h"
+#include "vdec_helpers.h"
+#include "hevc_regs.h"
+
+#define MMU_COMPRESS_HEADER_SIZE 0x48000
+#define MMU_MAP_SIZE 0x4800
+
+/* Configure decode head read mode */
+void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit)
+{
+ struct amvdec_core *core = sess->core;
+ u32 body_size = amvdec_am21c_body_size(sess->width, sess->height);
+ u32 head_size = amvdec_am21c_head_size(sess->width, sess->height);
+
+ if (!codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) {
+ /* Enable 2-plane reference read mode */
+ amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(31));
+ return;
+ }
+
+ if (codec_hevc_use_mmu(core->platform->revision,
+ sess->pixfmt_cap, is_10bit))
+ amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(4));
+ else
+ amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, 0);
+
+ if (core->platform->revision < VDEC_REVISION_SM1)
+ amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL2, body_size / 32);
+ amvdec_write_dos(core, HEVC_CM_BODY_LENGTH, body_size);
+ amvdec_write_dos(core, HEVC_CM_HEADER_OFFSET, body_size);
+ amvdec_write_dos(core, HEVC_CM_HEADER_LENGTH, head_size);
+}
+EXPORT_SYMBOL_GPL(codec_hevc_setup_decode_head);
+
+static void codec_hevc_setup_buffers_gxbb(struct amvdec_session *sess,
+ struct codec_hevc_common *comm,
+ int is_10bit)
+{
+ struct amvdec_core *core = sess->core;
+ struct v4l2_m2m_buffer *buf;
+ u32 buf_num = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
+ dma_addr_t buf_y_paddr = 0;
+ dma_addr_t buf_uv_paddr = 0;
+ u32 idx = 0;
+ u32 val;
+ int i;
+
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0);
+
+ v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
+ struct vb2_buffer *vb = &buf->vb.vb2_buf;
+
+ idx = vb->index;
+
+ if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit))
+ buf_y_paddr = comm->fbc_buffer_paddr[idx];
+ else
+ buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) {
+ val = buf_y_paddr | (idx << 8) | 1;
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
+ val);
+ } else {
+ buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1);
+ val = buf_y_paddr | ((idx * 2) << 8) | 1;
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
+ val);
+ val = buf_uv_paddr | ((idx * 2 + 1) << 8) | 1;
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
+ val);
+ }
+ }
+
+ if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit))
+ val = buf_y_paddr | (idx << 8) | 1;
+ else
+ val = buf_y_paddr | ((idx * 2) << 8) | 1;
+
+ /* Fill the remaining unused slots with the last buffer's Y addr */
+ for (i = buf_num; i < MAX_REF_PIC_NUM; ++i)
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, val);
+
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
+ amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1);
+ for (i = 0; i < 32; ++i)
+ amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
+}
+
+static void codec_hevc_setup_buffers_gxl(struct amvdec_session *sess,
+ struct codec_hevc_common *comm,
+ int is_10bit)
+{
+ struct amvdec_core *core = sess->core;
+ struct v4l2_m2m_buffer *buf;
+ u32 revision = core->platform->revision;
+ u32 pixfmt_cap = sess->pixfmt_cap;
+ int i;
+
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR,
+ BIT(2) | BIT(1));
+
+ v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
+ struct vb2_buffer *vb = &buf->vb.vb2_buf;
+ dma_addr_t buf_y_paddr = 0;
+ dma_addr_t buf_uv_paddr = 0;
+ u32 idx = vb->index;
+
+ if (codec_hevc_use_mmu(revision, pixfmt_cap, is_10bit))
+ buf_y_paddr = comm->mmu_header_paddr[idx];
+ else if (codec_hevc_use_downsample(pixfmt_cap, is_10bit))
+ buf_y_paddr = comm->fbc_buffer_paddr[idx];
+ else
+ buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA,
+ buf_y_paddr >> 5);
+
+ if (!codec_hevc_use_fbc(pixfmt_cap, is_10bit)) {
+ buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1);
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA,
+ buf_uv_paddr >> 5);
+ }
+ }
+
+ amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
+ amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1);
+ for (i = 0; i < 32; ++i)
+ amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
+}
+
+void codec_hevc_free_fbc_buffers(struct amvdec_session *sess,
+ struct codec_hevc_common *comm)
+{
+ struct device *dev = sess->core->dev;
+ u32 am21_size = amvdec_am21c_size(sess->width, sess->height);
+ int i;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; ++i) {
+ if (comm->fbc_buffer_vaddr[i]) {
+ dma_free_coherent(dev, am21_size,
+ comm->fbc_buffer_vaddr[i],
+ comm->fbc_buffer_paddr[i]);
+ comm->fbc_buffer_vaddr[i] = NULL;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(codec_hevc_free_fbc_buffers);
+
+static int codec_hevc_alloc_fbc_buffers(struct amvdec_session *sess,
+ struct codec_hevc_common *comm)
+{
+ struct device *dev = sess->core->dev;
+ struct v4l2_m2m_buffer *buf;
+ u32 am21_size = amvdec_am21c_size(sess->width, sess->height);
+
+ v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
+ u32 idx = buf->vb.vb2_buf.index;
+ dma_addr_t paddr;
+ void *vaddr = dma_alloc_coherent(dev, am21_size, &paddr,
+ GFP_KERNEL);
+ if (!vaddr) {
+ dev_err(dev, "Couldn't allocate FBC buffer %u\n", idx);
+ codec_hevc_free_fbc_buffers(sess, comm);
+ return -ENOMEM;
+ }
+
+ comm->fbc_buffer_vaddr[idx] = vaddr;
+ comm->fbc_buffer_paddr[idx] = paddr;
+ }
+
+ return 0;
+}
+
+void codec_hevc_free_mmu_headers(struct amvdec_session *sess,
+ struct codec_hevc_common *comm)
+{
+ struct device *dev = sess->core->dev;
+ int i;
+
+ for (i = 0; i < MAX_REF_PIC_NUM; ++i) {
+ if (comm->mmu_header_vaddr[i]) {
+ dma_free_coherent(dev, MMU_COMPRESS_HEADER_SIZE,
+ comm->mmu_header_vaddr[i],
+ comm->mmu_header_paddr[i]);
+ comm->mmu_header_vaddr[i] = NULL;
+ }
+ }
+
+ if (comm->mmu_map_vaddr) {
+ dma_free_coherent(dev, MMU_MAP_SIZE,
+ comm->mmu_map_vaddr,
+ comm->mmu_map_paddr);
+ comm->mmu_map_vaddr = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(codec_hevc_free_mmu_headers);
+
+static int codec_hevc_alloc_mmu_headers(struct amvdec_session *sess,
+ struct codec_hevc_common *comm)
+{
+ struct device *dev = sess->core->dev;
+ struct v4l2_m2m_buffer *buf;
+
+ comm->mmu_map_vaddr = dma_alloc_coherent(dev, MMU_MAP_SIZE,
+ &comm->mmu_map_paddr,
+ GFP_KERNEL);
+ if (!comm->mmu_map_vaddr)
+ return -ENOMEM;
+
+ v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
+ u32 idx = buf->vb.vb2_buf.index;
+ dma_addr_t paddr;
+ void *vaddr = dma_alloc_coherent(dev, MMU_COMPRESS_HEADER_SIZE,
+ &paddr, GFP_KERNEL);
+ if (!vaddr) {
+ dev_err(dev, "Couldn't allocate MMU header %u\n", idx);
+ codec_hevc_free_mmu_headers(sess, comm);
+ return -ENOMEM;
+ }
+
+ comm->mmu_header_vaddr[idx] = vaddr;
+ comm->mmu_header_paddr[idx] = paddr;
+ }
+
+ return 0;
+}
+
+int codec_hevc_setup_buffers(struct amvdec_session *sess,
+ struct codec_hevc_common *comm,
+ int is_10bit)
+{
+ struct amvdec_core *core = sess->core;
+ int ret;
+
+ if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit)) {
+ ret = codec_hevc_alloc_fbc_buffers(sess, comm);
+ if (ret)
+ return ret;
+ }
+
+ if (codec_hevc_use_mmu(core->platform->revision,
+ sess->pixfmt_cap, is_10bit)) {
+ ret = codec_hevc_alloc_mmu_headers(sess, comm);
+ if (ret) {
+ codec_hevc_free_fbc_buffers(sess, comm);
+ return ret;
+ }
+ }
+
+ if (core->platform->revision == VDEC_REVISION_GXBB)
+ codec_hevc_setup_buffers_gxbb(sess, comm, is_10bit);
+ else
+ codec_hevc_setup_buffers_gxl(sess, comm, is_10bit);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(codec_hevc_setup_buffers);
+
+void codec_hevc_fill_mmu_map(struct amvdec_session *sess,
+ struct codec_hevc_common *comm,
+ struct vb2_buffer *vb)
+{
+ u32 size = amvdec_am21c_size(sess->width, sess->height);
+ u32 nb_pages = size / PAGE_SIZE;
+ u32 *mmu_map = comm->mmu_map_vaddr;
+ u32 first_page;
+ u32 i;
+
+ if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M)
+ first_page = comm->fbc_buffer_paddr[vb->index] >> PAGE_SHIFT;
+ else
+ first_page = vb2_dma_contig_plane_dma_addr(vb, 0) >> PAGE_SHIFT;
+
+ for (i = 0; i < nb_pages; ++i)
+ mmu_map[i] = first_page + i;
+}
+EXPORT_SYMBOL_GPL(codec_hevc_fill_mmu_map);
diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.h b/drivers/staging/media/meson/vdec/codec_hevc_common.h
new file mode 100644
index 000000000000..de16d2e43061
--- /dev/null
+++ b/drivers/staging/media/meson/vdec/codec_hevc_common.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 BayLibre, SAS
+ * Author: Maxime Jourdan <[email protected]>
+ */
+
+#ifndef __MESON_VDEC_HEVC_COMMON_H_
+#define __MESON_VDEC_HEVC_COMMON_H_
+
+#include "vdec.h"
+
+#define PARSER_CMD_SKIP_CFG_0 0x0000090b
+#define PARSER_CMD_SKIP_CFG_1 0x1b14140f
+#define PARSER_CMD_SKIP_CFG_2 0x001b1910
+static const u16 vdec_hevc_parser_cmd[] = {
+ 0x0401, 0x8401, 0x0800, 0x0402,
+ 0x9002, 0x1423, 0x8CC3, 0x1423,
+ 0x8804, 0x9825, 0x0800, 0x04FE,
+ 0x8406, 0x8411, 0x1800, 0x8408,
+ 0x8409, 0x8C2A, 0x9C2B, 0x1C00,
+ 0x840F, 0x8407, 0x8000, 0x8408,
+ 0x2000, 0xA800, 0x8410, 0x04DE,
+ 0x840C, 0x840D, 0xAC00, 0xA000,
+ 0x08C0, 0x08E0, 0xA40E, 0xFC00,
+ 0x7C00
+};
+
+#define MAX_REF_PIC_NUM 24
+
+struct codec_hevc_common {
+ void *fbc_buffer_vaddr[MAX_REF_PIC_NUM];
+ dma_addr_t fbc_buffer_paddr[MAX_REF_PIC_NUM];
+
+ void *mmu_header_vaddr[MAX_REF_PIC_NUM];
+ dma_addr_t mmu_header_paddr[MAX_REF_PIC_NUM];
+
+ void *mmu_map_vaddr;
+ dma_addr_t mmu_map_paddr;
+};
+
+/* Returns 1 if we must use framebuffer compression */
+static inline int codec_hevc_use_fbc(u32 pixfmt, int is_10bit)
+{
+ /* TOFIX: Handle Amlogic Compressed buffer for 8bit also */
+ return is_10bit;
+}
+
+/* Returns 1 if we are decoding 10-bit but outputting 8-bit NV12 */
+static inline int codec_hevc_use_downsample(u32 pixfmt, int is_10bit)
+{
+ return is_10bit;
+}
+
+/* Returns 1 if we are decoding using the IOMMU */
+static inline int codec_hevc_use_mmu(u32 revision, u32 pixfmt, int is_10bit)
+{
+ return revision >= VDEC_REVISION_G12A &&
+ codec_hevc_use_fbc(pixfmt, is_10bit);
+}
+
+/**
+ * Configure decode head read mode
+ */
+void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit);
+
+void codec_hevc_free_fbc_buffers(struct amvdec_session *sess,
+ struct codec_hevc_common *comm);
+
+int codec_hevc_setup_buffers(struct amvdec_session *sess,
+ struct codec_hevc_common *comm,
+ int is_10bit);
+
+void codec_hevc_fill_mmu_map(struct amvdec_session *sess,
+ struct codec_hevc_common *comm,
+ struct vb2_buffer *vb);
+
+#endif
diff --git a/drivers/staging/media/meson/vdec/hevc_regs.h b/drivers/staging/media/meson/vdec/hevc_regs.h
new file mode 100644
index 000000000000..55c1a80b955a
--- /dev/null
+++ b/drivers/staging/media/meson/vdec/hevc_regs.h
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ */
+
+#ifndef __MESON_VDEC_HEVC_REGS_H_
+#define __MESON_VDEC_HEVC_REGS_H_
+
+#define HEVC_ASSIST_MMU_MAP_ADDR 0xc024
+
+#define HEVC_ASSIST_MBOX1_CLR_REG 0xc1d4
+#define HEVC_ASSIST_MBOX1_MASK 0xc1d8
+
+#define HEVC_ASSIST_SCRATCH_0 0xc300
+#define HEVC_ASSIST_SCRATCH_1 0xc304
+#define HEVC_ASSIST_SCRATCH_2 0xc308
+#define HEVC_ASSIST_SCRATCH_3 0xc30c
+#define HEVC_ASSIST_SCRATCH_4 0xc310
+#define HEVC_ASSIST_SCRATCH_5 0xc314
+#define HEVC_ASSIST_SCRATCH_6 0xc318
+#define HEVC_ASSIST_SCRATCH_7 0xc31c
+#define HEVC_ASSIST_SCRATCH_8 0xc320
+#define HEVC_ASSIST_SCRATCH_9 0xc324
+#define HEVC_ASSIST_SCRATCH_A 0xc328
+#define HEVC_ASSIST_SCRATCH_B 0xc32c
+#define HEVC_ASSIST_SCRATCH_C 0xc330
+#define HEVC_ASSIST_SCRATCH_D 0xc334
+#define HEVC_ASSIST_SCRATCH_E 0xc338
+#define HEVC_ASSIST_SCRATCH_F 0xc33c
+#define HEVC_ASSIST_SCRATCH_G 0xc340
+#define HEVC_ASSIST_SCRATCH_H 0xc344
+#define HEVC_ASSIST_SCRATCH_I 0xc348
+#define HEVC_ASSIST_SCRATCH_J 0xc34c
+#define HEVC_ASSIST_SCRATCH_K 0xc350
+#define HEVC_ASSIST_SCRATCH_L 0xc354
+#define HEVC_ASSIST_SCRATCH_M 0xc358
+#define HEVC_ASSIST_SCRATCH_N 0xc35c
+
+#define HEVC_PARSER_VERSION 0xc400
+#define HEVC_STREAM_CONTROL 0xc404
+#define HEVC_STREAM_START_ADDR 0xc408
+#define HEVC_STREAM_END_ADDR 0xc40c
+#define HEVC_STREAM_WR_PTR 0xc410
+#define HEVC_STREAM_RD_PTR 0xc414
+#define HEVC_STREAM_LEVEL 0xc418
+#define HEVC_STREAM_FIFO_CTL 0xc41c
+#define HEVC_SHIFT_CONTROL 0xc420
+#define HEVC_SHIFT_STARTCODE 0xc424
+#define HEVC_SHIFT_EMULATECODE 0xc428
+#define HEVC_SHIFT_STATUS 0xc42c
+#define HEVC_SHIFTED_DATA 0xc430
+#define HEVC_SHIFT_BYTE_COUNT 0xc434
+#define HEVC_SHIFT_COMMAND 0xc438
+#define HEVC_ELEMENT_RESULT 0xc43c
+#define HEVC_CABAC_CONTROL 0xc440
+#define HEVC_PARSER_SLICE_INFO 0xc444
+#define HEVC_PARSER_CMD_WRITE 0xc448
+#define HEVC_PARSER_CORE_CONTROL 0xc44c
+#define HEVC_PARSER_CMD_FETCH 0xc450
+#define HEVC_PARSER_CMD_STATUS 0xc454
+#define HEVC_PARSER_LCU_INFO 0xc458
+#define HEVC_PARSER_HEADER_INFO 0xc45c
+#define HEVC_PARSER_INT_CONTROL 0xc480
+#define HEVC_PARSER_INT_STATUS 0xc484
+#define HEVC_PARSER_IF_CONTROL 0xc488
+#define HEVC_PARSER_PICTURE_SIZE 0xc48c
+#define HEVC_PARSER_LCU_START 0xc490
+#define HEVC_PARSER_HEADER_INFO2 0xc494
+#define HEVC_PARSER_QUANT_READ 0xc498
+#define HEVC_PARSER_RESERVED_27 0xc49c
+#define HEVC_PARSER_CMD_SKIP_0 0xc4a0
+#define HEVC_PARSER_CMD_SKIP_1 0xc4a4
+#define HEVC_PARSER_CMD_SKIP_2 0xc4a8
+#define HEVC_SAO_IF_STATUS 0xc4c0
+#define HEVC_SAO_IF_DATA_Y 0xc4c4
+#define HEVC_SAO_IF_DATA_U 0xc4c8
+#define HEVC_SAO_IF_DATA_V 0xc4cc
+#define HEVC_STREAM_SWAP_ADDR 0xc4d0
+#define HEVC_STREAM_SWAP_CTRL 0xc4d4
+#define HEVC_IQIT_IF_WAIT_CNT 0xc4d8
+#define HEVC_MPRED_IF_WAIT_CNT 0xc4dc
+#define HEVC_SAO_IF_WAIT_CNT 0xc4e0
+
+#define HEVC_MPRED_VERSION 0xc800
+#define HEVC_MPRED_CTRL0 0xc804
+ #define MPRED_CTRL0_NEW_PIC BIT(2)
+ #define MPRED_CTRL0_NEW_TILE BIT(3)
+ #define MPRED_CTRL0_NEW_SLI_SEG BIT(4)
+ #define MPRED_CTRL0_TMVP BIT(5)
+ #define MPRED_CTRL0_LDC BIT(6)
+ #define MPRED_CTRL0_COL_FROM_L0 BIT(7)
+ #define MPRED_CTRL0_ABOVE_EN BIT(9)
+ #define MPRED_CTRL0_MV_WR_EN BIT(10)
+ #define MPRED_CTRL0_MV_RD_EN BIT(11)
+ #define MPRED_CTRL0_BUF_LINEAR BIT(13)
+#define HEVC_MPRED_CTRL1 0xc808
+#define HEVC_MPRED_INT_EN 0xc80c
+#define HEVC_MPRED_INT_STATUS 0xc810
+#define HEVC_MPRED_PIC_SIZE 0xc814
+#define HEVC_MPRED_PIC_SIZE_LCU 0xc818
+#define HEVC_MPRED_TILE_START 0xc81c
+#define HEVC_MPRED_TILE_SIZE_LCU 0xc820
+#define HEVC_MPRED_REF_NUM 0xc824
+#define HEVC_MPRED_REF_EN_L0 0xc830
+#define HEVC_MPRED_REF_EN_L1 0xc834
+#define HEVC_MPRED_COLREF_EN_L0 0xc838
+#define HEVC_MPRED_COLREF_EN_L1 0xc83c
+#define HEVC_MPRED_AXI_WCTRL 0xc840
+#define HEVC_MPRED_AXI_RCTRL 0xc844
+#define HEVC_MPRED_ABV_START_ADDR 0xc848
+#define HEVC_MPRED_MV_WR_START_ADDR 0xc84c
+#define HEVC_MPRED_MV_RD_START_ADDR 0xc850
+#define HEVC_MPRED_MV_WPTR 0xc854
+#define HEVC_MPRED_MV_RPTR 0xc858
+#define HEVC_MPRED_MV_WR_ROW_JUMP 0xc85c
+#define HEVC_MPRED_MV_RD_ROW_JUMP 0xc860
+#define HEVC_MPRED_CURR_LCU 0xc864
+#define HEVC_MPRED_ABV_WPTR 0xc868
+#define HEVC_MPRED_ABV_RPTR 0xc86c
+#define HEVC_MPRED_CTRL2 0xc870
+#define HEVC_MPRED_CTRL3 0xc874
+#define HEVC_MPRED_L0_REF00_POC 0xc880
+#define HEVC_MPRED_L1_REF00_POC 0xc8c0
+
+#define HEVC_MPRED_CUR_POC 0xc980
+#define HEVC_MPRED_COL_POC 0xc984
+#define HEVC_MPRED_MV_RD_END_ADDR 0xc988
+
+#define HEVC_MSP 0xcc00
+#define HEVC_MPSR 0xcc04
+#define HEVC_MCPU_INTR_MSK 0xcc10
+#define HEVC_MCPU_INTR_REQ 0xcc14
+#define HEVC_CPSR 0xcc84
+
+#define HEVC_IMEM_DMA_CTRL 0xcd00
+#define HEVC_IMEM_DMA_ADR 0xcd04
+#define HEVC_IMEM_DMA_COUNT 0xcd08
+
+#define HEVCD_IPP_TOP_CNTL 0xd000
+#define HEVCD_IPP_LINEBUFF_BASE 0xd024
+#define HEVCD_IPP_AXIIF_CONFIG 0xd02c
+
+#define HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR 0xd180
+#define HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR 0xd184
+#define HEVCD_MPP_ANC2AXI_TBL_DATA 0xd190
+
+#define HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR 0xd300
+#define HEVCD_MPP_ANC_CANVAS_DATA_ADDR 0xd304
+#define HEVCD_MPP_DECOMP_CTL1 0xd308
+#define HEVCD_MPP_DECOMP_CTL2 0xd30c
+#define HEVCD_MCRCC_CTL1 0xd3c0
+#define HEVCD_MCRCC_CTL2 0xd3c4
+#define HEVCD_MCRCC_CTL3 0xd3c8
+
+#define HEVC_DBLK_CFG0 0xd400
+#define HEVC_DBLK_CFG1 0xd404
+#define HEVC_DBLK_CFG2 0xd408
+#define HEVC_DBLK_CFG3 0xd40c
+#define HEVC_DBLK_CFG4 0xd410
+#define HEVC_DBLK_CFG5 0xd414
+#define HEVC_DBLK_CFG6 0xd418
+#define HEVC_DBLK_CFG7 0xd41c
+#define HEVC_DBLK_CFG8 0xd420
+#define HEVC_DBLK_CFG9 0xd424
+#define HEVC_DBLK_CFGA 0xd428
+#define HEVC_DBLK_STS0 0xd42c
+#define HEVC_DBLK_STS1 0xd430
+#define HEVC_DBLK_CFGE 0xd438
+
+#define HEVC_SAO_VERSION 0xd800
+#define HEVC_SAO_CTRL0 0xd804
+#define HEVC_SAO_CTRL1 0xd808
+#define HEVC_SAO_PIC_SIZE 0xd814
+#define HEVC_SAO_PIC_SIZE_LCU 0xd818
+#define HEVC_SAO_TILE_START 0xd81c
+#define HEVC_SAO_TILE_SIZE_LCU 0xd820
+#define HEVC_SAO_Y_START_ADDR 0xd82c
+#define HEVC_SAO_Y_LENGTH 0xd830
+#define HEVC_SAO_C_START_ADDR 0xd834
+#define HEVC_SAO_C_LENGTH 0xd838
+#define HEVC_SAO_Y_WPTR 0xd83c
+#define HEVC_SAO_C_WPTR 0xd840
+#define HEVC_SAO_ABV_START_ADDR 0xd844
+#define HEVC_SAO_VB_WR_START_ADDR 0xd848
+#define HEVC_SAO_VB_RD_START_ADDR 0xd84c
+#define HEVC_SAO_ABV_WPTR 0xd850
+#define HEVC_SAO_ABV_RPTR 0xd854
+#define HEVC_SAO_VB_WPTR 0xd858
+#define HEVC_SAO_VB_RPTR 0xd85c
+#define HEVC_SAO_CTRL2 0xd880
+#define HEVC_SAO_CTRL3 0xd884
+#define HEVC_SAO_CTRL4 0xd888
+#define HEVC_SAO_CTRL5 0xd88c
+#define HEVC_SAO_CTRL6 0xd890
+#define HEVC_SAO_CTRL7 0xd894
+#define HEVC_CM_BODY_START_ADDR 0xd898
+#define HEVC_CM_BODY_LENGTH 0xd89c
+#define HEVC_CM_HEADER_START_ADDR 0xd8a0
+#define HEVC_CM_HEADER_LENGTH 0xd8a4
+#define HEVC_CM_HEADER_OFFSET 0xd8ac
+#define HEVC_SAO_MMU_VH0_ADDR 0xd8e8
+#define HEVC_SAO_MMU_VH1_ADDR 0xd8ec
+
+#define HEVC_IQIT_CLK_RST_CTRL 0xdc00
+#define HEVC_IQIT_SCALELUT_WR_ADDR 0xdc08
+#define HEVC_IQIT_SCALELUT_RD_ADDR 0xdc0c
+#define HEVC_IQIT_SCALELUT_DATA 0xdc10
+
+#define HEVC_PSCALE_CTRL 0xe444
+
+#endif
diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.c b/drivers/staging/media/meson/vdec/vdec_hevc.c
new file mode 100644
index 000000000000..af41215e106c
--- /dev/null
+++ b/drivers/staging/media/meson/vdec/vdec_hevc.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Maxime Jourdan <[email protected]>
+ *
+ * VDEC_HEVC is a video decoding block that allows decoding of
+ * HEVC, VP9
+ */
+
+#include <linux/firmware.h>
+#include <linux/clk.h>
+
+#include "vdec_1.h"
+#include "vdec_helpers.h"
+#include "hevc_regs.h"
+#include "dos_regs.h"
+
+/* AO Registers */
+#define AO_RTI_GEN_PWR_SLEEP0 0xe8
+#define AO_RTI_GEN_PWR_ISO0 0xec
+ #define GEN_PWR_VDEC_HEVC (BIT(7) | BIT(6))
+ #define GEN_PWR_VDEC_HEVC_SM1 (BIT(2))
+
+#define MC_SIZE (4096 * 4)
+
+static int vdec_hevc_load_firmware(struct amvdec_session *sess,
+ const char *fwname)
+{
+ struct amvdec_core *core = sess->core;
+ struct device *dev = core->dev_dec;
+ const struct firmware *fw;
+ static void *mc_addr;
+ static dma_addr_t mc_addr_map;
+ int ret;
+ u32 i = 100;
+
+ ret = request_firmware(&fw, fwname, dev);
+ if (ret < 0) {
+ dev_err(dev, "Unable to request firmware %s\n", fwname);
+ return ret;
+ }
+
+ if (fw->size < MC_SIZE) {
+ dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
+ fw->size, MC_SIZE);
+ ret = -EINVAL;
+ goto release_firmware;
+ }
+
+ mc_addr = dma_alloc_coherent(core->dev, MC_SIZE, &mc_addr_map,
+ GFP_KERNEL);
+ if (!mc_addr) {
+ dev_err(dev, "Failed allocating memory for firmware loading\n");
+ ret = -ENOMEM;
+ goto release_firmware;
+ }
+
+ memcpy(mc_addr, fw->data, MC_SIZE);
+
+ amvdec_write_dos(core, HEVC_MPSR, 0);
+ amvdec_write_dos(core, HEVC_CPSR, 0);
+
+ amvdec_write_dos(core, HEVC_IMEM_DMA_ADR, mc_addr_map);
+ amvdec_write_dos(core, HEVC_IMEM_DMA_COUNT, MC_SIZE / 4);
+ amvdec_write_dos(core, HEVC_IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
+
+ while (i && (readl(core->dos_base + HEVC_IMEM_DMA_CTRL) & 0x8000))
+ i--;
+
+ if (i == 0) {
+ dev_err(dev, "Firmware load fail (DMA hang?)\n");
+ ret = -ENODEV;
+ }
+
+ dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map);
+release_firmware:
+ release_firmware(fw);
+ return ret;
+}
+
+static void vdec_hevc_stbuf_init(struct amvdec_session *sess)
+{
+ struct amvdec_core *core = sess->core;
+
+ amvdec_write_dos(core, HEVC_STREAM_CONTROL,
+ amvdec_read_dos(core, HEVC_STREAM_CONTROL) & ~1);
+ amvdec_write_dos(core, HEVC_STREAM_START_ADDR, sess->vififo_paddr);
+ amvdec_write_dos(core, HEVC_STREAM_END_ADDR,
+ sess->vififo_paddr + sess->vififo_size);
+ amvdec_write_dos(core, HEVC_STREAM_RD_PTR, sess->vififo_paddr);
+ amvdec_write_dos(core, HEVC_STREAM_WR_PTR, sess->vififo_paddr);
+}
+
+/* VDEC_HEVC specific ESPARSER configuration */
+static void vdec_hevc_conf_esparser(struct amvdec_session *sess)
+{
+ struct amvdec_core *core = sess->core;
+
+ /* set vififo_vbuf_rp_sel=>vdec_hevc */
+ amvdec_write_dos(core, DOS_GEN_CTRL0, 3 << 1);
+ amvdec_write_dos(core, HEVC_STREAM_CONTROL,
+ amvdec_read_dos(core, HEVC_STREAM_CONTROL) | BIT(3));
+ amvdec_write_dos(core, HEVC_STREAM_CONTROL,
+ amvdec_read_dos(core, HEVC_STREAM_CONTROL) | 1);
+ amvdec_write_dos(core, HEVC_STREAM_FIFO_CTL,
+ amvdec_read_dos(core, HEVC_STREAM_FIFO_CTL) | BIT(29));
+}
+
+static u32 vdec_hevc_vififo_level(struct amvdec_session *sess)
+{
+ return readl_relaxed(sess->core->dos_base + HEVC_STREAM_LEVEL);
+}
+
+static int vdec_hevc_stop(struct amvdec_session *sess)
+{
+ struct amvdec_core *core = sess->core;
+ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
+
+ /* Disable interrupt */
+ amvdec_write_dos(core, HEVC_ASSIST_MBOX1_MASK, 0);
+ /* Disable firmware processor */
+ amvdec_write_dos(core, HEVC_MPSR, 0);
+
+ if (sess->priv)
+ codec_ops->stop(sess);
+
+ /* Enable VDEC_HEVC Isolation */
+ if (core->platform->revision == VDEC_REVISION_SM1)
+ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
+ GEN_PWR_VDEC_HEVC_SM1,
+ GEN_PWR_VDEC_HEVC_SM1);
+ else
+ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
+ 0xc00, 0xc00);
+
+ /* VDEC_HEVC Memories */
+ amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0xffffffffUL);
+
+ if (core->platform->revision == VDEC_REVISION_SM1)
+ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
+ GEN_PWR_VDEC_HEVC_SM1,
+ GEN_PWR_VDEC_HEVC_SM1);
+ else
+ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
+ GEN_PWR_VDEC_HEVC, GEN_PWR_VDEC_HEVC);
+
+ clk_disable_unprepare(core->vdec_hevc_clk);
+ if (core->platform->revision == VDEC_REVISION_G12A ||
+ core->platform->revision == VDEC_REVISION_SM1)
+ clk_disable_unprepare(core->vdec_hevcf_clk);
+
+ return 0;
+}
+
+static int vdec_hevc_start(struct amvdec_session *sess)
+{
+ int ret;
+ struct amvdec_core *core = sess->core;
+ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
+
+ if (core->platform->revision == VDEC_REVISION_G12A ||
+ core->platform->revision == VDEC_REVISION_SM1) {
+ clk_set_rate(core->vdec_hevcf_clk, 666666666);
+ ret = clk_prepare_enable(core->vdec_hevcf_clk);
+ if (ret)
+ return ret;
+ }
+
+ clk_set_rate(core->vdec_hevc_clk, 666666666);
+ ret = clk_prepare_enable(core->vdec_hevc_clk);
+ if (ret)
+ return ret;
+
+ if (core->platform->revision == VDEC_REVISION_SM1)
+ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
+ GEN_PWR_VDEC_HEVC_SM1, 0);
+ else
+ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
+ GEN_PWR_VDEC_HEVC, 0);
+ udelay(10);
+
+ /* Reset VDEC_HEVC*/
+ amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff);
+ amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000);
+
+ amvdec_write_dos(core, DOS_GCLK_EN3, 0xffffffff);
+
+ /* VDEC_HEVC Memories */
+ amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0x00000000);
+
+ /* Remove VDEC_HEVC Isolation */
+ if (core->platform->revision == VDEC_REVISION_SM1)
+ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
+ GEN_PWR_VDEC_HEVC_SM1, 0);
+ else
+ regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
+ 0xc00, 0);
+
+ amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff);
+ amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000);
+
+ vdec_hevc_stbuf_init(sess);
+
+ ret = vdec_hevc_load_firmware(sess, sess->fmt_out->firmware_path);
+ if (ret)
+ goto stop;
+
+ ret = codec_ops->start(sess);
+ if (ret)
+ goto stop;
+
+ amvdec_write_dos(core, DOS_SW_RESET3, BIT(12) | BIT(11));
+ amvdec_write_dos(core, DOS_SW_RESET3, 0);
+ amvdec_read_dos(core, DOS_SW_RESET3);
+
+ amvdec_write_dos(core, HEVC_MPSR, 1);
+ /* Let the firmware settle */
+ udelay(10);
+
+ return 0;
+
+stop:
+ vdec_hevc_stop(sess);
+ return ret;
+}
+
+struct amvdec_ops vdec_hevc_ops = {
+ .start = vdec_hevc_start,
+ .stop = vdec_hevc_stop,
+ .conf_esparser = vdec_hevc_conf_esparser,
+ .vififo_level = vdec_hevc_vififo_level,
+};
diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.h b/drivers/staging/media/meson/vdec/vdec_hevc.h
new file mode 100644
index 000000000000..cd576a73a966
--- /dev/null
+++ b/drivers/staging/media/meson/vdec/vdec_hevc.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 Maxime Jourdan <[email protected]>
+ */
+
+#ifndef __MESON_VDEC_VDEC_HEVC_H_
+#define __MESON_VDEC_VDEC_HEVC_H_
+
+#include "vdec.h"
+
+extern struct amvdec_ops vdec_hevc_ops;
+
+#endif
--
2.22.0

2020-02-06 09:23:33

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 4/5] media: meson: vdec: add VP9 input support

From: Maxime Jourdan <[email protected]>

Amlogic VP9 decoder requires an additional 16-byte payload before every
frame header.

The source buffer is updated in-place, then given to the Parser FIFO DMA.

The FIFO DMA copies the blocks into the 16MiB parser ring buffer, then parses
and copies the slice into the decoder "workspace".

Signed-off-by: Maxime Jourdan <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/staging/media/meson/vdec/esparser.c | 150 +++++++++++++++++++-
1 file changed, 146 insertions(+), 4 deletions(-)

diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c
index adc5c1e81a4c..4a9aad3fafeb 100644
--- a/drivers/staging/media/meson/vdec/esparser.c
+++ b/drivers/staging/media/meson/vdec/esparser.c
@@ -52,6 +52,7 @@
#define PARSER_VIDEO_HOLE 0x90

#define SEARCH_PATTERN_LEN 512
+#define VP9_HEADER_SIZE 16

static DECLARE_WAIT_QUEUE_HEAD(wq);
static int search_done;
@@ -74,14 +75,121 @@ static irqreturn_t esparser_isr(int irq, void *dev)
return IRQ_HANDLED;
}

+/**
+ * VP9 frame headers need to be appended by a 16-byte long
+ * Amlogic custom header
+ */
+static int vp9_update_header(struct amvdec_core *core, struct vb2_buffer *buf)
+{
+ u8 *dp;
+ u8 marker;
+ int dsize;
+ int num_frames, cur_frame;
+ int cur_mag, mag, mag_ptr;
+ int frame_size[8], tot_frame_size[8];
+ int total_datasize = 0;
+ int new_frame_size;
+ unsigned char *old_header = NULL;
+
+ dp = (uint8_t *)vb2_plane_vaddr(buf, 0);
+ dsize = vb2_get_plane_payload(buf, 0);
+
+ if (dsize == vb2_plane_size(buf, 0)) {
+ dev_warn(core->dev, "%s: unable to update header\n", __func__);
+ return 0;
+ }
+
+ marker = dp[dsize - 1];
+ if ((marker & 0xe0) == 0xc0) {
+ num_frames = (marker & 0x7) + 1;
+ mag = ((marker >> 3) & 0x3) + 1;
+ mag_ptr = dsize - mag * num_frames - 2;
+ if (dp[mag_ptr] != marker)
+ return 0;
+
+ mag_ptr++;
+ for (cur_frame = 0; cur_frame < num_frames; cur_frame++) {
+ frame_size[cur_frame] = 0;
+ for (cur_mag = 0; cur_mag < mag; cur_mag++) {
+ frame_size[cur_frame] |=
+ (dp[mag_ptr] << (cur_mag * 8));
+ mag_ptr++;
+ }
+ if (cur_frame == 0)
+ tot_frame_size[cur_frame] =
+ frame_size[cur_frame];
+ else
+ tot_frame_size[cur_frame] =
+ tot_frame_size[cur_frame - 1] +
+ frame_size[cur_frame];
+ total_datasize += frame_size[cur_frame];
+ }
+ } else {
+ num_frames = 1;
+ frame_size[0] = dsize;
+ tot_frame_size[0] = dsize;
+ total_datasize = dsize;
+ }
+
+ new_frame_size = total_datasize + num_frames * VP9_HEADER_SIZE;
+
+ if (new_frame_size >= vb2_plane_size(buf, 0)) {
+ dev_warn(core->dev, "%s: unable to update header\n", __func__);
+ return 0;
+ }
+
+ for (cur_frame = num_frames - 1; cur_frame >= 0; cur_frame--) {
+ int framesize = frame_size[cur_frame];
+ int framesize_header = framesize + 4;
+ int oldframeoff = tot_frame_size[cur_frame] - framesize;
+ int outheaderoff = oldframeoff + cur_frame * VP9_HEADER_SIZE;
+ u8 *fdata = dp + outheaderoff;
+ u8 *old_framedata = dp + oldframeoff;
+
+ memmove(fdata + VP9_HEADER_SIZE, old_framedata, framesize);
+
+ fdata[0] = (framesize_header >> 24) & 0xff;
+ fdata[1] = (framesize_header >> 16) & 0xff;
+ fdata[2] = (framesize_header >> 8) & 0xff;
+ fdata[3] = (framesize_header >> 0) & 0xff;
+ fdata[4] = ((framesize_header >> 24) & 0xff) ^ 0xff;
+ fdata[5] = ((framesize_header >> 16) & 0xff) ^ 0xff;
+ fdata[6] = ((framesize_header >> 8) & 0xff) ^ 0xff;
+ fdata[7] = ((framesize_header >> 0) & 0xff) ^ 0xff;
+ fdata[8] = 0;
+ fdata[9] = 0;
+ fdata[10] = 0;
+ fdata[11] = 1;
+ fdata[12] = 'A';
+ fdata[13] = 'M';
+ fdata[14] = 'L';
+ fdata[15] = 'V';
+
+ if (!old_header) {
+ /* nothing */
+ } else if (old_header > fdata + 16 + framesize) {
+ dev_dbg(core->dev, "%s: data has gaps, setting to 0\n",
+ __func__);
+ memset(fdata + 16 + framesize, 0,
+ (old_header - fdata + 16 + framesize));
+ } else if (old_header < fdata + 16 + framesize) {
+ dev_err(core->dev, "%s: data overwritten\n", __func__);
+ }
+ old_header = fdata;
+ }
+
+ return new_frame_size;
+}
+
/* Pad the packet to at least 4KiB bytes otherwise the VDEC unit won't trigger
* ISRs.
* Also append a start code 000001ff at the end to trigger
* the ESPARSER interrupt.
*/
-static u32 esparser_pad_start_code(struct amvdec_core *core, struct vb2_buffer *vb)
+static u32 esparser_pad_start_code(struct amvdec_core *core,
+ struct vb2_buffer *vb,
+ u32 payload_size)
{
- u32 payload_size = vb2_get_plane_payload(vb, 0);
u32 pad_size = 0;
u8 *vaddr = vb2_plane_vaddr(vb, 0);

@@ -186,13 +294,35 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
int ret;
struct vb2_buffer *vb = &vbuf->vb2_buf;
struct amvdec_core *core = sess->core;
+ struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
u32 payload_size = vb2_get_plane_payload(vb, 0);
dma_addr_t phy = vb2_dma_contig_plane_dma_addr(vb, 0);
+ u32 num_dst_bufs = 0;
u32 offset;
u32 pad_size;

- if (esparser_vififo_get_free_space(sess) < payload_size)
+ /*
+ * When max ref frame is held by VP9, this should be -= 3 to prevent a
+ * shortage of CAPTURE buffers on the decoder side.
+ * For the future, a good enhancement of the way this is handled could
+ * be to notify new capture buffers to the decoding modules, so that
+ * they could pause when there is no capture buffer available and
+ * resume on this notification.
+ */
+ if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
+ if (codec_ops->num_pending_bufs)
+ num_dst_bufs = codec_ops->num_pending_bufs(sess);
+
+ num_dst_bufs += v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
+ if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9)
+ num_dst_bufs -= 3;
+
+ if (esparser_vififo_get_free_space(sess) < payload_size ||
+ atomic_read(&sess->esparser_queued_bufs) >= num_dst_bufs)
+ return -EAGAIN;
+ } else if (esparser_vififo_get_free_space(sess) < payload_size) {
return -EAGAIN;
+ }

v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf);

@@ -206,7 +336,19 @@ esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf)
vbuf->field = V4L2_FIELD_NONE;
vbuf->sequence = sess->sequence_out++;

- pad_size = esparser_pad_start_code(core, vb);
+ if (sess->fmt_out->pixfmt == V4L2_PIX_FMT_VP9) {
+ payload_size = vp9_update_header(core, vb);
+
+ /* If unable to alter buffer to add headers */
+ if (payload_size == 0) {
+ amvdec_remove_ts(sess, vb->timestamp);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+
+ return 0;
+ }
+ }
+
+ pad_size = esparser_pad_start_code(core, vb, payload_size);
ret = esparser_write_data(core, phy, payload_size + pad_size);

if (ret <= 0) {
--
2.22.0

2020-02-14 15:07:43

by Hans Verkuil

[permalink] [raw]
Subject: Re: [PATCH v4 3/5] media: meson: vdec: add common HEVC decoder support

On 2/6/20 9:41 AM, Neil Armstrong wrote:
> From: Maxime Jourdan <[email protected]>
>
> Add support for the HEVC & VP9 common decoder support, handling
> Amlogic GXBB, GXL, G12A and SM1 platforms.
>
> This handles the "HEVC" hw decoder used for HEVC and VP9, and will be
> using in the new H264 multi-instance decoder for G12A & SM1 platforms.
>
> Signed-off-by: Maxime Jourdan <[email protected]>
> Signed-off-by: Neil Armstrong <[email protected]>

I'm getting some checkpatch warnings/checks:

WARNING: Possible unnecessary 'out of memory' message
#219: FILE: drivers/staging/media/meson/vdec/codec_hevc_common.c:171:
+ if (!vaddr) {
+ dev_err(dev, "Couldn't allocate FBC buffer %u\n", idx);

WARNING: Possible unnecessary 'out of memory' message
#273: FILE: drivers/staging/media/meson/vdec/codec_hevc_common.c:225:
+ if (!vaddr) {
+ dev_err(dev, "Couldn't allocate MMU header %u\n", idx);

WARNING: Possible unnecessary 'out of memory' message
#692: FILE: drivers/staging/media/meson/vdec/vdec_hevc.c:52:
+ if (!mc_addr) {
+ dev_err(dev, "Failed allocating memory for firmware loading\n");

CHECK: usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst
#819: FILE: drivers/staging/media/meson/vdec/vdec_hevc.c:179:
+ udelay(10);

CHECK: usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst
#857: FILE: drivers/staging/media/meson/vdec/vdec_hevc.c:217:
+ udelay(10);

Can you take a look?

Regards,

Hans

> ---
> drivers/staging/media/meson/vdec/Makefile | 4 +-
> .../media/meson/vdec/codec_hevc_common.c | 286 ++++++++++++++++++
> .../media/meson/vdec/codec_hevc_common.h | 77 +++++
> drivers/staging/media/meson/vdec/hevc_regs.h | 211 +++++++++++++
> drivers/staging/media/meson/vdec/vdec_hevc.c | 231 ++++++++++++++
> drivers/staging/media/meson/vdec/vdec_hevc.h | 13 +
> 6 files changed, 820 insertions(+), 2 deletions(-)
> create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.c
> create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.h
> create mode 100644 drivers/staging/media/meson/vdec/hevc_regs.h
> create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.c
> create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.h
>
> diff --git a/drivers/staging/media/meson/vdec/Makefile b/drivers/staging/media/meson/vdec/Makefile
> index 711d990c760e..f55b6e625034 100644
> --- a/drivers/staging/media/meson/vdec/Makefile
> +++ b/drivers/staging/media/meson/vdec/Makefile
> @@ -2,7 +2,7 @@
> # Makefile for Amlogic meson video decoder driver
>
> meson-vdec-objs = esparser.o vdec.o vdec_helpers.o vdec_platform.o
> -meson-vdec-objs += vdec_1.o
> -meson-vdec-objs += codec_mpeg12.o codec_h264.o
> +meson-vdec-objs += vdec_1.o vdec_hevc.o
> +meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_hevc_common.o
>
> obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o
> diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.c b/drivers/staging/media/meson/vdec/codec_hevc_common.c
> new file mode 100644
> index 000000000000..335bcba062ac
> --- /dev/null
> +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2018 Maxime Jourdan <[email protected]>
> + */
> +
> +#include <media/v4l2-mem2mem.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#include "codec_hevc_common.h"
> +#include "vdec_helpers.h"
> +#include "hevc_regs.h"
> +
> +#define MMU_COMPRESS_HEADER_SIZE 0x48000
> +#define MMU_MAP_SIZE 0x4800
> +
> +/* Configure decode head read mode */
> +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit)
> +{
> + struct amvdec_core *core = sess->core;
> + u32 body_size = amvdec_am21c_body_size(sess->width, sess->height);
> + u32 head_size = amvdec_am21c_head_size(sess->width, sess->height);
> +
> + if (!codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) {
> + /* Enable 2-plane reference read mode */
> + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(31));
> + return;
> + }
> +
> + if (codec_hevc_use_mmu(core->platform->revision,
> + sess->pixfmt_cap, is_10bit))
> + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(4));
> + else
> + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, 0);
> +
> + if (core->platform->revision < VDEC_REVISION_SM1)
> + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL2, body_size / 32);
> + amvdec_write_dos(core, HEVC_CM_BODY_LENGTH, body_size);
> + amvdec_write_dos(core, HEVC_CM_HEADER_OFFSET, body_size);
> + amvdec_write_dos(core, HEVC_CM_HEADER_LENGTH, head_size);
> +}
> +EXPORT_SYMBOL_GPL(codec_hevc_setup_decode_head);
> +
> +static void codec_hevc_setup_buffers_gxbb(struct amvdec_session *sess,
> + struct codec_hevc_common *comm,
> + int is_10bit)
> +{
> + struct amvdec_core *core = sess->core;
> + struct v4l2_m2m_buffer *buf;
> + u32 buf_num = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
> + dma_addr_t buf_y_paddr = 0;
> + dma_addr_t buf_uv_paddr = 0;
> + u32 idx = 0;
> + u32 val;
> + int i;
> +
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0);
> +
> + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
> + struct vb2_buffer *vb = &buf->vb.vb2_buf;
> +
> + idx = vb->index;
> +
> + if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit))
> + buf_y_paddr = comm->fbc_buffer_paddr[idx];
> + else
> + buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
> +
> + if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) {
> + val = buf_y_paddr | (idx << 8) | 1;
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
> + val);
> + } else {
> + buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1);
> + val = buf_y_paddr | ((idx * 2) << 8) | 1;
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
> + val);
> + val = buf_uv_paddr | ((idx * 2 + 1) << 8) | 1;
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
> + val);
> + }
> + }
> +
> + if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit))
> + val = buf_y_paddr | (idx << 8) | 1;
> + else
> + val = buf_y_paddr | ((idx * 2) << 8) | 1;
> +
> + /* Fill the remaining unused slots with the last buffer's Y addr */
> + for (i = buf_num; i < MAX_REF_PIC_NUM; ++i)
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, val);
> +
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
> + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1);
> + for (i = 0; i < 32; ++i)
> + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
> +}
> +
> +static void codec_hevc_setup_buffers_gxl(struct amvdec_session *sess,
> + struct codec_hevc_common *comm,
> + int is_10bit)
> +{
> + struct amvdec_core *core = sess->core;
> + struct v4l2_m2m_buffer *buf;
> + u32 revision = core->platform->revision;
> + u32 pixfmt_cap = sess->pixfmt_cap;
> + int i;
> +
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR,
> + BIT(2) | BIT(1));
> +
> + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
> + struct vb2_buffer *vb = &buf->vb.vb2_buf;
> + dma_addr_t buf_y_paddr = 0;
> + dma_addr_t buf_uv_paddr = 0;
> + u32 idx = vb->index;
> +
> + if (codec_hevc_use_mmu(revision, pixfmt_cap, is_10bit))
> + buf_y_paddr = comm->mmu_header_paddr[idx];
> + else if (codec_hevc_use_downsample(pixfmt_cap, is_10bit))
> + buf_y_paddr = comm->fbc_buffer_paddr[idx];
> + else
> + buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
> +
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA,
> + buf_y_paddr >> 5);
> +
> + if (!codec_hevc_use_fbc(pixfmt_cap, is_10bit)) {
> + buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1);
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA,
> + buf_uv_paddr >> 5);
> + }
> + }
> +
> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
> + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1);
> + for (i = 0; i < 32; ++i)
> + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
> +}
> +
> +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess,
> + struct codec_hevc_common *comm)
> +{
> + struct device *dev = sess->core->dev;
> + u32 am21_size = amvdec_am21c_size(sess->width, sess->height);
> + int i;
> +
> + for (i = 0; i < MAX_REF_PIC_NUM; ++i) {
> + if (comm->fbc_buffer_vaddr[i]) {
> + dma_free_coherent(dev, am21_size,
> + comm->fbc_buffer_vaddr[i],
> + comm->fbc_buffer_paddr[i]);
> + comm->fbc_buffer_vaddr[i] = NULL;
> + }
> + }
> +}
> +EXPORT_SYMBOL_GPL(codec_hevc_free_fbc_buffers);
> +
> +static int codec_hevc_alloc_fbc_buffers(struct amvdec_session *sess,
> + struct codec_hevc_common *comm)
> +{
> + struct device *dev = sess->core->dev;
> + struct v4l2_m2m_buffer *buf;
> + u32 am21_size = amvdec_am21c_size(sess->width, sess->height);
> +
> + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
> + u32 idx = buf->vb.vb2_buf.index;
> + dma_addr_t paddr;
> + void *vaddr = dma_alloc_coherent(dev, am21_size, &paddr,
> + GFP_KERNEL);
> + if (!vaddr) {
> + dev_err(dev, "Couldn't allocate FBC buffer %u\n", idx);
> + codec_hevc_free_fbc_buffers(sess, comm);
> + return -ENOMEM;
> + }
> +
> + comm->fbc_buffer_vaddr[idx] = vaddr;
> + comm->fbc_buffer_paddr[idx] = paddr;
> + }
> +
> + return 0;
> +}
> +
> +void codec_hevc_free_mmu_headers(struct amvdec_session *sess,
> + struct codec_hevc_common *comm)
> +{
> + struct device *dev = sess->core->dev;
> + int i;
> +
> + for (i = 0; i < MAX_REF_PIC_NUM; ++i) {
> + if (comm->mmu_header_vaddr[i]) {
> + dma_free_coherent(dev, MMU_COMPRESS_HEADER_SIZE,
> + comm->mmu_header_vaddr[i],
> + comm->mmu_header_paddr[i]);
> + comm->mmu_header_vaddr[i] = NULL;
> + }
> + }
> +
> + if (comm->mmu_map_vaddr) {
> + dma_free_coherent(dev, MMU_MAP_SIZE,
> + comm->mmu_map_vaddr,
> + comm->mmu_map_paddr);
> + comm->mmu_map_vaddr = NULL;
> + }
> +}
> +EXPORT_SYMBOL_GPL(codec_hevc_free_mmu_headers);
> +
> +static int codec_hevc_alloc_mmu_headers(struct amvdec_session *sess,
> + struct codec_hevc_common *comm)
> +{
> + struct device *dev = sess->core->dev;
> + struct v4l2_m2m_buffer *buf;
> +
> + comm->mmu_map_vaddr = dma_alloc_coherent(dev, MMU_MAP_SIZE,
> + &comm->mmu_map_paddr,
> + GFP_KERNEL);
> + if (!comm->mmu_map_vaddr)
> + return -ENOMEM;
> +
> + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
> + u32 idx = buf->vb.vb2_buf.index;
> + dma_addr_t paddr;
> + void *vaddr = dma_alloc_coherent(dev, MMU_COMPRESS_HEADER_SIZE,
> + &paddr, GFP_KERNEL);
> + if (!vaddr) {
> + dev_err(dev, "Couldn't allocate MMU header %u\n", idx);
> + codec_hevc_free_mmu_headers(sess, comm);
> + return -ENOMEM;
> + }
> +
> + comm->mmu_header_vaddr[idx] = vaddr;
> + comm->mmu_header_paddr[idx] = paddr;
> + }
> +
> + return 0;
> +}
> +
> +int codec_hevc_setup_buffers(struct amvdec_session *sess,
> + struct codec_hevc_common *comm,
> + int is_10bit)
> +{
> + struct amvdec_core *core = sess->core;
> + int ret;
> +
> + if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit)) {
> + ret = codec_hevc_alloc_fbc_buffers(sess, comm);
> + if (ret)
> + return ret;
> + }
> +
> + if (codec_hevc_use_mmu(core->platform->revision,
> + sess->pixfmt_cap, is_10bit)) {
> + ret = codec_hevc_alloc_mmu_headers(sess, comm);
> + if (ret) {
> + codec_hevc_free_fbc_buffers(sess, comm);
> + return ret;
> + }
> + }
> +
> + if (core->platform->revision == VDEC_REVISION_GXBB)
> + codec_hevc_setup_buffers_gxbb(sess, comm, is_10bit);
> + else
> + codec_hevc_setup_buffers_gxl(sess, comm, is_10bit);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(codec_hevc_setup_buffers);
> +
> +void codec_hevc_fill_mmu_map(struct amvdec_session *sess,
> + struct codec_hevc_common *comm,
> + struct vb2_buffer *vb)
> +{
> + u32 size = amvdec_am21c_size(sess->width, sess->height);
> + u32 nb_pages = size / PAGE_SIZE;
> + u32 *mmu_map = comm->mmu_map_vaddr;
> + u32 first_page;
> + u32 i;
> +
> + if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M)
> + first_page = comm->fbc_buffer_paddr[vb->index] >> PAGE_SHIFT;
> + else
> + first_page = vb2_dma_contig_plane_dma_addr(vb, 0) >> PAGE_SHIFT;
> +
> + for (i = 0; i < nb_pages; ++i)
> + mmu_map[i] = first_page + i;
> +}
> +EXPORT_SYMBOL_GPL(codec_hevc_fill_mmu_map);
> diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.h b/drivers/staging/media/meson/vdec/codec_hevc_common.h
> new file mode 100644
> index 000000000000..de16d2e43061
> --- /dev/null
> +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.h
> @@ -0,0 +1,77 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2018 BayLibre, SAS
> + * Author: Maxime Jourdan <[email protected]>
> + */
> +
> +#ifndef __MESON_VDEC_HEVC_COMMON_H_
> +#define __MESON_VDEC_HEVC_COMMON_H_
> +
> +#include "vdec.h"
> +
> +#define PARSER_CMD_SKIP_CFG_0 0x0000090b
> +#define PARSER_CMD_SKIP_CFG_1 0x1b14140f
> +#define PARSER_CMD_SKIP_CFG_2 0x001b1910
> +static const u16 vdec_hevc_parser_cmd[] = {
> + 0x0401, 0x8401, 0x0800, 0x0402,
> + 0x9002, 0x1423, 0x8CC3, 0x1423,
> + 0x8804, 0x9825, 0x0800, 0x04FE,
> + 0x8406, 0x8411, 0x1800, 0x8408,
> + 0x8409, 0x8C2A, 0x9C2B, 0x1C00,
> + 0x840F, 0x8407, 0x8000, 0x8408,
> + 0x2000, 0xA800, 0x8410, 0x04DE,
> + 0x840C, 0x840D, 0xAC00, 0xA000,
> + 0x08C0, 0x08E0, 0xA40E, 0xFC00,
> + 0x7C00
> +};
> +
> +#define MAX_REF_PIC_NUM 24
> +
> +struct codec_hevc_common {
> + void *fbc_buffer_vaddr[MAX_REF_PIC_NUM];
> + dma_addr_t fbc_buffer_paddr[MAX_REF_PIC_NUM];
> +
> + void *mmu_header_vaddr[MAX_REF_PIC_NUM];
> + dma_addr_t mmu_header_paddr[MAX_REF_PIC_NUM];
> +
> + void *mmu_map_vaddr;
> + dma_addr_t mmu_map_paddr;
> +};
> +
> +/* Returns 1 if we must use framebuffer compression */
> +static inline int codec_hevc_use_fbc(u32 pixfmt, int is_10bit)
> +{
> + /* TOFIX: Handle Amlogic Compressed buffer for 8bit also */
> + return is_10bit;
> +}
> +
> +/* Returns 1 if we are decoding 10-bit but outputting 8-bit NV12 */
> +static inline int codec_hevc_use_downsample(u32 pixfmt, int is_10bit)
> +{
> + return is_10bit;
> +}
> +
> +/* Returns 1 if we are decoding using the IOMMU */
> +static inline int codec_hevc_use_mmu(u32 revision, u32 pixfmt, int is_10bit)
> +{
> + return revision >= VDEC_REVISION_G12A &&
> + codec_hevc_use_fbc(pixfmt, is_10bit);
> +}
> +
> +/**
> + * Configure decode head read mode
> + */
> +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit);
> +
> +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess,
> + struct codec_hevc_common *comm);
> +
> +int codec_hevc_setup_buffers(struct amvdec_session *sess,
> + struct codec_hevc_common *comm,
> + int is_10bit);
> +
> +void codec_hevc_fill_mmu_map(struct amvdec_session *sess,
> + struct codec_hevc_common *comm,
> + struct vb2_buffer *vb);
> +
> +#endif
> diff --git a/drivers/staging/media/meson/vdec/hevc_regs.h b/drivers/staging/media/meson/vdec/hevc_regs.h
> new file mode 100644
> index 000000000000..55c1a80b955a
> --- /dev/null
> +++ b/drivers/staging/media/meson/vdec/hevc_regs.h
> @@ -0,0 +1,211 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
> + */
> +
> +#ifndef __MESON_VDEC_HEVC_REGS_H_
> +#define __MESON_VDEC_HEVC_REGS_H_
> +
> +#define HEVC_ASSIST_MMU_MAP_ADDR 0xc024
> +
> +#define HEVC_ASSIST_MBOX1_CLR_REG 0xc1d4
> +#define HEVC_ASSIST_MBOX1_MASK 0xc1d8
> +
> +#define HEVC_ASSIST_SCRATCH_0 0xc300
> +#define HEVC_ASSIST_SCRATCH_1 0xc304
> +#define HEVC_ASSIST_SCRATCH_2 0xc308
> +#define HEVC_ASSIST_SCRATCH_3 0xc30c
> +#define HEVC_ASSIST_SCRATCH_4 0xc310
> +#define HEVC_ASSIST_SCRATCH_5 0xc314
> +#define HEVC_ASSIST_SCRATCH_6 0xc318
> +#define HEVC_ASSIST_SCRATCH_7 0xc31c
> +#define HEVC_ASSIST_SCRATCH_8 0xc320
> +#define HEVC_ASSIST_SCRATCH_9 0xc324
> +#define HEVC_ASSIST_SCRATCH_A 0xc328
> +#define HEVC_ASSIST_SCRATCH_B 0xc32c
> +#define HEVC_ASSIST_SCRATCH_C 0xc330
> +#define HEVC_ASSIST_SCRATCH_D 0xc334
> +#define HEVC_ASSIST_SCRATCH_E 0xc338
> +#define HEVC_ASSIST_SCRATCH_F 0xc33c
> +#define HEVC_ASSIST_SCRATCH_G 0xc340
> +#define HEVC_ASSIST_SCRATCH_H 0xc344
> +#define HEVC_ASSIST_SCRATCH_I 0xc348
> +#define HEVC_ASSIST_SCRATCH_J 0xc34c
> +#define HEVC_ASSIST_SCRATCH_K 0xc350
> +#define HEVC_ASSIST_SCRATCH_L 0xc354
> +#define HEVC_ASSIST_SCRATCH_M 0xc358
> +#define HEVC_ASSIST_SCRATCH_N 0xc35c
> +
> +#define HEVC_PARSER_VERSION 0xc400
> +#define HEVC_STREAM_CONTROL 0xc404
> +#define HEVC_STREAM_START_ADDR 0xc408
> +#define HEVC_STREAM_END_ADDR 0xc40c
> +#define HEVC_STREAM_WR_PTR 0xc410
> +#define HEVC_STREAM_RD_PTR 0xc414
> +#define HEVC_STREAM_LEVEL 0xc418
> +#define HEVC_STREAM_FIFO_CTL 0xc41c
> +#define HEVC_SHIFT_CONTROL 0xc420
> +#define HEVC_SHIFT_STARTCODE 0xc424
> +#define HEVC_SHIFT_EMULATECODE 0xc428
> +#define HEVC_SHIFT_STATUS 0xc42c
> +#define HEVC_SHIFTED_DATA 0xc430
> +#define HEVC_SHIFT_BYTE_COUNT 0xc434
> +#define HEVC_SHIFT_COMMAND 0xc438
> +#define HEVC_ELEMENT_RESULT 0xc43c
> +#define HEVC_CABAC_CONTROL 0xc440
> +#define HEVC_PARSER_SLICE_INFO 0xc444
> +#define HEVC_PARSER_CMD_WRITE 0xc448
> +#define HEVC_PARSER_CORE_CONTROL 0xc44c
> +#define HEVC_PARSER_CMD_FETCH 0xc450
> +#define HEVC_PARSER_CMD_STATUS 0xc454
> +#define HEVC_PARSER_LCU_INFO 0xc458
> +#define HEVC_PARSER_HEADER_INFO 0xc45c
> +#define HEVC_PARSER_INT_CONTROL 0xc480
> +#define HEVC_PARSER_INT_STATUS 0xc484
> +#define HEVC_PARSER_IF_CONTROL 0xc488
> +#define HEVC_PARSER_PICTURE_SIZE 0xc48c
> +#define HEVC_PARSER_LCU_START 0xc490
> +#define HEVC_PARSER_HEADER_INFO2 0xc494
> +#define HEVC_PARSER_QUANT_READ 0xc498
> +#define HEVC_PARSER_RESERVED_27 0xc49c
> +#define HEVC_PARSER_CMD_SKIP_0 0xc4a0
> +#define HEVC_PARSER_CMD_SKIP_1 0xc4a4
> +#define HEVC_PARSER_CMD_SKIP_2 0xc4a8
> +#define HEVC_SAO_IF_STATUS 0xc4c0
> +#define HEVC_SAO_IF_DATA_Y 0xc4c4
> +#define HEVC_SAO_IF_DATA_U 0xc4c8
> +#define HEVC_SAO_IF_DATA_V 0xc4cc
> +#define HEVC_STREAM_SWAP_ADDR 0xc4d0
> +#define HEVC_STREAM_SWAP_CTRL 0xc4d4
> +#define HEVC_IQIT_IF_WAIT_CNT 0xc4d8
> +#define HEVC_MPRED_IF_WAIT_CNT 0xc4dc
> +#define HEVC_SAO_IF_WAIT_CNT 0xc4e0
> +
> +#define HEVC_MPRED_VERSION 0xc800
> +#define HEVC_MPRED_CTRL0 0xc804
> + #define MPRED_CTRL0_NEW_PIC BIT(2)
> + #define MPRED_CTRL0_NEW_TILE BIT(3)
> + #define MPRED_CTRL0_NEW_SLI_SEG BIT(4)
> + #define MPRED_CTRL0_TMVP BIT(5)
> + #define MPRED_CTRL0_LDC BIT(6)
> + #define MPRED_CTRL0_COL_FROM_L0 BIT(7)
> + #define MPRED_CTRL0_ABOVE_EN BIT(9)
> + #define MPRED_CTRL0_MV_WR_EN BIT(10)
> + #define MPRED_CTRL0_MV_RD_EN BIT(11)
> + #define MPRED_CTRL0_BUF_LINEAR BIT(13)
> +#define HEVC_MPRED_CTRL1 0xc808
> +#define HEVC_MPRED_INT_EN 0xc80c
> +#define HEVC_MPRED_INT_STATUS 0xc810
> +#define HEVC_MPRED_PIC_SIZE 0xc814
> +#define HEVC_MPRED_PIC_SIZE_LCU 0xc818
> +#define HEVC_MPRED_TILE_START 0xc81c
> +#define HEVC_MPRED_TILE_SIZE_LCU 0xc820
> +#define HEVC_MPRED_REF_NUM 0xc824
> +#define HEVC_MPRED_REF_EN_L0 0xc830
> +#define HEVC_MPRED_REF_EN_L1 0xc834
> +#define HEVC_MPRED_COLREF_EN_L0 0xc838
> +#define HEVC_MPRED_COLREF_EN_L1 0xc83c
> +#define HEVC_MPRED_AXI_WCTRL 0xc840
> +#define HEVC_MPRED_AXI_RCTRL 0xc844
> +#define HEVC_MPRED_ABV_START_ADDR 0xc848
> +#define HEVC_MPRED_MV_WR_START_ADDR 0xc84c
> +#define HEVC_MPRED_MV_RD_START_ADDR 0xc850
> +#define HEVC_MPRED_MV_WPTR 0xc854
> +#define HEVC_MPRED_MV_RPTR 0xc858
> +#define HEVC_MPRED_MV_WR_ROW_JUMP 0xc85c
> +#define HEVC_MPRED_MV_RD_ROW_JUMP 0xc860
> +#define HEVC_MPRED_CURR_LCU 0xc864
> +#define HEVC_MPRED_ABV_WPTR 0xc868
> +#define HEVC_MPRED_ABV_RPTR 0xc86c
> +#define HEVC_MPRED_CTRL2 0xc870
> +#define HEVC_MPRED_CTRL3 0xc874
> +#define HEVC_MPRED_L0_REF00_POC 0xc880
> +#define HEVC_MPRED_L1_REF00_POC 0xc8c0
> +
> +#define HEVC_MPRED_CUR_POC 0xc980
> +#define HEVC_MPRED_COL_POC 0xc984
> +#define HEVC_MPRED_MV_RD_END_ADDR 0xc988
> +
> +#define HEVC_MSP 0xcc00
> +#define HEVC_MPSR 0xcc04
> +#define HEVC_MCPU_INTR_MSK 0xcc10
> +#define HEVC_MCPU_INTR_REQ 0xcc14
> +#define HEVC_CPSR 0xcc84
> +
> +#define HEVC_IMEM_DMA_CTRL 0xcd00
> +#define HEVC_IMEM_DMA_ADR 0xcd04
> +#define HEVC_IMEM_DMA_COUNT 0xcd08
> +
> +#define HEVCD_IPP_TOP_CNTL 0xd000
> +#define HEVCD_IPP_LINEBUFF_BASE 0xd024
> +#define HEVCD_IPP_AXIIF_CONFIG 0xd02c
> +
> +#define HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR 0xd180
> +#define HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR 0xd184
> +#define HEVCD_MPP_ANC2AXI_TBL_DATA 0xd190
> +
> +#define HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR 0xd300
> +#define HEVCD_MPP_ANC_CANVAS_DATA_ADDR 0xd304
> +#define HEVCD_MPP_DECOMP_CTL1 0xd308
> +#define HEVCD_MPP_DECOMP_CTL2 0xd30c
> +#define HEVCD_MCRCC_CTL1 0xd3c0
> +#define HEVCD_MCRCC_CTL2 0xd3c4
> +#define HEVCD_MCRCC_CTL3 0xd3c8
> +
> +#define HEVC_DBLK_CFG0 0xd400
> +#define HEVC_DBLK_CFG1 0xd404
> +#define HEVC_DBLK_CFG2 0xd408
> +#define HEVC_DBLK_CFG3 0xd40c
> +#define HEVC_DBLK_CFG4 0xd410
> +#define HEVC_DBLK_CFG5 0xd414
> +#define HEVC_DBLK_CFG6 0xd418
> +#define HEVC_DBLK_CFG7 0xd41c
> +#define HEVC_DBLK_CFG8 0xd420
> +#define HEVC_DBLK_CFG9 0xd424
> +#define HEVC_DBLK_CFGA 0xd428
> +#define HEVC_DBLK_STS0 0xd42c
> +#define HEVC_DBLK_STS1 0xd430
> +#define HEVC_DBLK_CFGE 0xd438
> +
> +#define HEVC_SAO_VERSION 0xd800
> +#define HEVC_SAO_CTRL0 0xd804
> +#define HEVC_SAO_CTRL1 0xd808
> +#define HEVC_SAO_PIC_SIZE 0xd814
> +#define HEVC_SAO_PIC_SIZE_LCU 0xd818
> +#define HEVC_SAO_TILE_START 0xd81c
> +#define HEVC_SAO_TILE_SIZE_LCU 0xd820
> +#define HEVC_SAO_Y_START_ADDR 0xd82c
> +#define HEVC_SAO_Y_LENGTH 0xd830
> +#define HEVC_SAO_C_START_ADDR 0xd834
> +#define HEVC_SAO_C_LENGTH 0xd838
> +#define HEVC_SAO_Y_WPTR 0xd83c
> +#define HEVC_SAO_C_WPTR 0xd840
> +#define HEVC_SAO_ABV_START_ADDR 0xd844
> +#define HEVC_SAO_VB_WR_START_ADDR 0xd848
> +#define HEVC_SAO_VB_RD_START_ADDR 0xd84c
> +#define HEVC_SAO_ABV_WPTR 0xd850
> +#define HEVC_SAO_ABV_RPTR 0xd854
> +#define HEVC_SAO_VB_WPTR 0xd858
> +#define HEVC_SAO_VB_RPTR 0xd85c
> +#define HEVC_SAO_CTRL2 0xd880
> +#define HEVC_SAO_CTRL3 0xd884
> +#define HEVC_SAO_CTRL4 0xd888
> +#define HEVC_SAO_CTRL5 0xd88c
> +#define HEVC_SAO_CTRL6 0xd890
> +#define HEVC_SAO_CTRL7 0xd894
> +#define HEVC_CM_BODY_START_ADDR 0xd898
> +#define HEVC_CM_BODY_LENGTH 0xd89c
> +#define HEVC_CM_HEADER_START_ADDR 0xd8a0
> +#define HEVC_CM_HEADER_LENGTH 0xd8a4
> +#define HEVC_CM_HEADER_OFFSET 0xd8ac
> +#define HEVC_SAO_MMU_VH0_ADDR 0xd8e8
> +#define HEVC_SAO_MMU_VH1_ADDR 0xd8ec
> +
> +#define HEVC_IQIT_CLK_RST_CTRL 0xdc00
> +#define HEVC_IQIT_SCALELUT_WR_ADDR 0xdc08
> +#define HEVC_IQIT_SCALELUT_RD_ADDR 0xdc0c
> +#define HEVC_IQIT_SCALELUT_DATA 0xdc10
> +
> +#define HEVC_PSCALE_CTRL 0xe444
> +
> +#endif
> diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.c b/drivers/staging/media/meson/vdec/vdec_hevc.c
> new file mode 100644
> index 000000000000..af41215e106c
> --- /dev/null
> +++ b/drivers/staging/media/meson/vdec/vdec_hevc.c
> @@ -0,0 +1,231 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2018 Maxime Jourdan <[email protected]>
> + *
> + * VDEC_HEVC is a video decoding block that allows decoding of
> + * HEVC, VP9
> + */
> +
> +#include <linux/firmware.h>
> +#include <linux/clk.h>
> +
> +#include "vdec_1.h"
> +#include "vdec_helpers.h"
> +#include "hevc_regs.h"
> +#include "dos_regs.h"
> +
> +/* AO Registers */
> +#define AO_RTI_GEN_PWR_SLEEP0 0xe8
> +#define AO_RTI_GEN_PWR_ISO0 0xec
> + #define GEN_PWR_VDEC_HEVC (BIT(7) | BIT(6))
> + #define GEN_PWR_VDEC_HEVC_SM1 (BIT(2))
> +
> +#define MC_SIZE (4096 * 4)
> +
> +static int vdec_hevc_load_firmware(struct amvdec_session *sess,
> + const char *fwname)
> +{
> + struct amvdec_core *core = sess->core;
> + struct device *dev = core->dev_dec;
> + const struct firmware *fw;
> + static void *mc_addr;
> + static dma_addr_t mc_addr_map;
> + int ret;
> + u32 i = 100;
> +
> + ret = request_firmware(&fw, fwname, dev);
> + if (ret < 0) {
> + dev_err(dev, "Unable to request firmware %s\n", fwname);
> + return ret;
> + }
> +
> + if (fw->size < MC_SIZE) {
> + dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
> + fw->size, MC_SIZE);
> + ret = -EINVAL;
> + goto release_firmware;
> + }
> +
> + mc_addr = dma_alloc_coherent(core->dev, MC_SIZE, &mc_addr_map,
> + GFP_KERNEL);
> + if (!mc_addr) {
> + dev_err(dev, "Failed allocating memory for firmware loading\n");
> + ret = -ENOMEM;
> + goto release_firmware;
> + }
> +
> + memcpy(mc_addr, fw->data, MC_SIZE);
> +
> + amvdec_write_dos(core, HEVC_MPSR, 0);
> + amvdec_write_dos(core, HEVC_CPSR, 0);
> +
> + amvdec_write_dos(core, HEVC_IMEM_DMA_ADR, mc_addr_map);
> + amvdec_write_dos(core, HEVC_IMEM_DMA_COUNT, MC_SIZE / 4);
> + amvdec_write_dos(core, HEVC_IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
> +
> + while (i && (readl(core->dos_base + HEVC_IMEM_DMA_CTRL) & 0x8000))
> + i--;
> +
> + if (i == 0) {
> + dev_err(dev, "Firmware load fail (DMA hang?)\n");
> + ret = -ENODEV;
> + }
> +
> + dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map);
> +release_firmware:
> + release_firmware(fw);
> + return ret;
> +}
> +
> +static void vdec_hevc_stbuf_init(struct amvdec_session *sess)
> +{
> + struct amvdec_core *core = sess->core;
> +
> + amvdec_write_dos(core, HEVC_STREAM_CONTROL,
> + amvdec_read_dos(core, HEVC_STREAM_CONTROL) & ~1);
> + amvdec_write_dos(core, HEVC_STREAM_START_ADDR, sess->vififo_paddr);
> + amvdec_write_dos(core, HEVC_STREAM_END_ADDR,
> + sess->vififo_paddr + sess->vififo_size);
> + amvdec_write_dos(core, HEVC_STREAM_RD_PTR, sess->vififo_paddr);
> + amvdec_write_dos(core, HEVC_STREAM_WR_PTR, sess->vififo_paddr);
> +}
> +
> +/* VDEC_HEVC specific ESPARSER configuration */
> +static void vdec_hevc_conf_esparser(struct amvdec_session *sess)
> +{
> + struct amvdec_core *core = sess->core;
> +
> + /* set vififo_vbuf_rp_sel=>vdec_hevc */
> + amvdec_write_dos(core, DOS_GEN_CTRL0, 3 << 1);
> + amvdec_write_dos(core, HEVC_STREAM_CONTROL,
> + amvdec_read_dos(core, HEVC_STREAM_CONTROL) | BIT(3));
> + amvdec_write_dos(core, HEVC_STREAM_CONTROL,
> + amvdec_read_dos(core, HEVC_STREAM_CONTROL) | 1);
> + amvdec_write_dos(core, HEVC_STREAM_FIFO_CTL,
> + amvdec_read_dos(core, HEVC_STREAM_FIFO_CTL) | BIT(29));
> +}
> +
> +static u32 vdec_hevc_vififo_level(struct amvdec_session *sess)
> +{
> + return readl_relaxed(sess->core->dos_base + HEVC_STREAM_LEVEL);
> +}
> +
> +static int vdec_hevc_stop(struct amvdec_session *sess)
> +{
> + struct amvdec_core *core = sess->core;
> + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
> +
> + /* Disable interrupt */
> + amvdec_write_dos(core, HEVC_ASSIST_MBOX1_MASK, 0);
> + /* Disable firmware processor */
> + amvdec_write_dos(core, HEVC_MPSR, 0);
> +
> + if (sess->priv)
> + codec_ops->stop(sess);
> +
> + /* Enable VDEC_HEVC Isolation */
> + if (core->platform->revision == VDEC_REVISION_SM1)
> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
> + GEN_PWR_VDEC_HEVC_SM1,
> + GEN_PWR_VDEC_HEVC_SM1);
> + else
> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
> + 0xc00, 0xc00);
> +
> + /* VDEC_HEVC Memories */
> + amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0xffffffffUL);
> +
> + if (core->platform->revision == VDEC_REVISION_SM1)
> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
> + GEN_PWR_VDEC_HEVC_SM1,
> + GEN_PWR_VDEC_HEVC_SM1);
> + else
> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
> + GEN_PWR_VDEC_HEVC, GEN_PWR_VDEC_HEVC);
> +
> + clk_disable_unprepare(core->vdec_hevc_clk);
> + if (core->platform->revision == VDEC_REVISION_G12A ||
> + core->platform->revision == VDEC_REVISION_SM1)
> + clk_disable_unprepare(core->vdec_hevcf_clk);
> +
> + return 0;
> +}
> +
> +static int vdec_hevc_start(struct amvdec_session *sess)
> +{
> + int ret;
> + struct amvdec_core *core = sess->core;
> + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
> +
> + if (core->platform->revision == VDEC_REVISION_G12A ||
> + core->platform->revision == VDEC_REVISION_SM1) {
> + clk_set_rate(core->vdec_hevcf_clk, 666666666);
> + ret = clk_prepare_enable(core->vdec_hevcf_clk);
> + if (ret)
> + return ret;
> + }
> +
> + clk_set_rate(core->vdec_hevc_clk, 666666666);
> + ret = clk_prepare_enable(core->vdec_hevc_clk);
> + if (ret)
> + return ret;
> +
> + if (core->platform->revision == VDEC_REVISION_SM1)
> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
> + GEN_PWR_VDEC_HEVC_SM1, 0);
> + else
> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
> + GEN_PWR_VDEC_HEVC, 0);
> + udelay(10);
> +
> + /* Reset VDEC_HEVC*/
> + amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff);
> + amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000);
> +
> + amvdec_write_dos(core, DOS_GCLK_EN3, 0xffffffff);
> +
> + /* VDEC_HEVC Memories */
> + amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0x00000000);
> +
> + /* Remove VDEC_HEVC Isolation */
> + if (core->platform->revision == VDEC_REVISION_SM1)
> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
> + GEN_PWR_VDEC_HEVC_SM1, 0);
> + else
> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
> + 0xc00, 0);
> +
> + amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff);
> + amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000);
> +
> + vdec_hevc_stbuf_init(sess);
> +
> + ret = vdec_hevc_load_firmware(sess, sess->fmt_out->firmware_path);
> + if (ret)
> + goto stop;
> +
> + ret = codec_ops->start(sess);
> + if (ret)
> + goto stop;
> +
> + amvdec_write_dos(core, DOS_SW_RESET3, BIT(12) | BIT(11));
> + amvdec_write_dos(core, DOS_SW_RESET3, 0);
> + amvdec_read_dos(core, DOS_SW_RESET3);
> +
> + amvdec_write_dos(core, HEVC_MPSR, 1);
> + /* Let the firmware settle */
> + udelay(10);
> +
> + return 0;
> +
> +stop:
> + vdec_hevc_stop(sess);
> + return ret;
> +}
> +
> +struct amvdec_ops vdec_hevc_ops = {
> + .start = vdec_hevc_start,
> + .stop = vdec_hevc_stop,
> + .conf_esparser = vdec_hevc_conf_esparser,
> + .vififo_level = vdec_hevc_vififo_level,
> +};
> diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.h b/drivers/staging/media/meson/vdec/vdec_hevc.h
> new file mode 100644
> index 000000000000..cd576a73a966
> --- /dev/null
> +++ b/drivers/staging/media/meson/vdec/vdec_hevc.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2018 Maxime Jourdan <[email protected]>
> + */
> +
> +#ifndef __MESON_VDEC_VDEC_HEVC_H_
> +#define __MESON_VDEC_VDEC_HEVC_H_
> +
> +#include "vdec.h"
> +
> +extern struct amvdec_ops vdec_hevc_ops;
> +
> +#endif
>

2020-02-14 15:24:44

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 3/5] media: meson: vdec: add common HEVC decoder support

On 14/02/2020 16:07, Hans Verkuil wrote:
> On 2/6/20 9:41 AM, Neil Armstrong wrote:
>> From: Maxime Jourdan <[email protected]>
>>
>> Add support for the HEVC & VP9 common decoder support, handling
>> Amlogic GXBB, GXL, G12A and SM1 platforms.
>>
>> This handles the "HEVC" hw decoder used for HEVC and VP9, and will be
>> using in the new H264 multi-instance decoder for G12A & SM1 platforms.
>>
>> Signed-off-by: Maxime Jourdan <[email protected]>
>> Signed-off-by: Neil Armstrong <[email protected]>
>
> I'm getting some checkpatch warnings/checks:
>
> WARNING: Possible unnecessary 'out of memory' message
> #219: FILE: drivers/staging/media/meson/vdec/codec_hevc_common.c:171:
> + if (!vaddr) {
> + dev_err(dev, "Couldn't allocate FBC buffer %u\n", idx);
>
> WARNING: Possible unnecessary 'out of memory' message
> #273: FILE: drivers/staging/media/meson/vdec/codec_hevc_common.c:225:
> + if (!vaddr) {
> + dev_err(dev, "Couldn't allocate MMU header %u\n", idx);
>
> WARNING: Possible unnecessary 'out of memory' message
> #692: FILE: drivers/staging/media/meson/vdec/vdec_hevc.c:52:
> + if (!mc_addr) {
> + dev_err(dev, "Failed allocating memory for firmware loading\n");
>
> CHECK: usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst
> #819: FILE: drivers/staging/media/meson/vdec/vdec_hevc.c:179:
> + udelay(10);
>
> CHECK: usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst
> #857: FILE: drivers/staging/media/meson/vdec/vdec_hevc.c:217:
> + udelay(10);
>
> Can you take a look?

I'll fix all these.

Neil

>
> Regards,
>
> Hans
>
>> ---
>> drivers/staging/media/meson/vdec/Makefile | 4 +-
>> .../media/meson/vdec/codec_hevc_common.c | 286 ++++++++++++++++++
>> .../media/meson/vdec/codec_hevc_common.h | 77 +++++
>> drivers/staging/media/meson/vdec/hevc_regs.h | 211 +++++++++++++
>> drivers/staging/media/meson/vdec/vdec_hevc.c | 231 ++++++++++++++
>> drivers/staging/media/meson/vdec/vdec_hevc.h | 13 +
>> 6 files changed, 820 insertions(+), 2 deletions(-)
>> create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.c
>> create mode 100644 drivers/staging/media/meson/vdec/codec_hevc_common.h
>> create mode 100644 drivers/staging/media/meson/vdec/hevc_regs.h
>> create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.c
>> create mode 100644 drivers/staging/media/meson/vdec/vdec_hevc.h
>>
>> diff --git a/drivers/staging/media/meson/vdec/Makefile b/drivers/staging/media/meson/vdec/Makefile
>> index 711d990c760e..f55b6e625034 100644
>> --- a/drivers/staging/media/meson/vdec/Makefile
>> +++ b/drivers/staging/media/meson/vdec/Makefile
>> @@ -2,7 +2,7 @@
>> # Makefile for Amlogic meson video decoder driver
>>
>> meson-vdec-objs = esparser.o vdec.o vdec_helpers.o vdec_platform.o
>> -meson-vdec-objs += vdec_1.o
>> -meson-vdec-objs += codec_mpeg12.o codec_h264.o
>> +meson-vdec-objs += vdec_1.o vdec_hevc.o
>> +meson-vdec-objs += codec_mpeg12.o codec_h264.o codec_hevc_common.o
>>
>> obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o
>> diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.c b/drivers/staging/media/meson/vdec/codec_hevc_common.c
>> new file mode 100644
>> index 000000000000..335bcba062ac
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.c
>> @@ -0,0 +1,286 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright (C) 2018 Maxime Jourdan <[email protected]>
>> + */
>> +
>> +#include <media/v4l2-mem2mem.h>
>> +#include <media/videobuf2-dma-contig.h>
>> +
>> +#include "codec_hevc_common.h"
>> +#include "vdec_helpers.h"
>> +#include "hevc_regs.h"
>> +
>> +#define MMU_COMPRESS_HEADER_SIZE 0x48000
>> +#define MMU_MAP_SIZE 0x4800
>> +
>> +/* Configure decode head read mode */
>> +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit)
>> +{
>> + struct amvdec_core *core = sess->core;
>> + u32 body_size = amvdec_am21c_body_size(sess->width, sess->height);
>> + u32 head_size = amvdec_am21c_head_size(sess->width, sess->height);
>> +
>> + if (!codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) {
>> + /* Enable 2-plane reference read mode */
>> + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(31));
>> + return;
>> + }
>> +
>> + if (codec_hevc_use_mmu(core->platform->revision,
>> + sess->pixfmt_cap, is_10bit))
>> + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, BIT(4));
>> + else
>> + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL1, 0);
>> +
>> + if (core->platform->revision < VDEC_REVISION_SM1)
>> + amvdec_write_dos(core, HEVCD_MPP_DECOMP_CTL2, body_size / 32);
>> + amvdec_write_dos(core, HEVC_CM_BODY_LENGTH, body_size);
>> + amvdec_write_dos(core, HEVC_CM_HEADER_OFFSET, body_size);
>> + amvdec_write_dos(core, HEVC_CM_HEADER_LENGTH, head_size);
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_setup_decode_head);
>> +
>> +static void codec_hevc_setup_buffers_gxbb(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm,
>> + int is_10bit)
>> +{
>> + struct amvdec_core *core = sess->core;
>> + struct v4l2_m2m_buffer *buf;
>> + u32 buf_num = v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx);
>> + dma_addr_t buf_y_paddr = 0;
>> + dma_addr_t buf_uv_paddr = 0;
>> + u32 idx = 0;
>> + u32 val;
>> + int i;
>> +
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 0);
>> +
>> + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
>> + struct vb2_buffer *vb = &buf->vb.vb2_buf;
>> +
>> + idx = vb->index;
>> +
>> + if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit))
>> + buf_y_paddr = comm->fbc_buffer_paddr[idx];
>> + else
>> + buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
>> +
>> + if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit)) {
>> + val = buf_y_paddr | (idx << 8) | 1;
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
>> + val);
>> + } else {
>> + buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1);
>> + val = buf_y_paddr | ((idx * 2) << 8) | 1;
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
>> + val);
>> + val = buf_uv_paddr | ((idx * 2 + 1) << 8) | 1;
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR,
>> + val);
>> + }
>> + }
>> +
>> + if (codec_hevc_use_fbc(sess->pixfmt_cap, is_10bit))
>> + val = buf_y_paddr | (idx << 8) | 1;
>> + else
>> + val = buf_y_paddr | ((idx * 2) << 8) | 1;
>> +
>> + /* Fill the remaining unused slots with the last buffer's Y addr */
>> + for (i = buf_num; i < MAX_REF_PIC_NUM; ++i)
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR, val);
>> +
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
>> + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1);
>> + for (i = 0; i < 32; ++i)
>> + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
>> +}
>> +
>> +static void codec_hevc_setup_buffers_gxl(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm,
>> + int is_10bit)
>> +{
>> + struct amvdec_core *core = sess->core;
>> + struct v4l2_m2m_buffer *buf;
>> + u32 revision = core->platform->revision;
>> + u32 pixfmt_cap = sess->pixfmt_cap;
>> + int i;
>> +
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR,
>> + BIT(2) | BIT(1));
>> +
>> + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
>> + struct vb2_buffer *vb = &buf->vb.vb2_buf;
>> + dma_addr_t buf_y_paddr = 0;
>> + dma_addr_t buf_uv_paddr = 0;
>> + u32 idx = vb->index;
>> +
>> + if (codec_hevc_use_mmu(revision, pixfmt_cap, is_10bit))
>> + buf_y_paddr = comm->mmu_header_paddr[idx];
>> + else if (codec_hevc_use_downsample(pixfmt_cap, is_10bit))
>> + buf_y_paddr = comm->fbc_buffer_paddr[idx];
>> + else
>> + buf_y_paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
>> +
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA,
>> + buf_y_paddr >> 5);
>> +
>> + if (!codec_hevc_use_fbc(pixfmt_cap, is_10bit)) {
>> + buf_uv_paddr = vb2_dma_contig_plane_dma_addr(vb, 1);
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_DATA,
>> + buf_uv_paddr >> 5);
>> + }
>> + }
>> +
>> + amvdec_write_dos(core, HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR, 1);
>> + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR, 1);
>> + for (i = 0; i < 32; ++i)
>> + amvdec_write_dos(core, HEVCD_MPP_ANC_CANVAS_DATA_ADDR, 0);
>> +}
>> +
>> +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm)
>> +{
>> + struct device *dev = sess->core->dev;
>> + u32 am21_size = amvdec_am21c_size(sess->width, sess->height);
>> + int i;
>> +
>> + for (i = 0; i < MAX_REF_PIC_NUM; ++i) {
>> + if (comm->fbc_buffer_vaddr[i]) {
>> + dma_free_coherent(dev, am21_size,
>> + comm->fbc_buffer_vaddr[i],
>> + comm->fbc_buffer_paddr[i]);
>> + comm->fbc_buffer_vaddr[i] = NULL;
>> + }
>> + }
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_free_fbc_buffers);
>> +
>> +static int codec_hevc_alloc_fbc_buffers(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm)
>> +{
>> + struct device *dev = sess->core->dev;
>> + struct v4l2_m2m_buffer *buf;
>> + u32 am21_size = amvdec_am21c_size(sess->width, sess->height);
>> +
>> + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
>> + u32 idx = buf->vb.vb2_buf.index;
>> + dma_addr_t paddr;
>> + void *vaddr = dma_alloc_coherent(dev, am21_size, &paddr,
>> + GFP_KERNEL);
>> + if (!vaddr) {
>> + dev_err(dev, "Couldn't allocate FBC buffer %u\n", idx);
>> + codec_hevc_free_fbc_buffers(sess, comm);
>> + return -ENOMEM;
>> + }
>> +
>> + comm->fbc_buffer_vaddr[idx] = vaddr;
>> + comm->fbc_buffer_paddr[idx] = paddr;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +void codec_hevc_free_mmu_headers(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm)
>> +{
>> + struct device *dev = sess->core->dev;
>> + int i;
>> +
>> + for (i = 0; i < MAX_REF_PIC_NUM; ++i) {
>> + if (comm->mmu_header_vaddr[i]) {
>> + dma_free_coherent(dev, MMU_COMPRESS_HEADER_SIZE,
>> + comm->mmu_header_vaddr[i],
>> + comm->mmu_header_paddr[i]);
>> + comm->mmu_header_vaddr[i] = NULL;
>> + }
>> + }
>> +
>> + if (comm->mmu_map_vaddr) {
>> + dma_free_coherent(dev, MMU_MAP_SIZE,
>> + comm->mmu_map_vaddr,
>> + comm->mmu_map_paddr);
>> + comm->mmu_map_vaddr = NULL;
>> + }
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_free_mmu_headers);
>> +
>> +static int codec_hevc_alloc_mmu_headers(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm)
>> +{
>> + struct device *dev = sess->core->dev;
>> + struct v4l2_m2m_buffer *buf;
>> +
>> + comm->mmu_map_vaddr = dma_alloc_coherent(dev, MMU_MAP_SIZE,
>> + &comm->mmu_map_paddr,
>> + GFP_KERNEL);
>> + if (!comm->mmu_map_vaddr)
>> + return -ENOMEM;
>> +
>> + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) {
>> + u32 idx = buf->vb.vb2_buf.index;
>> + dma_addr_t paddr;
>> + void *vaddr = dma_alloc_coherent(dev, MMU_COMPRESS_HEADER_SIZE,
>> + &paddr, GFP_KERNEL);
>> + if (!vaddr) {
>> + dev_err(dev, "Couldn't allocate MMU header %u\n", idx);
>> + codec_hevc_free_mmu_headers(sess, comm);
>> + return -ENOMEM;
>> + }
>> +
>> + comm->mmu_header_vaddr[idx] = vaddr;
>> + comm->mmu_header_paddr[idx] = paddr;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +int codec_hevc_setup_buffers(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm,
>> + int is_10bit)
>> +{
>> + struct amvdec_core *core = sess->core;
>> + int ret;
>> +
>> + if (codec_hevc_use_downsample(sess->pixfmt_cap, is_10bit)) {
>> + ret = codec_hevc_alloc_fbc_buffers(sess, comm);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + if (codec_hevc_use_mmu(core->platform->revision,
>> + sess->pixfmt_cap, is_10bit)) {
>> + ret = codec_hevc_alloc_mmu_headers(sess, comm);
>> + if (ret) {
>> + codec_hevc_free_fbc_buffers(sess, comm);
>> + return ret;
>> + }
>> + }
>> +
>> + if (core->platform->revision == VDEC_REVISION_GXBB)
>> + codec_hevc_setup_buffers_gxbb(sess, comm, is_10bit);
>> + else
>> + codec_hevc_setup_buffers_gxl(sess, comm, is_10bit);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_setup_buffers);
>> +
>> +void codec_hevc_fill_mmu_map(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm,
>> + struct vb2_buffer *vb)
>> +{
>> + u32 size = amvdec_am21c_size(sess->width, sess->height);
>> + u32 nb_pages = size / PAGE_SIZE;
>> + u32 *mmu_map = comm->mmu_map_vaddr;
>> + u32 first_page;
>> + u32 i;
>> +
>> + if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M)
>> + first_page = comm->fbc_buffer_paddr[vb->index] >> PAGE_SHIFT;
>> + else
>> + first_page = vb2_dma_contig_plane_dma_addr(vb, 0) >> PAGE_SHIFT;
>> +
>> + for (i = 0; i < nb_pages; ++i)
>> + mmu_map[i] = first_page + i;
>> +}
>> +EXPORT_SYMBOL_GPL(codec_hevc_fill_mmu_map);
>> diff --git a/drivers/staging/media/meson/vdec/codec_hevc_common.h b/drivers/staging/media/meson/vdec/codec_hevc_common.h
>> new file mode 100644
>> index 000000000000..de16d2e43061
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/codec_hevc_common.h
>> @@ -0,0 +1,77 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Copyright (C) 2018 BayLibre, SAS
>> + * Author: Maxime Jourdan <[email protected]>
>> + */
>> +
>> +#ifndef __MESON_VDEC_HEVC_COMMON_H_
>> +#define __MESON_VDEC_HEVC_COMMON_H_
>> +
>> +#include "vdec.h"
>> +
>> +#define PARSER_CMD_SKIP_CFG_0 0x0000090b
>> +#define PARSER_CMD_SKIP_CFG_1 0x1b14140f
>> +#define PARSER_CMD_SKIP_CFG_2 0x001b1910
>> +static const u16 vdec_hevc_parser_cmd[] = {
>> + 0x0401, 0x8401, 0x0800, 0x0402,
>> + 0x9002, 0x1423, 0x8CC3, 0x1423,
>> + 0x8804, 0x9825, 0x0800, 0x04FE,
>> + 0x8406, 0x8411, 0x1800, 0x8408,
>> + 0x8409, 0x8C2A, 0x9C2B, 0x1C00,
>> + 0x840F, 0x8407, 0x8000, 0x8408,
>> + 0x2000, 0xA800, 0x8410, 0x04DE,
>> + 0x840C, 0x840D, 0xAC00, 0xA000,
>> + 0x08C0, 0x08E0, 0xA40E, 0xFC00,
>> + 0x7C00
>> +};
>> +
>> +#define MAX_REF_PIC_NUM 24
>> +
>> +struct codec_hevc_common {
>> + void *fbc_buffer_vaddr[MAX_REF_PIC_NUM];
>> + dma_addr_t fbc_buffer_paddr[MAX_REF_PIC_NUM];
>> +
>> + void *mmu_header_vaddr[MAX_REF_PIC_NUM];
>> + dma_addr_t mmu_header_paddr[MAX_REF_PIC_NUM];
>> +
>> + void *mmu_map_vaddr;
>> + dma_addr_t mmu_map_paddr;
>> +};
>> +
>> +/* Returns 1 if we must use framebuffer compression */
>> +static inline int codec_hevc_use_fbc(u32 pixfmt, int is_10bit)
>> +{
>> + /* TOFIX: Handle Amlogic Compressed buffer for 8bit also */
>> + return is_10bit;
>> +}
>> +
>> +/* Returns 1 if we are decoding 10-bit but outputting 8-bit NV12 */
>> +static inline int codec_hevc_use_downsample(u32 pixfmt, int is_10bit)
>> +{
>> + return is_10bit;
>> +}
>> +
>> +/* Returns 1 if we are decoding using the IOMMU */
>> +static inline int codec_hevc_use_mmu(u32 revision, u32 pixfmt, int is_10bit)
>> +{
>> + return revision >= VDEC_REVISION_G12A &&
>> + codec_hevc_use_fbc(pixfmt, is_10bit);
>> +}
>> +
>> +/**
>> + * Configure decode head read mode
>> + */
>> +void codec_hevc_setup_decode_head(struct amvdec_session *sess, int is_10bit);
>> +
>> +void codec_hevc_free_fbc_buffers(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm);
>> +
>> +int codec_hevc_setup_buffers(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm,
>> + int is_10bit);
>> +
>> +void codec_hevc_fill_mmu_map(struct amvdec_session *sess,
>> + struct codec_hevc_common *comm,
>> + struct vb2_buffer *vb);
>> +
>> +#endif
>> diff --git a/drivers/staging/media/meson/vdec/hevc_regs.h b/drivers/staging/media/meson/vdec/hevc_regs.h
>> new file mode 100644
>> index 000000000000..55c1a80b955a
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/hevc_regs.h
>> @@ -0,0 +1,211 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
>> + */
>> +
>> +#ifndef __MESON_VDEC_HEVC_REGS_H_
>> +#define __MESON_VDEC_HEVC_REGS_H_
>> +
>> +#define HEVC_ASSIST_MMU_MAP_ADDR 0xc024
>> +
>> +#define HEVC_ASSIST_MBOX1_CLR_REG 0xc1d4
>> +#define HEVC_ASSIST_MBOX1_MASK 0xc1d8
>> +
>> +#define HEVC_ASSIST_SCRATCH_0 0xc300
>> +#define HEVC_ASSIST_SCRATCH_1 0xc304
>> +#define HEVC_ASSIST_SCRATCH_2 0xc308
>> +#define HEVC_ASSIST_SCRATCH_3 0xc30c
>> +#define HEVC_ASSIST_SCRATCH_4 0xc310
>> +#define HEVC_ASSIST_SCRATCH_5 0xc314
>> +#define HEVC_ASSIST_SCRATCH_6 0xc318
>> +#define HEVC_ASSIST_SCRATCH_7 0xc31c
>> +#define HEVC_ASSIST_SCRATCH_8 0xc320
>> +#define HEVC_ASSIST_SCRATCH_9 0xc324
>> +#define HEVC_ASSIST_SCRATCH_A 0xc328
>> +#define HEVC_ASSIST_SCRATCH_B 0xc32c
>> +#define HEVC_ASSIST_SCRATCH_C 0xc330
>> +#define HEVC_ASSIST_SCRATCH_D 0xc334
>> +#define HEVC_ASSIST_SCRATCH_E 0xc338
>> +#define HEVC_ASSIST_SCRATCH_F 0xc33c
>> +#define HEVC_ASSIST_SCRATCH_G 0xc340
>> +#define HEVC_ASSIST_SCRATCH_H 0xc344
>> +#define HEVC_ASSIST_SCRATCH_I 0xc348
>> +#define HEVC_ASSIST_SCRATCH_J 0xc34c
>> +#define HEVC_ASSIST_SCRATCH_K 0xc350
>> +#define HEVC_ASSIST_SCRATCH_L 0xc354
>> +#define HEVC_ASSIST_SCRATCH_M 0xc358
>> +#define HEVC_ASSIST_SCRATCH_N 0xc35c
>> +
>> +#define HEVC_PARSER_VERSION 0xc400
>> +#define HEVC_STREAM_CONTROL 0xc404
>> +#define HEVC_STREAM_START_ADDR 0xc408
>> +#define HEVC_STREAM_END_ADDR 0xc40c
>> +#define HEVC_STREAM_WR_PTR 0xc410
>> +#define HEVC_STREAM_RD_PTR 0xc414
>> +#define HEVC_STREAM_LEVEL 0xc418
>> +#define HEVC_STREAM_FIFO_CTL 0xc41c
>> +#define HEVC_SHIFT_CONTROL 0xc420
>> +#define HEVC_SHIFT_STARTCODE 0xc424
>> +#define HEVC_SHIFT_EMULATECODE 0xc428
>> +#define HEVC_SHIFT_STATUS 0xc42c
>> +#define HEVC_SHIFTED_DATA 0xc430
>> +#define HEVC_SHIFT_BYTE_COUNT 0xc434
>> +#define HEVC_SHIFT_COMMAND 0xc438
>> +#define HEVC_ELEMENT_RESULT 0xc43c
>> +#define HEVC_CABAC_CONTROL 0xc440
>> +#define HEVC_PARSER_SLICE_INFO 0xc444
>> +#define HEVC_PARSER_CMD_WRITE 0xc448
>> +#define HEVC_PARSER_CORE_CONTROL 0xc44c
>> +#define HEVC_PARSER_CMD_FETCH 0xc450
>> +#define HEVC_PARSER_CMD_STATUS 0xc454
>> +#define HEVC_PARSER_LCU_INFO 0xc458
>> +#define HEVC_PARSER_HEADER_INFO 0xc45c
>> +#define HEVC_PARSER_INT_CONTROL 0xc480
>> +#define HEVC_PARSER_INT_STATUS 0xc484
>> +#define HEVC_PARSER_IF_CONTROL 0xc488
>> +#define HEVC_PARSER_PICTURE_SIZE 0xc48c
>> +#define HEVC_PARSER_LCU_START 0xc490
>> +#define HEVC_PARSER_HEADER_INFO2 0xc494
>> +#define HEVC_PARSER_QUANT_READ 0xc498
>> +#define HEVC_PARSER_RESERVED_27 0xc49c
>> +#define HEVC_PARSER_CMD_SKIP_0 0xc4a0
>> +#define HEVC_PARSER_CMD_SKIP_1 0xc4a4
>> +#define HEVC_PARSER_CMD_SKIP_2 0xc4a8
>> +#define HEVC_SAO_IF_STATUS 0xc4c0
>> +#define HEVC_SAO_IF_DATA_Y 0xc4c4
>> +#define HEVC_SAO_IF_DATA_U 0xc4c8
>> +#define HEVC_SAO_IF_DATA_V 0xc4cc
>> +#define HEVC_STREAM_SWAP_ADDR 0xc4d0
>> +#define HEVC_STREAM_SWAP_CTRL 0xc4d4
>> +#define HEVC_IQIT_IF_WAIT_CNT 0xc4d8
>> +#define HEVC_MPRED_IF_WAIT_CNT 0xc4dc
>> +#define HEVC_SAO_IF_WAIT_CNT 0xc4e0
>> +
>> +#define HEVC_MPRED_VERSION 0xc800
>> +#define HEVC_MPRED_CTRL0 0xc804
>> + #define MPRED_CTRL0_NEW_PIC BIT(2)
>> + #define MPRED_CTRL0_NEW_TILE BIT(3)
>> + #define MPRED_CTRL0_NEW_SLI_SEG BIT(4)
>> + #define MPRED_CTRL0_TMVP BIT(5)
>> + #define MPRED_CTRL0_LDC BIT(6)
>> + #define MPRED_CTRL0_COL_FROM_L0 BIT(7)
>> + #define MPRED_CTRL0_ABOVE_EN BIT(9)
>> + #define MPRED_CTRL0_MV_WR_EN BIT(10)
>> + #define MPRED_CTRL0_MV_RD_EN BIT(11)
>> + #define MPRED_CTRL0_BUF_LINEAR BIT(13)
>> +#define HEVC_MPRED_CTRL1 0xc808
>> +#define HEVC_MPRED_INT_EN 0xc80c
>> +#define HEVC_MPRED_INT_STATUS 0xc810
>> +#define HEVC_MPRED_PIC_SIZE 0xc814
>> +#define HEVC_MPRED_PIC_SIZE_LCU 0xc818
>> +#define HEVC_MPRED_TILE_START 0xc81c
>> +#define HEVC_MPRED_TILE_SIZE_LCU 0xc820
>> +#define HEVC_MPRED_REF_NUM 0xc824
>> +#define HEVC_MPRED_REF_EN_L0 0xc830
>> +#define HEVC_MPRED_REF_EN_L1 0xc834
>> +#define HEVC_MPRED_COLREF_EN_L0 0xc838
>> +#define HEVC_MPRED_COLREF_EN_L1 0xc83c
>> +#define HEVC_MPRED_AXI_WCTRL 0xc840
>> +#define HEVC_MPRED_AXI_RCTRL 0xc844
>> +#define HEVC_MPRED_ABV_START_ADDR 0xc848
>> +#define HEVC_MPRED_MV_WR_START_ADDR 0xc84c
>> +#define HEVC_MPRED_MV_RD_START_ADDR 0xc850
>> +#define HEVC_MPRED_MV_WPTR 0xc854
>> +#define HEVC_MPRED_MV_RPTR 0xc858
>> +#define HEVC_MPRED_MV_WR_ROW_JUMP 0xc85c
>> +#define HEVC_MPRED_MV_RD_ROW_JUMP 0xc860
>> +#define HEVC_MPRED_CURR_LCU 0xc864
>> +#define HEVC_MPRED_ABV_WPTR 0xc868
>> +#define HEVC_MPRED_ABV_RPTR 0xc86c
>> +#define HEVC_MPRED_CTRL2 0xc870
>> +#define HEVC_MPRED_CTRL3 0xc874
>> +#define HEVC_MPRED_L0_REF00_POC 0xc880
>> +#define HEVC_MPRED_L1_REF00_POC 0xc8c0
>> +
>> +#define HEVC_MPRED_CUR_POC 0xc980
>> +#define HEVC_MPRED_COL_POC 0xc984
>> +#define HEVC_MPRED_MV_RD_END_ADDR 0xc988
>> +
>> +#define HEVC_MSP 0xcc00
>> +#define HEVC_MPSR 0xcc04
>> +#define HEVC_MCPU_INTR_MSK 0xcc10
>> +#define HEVC_MCPU_INTR_REQ 0xcc14
>> +#define HEVC_CPSR 0xcc84
>> +
>> +#define HEVC_IMEM_DMA_CTRL 0xcd00
>> +#define HEVC_IMEM_DMA_ADR 0xcd04
>> +#define HEVC_IMEM_DMA_COUNT 0xcd08
>> +
>> +#define HEVCD_IPP_TOP_CNTL 0xd000
>> +#define HEVCD_IPP_LINEBUFF_BASE 0xd024
>> +#define HEVCD_IPP_AXIIF_CONFIG 0xd02c
>> +
>> +#define HEVCD_MPP_ANC2AXI_TBL_CONF_ADDR 0xd180
>> +#define HEVCD_MPP_ANC2AXI_TBL_CMD_ADDR 0xd184
>> +#define HEVCD_MPP_ANC2AXI_TBL_DATA 0xd190
>> +
>> +#define HEVCD_MPP_ANC_CANVAS_ACCCONFIG_ADDR 0xd300
>> +#define HEVCD_MPP_ANC_CANVAS_DATA_ADDR 0xd304
>> +#define HEVCD_MPP_DECOMP_CTL1 0xd308
>> +#define HEVCD_MPP_DECOMP_CTL2 0xd30c
>> +#define HEVCD_MCRCC_CTL1 0xd3c0
>> +#define HEVCD_MCRCC_CTL2 0xd3c4
>> +#define HEVCD_MCRCC_CTL3 0xd3c8
>> +
>> +#define HEVC_DBLK_CFG0 0xd400
>> +#define HEVC_DBLK_CFG1 0xd404
>> +#define HEVC_DBLK_CFG2 0xd408
>> +#define HEVC_DBLK_CFG3 0xd40c
>> +#define HEVC_DBLK_CFG4 0xd410
>> +#define HEVC_DBLK_CFG5 0xd414
>> +#define HEVC_DBLK_CFG6 0xd418
>> +#define HEVC_DBLK_CFG7 0xd41c
>> +#define HEVC_DBLK_CFG8 0xd420
>> +#define HEVC_DBLK_CFG9 0xd424
>> +#define HEVC_DBLK_CFGA 0xd428
>> +#define HEVC_DBLK_STS0 0xd42c
>> +#define HEVC_DBLK_STS1 0xd430
>> +#define HEVC_DBLK_CFGE 0xd438
>> +
>> +#define HEVC_SAO_VERSION 0xd800
>> +#define HEVC_SAO_CTRL0 0xd804
>> +#define HEVC_SAO_CTRL1 0xd808
>> +#define HEVC_SAO_PIC_SIZE 0xd814
>> +#define HEVC_SAO_PIC_SIZE_LCU 0xd818
>> +#define HEVC_SAO_TILE_START 0xd81c
>> +#define HEVC_SAO_TILE_SIZE_LCU 0xd820
>> +#define HEVC_SAO_Y_START_ADDR 0xd82c
>> +#define HEVC_SAO_Y_LENGTH 0xd830
>> +#define HEVC_SAO_C_START_ADDR 0xd834
>> +#define HEVC_SAO_C_LENGTH 0xd838
>> +#define HEVC_SAO_Y_WPTR 0xd83c
>> +#define HEVC_SAO_C_WPTR 0xd840
>> +#define HEVC_SAO_ABV_START_ADDR 0xd844
>> +#define HEVC_SAO_VB_WR_START_ADDR 0xd848
>> +#define HEVC_SAO_VB_RD_START_ADDR 0xd84c
>> +#define HEVC_SAO_ABV_WPTR 0xd850
>> +#define HEVC_SAO_ABV_RPTR 0xd854
>> +#define HEVC_SAO_VB_WPTR 0xd858
>> +#define HEVC_SAO_VB_RPTR 0xd85c
>> +#define HEVC_SAO_CTRL2 0xd880
>> +#define HEVC_SAO_CTRL3 0xd884
>> +#define HEVC_SAO_CTRL4 0xd888
>> +#define HEVC_SAO_CTRL5 0xd88c
>> +#define HEVC_SAO_CTRL6 0xd890
>> +#define HEVC_SAO_CTRL7 0xd894
>> +#define HEVC_CM_BODY_START_ADDR 0xd898
>> +#define HEVC_CM_BODY_LENGTH 0xd89c
>> +#define HEVC_CM_HEADER_START_ADDR 0xd8a0
>> +#define HEVC_CM_HEADER_LENGTH 0xd8a4
>> +#define HEVC_CM_HEADER_OFFSET 0xd8ac
>> +#define HEVC_SAO_MMU_VH0_ADDR 0xd8e8
>> +#define HEVC_SAO_MMU_VH1_ADDR 0xd8ec
>> +
>> +#define HEVC_IQIT_CLK_RST_CTRL 0xdc00
>> +#define HEVC_IQIT_SCALELUT_WR_ADDR 0xdc08
>> +#define HEVC_IQIT_SCALELUT_RD_ADDR 0xdc0c
>> +#define HEVC_IQIT_SCALELUT_DATA 0xdc10
>> +
>> +#define HEVC_PSCALE_CTRL 0xe444
>> +
>> +#endif
>> diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.c b/drivers/staging/media/meson/vdec/vdec_hevc.c
>> new file mode 100644
>> index 000000000000..af41215e106c
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/vdec_hevc.c
>> @@ -0,0 +1,231 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright (C) 2018 Maxime Jourdan <[email protected]>
>> + *
>> + * VDEC_HEVC is a video decoding block that allows decoding of
>> + * HEVC, VP9
>> + */
>> +
>> +#include <linux/firmware.h>
>> +#include <linux/clk.h>
>> +
>> +#include "vdec_1.h"
>> +#include "vdec_helpers.h"
>> +#include "hevc_regs.h"
>> +#include "dos_regs.h"
>> +
>> +/* AO Registers */
>> +#define AO_RTI_GEN_PWR_SLEEP0 0xe8
>> +#define AO_RTI_GEN_PWR_ISO0 0xec
>> + #define GEN_PWR_VDEC_HEVC (BIT(7) | BIT(6))
>> + #define GEN_PWR_VDEC_HEVC_SM1 (BIT(2))
>> +
>> +#define MC_SIZE (4096 * 4)
>> +
>> +static int vdec_hevc_load_firmware(struct amvdec_session *sess,
>> + const char *fwname)
>> +{
>> + struct amvdec_core *core = sess->core;
>> + struct device *dev = core->dev_dec;
>> + const struct firmware *fw;
>> + static void *mc_addr;
>> + static dma_addr_t mc_addr_map;
>> + int ret;
>> + u32 i = 100;
>> +
>> + ret = request_firmware(&fw, fwname, dev);
>> + if (ret < 0) {
>> + dev_err(dev, "Unable to request firmware %s\n", fwname);
>> + return ret;
>> + }
>> +
>> + if (fw->size < MC_SIZE) {
>> + dev_err(dev, "Firmware size %zu is too small. Expected %u.\n",
>> + fw->size, MC_SIZE);
>> + ret = -EINVAL;
>> + goto release_firmware;
>> + }
>> +
>> + mc_addr = dma_alloc_coherent(core->dev, MC_SIZE, &mc_addr_map,
>> + GFP_KERNEL);
>> + if (!mc_addr) {
>> + dev_err(dev, "Failed allocating memory for firmware loading\n");
>> + ret = -ENOMEM;
>> + goto release_firmware;
>> + }
>> +
>> + memcpy(mc_addr, fw->data, MC_SIZE);
>> +
>> + amvdec_write_dos(core, HEVC_MPSR, 0);
>> + amvdec_write_dos(core, HEVC_CPSR, 0);
>> +
>> + amvdec_write_dos(core, HEVC_IMEM_DMA_ADR, mc_addr_map);
>> + amvdec_write_dos(core, HEVC_IMEM_DMA_COUNT, MC_SIZE / 4);
>> + amvdec_write_dos(core, HEVC_IMEM_DMA_CTRL, (0x8000 | (7 << 16)));
>> +
>> + while (i && (readl(core->dos_base + HEVC_IMEM_DMA_CTRL) & 0x8000))
>> + i--;
>> +
>> + if (i == 0) {
>> + dev_err(dev, "Firmware load fail (DMA hang?)\n");
>> + ret = -ENODEV;
>> + }
>> +
>> + dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map);
>> +release_firmware:
>> + release_firmware(fw);
>> + return ret;
>> +}
>> +
>> +static void vdec_hevc_stbuf_init(struct amvdec_session *sess)
>> +{
>> + struct amvdec_core *core = sess->core;
>> +
>> + amvdec_write_dos(core, HEVC_STREAM_CONTROL,
>> + amvdec_read_dos(core, HEVC_STREAM_CONTROL) & ~1);
>> + amvdec_write_dos(core, HEVC_STREAM_START_ADDR, sess->vififo_paddr);
>> + amvdec_write_dos(core, HEVC_STREAM_END_ADDR,
>> + sess->vififo_paddr + sess->vififo_size);
>> + amvdec_write_dos(core, HEVC_STREAM_RD_PTR, sess->vififo_paddr);
>> + amvdec_write_dos(core, HEVC_STREAM_WR_PTR, sess->vififo_paddr);
>> +}
>> +
>> +/* VDEC_HEVC specific ESPARSER configuration */
>> +static void vdec_hevc_conf_esparser(struct amvdec_session *sess)
>> +{
>> + struct amvdec_core *core = sess->core;
>> +
>> + /* set vififo_vbuf_rp_sel=>vdec_hevc */
>> + amvdec_write_dos(core, DOS_GEN_CTRL0, 3 << 1);
>> + amvdec_write_dos(core, HEVC_STREAM_CONTROL,
>> + amvdec_read_dos(core, HEVC_STREAM_CONTROL) | BIT(3));
>> + amvdec_write_dos(core, HEVC_STREAM_CONTROL,
>> + amvdec_read_dos(core, HEVC_STREAM_CONTROL) | 1);
>> + amvdec_write_dos(core, HEVC_STREAM_FIFO_CTL,
>> + amvdec_read_dos(core, HEVC_STREAM_FIFO_CTL) | BIT(29));
>> +}
>> +
>> +static u32 vdec_hevc_vififo_level(struct amvdec_session *sess)
>> +{
>> + return readl_relaxed(sess->core->dos_base + HEVC_STREAM_LEVEL);
>> +}
>> +
>> +static int vdec_hevc_stop(struct amvdec_session *sess)
>> +{
>> + struct amvdec_core *core = sess->core;
>> + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
>> +
>> + /* Disable interrupt */
>> + amvdec_write_dos(core, HEVC_ASSIST_MBOX1_MASK, 0);
>> + /* Disable firmware processor */
>> + amvdec_write_dos(core, HEVC_MPSR, 0);
>> +
>> + if (sess->priv)
>> + codec_ops->stop(sess);
>> +
>> + /* Enable VDEC_HEVC Isolation */
>> + if (core->platform->revision == VDEC_REVISION_SM1)
>> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
>> + GEN_PWR_VDEC_HEVC_SM1,
>> + GEN_PWR_VDEC_HEVC_SM1);
>> + else
>> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
>> + 0xc00, 0xc00);
>> +
>> + /* VDEC_HEVC Memories */
>> + amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0xffffffffUL);
>> +
>> + if (core->platform->revision == VDEC_REVISION_SM1)
>> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
>> + GEN_PWR_VDEC_HEVC_SM1,
>> + GEN_PWR_VDEC_HEVC_SM1);
>> + else
>> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
>> + GEN_PWR_VDEC_HEVC, GEN_PWR_VDEC_HEVC);
>> +
>> + clk_disable_unprepare(core->vdec_hevc_clk);
>> + if (core->platform->revision == VDEC_REVISION_G12A ||
>> + core->platform->revision == VDEC_REVISION_SM1)
>> + clk_disable_unprepare(core->vdec_hevcf_clk);
>> +
>> + return 0;
>> +}
>> +
>> +static int vdec_hevc_start(struct amvdec_session *sess)
>> +{
>> + int ret;
>> + struct amvdec_core *core = sess->core;
>> + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops;
>> +
>> + if (core->platform->revision == VDEC_REVISION_G12A ||
>> + core->platform->revision == VDEC_REVISION_SM1) {
>> + clk_set_rate(core->vdec_hevcf_clk, 666666666);
>> + ret = clk_prepare_enable(core->vdec_hevcf_clk);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + clk_set_rate(core->vdec_hevc_clk, 666666666);
>> + ret = clk_prepare_enable(core->vdec_hevc_clk);
>> + if (ret)
>> + return ret;
>> +
>> + if (core->platform->revision == VDEC_REVISION_SM1)
>> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
>> + GEN_PWR_VDEC_HEVC_SM1, 0);
>> + else
>> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0,
>> + GEN_PWR_VDEC_HEVC, 0);
>> + udelay(10);
>> +
>> + /* Reset VDEC_HEVC*/
>> + amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff);
>> + amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000);
>> +
>> + amvdec_write_dos(core, DOS_GCLK_EN3, 0xffffffff);
>> +
>> + /* VDEC_HEVC Memories */
>> + amvdec_write_dos(core, DOS_MEM_PD_HEVC, 0x00000000);
>> +
>> + /* Remove VDEC_HEVC Isolation */
>> + if (core->platform->revision == VDEC_REVISION_SM1)
>> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
>> + GEN_PWR_VDEC_HEVC_SM1, 0);
>> + else
>> + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_ISO0,
>> + 0xc00, 0);
>> +
>> + amvdec_write_dos(core, DOS_SW_RESET3, 0xffffffff);
>> + amvdec_write_dos(core, DOS_SW_RESET3, 0x00000000);
>> +
>> + vdec_hevc_stbuf_init(sess);
>> +
>> + ret = vdec_hevc_load_firmware(sess, sess->fmt_out->firmware_path);
>> + if (ret)
>> + goto stop;
>> +
>> + ret = codec_ops->start(sess);
>> + if (ret)
>> + goto stop;
>> +
>> + amvdec_write_dos(core, DOS_SW_RESET3, BIT(12) | BIT(11));
>> + amvdec_write_dos(core, DOS_SW_RESET3, 0);
>> + amvdec_read_dos(core, DOS_SW_RESET3);
>> +
>> + amvdec_write_dos(core, HEVC_MPSR, 1);
>> + /* Let the firmware settle */
>> + udelay(10);
>> +
>> + return 0;
>> +
>> +stop:
>> + vdec_hevc_stop(sess);
>> + return ret;
>> +}
>> +
>> +struct amvdec_ops vdec_hevc_ops = {
>> + .start = vdec_hevc_start,
>> + .stop = vdec_hevc_stop,
>> + .conf_esparser = vdec_hevc_conf_esparser,
>> + .vififo_level = vdec_hevc_vififo_level,
>> +};
>> diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.h b/drivers/staging/media/meson/vdec/vdec_hevc.h
>> new file mode 100644
>> index 000000000000..cd576a73a966
>> --- /dev/null
>> +++ b/drivers/staging/media/meson/vdec/vdec_hevc.h
>> @@ -0,0 +1,13 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Copyright (C) 2018 Maxime Jourdan <[email protected]>
>> + */
>> +
>> +#ifndef __MESON_VDEC_VDEC_HEVC_H_
>> +#define __MESON_VDEC_VDEC_HEVC_H_
>> +
>> +#include "vdec.h"
>> +
>> +extern struct amvdec_ops vdec_hevc_ops;
>> +
>> +#endif
>>
>