/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support
 * ----------------------------------------------------------------------------
 * Copyright (c) 2011, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 * ----------------------------------------------------------------------------
 */

/**
 * \page ssc_pdc_audio SSC with PDC Audio Example
 *
 * \section Purpose
 * This example uses the Synchronous Serial Controller (SSC) of an SAM3S microcontroller
 * to output an audio steam through the WM8731 CODEC on external board.
 *
 * \section Requirements
 *
 * This package can be used with SAM3S-VB board with external codec WM8731 components.
 *
 * \section Description
 * This program plays a WAV file pre-loaded into the sram. The sample WAV file
 * can be found in the same directory of main.c.
 *
 * The audio stream is sent through the SSC interface connected to the WM8731,
 * enabling the sound to be audible using a pair of headphones.
 *
 * Since the WM8731 DAC requires that it be feeded a master clock multiple of the sample rate,
 * it is difficult to handle any WAV file. As such, this example application is limited to
 * playing files with the following format:
 * - Format:          WAV
 * - Sample rate:     8 kHz
 * - Bits Per Sample: 16
 * - Channels:        2 (stereo)
 *
 * \section usage
 * -# Build the program and download it inside the evaluation board. Please
 *    refer to the
 *    <a href="http://www.atmel.com/dyn/resources/prod_documents/6421B.pdf">
 *    SAM-BA User Guide</a>, the
 *    <a href="http://www.atmel.com/dyn/resources/prod_documents/doc6310.pdf">
 *    GNU-Based Software Development</a> application note or to the
 *    <a href="http://www.iar.com/website1/1.0.1.0/78/1/">
 *    IAR EWARM User and reference guides</a>, depending on your chosen solution.
 * -# On the computer, open and configure a terminal application
 *    (e.g. HyperTerminal on Microsoft Windows) with these settings:
 *   - 115200 bauds
 *   - 8 bits of data
 *   - No parity
 *   - 1 stop bit
 *   - No flow control
 * -# Start the application.
 * -# In the terminal window, the following text should appear:
 *    \code
 *     -- SSC with PDC Audio Example xxx --
 *     -- xxxxxx-xx
 *     -- Compiled: xxx xx xxxx xx:xx:xx --
 *     Please send a sample wav file using Xmodem protocol.
 *     The sample wav file size must small than xxx bytes!
 *    \endcode
 * -# After downloading a sample wav file using XMODEM protocol by UART,
 *    the following text should appear:
 *    \code
 *     Menu :
 *     ------
 *     W: Play the pre-loaded WAV file.
 *     I: Display the information of the WAV file.
 *    \endcode
 * The user can then choose any of the available options to perform the described action.
 *
 * \section References
 * - ssc_pdc_audio/main.c
 * - ssc.c
 * - twi.c
 * - twid.c
 */

/**
 * \file
 *
 * This file contains all the specific code for the ssc audio example.
 */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "board.h"

#include <assert.h>

/*----------------------------------------------------------------------------
 *        Local macros
 *----------------------------------------------------------------------------*/

#define min( a, b ) (((a) < (b)) ? (a) : (b))

/*----------------------------------------------------------------------------
 *        Local definitions
 *----------------------------------------------------------------------------*/

#define I2S_MASTER_TX_SETTING(nb_bit_by_slot, nb_slot_by_frame) (\
                               SSC_TCMR_CKS_MCK |\
                               SSC_TCMR_CKO_CONTINUOUS |\
                               SSC_TCMR_START_RF_FALLING |\
                               SSC_TCMR_STTDLY(1) | \
                               SSC_TCMR_PERIOD(((nb_bit_by_slot * nb_slot_by_frame) / 2) - 1) )

#define I2S_TX_FRAME_SETTING(nb_bit_by_slot, nb_slot_by_frame) (\
                               (nb_bit_by_slot - 1) |\
                               SSC_TFMR_MSBF |\
                               SSC_TFMR_DATNB(nb_slot_by_frame - 1) |\
                               SSC_TFMR_FSLEN(nb_bit_by_slot - 1) |\
                               SSC_TFMR_FSOS_NEGATIVE)

/** Master clock frequency in Hz */
#define SSC_MCK               49152000

/** Maximum size in bytes of the WAV file. */
#define MAX_WAV_SIZE          (24 * 1024)

/** TWI clock */
#define TWI_CLOCK             100000

/** Wav feature. */
#define SAMPLE_RATE           8000
#define SLOT_BY_FRAME         2
#define BITS_BY_SLOT          16

/** PCK0 on SAM3S-VB board */
#define PIN_PCK0_VB       {PIO_PB13B_PCK0, PIOB, ID_PIOB, PIO_PERIPH_B, PIO_DEFAULT}

/*----------------------------------------------------------------------------
 *        Local variables
 *----------------------------------------------------------------------------*/
/** List of pins to configure. */
static const Pin pins[] = { PINS_TWI0,
                            PINS_SSC_CODEC,
                            PIN_PCK0_VB};

/** Indicates if the WAV file is currently being played. */
static uint8_t isWavPlaying;

/** Number of samples which have already been transmitted. */
static uint32_t transmittedSamples;

/** Number of samples that have not yet been transmitted. */
static uint32_t remainingSamples;

/** Twi instance */
static Twid twid;

/** Wave file buffer */
static uint32_t wavFileData[MAX_WAV_SIZE/sizeof(uint32_t)+1];

/** Pointer to the playback WAV file header.*/
static WavHeader *pWavFileHeader = (WavHeader *) wavFileData;

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

/**
 * \brief Display the information of the WAV file (sample rate, stereo/mono and frame
 * size).
 */
static void DisplayWavInfo(void)
{
    printf("%c[2J", 27);
    WAV_DisplayInfo(pWavFileHeader);
    UART_GetChar();
}

/**
 * \brief Displays the user menu.
 */
static void DisplayMenu(void)
{
    printf("%c[2J-- ssc_pdc_audio --\n\r", 27);
    printf("Menu :\n\r");
    printf("------\n\r");

    /* Play a pre-loaded WAV file */
    if (!isWavPlaying)
    {

        printf("  W: Play the pre-loaded WAV file.\n\r");
    }

    /* Display the information of the WAV file (sample rate, stereo/mono and frame size) */
    printf("  I: Display the information of the WAV file.\n\r");

    /* Stop the current playback (if any) */
    if (isWavPlaying)
    {

        printf("  S: Stop playback\n\r");
    }
}

/**
 * \brief Play a pre-loaded WAV file.
 */
static void PlayWavFile(void)
{
    uint32_t size;

    assert( pWavFileHeader->chunkSize < MAX_WAV_SIZE ) ; /* "-F- WAV file too big (increase MAX_WAV_SIZE)\n\r" */

    /* Start transmitting WAV file to SSC */
    remainingSamples = pWavFileHeader->subchunk2Size;
    transmittedSamples = 0;

    /* Fill first PDC buffer */
    size = min(remainingSamples / (pWavFileHeader->bitsPerSample / 8), 65535);
    SSC_WriteBuffer((void *) ((uint32_t)wavFileData + sizeof(WavHeader) + transmittedSamples), size);
    remainingSamples -= size * (pWavFileHeader->bitsPerSample / 8);
    transmittedSamples += size * (pWavFileHeader->bitsPerSample / 8);

    /* Fill second buffer if necessary */
    if (remainingSamples > 0)
    {
        size = min(remainingSamples / (pWavFileHeader->bitsPerSample / 8), 65535);
        SSC_WriteBuffer((void *) ((uint32_t)wavFileData + sizeof(WavHeader) + transmittedSamples), size);
        remainingSamples -= size * (pWavFileHeader->bitsPerSample / 8);
        transmittedSamples += size * (pWavFileHeader->bitsPerSample / 8);
    }

    SSC_EnableInterrupts(SSC_IER_TXBUFE | SSC_IER_ENDTX);
    SSC_EnableTransmitter();
}

/**
 * \brief  Stop the current playback (if any).
 */
static void StopPlayback(void)
{
    isWavPlaying = 0;
    SSC_DisableInterrupts(SSC_IDR_TXBUFE | SSC_IDR_ENDTX);
    SSC->SSC_PTCR = SSC_PTCR_TXTDIS;
    SSC->SSC_TNCR = 0;
    SSC->SSC_TCR = 0;
    SSC_DisableTransmitter();
}

/*----------------------------------------------------------------------------
 *        Global functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Interrupt handler for the SSC. Loads the PDC with the audio data to stream.
 */
 void SSC_IrqHandler(void)
{
    uint32_t status;
    uint32_t size;

    status = SSC->SSC_SR;

    /* Last buffer sent */
    if ((status & SSC_SR_TXBUFE) != 0)
    {

        //isWavPlaying = 0;
        //SSC_DisableInterrupts(SSC_IDR_TXBUFE | SSC_IDR_ENDTX);
        //SSC->SSC_PTCR = SSC_PTCR_TXTDIS;
        StopPlayback();
        DisplayMenu();
    }
    /* One buffer sent & more buffers to send */
    else if (remainingSamples > 0)
    {

        size = min(remainingSamples / (pWavFileHeader->bitsPerSample / 8), 65535);
        SSC_WriteBuffer((void *) ((uint32_t)wavFileData + sizeof(WavHeader) + transmittedSamples), size);
        remainingSamples -= size * (pWavFileHeader->bitsPerSample / 8);
        transmittedSamples += size * (pWavFileHeader->bitsPerSample / 8);
    }
    /* One buffer sent, no more buffers */
    else
    {
        SSC_DisableInterrupts(SSC_IDR_ENDTX);
    }
}

/**
 * \brief Application entry point.
 *
 * \return Unused (ANSI-C compatibility).
 */
int main(void)
{
    uint8_t key;
    uint8_t isValid;

    /* Disable watchdog */
    WDT_Disable( WDT ) ;

    /* Output example information */
    printf("\n\r\n\r\n\r");
    printf("-- SSC with PDC Audio Example %s --\n\r", SOFTPACK_VERSION);
    printf("-- %s\n\r", BOARD_NAME);
    printf("-- Compiled: %s %s --\n\r", __DATE__, __TIME__);

    /* Configure all pins */
    PIO_Configure(pins, PIO_LISTSIZE(pins));

    /* Disable Clock 0 Output */
    REG_PMC_SCDR = PMC_SCDR_PCK0;

    /*
     * Reconfigure PLL for MCLK of WM8731 which is 12.288MHz.
     */
    /* Switch to Main clock */
    PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK;
    while ((PMC->PMC_SR & PMC_SR_MCKRDY) == 0);

    /* Configure PLLB to 98.285MHz */
    PMC->CKGR_PLLBR = (CKGR_PLLBR_MULB(171) \
                     | CKGR_PLLBR_PLLBCOUNT(1) \
                     | CKGR_PLLBR_DIVB(21));
    while (!(PMC->PMC_SR & PMC_SR_LOCKB));

    /* Configure master clock in two operations */
    PMC->PMC_MCKR = (( PMC_MCKR_PRES_CLK_2 | PMC_MCKR_CSS_PLLB_CLK) & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS_MAIN_CLK;
    while ((PMC->PMC_SR & PMC_SR_MCKRDY) == 0);
    PMC->PMC_MCKR = ( PMC_MCKR_PRES_CLK_2 | PMC_MCKR_CSS_PLLB_CLK);
    while ((PMC->PMC_SR & PMC_SR_MCKRDY) == 0);

    /* Reconfiguration UART */
    UART_Configure(115200, SSC_MCK);

    /* Enable TWI peripheral clock */
    PMC_EnablePeripheral(ID_TWI0);
    /* Configure and enable the TWI (required for accessing the WM8731) */
    TWI_ConfigureMaster(TWI0, TWI_CLOCK, SSC_MCK);
    TWID_Initialize(&twid, TWI0);

    /* Initialize the audio DAC */
    WM8731_DAC_Init(&twid, WM8731_SLAVE_ADDRESS);

    /* Enable the WM8731 master clock */
    REG_PMC_PCK = PMC_MCKR_CSS_PLLB_CLK | PMC_MCKR_PRES_CLK_8;
    /* Enable Clock 0 Output */
    REG_PMC_SCER = PMC_SCER_PCK0;
    /* Wait for the PCKRDYx bit to be set in the PMC_SR register*/
    while ((REG_PMC_SR & PMC_SR_PCKRDY0) == 0);

    /* Configure SSC*/
    SSC_Configure(SAMPLE_RATE * BITS_BY_SLOT * 2, SSC_MCK);
    SSC_ConfigureReceiver(0, 0);
    SSC_ConfigureTransmitter( I2S_MASTER_TX_SETTING(BITS_BY_SLOT, SLOT_BY_FRAME),
                              I2S_TX_FRAME_SETTING(BITS_BY_SLOT, SLOT_BY_FRAME) );
    SSC_DisableTransmitter();

    /* Configure SSC interrupts */
    NVIC_DisableIRQ(SSC_IRQn);
    NVIC_ClearPendingIRQ(SSC_IRQn);
    NVIC_SetPriority(SSC_IRQn, 0);
    NVIC_EnableIRQ(SSC_IRQn);

    /* Send a sample WAV file */
    while (1)
    {
        isValid = WAV_IsValid(pWavFileHeader);
        if (isValid)
        {
            break;
        }
        else
        {
            printf("Please send a sample wav file using Xmodem protocol\n\r");
            printf("The sample wav file size must small than %d bytes!\n\r", (int)MAX_WAV_SIZE);
            XMODEM_ReceiveFile((int8_t *)wavFileData);
        }
    }
    isWavPlaying = 0;

    /* Check Sample rate */
    printf("-I- Sample rate = %u Hz\n\r", pWavFileHeader->sampleRate);
    assert( pWavFileHeader->sampleRate == SAMPLE_RATE ) ;

    /* Enter menu loop */
    while (1)
    {

        /* Display menu */
        DisplayMenu();

        /* Process user input */
        key = UART_GetChar();

        /* Play WAV file */
        if ((key == 'W') && !isWavPlaying)
        {
            PlayWavFile();
            isWavPlaying = 1;
        }
        /* Display WAV information */
        else if (key == 'I')
        {
            DisplayWavInfo();
        }
        /* Stop playback*/
        else if ((key == 'S') && isWavPlaying)
        {
            StopPlayback();
            isWavPlaying = 0;
        }
    }
}

