/**************************************************************************//**
 * @file     blt.c
 * @version  V3.00
 * @brief    N9H20 series BLT driver source file
 *
 * SPDX-License-Identifier: Apache-2.0
 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/

#include <stdio.h>
#include "N9H20.h"

static PFN_BLT_CALLBACK _bltCompCb          =   NULL;

static void _blt_intr_hdlr (void)
{
    UINT32 bltIntStat = inp32 (REG_BLTINTCR);
    
    if (bltIntStat & BLT_INTS) {    // BLT complete
        // clear interrupt (write 1 to clear)
        outp32 (REG_BLTINTCR, bltIntStat & BLT_INTE | BLT_INTS);
        if (_bltCompCb) {
            _bltCompCb ();
        }
    }
}

int /* ERRCODE */ bltOpen(void)
{
    // 1.Check I/O pins. If I/O pins are used by other IPs, return error code.
    // 2.Enable IP��s clock
    outp32 (REG_AHBCLK, inp32 (REG_AHBCLK) | BLT_CKE | HCLK4_CKE);
    // 3.Reset IP
    outp32 (REG_AHBIPRST, inp32 (REG_AHBIPRST) | BLTRST);
    outp32 (REG_AHBIPRST, inp32 (REG_AHBIPRST) & ~BLTRST);
    // 4.Configure IP according to inputted arguments.
    // 5.Enable IP I/O pins
    // 6.Register ISR.
    sysInstallISR (IRQ_LEVEL_1, IRQ_BLT, (void *) _blt_intr_hdlr);                      
    sysEnableInterrupt (IRQ_BLT);
    
    // 7.Return 0 to present success
    return Successful;
}


void bltClose(void)
{
    // 1.Disable IP I/O pins
    // 2.Disable IP��s clock
    outp32 (REG_AHBCLK, inp32 (REG_AHBCLK) & ~BLT_CKE);
}

//  Set Transform Matrix | a c |
//                       | b d |
void bltSetTransformMatrix(
    S_DRVBLT_MATRIX sMatrix     // [in] Transformation Matrix 
)
{
    outp32(REG_ELEMENTA, sMatrix.a);
    outp32(REG_ELEMENTB, sMatrix.b);
    outp32(REG_ELEMENTC, sMatrix.c);
    outp32(REG_ELEMENTD, sMatrix.d);    
}

// Get Transform Matrix
void bltGetTransformMatrix(
    S_DRVBLT_MATRIX* psMatrix       // [out] Transformation Matrix 
)
{
    psMatrix->a = inp32(REG_ELEMENTA);
    psMatrix->b = inp32(REG_ELEMENTB);
    psMatrix->c = inp32(REG_ELEMENTC);
    psMatrix->d = inp32(REG_ELEMENTD);  
}

//  Set Pixel Format of Source Image
int /* ERRCODE */
bltSetSrcFormat(
    E_DRVBLT_BMPIXEL_FORMAT eSrcFmt // [in] Source Image Format 
)
{
    if (eSrcFmt & ~0x3F)
        return ERR_BLT_INVALID_SRCFMT;
        
    outp32(REG_SFMT, eSrcFmt);
    return  Successful; 
}

// Get Pixel Format of Source Image
E_DRVBLT_BMPIXEL_FORMAT
bltGetSrcFormat(void)
{
    return (E_DRVBLT_BMPIXEL_FORMAT) (inp32(REG_SFMT) & 0x3F);
}

//  Set Pixel Format of Display 
int /* ERRCODE */
bltSetDisplayFormat(
    E_DRVBLT_DISPLAY_FORMAT eDisplayFmt // [in] Display Format 
)
{
    if (eDisplayFmt & ~0x07)
        return ERR_BLT_INVALID_DSTFMT;
        
    outp32(REG_DFMT, eDisplayFmt);        
    return  Successful; 
}

//  Get Pixel Format of Display 
E_DRVBLT_DISPLAY_FORMAT 
bltGetDisplayFormat(void)
{
    return (E_DRVBLT_DISPLAY_FORMAT) (inp32(REG_DFMT) & 0x07);
}

void 
bltEnableInt(
    E_BLT_INT_TYPE eIntType
)
{
    switch (eIntType) {
    case BLT_INT_CMPLT:
        outp32 (REG_BLTINTCR, inp32 (REG_BLTINTCR) | BLT_INTE); 
        break;
    }
}   

void 
bltDisableInt(
    E_BLT_INT_TYPE eIntType
)
{
    switch (eIntType) {
    case BLT_INT_CMPLT:
        outp32 (REG_BLTINTCR, inp32 (REG_BLTINTCR) & ~BLT_INTE);    
        break;
    }
}

//  Get Interrupt enable/disable status
BOOL bltIsIntEnabled (E_BLT_INT_TYPE eIntType)
{
    BOOL enabled = FALSE;
    
    switch (eIntType) {
    case BLT_INT_CMPLT:
        enabled = !! (inp32 (REG_BLTINTCR) & BLT_INTE);
        break;
    }
    
    return enabled;
}

//  Polling BitBlt Interrupt status
BOOL bltPollInt(E_BLT_INT_TYPE eIntType)
{
    BOOL status = FALSE;
    
    switch (eIntType) {
    case BLT_INT_CMPLT:
        status = !! (inp32 (REG_BLTINTCR) & BLT_INTS);
        break;
    }
    
    return status;
}

void bltInstallCallback (E_BLT_INT_TYPE eIntType, PFN_BLT_CALLBACK pfnCallback, PFN_BLT_CALLBACK* pfnOldCallback)
{
    if (eIntType == BLT_INT_CMPLT) {    // BLT complete
        if (pfnOldCallback) {
            *pfnOldCallback = _bltCompCb;
        }
        _bltCompCb = pfnCallback;
    }
}

// Set A/R/G/B Color Multiplier
void bltSetColorMultiplier(
    S_DRVBLT_ARGB16 sARGB16 // [in] ARGB Multiplier 
)
{
/*
    if ((sARGB16.i16Blue & 0xFFFF0000) || (sARGB16.i16Green & 0xFFFF0000) 
        || (sARGB16.i16Red & 0xFFFF0000) || (sARGB16.i16Alpha & 0xFFFF0000))
        return E_DRVBLT_FALSE_INPUT;    
*/        
    outp32(REG_MLTB, (inp32(REG_MLTB)  & 0xFFFF0000 ) | (sARGB16.i16Blue  & 0x0FFFF));
    outp32(REG_MLTG, (inp32(REG_MLTG)  & 0xFFFF0000 ) | (sARGB16.i16Green & 0x0FFFF));
    outp32(REG_MLTR, (inp32(REG_MLTR)  & 0xFFFF0000 ) | (sARGB16.i16Red   & 0x0FFFF));          
    outp32(REG_MLTA, (inp32(REG_MLTA)  & 0xFFFF0000 ) | (sARGB16.i16Alpha & 0x0FFFF));
}

// Get A/R/G/B Color Multiplier
void bltGetColorMultiplier(
    S_DRVBLT_ARGB16* psARGB16   // [out] ARGB Multiplier 
)
{
    psARGB16->i16Blue   = inp32(REG_MLTB) & 0xFFFF;
    psARGB16->i16Green  = inp32(REG_MLTG) & 0xFFFF;
    psARGB16->i16Red    = inp32(REG_MLTR) & 0xFFFF;
    psARGB16->i16Alpha  = inp32(REG_MLTA) & 0xFFFF;
}

// Set A/R/G/B Color Offset
void bltSetColorOffset(
    S_DRVBLT_ARGB16 sARGB16 // [in] ARGB offset 
)
{

    outp32(REG_MLTB, (inp32(REG_MLTB)  & 0x0000FFFF ) | ((UINT32)(sARGB16.i16Blue  & 0x0FFFF) << 16));
    outp32(REG_MLTG, (inp32(REG_MLTG)  & 0x0000FFFF ) | ((UINT32)(sARGB16.i16Green & 0x0FFFF) << 16));
    outp32(REG_MLTR, (inp32(REG_MLTR)  & 0x0000FFFF ) | ((UINT32)(sARGB16.i16Red   & 0x0FFFF) << 16));          
    outp32(REG_MLTA, (inp32(REG_MLTA)  & 0x0000FFFF ) | ((UINT32)(sARGB16.i16Alpha & 0x0FFFF) << 16));
      
}

// Get A/R/G/B Color Offset
void bltGetColorOffset(
    S_DRVBLT_ARGB16* psARGB16   // [out] ARGB offset 
)
{

    psARGB16->i16Blue   = inp32(REG_MLTB) >> 16;
    psARGB16->i16Green  = inp32(REG_MLTG) >> 16;
    psARGB16->i16Red    = inp32(REG_MLTR) >> 16;
    psARGB16->i16Alpha  = inp32(REG_MLTA) >> 16;
}

// Set Source Image
void bltSetSrcImage(
    S_DRVBLT_SRC_IMAGE sSrcImage    // [in] Source Image Setting
)
{

    outp32(REG_SADDR,    sSrcImage.u32SrcImageAddr);    // Set Source Image Start Addr
    outp32(REG_SSTRIDE,  sSrcImage.i32Stride);          // Set Source Image Stride  
    outp32(REG_OFFSETSX, sSrcImage.i32XOffset);         // Set X offset into the source to start rendering from
    outp32(REG_OFFSETSY, sSrcImage.i32YOffset);         // Set Y offset into the source to start rendering from
    outp32(REG_SWIDTH,   sSrcImage.i16Width);           // Set width of source image
    outp32(REG_SHEIGHT,  sSrcImage.i16Height);          // Set height of source image
}

// Set Destination Frame Buffer
void bltSetDestFrameBuf(
    S_DRVBLT_DEST_FB sFrameBuf  // [in] Frame Buffer Setting
)
{
    outp32(REG_DADDR,    sFrameBuf.u32FrameBufAddr);    // Set Frame Buffer Start Addr
    //outp32(OFFSETDX, sFrameBuf.i32XOffset);           // X offset into the frame buffer in pixels
    //outp32(OFFSETDY, sFrameBuf.i32YOffset);           // Y offset into the frame buffer in pixels
    outp32(REG_DSTRIDE,  sFrameBuf.i32Stride);          // Set Frame Buffer Stride
    outp32(REG_DWIDTH,   sFrameBuf.i16Width);           // Set width of Frame Buffer
    outp32(REG_DHEIGHT,  sFrameBuf.i16Height);          // Set height of Frame Buffer
}

//  Set ARGB color for Fill Operation
void bltSetARGBFillColor(
    S_DRVBLT_ARGB8 sARGB8   // [in] ARGB value for fill operation
)
{
    UINT32 u32ARGB;
    
    u32ARGB = ((UINT32)sARGB8.u8Alpha << 24) | ((UINT32)sARGB8.u8Red << 16) 
              | ((UINT32)sARGB8.u8Green << 8) | sARGB8.u8Blue;  
    outp32(REG_FILLARGB, u32ARGB);            
}

//  Get ARGB color for Fill Operation
void bltGetARGBFillColor(
    S_DRVBLT_ARGB8* psARGB8 // [out] ARGB value for fill operation
)
{
    UINT32 u32ARGB;
    
    u32ARGB = inp32(REG_FILLARGB);
    psARGB8->u8Alpha    = (u32ARGB >> 24) & 0xFF;
    psARGB8->u8Red      = (u32ARGB >> 16) & 0xFF;
    psARGB8->u8Green    = (u32ARGB >> 8) & 0xFF;
    psARGB8->u8Blue     = u32ARGB & 0xFF;   
}

//  Get BitBlt engine busy(rendering) status
BOOL 
bltGetBusyStatus(void)
{
    return !! (inp32(REG_SET2DA) & BLIT_EN);
}

// Set Fill Blending bit
void bltSetFillAlpha(BOOL bEnable)
{
    outp32(REG_SET2DA, (inp32(REG_SET2DA) & ~FILL_BLEND) | (bEnable << 2));
}

// Get Fill Blending bit
BOOL 
bltGetFillAlpha(void)
{
    return !! ((inp32(REG_SET2DA) & FILL_BLEND) >> 2);
}


//  Set Transform Flag
void bltSetTransformFlag(
    UINT32 u32TransFlag         // [in] A combination of the enum E_DRVBLT_TRANSFORM_FLAG
)
{
    outp32(REG_SET2DA, (inp32(REG_SET2DA) & ~TRANS_FLAG) | (u32TransFlag << 4));
}

//  Get Transform Flag
UINT32 
bltGetTransformFlag(void)
{
    return (inp32(REG_SET2DA)  & TRANS_FLAG) >> 4;
}

// Set Palette Endian
void bltSetPaletteEndian(
    E_DRVBLT_PALETTE_ORDER eEndian  // [in] Palette Endian Type
)
{
    outp32(REG_SET2DA, (inp32(REG_SET2DA) & ~L_ENDIAN) | (eEndian << 1));
}

// Get Palette Endian
E_DRVBLT_PALETTE_ORDER 
bltGetPaletteEndian(void)
{
    return (E_DRVBLT_PALETTE_ORDER) ((inp32(REG_SET2DA) & L_ENDIAN) >> 1);
}

// Set the Color Palette used in BitBlt
// The format pointer by pu32ARGB are [31:24] -> Alpha, [23:16] -> Red, [15:8] -> Green, [7:0] -> Blue
void bltSetColorPalette(
    UINT32 u32PaletteInx,       // [in] Color Palette Start index
    UINT32 u32Num,              // [in] Color Palette number to set
    S_DRVBLT_ARGB8* psARGB          // [in] pointer for Color palette from u32PaletteInx
)
{
    UINT32 u32PaletteStartInx;
    UINT32 u32ARGB;
    int i;
    
    u32PaletteStartInx = REG_PALETTE + u32PaletteInx;
    for(i=0;i<u32Num;i++)
    {
        u32ARGB = ((UINT32)(psARGB-> u8Alpha) << 24) | ((UINT32)(psARGB-> u8Red) << 16) | 
                  ((UINT32)(psARGB-> u8Green) << 8) | psARGB-> u8Blue; 

        outp32(u32PaletteStartInx+i*4, u32ARGB);
        psARGB++;
    }
}

// Enable/Disable Fill Operation
void bltSetFillOP(
    E_DRVBLT_FILLOP eOP     // [in] Enable/Disable FillOP
)
{
    outp32(REG_SET2DA,(inp32(REG_SET2DA) &  ~FILL_OP) | (eOP<<11));
}

// Get Fill or Blit Operation 
BOOL 
bltGetFillOP(void)
{
    return !! ((inp32(REG_SET2DA) & FILL_OP) >> 11);
}

// Set Fill stype 
void bltSetFillStyle(
    E_DRVBLT_FILL_STYLE eStyle      // [in] Fill Style for Operation
)
{
    outp32(REG_SET2DA,(inp32(REG_SET2DA) &  ~FILL_STYLE) | (eStyle <<8));
}

// Get Fill stype 
E_DRVBLT_FILL_STYLE 
bltGetFillStyle(void)
{
    return (E_DRVBLT_FILL_STYLE) ((inp32(REG_SET2DA) & FILL_STYLE) >> 8);
}

// Enable/Disable Pre-multiply with alpha
void bltSetRevealAlpha(
    E_DRVBLT_REVEAL_ALPHA eAlpha        // [in] need / no need un-multiply alpha on source image
)
{
    outp32(REG_SET2DA,(inp32(REG_SET2DA) &  ~S_ALPHA) | (eAlpha << 3));
}

// Get Pre-multiply status
BOOL 
bltGetRevealAlpha(void)
{
    return !! ((inp32(REG_SET2DA) & S_ALPHA) >> 3);
}


//  Trigger BitBlt engine to render image
void bltTrigger(void)
{
    outp32(REG_SET2DA,inp32(REG_SET2DA) | BLIT_EN);
}

void bltSetRGB565TransparentColor(
    UINT16 u16RGB565    // [in] RGB565 Transparent Color
)
{
    outp32(REG_TRCOLOR, u16RGB565 & TRCOLOR);
    
}

UINT16 
bltGetRGB565TransparentColor(void)
{
    return inp32(REG_TRCOLOR) & TRCOLOR;
}


void bltSetRGB565TransparentCtl(
BOOL bEnable
)
{
    outp32(REG_SET2DA, (inp32(REG_SET2DA) & ~TRCOLOR_E) | (bEnable << 7));
}

BOOL 
bltGetRGB565TransparentCtl(void)
{
    return !! (inp32(REG_SET2DA) & TRCOLOR_E);
}

void bltFlush (void)
{
    while (bltGetBusyStatus() !=0 );
}

void bltFIBlit(S_FI_BLITOP sBlitOp)
{
    S_DRVBLT_DEST_FB sNewDestFB;
    UINT32 u32BytePerPixel;    
       
    bltFlush ();
    
    bltSetFillOP((E_DRVBLT_FILLOP) FALSE);    
    
    if (sBlitOp.sFISrcImage.psARGB8 != 0)
        bltSetColorPalette(0, 256, sBlitOp.sFISrcImage.psARGB8);
        
#if 1
    // Src Offset is 16.16 formt
    sBlitOp.sFISrcImage.sDrvSrcImage.i32XOffset = sBlitOp.sFISrcImage.sDrvSrcImage.i32XOffset << 16;        
    sBlitOp.sFISrcImage.sDrvSrcImage.i32YOffset = sBlitOp.sFISrcImage.sDrvSrcImage.i32YOffset << 16;    
#endif          
    bltSetSrcImage(sBlitOp.sFISrcImage.sDrvSrcImage);    

    // Remove OFFSETDX and OFFSETDY
    if (inp32(REG_DFMT) & D_ARGB8888)
        u32BytePerPixel = 4;
    else
        u32BytePerPixel = 2;
        
    sNewDestFB.i32Stride       = sBlitOp.sDestFB.i32Stride;  
    sNewDestFB.i16Width        = sBlitOp.sDestFB.i16Width; 
    sNewDestFB.i16Height       = sBlitOp.sDestFB.i16Height; 

    sNewDestFB.u32FrameBufAddr = sBlitOp.sDestFB.u32FrameBufAddr + sBlitOp.sDestFB.i32YOffset * sBlitOp.sDestFB.i32Stride 
                                 +  sBlitOp.sDestFB.i32XOffset *u32BytePerPixel;
                                 
    sNewDestFB.i32XOffset      = 0; 
    sNewDestFB.i32YOffset      = 0;  
    
    bltSetDestFrameBuf(sNewDestFB);
    
    if (sBlitOp.psTransform != NULL)
    {
        bltSetTransformMatrix(sBlitOp.psTransform->sMatrix);    
        bltSetSrcFormat(sBlitOp.psTransform->eSrcFmt);
        bltSetDisplayFormat(sBlitOp.psTransform->eDestFmt);     
        bltSetTransformFlag(sBlitOp.psTransform->i32Flag);
        bltSetColorMultiplier(sBlitOp.psTransform->sColorMultiplier);  
        bltSetColorOffset(sBlitOp.psTransform->sColorOffset);           
        bltSetFillStyle(sBlitOp.psTransform->eFilStyle);
    }   
    else {
        // Nuvoton CCLi8 (2012.08.06)
        // FIXME
        //defaultSetting();
        //outp32(REG_MLTA, 0x00000100);
        //outp32(REG_MLTR, 0x00000100);
        //outp32(REG_MLTG, 0x00000100);
        //outp32(REG_MLTB, 0x00000100);

        //outp32(REG_ELEMENTA, 0x00010000); 
        //outp32(REG_ELEMENTB, 0x00000000);
        //outp32(REG_ELEMENTC, 0x00000000);
        //outp32(REG_ELEMENTD, 0x00010000); 
        
        S_DRVBLT_MATRIX xform_mx = {0x00010000, 0x00000000, 0x00000000, 0x00010000};
        bltSetTransformMatrix (xform_mx);
        bltSetTransformFlag (0);
        bltSetFillStyle ((E_DRVBLT_FILL_STYLE) 0);
    }
    
    bltTrigger(); 
    
    bltFlush ();
}


void bltFIFill(S_FI_FILLOP sFillOp)
{
    S_DRVBLT_DEST_FB sDestFB;
    S_DRVBLT_ARGB8   sARGB8;
    UINT32 u32BytePerPixel;
    
    
    bltFlush ();
    
    bltSetFillOP((E_DRVBLT_FILLOP) TRUE);
    
    sARGB8.u8Blue   = sFillOp.sARGB8.u8Blue;
    sARGB8.u8Green  = sFillOp.sARGB8.u8Green;
    sARGB8.u8Red    = sFillOp.sARGB8.u8Red; 
    sARGB8.u8Alpha  = sFillOp.sARGB8.u8Alpha;             
    bltSetARGBFillColor(sARGB8);
    
    if (inp32(REG_DFMT) & D_ARGB8888)
        u32BytePerPixel = 4;
    else
        u32BytePerPixel = 2;
        
    sDestFB.i32Stride       = sFillOp.i32Stride;  
    sDestFB.i16Width        = sFillOp.sRect.i16Xmax - sFillOp.sRect.i16Xmin; 
    sDestFB.i16Height       = sFillOp.sRect.i16Ymax - sFillOp.sRect.i16Ymin; 

    sDestFB.u32FrameBufAddr = sFillOp.u32FBAddr + sFillOp.sRect.i16Ymin*sFillOp.i32Stride +  sFillOp.sRect.i16Xmin *u32BytePerPixel; 

    sDestFB.i32XOffset      = 0; 
    sDestFB.i32YOffset      = 0;     
    
    bltSetDestFrameBuf(sDestFB);
    
    bltSetDisplayFormat(sFillOp.eDisplayFmt);
    
    bltSetFillAlpha(sFillOp.i32Blend);
    
    bltTrigger();
    
    bltFlush ();
}