Re: [PATCH v4 3/5] media: meson: vdec: add common HEVC decoder support
From: Hans Verkuil
Date: Fri Feb 14 2020 - 10:07:16 EST
On 2/6/20 9:41 AM, Neil Armstrong wrote:
> From: Maxime Jourdan <mjourdan@xxxxxxxxxxxx>
>
> 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 <mjourdan@xxxxxxxxxxxx>
> Signed-off-by: Neil Armstrong <narmstrong@xxxxxxxxxxxx>
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 <mjourdan@xxxxxxxxxxxx>
> + */
> +
> +#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 <mjourdan@xxxxxxxxxxxx>
> + */
> +
> +#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 <maxi.jourdan@xxxxxxxxxx>
> + *
> + * 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 <maxi.jourdan@xxxxxxxxxx>
> + */
> +
> +#ifndef __MESON_VDEC_VDEC_HEVC_H_
> +#define __MESON_VDEC_VDEC_HEVC_H_
> +
> +#include "vdec.h"
> +
> +extern struct amvdec_ops vdec_hevc_ops;
> +
> +#endif
>