Message ID | 20200206084152.7070-4-narmstrong@baylibre.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | media: meson: vdec: Add VP9 decoding support | expand |
On 2/6/20 9:41 AM, Neil Armstrong wrote: > From: Maxime Jourdan <mjourdan@baylibre.com> > > 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@baylibre.com> > Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> 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@baylibre.com> > + */ > + > +#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@baylibre.com> > + */ > + > +#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@wanadoo.fr> > + * > + * 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@wanadoo.fr> > + */ > + > +#ifndef __MESON_VDEC_VDEC_HEVC_H_ > +#define __MESON_VDEC_VDEC_HEVC_H_ > + > +#include "vdec.h" > + > +extern struct amvdec_ops vdec_hevc_ops; > + > +#endif >
On 14/02/2020 16:07, Hans Verkuil wrote: > On 2/6/20 9:41 AM, Neil Armstrong wrote: >> From: Maxime Jourdan <mjourdan@baylibre.com> >> >> 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@baylibre.com> >> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> > > 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 <mjourdan@baylibre.com> >> + */ >> + >> +#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@baylibre.com> >> + */ >> + >> +#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@wanadoo.fr> >> + * >> + * 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@wanadoo.fr> >> + */ >> + >> +#ifndef __MESON_VDEC_VDEC_HEVC_H_ >> +#define __MESON_VDEC_VDEC_HEVC_H_ >> + >> +#include "vdec.h" >> + >> +extern struct amvdec_ops vdec_hevc_ops; >> + >> +#endif >> >
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@baylibre.com> + */ + +#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@baylibre.com> + */ + +#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@wanadoo.fr> + * + * 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@wanadoo.fr> + */ + +#ifndef __MESON_VDEC_VDEC_HEVC_H_ +#define __MESON_VDEC_VDEC_HEVC_H_ + +#include "vdec.h" + +extern struct amvdec_ops vdec_hevc_ops; + +#endif