| // 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 |
| |
| 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 |
| }; |
| |
| /* 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) { |
| 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) { |
| 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); |