/**************************************************************************//**
 * @file     jpegcodec.c
 * @version  V3.00
 * @brief    N9H26 series JPEG driver source file
 *
 * SPDX-License-Identifier: Apache-2.0
 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wbio.h"
#include "wblib.h"
#include "wbtypes.h"
#include "N9H26_JPEG.h"
#include "jpeg.h"

BOOL volatile g_bWait = FALSE, g_jpegError = FALSE, g_bScale = FALSE,g_OutputWait = FALSE, g_InputWait = FALSE,g_u32WindowDec = FALSE,g_bEncThumbnailDownScale = FALSE,g_bEncPrimaryDownScale =FALSE;
UINT32 volatile	g_u32Stride, g_u32ScaleWidth, g_u32ScaleHeight, g_u32OpMode,g_u32EncRotate = 0;
UINT32 volatile	g_u32BufferCount,g_u32DecInputWaitAddr;
UINT16 volatile g_u16BufferSize, g_u16ReserveSize;
UINT32 volatile g_u32OutputFormat,g_u32windowSizeX,g_u32windowSizeY;

PFN_JPEG_CALLBACK pfnJpegDecodeComplete = NULL;
PFN_JPEG_CALLBACK pfnJpegDecodeError = NULL;
PFN_JPEG_CALLBACK pfnJpegEncodeComplete = NULL;
PFN_JPEG_HEADERDECODE_CALLBACK pfnJpegHeaderDecode = NULL;
PFN_JPEG_DECWAIT_CALLBACK pfnJpegDecInputWait = NULL;
PFN_JPEG_DECWAIT_CALLBACK pfnJpegDecOutputWait = NULL;

static JPEG_INFO_T jpegInfo;

/* Default Quantization-Table 0 ~ 2 */
UINT8 g_au8QTable0[64] = { 0x06, 0x04, 0x04, 0x05, 0x04, 0x04, 0x06, 0x05,
                      0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x08, 0x0E,
                      0x09, 0x08, 0x08, 0x08, 0x08, 0x11, 0x0C, 0x0D,
                      0x0A, 0x0E, 0x14, 0x11, 0x15, 0x14, 0x13, 0x11,
                      0x13, 0x13, 0x16, 0x18, 0x1F, 0x1A, 0x16, 0x17,
                      0x1D, 0x17, 0x13, 0x13, 0x1B, 0x25, 0x1B, 0x1D,
                      0x20, 0x21, 0x23, 0x23, 0x23, 0x15, 0x1A, 0x26,
                      0x29, 0x26, 0x22, 0x28, 0x1F, 0x22, 0x23, 0x21 },
    g_au8QTable1[64] = { 0x06, 0x06, 0x06, 0x08, 0x07, 0x08, 0x10, 0x09,
                      0x09, 0x10, 0x21, 0x16, 0x13, 0x16, 0x21, 0x21,
                      0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
                      0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
                      0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
                      0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
                      0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
                      0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21 },
    g_au8QTable2[64] = { 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
                      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
                      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
                      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
                      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
                      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
                      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
                      0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 };
                      
/* Interrupt Service Routine for H/W JPEG CODEC */
void jpegISR(void)
{
    UINT32 u32interruptStatus;

    /* Get the interrupt status */
    u32interruptStatus = JPEG_GET_INT_STATUS();

    /* It's Header Decode Complete Interrupt */
    if(u32interruptStatus & DHE_INTS)
    {
        UINT32 u32YuvFormat;
        UINT16 u16Height,u16Width,UVHeight,UVWidth;
        UINT16 u16WidthTmp = 0, u16HeightTmp = 0;

        /* Get the JPEG format */
        u32YuvFormat = JPEG_DEC_GET_DECODED_IMAGE_FORMAT();

        /* Get the decoded image dimension */
        jpegGetDecodedDimension(&u16Height,&u16Width);

        jpegInfo.jpeg_width = u16Width;
        jpegInfo.jpeg_height = u16Height;
        jpegInfo.yuvformat = u32YuvFormat;

        if(pfnJpegHeaderDecode!= NULL)
        {
            if(!pfnJpegHeaderDecode())
            {
                jpegInit();
                g_bWait = FALSE;
                return;
            }
        }

        if(g_bScale)   
        {
            UINT16 u16RatioH,u16RatioW;

            if(jpegCalScalingFactor(
                g_u32OpMode,         /* Up / Down Scaling */
                u16Height,           /* Original Height */
                u16Width,            /* Original Width */
                g_u32ScaleHeight,    /* Scaled Height */
                g_u32ScaleWidth,     /* Scaled Width */
                &u16RatioH,          /* Horizontal Ratio */
                &u16RatioW           /* Vertical Ratio */
                ) != E_JPEG_SUCCESS)
            {
                g_bWait = FALSE;
                g_jpegError = TRUE;   
            }
            else
            {
                jpegSetScalingFactor(g_u32OpMode,u16RatioH,u16RatioW);
                u16Width = 	g_u32ScaleWidth;
                u16Height = g_u32ScaleHeight;
            }
        }
        else
        {
            if(u32YuvFormat == JPEG_DEC_YUV411)
            {
                /* 32-pixel alignment for YUV411 raw data */
                if(u16Width % 32)
                    u16Width = (u16Width & 0xFFFFFFE0) + 32;
            }
            else if((u32YuvFormat == JPEG_DEC_YUV444) || (u32YuvFormat == JPEG_DEC_YUV422T))
            {
                /* 8-pixel alignment for YUV444 raw data */
                if(u16Width % 8)
                    u16Width = (u16Width & 0xFFFFFFF8) + 8; 
            }
            else
            {
                /* 16-pixel alignment for YUV422 or YUV420 raw data */
                if(u16Width % 16)
                u16Width = (u16Width & 0xFFFFFFF0) + 16;
            } 
        }

        if(g_u32Stride >= u16Width)
        {
            jpegInfo.stride = g_u32Stride;
            g_u32Stride = g_u32Stride - u16Width;
            JPEG_SET_YSTRIDE(g_u32Stride);
            u16Width = jpegInfo.stride; 
        }
        else
        {
            g_u32Stride = 0;
            JPEG_SET_YSTRIDE(0);
            jpegInfo.stride = 0;
        }

        if(g_u32OutputFormat == JPEG_DEC_PRIMARY_PLANAR_YUV || g_u32OutputFormat ==JPEG_DEC_THUMBNAIL_PLANAR_YUV)
        {
            if(g_u32WindowDec)
            {
                u16WidthTmp = u16Width;
                u16HeightTmp = u16Height;
                u16Width = g_u32windowSizeX;
                u16Height = g_u32windowSizeY;
            }

            if(u32YuvFormat == JPEG_DEC_YUV411)
            {
                /* For YUV411 raw data */
                UVWidth = u16Width/4;
            }
            else if((u32YuvFormat == JPEG_DEC_YUV444) || (u32YuvFormat == JPEG_DEC_YUV422T))
            {
                /* For YUV444 raw data */
                UVWidth = u16Width;
            }
            /* Set the U-component and V-componente width for YUV422 or YUV420 raw data */
            else if(u16Width % 2) 
                UVWidth = u16Width / 2 + 1;
            else
                UVWidth = u16Width / 2;

            /* Sets the height of U and V for YUV420 image */
            if(u32YuvFormat == JPEG_DEC_YUV420)
            {
                /* 16-pixel alignment for YUV422 or YUV420 raw data */
                if(u16Height % 16)
                    u16Height = (u16Height & 0xFFFFFFF0) + 16;
                UVHeight = u16Height / 2;
            }
            else if(u32YuvFormat == JPEG_DEC_YUV422)
            {
                /* 8-pixel alignment for YUV444 raw data */
                if(u16Height % 8)
                    u16Height = (u16Height & 0xFFFFFFF8) + 8; 
                UVHeight = u16Height;
            }
            else if(u32YuvFormat == JPEG_DEC_YUV444)
            {
                /* 8-pixel alignment for YUV444 raw data */
                if(u16Height % 8)
                    u16Height = (u16Height & 0xFFFFFFF8) + 8; 
                UVHeight = u16Height;
            }
            else if(u32YuvFormat == JPEG_DEC_YUV411)
            {
                /* 8-pixel alignment for YUV411 raw data */
                if(u16Height % 8)
                    u16Height = (u16Height & 0xFFFFFFF8) + 8; 
                UVHeight = u16Height;
            }
            else if(u32YuvFormat == JPEG_DEC_YUV422T)
            {
                /* 16-pixel alignment for YUV422 or YUV420 raw data */
                if(u16Height % 16)
                    u16Height = (u16Height & 0xFFFFFFF0) + 16;
                UVHeight = u16Height / 2;
            }
            else
            {
                /* 8-pixel alignment for raw data */
                if(u16Height % 8)
                    u16Height = (u16Height & 0xFFFFFFF8) + 8;
                UVHeight = u16Height;
            }
            JPEG_SET_UADDR(JPEG_GET_YADDR() + u16Width * u16Height);
            JPEG_SET_VADDR(JPEG_GET_UADDR() + UVWidth * UVHeight);

            if(u32YuvFormat == JPEG_DEC_GRAY)
                jpegInfo.image_size[0] = u16Width * u16Height;
            else
                jpegInfo.image_size[0] = u16Width * u16Height + 2* UVWidth * UVHeight;

            if(g_u32WindowDec)
            {
                u16Width = u16WidthTmp;
                u16Height = u16HeightTmp;
            }
        }
        else
        {
            if(g_u32WindowDec)
            {
                u16WidthTmp = u16Width;
                u16HeightTmp = u16Height;
                u16Width = g_u32windowSizeX;
                u16Height = g_u32windowSizeY;
            }

            if(jpegInfo.stride)
                jpegInfo.image_size[0] = jpegInfo.stride * u16Height;
            else
                jpegInfo.image_size[0] =  u16Width * u16Height;

            if (g_u32OutputFormat == JPEG_DEC_PRIMARY_PACKET_RGB888)
                jpegInfo.image_size[0] = 4 * jpegInfo.image_size[0]; 
            else
                jpegInfo.image_size[0] = 2 * jpegInfo.image_size[0];

            if(g_u32WindowDec)
            {
                u16Width = u16WidthTmp;
                u16Height = u16HeightTmp;
            }
        }

        /* Set the image dimension */
        jpegSetDimension(u16Height, u16Width);

        outp32(JITCR, inp32(JITCR) | Dec_Scatter_Gather);

        /* Clear interrupt status */
        JPEG_CLEAR_INT(DHE_INTS);
    }
    /* It's Decode Output Wait Interrupt */
    else if(u32interruptStatus & JPG_DOW_INTS)
    {
        if(pfnJpegDecOutputWait!= NULL)
        {
            pfnJpegDecOutputWait(inp32(JYADDR0) , inp32(JDOWFBS));
        }
        JPEG_CLEAR_INT(JPG_DOW_INTS); 

        outp32(JITCR, inp32(JITCR) | Dec_Scatter_Gather);
    }
    /* It's Encode Complete Interrupt */
    else if(u32interruptStatus & ENC_INTS)
    {
        /* Get the Encode Bit stream length */
        jpegInfo.image_size[0] = JPEG_GET_ENC_PRIMARY_BITSTREAM_SIZE();
        jpegInfo.image_size[1] = JPEG_GET_ENC_THUMBNAIL_BITSTREAM_SIZE();
        /* Clear interrupt status */
        JPEG_CLEAR_INT(ENC_INTS);
        g_bWait = FALSE;

        if(pfnJpegEncodeComplete!= NULL)
            pfnJpegEncodeComplete();
    }
    /* It's Decode Complete Interrupt */
    else if(u32interruptStatus &DEC_INTS) 
    {
        UINT16 imageWidth,imageHeight;
        /* Get the image dimension */
        jpegGetDimension(&imageHeight,&imageWidth);

        if(g_u32Stride!=0)
        {
            imageWidth = imageWidth - g_u32Stride;
        }

        jpegInfo.width = imageWidth;
        jpegInfo.height = imageHeight;

        /* Clear interrupt status */
        JPEG_CLEAR_INT(DEC_INTS);
        JPEG_CLEAR_INT(JPG_DOW_INTS); 

        g_bWait = FALSE;

        if(pfnJpegDecodeComplete!= NULL)
            pfnJpegDecodeComplete();
    }
    /* It's Decode Error Interrupt */
    else if(u32interruptStatus & DER_INTS)
    {
        /* Clear interrupt status */
        JPEG_CLEAR_INT(DER_INTS);
        g_bWait = FALSE;
        g_jpegError = TRUE;

        if(pfnJpegDecodeError!= NULL)
            pfnJpegDecodeError();
    }
    else if (u32interruptStatus & IPW_INTS)
    {
        /* Clear interrupt status */
        JPEG_CLEAR_INT(IPW_INTS);
        JPEG_DEC_RESUME_INPUT_WAIT();

        if(pfnJpegDecInputWait!= NULL)
        {
            if(!pfnJpegDecInputWait((g_u32DecInputWaitAddr + (g_u32BufferCount %2) * g_u16BufferSize),g_u16BufferSize))
            {
                jpegInit();
                g_bWait = FALSE;
                return;
            }
            g_u32BufferCount++;
        }
        else
            while(1);
    }     
}

INT jpegWait(VOID)
{
    while(1)
    {
        if(g_bWait == FALSE)
            break;
    }

    if(g_jpegError)
        return E_JPEG_FAIL;

    return E_JPEG_SUCCESS;
}


BOOL jpegIsReady(VOID)
{
    if (g_bWait == FALSE)
        return TRUE;
    else
        return FALSE;
}


VOID jpegGetInfo(JPEG_INFO_T *info)
{
    memcpy(info, &jpegInfo, sizeof(JPEG_INFO_T));
}

UINT32 jpegPower(UINT32 u32Index, UINT32 u32Exp)
{
    if(u32Exp==0)
        return 1;
    else
    {
        UINT32 u32Idx;
        for(u32Idx=1; u32Idx<u32Exp; u32Idx = u32Idx+1)
        {
            u32Index = 2 * u32Index;
        }
    }
    return u32Index;
}

INT jpegOpen(VOID)
{
    UINT32 u32JPGDiv;
    UINT32 u32JPGSource;
    UINT32  u32HclkHz;

    /* 1.Check I/O pins. If I/O pins are used by other IPs, return error code (check PINFUN) */
    /* 2.Enable IP¡¦s clock */
    outp32(REG_AHBCLK, (inp32(REG_AHBCLK) | JPG_CKE));

    u32HclkHz = sysGetHCLK1Clock();
    u32JPGSource = u32HclkHz / (((inp32(REG_CLKDIV4) & HCLK234_N) >> 4) + 1);

    u32JPGDiv = 0;

    if(u32JPGSource > 130000000)
    {
        if(u32JPGSource % 130000000)
        {
            u32JPGDiv = (u32JPGSource / 130000000);
        }
        else
            u32JPGDiv = (u32JPGSource / 130000000) - 1;
    }
    /* Note:APB_N[3] must be set to logic "HIGH" to configure clock divide number for enerating the PCLKof APB bus and controllers in APB bus */
    outp32(REG_CLKDIV4, (inp32(REG_CLKDIV4) & ~JPG_N) | ((u32JPGDiv & 0x7) << 24) | 0x800);

    //sysprintf("jpeg engine is %dHz\n",u32JPGSource / (u32JPGDiv+1));

    /* 3.Reset IP */
    outp32(REG_AHBIPRST, JPG_RST);
    outp32(REG_AHBIPRST, 0);
    /* 4.Configure IP according to inputted arguments (check CLKSEL) */
    sysInstallISR(IRQ_LEVEL_1, IRQ_JPG, (PVOID)jpegISR);
    sysSetLocalInterrupt(ENABLE_IRQ);
    sysEnableInterrupt(IRQ_JPG);
    /* 5.Enable IP I/O pins */
    /* 6.Return E_JPEG_SUCCESS to present success */
    return E_JPEG_SUCCESS;
}

VOID jpegInit(VOID)
{
    /* Set the default values of the JPEG registers */
    g_bScale = FALSE;
    g_u32Stride = 0;
    g_u32BufferCount = 0;
    g_u16ReserveSize = 0;
    g_u32WindowDec = FALSE;
    g_u32windowSizeX = 0;
    g_u32windowSizeY = 0;
    g_InputWait = FALSE;
    g_bEncThumbnailDownScale = FALSE;
    g_bEncPrimaryDownScale = FALSE;
    g_OutputWait = FALSE;
    g_u32EncRotate = 0;
    pfnJpegHeaderDecode = NULL;
    pfnJpegDecInputWait = NULL;
    pfnJpegDecOutputWait = NULL;
    outp32(REG_JPRIQC, 0x000000F4);
    outp32(REG_JTHBQC, 0x000000F4);
    outp32(REG_JPRST, 0x00000004);
    outp32(REG_JTRST, 0x00000004);
    outp32(REG_JITCR, 0x00000000);
    outp32(REG_JINTCR, 0x00000000);
    outp32(JDOWFBS,0xFFFFFFFF);

    /* Disable the Primary Up-scaling & Scaling-down */
    outp32(REG_JPSCALU, 0x00000000);
    outp32(REG_JPSCALD, 0x00000000);

    /* Reset JUPRAT and JSRCH */
    outp32(REG_JUPRAT, 0x00000000);
    outp32(REG_JSRCH, 0x00000FFF);

    /* Reset JPEG (JMCR [1]) */
    outp32(REG_JMCR,0x00000002);
    outp32(REG_JMCR,0x00000000);
    outp32(REG_JMACR, 0x00400000);  /* Can't use single buffer */
}

INT jpegSetEncodeMode(UINT8 u8SourceFormat, UINT16 u16JpegFormat)
{
    UINT8 u8Gray = 0;
    switch(u16JpegFormat)
    {
        case JPEG_ENC_PRIMARY_YUV420:
        case JPEG_ENC_PRIMARY_YUV422:
        case JPEG_ENC_THUMBNAIL_YUV420:
        case JPEG_ENC_THUMBNAIL_YUV422:
        case (JPEG_ENC_PRIMARY_YUV420 | JPEG_ENC_THUMBNAIL_YUV420):
        case (JPEG_ENC_PRIMARY_YUV422 | JPEG_ENC_THUMBNAIL_YUV422):
            outp32(REG_JMCR,(inp32(REG_JMCR) & WIN_DEC )| u16JpegFormat);
            u8Gray = 0;
            break;
        case JPEG_ENC_PRIMARY_GRAY:
        case JPEG_ENC_THUMBNAIL_GRAY:
        case (JPEG_ENC_PRIMARY_GRAY | JPEG_ENC_THUMBNAIL_GRAY):
            if(u8SourceFormat == JPEG_ENC_SOURCE_PACKET)
                return E_JPEG_INVALID_PARAM;
            else
            {
                if(u16JpegFormat == (JPEG_ENC_PRIMARY_GRAY | JPEG_ENC_THUMBNAIL_GRAY))
                    outp32(REG_JMCR, 0xB0);
                else
                    outp32(REG_JMCR, 0xA0);
            }
            u8Gray = EY_ONLY;
            break;
        default:
            return E_JPEG_INVALID_PARAM;
    }
    g_u32OpMode = JPEG_ENC_UPSCALE_MODE;

    if(g_bEncPrimaryDownScale)
        g_u32OpMode = JPEG_ENC_PLANAR_PRIMARY_DOWNSCALE_MODE;

    if (g_bEncThumbnailDownScale)
        g_u32OpMode = JPEG_ENC_PLANAR_THUMBNAIL_DOWNSCALE_MODE;

    if(u8SourceFormat == JPEG_ENC_SOURCE_PLANAR)
        outp32(REG_JITCR, (inp32(REG_JITCR) & (0x8 | ROTATE)) | PLANAR_ON | u8Gray);
    else if(u8SourceFormat == JPEG_ENC_SOURCE_PACKET)
        outp32(REG_JITCR, inp32(REG_JITCR) & ~(PLANAR_ON | ROTATE));
    else
        return E_JPEG_INVALID_PARAM;

    return E_JPEG_SUCCESS;
}

INT jpegSetDecodeMode(UINT32 u32OutputFormat)
{
    switch(u32OutputFormat)
    {
        case JPEG_DEC_PRIMARY_PLANAR_YUV:
        case JPEG_DEC_PRIMARY_PACKET_YUV422:
        case JPEG_DEC_PRIMARY_PACKET_RGB555:
        case JPEG_DEC_PRIMARY_PACKET_RGB555R1:
        case JPEG_DEC_PRIMARY_PACKET_RGB555R2:
        case JPEG_DEC_THUMBNAIL_PLANAR_YUV:
        case JPEG_DEC_THUMBNAIL_PACKET_YUV422:
        case JPEG_DEC_THUMBNAIL_PACKET_RGB555:
        case JPEG_DEC_PRIMARY_PACKET_RGB565:
        case JPEG_DEC_PRIMARY_PACKET_RGB565R1:
        case JPEG_DEC_PRIMARY_PACKET_RGB565R2:
        case JPEG_DEC_PRIMARY_PACKET_RGB888:
            outp32(REG_JITCR, u32OutputFormat);
            outp32(REG_JMCR,inp32(REG_JMCR) & ~ENC_DEC);
            g_u32OpMode = JPEG_DEC_PACKET_DOWNSCALE_MODE;
            g_u32OutputFormat = u32OutputFormat;
            if(u32OutputFormat == JPEG_DEC_PRIMARY_PLANAR_YUV || u32OutputFormat == JPEG_DEC_THUMBNAIL_PLANAR_YUV)
                g_u32OpMode = JPEG_DEC_PLANAR_DOWNSCALE_MODE;
            break;
        default:
            return E_JPEG_INVALID_PARAM;
    }
    return E_JPEG_SUCCESS;
}

VOID jpegDecodeTrigger(VOID)
{
    g_bWait = TRUE;
    g_jpegError	= FALSE;
    
    memset(&jpegInfo, 0, sizeof(jpegInfo));
    
    /* Decode Complete /Decode Header End/Decode Error Interrupt Enable and clear the Decode Complete /Decode Header End/Decode Error Interrupt */
    if(g_InputWait)
    {
        g_u32BufferCount = 0;
        g_u32DecInputWaitAddr = JPEG_GET_BITSTREAM_ADDR();
        if(g_OutputWait)
            JPEG_INT_ENABLE( DEC_INTE | DER_INTE | DHE_INTE | IPW_INTE | JPG_DOW_INTE);
        else
            JPEG_INT_ENABLE( DEC_INTE | DER_INTE | DHE_INTE | IPW_INTE);
    }
    else if (g_OutputWait)
    {
        JPEG_INT_ENABLE( DEC_INTE | DER_INTE | DHE_INTE | JPG_DOW_INTE);
    }
    else
        JPEG_INT_ENABLE( DEC_INTE | DER_INTE | DHE_INTE);

    JPEG_CLEAR_INT(DEC_INTS | JPEG_DER_INTS | JPEG_DHE_INTS | JPEG_IPW_INTS | JPG_DOW_INTS);

    outp32(REG_JMCR, JPG_EN | inp32(REG_JMCR));
    outp32(REG_JMCR, ~JPG_EN & inp32(REG_JMCR));
}

VOID jpegEncodeTrigger(VOID)
{
    g_bWait = TRUE;
    g_jpegError = FALSE;
    
    g_u32OpMode = JPEG_ENC_UPSCALE_MODE;

    if(g_bEncPrimaryDownScale)
        g_u32OpMode = JPEG_ENC_PLANAR_PRIMARY_DOWNSCALE_MODE;

    if (g_bEncThumbnailDownScale)
        g_u32OpMode = JPEG_ENC_PLANAR_THUMBNAIL_DOWNSCALE_MODE;

    memset(&jpegInfo, 0, sizeof(jpegInfo));
    
    if(g_u32EncRotate!=0)
    {
        UINT16 u16Height,u16Width;

        if(((inp32(REG_JITCR) & (PLANAR_ON | EY_ONLY)) != (PLANAR_ON)) && ((inp32(REG_JMCR) & EY422) != 0))
        {
            g_jpegError = TRUE;
            g_bWait = FALSE; 
            return;
        }
        jpegGetDimension(&u16Width,&u16Height);

        if(g_u32EncRotate == JPEG_IOCTL_SET_ENCODE_PRIMARY_ROTATE_LEFT)
        {
            JPEG_SET_YADDR((JPEG_GET_YADDR() + (u16Width - 1)));
            JPEG_SET_UADDR((JPEG_GET_UADDR() + (u16Width/2-1)));
            JPEG_SET_VADDR((JPEG_GET_VADDR() + (u16Width/2-1)));
        }
        else
        {
            JPEG_SET_YADDR((JPEG_GET_YADDR() + ((u16Height-1) * u16Width)));
            u16Width = JPEG_GET_USTRIDE();
            JPEG_SET_UADDR((JPEG_GET_UADDR() + ((u16Height-2) * u16Width/2)));
            JPEG_SET_VADDR((JPEG_GET_VADDR() + ((u16Height-2) * u16Width/2)));
        } 
    }
    
    if(g_bScale)
    {
        UINT16 u16Height,u16Width,u16ratioH,u16ratioW;

        if(g_u32EncRotate!=0)
            jpegGetDimension(&u16Width,&u16Height);
        else
            jpegGetDimension(&u16Height,&u16Width);

        if(jpegCalScalingFactor(
            g_u32OpMode,        /* Up / Down Scaling */
            u16Height,          /* Original Height */
            u16Width,           /* Original Width */
            g_u32ScaleHeight,   /* Scaled Height */
            g_u32ScaleWidth,    /* Scaled Width */
            &u16ratioH,         /* Horizontal Ratio */
            &u16ratioW          /* Vertical Ratio */
        ) != E_JPEG_SUCCESS)
        {
            g_jpegError = TRUE;
            g_bWait = FALSE; 
            return;
        }
        else
        {
            jpegSetScalingFactor(g_u32OpMode, u16ratioH, u16ratioW);
            if(g_bEncThumbnailDownScale)
                outp32(REG_JTHBWH,((g_u32ScaleHeight & 0x1FFF)<<16) | (g_u32ScaleWidth & 0x1FFF) );
            else
            {
                if(g_u32EncRotate!=0)
                    jpegSetDimension(g_u32ScaleWidth, g_u32ScaleHeight);
                else
                    jpegSetDimension(g_u32ScaleHeight, g_u32ScaleWidth);
            }
        }
    }

    /* Encode Complete Interrupt Enable and clear the Encode Complete Interrupt */
    JPEG_INT_ENABLE(ENC_INTE);
    JPEG_CLEAR_INT(ENC_INTS);

    outp32(REG_JMCR, JPG_EN | inp32(REG_JMCR));    
    outp32(REG_JMCR, ~JPG_EN & inp32(REG_JMCR));
}

INT jpegCalScalingFactor(
    UINT8 u8Mode,             /* Up / Down Scaling */
    UINT16 u16Height,         /* Original Height */
    UINT16 u16Width,          /* Original Width */
    UINT16 u16ScalingHeight,  /* Scaled Height */
    UINT16 u16ScalingWidth,   /* Scaled Width */
    PUINT16 pu16RatioH,       /* Horizontal Ratio */
    PUINT16 pu16RatioW        /* Vertical Ratio */
)
{
    if(u8Mode == JPEG_ENC_UPSCALE_MODE)
    {
        if(u16ScalingHeight < u16Height || u16ScalingWidth < u16Width)
            return E_JPEG_INVALID_PARAM;

        *pu16RatioW = (UINT32)((float) (u16ScalingWidth-1) / (float)(u16Width-2) * 1024);
        *pu16RatioH = (UINT32)((float) (u16ScalingHeight-1) / (float)(u16Height-2) * 1024);
    }
    else if(u8Mode == JPEG_DEC_PACKET_DOWNSCALE_MODE) 
    {
        if(u16ScalingHeight > u16Height || u16ScalingWidth> u16Width)
            return E_JPEG_INVALID_PARAM;

        *pu16RatioW = (UINT32)(((float) (u16ScalingWidth) / (u16Width-1) * 8192));

        if(*pu16RatioW > 8192)
            *pu16RatioW = 8192;

        *pu16RatioH = (UINT32)((float) (u16ScalingHeight) / (u16Height - 1) * 8192);

        if(*pu16RatioH > 8192)
            *pu16RatioH = 8192;
    }
    else if(u8Mode == JPEG_DEC_PLANAR_DOWNSCALE_MODE || u8Mode == JPEG_ENC_PLANAR_PRIMARY_DOWNSCALE_MODE || u8Mode == JPEG_ENC_PLANAR_THUMBNAIL_DOWNSCALE_MODE)
    {
        UINT16 u16RatioW,u16RatioH;
        if(u16ScalingHeight > u16Height || u16ScalingWidth> u16Width)
            return E_JPEG_INVALID_PARAM;
        if(u16Height % u16ScalingHeight)
            return E_JPEG_INVALID_PARAM;
        if(u16Width % u16ScalingWidth)
            return E_JPEG_INVALID_PARAM;

        u16RatioW = u16Width / u16ScalingWidth;

        u16RatioW = u16RatioW / 2 - 1;

        if((u16RatioW != 0) && (u16RatioW != 1) && (u16RatioW != 3))
            return E_JPEG_INVALID_PARAM;

        u16RatioH = u16Height / u16ScalingHeight - 1;

        if((u16RatioH != 0) && (u16RatioH != 1) && (u16RatioH != 3) && (u16RatioH != 7))
            return E_JPEG_INVALID_PARAM;

        *pu16RatioW = u16RatioW;
        *pu16RatioH = u16RatioH;
    }
    else
        return E_JPEG_INVALID_PARAM;

    return E_JPEG_SUCCESS;
}

INT jpegSetScalingFactor(
    UINT8 u8Mode,         /* Up / Down Scaling */
    UINT16 u16FactorH,    /* Vertical Scaling Factor */
    UINT16 u16FactorW     /* Horizontal Scaling Factor */
)
{
    if(u8Mode == JPEG_ENC_UPSCALE_MODE)
    {
        JPEG_DEC_DISABLE_DOWNSCALING();
        JPEG_ENC_ENABLE_UPSCALING();
    }
    else if(u8Mode == JPEG_DEC_PACKET_DOWNSCALE_MODE || u8Mode == JPEG_DEC_PLANAR_DOWNSCALE_MODE || u8Mode == JPEG_ENC_PLANAR_PRIMARY_DOWNSCALE_MODE)
    {
        JPEG_DEC_ENABLE_DOWNSCALING();
        JPEG_ENC_DISABLE_UPSCALING();
    }
    else if(u8Mode == JPEG_ENC_PLANAR_THUMBNAIL_DOWNSCALE_MODE)
    {
        outp32(REG_JTSCALD, TSX_ON);
    }

    if(u8Mode == JPEG_DEC_PLANAR_DOWNSCALE_MODE || u8Mode == JPEG_ENC_PLANAR_PRIMARY_DOWNSCALE_MODE)
        outp32(REG_JPSCALD,  (inp32(REG_JPSCALD) & ~(PSCALX_F | PSCALY_F)) | ((u16FactorW & 0x1F) << 8) | (u16FactorH & 0x1F));
    else if(u8Mode == JPEG_ENC_PLANAR_THUMBNAIL_DOWNSCALE_MODE)
        outp32(REG_JTSCALD,  (inp32(REG_JTSCALD) & ~(TSCALX_F | TSCALY_F)) | ((u16FactorW & 0x1F) << 8) | (u16FactorH & 0x1F));
    else
    {
        outp32(REG_JPSCALD, inp32(REG_JPSCALD) & ~(PSCALX_F | PSCALY_F));
        outp32(REG_JUPRAT, ((u16FactorH & 0x3FFF) << 16) | (u16FactorW & 0x3FFF));
    }
    return E_JPEG_SUCCESS;
}

VOID jpegGetDecodedDimension(
    PUINT16 pu16Height,    /* Decode/Encode Height */
    PUINT16	pu16Width      /* Decode/Encode Width */
)
{
    *pu16Width = inp32(REG_JDECWH) & 0x0000FFFF;
    *pu16Height = inp32(REG_JDECWH) >> 16;
}


VOID jpegSetDimension(
    UINT16 u16Height,      /* Decode/Encode Height */
    UINT16 u16Width        /* Decode/Encode Width */
)
{
    outp32(REG_JPRIWH,((u16Height & 0x1FFF)<<16) | (u16Width & 0x1FFF) );
}

VOID jpegGetDimension(
    PUINT16 pu16Height,    /* Decoded Height from bit stream */
    PUINT16	pu16Width      /* Decoded Width  from bit stream */
)
{
    *pu16Height = inp32(REG_JPRIWH) >> 16; 
    *pu16Width = inp32(REG_JPRIWH) & 0x1FFF;
}

VOID jpegGetScalingFactor(
    UINT8	u8Mode,            /* Up / Down Scaling */
    PUINT16 pu16FactorH,     /* Vertical Scaling Factor */
    PUINT16	pu16FactorW      /* Horizontal Scaling Factor */
)
{
    if(u8Mode == JPEG_DEC_PLANAR_DOWNSCALE_MODE)
    {
        *pu16FactorH = inp32(REG_JPSCALD) & 0x3F;
        *pu16FactorW = (inp32(REG_JPSCALD) >> 8) & 0x1F;
    }
    else
    {
        *pu16FactorH = (inp32(REG_JUPRAT) >> 16) & 0x3FFF;
        *pu16FactorW = inp32(REG_JUPRAT) & 0x3FFF;
    }
}

INT jpegSetWindowDecode(
    UINT16  u16StartMCUX,    /* Start X MCU */
    UINT16  u16StartMCUY,    /* Horizontal Scaling Factor */
    UINT16  u16EndMCUX,      /* Vertical Scaling Factor */
    UINT16  u16EndMCUY,      /* Horizontal Scaling Factor */
    UINT32  u32Stride        /* Decode Output Stride */
)
{
    if(u16StartMCUX >= u16EndMCUX || u16StartMCUY >= u16EndMCUY)
        return E_JPEG_INVALID_PARAM;
    
    outp32(REG_JWINDEC0, u16StartMCUY << 16 | u16StartMCUX);      
    outp32(REG_JWINDEC1, u16EndMCUY << 16 | u16EndMCUX); 
    outp32(REG_JWINDEC2, u32Stride);
    outp32(REG_JMCR, WIN_DEC);
    return E_JPEG_SUCCESS;
}



INT jpegAdjustQTAB(
    UINT8 u8Mode,
    UINT8 u8Qadjust,
    UINT8 u8Qscaling
)
{
    UINT32 u32Addr;
    if(u8Mode == JPEG_ENC_PRIMARY)
        u32Addr = REG_JPRIQC;
    else if(u8Mode == JPEG_ENC_THUMBNAIL)
        u32Addr = REG_JTHBQC;
    else
        return E_JPEG_INVALID_PARAM;

    outp32(u32Addr,((u8Qadjust & 0xF) << 4 )| (u8Qscaling & 0xF));
    return E_JPEG_SUCCESS;
}

INT jpegSetQTAB(
    PUINT8 puQTable0,
    PUINT8 puQTable1,
    PUINT8 puQTable2,
    UINT8 u8num
)
{
    UINT32 u32value;
    UINT32 u32TimeOut;
    int i;

    u32TimeOut = 0xFFFFFF;
    for(i = 0; i < 64; i=i+4)
    {
        while((inp32(REG_JMCR) & QT_BUSY) & u32TimeOut)
            u32TimeOut--;

        if(!u32TimeOut)
            return E_JPEG_TIMEOUT;

        u32value = puQTable0[i] | (puQTable0[i+1]<<8) | (puQTable0[i+2]<<16) | (puQTable0[i+3]<<24);
        outp32((REG_JQTAB0 + i),u32value); 
    }     
    
    u32TimeOut = 0xFFFFFF;
    for(i = 0; i < 64; i=i+4)
    {
        while((inp32(REG_JMCR) & QT_BUSY) & u32TimeOut)
            u32TimeOut--;

        if(!u32TimeOut)
            return E_JPEG_TIMEOUT;

        u32value = puQTable1[i] | (puQTable1[i+1]<<8) | (puQTable1[i+2]<<16) | (puQTable1[i+3]<<24);
        outp32((REG_JQTAB1 + i),u32value); 
    }          
 
    if (u8num <3)
        return E_JPEG_SUCCESS;
       
    outp32(JITCR, inp32(JITCR) | 0x8);
    
    u32TimeOut = 0xFFFFFF;

    for(i = 0; i < 64; i=i+4)
    {
        while((inp32(REG_JMCR) & QT_BUSY) & u32TimeOut)
            u32TimeOut--;

        if(!u32TimeOut)
            return E_JPEG_TIMEOUT;

        u32value = puQTable2[i] | (puQTable2[i+1]<<8) | (puQTable2[i+2]<<16) | (puQTable2[i+3]<<24);
        outp32((REG_JQTAB2 + i),u32value); 
    }   
    
    u32TimeOut = 0xFFFFFF;
    while((inp32(REG_JMCR) & QT_BUSY) & u32TimeOut)             
        u32TimeOut--;

    if(!u32TimeOut)
        return E_JPEG_TIMEOUT;
    else
        return E_JPEG_SUCCESS;
}


VOID jpegIoctl(UINT32 cmd, UINT32 arg0, UINT32 arg1)
{
    JPEG_WINDOW_DECODE_T *winDecode;
    PUINT32 pu32Tmp;

    switch(cmd)
    {
        case JPEG_IOCTL_SET_YADDR:
            JPEG_SET_YADDR(arg0);
            break;
        case JPEG_IOCTL_SET_UADDR:
            JPEG_SET_UADDR(arg0);
            break;
        case JPEG_IOCTL_SET_VADDR:
            JPEG_SET_VADDR(arg0);
            break;
        case JPEG_IOCTL_SET_YSTRIDE:
            JPEG_SET_YSTRIDE(arg0);
            break;
        case JPEG_IOCTL_SET_USTRIDE:
            JPEG_SET_USTRIDE(arg0);
            break;
        case JPEG_IOCTL_SET_VSTRIDE:
            JPEG_SET_VSTRIDE(arg0);
            break;
        case JPEG_IOCTL_SET_BITSTREAM_ADDR:
            JPEG_SET_BITSTREAM_ADDR(arg0);
            break;
        case JPEG_IOCTL_SET_SOURCE_IMAGE_HEIGHT:
            JPEG_SET_SOURCE_IMAGE_HEIGHT(arg0);
            break;
        case JPEG_IOCTL_ENC_SET_HEADER_CONTROL:
            JPEG_ENC_SET_HEADER_CONTROL(arg0); 
            break;
        case JPEG_IOCTL_SET_DEFAULT_QTAB:
            jpegSetQTAB(g_au8QTable0,g_au8QTable1, 0, 2);
            break;
        case JPEG_IOCTL_SET_DECODE_MODE:
            jpegSetDecodeMode(arg0);
            break;
        case JPEG_IOCTL_SET_ENCODE_MODE:
            jpegSetEncodeMode(arg0, arg1);
            break;
        case JPEG_IOCTL_SET_DIMENSION:
            jpegSetDimension(arg0, arg1);
            break;
        case JPEG_IOCTL_ENCODE_TRIGGER:
            jpegEncodeTrigger(); 
            if(g_u16ReserveSize != 0)
            {
                UINT32 u32Addr = JPEG_GET_BITSTREAM_ADDR();
                outp8(u32Addr + 2,0xFF);
                outp8(u32Addr + 3,0xE0);
                outp8(u32Addr + 4,((g_u16ReserveSize - 4) & 0xFF00) >> 8);
                outp8(u32Addr + 5,(g_u16ReserveSize - 4) & 0xFF);
            }
            break;
        case JPEG_IOCTL_DECODE_TRIGGER:
            jpegDecodeTrigger(); 
            break;
        case JPEG_IOCTL_WINDOW_DECODE:
            winDecode = (JPEG_WINDOW_DECODE_T *)arg0;
            jpegSetWindowDecode(winDecode->u16StartMCUX, winDecode->u16StartMCUY, 
            winDecode->u16EndMCUX, winDecode->u16EndMCUY, winDecode->u32Stride);
            g_u32WindowDec = TRUE;
            g_u32windowSizeX = winDecode->u32Stride; 
            g_u32windowSizeY = 16 * (winDecode->u16EndMCUY - winDecode->u16StartMCUY+1);
            break;
        case JPEG_IOCTL_SET_DECODE_STRIDE:
            g_u32Stride = arg0;
            break;
        case JPEG_IOCTL_SET_DECODE_DOWNSCALE:
            g_bScale = TRUE;
            g_u32ScaleWidth = arg1;
            g_u32ScaleHeight = arg0;
            break;
        case JPEG_IOCTL_SET_ENCODE_UPSCALE:
            g_bScale = TRUE;
            g_u32ScaleWidth = arg1;
            g_u32ScaleHeight = arg0;
            break;
        case JPEG_IOCTL_SET_HEADERDECODE_CALBACKFUN:
            pfnJpegHeaderDecode =(PFN_JPEG_HEADERDECODE_CALLBACK) arg0;
            break;
        case JPEG_IOCTL_SET_DECINPUTWAIT_CALBACKFUN:
            g_InputWait = TRUE;
            pfnJpegDecInputWait =(PFN_JPEG_DECWAIT_CALLBACK) arg0;
            JPEG_DEC_SET_INPUT_WAIT(((UINT16) arg1 / 2048));
            g_u16BufferSize	= arg1 / 2;
            break;
        case JPEG_IOCTL_ADJUST_QTAB:
            jpegAdjustQTAB(arg0,((arg1 & 0xFF00) >> 4) ,(arg1 & 0xFF));
            break;
        case JPEG_IOCTL_ENC_RESERVED_FOR_SOFTWARE:
            if(arg0 > 0)
            {
                UINT32 u32Tmp;

                u32Tmp = arg0 + 4;
                if(u32Tmp % 2)
                    u32Tmp++;
                if((u32Tmp % 4) == 0)
                    u32Tmp+=2;
                if(u32Tmp >= 0xFFFF)
                    u32Tmp = 65534;

                outp32(REG_JPSCALU, inp32(REG_JPSCALU) | A_JUMP);
                outp32(JRESERVE, u32Tmp);
                g_u16ReserveSize = u32Tmp;
            }
            break;
        case JPEG_IOCTL_SET_ENCODE_PRIMARY_RESTART_INTERVAL:
            outp32(REG_JPRST,arg0);
            break;
        case JPEG_IOCTL_SET_ENCODE_THUMBNAIL_RESTART_INTERVAL:
            outp32(REG_JTRST,arg0);
            break;
        case JPEG_IOCTL_GET_ENCODE_PRIMARY_RESTART_INTERVAL:
            pu32Tmp = (PUINT32) arg0;
            *pu32Tmp = inp32(REG_JPRST);
            break;
        case JPEG_IOCTL_GET_ENCODE_THUMBNAIL_RESTART_INTERVAL:
            pu32Tmp = (PUINT32) arg0;
            *pu32Tmp = inp32(REG_JTRST);
            break;
        case JPEG_IOCTL_SET_THUMBNAIL_DIMENSION:
            outp32(REG_JTHBWH,((arg0 & 0x1FFF)<<16) | (arg1 & 0x1FFF) );
            break;
        case JPEG_IOCTL_SET_ENCODE_SW_OFFSET:
            outp32(REG_JOFFSET, arg0);
            break;
        case JPEG_IOCTL_GET_THUMBNAIL_DIMENSION:
            pu32Tmp = (PUINT32) arg0;
            *pu32Tmp = inp32(REG_JTHBWH) >> 16;

            pu32Tmp = (PUINT32) arg1;
            *pu32Tmp = inp32(REG_JTHBWH) & 0x1FFF;
            break;
        case JPEG_IOCTL_GET_ENCODE_SW_OFFSET:
            pu32Tmp = (PUINT32) arg0;
            *pu32Tmp = inp32(REG_JOFFSET);
            break;
        case JPEG_IOCTL_SET_ENCODE_PRIMARY_DOWNSCALE:
            g_bScale = TRUE;
            g_bEncPrimaryDownScale = TRUE;
            g_u32ScaleWidth = arg1;
            g_u32ScaleHeight = arg0;
            break;
        case JPEG_IOCTL_SET_ENCODE_THUMBNAIL_DOWNSCALE:
            g_bScale = TRUE;
            g_bEncThumbnailDownScale = TRUE;
            g_u32ScaleWidth = arg1;
            g_u32ScaleHeight = arg0;
            break;
        case JPEG_IOCTL_SET_ENCODE_PRIMARY_ROTATE_RIGHT:
            g_u32EncRotate = JPEG_IOCTL_SET_ENCODE_PRIMARY_ROTATE_RIGHT;
            outp32(REG_JITCR, (inp32(REG_JITCR) & ~ROTATE) | ROTATE);
            break;
        case JPEG_IOCTL_SET_ENCODE_PRIMARY_ROTATE_LEFT:
            g_u32EncRotate = JPEG_IOCTL_SET_ENCODE_PRIMARY_ROTATE_LEFT;
            outp32(REG_JITCR, (inp32(REG_JITCR) & ~ROTATE) | 0x1000);
            break;
        case JPEG_IOCTL_SET_ENCODE_PRIMARY_ROTATE_NORMAL:
            g_u32EncRotate = 0;
            outp32(REG_JITCR, (inp32(REG_JITCR) & ~ROTATE));
            break;
        case JPEG_IOCTL_SET_DECOUTPUTWAIT_CALBACKFUN:
            pfnJpegDecOutputWait = (PFN_JPEG_DECWAIT_CALLBACK) arg0;
            break;
        case JPEG_IOCTL_SET_DECOUTPUTWAIT:
            outp32(JYADDR0, arg0);
            outp32(JDOWFBS, arg1);
            g_OutputWait = TRUE;
            break;
        case JPEG_IOCTL_GET_DECOUTPUTWAIT_ADDR:
            pu32Tmp = (PUINT32) arg0;
            *pu32Tmp = inp32(JYADDR0);
            break;
        case JPEG_IOCTL_GET_DECOUTPUTWAIT_SIZE:
            pu32Tmp = (PUINT32) arg0;
            *pu32Tmp = inp32(JDOWFBS);
            break;
        case JPEG_IOCTL_SET_DECODE_COMPLETE_CALBACKFUN:
            pfnJpegDecodeComplete = (PFN_JPEG_CALLBACK) arg0;
            break;
        case JPEG_IOCTL_SET_ENCODE_COMPLETE_CALBACKFUN:
            pfnJpegEncodeComplete = (PFN_JPEG_CALLBACK) arg0;
            break;
        case JPEG_IOCTL_SET_DECODE_ERROR_CALBACKFUN:
            pfnJpegDecodeError = (PFN_JPEG_CALLBACK) arg0;
            break;
        default:
            break;
    }
}

VOID jpegClose(VOID)
{
    /* Reset JPEG (JMCR [1]) */
    outp32(REG_JMCR,0x00000002);
    outp32(REG_JMCR,0x00000000);

    outp32(REG_AHBCLK, (inp32(REG_AHBCLK) & ~JPG_CKE));

    sysDisableInterrupt(IRQ_JPG);
}


/* Poll the interrupt status and get if the interrupt is generated */
BOOL jpegPollInt(UINT32 u32Intflag)
{
    if(JPEG_GET_INT_STATUS() & u32Intflag)
        return 1;
    else
        return 0;	
}
