/*
 * Copyright 2022 MindMotion Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#include "audio_i2s_dma.h"
#include "board_init.h"

static volatile uint32_t audio_status = 0u;
static volatile uint32_t audio_reqs = 0u;

uint32_t Audio_GetStatus(void)
{
    return audio_status;
}

void Audio_ClearStatus(uint32_t status)
{
    audio_status &= ~status;
}

/* enable the dma channel to start the transfer. */
void Audio_Enable(bool enable)
{
    if (enable)
    {
        DMA_EnableChannel(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN, true);
        audio_reqs &= ~AUDIO_REQ_XFER_STOP;
    }
    else
    {
        audio_reqs |= AUDIO_REQ_XFER_STOP; /* request to disable dma softly. */
    }
}

/* configure audio play. */
void Audio_Setup(uint32_t sample_rate, uint16_t * buf, uint32_t buf_cnt)
{
    /* disable the dma to reset the conf. */
    DMA_EnableChannel(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN, false);
    DMA_ClearChannelInterruptStatus(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN, DMA_CHN_INT_XFER_HALF_DONE | DMA_CHN_INT_XFER_DONE);

    /* setup dma. */
    DMA_Channel_Init_Type dma_channel_init;
    dma_channel_init.MemAddr           = (uint32_t)(buf);
    dma_channel_init.MemAddrIncMode    = DMA_AddrIncMode_IncAfterXfer;
    dma_channel_init.PeriphAddr        = I2S_GetTxDataRegAddr(BOARD_AUDIO_I2S_PORT); /* use tx data register here. */
    dma_channel_init.PeriphAddrIncMode = DMA_AddrIncMode_StayAfterXfer;
    dma_channel_init.Priority          = DMA_Priority_Highest;
    dma_channel_init.XferCount         = buf_cnt;
    dma_channel_init.XferMode          = DMA_XferMode_MemoryToPeriph;
    dma_channel_init.ReloadMode        = DMA_ReloadMode_AutoReloadContinuous; /* DMA_ReloadMode_AutoReloadContinuous */ /* DMA_ReloadMode_AutoReload */
    dma_channel_init.XferWidth         = DMA_XferWidth_16b;
    DMA_InitChannel(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN, &dma_channel_init);

    /* enable DMA transfer half & done interrupt. */
    DMA_EnableChannelInterrupts(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN, DMA_CHN_INT_XFER_DONE | DMA_CHN_INT_XFER_HALF_DONE, true);
    NVIC_EnableIRQ(BOARD_AUDIO_DMA_IRQn);

    /* setup i2s. */
    I2S_Master_Init_Type i2s_init;
    i2s_init.ClockFreqHz    = CLOCK_APB1_FREQ;
    i2s_init.DataWidth      = I2S_DataWidth_16b;
    i2s_init.EnableMCLK     = true;
    i2s_init.Polarity       = I2S_Polarity_1;
    i2s_init.Protocol       = I2S_Protocol_PHILIPS;
    i2s_init.SampleRate     = sample_rate;
    i2s_init.XferMode       = I2S_XferMode_TxOnly;
    I2S_InitMaster(BOARD_AUDIO_I2S_PORT, &i2s_init);
    I2S_EnableDMA(BOARD_AUDIO_I2S_PORT, true);/* enable DMA. */

    /* enable i2s with dma and interrupt. */
    I2S_Enable(BOARD_AUDIO_I2S_PORT, true);/* enable I2S. */
}

/* handle dma interrupt. */
void BOARD_AUDIO_DMA_IRQHandler(void)
{
    /* dma tx half interrupt done. */
    if (0u != (DMA_CHN_INT_XFER_HALF_DONE & DMA_GetChannelInterruptStatus(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN)) )
    {
        audio_status |= AUDIO_STATUS_XFER_HALF;
        DMA_ClearChannelInterruptStatus(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN, DMA_CHN_INT_XFER_HALF_DONE);
    }

    /* dma tx interrupt done. */
    if (0u != (DMA_CHN_INT_XFER_DONE & DMA_GetChannelInterruptStatus(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN)) )
    {
        audio_status |= AUDIO_STATUS_XFER_DONE;
        DMA_ClearChannelInterruptStatus(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN, DMA_CHN_INT_XFER_DONE);
    }

    /* disable the dma after the current transfer is finished per the latest request. */
    if ((audio_reqs & AUDIO_REQ_XFER_STOP) == AUDIO_REQ_XFER_STOP)
    {
        DMA_EnableChannel(BOARD_AUDIO_DMA_PORT, BOARD_AUDIO_DMA_CHN, false);
        audio_reqs &= ~AUDIO_REQ_XFER_STOP;
    }
}

/* EOF. */
