diff mbox series

[2/4] media: cedrus: Add JPEG decoder

Message ID 20240731164422.206503-3-linkmauve@linkmauve.fr
State New
Headers show
Series media: cedrus: Add a JPEG decoder | expand

Commit Message

Emmanuel Gil Peyrot July 31, 2024, 4:44 p.m. UTC
Basically all Cedrus variants support JPEG decoding. Add code for it.

Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
 drivers/staging/media/sunxi/cedrus/Kconfig    |   1 +
 drivers/staging/media/sunxi/cedrus/Makefile   |   2 +-
 drivers/staging/media/sunxi/cedrus/cedrus.h   |   5 +
 .../staging/media/sunxi/cedrus/cedrus_hw.c    |   2 +
 .../staging/media/sunxi/cedrus/cedrus_jpeg.c  | 352 ++++++++++++++++++
 .../staging/media/sunxi/cedrus/cedrus_regs.h  |  16 +
 .../staging/media/sunxi/cedrus/cedrus_video.c |   9 +
 7 files changed, 386 insertions(+), 1 deletion(-)
 create mode 100644 drivers/staging/media/sunxi/cedrus/cedrus_jpeg.c

Comments

Sebastian Fricke Feb. 13, 2025, 5:12 p.m. UTC | #1
Hey Emmanuel,

first of all sorry for taking that long to reply to your patches ...

I notice that there has been no movement on this so I wonder how we should proceed with this patch, our CI shows at least that you have some style issues on the patches (https://gitlab.freedesktop.org/linux-media/users/sebastianfricke/-/jobs/71044070) and I would like to have a reviewer first before I can think about merging this.

For a start can you rebase this patch series and apply the required fixes to make our CI happy and then we have to look for a reviewer as this is a new driver and I don't want to solely rely on my opinion as I am not well versed in the Cedrus driver.

Regards,
Sebastian

 ---- On Wed, 31 Jul 2024 18:44:12 +0200  Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> wrote --- 
 > Basically all Cedrus variants support JPEG decoding. Add code for it.
 > 
 > Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
 > Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
 > ---
 >  drivers/staging/media/sunxi/cedrus/Kconfig    |   1 +
 >  drivers/staging/media/sunxi/cedrus/Makefile   |   2 +-
 >  drivers/staging/media/sunxi/cedrus/cedrus.h   |   5 +
 >  .../staging/media/sunxi/cedrus/cedrus_hw.c    |   2 +
 >  .../staging/media/sunxi/cedrus/cedrus_jpeg.c  | 352 ++++++++++++++++++
 >  .../staging/media/sunxi/cedrus/cedrus_regs.h  |  16 +
 >  .../staging/media/sunxi/cedrus/cedrus_video.c |   9 +
 >  7 files changed, 386 insertions(+), 1 deletion(-)
 >  create mode 100644 drivers/staging/media/sunxi/cedrus/cedrus_jpeg.c
 > 
 > diff --git a/drivers/staging/media/sunxi/cedrus/Kconfig b/drivers/staging/media/sunxi/cedrus/Kconfig
 > index cb07a343c9c2..5683519aead0 100644
 > --- a/drivers/staging/media/sunxi/cedrus/Kconfig
 > +++ b/drivers/staging/media/sunxi/cedrus/Kconfig
 > @@ -9,6 +9,7 @@ config VIDEO_SUNXI_CEDRUS
 >      select SUNXI_SRAM
 >      select VIDEOBUF2_DMA_CONTIG
 >      select V4L2_MEM2MEM_DEV
 > +    select V4L2_JPEG_HELPER
 >      help
 >        Support for the VPU found in Allwinner SoCs, also known as the Cedar
 >        video engine.
 > diff --git a/drivers/staging/media/sunxi/cedrus/Makefile b/drivers/staging/media/sunxi/cedrus/Makefile
 > index a647b3690bf8..fa3e949e0788 100644
 > --- a/drivers/staging/media/sunxi/cedrus/Makefile
 > +++ b/drivers/staging/media/sunxi/cedrus/Makefile
 > @@ -3,4 +3,4 @@ obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi-cedrus.o
 >  
 >  sunxi-cedrus-y = cedrus.o cedrus_video.o cedrus_hw.o cedrus_dec.o \
 >           cedrus_mpeg2.o cedrus_h264.o cedrus_h265.o \
 > -         cedrus_vp8.o
 > +         cedrus_vp8.o cedrus_jpeg.o
 > diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h
 > index 522c184e2afc..555f8d124d47 100644
 > --- a/drivers/staging/media/sunxi/cedrus/cedrus.h
 > +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h
 > @@ -34,6 +34,7 @@
 >  #define CEDRUS_CAPABILITY_MPEG2_DEC    BIT(3)
 >  #define CEDRUS_CAPABILITY_VP8_DEC    BIT(4)
 >  #define CEDRUS_CAPABILITY_H265_10_DEC    BIT(5)
 > +#define CEDRUS_CAPABILITY_JPEG_DEC    BIT(6)
 >  
 >  enum cedrus_irq_status {
 >      CEDRUS_IRQ_NONE,
 > @@ -152,6 +153,9 @@ struct cedrus_ctx {
 >              u8        *entropy_probs_buf;
 >              dma_addr_t    entropy_probs_buf_dma;
 >          } vp8;
 > +        struct {
 > +            unsigned int    subsampling;
 > +        } jpeg;
 >      } codec;
 >  };
 >  
 > @@ -201,6 +205,7 @@ extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2;
 >  extern struct cedrus_dec_ops cedrus_dec_ops_h264;
 >  extern struct cedrus_dec_ops cedrus_dec_ops_h265;
 >  extern struct cedrus_dec_ops cedrus_dec_ops_vp8;
 > +extern struct cedrus_dec_ops cedrus_dec_ops_jpeg;
 >  
 >  static inline void cedrus_write(struct cedrus_dev *dev, u32 reg, u32 val)
 >  {
 > diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
 > index 582b6cb796ea..e237f7d66f7e 100644
 > --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
 > +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
 > @@ -43,7 +43,9 @@ int cedrus_engine_enable(struct cedrus_ctx *ctx)
 >      reg |= VE_MODE_DDR_MODE_BW_128;
 >  
 >      switch (ctx->src_fmt.pixelformat) {
 > +    /* MPEG2 and JPEG both use the same decoding mode bit. */
 >      case V4L2_PIX_FMT_MPEG2_SLICE:
 > +    case V4L2_PIX_FMT_JPEG:
 >          reg |= VE_MODE_DEC_MPEG;
 >          break;
 >  
 > diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_jpeg.c b/drivers/staging/media/sunxi/cedrus/cedrus_jpeg.c
 > new file mode 100644
 > index 000000000000..1e8978ebf9dd
 > --- /dev/null
 > +++ b/drivers/staging/media/sunxi/cedrus/cedrus_jpeg.c
 > @@ -0,0 +1,352 @@
 > +// SPDX-License-Identifier: GPL-2.0
 > +/*
 > + * Cedrus VPU driver
 > + *
 > + * Copyright (C) 2022 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
 > + */
 > +
 > +#include <media/videobuf2-dma-contig.h>
 > +#include <media/v4l2-jpeg.h>
 > +
 > +#include "cedrus.h"
 > +#include "cedrus_hw.h"
 > +#include "cedrus_regs.h"
 > +
 > +static enum cedrus_irq_status cedrus_jpeg_irq_status(struct cedrus_ctx *ctx)
 > +{
 > +    struct cedrus_dev *dev = ctx->dev;
 > +    u32 reg;
 > +
 > +    reg = cedrus_read(dev, VE_DEC_MPEG_STATUS);
 > +    reg &= VE_DEC_MPEG_STATUS_CHECK_MASK;
 > +
 > +    if (!reg)
 > +        return CEDRUS_IRQ_NONE;
 > +
 > +    if (reg & VE_DEC_MPEG_STATUS_CHECK_ERROR)
 > +        return CEDRUS_IRQ_ERROR;
 > +
 > +    return CEDRUS_IRQ_OK;
 > +}
 > +
 > +static void cedrus_jpeg_irq_clear(struct cedrus_ctx *ctx)
 > +{
 > +    struct cedrus_dev *dev = ctx->dev;
 > +
 > +    cedrus_write(dev, VE_DEC_MPEG_STATUS, VE_DEC_MPEG_STATUS_CHECK_MASK);
 > +}
 > +
 > +static void cedrus_jpeg_irq_disable(struct cedrus_ctx *ctx)
 > +{
 > +    struct cedrus_dev *dev = ctx->dev;
 > +    u32 reg = cedrus_read(dev, VE_DEC_MPEG_CTRL);
 > +
 > +    reg &= ~VE_DEC_MPEG_CTRL_IRQ_MASK;
 > +
 > +    cedrus_write(dev, VE_DEC_MPEG_CTRL, reg);
 > +}
 > +
 > +static int cedrus_write_table_header(struct cedrus_dev *dev,
 > +                                     struct v4l2_jpeg_reference *table)
 > +{
 > +    u16 start_codes[16], code;
 > +    u8 offsets[16], *ptr;
 > +    unsigned int count;
 > +    u32 *ptr32;
 > +    int i;
 > +
 > +    ptr = table->start;
 > +    if (!ptr)
 > +        return -EINVAL;
 > +
 > +    count = 0;
 > +    code = 0;
 > +    for (i = 0; i < 16; i++) {
 > +        offsets[i] = count;
 > +        start_codes[i] = code;
 > +        count += ptr[i];
 > +        code += ptr[i];
 > +        code *= 2;
 > +    }
 > +
 > +    for (i = 15; i >= 0 && !ptr[i]; i--)
 > +        start_codes[i] = 0xffff;
 > +
 > +    ptr32 = (u32*)start_codes;
 > +    for (i = 0; i < 8; i++)
 > +        cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, ptr32[i]);
 > +
 > +    ptr32 = (u32*)offsets;
 > +    for (i = 0; i < 4; i++)
 > +        cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, ptr32[i]);
 > +
 > +    for (i = 0; i < 4; i++)
 > +        cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, 0);
 > +
 > +    return 0;
 > +}
 > +
 > +static int cedrus_jpeg_write_dh_tables(struct cedrus_dev *dev,
 > +                                       struct v4l2_jpeg_header *hdr)
 > +{
 > +    struct v4l2_jpeg_reference *tables[4], *table;
 > +    struct v4l2_jpeg_scan_component_spec *comp;
 > +    unsigned int i, j, ret;
 > +    size_t length;
 > +    u32 *ptr, val;
 > +
 > +    cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_OFFSET, 0);
 > +
 > +    j = 0;
 > +    for (i = 0; i < 2; i++) {
 > +        comp = &hdr->scan->component[i];
 > +
 > +        tables[j++] = &hdr->huffman_tables[comp->dc_entropy_coding_table_selector];
 > +        tables[j++] = &hdr->huffman_tables[comp->ac_entropy_coding_table_selector + 2];
 > +    }
 > +
 > +    for (i = 0; i < 4; i++) {
 > +        ret = cedrus_write_table_header(dev, tables[i]);
 > +        if (ret)
 > +            return ret;
 > +    }
 > +
 > +    for (i = 0; i < 192; i++)
 > +        cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, 0);
 > +
 > +    for (i = 0; i < 4; i++) {
 > +        table = tables[i];
 > +        ptr = (u32*)&table->start[16];
 > +        length = table->length - 16;
 > +
 > +        for (j = 0; j < length / 4; j++)
 > +            cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, ptr[j]);
 > +
 > +        if (length & 3) {
 > +            val = 0;
 > +            for (j = 0; j < (length & 3); j++)
 > +                val = (val << 8) | table->start[15 + length - j];
 > +            cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, val);
 > +        }
 > +
 > +        for (j = 0; j < 64 - DIV_ROUND_UP(length, 4); j++)
 > +            cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, 0);
 > +    }
 > +
 > +    return 0;
 > +}
 > +
 > +static int cedrus_write_quantization_matrix(struct cedrus_dev *dev, u32 flags,
 > +                                            struct v4l2_jpeg_reference *table)
 > +{
 > +    const u8 *matrix;
 > +    u32 reg, val;
 > +    int i;
 > +
 > +    matrix = table->start;
 > +    if (!matrix)
 > +        return -EINVAL;
 > +
 > +    for (i = 0; i < 64; i++) {
 > +        /* determine if values are 8 or 16 bits */
 > +        val = *matrix++;
 > +        if (table->length > 64)
 > +            val = (val << 8) | *matrix++;
 > +
 > +        reg = VE_DEC_MPEG_IQMINPUT_WEIGHT(i, val);
 > +        reg |= flags;
 > +
 > +        cedrus_write(dev, VE_DEC_MPEG_IQMINPUT, reg);
 > +    }
 > +
 > +    return 0;
 > +}
 > +
 > +static int cedrus_jpeg_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
 > +{
 > +    struct cedrus_dev *dev = ctx->dev;
 > +    dma_addr_t src_buf_addr, dst_luma_addr, dst_chroma_addr;
 > +    struct v4l2_jpeg_scan_header scan_header;
 > +    struct v4l2_jpeg_reference quantization_tables[4] = { };
 > +    struct v4l2_jpeg_reference huffman_tables[4] = { };
 > +    struct v4l2_jpeg_header header = {
 > +        .scan = &scan_header,
 > +        .quantization_tables = quantization_tables,
 > +        .huffman_tables = huffman_tables,
 > +    };
 > +    struct vb2_buffer *src_buf = &run->src->vb2_buf;
 > +    struct v4l2_jpeg_frame_component_spec *components;
 > +    u32 reg, subsampling;
 > +    unsigned long size;
 > +    int ret, index;
 > +    u8 hmax, vmax;
 > +    u16 width, height;
 > +
 > +    size = vb2_get_plane_payload(src_buf, 0);
 > +    ret = v4l2_jpeg_parse_header(vb2_plane_vaddr(src_buf, 0), size, &header);
 > +    if (ret < 0) {
 > +        v4l2_err(&dev->v4l2_dev,
 > +                 "failed to parse JPEG header: %d\n", ret);
 > +        return -EINVAL;
 > +    }
 > +
 > +    width = header.frame.width;
 > +    height = header.frame.height;
 > +    if (width > 2048 || height > 2048) {
 > +        v4l2_err(&dev->v4l2_dev,
 > +                 "unsupported JPEG of resolution %ux%u (max 2048x2048)\n",
 > +                 width, height);
 > +        return -EINVAL;
 > +    }
 > +
 > +    if (header.frame.precision != 8) {
 > +        v4l2_err(&dev->v4l2_dev,
 > +                 "unsupported JPEG with %u bits of precision (8 required)\n",
 > +                 header.frame.precision);
 > +        return -EINVAL;
 > +    }
 > +
 > +    if (header.frame.num_components != 3) {
 > +        v4l2_err(&dev->v4l2_dev,
 > +                 "unsupported JPEG with %u components (3 required)\n",
 > +                 header.frame.num_components);
 > +        return -EINVAL;
 > +    }
 > +
 > +    components = header.frame.component;
 > +    index = components[0].horizontal_sampling_factor << 20 |
 > +        components[0].vertical_sampling_factor << 16 |
 > +        components[1].horizontal_sampling_factor << 12 |
 > +        components[1].vertical_sampling_factor << 8 |
 > +        components[2].horizontal_sampling_factor << 4 |
 > +        components[2].vertical_sampling_factor;
 > +
 > +    switch (index) {
 > +    case 0x221111:
 > +        subsampling = VE_DEC_MPEG_TRIGGER_CHROMA_FMT_420;
 > +        break;
 > +    case 0x211111:
 > +        subsampling = VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422;
 > +        break;
 > +    case 0x111111:
 > +        subsampling = VE_DEC_MPEG_TRIGGER_CHROMA_FMT_444;
 > +        break;
 > +    case 0x121111:
 > +        subsampling = VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422T;
 > +        break;
 > +    default:
 > +        v4l2_err(&dev->v4l2_dev, "unsupported subsampling %d\n", index);
 > +        return -EINVAL;
 > +    }
 > +
 > +    ctx->codec.jpeg.subsampling = subsampling;
 > +
 > +    /* Activate MPEG engine and select JPEG subengine. */
 > +    cedrus_engine_enable(ctx);
 > +
 > +    reg = VE_DEC_MPEG_TRIGGER_JPEG | subsampling;
 > +    cedrus_write(dev, VE_DEC_MPEG_TRIGGER, reg);
 > +
 > +    /* Set restart interval. */
 > +    cedrus_write(dev, VE_DEC_MPEG_JPEG_RES_INT, header.restart_interval);
 > +
 > +    /* Set resolution in blocks. */
 > +    hmax = components[0].horizontal_sampling_factor;
 > +    vmax = components[0].vertical_sampling_factor;
 > +    for (index = 1; index < 3; index++) {
 > +        if (hmax < components[index].horizontal_sampling_factor)
 > +            hmax = components[index].horizontal_sampling_factor;
 > +        if (vmax < components[index].vertical_sampling_factor)
 > +            vmax = components[index].vertical_sampling_factor;
 > +    }
 > +
 > +    reg = VE_DEC_MPEG_JPEG_SIZE_WIDTH(DIV_ROUND_UP(width, 8 * hmax));
 > +    reg |= VE_DEC_MPEG_JPEG_SIZE_HEIGHT(DIV_ROUND_UP(height, 8 * vmax));
 > +    cedrus_write(dev, VE_DEC_MPEG_JPEG_SIZE, reg);
 > +
 > +    /* Set intra quantisation matrix. */
 > +    index = components[0].quantization_table_selector;
 > +    ret = cedrus_write_quantization_matrix(dev,
 > +                                           VE_DEC_MPEG_IQMINPUT_FLAG_INTRA,
 > +                                           &quantization_tables[index]);
 > +    if (ret)
 > +        return ret;
 > +
 > +    /* Set non-intra quantisation matrix. */
 > +    index = components[1].quantization_table_selector;
 > +    ret = cedrus_write_quantization_matrix(dev,
 > +                                           VE_DEC_MPEG_IQMINPUT_FLAG_NON_INTRA,
 > +                                           &quantization_tables[index]);
 > +    if (ret)
 > +        return ret;
 > +
 > +    /* Set Diffie-Huffman tables. */
 > +    ret = cedrus_jpeg_write_dh_tables(dev, &header);
 > +    if (ret)
 > +        return ret;
 > +
 > +    /* Destination luma and chroma buffers. */
 > +
 > +    dst_luma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 0);
 > +    dst_chroma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 1);
 > +
 > +    /* JPEG outputs to rotation/scale down output buffers */
 > +    cedrus_write(dev, VE_DEC_MPEG_ROT_LUMA, dst_luma_addr);
 > +    cedrus_write(dev, VE_DEC_MPEG_ROT_CHROMA, dst_chroma_addr);
 > +
 > +    /* Disable rotation and scaling. */
 > +    cedrus_write(dev, VE_DEC_MPEG_SD_ROT_DBLK_CTL, 0);
 > +
 > +    /* Source offset and length in bits. */
 > +
 > +    cedrus_write(dev, VE_DEC_MPEG_VLD_OFFSET, 8 * header.ecs_offset);
 > +
 > +    reg = size * 8;
 > +    cedrus_write(dev, VE_DEC_MPEG_VLD_LEN, reg);
 > +
 > +    /* Source beginning and end addresses. */
 > +
 > +    src_buf_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
 > +
 > +    reg = VE_DEC_MPEG_VLD_ADDR_BASE(src_buf_addr);
 > +    reg |= VE_DEC_MPEG_VLD_ADDR_VALID_PIC_DATA;
 > +    reg |= VE_DEC_MPEG_VLD_ADDR_LAST_PIC_DATA;
 > +    reg |= VE_DEC_MPEG_VLD_ADDR_FIRST_PIC_DATA;
 > +
 > +    cedrus_write(dev, VE_DEC_MPEG_VLD_ADDR, reg);
 > +
 > +    reg = src_buf_addr + size;
 > +    cedrus_write(dev, VE_DEC_MPEG_VLD_END_ADDR, reg);
 > +
 > +    /* Enable appropriate interrupts and components. */
 > +
 > +    reg = VE_DEC_MPEG_CTRL_IRQ_MASK;
 > +    if (subsampling == VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422 ||
 > +        subsampling == VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422T ||
 > +        subsampling == VE_DEC_MPEG_TRIGGER_CHROMA_FMT_444)
 > +        reg |= VE_DEC_MPEG_CTRL_JPEG_FORCE_420;
 > +
 > +    cedrus_write(dev, VE_DEC_MPEG_CTRL, reg);
 > +
 > +    return 0;
 > +}
 > +
 > +static void cedrus_jpeg_trigger(struct cedrus_ctx *ctx)
 > +{
 > +    struct cedrus_dev *dev = ctx->dev;
 > +    u32 reg;
 > +
 > +    /* Trigger JPEG engine. */
 > +    reg = VE_DEC_MPEG_TRIGGER_HW_JPEG_VLD | VE_DEC_MPEG_TRIGGER_JPEG;
 > +    reg |= ctx->codec.jpeg.subsampling;
 > +
 > +    cedrus_write(dev, VE_DEC_MPEG_TRIGGER, reg);
 > +}
 > +
 > +struct cedrus_dec_ops cedrus_dec_ops_jpeg = {
 > +    .irq_clear   = cedrus_jpeg_irq_clear,
 > +    .irq_disable = cedrus_jpeg_irq_disable,
 > +    .irq_status  = cedrus_jpeg_irq_status,
 > +    .setup       = cedrus_jpeg_setup,
 > +    .trigger     = cedrus_jpeg_trigger,
 > +};
 > diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
 > index 3acc05e0fb54..cb8b4bb4f44e 100644
 > --- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
 > +++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
 > @@ -144,6 +144,7 @@
 >  
 >  #define VE_DEC_MPEG_CTRL_MC_CACHE_EN        BIT(31)
 >  #define VE_DEC_MPEG_CTRL_SW_VLD            BIT(27)
 > +#define VE_DEC_MPEG_CTRL_JPEG_FORCE_420        BIT(18)
 >  #define VE_DEC_MPEG_CTRL_SW_IQ_IS        BIT(17)
 >  #define VE_DEC_MPEG_CTRL_QP_AC_DC_OUT_EN    BIT(14)
 >  #define VE_DEC_MPEG_CTRL_ROTATE_SCALE_OUT_EN    BIT(8)
 > @@ -165,6 +166,7 @@
 >  #define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422    (0x02 << 27)
 >  #define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_444    (0x03 << 27)
 >  #define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422T    (0x04 << 27)
 > +#define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_400    (0x05 << 27)
 >  
 >  #define VE_DEC_MPEG_TRIGGER_MPEG1        (0x01 << 24)
 >  #define VE_DEC_MPEG_TRIGGER_MPEG2        (0x02 << 24)
 > @@ -255,10 +257,24 @@
 >  #define VE_DEC_MPEG_IQMINPUT_WEIGHT(i, v) \
 >      (SHIFT_AND_MASK_BITS(i, 13, 8) | SHIFT_AND_MASK_BITS(v, 7, 0))
 >  
 > +#define VE_DEC_MPEG_JPEG_SIZE            (VE_ENGINE_DEC_MPEG + 0xb8)
 > +
 > +#define VE_DEC_MPEG_JPEG_SIZE_WIDTH(w) \
 > +    SHIFT_AND_MASK_BITS((w) - 1, 10, 0)
 > +#define VE_DEC_MPEG_JPEG_SIZE_HEIGHT(h) \
 > +    SHIFT_AND_MASK_BITS((h) - 1, 26, 16)
 > +
 > +#define VE_DEC_MPEG_JPEG_MCU            (VE_ENGINE_DEC_MPEG + 0xbc)
 > +#define VE_DEC_MPEG_JPEG_RES_INT        (VE_ENGINE_DEC_MPEG + 0xc0)
 >  #define VE_DEC_MPEG_ERROR            (VE_ENGINE_DEC_MPEG + 0xc4)
 >  #define VE_DEC_MPEG_CRTMBADDR            (VE_ENGINE_DEC_MPEG + 0xc8)
 >  #define VE_DEC_MPEG_ROT_LUMA            (VE_ENGINE_DEC_MPEG + 0xcc)
 >  #define VE_DEC_MPEG_ROT_CHROMA            (VE_ENGINE_DEC_MPEG + 0xd0)
 > +#define VE_DEC_MPEG_SD_ROT_DBLK_CTL        (VE_ENGINE_DEC_MPEG + 0xd4)
 > +#define VE_DEC_MPEG_JPEG_MCU_START        (VE_ENGINE_DEC_MPEG + 0xd8)
 > +#define VE_DEC_MPEG_JPEG_MCU_END        (VE_ENGINE_DEC_MPEG + 0xdc)
 > +#define VE_DEC_MPEG_SRAM_RW_OFFSET        (VE_ENGINE_DEC_MPEG + 0xe0)
 > +#define VE_DEC_MPEG_SRAM_RW_DATA        (VE_ENGINE_DEC_MPEG + 0xe4)
 >  
 >  #define VE_DEC_H265_DEC_NAL_HDR            (VE_ENGINE_DEC_H265 + 0x00)
 >  
 > diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
 > index b00feaf4072c..7205c2315bc5 100644
 > --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
 > +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
 > @@ -55,6 +55,11 @@ static struct cedrus_format cedrus_formats[] = {
 >          .directions    = CEDRUS_DECODE_SRC,
 >          .capabilities    = CEDRUS_CAPABILITY_VP8_DEC,
 >      },
 > +    {
 > +        .pixelformat    = V4L2_PIX_FMT_JPEG,
 > +        .directions    = CEDRUS_DECODE_SRC,
 > +        .capabilities    = CEDRUS_CAPABILITY_JPEG_DEC,
 > +    },
 >      {
 >          .pixelformat    = V4L2_PIX_FMT_NV12,
 >          .directions    = CEDRUS_DECODE_DST,
 > @@ -118,6 +123,7 @@ void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt)
 >      case V4L2_PIX_FMT_H264_SLICE:
 >      case V4L2_PIX_FMT_HEVC_SLICE:
 >      case V4L2_PIX_FMT_VP8_FRAME:
 > +    case V4L2_PIX_FMT_JPEG:
 >          /* Zero bytes per line for encoded source. */
 >          bytesperline = 0;
 >          /* Choose some minimum size since this can't be 0 */
 > @@ -350,6 +356,9 @@ static int cedrus_s_fmt_vid_out_p(struct cedrus_ctx *ctx,
 >      case V4L2_PIX_FMT_VP8_FRAME:
 >          ctx->current_codec = &cedrus_dec_ops_vp8;
 >          break;
 > +    case V4L2_PIX_FMT_JPEG:
 > +        ctx->current_codec = &cedrus_dec_ops_jpeg;
 > +        break;
 >      }
 >  
 >      /* Propagate format information to capture. */
 > -- 
 > 2.45.2
 > 
 > 
 >
diff mbox series

Patch

diff --git a/drivers/staging/media/sunxi/cedrus/Kconfig b/drivers/staging/media/sunxi/cedrus/Kconfig
index cb07a343c9c2..5683519aead0 100644
--- a/drivers/staging/media/sunxi/cedrus/Kconfig
+++ b/drivers/staging/media/sunxi/cedrus/Kconfig
@@ -9,6 +9,7 @@  config VIDEO_SUNXI_CEDRUS
 	select SUNXI_SRAM
 	select VIDEOBUF2_DMA_CONTIG
 	select V4L2_MEM2MEM_DEV
+	select V4L2_JPEG_HELPER
 	help
 	  Support for the VPU found in Allwinner SoCs, also known as the Cedar
 	  video engine.
diff --git a/drivers/staging/media/sunxi/cedrus/Makefile b/drivers/staging/media/sunxi/cedrus/Makefile
index a647b3690bf8..fa3e949e0788 100644
--- a/drivers/staging/media/sunxi/cedrus/Makefile
+++ b/drivers/staging/media/sunxi/cedrus/Makefile
@@ -3,4 +3,4 @@  obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi-cedrus.o
 
 sunxi-cedrus-y = cedrus.o cedrus_video.o cedrus_hw.o cedrus_dec.o \
 		 cedrus_mpeg2.o cedrus_h264.o cedrus_h265.o \
-		 cedrus_vp8.o
+		 cedrus_vp8.o cedrus_jpeg.o
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h
index 522c184e2afc..555f8d124d47 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.h
@@ -34,6 +34,7 @@ 
 #define CEDRUS_CAPABILITY_MPEG2_DEC	BIT(3)
 #define CEDRUS_CAPABILITY_VP8_DEC	BIT(4)
 #define CEDRUS_CAPABILITY_H265_10_DEC	BIT(5)
+#define CEDRUS_CAPABILITY_JPEG_DEC	BIT(6)
 
 enum cedrus_irq_status {
 	CEDRUS_IRQ_NONE,
@@ -152,6 +153,9 @@  struct cedrus_ctx {
 			u8		*entropy_probs_buf;
 			dma_addr_t	entropy_probs_buf_dma;
 		} vp8;
+		struct {
+			unsigned int	subsampling;
+		} jpeg;
 	} codec;
 };
 
@@ -201,6 +205,7 @@  extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2;
 extern struct cedrus_dec_ops cedrus_dec_ops_h264;
 extern struct cedrus_dec_ops cedrus_dec_ops_h265;
 extern struct cedrus_dec_ops cedrus_dec_ops_vp8;
+extern struct cedrus_dec_ops cedrus_dec_ops_jpeg;
 
 static inline void cedrus_write(struct cedrus_dev *dev, u32 reg, u32 val)
 {
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
index 582b6cb796ea..e237f7d66f7e 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
@@ -43,7 +43,9 @@  int cedrus_engine_enable(struct cedrus_ctx *ctx)
 	reg |= VE_MODE_DDR_MODE_BW_128;
 
 	switch (ctx->src_fmt.pixelformat) {
+	/* MPEG2 and JPEG both use the same decoding mode bit. */
 	case V4L2_PIX_FMT_MPEG2_SLICE:
+	case V4L2_PIX_FMT_JPEG:
 		reg |= VE_MODE_DEC_MPEG;
 		break;
 
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_jpeg.c b/drivers/staging/media/sunxi/cedrus/cedrus_jpeg.c
new file mode 100644
index 000000000000..1e8978ebf9dd
--- /dev/null
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_jpeg.c
@@ -0,0 +1,352 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cedrus VPU driver
+ *
+ * Copyright (C) 2022 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+ */
+
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-jpeg.h>
+
+#include "cedrus.h"
+#include "cedrus_hw.h"
+#include "cedrus_regs.h"
+
+static enum cedrus_irq_status cedrus_jpeg_irq_status(struct cedrus_ctx *ctx)
+{
+	struct cedrus_dev *dev = ctx->dev;
+	u32 reg;
+
+	reg = cedrus_read(dev, VE_DEC_MPEG_STATUS);
+	reg &= VE_DEC_MPEG_STATUS_CHECK_MASK;
+
+	if (!reg)
+		return CEDRUS_IRQ_NONE;
+
+	if (reg & VE_DEC_MPEG_STATUS_CHECK_ERROR)
+		return CEDRUS_IRQ_ERROR;
+
+	return CEDRUS_IRQ_OK;
+}
+
+static void cedrus_jpeg_irq_clear(struct cedrus_ctx *ctx)
+{
+	struct cedrus_dev *dev = ctx->dev;
+
+	cedrus_write(dev, VE_DEC_MPEG_STATUS, VE_DEC_MPEG_STATUS_CHECK_MASK);
+}
+
+static void cedrus_jpeg_irq_disable(struct cedrus_ctx *ctx)
+{
+	struct cedrus_dev *dev = ctx->dev;
+	u32 reg = cedrus_read(dev, VE_DEC_MPEG_CTRL);
+
+	reg &= ~VE_DEC_MPEG_CTRL_IRQ_MASK;
+
+	cedrus_write(dev, VE_DEC_MPEG_CTRL, reg);
+}
+
+static int cedrus_write_table_header(struct cedrus_dev *dev,
+                                     struct v4l2_jpeg_reference *table)
+{
+	u16 start_codes[16], code;
+	u8 offsets[16], *ptr;
+	unsigned int count;
+	u32 *ptr32;
+	int i;
+
+	ptr = table->start;
+	if (!ptr)
+		return -EINVAL;
+
+	count = 0;
+	code = 0;
+	for (i = 0; i < 16; i++) {
+		offsets[i] = count;
+		start_codes[i] = code;
+		count += ptr[i];
+		code += ptr[i];
+		code *= 2;
+	}
+
+	for (i = 15; i >= 0 && !ptr[i]; i--)
+		start_codes[i] = 0xffff;
+
+	ptr32 = (u32*)start_codes;
+	for (i = 0; i < 8; i++)
+		cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, ptr32[i]);
+
+	ptr32 = (u32*)offsets;
+	for (i = 0; i < 4; i++)
+		cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, ptr32[i]);
+
+	for (i = 0; i < 4; i++)
+		cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, 0);
+
+	return 0;
+}
+
+static int cedrus_jpeg_write_dh_tables(struct cedrus_dev *dev,
+                                       struct v4l2_jpeg_header *hdr)
+{
+	struct v4l2_jpeg_reference *tables[4], *table;
+	struct v4l2_jpeg_scan_component_spec *comp;
+	unsigned int i, j, ret;
+	size_t length;
+	u32 *ptr, val;
+
+	cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_OFFSET, 0);
+
+	j = 0;
+	for (i = 0; i < 2; i++) {
+		comp = &hdr->scan->component[i];
+
+		tables[j++] = &hdr->huffman_tables[comp->dc_entropy_coding_table_selector];
+		tables[j++] = &hdr->huffman_tables[comp->ac_entropy_coding_table_selector + 2];
+	}
+
+	for (i = 0; i < 4; i++) {
+		ret = cedrus_write_table_header(dev, tables[i]);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < 192; i++)
+		cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, 0);
+
+	for (i = 0; i < 4; i++) {
+		table = tables[i];
+		ptr = (u32*)&table->start[16];
+		length = table->length - 16;
+
+		for (j = 0; j < length / 4; j++)
+			cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, ptr[j]);
+
+		if (length & 3) {
+			val = 0;
+			for (j = 0; j < (length & 3); j++)
+				val = (val << 8) | table->start[15 + length - j];
+			cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, val);
+		}
+
+		for (j = 0; j < 64 - DIV_ROUND_UP(length, 4); j++)
+			cedrus_write(dev, VE_DEC_MPEG_SRAM_RW_DATA, 0);
+	}
+
+	return 0;
+}
+
+static int cedrus_write_quantization_matrix(struct cedrus_dev *dev, u32 flags,
+                                            struct v4l2_jpeg_reference *table)
+{
+	const u8 *matrix;
+	u32 reg, val;
+	int i;
+
+	matrix = table->start;
+	if (!matrix)
+		return -EINVAL;
+
+	for (i = 0; i < 64; i++) {
+		/* determine if values are 8 or 16 bits */
+		val = *matrix++;
+		if (table->length > 64)
+			val = (val << 8) | *matrix++;
+
+		reg = VE_DEC_MPEG_IQMINPUT_WEIGHT(i, val);
+		reg |= flags;
+
+		cedrus_write(dev, VE_DEC_MPEG_IQMINPUT, reg);
+	}
+
+	return 0;
+}
+
+static int cedrus_jpeg_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
+{
+	struct cedrus_dev *dev = ctx->dev;
+	dma_addr_t src_buf_addr, dst_luma_addr, dst_chroma_addr;
+	struct v4l2_jpeg_scan_header scan_header;
+	struct v4l2_jpeg_reference quantization_tables[4] = { };
+	struct v4l2_jpeg_reference huffman_tables[4] = { };
+	struct v4l2_jpeg_header header = {
+		.scan = &scan_header,
+		.quantization_tables = quantization_tables,
+		.huffman_tables = huffman_tables,
+	};
+	struct vb2_buffer *src_buf = &run->src->vb2_buf;
+	struct v4l2_jpeg_frame_component_spec *components;
+	u32 reg, subsampling;
+	unsigned long size;
+	int ret, index;
+	u8 hmax, vmax;
+	u16 width, height;
+
+	size = vb2_get_plane_payload(src_buf, 0);
+	ret = v4l2_jpeg_parse_header(vb2_plane_vaddr(src_buf, 0), size, &header);
+	if (ret < 0) {
+		v4l2_err(&dev->v4l2_dev,
+		         "failed to parse JPEG header: %d\n", ret);
+		return -EINVAL;
+	}
+
+	width = header.frame.width;
+	height = header.frame.height;
+	if (width > 2048 || height > 2048) {
+		v4l2_err(&dev->v4l2_dev,
+		         "unsupported JPEG of resolution %ux%u (max 2048x2048)\n",
+		         width, height);
+		return -EINVAL;
+	}
+
+	if (header.frame.precision != 8) {
+		v4l2_err(&dev->v4l2_dev,
+		         "unsupported JPEG with %u bits of precision (8 required)\n",
+		         header.frame.precision);
+		return -EINVAL;
+	}
+
+	if (header.frame.num_components != 3) {
+		v4l2_err(&dev->v4l2_dev,
+		         "unsupported JPEG with %u components (3 required)\n",
+		         header.frame.num_components);
+		return -EINVAL;
+	}
+
+	components = header.frame.component;
+	index = components[0].horizontal_sampling_factor << 20 |
+		components[0].vertical_sampling_factor << 16 |
+		components[1].horizontal_sampling_factor << 12 |
+		components[1].vertical_sampling_factor << 8 |
+		components[2].horizontal_sampling_factor << 4 |
+		components[2].vertical_sampling_factor;
+
+	switch (index) {
+	case 0x221111:
+		subsampling = VE_DEC_MPEG_TRIGGER_CHROMA_FMT_420;
+		break;
+	case 0x211111:
+		subsampling = VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422;
+		break;
+	case 0x111111:
+		subsampling = VE_DEC_MPEG_TRIGGER_CHROMA_FMT_444;
+		break;
+	case 0x121111:
+		subsampling = VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422T;
+		break;
+	default:
+		v4l2_err(&dev->v4l2_dev, "unsupported subsampling %d\n", index);
+		return -EINVAL;
+	}
+
+	ctx->codec.jpeg.subsampling = subsampling;
+
+	/* Activate MPEG engine and select JPEG subengine. */
+	cedrus_engine_enable(ctx);
+
+	reg = VE_DEC_MPEG_TRIGGER_JPEG | subsampling;
+	cedrus_write(dev, VE_DEC_MPEG_TRIGGER, reg);
+
+	/* Set restart interval. */
+	cedrus_write(dev, VE_DEC_MPEG_JPEG_RES_INT, header.restart_interval);
+
+	/* Set resolution in blocks. */
+	hmax = components[0].horizontal_sampling_factor;
+	vmax = components[0].vertical_sampling_factor;
+	for (index = 1; index < 3; index++) {
+		if (hmax < components[index].horizontal_sampling_factor)
+			hmax = components[index].horizontal_sampling_factor;
+		if (vmax < components[index].vertical_sampling_factor)
+			vmax = components[index].vertical_sampling_factor;
+	}
+
+	reg = VE_DEC_MPEG_JPEG_SIZE_WIDTH(DIV_ROUND_UP(width, 8 * hmax));
+	reg |= VE_DEC_MPEG_JPEG_SIZE_HEIGHT(DIV_ROUND_UP(height, 8 * vmax));
+	cedrus_write(dev, VE_DEC_MPEG_JPEG_SIZE, reg);
+
+	/* Set intra quantisation matrix. */
+	index = components[0].quantization_table_selector;
+	ret = cedrus_write_quantization_matrix(dev,
+	                                       VE_DEC_MPEG_IQMINPUT_FLAG_INTRA,
+	                                       &quantization_tables[index]);
+	if (ret)
+		return ret;
+
+	/* Set non-intra quantisation matrix. */
+	index = components[1].quantization_table_selector;
+	ret = cedrus_write_quantization_matrix(dev,
+	                                       VE_DEC_MPEG_IQMINPUT_FLAG_NON_INTRA,
+	                                       &quantization_tables[index]);
+	if (ret)
+		return ret;
+
+	/* Set Diffie-Huffman tables. */
+	ret = cedrus_jpeg_write_dh_tables(dev, &header);
+	if (ret)
+		return ret;
+
+	/* Destination luma and chroma buffers. */
+
+	dst_luma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 0);
+	dst_chroma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 1);
+
+	/* JPEG outputs to rotation/scale down output buffers */
+	cedrus_write(dev, VE_DEC_MPEG_ROT_LUMA, dst_luma_addr);
+	cedrus_write(dev, VE_DEC_MPEG_ROT_CHROMA, dst_chroma_addr);
+
+	/* Disable rotation and scaling. */
+	cedrus_write(dev, VE_DEC_MPEG_SD_ROT_DBLK_CTL, 0);
+
+	/* Source offset and length in bits. */
+
+	cedrus_write(dev, VE_DEC_MPEG_VLD_OFFSET, 8 * header.ecs_offset);
+
+	reg = size * 8;
+	cedrus_write(dev, VE_DEC_MPEG_VLD_LEN, reg);
+
+	/* Source beginning and end addresses. */
+
+	src_buf_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+
+	reg = VE_DEC_MPEG_VLD_ADDR_BASE(src_buf_addr);
+	reg |= VE_DEC_MPEG_VLD_ADDR_VALID_PIC_DATA;
+	reg |= VE_DEC_MPEG_VLD_ADDR_LAST_PIC_DATA;
+	reg |= VE_DEC_MPEG_VLD_ADDR_FIRST_PIC_DATA;
+
+	cedrus_write(dev, VE_DEC_MPEG_VLD_ADDR, reg);
+
+	reg = src_buf_addr + size;
+	cedrus_write(dev, VE_DEC_MPEG_VLD_END_ADDR, reg);
+
+	/* Enable appropriate interrupts and components. */
+
+	reg = VE_DEC_MPEG_CTRL_IRQ_MASK;
+	if (subsampling == VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422 ||
+	    subsampling == VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422T ||
+	    subsampling == VE_DEC_MPEG_TRIGGER_CHROMA_FMT_444)
+		reg |= VE_DEC_MPEG_CTRL_JPEG_FORCE_420;
+
+	cedrus_write(dev, VE_DEC_MPEG_CTRL, reg);
+
+	return 0;
+}
+
+static void cedrus_jpeg_trigger(struct cedrus_ctx *ctx)
+{
+	struct cedrus_dev *dev = ctx->dev;
+	u32 reg;
+
+	/* Trigger JPEG engine. */
+	reg = VE_DEC_MPEG_TRIGGER_HW_JPEG_VLD | VE_DEC_MPEG_TRIGGER_JPEG;
+	reg |= ctx->codec.jpeg.subsampling;
+
+	cedrus_write(dev, VE_DEC_MPEG_TRIGGER, reg);
+}
+
+struct cedrus_dec_ops cedrus_dec_ops_jpeg = {
+	.irq_clear   = cedrus_jpeg_irq_clear,
+	.irq_disable = cedrus_jpeg_irq_disable,
+	.irq_status  = cedrus_jpeg_irq_status,
+	.setup       = cedrus_jpeg_setup,
+	.trigger     = cedrus_jpeg_trigger,
+};
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
index 3acc05e0fb54..cb8b4bb4f44e 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h
@@ -144,6 +144,7 @@ 
 
 #define VE_DEC_MPEG_CTRL_MC_CACHE_EN		BIT(31)
 #define VE_DEC_MPEG_CTRL_SW_VLD			BIT(27)
+#define VE_DEC_MPEG_CTRL_JPEG_FORCE_420		BIT(18)
 #define VE_DEC_MPEG_CTRL_SW_IQ_IS		BIT(17)
 #define VE_DEC_MPEG_CTRL_QP_AC_DC_OUT_EN	BIT(14)
 #define VE_DEC_MPEG_CTRL_ROTATE_SCALE_OUT_EN	BIT(8)
@@ -165,6 +166,7 @@ 
 #define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422	(0x02 << 27)
 #define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_444	(0x03 << 27)
 #define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_422T	(0x04 << 27)
+#define VE_DEC_MPEG_TRIGGER_CHROMA_FMT_400	(0x05 << 27)
 
 #define VE_DEC_MPEG_TRIGGER_MPEG1		(0x01 << 24)
 #define VE_DEC_MPEG_TRIGGER_MPEG2		(0x02 << 24)
@@ -255,10 +257,24 @@ 
 #define VE_DEC_MPEG_IQMINPUT_WEIGHT(i, v) \
 	(SHIFT_AND_MASK_BITS(i, 13, 8) | SHIFT_AND_MASK_BITS(v, 7, 0))
 
+#define VE_DEC_MPEG_JPEG_SIZE			(VE_ENGINE_DEC_MPEG + 0xb8)
+
+#define VE_DEC_MPEG_JPEG_SIZE_WIDTH(w) \
+	SHIFT_AND_MASK_BITS((w) - 1, 10, 0)
+#define VE_DEC_MPEG_JPEG_SIZE_HEIGHT(h) \
+	SHIFT_AND_MASK_BITS((h) - 1, 26, 16)
+
+#define VE_DEC_MPEG_JPEG_MCU			(VE_ENGINE_DEC_MPEG + 0xbc)
+#define VE_DEC_MPEG_JPEG_RES_INT		(VE_ENGINE_DEC_MPEG + 0xc0)
 #define VE_DEC_MPEG_ERROR			(VE_ENGINE_DEC_MPEG + 0xc4)
 #define VE_DEC_MPEG_CRTMBADDR			(VE_ENGINE_DEC_MPEG + 0xc8)
 #define VE_DEC_MPEG_ROT_LUMA			(VE_ENGINE_DEC_MPEG + 0xcc)
 #define VE_DEC_MPEG_ROT_CHROMA			(VE_ENGINE_DEC_MPEG + 0xd0)
+#define VE_DEC_MPEG_SD_ROT_DBLK_CTL		(VE_ENGINE_DEC_MPEG + 0xd4)
+#define VE_DEC_MPEG_JPEG_MCU_START		(VE_ENGINE_DEC_MPEG + 0xd8)
+#define VE_DEC_MPEG_JPEG_MCU_END		(VE_ENGINE_DEC_MPEG + 0xdc)
+#define VE_DEC_MPEG_SRAM_RW_OFFSET		(VE_ENGINE_DEC_MPEG + 0xe0)
+#define VE_DEC_MPEG_SRAM_RW_DATA		(VE_ENGINE_DEC_MPEG + 0xe4)
 
 #define VE_DEC_H265_DEC_NAL_HDR			(VE_ENGINE_DEC_H265 + 0x00)
 
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
index b00feaf4072c..7205c2315bc5 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
@@ -55,6 +55,11 @@  static struct cedrus_format cedrus_formats[] = {
 		.directions	= CEDRUS_DECODE_SRC,
 		.capabilities	= CEDRUS_CAPABILITY_VP8_DEC,
 	},
+	{
+		.pixelformat	= V4L2_PIX_FMT_JPEG,
+		.directions	= CEDRUS_DECODE_SRC,
+		.capabilities	= CEDRUS_CAPABILITY_JPEG_DEC,
+	},
 	{
 		.pixelformat	= V4L2_PIX_FMT_NV12,
 		.directions	= CEDRUS_DECODE_DST,
@@ -118,6 +123,7 @@  void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt)
 	case V4L2_PIX_FMT_H264_SLICE:
 	case V4L2_PIX_FMT_HEVC_SLICE:
 	case V4L2_PIX_FMT_VP8_FRAME:
+	case V4L2_PIX_FMT_JPEG:
 		/* Zero bytes per line for encoded source. */
 		bytesperline = 0;
 		/* Choose some minimum size since this can't be 0 */
@@ -350,6 +356,9 @@  static int cedrus_s_fmt_vid_out_p(struct cedrus_ctx *ctx,
 	case V4L2_PIX_FMT_VP8_FRAME:
 		ctx->current_codec = &cedrus_dec_ops_vp8;
 		break;
+	case V4L2_PIX_FMT_JPEG:
+		ctx->current_codec = &cedrus_dec_ops_jpeg;
+		break;
 	}
 
 	/* Propagate format information to capture. */