/*
 * Copyright 2017-2020 NXP
 * This software is owned or controlled by NXP and may only be used strictly
 * in accordance with the applicable license terms.  By expressly accepting
 * such terms or by downloading, installing, activating and/or otherwise using
 * the software, you are agreeing that you have read, and that you agree to
 * comply with and are bound by, such license terms.  If you do not agree to
 * be bound by the applicable license terms, then you may not retain, install,
 * activate or otherwise use the software.
 */

/**
 * @defgroup KL_MODULES_AUDIOSERVICE_MAIN audio
 * @ingroup KL_MODULES_AUDIOSERVICE
 * @ref KL_MODULES_AudioStreaming audio runs only on KL-USB application.
 * It streams/copies/sends the audio packets from USBHeadSetFunction to NxH3670 chip.
 * For this it initializes the I2S and DMA interfaces using the KL ports and registers.
 * Audio packets are copied from USB application and put in to a ring buffer and transfered to NxH3670 via the DMA.
 * @ref KL_MODULES_AudioStreaming audio uses sub-layers/modules like, audio_ringbuffer, dma_interface and i2s_interface.
 * Currently Tx, ie. Audio transfer from USB to NxH3670 is only in the scope.
 * @{
 */

/* ---------------------------------------------------------------------------- */
/* Include files                                                                */
/* ---------------------------------------------------------------------------- */

#include "fsl_sai.h"
#include "fsl_clock.h"
#include "fsl_port.h"
#include "fsl_dma.h"
#include "fsl_dmamux.h"
#include "board.h"
#include "dma_interface.h"
#include "audio.h"
#include "audio_tx.h"
#include "audio_rx.h"
#include "audio_mixer.h"
#include "audio_common.h"
#include "audio_ringbuffer.h"
#include "audio_internal.h"
#include "clock_config.h"

static volatile bool gs_AudioStarted = false;
static bool gs_AudioBusy = false;

audio_service_ctxt_t g_AudioServiceCtxt = {
    .currentState = kSTATE_Uninitialized,
    .isBusy = false,
};

/* -------------------------------------------------------------------------
 * Local functions
 * ------------------------------------------------------------------------- */

#if AUDIO_SERVICE_TX_ENABLE
static void MixerReadyStateChangedCb(audio_mixer_state_t state)
{
    /**
     * When the Mixer becomes ready to output mixed samples, start the Audio TX.
     * Configure that the Audio TX has to ask the Mixer for samples to transmit.
     */
    if (kAUDIO_MIXER_STATE_Ready == state) {
        audio_StartTx(AUDIO_MIXER_GetEmptySamples, AUDIO_MIXER_GetMixedSamples);
    } else if (kAUDIO_MIXER_STATE_NotReady == state) {
        audio_StopTx();
    }
}
#endif

static void EnableI2sPinPortClock(void)
{
    CLOCK_EnableClock(BOARD_I2S_TXD_PORT_CLOCK);
    CLOCK_EnableClock(BOARD_I2S_RXD_PORT_CLOCK);
    CLOCK_EnableClock(BOARD_I2S_BCLK_PORT_CLOCK);
    CLOCK_EnableClock(BOARD_I2S_FS_PORT_CLOCK);
}

/* ---------------------------------------------------------------------------- */
/* Public Functions-Common Service Specific                                     */
/* ---------------------------------------------------------------------------- */

void audio_Init(void)
{
    /** Enable the clock for the SAI module */
    SAI_Init(AUDIO_SERVICE_I2S_BASEADDR);

    /* I2S master with MCLK at 24.5768 MHz coming from OSCERCLK */
    sai_master_clock_t audioService_MasterClkConfig = {
        .mclkOutputEnable = true,
        .mclkSource = kSAI_MclkSourceSysclk
    };
    SAI_SetMasterClockConfig(AUDIO_SERVICE_I2S_BASEADDR, &audioService_MasterClkConfig);

    /** Initializes the ring buffer*/
#if AUDIO_SERVICE_TX_ENABLE
    audio_RingbufferTxInit();
#endif
#if AUDIO_SERVICE_RX_ENABLE
    audio_RingbufferRxInit();
#endif

    EnableI2sPinPortClock();
}

void audio_Deinit(void)
{
    /** Disables SAI Tx & Rx and Clock  */
    SAI_Deinit(AUDIO_SERVICE_I2S_BASEADDR);
}

void audio_Start(void)
{
    if (!gs_AudioStarted) {
        gs_AudioStarted = true;

#if AUDIO_SERVICE_TX_ENABLE
        /** Initializes and starts the AudioService Tx part */
        audio_InitTx();
        AUDIO_MIXER_Init();
        AUDIO_MIXER_RegisterStateCb(MixerReadyStateChangedCb);
#endif
#if AUDIO_SERVICE_RX_ENABLE
        /** Initializes and starts the AudioService Rx part */
        audio_InitRx();
#endif

    }
}

void audio_Stop(void)
{
    if (gs_AudioStarted) {
        gs_AudioStarted = false;
#if AUDIO_SERVICE_TX_ENABLE
        /** Stops data transmission */
        audio_StopTx();
        AUDIO_MIXER_StopAllInterfaces();
        AUDIO_MIXER_RegisterStateCb(NULL);
#endif
#if AUDIO_SERVICE_RX_ENABLE
        /** Stops data transmission */
        audio_StopRx();
#endif
#if AUDIO_SERVICE_TX_ENABLE
        /** Disables DMA Hardware request */
        dma_DisableHwRequest(AUDIO_SERVICE_DMA_BASE, AUDIO_SERVICE_DMA_CHANNEL_OUT, true);
#endif
#if AUDIO_SERVICE_RX_ENABLE
        dma_DisableHwRequest(AUDIO_SERVICE_DMA_BASE, AUDIO_SERVICE_DMA_CHANNEL_IN, true);
#endif
    }
}

void audio_EnableI2sPins(bool enable)
{
    if (enable) {
#if AUDIO_SERVICE_TX_ENABLE
        PORT_SetPinMux(BOARD_I2S_TXD_PORT, BOARD_I2S_TXD_PORT_PIN, BOARD_I2S_TXD_PORT_MODE);
#endif
#if AUDIO_SERVICE_RX_ENABLE
        PORT_SetPinMux(BOARD_I2S_RXD_PORT, BOARD_I2S_RXD_PORT_PIN, BOARD_I2S_RXD_PORT_MODE);
#endif
        PORT_SetPinMux(BOARD_I2S_BCLK_PORT, BOARD_I2S_BCLK_PORT_PIN, BOARD_I2S_BCLK_PORT_MODE);
        PORT_SetPinMux(BOARD_I2S_FS_PORT, BOARD_I2S_FS_PORT_PIN, BOARD_I2S_FS_PORT_MODE);        
        PORT_SetPinMux(BOARD_I2S_MCLK_PORT, BOARD_I2S_MCLK_PORT_PIN, BOARD_I2S_MCLK_PORT_MODE);
        
    } else {
        PORT_SetPinMux(BOARD_I2S_TXD_PORT, BOARD_I2S_TXD_PORT_PIN, kPORT_PinDisabledOrAnalog);
        PORT_SetPinMux(BOARD_I2S_RXD_PORT, BOARD_I2S_RXD_PORT_PIN, kPORT_PinDisabledOrAnalog);
        PORT_SetPinMux(BOARD_I2S_BCLK_PORT, BOARD_I2S_BCLK_PORT_PIN, kPORT_PinDisabledOrAnalog);
        PORT_SetPinMux(BOARD_I2S_FS_PORT, BOARD_I2S_FS_PORT_PIN, kPORT_PinDisabledOrAnalog);
    }
}

void audio_SetBusy(bool status)
{
    gs_AudioBusy = status;
}

bool audio_IsBusy(void)
{
    return gs_AudioBusy;
}

/** @} */
