//*****************************************************************************
//
//! @file mspi_iom_display.c
//!
//! @brief Example demonstrating the hardware assisted MSPI to IOM transfer
//!
//! This example demonstrates transferring a large buffer from a PSRAM device
//! connected on MSPI, to a Display device connected to IOM, using hardware
//! handshaking in Apollo3 - with minimal CPU involvement.
//!
//! At initialization, both the Display and PSRAM are initialized and a set image
//! data is written to the PSRAM for transfer to Display.
//!
//! The Display is connected to IOM using SPI interface, and hence the transactions
//! are limited in size to 4095 Bytes.
//! However MSPI PSRAM imposes further limit on max transaction size as the page size
//! of 1K limits us to use max 1K bursts
//! The program here creates a command queue for both the MSPI and IOM, to
//! create a sequence of transactions - each reading a segment of the source
//! buffer to a temp buffer in internal SRAM, and then writing the same to the
//! Display using the IOM. It uses hardware handshaking so that the IOM transaction
//! is started only once the segement is read out completely from MSPI PSRAM.
//!
//! To best utilize the buses, a ping-pong model is used using two temporary
//! buffers in SRAM. This allows the interfaces to not idle while waiting for
//! other to finish - essentially achieving close to the bandwidth achieved by
//! the slower of the two.
//!
//! Configurable parameters at compile time:
//! IOM to use (DISPLAY_IOM_MODULE)
//! Display device to use (define one of DISPLAY_DEVICE_* to 1)
//! MSPI PSRAM to use - uses compile time definitions from am_device_mspi_psram.h
//! FRAME_SIZE - total size of transaction - controlled by ROW_NUM & COLUMN_NUM of display
//! SPI_TXN_SIZE - size of temporary ping-pong buffer
//! CPU_SLEEP_GPIO - tracks CPU sleeping on analyzer
//!
//! Operating modes:
//! SEQLOOP not defined (normal mode) - The CQ is programmed each iteration using HAL APIs
//! SEQLOOP - Create sequence once, which repeats when triggered by callback at the end of each iteration (should be used if the same framebuffer is used every time)
//! CQ_RAW - Uses Preconstructed CQ for IOM and MSPI (only small changes done at run time) - to save on the time to program the same at run time
//!
//! Memory Impacts of modes: If CQ_RAW is used - the buffer supplied to HAL for CQ could be very small,
//! as the raw CQ is supplied by the application. So overall memory usage is still about the same
//!
//! @verbatim
//! Pin connections:
//! IOM:
//! Particular IOM to use for this example is controlled by macro DISPLAY_IOM_MODULE
//! This example use apollo3_evb board connected to a display board
//! Default pin settings for this example using IOM1 are:
//! #define AM_DISPLAY_GPIO_CSB            13
//! #define AM_DISPLAY_GPIO_TE_PIN         19
//! #define AM_DISPLAY_GPIO_A0             9
//! #define AM_DISPLAY_GPIO_SDA            7
//! #define AM_DISPLAY_GPIO_SCK            8
//!
//! MSPI:
//! The MSPI PSRAM device uses is controlled by macro DISPLAY_DEVICE_* (set one of them to 1)
//! This example uses apollo3_evb board connected to a psram board
//! #define AM_BSP_GPIO_MSPI_CE0            1
//! #define AM_BSP_MSPI_CE0_CHNL            1
//! #define AM_BSP_GPIO_MSPI_D0             22
//! #define AM_BSP_GPIO_MSPI_D1             26
//! #define AM_BSP_GPIO_MSPI_D2             4
//! #define AM_BSP_GPIO_MSPI_D3             23
//! #define AM_BSP_GPIO_MSPI_SCK            24
//!
//! @endverbatim
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2019, Ambiq Micro
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
// 
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision v2.0.0-282-ga0939c5f8 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_devices_mspi_psram.h"
#include "am_util.h"

//*****************************************************************************
// Customize the following for the test
//*****************************************************************************
// Control the example execution
#define DISPLAY_IOM_MODULE      AM_BSP_DISPLAY_IOM
#define IOM_FREQ                AM_HAL_IOM_48MHZ
#define MSPI_FREQ               AM_HAL_MSPI_CLK_48MHZ

#define FRAME_BUF_START_OFFSET  0 // 100
// #define MSPI_PSRAM_SERIAL // Configures PSRAM in serial mode (default is quad)
// Control the size of the block of data to be transferred from PSRAM to Display
#define ROW_NUM                 240
#define COLUMN_NUM              120
#define PIXEL_BYTE              2
#define FRAME_SIZE              ROW_NUM * COLUMN_NUM * PIXEL_BYTE // Display device is limited to its resolution ratio

// #define ENABLE_LOGGING

#define CPU_SLEEP_GPIO          43      // GPIO used to track CPU in sleep on logic analyzer
#define TEST_GPIO1              33      // xip
#define TEST_GPIO2              42      // compose
#define TEST_GPIO3              44      // xipmm

// Whether to set the display render window
// This example always uses full screen, and hence this can be disabled for optimization
// Needed for Partial rendering
// #define CONFIG_DISPLAY_WINDOW

// CQ Raw feature - enables preconstructed CQ using the raw interface
// This is an optimization of the normal mode (when SEQLOOP can not be used)
// This can be used even if the FB location and size of transfer
// is not fixed (e.g. if application is using more than one scratch FB space, and also if we are doing 
// partial updates based on the display area changing
#define CQ_RAW
#define CQ_RAW_OPTIMIZED
// #define PARTIAL_RENDER_ONLY

#if defined(PARTIAL_RENDER_ONLY) && !defined(CONFIG_DISPLAY_WINDOW)
#error "CONFIG_DISPLAY_WINDOW needs to be defined when doing Partial rendering"
#endif

// Size of temporary buffer being used
// Ideally this should be as large as possible for efficiency - within the limits of MSPI and IOM max transaction sizes
// The impact on memory could be either ways - as increasing this increases the temp buf size
// however, at the same time - it reduces the memory required for command queue, as there are less
// number of transactions needed to transfer full frame buffer
// Currently limited to 1024 - which is the page size of MSPI PSRAM - as it always operates in wrap mode
// hence we're limited to bursts of size 1K
#ifndef CQ_RAW_OPTIMIZED
#define TEMP_BUFFER_SIZE        AM_DEVICES_MSPI_PSRAM_PAGE_SIZE // ((AM_HAL_IOM_MAX_TXNSIZE_SPI + 256) & 0xFFFFFF00)
// Size of SPI Transaction
#define SPI_TXN_SIZE            TEMP_BUFFER_SIZE
// Total number of SPI_TXN_SIZE fragments needs to transfer one full frame buffer
#define NUM_FRAGMENTS           ((FRAME_SIZE + SPI_TXN_SIZE - 1) / SPI_TXN_SIZE)
#else
// Size of SPI Transaction
#define SPI_TXN_SIZE            (AM_HAL_IOM_MAX_TXNSIZE_SPI & ~0xFF)
#define TEMP_BUFFER_SIZE        SPI_TXN_SIZE
// Total number of SPI_TXN_SIZE fragments needs to transfer one full frame buffer
#define NUM_FRAGMENTS           ((FRAME_SIZE + SPI_TXN_SIZE - 1) / SPI_TXN_SIZE)
#define MSPI_TXN_SIZE           AM_DEVICES_MSPI_PSRAM_48MHZ_MAX_BYTES
#define NUM_MSPI_TXN_PER_PAGE   ((AM_DEVICES_MSPI_PSRAM_PAGE_SIZE + MSPI_TXN_SIZE - 1)/MSPI_TXN_SIZE)
#define NUM_MSPI_TXN_PER_FRAG   ((SPI_TXN_SIZE + MSPI_TXN_SIZE - 1)/MSPI_TXN_SIZE)
#define NUM_MSPI_TXN_LAST_FRAG  (((FRAME_SIZE - (NUM_FRAGMENTS - 1)*SPI_TXN_SIZE) + MSPI_TXN_SIZE - 1)/MSPI_TXN_SIZE)
#endif

#define BACKGROUND_COLOR          0x80
#define BAND_COLOR                0xff
#define COLOR_MAX                 0xff

// 4 pixel worth of data
#define COLOR_4P(color)           ((color) | ((color) << 8) | ((color) << 16) | ((color) << 24))
// Depth of the color band for animation
// Currently using same temp - buffer so limiting the size accordingly
#define BAND_DEPTH                4 // (TEMP_BUFFER_SIZE/COLUMN_NUM)
#if (ROW_NUM % BAND_DEPTH)
#error "ROW_NUM needs to be an integer multiple of BAND_DEPTH for this implementation" 
#endif

// Select the Display Device
#define DISPLAY_DEVICE_RM69310     1
//*****************************************************************************
//*****************************************************************************

#if (DISPLAY_DEVICE_RM69310 == 1)
#include "am_devices_rm69310.h"
#define DISPLAY_IOM_MODE           AM_HAL_IOM_SPI_MODE
#else
#error "Unknown Display Device"
#endif

// Helper Macros to map the ISR based on the IOM being used
#define IOM_INTERRUPT1(n)       AM_HAL_INTERRUPT_IOMASTER ## n
#define IOM_INTERRUPT(n)        IOM_INTERRUPT1(n)
#define DISPLAY_IOM_IRQn        ((IRQn_Type)(IOMSTR0_IRQn + DISPLAY_IOM_MODULE))

//
// Take over the interrupt handler for whichever IOM we're using.
//
#define display_iom_isr                                                          \
    am_iom_isr1(DISPLAY_IOM_MODULE)
#define am_iom_isr1(n)                                                        \
    am_iom_isr(n)
#define am_iom_isr(n)                                                         \
    am_iomaster ## n ## _isr

// Friendlier names for the bit masks
#define AM_REG_IOM_CQFLAGS_CQFLAGS_MSPI1START   (_VAL2FLD(IOM0_CQPAUSEEN_CQPEN, IOM0_CQPAUSEEN_CQPEN_SWFLAGEN1))
#define AM_REG_IOM_CQFLAGS_CQFLAGS_MSPI0START   (_VAL2FLD(IOM0_CQPAUSEEN_CQPEN, IOM0_CQPAUSEEN_CQPEN_SWFLAGEN0))

#define IOM_SIGNAL_MSPI_BUFFER0       (AM_REG_IOM_CQFLAGS_CQFLAGS_MSPI0START << 8)
#define IOM_SIGNAL_MSPI_BUFFER1       (AM_REG_IOM_CQFLAGS_CQFLAGS_MSPI1START << 8)
#define MSPI_SIGNAL_IOM_BUFFER0       (MSPI_CQFLAGS_CQFLAGS_SWFLAG0 << 8)
#define MSPI_SIGNAL_IOM_BUFFER1       (MSPI_CQFLAGS_CQFLAGS_SWFLAG1 << 8)

#define IOM_WAIT_FOR_MSPI_BUFFER0     (_VAL2FLD(IOM0_CQPAUSEEN_CQPEN, IOM0_CQPAUSEEN_CQPEN_MSPI0XNOREN))
#define IOM_WAIT_FOR_MSPI_BUFFER1     (_VAL2FLD(IOM0_CQPAUSEEN_CQPEN, IOM0_CQPAUSEEN_CQPEN_MSPI1XNOREN))
#define MSPI_WAIT_FOR_IOM_BUFFER0     (_VAL2FLD(MSPI_CQFLAGS_CQFLAGS, MSPI_CQFLAGS_CQFLAGS_IOM0READY))
#define MSPI_WAIT_FOR_IOM_BUFFER1     (_VAL2FLD(MSPI_CQFLAGS_CQFLAGS, MSPI_CQFLAGS_CQFLAGS_IOM1READY))

#ifdef ENABLE_LOGGING
#define DEBUG_PRINT am_util_stdio_printf
#else
#define DEBUG_PRINT(...)
#endif

#define DEBUG_GPIO_HIGH(gpio)   am_hal_gpio_state_write(gpio, AM_HAL_GPIO_OUTPUT_SET)
#define DEBUG_GPIO_LOW(gpio)    am_hal_gpio_state_write(gpio, AM_HAL_GPIO_OUTPUT_CLEAR)

//
// Typedef - to encapsulate device driver functions
//
typedef struct
{
    uint8_t  devName[20];
    uint32_t (*display_init)(uint32_t ui32Module, am_hal_iom_config_t *psIOMSettings, void **ppIomHandle);
    uint32_t (*display_term)(uint32_t ui32Module);

    uint32_t (*display_blocking_write)(uint8_t *ui8TxBuffer, uint32_t ui32NumBytes);

    uint32_t (*display_blocking_cmd_write)(bool bHiPrio, uint32_t cmd, bool bContinue);

    uint32_t (*display_nonblocking_write)(uint8_t *ui8TxBuffer,
                                uint32_t ui32NumBytes,
                                bool bContinue,
                                am_hal_iom_callback_t pfnCallback,
                                void *pCallbackCtxt);

    uint32_t (*display_nonblocking_write_adv)(uint8_t *ui8TxBuffer,
                                uint32_t ui32NumBytes,
                                bool bContinue,
                                uint32_t ui32Instr,
                                uint32_t ui32InstrLen,
                                uint32_t ui32PauseCondition,
                                uint32_t ui32StatusSetClr,
                                am_hal_iom_callback_t pfnCallback,
                                void *pCallbackCtxt);

    uint32_t (*display_blocking_read)(uint8_t *pui8RxBuffer,
                            uint32_t ui32NumBytes);

    uint32_t (*display_nonblocking_read)(uint8_t *pui8RxBuffer,
                                                      uint32_t ui32NumBytes,
                                                      am_hal_iom_callback_t pfnCallback,
                                                      void *pCallbackCtxt);
    uint32_t (*display_set_transfer_window)(bool bHiPrio, uint32_t startRow, uint32_t startCol, uint32_t endRow, uint32_t endCol);
} display_device_func_t;

// Buffer for non-blocking transactions for IOM - can be much smaller as the CQ is preconstructed in a separare memory
// all the transactions
uint32_t        g_IomQBuffer[(AM_HAL_IOM_CQ_ENTRY_SIZE/4)*(2+1)];
// Buffer for non-blocking transactions for MSPI - can be much smaller as the CQ is preconstructed in a separare memory
// all the transactions
uint32_t        g_MspiQBuffer[(AM_HAL_MSPI_CQ_ENTRY_SIZE / 4) * (32 + 1)];

// Temp Buffer in SRAM to read PSRAM data to, and write DISPLAY data from
uint32_t        g_TempBuf[2][TEMP_BUFFER_SIZE / 4];

void            *g_MSPIHdl;
void            *g_IOMHandle;
volatile bool   g_bDone = false; // Render done
volatile bool   g_bReady = true; // New buffer ready for display

am_hal_mspi_dev_config_t  MSPI_PSRAM_SerialCE0MSPIConfig =
{
    .eSpiMode             = AM_HAL_MSPI_SPI_MODE_0,
    .eClockFreq           = MSPI_FREQ,
#if defined(APS6404L)
    .ui8TurnAround        = 8,
#endif
    .eAddrCfg             = AM_HAL_MSPI_ADDR_3_BYTE,
    .eInstrCfg            = AM_HAL_MSPI_INSTR_1_BYTE,
    .eDeviceConfig        = AM_HAL_MSPI_FLASH_SERIAL_CE0,
    .bSeparateIO          = true,
    .bSendInstr           = true,
    .bSendAddr            = true,
    .bTurnaround          = true,
    .ui8ReadInstr         = AM_DEVICES_MSPI_PSRAM_FAST_READ,
    .ui8WriteInstr        = AM_DEVICES_MSPI_PSRAM_WRITE,
    .ui32TCBSize          = (sizeof(g_MspiQBuffer) / sizeof(uint32_t)),
    .pTCB                 = g_MspiQBuffer,
    .scramblingStartAddr  = 0,
    .scramblingEndAddr    = 0,
};

am_hal_mspi_dev_config_t  MSPI_PSRAM_QuadCE0MSPIConfig =
{
    .eSpiMode             = AM_HAL_MSPI_SPI_MODE_0,
    .eClockFreq           = MSPI_FREQ,
#if defined(APS6404L)
    .ui8TurnAround        = 6,
#endif
    .eAddrCfg             = AM_HAL_MSPI_ADDR_3_BYTE,
    .eInstrCfg            = AM_HAL_MSPI_INSTR_1_BYTE,
    .eDeviceConfig        = AM_HAL_MSPI_FLASH_QUAD_CE0,
    .bSeparateIO          = false,
    .bSendInstr           = true,
    .bSendAddr            = true,
    .bTurnaround          = true,
    .ui8ReadInstr         = AM_DEVICES_MSPI_PSRAM_QUAD_READ,
    .ui8WriteInstr        = AM_DEVICES_MSPI_PSRAM_QUAD_WRITE,
    .ui32TCBSize          = (sizeof(g_MspiQBuffer) / sizeof(uint32_t)),
    .pTCB                 = g_MspiQBuffer,
    .scramblingStartAddr  = 0,
    .scramblingEndAddr    = 0,
};

am_hal_iom_config_t     g_sIomCfg =
{
    .eInterfaceMode       = AM_HAL_IOM_SPI_MODE,
    .ui32ClockFreq        = IOM_FREQ,
    .eSpiMode             = AM_HAL_IOM_SPI_MODE_3,
    .ui32NBTxnBufLength   = sizeof(g_IomQBuffer) / 4,
    .pNBTxnBuf            = g_IomQBuffer,
};

display_device_func_t display_func =
{
#if (DISPLAY_DEVICE_RM69310 == 1)
    // Fireball installed SPI Display device
    .devName = "SPI DISPLAY RM69310",
    .display_init = am_devices_rm69310_init,
    .display_term = am_devices_rm69310_term,
    .display_blocking_write = am_devices_rm69310_blocking_write,
    .display_blocking_cmd_write = am_devices_rm69310_command_write,
    .display_nonblocking_write = am_devices_rm69310_nonblocking_write,
    .display_nonblocking_write_adv = am_devices_rm69310_nonblocking_write_adv,
    .display_blocking_read = am_devices_rm69310_blocking_read,
    .display_nonblocking_read = am_devices_rm69310_nonblocking_read,
    .display_set_transfer_window = am_devices_rm69310_set_transfer_window,
#else
#error "Unknown Display Device"
#endif
};

//*****************************************************************************
//
// IOM ISRs.
//
//*****************************************************************************
//
//! Take over default ISR. (Queue mode service)
//
void display_iom_isr(void)
{
    uint32_t ui32Status;
    // DEBUG_GPIO_HIGH(TEST_GPIO1);
    
    // DEBUG_GPIO_LOW(TEST_GPIO1);

    if (!am_hal_iom_interrupt_status_get(g_IOMHandle, true, &ui32Status))
    {
        if ( ui32Status )
        {
            am_hal_iom_interrupt_clear(g_IOMHandle, ui32Status);
            am_hal_iom_interrupt_service(g_IOMHandle, ui32Status);
        }
    }
}

volatile bool bTEInt = false;

static void teInt_handler(void)
{
    bTEInt = true;
}

//*****************************************************************************
//
// Interrupt handler for the GPIO pins.
//
//*****************************************************************************
void am_gpio_isr(void)
{
    uint64_t ui64Status;

    //
    // Read and clear the GPIO interrupt status.
    //
    am_hal_gpio_interrupt_status_get(false, &ui64Status);
    am_hal_gpio_interrupt_clear(ui64Status);
    am_hal_gpio_interrupt_service(ui64Status);
}


int
display_init(void)
{
    uint32_t ui32Status;
    //uint32_t ui32DeviceId;

    ui32Status = display_func.display_init(DISPLAY_IOM_MODULE, &g_sIomCfg, &g_IOMHandle);
    if (0 != ui32Status)
    {
        DEBUG_PRINT("Failed to init Display device\n");
        return -1;
    }
    //
    // Enable the IOM interrupt in the NVIC.
    //
    NVIC_EnableIRQ(DISPLAY_IOM_IRQn);
    am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_DISPLAY_TE));
    am_hal_gpio_interrupt_register(AM_BSP_GPIO_DISPLAY_TE, teInt_handler);
    am_hal_gpio_interrupt_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_DISPLAY_TE));
    NVIC_EnableIRQ(GPIO_IRQn);
    return 0;
}

int
display_deinit(void)
{
    uint32_t ui32Status;

    //
    // Disable the IOM interrupt in the NVIC.
    //
    NVIC_DisableIRQ(DISPLAY_IOM_IRQn);

    ui32Status = display_func.display_term(DISPLAY_IOM_MODULE);
    if (0 != ui32Status)
    {
        DEBUG_PRINT("Failed to terminate Display device\n");
        return -1;
    }
    return 0;
}

int
mspi_psram_init(const am_hal_mspi_dev_config_t *mspiPSRAMConfig)
{
    uint32_t      ui32Status;

    //
    // Configure the MSPI and PSRAM Device.
    //
    ui32Status = am_devices_mspi_psram_init((am_hal_mspi_dev_config_t *)&MSPI_PSRAM_QuadCE0MSPIConfig, &g_MSPIHdl);
    if (AM_DEVICES_MSPI_PSRAM_STATUS_SUCCESS != ui32Status)
    {
        DEBUG_PRINT("Failed to configure the MSPI and PSRAM Device correctly!\n");
        return -1;
    }

    //
    // Make sure we aren't in XIP mode.
    //
    ui32Status = am_devices_mspi_psram_disable_xip();
    if (AM_DEVICES_MSPI_PSRAM_STATUS_SUCCESS != ui32Status)
    {
        DEBUG_PRINT("Failed to disable XIP mode in the MSPI!\n");
        return -1;
    }
    return 0;
}

uint8_t data_value = 0;
int
init_mspi_psram_data(void)
{
    uint32_t      ui32Status;
    uint32_t      address = FRAME_BUF_START_OFFSET;
    uint32_t      i;
    DEBUG_PRINT("Writing a known pattern to psram!\n");
    for (address = FRAME_BUF_START_OFFSET; address < (FRAME_BUF_START_OFFSET + FRAME_SIZE); address += (COLUMN_NUM * PIXEL_BYTE))
    {
        //
        // Generate raw color data into PSRAM frame buffer
        //
        data_value += 0x1;
        for (i = 0; i < (COLUMN_NUM * PIXEL_BYTE) / 4; i++)
        {
            g_TempBuf[0][i] = COLOR_4P(data_value);
        }

        //
        // Write the buffer into the target address in PSRAM
        //
        ui32Status = am_devices_mspi_psram_write((uint8_t *)g_TempBuf[0], address, (COLUMN_NUM * PIXEL_BYTE), true);
        if (AM_DEVICES_MSPI_PSRAM_STATUS_SUCCESS != ui32Status)
        {
            DEBUG_PRINT("Failed to write buffer to PSRAM Device!\n");
            return -1;
        }
    }
    for (; address < (FRAME_SIZE*2 + FRAME_BUF_START_OFFSET); address += (COLUMN_NUM * PIXEL_BYTE))
    {
        // uint32_t i;
        //
        // Generate raw color data into PSRAM frame buffer
        //
        data_value += 0x1;
        for (i = 0; i < (COLUMN_NUM * PIXEL_BYTE) / 4; i++)
        {
            g_TempBuf[0][i] = COLOR_4P(data_value);
        }

        //
        // Write the buffer into the target address in PSRAM
        //
        ui32Status = am_devices_mspi_psram_write((uint8_t *)g_TempBuf[0], address, (COLUMN_NUM * PIXEL_BYTE), true);
        if (AM_DEVICES_MSPI_PSRAM_STATUS_SUCCESS != ui32Status)
        {
            DEBUG_PRINT("Failed to write buffer to PSRAM Device!\n");
            return -1;
        }
    }
    return 0;
}

int
init_iom_display_data(void)
{
    uint32_t      ui32Status;

    for (uint32_t address = 0; address < FRAME_SIZE; address += TEMP_BUFFER_SIZE)
    {
        ui32Status = display_func.display_blocking_write((uint8_t *)g_TempBuf[0], TEMP_BUFFER_SIZE);
        if (0 != ui32Status)
        {
            DEBUG_PRINT("Failed to read buffer to display!\n");
            return -1;
        }
    }
    return 0;
}

void
psram_read_complete(void *pCallbackCtxt, uint32_t transactionStatus)
{
    // DEBUG_GPIO_HIGH(TEST_GPIO3);
    
    // DEBUG_GPIO_LOW(TEST_GPIO3);
    if (transactionStatus != AM_HAL_STATUS_SUCCESS)
    {
        DEBUG_PRINT("\nPSRAM Read Failed 0x%x\n", transactionStatus);
    }
    else
    {
        DEBUG_PRINT("\nPSRAM Read Done 0x%x\n", transactionStatus);
    }
}

void
display_write_complete(void *pCallbackCtxt, uint32_t transactionStatus)
{
    // DEBUG_GPIO_HIGH(TEST_GPIO3);
    
    // DEBUG_GPIO_LOW(TEST_GPIO3);
    if (transactionStatus != AM_HAL_STATUS_SUCCESS)
    {
        DEBUG_PRINT("\nDisplay Write Failed 0x%x\n", transactionStatus);
    }
    else
    {
        DEBUG_PRINT("\nDisplay Write Done 0x%x\n", transactionStatus);
        g_bDone = true;
    }
}

#ifdef CQ_RAW
#ifdef CQ_RAW_OPTIMIZED // Optimized implementation - assumes full frame size rendering always
// 2 blocks are includes in head and tail
#define MAX_INT_BLOCKS    (NUM_FRAGMENTS - 1)
// Jump - by reprogramming the CQADDR
typedef struct
{
    uint32_t    ui32CQAddrAddr;
    uint32_t    ui32CQAddrVal;
} am_hal_cq_jmp_t;

// IOM
//
// Command Queue entry structure.
//
typedef struct
{
    uint32_t    ui32PAUSENAddr;
    uint32_t    ui32PAUSEENVal;
    uint32_t    ui32PAUSEN2Addr;
    uint32_t    ui32PAUSEEN2Val;
    // Following 4 fields are only needed for first block
    uint32_t    ui32OFFSETHIAddr;
    uint32_t    ui32OFFSETHIVal;
    uint32_t    ui32DEVCFGAddr;
    uint32_t    ui32DEVCFGVal;

    uint32_t    ui32DMACFGdis1Addr;
    uint32_t    ui32DMACFGdis1Val;
    uint32_t    ui32DMATOTCOUNTAddr;
    uint32_t    ui32DMATOTCOUNTVal;
    uint32_t    ui32DMATARGADDRAddr;
    uint32_t    ui32DMATARGADDRVal;
    uint32_t    ui32DMACFGAddr;
    uint32_t    ui32DMACFGVal;
    // CMDRPT register has been repurposed for DCX
    uint32_t    ui32DCXAddr;
    uint32_t    ui32DCXVal;
//    uint32_t    ui32PAUSEN3Addr;
//    uint32_t    ui32PAUSEEN3Val;
    uint32_t    ui32CMDAddr;
    uint32_t    ui32CMDVal;

    uint32_t    ui32SETCLRAddr;
    uint32_t    ui32SETCLRVal;
} am_hal_iom_txn_head_t;

typedef struct
{
    uint32_t    ui32PAUSENAddr;
    uint32_t    ui32PAUSEENVal;
    uint32_t    ui32PAUSEN2Addr;
    uint32_t    ui32PAUSEEN2Val;

    uint32_t    ui32DMACFGdis1Addr;
    uint32_t    ui32DMACFGdis1Val;
    uint32_t    ui32DMATOTCOUNTAddr;
    uint32_t    ui32DMATOTCOUNTVal;
    uint32_t    ui32DMATARGADDRAddr;
    uint32_t    ui32DMATARGADDRVal;
    uint32_t    ui32DMACFGAddr;
    uint32_t    ui32DMACFGVal;
    // CMDRPT register has been repurposed for DCX
    uint32_t    ui32DCXAddr;
    uint32_t    ui32DCXVal;
    uint32_t    ui32CMDAddr;
    uint32_t    ui32CMDVal;

    uint32_t    ui32SETCLRAddr;
    uint32_t    ui32SETCLRVal;
} am_hal_iom_txn_seg_t;

typedef struct
{
    // Each block will be max size transfer with CONT set
    am_hal_iom_txn_seg_t block[MAX_INT_BLOCKS]; // Fixed
    am_hal_cq_jmp_t  jmp; // Jump always to am_hal_iom_long_txn_t->tail
} am_hal_iom_txn_seq_t;

typedef struct
{
    // Head
    // Max size transfer with CONT Set
    am_hal_iom_txn_head_t    head; // Programmable per transaction: ui32OFFSETHIVal, ui32HdrDMATARGADDRVal, ui32HdrCMDVal
    // Jmp
    am_hal_cq_jmp_t          jmp; // Programmable address to jump inside am_hal_iom_txn_seq_t
    // Tail
    am_hal_iom_txn_seg_t     tail; // Programmable tail length, CMD Value
    am_hal_cq_jmp_t          jmpOut; // Programmable address to jump back to the original CQ
} am_hal_iom_long_txn_t;

am_hal_iom_long_txn_t  gIomLongTxn;
am_hal_iom_txn_seq_t   gIomSequence;

uint32_t firstSegSize; 
//*****************************************************************************
//
// Function to build the CMD value.
// Returns the CMD value, but does not set the CMD register.
//
// The OFFSETHI register must still be handled by the caller, e.g.
//      AM_REGn(IOM, ui32Module, OFFSETHI) = (uint16_t)(ui32Offset >> 8);
//
//*****************************************************************************
static uint32_t
build_iom_cmd(uint32_t ui32CS,     uint32_t ui32Dir, uint32_t ui32Cont,
          uint32_t ui32Offset, uint32_t ui32OffsetCnt,
          uint32_t ui32nBytes)
{
    //
    // Initialize the CMD variable
    //
    uint32_t ui32Cmd = 0;

    //
    // If SPI, we'll need the chip select
    //
    ui32Cmd |= _VAL2FLD(IOM0_CMD_CMDSEL, ui32CS);

    //
    // Build the CMD with number of bytes and direction.
    //
    ui32Cmd |= _VAL2FLD(IOM0_CMD_TSIZE, ui32nBytes);

    if (ui32Dir == AM_HAL_IOM_RX)
    {
        ui32Cmd |= _VAL2FLD(IOM0_CMD_CMD, IOM0_CMD_CMD_READ);
    }
    else
    {
        ui32Cmd |= _VAL2FLD(IOM0_CMD_CMD, IOM0_CMD_CMD_WRITE);
    }

    ui32Cmd |= _VAL2FLD(IOM0_CMD_CONT, ui32Cont);

    //
    // Now add the OFFSETLO and OFFSETCNT information.
    //
    ui32Cmd |= _VAL2FLD(IOM0_CMD_OFFSETLO, (uint8_t)ui32Offset);
    ui32Cmd |= _VAL2FLD(IOM0_CMD_OFFSETCNT, ui32OffsetCnt);

    return ui32Cmd;
} // build_cmd()


// One time initialization
void iom_init_cq_long(uint32_t ui32Module)
{
    am_hal_iom_long_txn_t *pIomLong = &gIomLongTxn;
    // Initialize the head block
    // Initialize the tail block
    //
    // Command for OFFSETHI
    //
    pIomLong->head.ui32OFFSETHIAddr  = (uint32_t)&IOMn(ui32Module)->OFFSETHI;

    //
    // Command for I2C DEVADDR field in DEVCFG
    //
    pIomLong->head.ui32DEVCFGAddr    = (uint32_t)&IOMn(ui32Module)->DEVCFG;

    //
    // Command to disable DMA before writing TOTCOUNT.
    //
    pIomLong->tail.ui32DMACFGdis1Addr = pIomLong->head.ui32DMACFGdis1Addr   = (uint32_t)&IOMn(ui32Module)->DMACFG;
    pIomLong->tail.ui32DMACFGdis1Val = pIomLong->head.ui32DMACFGdis1Val    = 0x0;

    //
    // Command to set DMATOTALCOUNT
    //
    pIomLong->tail.ui32DMATOTCOUNTAddr = pIomLong->head.ui32DMATOTCOUNTAddr = (uint32_t)&IOMn(ui32Module)->DMATOTCOUNT;

    //
    // Command to set DMATARGADDR
    //
    pIomLong->head.ui32DMATARGADDRAddr = (uint32_t)&IOMn(ui32Module)->DMATARGADDR;
    pIomLong->tail.ui32DMATARGADDRAddr = (uint32_t)&IOMn(ui32Module)->DMATARGADDR;
    pIomLong->tail.ui32DMATARGADDRVal = (MAX_INT_BLOCKS % 2) ? (uint32_t)&g_TempBuf[0] : (uint32_t)&g_TempBuf[1];
    //
    // Command to set DMACFG to start the DMA operation
    //
    pIomLong->tail.ui32DMACFGAddr = pIomLong->head.ui32DMACFGAddr    = (uint32_t)&IOMn(ui32Module)->DMACFG;

    // CMDRPT register has been repurposed for DCX
    pIomLong->head.ui32DCXAddr = pIomLong->tail.ui32DCXAddr = (uint32_t)&IOMn(ui32Module)->DCX;
#ifdef USE_HW_DCX
    pIomLong->head.ui32DCXVal = pIomLong->tail.ui32DCXVal = IOM0_DCX_DCXEN_Msk | (0x1 << AM_BSP_DISPLAY_DCX_CE);
#else
    pIomLong->head.ui32DCXVal = pIomLong->tail.ui32DCXVal = 0;
#endif

    pIomLong->tail.ui32CMDAddr = pIomLong->head.ui32CMDAddr = (uint32_t)&IOMn(ui32Module)->CMD;

    // Initialize the jumps
    gIomSequence.jmp.ui32CQAddrAddr = pIomLong->jmpOut.ui32CQAddrAddr = pIomLong->jmp.ui32CQAddrAddr = (uint32_t)&IOMn(ui32Module)->CQADDR;
    gIomSequence.jmp.ui32CQAddrVal = (uint32_t)&pIomLong->tail;
    // Initialize the sequence blocks
    for (uint32_t i = 0; i < MAX_INT_BLOCKS; i++)
    {
        gIomSequence.block[i].ui32PAUSENAddr = gIomSequence.block[i].ui32PAUSEN2Addr = (uint32_t)&IOMn(ui32Module)->CQPAUSEEN;
        gIomSequence.block[i].ui32SETCLRAddr = (uint32_t)&IOMn(ui32Module)->CQSETCLEAR;
        //
        // Command to disable DMA before writing TOTCOUNT.
        //
        gIomSequence.block[i].ui32DMACFGdis1Addr = (uint32_t)&IOMn(ui32Module)->DMACFG;
        gIomSequence.block[i].ui32DMACFGdis1Val = 0x0;

        //
        // Command to set DMATOTALCOUNT
        //
        gIomSequence.block[i].ui32DMATOTCOUNTAddr = (uint32_t)&IOMn(ui32Module)->DMATOTCOUNT;

        //
        // Command to set DMACFG to start the DMA operation
        //
        gIomSequence.block[i].ui32DMACFGAddr = (uint32_t)&IOMn(ui32Module)->DMACFG;

        gIomSequence.block[i].ui32DCXAddr = (uint32_t)&IOMn(ui32Module)->DCX;
#ifdef USE_HW_DCX
        gIomSequence.block[i].ui32DCXVal = IOM0_DCX_DCXEN_Msk | (0x1 << AM_BSP_DISPLAY_DCX_CE);
#else
        gIomSequence.block[i].ui32DCXVal = 0;
#endif
        gIomSequence.block[i].ui32CMDAddr = (uint32_t)&IOMn(ui32Module)->CMD;
        // Pause Conditions
        // This is the Pause Boundary for HiPrio transactions
        gIomSequence.block[i].ui32PAUSEENVal = AM_HAL_IOM_CQP_PAUSE_DEFAULT | ((i % 2) ? IOM_WAIT_FOR_MSPI_BUFFER0 : IOM_WAIT_FOR_MSPI_BUFFER1);
        gIomSequence.block[i].ui32PAUSEEN2Val = AM_HAL_IOM_PAUSE_DEFAULT;
        gIomSequence.block[i].ui32SETCLRVal = (i % 2) ? IOM_SIGNAL_MSPI_BUFFER0 : IOM_SIGNAL_MSPI_BUFFER1;
        gIomSequence.block[i].ui32DMATARGADDRAddr = (uint32_t)&IOMn(ui32Module)->DMATARGADDR;
        gIomSequence.block[i].ui32DMATARGADDRVal = (i % 2) ? (uint32_t)&g_TempBuf[0] : (uint32_t)&g_TempBuf[1];
    }
    // Initialize the Pause conditions
    pIomLong->head.ui32PAUSENAddr = pIomLong->tail.ui32PAUSENAddr = (uint32_t)&IOMn(ui32Module)->CQPAUSEEN;
    pIomLong->head.ui32PAUSEN2Addr = pIomLong->tail.ui32PAUSEN2Addr = (uint32_t)&IOMn(ui32Module)->CQPAUSEEN;
    pIomLong->head.ui32SETCLRAddr = pIomLong->tail.ui32SETCLRAddr = (uint32_t)&IOMn(ui32Module)->CQSETCLEAR;

    pIomLong->tail.ui32PAUSEEN2Val = AM_HAL_IOM_PAUSE_DEFAULT;
    pIomLong->head.ui32PAUSEEN2Val = AM_HAL_IOM_PAUSE_DEFAULT;
    // This is the Pause Boundary for HiPrio transactions
    pIomLong->tail.ui32PAUSEENVal = AM_HAL_IOM_CQP_PAUSE_DEFAULT | ((MAX_INT_BLOCKS % 2) ? IOM_WAIT_FOR_MSPI_BUFFER0 : IOM_WAIT_FOR_MSPI_BUFFER1);
    pIomLong->tail.ui32SETCLRVal = (MAX_INT_BLOCKS % 2) ? IOM_SIGNAL_MSPI_BUFFER0 : IOM_SIGNAL_MSPI_BUFFER1;
}

// Initialize for each type of transacation
void iom_setup_cq_long(uint32_t ui32Module, uint8_t ui8Priority, uint32_t ui32Dir, uint32_t blockSize, uint32_t ui32SpiChipSelect, uint32_t ui32I2CDevAddr)
{
    am_hal_iom_long_txn_t *pIomLong = &gIomLongTxn;
    uint32_t ui32DMACFGVal;
    ui32DMACFGVal     =
        _VAL2FLD(IOM0_DMACFG_DMAPRI, ui8Priority)     |
        _VAL2FLD(IOM0_DMACFG_DMADIR, ui32Dir == AM_HAL_IOM_TX ? 1 : 0) |
        IOM0_DMACFG_DMAEN_Msk;

    // Command for I2C DEVADDR field in DEVCFG
    pIomLong->head.ui32DEVCFGVal     = _VAL2FLD(IOM0_DEVCFG_DEVADDR, ui32I2CDevAddr);

    //
    // Command to set DMACFG to start the DMA operation
    //
    pIomLong->tail.ui32DMACFGVal = pIomLong->head.ui32DMACFGVal = ui32DMACFGVal;

    // Initialize the sequence blocks
    for (uint32_t i = 0; i < MAX_INT_BLOCKS; i++)
    {
        uint32_t ui32Cmd;
        //
        // Command to start the transfer.
        //
        ui32Cmd = build_iom_cmd(ui32SpiChipSelect, // ChipSelect
                            ui32Dir,          // ui32Dir
                            true,           // ui32Cont
                            0,           // ui32Offset
                            0,        // ui32OffsetCnt
                            blockSize);  // ui32Bytes

        //
        // Command to set DMATOTALCOUNT
        //
        gIomSequence.block[i].ui32DMATOTCOUNTVal = blockSize;

        //
        // Command to set DMACFG to start the DMA operation
        //
        gIomSequence.block[i].ui32DMACFGVal = ui32DMACFGVal;

        // Command
        gIomSequence.block[i].ui32CMDVal = ui32Cmd;
    }
}

static void
create_iom_mspi_read_transaction(uint32_t blockSize,
                                 uint32_t ui32Dir,
                                 uint32_t ui32NumBytes,
                                 uint32_t ui32Instr,
                                 uint32_t ui32InstrLen,
                                 uint32_t ui32SpiChipSelect)
{
    uint32_t ui32Cmd;
    // initialize various fields
    // initialize gIomLongTxn
    // head: ui32OFFSETHIVal, ui32HdrDMATARGADDRVal, ui32HdrCMDVal
    gIomLongTxn.head.ui32OFFSETHIVal   = (uint16_t)(ui32Instr >> 8);

    // Assumption: AM_DEVICES_MSPI_PSRAM_PAGE_SIZE is a whole multiple of MSPI_TXN_SIZE
    uint32_t numBlocks = (ui32NumBytes + (MSPI_TXN_SIZE - firstSegSize) + blockSize - 1) / blockSize;
    

    // jmp: ui32CQAddrVal
    if (numBlocks > 1)
    {
        //
        // Command to set DMATOTALCOUNT
        //
        gIomLongTxn.head.ui32DMATOTCOUNTVal = blockSize - (MSPI_TXN_SIZE - firstSegSize);
        ui32Cmd = build_iom_cmd(ui32SpiChipSelect, // ChipSelect
                            ui32Dir,          // ui32Dir
                            true,           // ui32Cont
                            ui32Instr,           // ui32Offset
                            ui32InstrLen,        // ui32OffsetCnt
                            blockSize - (MSPI_TXN_SIZE - firstSegSize));  // ui32Bytes
        gIomLongTxn.head.ui32CMDVal   = ui32Cmd;
        if (numBlocks > 2)
        {
            // Identify where in the block array to jump to
            gIomLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gIomSequence.block[MAX_INT_BLOCKS - (numBlocks - 2)];
        }
        else
        {
            // Just jump to tail
            gIomLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gIomLongTxn.tail;
        }
        ui32NumBytes -= blockSize*(numBlocks - 1) - (MSPI_TXN_SIZE - firstSegSize);
        // tail: ui32HdrCMDVal & ui32DMATOTCOUNTVal
        // Initialize the count & command for tail
        gIomLongTxn.tail.ui32DMATOTCOUNTVal  = ui32NumBytes;
        ui32Cmd = build_iom_cmd(ui32SpiChipSelect, // ChipSelect
                            ui32Dir,          // ui32Dir
                            false,           // ui32Cont
                            0,           // ui32Offset
                            0,        // ui32OffsetCnt
                            ui32NumBytes);  // ui32Bytes
        gIomLongTxn.tail.ui32CMDVal   = ui32Cmd;
    }
    else
    {
        ui32Cmd = build_iom_cmd(ui32SpiChipSelect, // ChipSelect
                            ui32Dir,          // ui32Dir
                            false,           // ui32Cont
                            ui32Instr,           // ui32Offset
                            ui32InstrLen,        // ui32OffsetCnt
                            ui32NumBytes);  // ui32Bytes
        gIomLongTxn.head.ui32CMDVal   = ui32Cmd;
        // Just jump to end
        gIomLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gIomLongTxn.jmpOut;
        //
        // Command to set DMATOTALCOUNT
        //
        gIomLongTxn.head.ui32DMATOTCOUNTVal = ui32NumBytes;
    }

    // Initialize head - based on how many blocks
    if ((MAX_INT_BLOCKS + 2 - numBlocks) % 2)
    {
        gIomLongTxn.head.ui32DMATARGADDRVal = (uint32_t)&g_TempBuf[1];
        // This is the Pause Boundary for HiPrio transactions
        gIomLongTxn.head.ui32PAUSEENVal = AM_HAL_IOM_CQP_PAUSE_DEFAULT | IOM_WAIT_FOR_MSPI_BUFFER1;
        gIomLongTxn.head.ui32SETCLRVal = IOM_SIGNAL_MSPI_BUFFER1;
    }
    else
    {
        gIomLongTxn.head.ui32DMATARGADDRVal = (uint32_t)&g_TempBuf[0];
        // This is the Pause Boundary for HiPrio transactions
        gIomLongTxn.head.ui32PAUSEENVal = AM_HAL_IOM_CQP_PAUSE_DEFAULT | IOM_WAIT_FOR_MSPI_BUFFER0;
        gIomLongTxn.head.ui32SETCLRVal = IOM_SIGNAL_MSPI_BUFFER0;
    }
}

// MSPI
typedef struct
{
    uint32_t                    ui32PAUSENAddr;
    uint32_t                    ui32PAUSEENVal;
} mspi_break_t;

mspi_break_t mspi_break = 
{
    .ui32PAUSENAddr = (uint32_t)&MSPIn(0)->CQPAUSE,
    .ui32PAUSEENVal = MSPI_CQFLAGS_CQFLAGS_SWFLAG3,
};
//
// Command Queue entry structure.
//
typedef struct
{
    uint32_t                    ui32DMADEVADDRAddr;
    uint32_t                    ui32DMADEVADDRVal;
    uint32_t                    ui32DMATOTCOUNTAddr;
    uint32_t                    ui32DMATOTCOUNTVal;
    uint32_t                    ui32DMACFG1Addr;
    uint32_t                    ui32DMACFG1Val;
    uint32_t                    ui32DMACFG2Addr;
    uint32_t                    ui32DMACFG2Val;
} am_hal_mspi_txn_subseg_t;

typedef struct
{
    uint32_t                    ui32PAUSENAddr;
    uint32_t                    ui32PAUSEENVal;
    uint32_t                    ui32PAUSEN2Addr;
    uint32_t                    ui32PAUSEEN2Val;
    uint32_t                    ui32DMATARGADDRAddr;
    uint32_t                    ui32DMATARGADDRVal;
    am_hal_mspi_txn_subseg_t    subseg[NUM_MSPI_TXN_PER_FRAG];
    uint32_t                    ui32SETCLRAddr;
    uint32_t                    ui32SETCLRVal;
} am_hal_mspi_txn_seg_t;

typedef struct
{
    uint32_t                    ui32PAUSENAddr;
    uint32_t                    ui32PAUSEENVal;
    uint32_t                    ui32PAUSEN2Addr;
    uint32_t                    ui32PAUSEEN2Val;
    uint32_t                    ui32DMATARGADDRAddr;
    uint32_t                    ui32DMATARGADDRVal;
    am_hal_mspi_txn_subseg_t    subseg0;
    am_hal_cq_jmp_t             jmp;
    am_hal_mspi_txn_subseg_t    subseg[NUM_MSPI_TXN_PER_FRAG - 1];
    uint32_t                    ui32SETCLRAddr;
    uint32_t                    ui32SETCLRVal;
} am_hal_mspi_txn_head_t;

typedef struct
{
    uint32_t                    ui32PAUSENAddr;
    uint32_t                    ui32PAUSEENVal;
    uint32_t                    ui32PAUSEN2Addr;
    uint32_t                    ui32PAUSEEN2Val;
    uint32_t                    ui32DMATARGADDRAddr;
    uint32_t                    ui32DMATARGADDRVal;
    am_hal_cq_jmp_t             jmp; // Jump to subseg based on tail size
    am_hal_mspi_txn_subseg_t    subseg[NUM_MSPI_TXN_PER_FRAG];
    uint32_t                    ui32SETCLRAddr;
    uint32_t                    ui32SETCLRVal;
} am_hal_mspi_txn_tail_t;

typedef struct
{
    // Each block will be max size transfer with CONT set
    am_hal_mspi_txn_seg_t block[MAX_INT_BLOCKS]; // Fixed
    am_hal_cq_jmp_t       jmp; // Jump always to am_hal_iom_long_txn_t->tail
} am_hal_mspi_txn_seq_t;


typedef struct
{
    // Head
    // Max size transfer with CONT Set
    am_hal_mspi_txn_head_t   head; // Programmable per transaction: ui32OFFSETHIVal, ui32HdrDMATARGADDRVal, ui32HdrCMDVal
    // Jmp
    am_hal_cq_jmp_t          jmp; // Programmable address to jump inside am_hal_iom_txn_seq_t
    // Tail
    am_hal_mspi_txn_tail_t   tail; // Programmable tail length, CMD Value
    am_hal_cq_jmp_t          jmpOut; // Programmable address to jump back to the original CQ
} am_hal_mspi_long_txn_t;

am_hal_mspi_long_txn_t gMspiLongTxn;
am_hal_mspi_txn_seq_t  gMspiSequence;

// MSPI
// One time initialization
void mspi_init_cq_long(uint32_t ui32Module)
{
    uint32_t i, j;

    // Initialize head
    gMspiLongTxn.head.ui32PAUSENAddr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    gMspiLongTxn.head.ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    gMspiLongTxn.head.ui32DMATARGADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMATARGADDR;
    gMspiLongTxn.head.ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
    gMspiLongTxn.head.ui32PAUSEEN2Val = AM_HAL_MSPI_PAUSE_DEFAULT;

    // First segment
    gMspiLongTxn.head.subseg0.ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;
    gMspiLongTxn.head.subseg0.ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
    gMspiLongTxn.head.subseg0.ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
    gMspiLongTxn.head.subseg0.ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
    gMspiLongTxn.head.subseg0.ui32DMACFG2Val = _VAL2FLD(MSPI_DMACFG_DMAEN, 0);

    // remaining segments
    for (j = 0; j < NUM_MSPI_TXN_PER_FRAG - 1; j++)
    {
        gMspiLongTxn.head.subseg[j].ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;
        gMspiLongTxn.head.subseg[j].ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
        gMspiLongTxn.head.subseg[j].ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
        gMspiLongTxn.head.subseg[j].ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
        gMspiLongTxn.head.subseg[j].ui32DMACFG2Val = _VAL2FLD(MSPI_DMACFG_DMAEN, 0);
        gMspiLongTxn.head.subseg[j].ui32DMATOTCOUNTVal = MSPI_TXN_SIZE;
    }
    // Initialize the sequence blocks
    for (i = 0; i < (MAX_INT_BLOCKS); i++)
    {
        gMspiSequence.block[i].ui32PAUSENAddr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
        gMspiSequence.block[i].ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
        gMspiSequence.block[i].ui32DMATARGADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMATARGADDR;
        gMspiSequence.block[i].ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
        gMspiSequence.block[i].ui32DMATARGADDRVal = (i % 2) ? (uint32_t)&g_TempBuf[0] : (uint32_t)&g_TempBuf[1];

        // segments
        for (j = 0; j < NUM_MSPI_TXN_PER_FRAG; j++)
        {
            gMspiSequence.block[i].subseg[j].ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;
            gMspiSequence.block[i].subseg[j].ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
            gMspiSequence.block[i].subseg[j].ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
            gMspiSequence.block[i].subseg[j].ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
            gMspiSequence.block[i].subseg[j].ui32DMACFG2Val = _VAL2FLD(MSPI_DMACFG_DMAEN, 0);
            gMspiSequence.block[i].subseg[j].ui32DMATOTCOUNTVal = MSPI_TXN_SIZE;
        }
        // Pause Conditions
        // This is the Pause Boundary for HiPrio transactions
        gMspiSequence.block[i].ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | ((i % 2) ? MSPI_WAIT_FOR_IOM_BUFFER0 : MSPI_WAIT_FOR_IOM_BUFFER1);
        gMspiSequence.block[i].ui32PAUSEEN2Val = AM_HAL_MSPI_PAUSE_DEFAULT;
        gMspiSequence.block[i].ui32SETCLRVal = (i % 2) ? MSPI_SIGNAL_IOM_BUFFER0 : MSPI_SIGNAL_IOM_BUFFER1;
    }

    // Initialize tail
    gMspiLongTxn.tail.ui32PAUSENAddr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    gMspiLongTxn.tail.ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    gMspiLongTxn.tail.ui32DMATARGADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMATARGADDR;
    gMspiLongTxn.tail.ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
    gMspiLongTxn.tail.ui32DMATARGADDRVal = (i % 2) ? (uint32_t)&g_TempBuf[0] : (uint32_t)&g_TempBuf[1];

    // segments
    for (j = 0; j < NUM_MSPI_TXN_PER_FRAG; j++)
    {
        gMspiLongTxn.tail.subseg[j].ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;
        gMspiLongTxn.tail.subseg[j].ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
        gMspiLongTxn.tail.subseg[j].ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
        gMspiLongTxn.tail.subseg[j].ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
        gMspiLongTxn.tail.subseg[j].ui32DMACFG2Val = _VAL2FLD(MSPI_DMACFG_DMAEN, 0);
        gMspiLongTxn.tail.subseg[j].ui32DMATOTCOUNTVal = MSPI_TXN_SIZE;
    }
    // Pause Conditions
    // This is the Pause Boundary for HiPrio transactions
    gMspiLongTxn.tail.ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | ((i % 2) ? MSPI_WAIT_FOR_IOM_BUFFER0 : MSPI_WAIT_FOR_IOM_BUFFER1);
    gMspiLongTxn.tail.ui32PAUSEEN2Val = AM_HAL_MSPI_PAUSE_DEFAULT;
    gMspiLongTxn.tail.ui32SETCLRVal = (i % 2) ? MSPI_SIGNAL_IOM_BUFFER0 : MSPI_SIGNAL_IOM_BUFFER1;

    // Initialize jump addresses
    gMspiLongTxn.jmp.ui32CQAddrAddr = (uint32_t)&MSPIn(ui32Module)->CQADDR;
    gMspiLongTxn.head.jmp.ui32CQAddrAddr = (uint32_t)&MSPIn(ui32Module)->CQADDR;
    gMspiLongTxn.tail.jmp.ui32CQAddrAddr = (uint32_t)&MSPIn(ui32Module)->CQADDR;
    gMspiLongTxn.jmpOut.ui32CQAddrAddr = (uint32_t)&MSPIn(ui32Module)->CQADDR;
    // Blocks always jump to tail
    gMspiSequence.jmp.ui32CQAddrAddr = (uint32_t)&MSPIn(ui32Module)->CQADDR;
    gMspiSequence.jmp.ui32CQAddrVal = (uint32_t)&gMspiLongTxn.tail;
}

// Initialize for each type of transacation
void mspi_setup_cq_long(uint32_t ui32Module, uint8_t ui8Priority, am_hal_mspi_dir_e eDirection, uint32_t blockSize)
{
    uint32_t i, j;
    uint32_t ui32DmaCfg =
        _VAL2FLD(MSPI_DMACFG_DMAPWROFF, 0)   |  // DMA Auto Power-off not supported!
        _VAL2FLD(MSPI_DMACFG_DMAPRI, ui8Priority)    |
        _VAL2FLD(MSPI_DMACFG_DMADIR, eDirection)     |
        _VAL2FLD(MSPI_DMACFG_DMAEN, 3);

    for (i = 0; i < (MAX_INT_BLOCKS); i++)
    {
        gMspiSequence.block[i].subseg[NUM_MSPI_TXN_PER_FRAG - 1].ui32DMATOTCOUNTVal = blockSize - (NUM_MSPI_TXN_PER_FRAG-1)*MSPI_TXN_SIZE;
        for (j = 0; j < (NUM_MSPI_TXN_PER_FRAG); j++)
        {
            gMspiSequence.block[i].subseg[j].ui32DMACFG1Val = ui32DmaCfg;
        }
    }
    gMspiLongTxn.head.subseg0.ui32DMACFG1Val = ui32DmaCfg;
    for (j = 0; j < (NUM_MSPI_TXN_PER_FRAG - 1); j++)
    {
        gMspiLongTxn.head.subseg[j].ui32DMACFG1Val = ui32DmaCfg;
    }
    gMspiLongTxn.head.subseg[NUM_MSPI_TXN_PER_FRAG - 2].ui32DMATOTCOUNTVal = blockSize - (NUM_MSPI_TXN_PER_FRAG-1)*MSPI_TXN_SIZE;
    for (j = 0; j < (NUM_MSPI_TXN_PER_FRAG); j++)
    {
        gMspiLongTxn.tail.subseg[j].ui32DMACFG1Val = ui32DmaCfg;
    }
}

static void
create_mspi_iom_write_transaction(uint32_t blockSize,
                                  am_hal_mspi_dir_e eDirection,
                                  uint32_t ui32DevAddr,
                                  uint32_t ui32NumBytes)
{
    uint32_t j;
    uint32_t numBlocks;
    firstSegSize = (ui32DevAddr & ~(MSPI_TXN_SIZE - 1)) + MSPI_TXN_SIZE - ui32DevAddr; 
    if (firstSegSize == 0)
    {
        firstSegSize = MSPI_TXN_SIZE;
    }
    numBlocks = (ui32NumBytes + MSPI_TXN_SIZE - firstSegSize + blockSize - 1) / blockSize;
    

    // Initialize the size of first header segment
    gMspiLongTxn.head.subseg0.ui32DMADEVADDRVal = ui32DevAddr;
    gMspiLongTxn.head.subseg0.ui32DMATOTCOUNTVal = firstSegSize;
    ui32DevAddr += firstSegSize;

    // Initialize the buffer - 0 or 1, for header
    if ((MAX_INT_BLOCKS + 2 - numBlocks) % 2)
    {
        gMspiLongTxn.head.ui32DMATARGADDRVal = (uint32_t)&g_TempBuf[1];
        // This is the Pause Boundary for HiPrio transactions
        gMspiLongTxn.head.ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | MSPI_WAIT_FOR_IOM_BUFFER1;
        gMspiLongTxn.head.ui32SETCLRVal = MSPI_SIGNAL_IOM_BUFFER1;
    }
    else
    {
        gMspiLongTxn.head.ui32DMATARGADDRVal = (uint32_t)&g_TempBuf[0];
        // This is the Pause Boundary for HiPrio transactions
        gMspiLongTxn.head.ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | MSPI_WAIT_FOR_IOM_BUFFER0;
        gMspiLongTxn.head.ui32SETCLRVal = MSPI_SIGNAL_IOM_BUFFER0;
    }
    
    // Initialize all addresses - header, blocks and tail
    // Initialize the jump after header to go to the correct block
    if (numBlocks > 1)
    {
        gMspiLongTxn.head.jmp.ui32CQAddrVal = (uint32_t)&gMspiLongTxn.head.subseg[0];
        for (j = 0; j < (NUM_MSPI_TXN_PER_FRAG - 1); j++)
        {
            gMspiLongTxn.head.subseg[j].ui32DMADEVADDRVal = ui32DevAddr;
            ui32DevAddr += gMspiLongTxn.head.subseg[j].ui32DMATOTCOUNTVal;
        }
        if (numBlocks > 2)
        {
            // Identify where in the block array to jump to
            gMspiLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gMspiSequence.block[MAX_INT_BLOCKS - (numBlocks - 2)];
            for (uint32_t i = 0; i < (numBlocks - 2); i++)
            {
                for (j = 0; j < (NUM_MSPI_TXN_PER_FRAG); j++)
                {
                    gMspiSequence.block[MAX_INT_BLOCKS - (numBlocks - 2) + i].subseg[j].ui32DMADEVADDRVal = ui32DevAddr;
                    ui32DevAddr += gMspiSequence.block[MAX_INT_BLOCKS - (numBlocks - 2) + i].subseg[j].ui32DMATOTCOUNTVal;
                }
            }
        }
        else
        {
            // Just jump to tail
            gMspiLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gMspiLongTxn.tail;
        }
        ui32NumBytes -= blockSize*(numBlocks - 1) - (MSPI_TXN_SIZE - firstSegSize);

    }
    else
    {
        // Just jump to tail
        gMspiLongTxn.head.jmp.ui32CQAddrVal = (uint32_t)&gMspiLongTxn.tail.jmp;
        if (ui32NumBytes < firstSegSize)
        {
            // size shorter than one MSPI TXN size currently not supported in code
            while(1);
        }
        else
        {
            ui32NumBytes -= firstSegSize;
        }
    }
    
    // Initialize tail
    uint32_t ui32NumSegs = (ui32NumBytes + MSPI_TXN_SIZE - 1)/MSPI_TXN_SIZE;
    // Identify where in the segment array to jump to
    gMspiLongTxn.tail.jmp.ui32CQAddrVal = (uint32_t)&gMspiLongTxn.tail.subseg[NUM_MSPI_TXN_PER_FRAG - ui32NumSegs];
    for (j = NUM_MSPI_TXN_PER_FRAG - ui32NumSegs; j < (NUM_MSPI_TXN_PER_FRAG - 1); j++)
    {
        gMspiLongTxn.tail.subseg[j].ui32DMADEVADDRVal = ui32DevAddr;
        ui32DevAddr += MSPI_TXN_SIZE;
        ui32NumBytes -= MSPI_TXN_SIZE;
    }
    
    // Initialize the size of last tail segment
    gMspiLongTxn.tail.subseg[j].ui32DMADEVADDRVal = ui32DevAddr;
    gMspiLongTxn.tail.subseg[j].ui32DMATOTCOUNTVal = ui32NumBytes;
}
#else
// 2 blocks are includes in head and tail
#define MAX_INT_BLOCKS    (NUM_FRAGMENTS - 2)
// Jump - by reprogramming the CQADDR
typedef struct
{
    uint32_t    ui32CQAddrAddr;
    uint32_t    ui32CQAddrVal;
} am_hal_cq_jmp_t;

// IOM
//
// Command Queue entry structure.
//
typedef struct
{
    uint32_t    ui32PAUSENAddr;
    uint32_t    ui32PAUSEENVal;
    uint32_t    ui32PAUSEN2Addr;
    uint32_t    ui32PAUSEEN2Val;
    // Following 4 fields are only needed for first block
    uint32_t    ui32OFFSETHIAddr;
    uint32_t    ui32OFFSETHIVal;
    uint32_t    ui32DEVCFGAddr;
    uint32_t    ui32DEVCFGVal;

    uint32_t    ui32DMACFGdis1Addr;
    uint32_t    ui32DMACFGdis1Val;
    uint32_t    ui32DMATOTCOUNTAddr;
    uint32_t    ui32DMATOTCOUNTVal;
    uint32_t    ui32DMATARGADDRAddr;
    uint32_t    ui32DMATARGADDRVal;
    uint32_t    ui32DMACFGAddr;
    uint32_t    ui32DMACFGVal;
    // CMDRPT register has been repurposed for DCX
    uint32_t    ui32DCXAddr;
    uint32_t    ui32DCXVal;
//    uint32_t    ui32PAUSEN3Addr;
//    uint32_t    ui32PAUSEEN3Val;
    uint32_t    ui32CMDAddr;
    uint32_t    ui32CMDVal;

    uint32_t    ui32SETCLRAddr;
    uint32_t    ui32SETCLRVal;
} am_hal_iom_txn_head_t;

typedef struct
{
    uint32_t    ui32PAUSENAddr;
    uint32_t    ui32PAUSEENVal;
    uint32_t    ui32PAUSEN2Addr;
    uint32_t    ui32PAUSEEN2Val;

    uint32_t    ui32DMACFGdis1Addr;
    uint32_t    ui32DMACFGdis1Val;
    uint32_t    ui32DMATOTCOUNTAddr;
    uint32_t    ui32DMATOTCOUNTVal;
    uint32_t    ui32DMATARGADDRAddr;
    uint32_t    ui32DMATARGADDRVal;
    uint32_t    ui32DMACFGAddr;
    uint32_t    ui32DMACFGVal;
    // CMDRPT register has been repurposed for DCX
    uint32_t    ui32DCXAddr;
    uint32_t    ui32DCXVal;
    uint32_t    ui32CMDAddr;
    uint32_t    ui32CMDVal;

    uint32_t    ui32SETCLRAddr;
    uint32_t    ui32SETCLRVal;
} am_hal_iom_txn_seg_t;

typedef struct
{
    // Each block will be max size transfer with CONT set
    am_hal_iom_txn_seg_t block[MAX_INT_BLOCKS]; // Fixed
    am_hal_cq_jmp_t  jmp; // Jump always to am_hal_iom_long_txn_t->tail
} am_hal_iom_txn_seq_t;

typedef struct
{
    // Head
    // Max size transfer with CONT Set
    am_hal_iom_txn_head_t    head; // Programmable per transaction: ui32OFFSETHIVal, ui32HdrDMATARGADDRVal, ui32HdrCMDVal
    // Jmp
    am_hal_cq_jmp_t          jmp; // Programmable address to jump inside am_hal_iom_txn_seq_t
    // Tail
    am_hal_iom_txn_seg_t     tail; // Programmable tail length, CMD Value
    am_hal_cq_jmp_t          jmpOut; // Programmable address to jump back to the original CQ
} am_hal_iom_long_txn_t;

am_hal_iom_long_txn_t  gIomLongTxn;
am_hal_iom_txn_seq_t   gIomSequence;

//*****************************************************************************
//
// Function to build the CMD value.
// Returns the CMD value, but does not set the CMD register.
//
// The OFFSETHI register must still be handled by the caller, e.g.
//      AM_REGn(IOM, ui32Module, OFFSETHI) = (uint16_t)(ui32Offset >> 8);
//
//*****************************************************************************
static uint32_t
build_iom_cmd(uint32_t ui32CS,     uint32_t ui32Dir, uint32_t ui32Cont,
              uint32_t ui32Offset, uint32_t ui32OffsetCnt,
              uint32_t ui32nBytes)
{
    //
    // Initialize the CMD variable
    //
    uint32_t ui32Cmd = 0;

    //
    // If SPI, we'll need the chip select
    //
    ui32Cmd |= _VAL2FLD(IOM0_CMD_CMDSEL, ui32CS);

    //
    // Build the CMD with number of bytes and direction.
    //
    ui32Cmd |= _VAL2FLD(IOM0_CMD_TSIZE, ui32nBytes);

    if (ui32Dir == AM_HAL_IOM_RX)
    {
        ui32Cmd |= _VAL2FLD(IOM0_CMD_CMD, IOM0_CMD_CMD_READ);
    }
    else
    {
        ui32Cmd |= _VAL2FLD(IOM0_CMD_CMD, IOM0_CMD_CMD_WRITE);
    }

    ui32Cmd |= _VAL2FLD(IOM0_CMD_CONT, ui32Cont);

    //
    // Now add the OFFSETLO and OFFSETCNT information.
    //
    ui32Cmd |= _VAL2FLD(IOM0_CMD_OFFSETLO, (uint8_t)ui32Offset);
    ui32Cmd |= _VAL2FLD(IOM0_CMD_OFFSETCNT, ui32OffsetCnt);

    return ui32Cmd;
} // build_cmd()


// One time initialization
void iom_init_cq_long(uint32_t ui32Module)
{
    am_hal_iom_long_txn_t *pIomLong = &gIomLongTxn;
    // Initialize the head block
    // Initialize the tail block
    //
    // Command for OFFSETHI
    //
    pIomLong->head.ui32OFFSETHIAddr  = (uint32_t)&IOMn(ui32Module)->OFFSETHI;

    //
    // Command for I2C DEVADDR field in DEVCFG
    //
    pIomLong->head.ui32DEVCFGAddr    = (uint32_t)&IOMn(ui32Module)->DEVCFG;

    //
    // Command to disable DMA before writing TOTCOUNT.
    //
    pIomLong->tail.ui32DMACFGdis1Addr = pIomLong->head.ui32DMACFGdis1Addr   = (uint32_t)&IOMn(ui32Module)->DMACFG;
    pIomLong->tail.ui32DMACFGdis1Val = pIomLong->head.ui32DMACFGdis1Val    = 0x0;

    //
    // Command to set DMATOTALCOUNT
    //
    pIomLong->tail.ui32DMATOTCOUNTAddr = pIomLong->head.ui32DMATOTCOUNTAddr = (uint32_t)&IOMn(ui32Module)->DMATOTCOUNT;

    //
    // Command to set DMATARGADDR
    //
    pIomLong->head.ui32DMATARGADDRAddr = (uint32_t)&IOMn(ui32Module)->DMATARGADDR;
    pIomLong->tail.ui32DMATARGADDRAddr = (uint32_t)&IOMn(ui32Module)->DMATARGADDR;
    pIomLong->tail.ui32DMATARGADDRVal = (MAX_INT_BLOCKS % 2) ? (uint32_t)&g_TempBuf[0] : (uint32_t)&g_TempBuf[1];
    //
    // Command to set DMACFG to start the DMA operation
    //
    pIomLong->tail.ui32DMACFGAddr = pIomLong->head.ui32DMACFGAddr    = (uint32_t)&IOMn(ui32Module)->DMACFG;

    // CMDRPT register has been repurposed for DCX
    pIomLong->head.ui32DCXAddr = pIomLong->tail.ui32DCXAddr = (uint32_t)&IOMn(ui32Module)->DCX;
#ifdef USE_HW_DCX
    pIomLong->head.ui32DCXVal = pIomLong->tail.ui32DCXVal = IOM0_DCX_DCXEN_Msk | (0x1 << AM_BSP_DISPLAY_DCX_CE);
#else
    pIomLong->head.ui32DCXVal = pIomLong->tail.ui32DCXVal = 0;
#endif

    pIomLong->tail.ui32CMDAddr = pIomLong->head.ui32CMDAddr = (uint32_t)&IOMn(ui32Module)->CMD;

    // Initialize the jumps
    gIomSequence.jmp.ui32CQAddrAddr = pIomLong->jmpOut.ui32CQAddrAddr = pIomLong->jmp.ui32CQAddrAddr = (uint32_t)&IOMn(ui32Module)->CQADDR;
    gIomSequence.jmp.ui32CQAddrVal = (uint32_t)&pIomLong->tail;
    // Initialize the sequence blocks
    for (uint32_t i = 0; i < MAX_INT_BLOCKS; i++)
    {
        gIomSequence.block[i].ui32PAUSENAddr = gIomSequence.block[i].ui32PAUSEN2Addr = (uint32_t)&IOMn(ui32Module)->CQPAUSEEN;
        gIomSequence.block[i].ui32SETCLRAddr = (uint32_t)&IOMn(ui32Module)->CQSETCLEAR;
        //
        // Command to disable DMA before writing TOTCOUNT.
        //
        gIomSequence.block[i].ui32DMACFGdis1Addr = (uint32_t)&IOMn(ui32Module)->DMACFG;
        gIomSequence.block[i].ui32DMACFGdis1Val = 0x0;

        //
        // Command to set DMATOTALCOUNT
        //
        gIomSequence.block[i].ui32DMATOTCOUNTAddr = (uint32_t)&IOMn(ui32Module)->DMATOTCOUNT;

        //
        // Command to set DMACFG to start the DMA operation
        //
        gIomSequence.block[i].ui32DMACFGAddr = (uint32_t)&IOMn(ui32Module)->DMACFG;

        gIomSequence.block[i].ui32DCXAddr = (uint32_t)&IOMn(ui32Module)->DCX;
#ifdef USE_HW_DCX
        gIomSequence.block[i].ui32DCXVal = IOM0_DCX_DCXEN_Msk | (0x1 << AM_BSP_DISPLAY_DCX_CE);
#else
        gIomSequence.block[i].ui32DCXVal = 0;
#endif
        gIomSequence.block[i].ui32CMDAddr = (uint32_t)&IOMn(ui32Module)->CMD;
        // Pause Conditions
        // This is the Pause Boundary for HiPrio transactions
        gIomSequence.block[i].ui32PAUSEENVal = AM_HAL_IOM_CQP_PAUSE_DEFAULT | ((i % 2) ? IOM_WAIT_FOR_MSPI_BUFFER0 : IOM_WAIT_FOR_MSPI_BUFFER1);
        gIomSequence.block[i].ui32PAUSEEN2Val = AM_HAL_IOM_PAUSE_DEFAULT;
        gIomSequence.block[i].ui32SETCLRVal = (i % 2) ? IOM_SIGNAL_MSPI_BUFFER0 : IOM_SIGNAL_MSPI_BUFFER1;
        gIomSequence.block[i].ui32DMATARGADDRAddr = (uint32_t)&IOMn(ui32Module)->DMATARGADDR;
        gIomSequence.block[i].ui32DMATARGADDRVal = (i % 2) ? (uint32_t)&g_TempBuf[0] : (uint32_t)&g_TempBuf[1];
    }
    // Initialize the Pause conditions
    pIomLong->head.ui32PAUSENAddr = pIomLong->tail.ui32PAUSENAddr = (uint32_t)&IOMn(ui32Module)->CQPAUSEEN;
    pIomLong->head.ui32PAUSEN2Addr = pIomLong->tail.ui32PAUSEN2Addr = (uint32_t)&IOMn(ui32Module)->CQPAUSEEN;
    pIomLong->head.ui32SETCLRAddr = pIomLong->tail.ui32SETCLRAddr = (uint32_t)&IOMn(ui32Module)->CQSETCLEAR;

    pIomLong->tail.ui32PAUSEEN2Val = AM_HAL_IOM_PAUSE_DEFAULT;
    pIomLong->head.ui32PAUSEEN2Val = AM_HAL_IOM_PAUSE_DEFAULT;
    // This is the Pause Boundary for HiPrio transactions
    pIomLong->tail.ui32PAUSEENVal = AM_HAL_IOM_CQP_PAUSE_DEFAULT | ((MAX_INT_BLOCKS % 2) ? IOM_WAIT_FOR_MSPI_BUFFER0 : IOM_WAIT_FOR_MSPI_BUFFER1);
    pIomLong->tail.ui32SETCLRVal = (MAX_INT_BLOCKS % 2) ? IOM_SIGNAL_MSPI_BUFFER0 : IOM_SIGNAL_MSPI_BUFFER1;
}

// Initialize for each type of transacation
void iom_setup_cq_long(uint32_t ui32Module, uint8_t ui8Priority, uint32_t ui32Dir, uint32_t blockSize, uint32_t ui32SpiChipSelect, uint32_t ui32I2CDevAddr)
{
    am_hal_iom_long_txn_t *pIomLong = &gIomLongTxn;
    uint32_t ui32DMACFGVal;
    ui32DMACFGVal     =
        _VAL2FLD(IOM0_DMACFG_DMAPRI, ui8Priority)     |
        _VAL2FLD(IOM0_DMACFG_DMADIR, ui32Dir == AM_HAL_IOM_TX ? 1 : 0) |
        IOM0_DMACFG_DMAEN_Msk;

    // Command for I2C DEVADDR field in DEVCFG
    pIomLong->head.ui32DEVCFGVal     = _VAL2FLD(IOM0_DEVCFG_DEVADDR, ui32I2CDevAddr);
    //
    // Command to set DMATOTALCOUNT
    //
    pIomLong->head.ui32DMATOTCOUNTVal = blockSize;

    //
    // Command to set DMACFG to start the DMA operation
    //
    pIomLong->tail.ui32DMACFGVal = pIomLong->head.ui32DMACFGVal = ui32DMACFGVal;

    // Initialize the sequence blocks
    for (uint32_t i = 0; i < MAX_INT_BLOCKS; i++)
    {
        uint32_t ui32Cmd;
        //
        // Command to start the transfer.
        //
        ui32Cmd = build_iom_cmd(ui32SpiChipSelect, // ChipSelect
                            ui32Dir,          // ui32Dir
                            true,           // ui32Cont
                            0,           // ui32Offset
                            0,        // ui32OffsetCnt
                            blockSize);  // ui32Bytes

        //
        // Command to set DMATOTALCOUNT
        //
        gIomSequence.block[i].ui32DMATOTCOUNTVal = blockSize;

        //
        // Command to set DMACFG to start the DMA operation
        //
        gIomSequence.block[i].ui32DMACFGVal = ui32DMACFGVal;

        // Command
        gIomSequence.block[i].ui32CMDVal = ui32Cmd;
    }
}

static void
create_iom_mspi_read_transaction(uint32_t blockSize,
                                 uint32_t ui32Dir,
                                 uint32_t ui32NumBytes,
                                 uint32_t ui32Instr,
                                 uint32_t ui32InstrLen,
                                 uint32_t ui32SpiChipSelect)
{
    uint32_t ui32Cmd;
    // initialize various fields
    // initialize gIomLongTxn
    // head: ui32OFFSETHIVal, ui32HdrDMATARGADDRVal, ui32HdrCMDVal
    gIomLongTxn.head.ui32OFFSETHIVal   = (uint16_t)(ui32Instr >> 8);

    uint32_t numBlocks = (ui32NumBytes + blockSize - 1) / blockSize;

    // jmp: ui32CQAddrVal
    if (numBlocks > 1)
    {
        ui32Cmd = build_iom_cmd(ui32SpiChipSelect, // ChipSelect
                            ui32Dir,          // ui32Dir
                            true,           // ui32Cont
                            ui32Instr,           // ui32Offset
                            ui32InstrLen,        // ui32OffsetCnt
                            blockSize);  // ui32Bytes
        gIomLongTxn.head.ui32CMDVal   = ui32Cmd;
        if (numBlocks > 2)
        {
            // Identify where in the block array to jump to
            gIomLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gIomSequence.block[MAX_INT_BLOCKS - (numBlocks - 2)];
        }
        else
        {
            // Just jump to tail
            gIomLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gIomLongTxn.tail;
        }
        ui32NumBytes -= blockSize*(numBlocks - 1);
        // tail: ui32HdrCMDVal & ui32DMATOTCOUNTVal
        // Initialize the count & command for tail
        gIomLongTxn.tail.ui32DMATOTCOUNTVal  = ui32NumBytes;
        ui32Cmd = build_iom_cmd(ui32SpiChipSelect, // ChipSelect
                            ui32Dir,          // ui32Dir
                            false,           // ui32Cont
                            0,           // ui32Offset
                            0,        // ui32OffsetCnt
                            ui32NumBytes);  // ui32Bytes
        gIomLongTxn.tail.ui32CMDVal   = ui32Cmd;
    }
    else
    {
        ui32Cmd = build_iom_cmd(ui32SpiChipSelect, // ChipSelect
                            ui32Dir,          // ui32Dir
                            false,           // ui32Cont
                            ui32Instr,           // ui32Offset
                            ui32InstrLen,        // ui32OffsetCnt
                            ui32NumBytes);  // ui32Bytes
        gIomLongTxn.head.ui32CMDVal   = ui32Cmd;
        // Just jump to end
        gIomLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gIomLongTxn.jmpOut;
    }

    // Initialize head - based on how many blocks
    if ((MAX_INT_BLOCKS + 2 - numBlocks) % 2)
    {
        gIomLongTxn.head.ui32DMATARGADDRVal = (uint32_t)&g_TempBuf[1];
        // This is the Pause Boundary for HiPrio transactions
        gIomLongTxn.head.ui32PAUSEENVal = AM_HAL_IOM_CQP_PAUSE_DEFAULT | IOM_WAIT_FOR_MSPI_BUFFER1;
        gIomLongTxn.head.ui32SETCLRVal = IOM_SIGNAL_MSPI_BUFFER1;
    }
    else
    {
        gIomLongTxn.head.ui32DMATARGADDRVal = (uint32_t)&g_TempBuf[0];
        // This is the Pause Boundary for HiPrio transactions
        gIomLongTxn.head.ui32PAUSEENVal = AM_HAL_IOM_CQP_PAUSE_DEFAULT | IOM_WAIT_FOR_MSPI_BUFFER0;
        gIomLongTxn.head.ui32SETCLRVal = IOM_SIGNAL_MSPI_BUFFER0;
    }
}

#if defined(CQ_RAW_OPTIMIZED2)
// MSPI
//
// Command Queue entry structure.
//
typedef struct
{
    uint32_t                    ui32DMADEVADDRAddr;
    uint32_t                    ui32DMADEVADDRVal;
    uint32_t                    ui32DMATOTCOUNTAddr;
    uint32_t                    ui32DMATOTCOUNTVal;
    uint32_t                    ui32DMACFG1Addr;
    uint32_t                    ui32DMACFG1Val;
    uint32_t                    ui32DMACFG2Addr;
    uint32_t                    ui32DMACFG2Val;
} am_hal_mspi_txn_subseg_t;

typedef struct
{
    uint32_t                    ui32PAUSENAddr;
    uint32_t                    ui32PAUSEENVal;
    uint32_t                    ui32PAUSEN2Addr;
    uint32_t                    ui32PAUSEEN2Val;
    uint32_t                    ui32DMATARGADDRAddr;
    uint32_t                    ui32DMATARGADDRVal;
    am_hal_mspi_txn_subseg_t    subseg[NUM_MSPI_TXN_PER_FRAG];
    uint32_t                    ui32SETCLRAddr;
    uint32_t                    ui32SETCLRVal;
} am_hal_mspi_txn_seg_t;

typedef struct
{
    uint32_t                    ui32PAUSENAddr;
    uint32_t                    ui32PAUSEENVal;
    uint32_t                    ui32PAUSEN2Addr;
    uint32_t                    ui32PAUSEEN2Val;
    uint32_t                    ui32DMATARGADDRAddr;
    uint32_t                    ui32DMATARGADDRVal;
    am_hal_mspi_txn_subseg_t    subseg[NUM_MSPI_TXN_LAST_FRAG];
    uint32_t                    ui32SETCLRAddr;
    uint32_t                    ui32SETCLRVal;
} am_hal_mspi_txn_tail_t;

typedef struct
{
    am_hal_mspi_txn_seg_t    block[MAX_INT_BLOCKS + 1];
    am_hal_mspi_txn_tail_t   tail;
    am_hal_cq_jmp_t      jmpOut; // Programmable address to jump back to the original CQ
} am_hal_mspi_long_txn_t;

am_hal_mspi_long_txn_t gMspiLongTxn;

// MSPI
// One time initialization
void mspi_init_cq_long(uint32_t ui32Module)
{
    uint32_t i, j;
    // Initialize the sequence blocks
    for (i = 0; i < (MAX_INT_BLOCKS + 1); i++)
    {
        gMspiLongTxn.block[i].ui32PAUSENAddr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
        gMspiLongTxn.block[i].ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
        gMspiLongTxn.block[i].ui32DMATARGADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMATARGADDR;
        gMspiLongTxn.block[i].ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
        gMspiLongTxn.block[i].ui32DMATARGADDRVal = (i % 2) ? (uint32_t)&g_TempBuf[1] : (uint32_t)&g_TempBuf[0];

        for (j = 0; j < NUM_MSPI_TXN_PER_FRAG; j++)
        {
            gMspiLongTxn.block[i].subseg[j].ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;
            gMspiLongTxn.block[i].subseg[j].ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
            gMspiLongTxn.block[i].subseg[j].ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
            gMspiLongTxn.block[i].subseg[j].ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
            gMspiLongTxn.block[i].subseg[j].ui32DMACFG2Val = _VAL2FLD(MSPI_DMACFG_DMAEN, 0);
            gMspiLongTxn.block[i].subseg[j].ui32DMATOTCOUNTVal = MSPI_TXN_SIZE;
        }
        // Pause Conditions
        // This is the Pause Boundary for HiPrio transactions
        gMspiLongTxn.block[i].ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | ((i % 2) ? MSPI_WAIT_FOR_IOM_BUFFER1 : MSPI_WAIT_FOR_IOM_BUFFER0);
        gMspiLongTxn.block[i].ui32PAUSEEN2Val = AM_HAL_MSPI_PAUSE_DEFAULT;
        gMspiLongTxn.block[i].ui32SETCLRVal = (i % 2) ? MSPI_SIGNAL_IOM_BUFFER1 : MSPI_SIGNAL_IOM_BUFFER0;
    }
    gMspiLongTxn.tail.ui32PAUSENAddr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    gMspiLongTxn.tail.ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    gMspiLongTxn.tail.ui32DMATARGADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMATARGADDR;
    gMspiLongTxn.tail.ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
    gMspiLongTxn.tail.ui32DMATARGADDRVal = (i % 2) ? (uint32_t)&g_TempBuf[1] : (uint32_t)&g_TempBuf[0];

    for (j = 0; j < NUM_MSPI_TXN_LAST_FRAG; j++)
    {
        gMspiLongTxn.tail.subseg[j].ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;
        gMspiLongTxn.tail.subseg[j].ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
        gMspiLongTxn.tail.subseg[j].ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
        gMspiLongTxn.tail.subseg[j].ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
        gMspiLongTxn.tail.subseg[j].ui32DMACFG2Val = _VAL2FLD(MSPI_DMACFG_DMAEN, 0);
        gMspiLongTxn.tail.subseg[j].ui32DMATOTCOUNTVal = MSPI_TXN_SIZE;
    }
    // Pause Conditions
    // This is the Pause Boundary for HiPrio transactions
    gMspiLongTxn.tail.ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | ((i % 2) ? MSPI_WAIT_FOR_IOM_BUFFER1 : MSPI_WAIT_FOR_IOM_BUFFER0);
    gMspiLongTxn.tail.ui32PAUSEEN2Val = AM_HAL_MSPI_PAUSE_DEFAULT;
    gMspiLongTxn.tail.ui32SETCLRVal = (i % 2) ? MSPI_SIGNAL_IOM_BUFFER1 : MSPI_SIGNAL_IOM_BUFFER0;

    gMspiLongTxn.jmpOut.ui32CQAddrAddr = (uint32_t)&MSPIn(ui32Module)->CQADDR;
}

// Initialize for each type of transacation
void mspi_setup_cq_long(uint32_t ui32Module, uint8_t ui8Priority, am_hal_mspi_dir_e eDirection, uint32_t blockSize)
{
    uint32_t i, j;
    uint32_t ui32DmaCfg =
        _VAL2FLD(MSPI_DMACFG_DMAPWROFF, 0)   |  // DMA Auto Power-off not supported!
        _VAL2FLD(MSPI_DMACFG_DMAPRI, ui8Priority)    |
        _VAL2FLD(MSPI_DMACFG_DMADIR, eDirection)     |
        _VAL2FLD(MSPI_DMACFG_DMAEN, 3);

    for (i = 0; i < (MAX_INT_BLOCKS + 1); i++)
    {
        gMspiLongTxn.block[i].subseg[NUM_MSPI_TXN_PER_FRAG - 1].ui32DMATOTCOUNTVal = blockSize - (NUM_MSPI_TXN_PER_FRAG-1)*MSPI_TXN_SIZE;
        for (j = 0; j < (NUM_MSPI_TXN_PER_FRAG); j++)
        {
            gMspiLongTxn.block[i].subseg[j].ui32DMACFG1Val = ui32DmaCfg;
        }
    }
    for (j = 0; j < (NUM_MSPI_TXN_LAST_FRAG); j++)
    {
        gMspiLongTxn.tail.subseg[j].ui32DMACFG1Val = ui32DmaCfg;
    }
}

static void
create_mspi_iom_write_transaction(uint32_t blockSize,
                                  am_hal_mspi_dir_e eDirection,
                                  uint32_t ui32DevAddr,
                                  uint32_t ui32NumBytes)
{
    uint32_t i, j;
    for (i = 0; i < (MAX_INT_BLOCKS + 1); i++)
    {
        uint32_t addr = ui32DevAddr;
        for (j = 0; j < (NUM_MSPI_TXN_PER_FRAG - 1); j++)
        {
            gMspiLongTxn.block[i].subseg[j].ui32DMADEVADDRVal = addr;
            addr += MSPI_TXN_SIZE;
        }
        gMspiLongTxn.block[i].subseg[j].ui32DMADEVADDRVal = addr;
        ui32DevAddr += blockSize;
    }
    for (j = 0; j < (NUM_MSPI_TXN_LAST_FRAG); j++)
    {
        gMspiLongTxn.tail.subseg[j].ui32DMADEVADDRVal = ui32DevAddr;
        ui32DevAddr += MSPI_TXN_SIZE;
    }
}
#else 

// MSPI
//
// Command Queue entry structure.
//
typedef struct
{
    uint32_t                    ui32PAUSENAddr;
    uint32_t                    ui32PAUSEENVal;
    uint32_t                    ui32PAUSEN2Addr;
    uint32_t                    ui32PAUSEEN2Val;
    uint32_t                    ui32DMATARGADDRAddr;
    uint32_t                    ui32DMATARGADDRVal;
    uint32_t                    ui32DMADEVADDRAddr;
    uint32_t                    ui32DMADEVADDRVal;
    uint32_t                    ui32DMATOTCOUNTAddr;
    uint32_t                    ui32DMATOTCOUNTVal;
    uint32_t                    ui32DMACFG1Addr;
    uint32_t                    ui32DMACFG1Val;
    uint32_t                    ui32DMACFG2Addr;
    uint32_t                    ui32DMACFG2Val;
    uint32_t                    ui32SETCLRAddr;
    uint32_t                    ui32SETCLRVal;
} am_hal_mspi_txn_seg_t;

typedef struct
{
    // Each block will be max size transfer with CONT set
    am_hal_mspi_txn_seg_t block[MAX_INT_BLOCKS]; // Fixed
    am_hal_cq_jmp_t  jmp; // Jump always to am_hal_iom_long_txn_t->tail
} am_hal_mspi_txn_seq_t;


typedef struct
{
    // Head
    // Max size transfer with CONT Set
    am_hal_mspi_txn_seg_t    head; // Programmable per transaction: ui32OFFSETHIVal, ui32HdrDMATARGADDRVal, ui32HdrCMDVal
    // Jmp
    am_hal_cq_jmp_t      jmp; // Programmable address to jump inside am_hal_iom_txn_seq_t
    // Tail
    am_hal_mspi_txn_seg_t     tail; // Programmable tail length, CMD Value
    am_hal_cq_jmp_t      jmpOut; // Programmable address to jump back to the original CQ
} am_hal_mspi_long_txn_t;

am_hal_mspi_long_txn_t gMspiLongTxn;
am_hal_mspi_txn_seq_t  gMspiSequence;

// MSPI
// One time initialization
void mspi_init_cq_long(uint32_t ui32Module)
{
    am_hal_mspi_long_txn_t *pMspiLong = &gMspiLongTxn;
    // Initialize the head block
    // Initialize the tail block
    pMspiLong->head.ui32PAUSENAddr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    pMspiLong->head.ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    pMspiLong->head.ui32DMATARGADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMATARGADDR;
    pMspiLong->head.ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;
    pMspiLong->head.ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
    pMspiLong->head.ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
    pMspiLong->head.ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
    pMspiLong->head.ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;

    pMspiLong->tail.ui32PAUSENAddr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    pMspiLong->tail.ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
    pMspiLong->tail.ui32DMATARGADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMATARGADDR;
    pMspiLong->tail.ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
    pMspiLong->tail.ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
    pMspiLong->tail.ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
    pMspiLong->tail.ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
    pMspiLong->tail.ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;

    pMspiLong->head.ui32DMACFG2Val = _VAL2FLD(MSPI_DMACFG_DMAEN, 0);
    pMspiLong->tail.ui32DMACFG2Val = _VAL2FLD(MSPI_DMACFG_DMAEN, 0);

    pMspiLong->tail.ui32DMATARGADDRVal = (MAX_INT_BLOCKS % 2) ? (uint32_t)&g_TempBuf[0] : (uint32_t)&g_TempBuf[1];

    // Initialize the jumps
    gMspiSequence.jmp.ui32CQAddrAddr = pMspiLong->jmpOut.ui32CQAddrAddr = pMspiLong->jmp.ui32CQAddrAddr = (uint32_t)&MSPIn(ui32Module)->CQADDR;
    gMspiSequence.jmp.ui32CQAddrVal = (uint32_t)&pMspiLong->tail;
    // Initialize the sequence blocks
    for (uint32_t i = 0; i < MAX_INT_BLOCKS; i++)
    {
        gMspiSequence.block[i].ui32PAUSENAddr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
        gMspiSequence.block[i].ui32PAUSEN2Addr = (uint32_t)&MSPIn(ui32Module)->CQPAUSE;
        gMspiSequence.block[i].ui32DMATARGADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMATARGADDR;
        gMspiSequence.block[i].ui32DMATOTCOUNTAddr = (uint32_t)&MSPIn(ui32Module)->DMATOTCOUNT;
        gMspiSequence.block[i].ui32DMACFG1Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
        gMspiSequence.block[i].ui32DMACFG2Addr = (uint32_t)&MSPIn(ui32Module)->DMACFG;
        gMspiSequence.block[i].ui32SETCLRAddr = (uint32_t)&MSPIn(ui32Module)->CQSETCLEAR;
        gMspiSequence.block[i].ui32DMADEVADDRAddr = (uint32_t)&MSPIn(ui32Module)->DMADEVADDR;
        // Pause Conditions
        // This is the Pause Boundary for HiPrio transactions
        gMspiSequence.block[i].ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | ((i % 2) ? MSPI_WAIT_FOR_IOM_BUFFER0 : MSPI_WAIT_FOR_IOM_BUFFER1);
        gMspiSequence.block[i].ui32PAUSEEN2Val = AM_HAL_MSPI_PAUSE_DEFAULT;
        gMspiSequence.block[i].ui32SETCLRVal = (i % 2) ? MSPI_SIGNAL_IOM_BUFFER0 : MSPI_SIGNAL_IOM_BUFFER1;
        gMspiSequence.block[i].ui32DMATARGADDRVal = (i % 2) ? (uint32_t)&g_TempBuf[0] : (uint32_t)&g_TempBuf[1];
        gMspiSequence.block[i].ui32DMACFG2Val = _VAL2FLD(MSPI_DMACFG_DMAEN, 0);
    }

    pMspiLong->tail.ui32PAUSEEN2Val = AM_HAL_MSPI_PAUSE_DEFAULT;
    pMspiLong->head.ui32PAUSEEN2Val = AM_HAL_MSPI_PAUSE_DEFAULT;
    // This is the Pause Boundary for HiPrio transactions
    pMspiLong->tail.ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | ((MAX_INT_BLOCKS % 2) ? MSPI_WAIT_FOR_IOM_BUFFER0 : MSPI_WAIT_FOR_IOM_BUFFER1);
    pMspiLong->tail.ui32SETCLRVal = (MAX_INT_BLOCKS % 2) ? MSPI_SIGNAL_IOM_BUFFER0 : MSPI_SIGNAL_IOM_BUFFER1;
}

// Initialize for each type of transacation
void mspi_setup_cq_long(uint32_t ui32Module, uint8_t ui8Priority, am_hal_mspi_dir_e eDirection, uint32_t blockSize)
{
    am_hal_mspi_long_txn_t *pMspiLong = &gMspiLongTxn;
    uint32_t ui32DmaCfg =
        _VAL2FLD(MSPI_DMACFG_DMAPWROFF, 0)   |  // DMA Auto Power-off not supported!
        _VAL2FLD(MSPI_DMACFG_DMAPRI, ui8Priority)    |
        _VAL2FLD(MSPI_DMACFG_DMADIR, eDirection)     |
        _VAL2FLD(MSPI_DMACFG_DMAEN, 3);

    pMspiLong->head.ui32DMATOTCOUNTVal = blockSize;
    pMspiLong->head.ui32DMACFG1Val     = pMspiLong->tail.ui32DMACFG1Val = ui32DmaCfg;

    // Initialize the sequence blocks
    for (uint32_t i = 0; i < MAX_INT_BLOCKS; i++)
    {
        gMspiSequence.block[i].ui32DMACFG1Val = ui32DmaCfg;
        gMspiSequence.block[i].ui32DMATOTCOUNTVal = blockSize;
    }
}

static void
create_mspi_iom_write_transaction(uint32_t blockSize,
                                  am_hal_mspi_dir_e eDirection,
                                  uint32_t ui32DevAddr,
                                  uint32_t ui32NumBytes)
{
    am_hal_mspi_long_txn_t *pMspiLong = &gMspiLongTxn;
    uint32_t numBlocks = (ui32NumBytes + blockSize - 1) / blockSize;

    pMspiLong->head.ui32DMADEVADDRVal = ui32DevAddr;
    // jmp: ui32CQAddrVal
    if (numBlocks > 1)
    {
        if (numBlocks > 2)
        {
            // Identify where in the block array to jump to
            gMspiLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gMspiSequence.block[MAX_INT_BLOCKS - (numBlocks - 2)];
            for (uint32_t i = 0; i < (numBlocks - 2); i++)
            {
                gMspiSequence.block[MAX_INT_BLOCKS - (numBlocks - 2) + i].ui32DMADEVADDRVal = ui32DevAddr + (i + 1) * blockSize;
            }
        }
        else
        {
            // Just jump to tail
            gMspiLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gMspiLongTxn.tail;
        }
        ui32NumBytes -= blockSize*(numBlocks - 1);
        // tail: ui32HdrCMDVal & ui32DMATOTCOUNTVal
        // Initialize the count & command for tail
        gMspiLongTxn.tail.ui32DMADEVADDRVal = ui32DevAddr + (numBlocks-1)*blockSize;
        gMspiLongTxn.tail.ui32DMATOTCOUNTVal  = ui32NumBytes;
    }
    else
    {
        // Just jump to end
        gMspiLongTxn.jmp.ui32CQAddrVal = (uint32_t)&gMspiLongTxn.jmpOut;
    }

    // Initialize head - based on how many blocks
    if ((MAX_INT_BLOCKS + 2 - numBlocks) % 2)
    {
        gMspiLongTxn.head.ui32DMATARGADDRVal = (uint32_t)&g_TempBuf[1];
        // This is the Pause Boundary for HiPrio transactions
        gMspiLongTxn.head.ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | MSPI_WAIT_FOR_IOM_BUFFER1;
        gMspiLongTxn.head.ui32SETCLRVal = MSPI_SIGNAL_IOM_BUFFER1;
    }
    else
    {
        gMspiLongTxn.head.ui32DMATARGADDRVal = (uint32_t)&g_TempBuf[0];
        // This is the Pause Boundary for HiPrio transactions
        gMspiLongTxn.head.ui32PAUSEENVal = AM_HAL_MSPI_CQP_PAUSE_DEFAULT | MSPI_WAIT_FOR_IOM_BUFFER0;
        gMspiLongTxn.head.ui32SETCLRVal = MSPI_SIGNAL_IOM_BUFFER0;
    }
}

#endif
#endif
#endif

uint32_t
init_mspi_iom_xfer(void)
{
    uint32_t ui32Status = 0;
    uint32_t      u32Arg;
#ifdef CQ_RAW
    // One time initialization of the preconstructed sequence
    iom_init_cq_long(DISPLAY_IOM_MODULE);
    iom_setup_cq_long(DISPLAY_IOM_MODULE, 1, AM_HAL_IOM_TX, SPI_TXN_SIZE, AM_BSP_DISPLAY_SPI_CS, 0);
    mspi_init_cq_long(0);
    mspi_setup_cq_long(0, 1, AM_HAL_MSPI_RX, SPI_TXN_SIZE);
#endif
    // Clear flags
    u32Arg = AM_HAL_IOM_SC_CLEAR(0xFF) & ~AM_HAL_IOM_SC_RESV_MASK;  // clear all flags
    am_hal_iom_control(g_IOMHandle, AM_HAL_IOM_REQ_FLAG_SETCLR, &u32Arg);
    u32Arg = AM_HAL_MSPI_SC_CLEAR(0xFF) & ~AM_HAL_MSPI_SC_RESV_MASK;  // clear all flags
    am_hal_mspi_control(g_MSPIHdl, AM_HAL_MSPI_REQ_FLAG_SETCLR, &u32Arg);
    // Link MSPI and IOM
    u32Arg = DISPLAY_IOM_MODULE;
    am_hal_mspi_control(g_MSPIHdl, AM_HAL_MSPI_REQ_LINK_IOM, &u32Arg);
#ifndef CONFIG_DISPLAY_WINDOW // Configure the window - just once
    display_func.display_set_transfer_window(false, 0, 0, ROW_NUM - 1, COLUMN_NUM - 1);
#endif
    return ui32Status;
}

int
start_mspi_iom_xfer(uint32_t psramOffset, uint32_t ui32NumBytes, uint32_t startRow, uint32_t startCol, uint32_t endRow, uint32_t endCol)
{
    uint32_t      ui32Status = 0;
    am_hal_iom_callback_t   iomCb = 0;
    am_hal_mspi_callback_t  mspiCb = 0;

    iomCb = display_write_complete;
    mspiCb = psram_read_complete;


    DEBUG_PRINT("\nInitiating MSP -> IOM Transfer\n");
    // Send Display command here
#ifdef CONFIG_DISPLAY_WINDOW
    display_func.display_set_transfer_window(false, startRow, startCol, endRow, endCol);
#endif
#ifndef USE_HW_DCX
    display_func.display_blocking_write(NULL, 0);
#endif

    if (ui32Status == 0)
    {
#ifdef CQ_RAW
        // Queue up the CQ Raw
        am_hal_cmdq_entry_t jump;
        // MSPI
        create_mspi_iom_write_transaction(SPI_TXN_SIZE,
                                        AM_HAL_MSPI_RX,
                                        psramOffset,
                                        ui32NumBytes);
        jump.address = (uint32_t)&MSPIn(0)->CQADDR;
        jump.value = (uint32_t)&gMspiLongTxn;

        am_hal_mspi_cq_raw_t rawMspiCfg;
        rawMspiCfg.ui32PauseCondition = 0; //MSPI_WAIT_FOR_IOM_BUFFER0;
        rawMspiCfg.ui32StatusSetClr = 0;
        rawMspiCfg.pCQEntry = &jump;
        rawMspiCfg.numEntries = sizeof(am_hal_cmdq_entry_t) / 8;
        rawMspiCfg.pfnCallback = mspiCb;
        rawMspiCfg.pCallbackCtxt = 0;
        rawMspiCfg.pJmpAddr = &gMspiLongTxn.jmpOut.ui32CQAddrVal;
        ui32Status = am_hal_mspi_control(g_MSPIHdl, AM_HAL_MSPI_REQ_CQ_RAW, &rawMspiCfg);
        if (ui32Status)
        {
            DEBUG_PRINT("\nFailed to queue up MSPI Read transaction\n");
            while(1);
        }
        // IOM
        create_iom_mspi_read_transaction(SPI_TXN_SIZE,
                                        AM_HAL_IOM_TX,
                                        ui32NumBytes,
#ifdef USE_HW_DCX
                                        AM_DEVICES_RM69310_MEMORY_WRITE,
                                        // AM_DEVICES_RM69310_MEMORY_WRITE,
                                        1, // TODO Address size - dependent on Display device - should be abstracted out in display_device_func_t
#else
                                        0,
                                        0, // TODO Address size - dependent on Display device - should be abstracted out in display_device_func_t
#endif
                                        AM_BSP_DISPLAY_SPI_CS);

        if (ui32Status)
        {
            DEBUG_PRINT("\nFailed to queue up SPI Write transaction\n");
            while(1);
        }
        // Queue up the CQ Raw
        jump.address = (uint32_t)&IOMn(DISPLAY_IOM_MODULE)->CQADDR;
        jump.value = (uint32_t)&gIomLongTxn;

        am_hal_iom_cq_raw_t rawIomCfg;
        rawIomCfg.ui32PauseCondition = 0; //IOM_WAIT_FOR_MSPI_BUFFER0;
        rawIomCfg.ui32StatusSetClr = 0;
        rawIomCfg.pCQEntry = &jump;
        rawIomCfg.numEntries = sizeof(am_hal_cmdq_entry_t) / 8;
        rawIomCfg.pfnCallback = iomCb;
        rawIomCfg.pCallbackCtxt = 0;
        rawIomCfg.pJmpAddr = &gIomLongTxn.jmpOut.ui32CQAddrVal;
        ui32Status = am_hal_iom_control(g_IOMHandle, AM_HAL_IOM_REQ_CQ_RAW, &rawIomCfg);
        if (ui32Status)
        {
            DEBUG_PRINT("\nFailed to queue up SPI Write transaction\n");
            while(1);
        }

#endif
    }
    else
    {
        while(1);
    }
    return ui32Status;
}

void
psram_write_complete(void *pCallbackCtxt, uint32_t transactionStatus)
{
    g_bReady = true;
}

static uint32_t bandOffset = 0;
uint32_t renderBandOffset = 0;
// This function does the necessary composition to create the scrolling color band
uint32_t compose(void)
{
    uint32_t i;
    uint32_t ui32Status;
    static uint8_t delta = 0;
    //
    // Generate data into the Sector Buffer
    //
    for (i = 0; i < (BAND_DEPTH * COLUMN_NUM / 4); i++)
    {
        g_TempBuf[0][i] = COLOR_4P(delta);
        g_TempBuf[1][i] = COLOR_4P((uint8_t)(BAND_COLOR));
    }
    if (bandOffset != (ROW_NUM*PIXEL_BYTE - BAND_DEPTH))
    {
        //
        // Write the TX buffer into the target sector.
        //
        ui32Status = am_devices_mspi_psram_nonblocking_write
                        ((uint8_t *)g_TempBuf[1], FRAME_BUF_START_OFFSET + (bandOffset + BAND_DEPTH) * COLUMN_NUM, BAND_DEPTH * COLUMN_NUM,
                               NULL , NULL);

        if (AM_DEVICES_MSPI_PSRAM_STATUS_SUCCESS != ui32Status)
        {
            DEBUG_PRINT("Failed to write buffer to PSRAM Device!\n");
            return -1;
        }
    } else {
        i = 0;
    }
    //
    // Write the TX buffer into the target sector.
    //
    ui32Status = am_devices_mspi_psram_nonblocking_write
                    ((uint8_t *)g_TempBuf[0], FRAME_BUF_START_OFFSET + bandOffset * COLUMN_NUM, BAND_DEPTH * COLUMN_NUM,
                                                         psram_write_complete, NULL);

    if (AM_DEVICES_MSPI_PSRAM_STATUS_SUCCESS != ui32Status)
    {
        DEBUG_PRINT("Failed to write buffer to PSRAM Device!\n");
        return -1;
    }
    renderBandOffset = bandOffset;
    bandOffset += BAND_DEPTH;
    if (bandOffset == ROW_NUM*PIXEL_BYTE)
    {
        bandOffset = 0;
        delta += 4;
        if (delta >= COLOR_MAX)
        {
            delta = 0;
        }
    }
    return ui32Status;
}

//*****************************************************************************
//
// MSPI Example Main.
//
//*****************************************************************************
int
main(void)
{
    uint32_t      ui32Status;
    int iRet;
    const am_hal_mspi_dev_config_t *mspiPSRAMCfg =
#ifdef MSPI_PSRAM_SERIAL
            &MSPI_PSRAM_SerialCE0MSPIConfig;
#else
            &MSPI_PSRAM_QuadCE0MSPIConfig;
#endif
    //
    // Set the clock frequency.
    //
    am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);

    //
    // Set the default cache configuration
    //
    am_hal_cachectrl_config(&am_hal_cachectrl_defaults);
    am_hal_cachectrl_enable();

    //
    // Configure the board for low power operation.
    //
    am_bsp_low_power_init();

    am_hal_gpio_pinconfig(CPU_SLEEP_GPIO, g_AM_HAL_GPIO_OUTPUT_12);
    DEBUG_GPIO_LOW(CPU_SLEEP_GPIO);
    am_hal_gpio_pinconfig(TEST_GPIO2, g_AM_HAL_GPIO_OUTPUT_12);
    DEBUG_GPIO_LOW(TEST_GPIO2);
#ifdef ENABLE_XIP
    am_hal_gpio_pinconfig(TEST_GPIO1, g_AM_HAL_GPIO_OUTPUT_12);
    DEBUG_GPIO_LOW(TEST_GPIO1);
#endif
#ifdef ENABLE_XIPMM
    am_hal_gpio_pinconfig(TEST_GPIO3, g_AM_HAL_GPIO_OUTPUT_12);
    DEBUG_GPIO_LOW(TEST_GPIO3);
#endif

    // Initialize the MSPI PSRAM
    iRet = mspi_psram_init(mspiPSRAMCfg);
    if (iRet)
    {
        DEBUG_PRINT("Unable to initialize MSPI psram\n");
        while(1);
    }

    // Initialize psram Data
    iRet = init_mspi_psram_data();
    if (iRet)
    {
        DEBUG_PRINT("Unable to initialize MSPI psram data\n");
        while(1);
    }

    // Initialize the IOM Display

    iRet = display_init();
    if (iRet)
    {
        DEBUG_PRINT("Unable to initialize Display\n");
        while(1);
    }

    // Initialize display data
    iRet = init_iom_display_data();
    if (iRet)
    {
        DEBUG_PRINT("Unable to initialize display data\n");
        while(1);
    }

    am_hal_interrupt_master_enable();
    
    init_mspi_iom_xfer();

    am_devices_rm69310_display_on();
    am_util_delay_ms(40); // wait for display stable

    bTEInt = false;

    DEBUG_PRINT("Getting into Wait Loop\n");
    //
    // Loop forever.
    //
    while(1)
    {
        //
        // Disable interrupt while we decide whether we're going to sleep.
        //
        uint32_t ui32IntStatus = am_hal_interrupt_master_disable();

        if (g_bReady && bTEInt)
        {
            // New buffer composed, and Display ready to take it
            //
            // Enable interrupts
            //
            am_hal_interrupt_master_set(ui32IntStatus);
            g_bReady = false;
            bTEInt = false;
#ifndef SEQLOOP
            uint32_t renderSize, rowStart, rowEnd;
            static uint32_t bufOffset = 0;
// #ifdef PARTIAL_RENDER_ONLY
            renderBandOffset = 100;
            if (render_flag == true) {
                // bufOffset = renderBandOffset * COLUMN_NUM;
                // rowStart = renderBandOffset;
                // if (renderBandOffset != (ROW_NUM - BAND_DEPTH))
                // {
                //     renderSize = 2 * BAND_DEPTH * COLUMN_NUM;
                //     rowEnd = rowStart + 2 * BAND_DEPTH - 1;
                // }
                // else
                // {
                //     renderSize = BAND_DEPTH * COLUMN_NUM;
                //     rowEnd = rowStart + 2 * BAND_DEPTH - 1;
                // }
                renderSize = FRAME_SIZE/2;
                rowStart = 120;
                rowEnd = ROW_NUM - 1;

            }
// #else
            if (render_flag == false) {
                // bufOffset = 0;
                renderSize = FRAME_SIZE/2;
                rowStart = 0;
                // rowEnd = ROW_NUM - 1;
                rowEnd = 119;
                j++;
                if (j == 5) {
                    j = 0;
                    bufOffset += 240; //1536 = 128*12; 1920 = 240*8 = 128*15; 1680 is fine
                    if (bufOffset == 67200) {
                        bufOffset = 0;
                    }
                }
            }
// #endif
            iRet = start_mspi_iom_xfer(FRAME_BUF_START_OFFSET + bufOffset, renderSize, rowStart, 0, rowEnd, COLUMN_NUM - 1);
#else
            iRet = trigger_mspi_iom_xfer(0, 0, ROW_NUM - 1, COLUMN_NUM - 1);
#endif
            if (iRet)
            {
                DEBUG_PRINT("Unable to initiate MSPI IOM transfer\n");
                while(1);
            }
        }
        else if (g_bDone)
        {
            render_flag ^= 0x1;
            if (render_flag == 0x1)
                bTEInt = true;
            // Redering complete
            //
            // Enable interrupts
            //
            am_hal_interrupt_master_set(ui32IntStatus);
            g_bDone = false;
            // // Compose
            // DEBUG_GPIO_HIGH(TEST_GPIO2);
            // iRet = compose();
            // DEBUG_GPIO_LOW(TEST_GPIO2);
            // if (iRet)
            // {
            //     DEBUG_PRINT("Unable to Compose\n");
            //     while(1);
            // }
            g_bReady = true;
        }
        else
        {
            DEBUG_GPIO_HIGH(CPU_SLEEP_GPIO);
            am_hal_sysctrl_sleep(true);
            DEBUG_GPIO_LOW(CPU_SLEEP_GPIO);
            //
            // Enable interrupts
            //
            am_hal_interrupt_master_set(ui32IntStatus);
        }
    }

    //
    // Clean up the Display before exit.
    //
    iRet = display_deinit();
    if (iRet)
    {
        DEBUG_PRINT("Unable to terminate display\n");
    }

    //
    // Clean up the MSPI before exit.
    //
    ui32Status = am_devices_mspi_psram_deinit((am_hal_mspi_dev_config_t *)mspiPSRAMCfg);
    if (AM_DEVICES_MSPI_PSRAM_STATUS_SUCCESS != ui32Status)
    {
        DEBUG_PRINT("Failed to shutdown the MSPI and PSRAM Device!\n");
    }

    //
    //  End banner.
    //
    DEBUG_PRINT("Apollo3 MSPI-IOM Display Example Complete\n");

    //
    // Loop forever while sleeping.
    //
    while (1)
    {
        //
        // Go to Deep Sleep.
        //
        am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
    }
}
