JPEG硬件解码

使用了DMA2D做调配

#ifndef JPEG_DECODE_H
#define JPEG_DECODE_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* 色度采样。解码方向不预设格式:Probe 从 SOF0 里读出实际值再决定,
 * 4:4:4 / 4:2:2 / 4:2:0 都接受。本机编码器(jpeg_encode.c)输出的是 4:2:0。 */
typedef enum {
    JPEG_DECODE_SUBSAMPLING_444 = 0,
    JPEG_DECODE_SUBSAMPLING_420 = 1,
    JPEG_DECODE_SUBSAMPLING_422 = 2
} JpegDecodeSubsampling_t;

typedef struct {
    uint16_t width;
    uint16_t height;
    JpegDecodeSubsampling_t subsampling;
} JpegDecodeInfo_t;

/* 解析 JPEG 头,校验 MH2457 JPEGD 的限制,并定位第一个 EOI。
 *
 *   jpeg        JPEG 数据
 *   bufferSize  可读字节数
 *   info        收到 SOF0 里的尺寸与色度采样
 *   jpegSize    收到"首个 EOI 结尾"的字节数(供 DMA 定长传输用)
 *
 * 返回 false 表示不是合法 JPEG,或含 JPEGD 不支持的标记(APP1 / SOF2 等)。 */
bool JpegDecode_Probe(const uint8_t *jpeg, size_t bufferSize,
                      JpegDecodeInfo_t *info, size_t *jpegSize);

/* JPEGD + DMA2 解码成 YCbCr,再用 DMA2D PFC 转 ARGB8888。
 *
 *   jpeg/jpegSize/jpegCapacity  输入缓冲、有效长度、可写容量
 *   yuv/yuvCapacity             中间 YCbCr 缓冲(JPEGD 输出,16 对齐行距)
 *   argb/argbPixels             输出 ARGB8888 缓冲,容量以像素计
 *   info                        收到实际解码尺寸(复用 Probe 的结果)
 *
 * 三个缓冲都必须 4 字节对齐;jpegCapacity 需比 jpegSize 多出最多 3 字节
 * (函数内部会补齐到 4 字节边界再做 DMA)。 */
bool JpegDecode_ToArgb8888(uint8_t *jpeg, size_t jpegSize, size_t jpegCapacity,
                           uint8_t *yuv, size_t yuvCapacity, uint32_t *argb,
                           size_t argbPixels, JpegDecodeInfo_t *info);

#ifdef __cplusplus
}
#endif

#endif /* JPEG_DECODE_H */

#include "jpeg_decode.h"

#include <string.h>

#ifndef JPEG_DECODE_HOST_TEST
#include "mh2457.h"
#include "mh245x_dma.h"
#include "mh245x_dma2d.h"
#include "mh245x_jpeg.h"
#include "peripheral.h"
#endif

static uint16_t be16(const uint8_t *p) { return (uint16_t)(((uint16_t)p[0] << 8) | p[1]); }

bool JpegDecode_Probe(const uint8_t *jpeg, size_t bufferSize,
                      JpegDecodeInfo_t *info, size_t *jpegSize)
{
    size_t pos = 2U;
    bool dqtSeen = false, sofSeen = false;
    JpegDecodeInfo_t found = {0U, 0U, JPEG_DECODE_SUBSAMPLING_444};
    if (jpegSize != NULL)
        *jpegSize = 0U;
    if (jpeg == NULL || info == NULL || jpegSize == NULL || bufferSize < 4U ||
        jpeg[0] != 0xffU || jpeg[1] != 0xd8U)
        return false;

    while (pos + 3U < bufferSize)
    {
        uint8_t marker;
        uint16_t length;
        while (pos < bufferSize && jpeg[pos] != 0xffU)
            ++pos;
        while (pos < bufferSize && jpeg[pos] == 0xffU)
            ++pos;
        if (pos >= bufferSize)
            return false;
        marker = jpeg[pos++];
        if (marker == 0xd9U)
        {
            *jpegSize = pos;
            *info = found;
            return sofSeen;
        }
        if (marker == 0x00U || (marker >= 0xd0U && marker <= 0xd7U))
            continue;
        if (pos + 2U > bufferSize)
            return false;
        length = be16(jpeg + pos);
        if (length < 2U || pos + length > bufferSize)
            return false;
        if (marker == 0xe1U || marker == 0xc2U)
            return false; /* JPEGD restrictions */
        if (marker == 0xdbU)
        {
            if (sofSeen)
                return false;
            dqtSeen = true;
        }
        else if (marker == 0xc0U)
        {
            const uint8_t *s = jpeg + pos + 2U;
            if (!dqtSeen || length < 17U || s[0] != 8U || s[5] != 3U)
                return false;
            found.height = be16(s + 1U);
            found.width = be16(s + 3U);
            if (s[7] == 0x22U)
                found.subsampling = JPEG_DECODE_SUBSAMPLING_420;
            else if (s[7] == 0x21U || s[7] == 0x12U)
                found.subsampling = JPEG_DECODE_SUBSAMPLING_422;
            else if (s[7] == 0x11U)
                found.subsampling = JPEG_DECODE_SUBSAMPLING_444;
            else
                return false;
            sofSeen = true;
        }
        else if (marker == 0xdaU)
        {
            size_t i = pos + length;
            if (!sofSeen)
                return false;
            while (i + 1U < bufferSize)
            {
                if (jpeg[i] != 0xffU)
                {
                    ++i;
                    continue;
                }
                if (jpeg[i + 1U] == 0x00U ||
                    (jpeg[i + 1U] >= 0xd0U && jpeg[i + 1U] <= 0xd7U))
                {
                    i += 2U;
                    continue;
                }
                if (jpeg[i + 1U] == 0xd9U)
                {
                    *jpegSize = i + 2U;
                    *info = found;
                    return true;
                }
                return false;
            }
            return false;
        }
        pos += length;
    }
    return false;
}

#define JPEG_DMA_WAIT_GUARD 80000000UL
#define JPEG_DMA2D_WAIT_GUARD 80000000UL

#ifndef JPEG_DECODE_HOST_TEST
bool JpegDecode_ToArgb8888(uint8_t *jpeg, size_t jpegSize,
                           size_t jpegCapacity, uint8_t *yuv,
                           size_t yuvCapacity, uint32_t *argb,
                           size_t argbPixels, JpegDecodeInfo_t *info)
{
    DMA_InitTypeDef d;
    JPEG_InfoTypeDef hw;
    size_t actualSize, paddedSize, worstYuv;
    uint32_t guard;
    if (jpeg == NULL || yuv == NULL || argb == NULL || info == NULL ||
        (((uintptr_t)jpeg | (uintptr_t)yuv | (uintptr_t)argb) & 3U) != 0U)
        return false;
    if (!JpegDecode_Probe(jpeg, jpegSize, info, &actualSize))
        return false;
    if (info->width == 0U || info->height == 0U ||
        (size_t)info->width * info->height > argbPixels)
        return false;
    paddedSize = (actualSize + 3U) & ~(size_t)3U;
    worstYuv = (size_t)((info->width + 15U) & ~15U) * info->height * 3U;
    if (paddedSize > jpegCapacity || worstYuv > yuvCapacity)
        return false;
    memset(jpeg + actualSize, 0, paddedSize - actualSize);

    PeripheralEnable(PeripheralJPEGD, 1);
    PeripheralEnable(PeripheralDMA2, 1);
    JPEG_Cmd(DISABLE);
    PeripheralReset(PeripheralJPEGD);
    DMA_Cmd(DMA2_Stream0, DISABLE);
    DMA_Cmd(DMA2_Stream5, DISABLE);
    guard = JPEG_DMA_WAIT_GUARD;
    while ((DMA_GetCmdStatus(DMA2_Stream0) || DMA_GetCmdStatus(DMA2_Stream5)) && --guard)
    {
    }
    if (guard == 0U)
        return false;
    DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_ALL);
    DMA_ClearFlag(DMA2_Stream5, DMA_FLAG_ALL);
    DMA_StructInit(&d);
    d.DMA_Channel = DMA_Channel_0;
    d.DMA_BufferSize = 1U;
    d.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    d.DMA_FIFOMode = DMA_FIFOMode_Enable;
    d.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    d.DMA_Memory0BaseAddr = (uint32_t)jpeg;
    d.DMA_MemoryBurst = DMA_MemoryBurst_INC4;
    d.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    d.DMA_MemoryInc = DMA_MemoryInc_Enable;
    d.DMA_Mode = DMA_Mode_Normal;
    d.DMA_PeripheralBaseAddr = (uint32_t)&JPEGD->DIR;
    d.DMA_PeripheralBurst = DMA_PeripheralBurst_INC16;
    d.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    d.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    d.DMA_Priority = DMA_Priority_Medium;
    DMA_Init(DMA2_Stream5, &d);
    d.DMA_Channel = DMA_Channel_7;
    d.DMA_DIR = DMA_DIR_PeripheralToMemory;
    d.DMA_Memory0BaseAddr = (uint32_t)yuv;
    d.DMA_PeripheralBaseAddr = (uint32_t)&JPEGD->DOR;
    DMA_Init(DMA2_Stream0, &d);
    DMA_FlowControllerConfig(DMA2_Stream0, DMA_FlowCtrl_Peripheral);
    DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, DISABLE);
    DMA_ITConfig(DMA2_Stream5, DMA_IT_TC, DISABLE);
    JPEG_SetInFifoThreshold(16U);
    JPEG_SetOutFifoThreshold(16U);
    DMA2_Stream5->M0AR = (uint32_t)jpeg;
    DMA2_Stream5->NDTR = (uint32_t)paddedSize;
    DMA2_Stream0->M0AR = (uint32_t)yuv;
    DMA2_Stream0->NDTR = 0U;
    JPEG_Cmd(ENABLE);
    DMA_Cmd(DMA2_Stream0, ENABLE);
    DMA_Cmd(DMA2_Stream5, ENABLE);
    guard = JPEG_DMA_WAIT_GUARD;
    while (DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF) == RESET && --guard)
    {
        if (JPEG_GetFlagStatus(JPEG_FLAG_CFGERR) != RESET)
            break;
    }
    if (guard == 0U || JPEG_GetFlagStatus(JPEG_FLAG_CFGERR) != RESET)
    {
        JPEG_Cmd(DISABLE);
        DMA_Cmd(DMA2_Stream0, DISABLE);
        DMA_Cmd(DMA2_Stream5, DISABLE);
        return false;
    }
    JPEG_GetInfo(&hw);
    JPEG_Cmd(DISABLE);
    DMA_Cmd(DMA2_Stream0, DISABLE);
    DMA_Cmd(DMA2_Stream5, DISABLE);
    if (hw.ImageWidth != info->width || hw.ImageHeight != info->height)
        return false;

    PeripheralEnable(PeripheralDMA2D, 1);
    DMA2D_DisableIT_TC(DMA2D);
    guard = JPEG_DMA2D_WAIT_GUARD;
    while (DMA2D_IsTransferOngoing(DMA2D) && --guard)
    {
    }
    if (guard == 0U)
        return false;
    DMA2D_SetMode(DMA2D, DMA2D_MODE_M2M_PFC);
    DMA2D_SetLineOffsetMode(DMA2D, DMA2D_LINE_OFFSET_PIXELS);
    DMA2D_SetOutputRotationMode(DMA2D, DMA2D_ROTATION_0);
    DMA2D_FGND_SetColorMode(DMA2D, DMA2D_INPUT_MODE_YCBCR);
    DMA2D_FGND_SetAlphaMode(DMA2D, DMA2D_ALPHA_MODE_REPLACE);
    DMA2D_FGND_SetAlpha(DMA2D, 0xffU);
    DMA2D_FGND_SetChrSubSampling(DMA2D,
                                 hw.ChromaSubsampling == JPEG_420_SUBSAMPLING ? DMA2D_CSS_420 : (hw.ChromaSubsampling == JPEG_422_SUBSAMPLING ? DMA2D_CSS_422 : DMA2D_CSS_444));
    DMA2D_FGND_SetLineOffset(DMA2D, (16U - (hw.ImageWidth & 15U)) & 15U);
    DMA2D_FGND_SetMemAddr(DMA2D, (uint32_t)yuv);
    DMA2D_SetOutputColorMode(DMA2D, DMA2D_OUTPUT_MODE_ARGB8888);
    DMA2D_SetOutputColor(DMA2D, 0xff000000UL);
    DMA2D_SetOutputMemAddr(DMA2D, (uint32_t)argb);
    DMA2D_SetLineOffset(DMA2D, 0U);
    DMA2D_SetNbrOfPixelsPerLines(DMA2D, hw.ImageWidth);
    DMA2D_SetNbrOfLines(DMA2D, hw.ImageHeight);
    DMA2D_ClearFlag_TC(DMA2D);
    DMA2D_ClearFlag_CE(DMA2D);
    DMA2D_ClearFlag_TE(DMA2D);
    DMA2D_Start(DMA2D);
    guard = JPEG_DMA2D_WAIT_GUARD;
    while (DMA2D_IsActiveFlag_TC(DMA2D) == 0U && --guard)
    {
        if (DMA2D_IsActiveFlag_CE(DMA2D) || DMA2D_IsActiveFlag_TE(DMA2D))
            break;
    }
    if (guard == 0U || DMA2D_IsActiveFlag_CE(DMA2D) || DMA2D_IsActiveFlag_TE(DMA2D))
        return false;
    DMA2D_ClearFlag_TC(DMA2D);
    return true;
}
#else
bool JpegDecode_ToArgb8888(uint8_t *jpeg, size_t jpegSize,
                           size_t jpegCapacity, uint8_t *yuv,
                           size_t yuvCapacity, uint32_t *argb,
                           size_t argbPixels, JpegDecodeInfo_t *info)
{
    (void)jpeg;
    (void)jpegSize;
    (void)jpegCapacity;
    (void)yuv;
    (void)yuvCapacity;
    (void)argb;
    (void)argbPixels;
    (void)info;
    return false;
}
#endif