/*
 * Copyright (c) 2016, Texas Instruments Incorporated
 * 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 following disclaimer.
 *
 * *  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.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 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 OWNER 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.
 *
 */

/***
 * \file : aic3104_headphone_out.c
 *
 * \brief: Routines to initialize the aic3104 Audio codec for Headphone output
 *
 * \b Desc:  This file contains the routines for configuring the aic3104 Audio Codec for
 * \n        driving the sine tone output to the Head-phones.
 *
 */

/*
 *====================
 * Includes
 *====================
 */
#include "dm8127_McASP.h"
#include "dm8127_platform.h"
#include "dm8127_i2c.h"
#include "aic3104.h"
#include <stdio.h>

/*
 *====================
 * Defines
 *====================
 */
#define   aic3104_HP_TEST_DURATION	(5u)
/**<Duration (in seconds) for which the test has to be run */
 
/*
 *====================
 * Static variables
 *====================
 */ 

 /* Pre-generated sine wave data, 16-bit signed samples */
//static SINT16 sinetable[48] = {
    SINT16 sinetable[48] = {
    0x0000, 0x10b4, 0x2120, 0x30fb, 0x3fff, 0x4dea, 0x5a81, 0x658b,
    0x6ed8, 0x763f, 0x7ba1, 0x7ee5, 0x7ffd, 0x7ee5, 0x7ba1, 0x763f,
    0x6ed8, 0x658b, 0x5a81, 0x4dea, 0x3fff, 0x30fb, 0x2120, 0x10b4,
    0x0000, 0xef4c, 0xdee0, 0xcf06, 0xc002, 0xb216, 0xa57f, 0x9a75,
    0x9128, 0x89c1, 0x845f, 0x811b, 0x8002, 0x811b, 0x845f, 0x89c1,
    0x9128, 0x9a76, 0xa57f, 0xb216, 0xc002, 0xcf06, 0xdee0, 0xef4c
};
 
/*
 *=========================
 * Function implementations
 *========================
 */
/***
 * \brief: Routine to configure the Headphone Out and play sine tone
 *
 * \b Descr: This routine initializes the aic3104 Audio Codec on the
 * \n       base board and plays a sine tone on the head-phone.
 *
 * \param : u8McaspNum	[IN]	Instance of McASP number
 *
 * \return:  SUCCESS  on success
 * \n        FAILED   if any error
 */

STATUS aic3104_headphone_out
(
	UINT8    u8McaspNum
)
{
	STATUS u32RetVal = SUCCESS;
	DM8127_MCASP_REGS stMcAspRegs;
	UINT16 u16Sample = 0;
	UINT16 u16MilliSec = 0;
	UINT16 u16Seconds = 0;
	
	if (u8McaspNum == 2)
		stMcAspRegs = (DM8127_MCASP_REGS) DM8127_EVM_aic3104_MCASP;

    /* Configure aic3104 */
	/* change the register Value internal into the function aic3104RegSet */

    u32RetVal = aic3104RegSet(  aic3104_PAGESELECT, 0 );       // Select page 0
    if(u32RetVal != SUCCESS)
    {
#ifdef DEBUG
    	printf("setting aic3104Reg to select page0 failed\n");
#else
    	Uart_stringSend("setting aic3104Reg to select page0 failed\r\n");
#endif
    	return -1;
    }
    u32RetVal = aic3104RegSet(  aic3104_RESET, 0x80 );         // Reset aic3104
    /* ------------------------------------------------------------------------ *
     *                                                                          *
     *  aic3104 Setup                                                           *
     *                                                                          *
     *      aic3104.MCLK = PLL1705.SCK02                                        *
     *      FS = ( aic3104.MCLK * K ) / ( 2048 * P )                            *
     *                                                                          *
     *      For a FS=[48 kHz] & MCLK=[24.576 MHz]                               *
     *          : 48kHz = ( 24.576 MHz * K ) / ( 2048 * P )                     *
     *          : P = 2, K[J.D] = 8.7075                                        *
     *                                                                          *
     * ------------------------------------------------------------------------ */
     /* Configure aic3104 registers */
      aic3104RegSet(  3, 0x22 );  // 5 PLL A                            <- [PLL=OFF][Q=4][P=2]
      aic3104RegSet(  4, 0x20 );  // 4 PLL B                            <- [J=8]
      aic3104RegSet(  5, 0x6E );  // 5 PLL C                            <- [D=7075]
      aic3104RegSet(  6, 0x23 );  // 6 PLL D                            <- [D=7075]
      aic3104RegSet(  7, 0x0A );  // 7 Codec Datapath Setup             <- [FS=48 kHz][LeftDAC=LEFT][RightDAC=RIGHT]
      aic3104RegSet(  8, 0xC0 );  // 8  Audio Interface Control A       <- [BCLK=Slave][MCLK=Slave]
      aic3104RegSet(  9, 0x00 );  // 9  Audio Interface Control B       <- [I2S mode][16 bit]
      aic3104RegSet(  10, 0x00);  // 10 Audio Interface Control C       <- [Data offset=0]
      aic3104RegSet(  15, 0x17 ); // 15  Left ADC PGA Gain              <- [Mute=OFF][Gain=8.5dB]
      aic3104RegSet(  16, 0x17 ); // 16 Right ADC PGA Gain              <- [Mute=OFF][Gain=8.5dB]
      //aic3104RegSet(  17, 0x0F ); // 17 MIC3L/R to  Left ADC            <- [MIC3L=0dBGain][MIC3R=NotConnect]
      //aic3104RegSet(  18, 0xF0 ); // 18 MIC3L/R to Right ADC            <- [MIC3L=NotConnect][MIC3R=0dBGain]
      aic3104RegSet(  17, 0 ); // 17 MIC3L/R to  Left ADC            		<- 	Input level control gain = 0 dB
      aic3104RegSet(  18, 0 ); // 18 MIC3L/R to Right ADC            		<- 	Input level control gain = 0 dB
      aic3104RegSet(  19, 0x7C ); // 19  LINE1L to  Left ADC            <- [SingleEnd][NotConnect][Power=ON][SoftStep=OncePerFS]
      aic3104RegSet(  22, 0x7C ); // 22  LINE1R to Right ADC            <- [SingleEnd][NotConnect][Power=ON][SoftStep=OncePerFS]
      aic3104RegSet(  25, 0x40 ); // 25 MICBIAS                         <- [MICBIAS=2.0V]
      aic3104RegSet(  27, 0 );    // 27  Left AGC B                     <- [OFF]
      aic3104RegSet(  30, 0 );    // 30 Right AGC B                     <- [OFF]
      aic3104RegSet(  37, 0xE0 ); // 37 DAC Power & Output Dvr          <- [LeftDAC=ON][RightDAC=ON][HPLCOM=SingleEnd]
      aic3104RegSet(  38, 0x10 ); // 38 High Power Output Dvr           <- [HPRCOM=SingleEnd][ShortCircuit=OFF]
      aic3104RegSet(  43, 0x50 );    // 43  Left DAC Digital Volume        <- [Mute=OFF][Gain=0dB]
      aic3104RegSet(  44, 0x50 );    // 44 Right DAC Digital Volume        <- [Mute=OFF][Gain=0dB]
      aic3104RegSet(  47, 0x80 ); // 47 DAC_L1 to HPLOUT Volume         <- [Routed]
      aic3104RegSet(  51, 0x09 ); // 51           HPLOUT Output         <- [Mute=OFF][Power=ON]
      aic3104RegSet(  58, 0 );    // 58           HPLCOM Output         <- []
      aic3104RegSet(  64, 0x80 ); // 64 DAC_R1 to HPROUT Volume         <- [Routed]
      aic3104RegSet(  65, 0x09 ); // 65           HPROUT Output         <- [Mute=OFF][Power=ON]
      aic3104RegSet(  72, 0 );    // 72           HPRCOM Output         <- []
      aic3104RegSet(  82, 0x80 ); // 82 DAC_L1 to LEFT_LOP/M Volume     <- [Routed]
      aic3104RegSet(  86, 0x09 ); // 86           LEFT_LOP/M Output     <- [Mute=OFF][Power=ON]
      aic3104RegSet(  92, 0x80 ); // 92 DAC_R1 to RIGHT_LOP/M Volume    <- [Routed]
      aic3104RegSet(  93, 0x09 ); // 93           RIGHT_LOP/M Output    <- [Mute=OFF][Power=ON]
      aic3104RegSet( 101, 0x01 ); // 101 GPIO Control Register B        <- [CODEC_CLKIN = CLKDIV_OUT]
      aic3104RegSet( 102, 0 );    // 102 Clock Generation Control       <- [PLLCLK_IN and CLKDIV_IN use MCLK]

#ifdef DEBUG
      	printf("Codec config done\n");
#else
    	Uart_stringSend("\r\nCodec config done\r");
#endif


	/* Initialize MCASP2 */
    /* ---------------------------------------------------------------- *
     *                                                                  *
     *      McASP2 is in MASTER mode.                                   *
     *      BCLK & WCLK come from McASP2                                *
     *      DIN is used by write16/write32                              *
     *      DOUT is usec by read16/read32                               *
     *                                                                  *
     * ---------------------------------------------------------------- */
	stMcAspRegs->u32GBLCTL = 0;		// Reset the McASP
	*(UINT32 *)0x48050044 |= 0x400;
	stMcAspRegs->u32RGBLCTL = 0; 	// Reset the Rx section
	stMcAspRegs->u32XGBLCTL	= 0;	// Reset the Tx section
	delay(10000);
	/* Rx side initialization */
	stMcAspRegs->u32RMASK	   = 0xFFFFFFFF;	// No padding used
	stMcAspRegs->u32RFMT	   = 0x00018078; // MSB 16bit, 1-delay, no pad, CFGBus
	stMcAspRegs->u32AFSRCTL    = 0x00000112; // 2TDM, 1bit Rising, INTERNAL FS, word
	stMcAspRegs->u32ACLKRCTL   = 0x000000AF; // Rising INTERNAL CLK (from tx side)
    stMcAspRegs->u32AHCLKRCTL  = 0x00000000; // INT CLK (from tx side)
    stMcAspRegs->u32RTDM       = 0x00000003; // Slots 0,1
    stMcAspRegs->u32RINTCTL    = 0x00000000; // Not used
    stMcAspRegs->u32RCLKCHK    = 0x00FF0008; // 255-MAX 0-MIN, div-by-256

    /* TX */
    stMcAspRegs->u32XMASK      = 0xffffffff; // No padding used
    stMcAspRegs->u32XFMT       = 0x00018078; // MSB 16bit, 1-delay, no pad, CFGBus
    stMcAspRegs->u32AFSXCTL    = 0x00000112; // 2TDM, 1bit Rising edge INTERNAL FS, word
    stMcAspRegs->u32ACLKXCTL   = 0x00000080; // ASYNC, Rising INTERNAL CLK, div-by-16
    stMcAspRegs->u32AHCLKXCTL  = 0x00000000; // EXTERNAL CLK, div-by-1
    stMcAspRegs->u32XTDM       = 0x00000003; // Slots 0,1
    stMcAspRegs->u32XINTCTL    = 0x00000000; // Not used
    stMcAspRegs->u32XCLKCHK    = 0x00FF0008; // 255-MAX 0-MIN, div-by-256

    stMcAspRegs->u32SRCTL0     = 0x000D;     // MCASP2.AXR0 --> DIN
    stMcAspRegs->u32SRCTL1     = 0x000E;     // MCASP2.AXR1 <-- DOUT
    stMcAspRegs->u32PINFUNC      = 0;          // All MCASPs
    stMcAspRegs->u32PDIR       = 0x00000001; // All inputs except AXR0, ACLKX1, AFSX1,

    stMcAspRegs->u32DITCTL     = 0x00000000; // Not used
    stMcAspRegs->u32DLBCTL     = 0x00000000; // Not used
    stMcAspRegs->u32AMUTE      = 0x00000000; // Not used


    /* Starting sections of the McASP*/
    stMcAspRegs->u32XGBLCTL |= GBLCTL_XHCLKRST_ON;                                    // HS Clk
    while ( ( stMcAspRegs->u32XGBLCTL & GBLCTL_XHCLKRST_ON ) != GBLCTL_XHCLKRST_ON );  
    stMcAspRegs->u32RGBLCTL |= GBLCTL_RHCLKRST_ON;                                    // HS Clk
    while ( ( stMcAspRegs->u32RGBLCTL & GBLCTL_RHCLKRST_ON ) != GBLCTL_RHCLKRST_ON );
   
    stMcAspRegs->u32XGBLCTL |= GBLCTL_XCLKRST_ON;                                     // Clk
    while ( ( stMcAspRegs->u32XGBLCTL & GBLCTL_XCLKRST_ON ) != GBLCTL_XCLKRST_ON );
    stMcAspRegs->u32RGBLCTL |= GBLCTL_RCLKRST_ON;                                     // Clk
    while ( ( stMcAspRegs->u32RGBLCTL & GBLCTL_RCLKRST_ON ) != GBLCTL_RCLKRST_ON );

    stMcAspRegs->u32XSTAT = 0x0000ffff;        // Clear all
    stMcAspRegs->u32RSTAT = 0x0000ffff;        // Clear all

    stMcAspRegs->u32XGBLCTL |= GBLCTL_XSRCLR_ON;                                   // Serialize
    while ( ( stMcAspRegs->u32XGBLCTL & GBLCTL_XSRCLR_ON ) != GBLCTL_XSRCLR_ON );
    stMcAspRegs->u32RGBLCTL |= GBLCTL_RSRCLR_ON;                                      // Serialize
    while ( ( stMcAspRegs->u32RGBLCTL & GBLCTL_RSRCLR_ON ) != GBLCTL_RSRCLR_ON );

    /* Write a 0, so that no underrun occurs after releasing the state machine */
    stMcAspRegs->u32XBUF0 = 0;
    //MCASP2_RBUF0 = 0;

    stMcAspRegs->u32XGBLCTL |= GBLCTL_XSMRST_ON;                                       // State Machine
    while ( ( stMcAspRegs->u32XGBLCTL & GBLCTL_XSMRST_ON ) != GBLCTL_XSMRST_ON );
    stMcAspRegs->u32RGBLCTL |= GBLCTL_RSMRST_ON;                                       // State Machine
    while ( ( stMcAspRegs->u32RGBLCTL & GBLCTL_RSMRST_ON ) != GBLCTL_RSMRST_ON );

    stMcAspRegs->u32XGBLCTL |= GBLCTL_XFRST_ON;                                        // Frame Sync
    while ( ( stMcAspRegs->u32XGBLCTL & GBLCTL_XFRST_ON ) != GBLCTL_XFRST_ON );
    stMcAspRegs->u32RGBLCTL |= GBLCTL_RFRST_ON;                                        // Frame Sync
    while ( ( stMcAspRegs->u32RGBLCTL & GBLCTL_RFRST_ON ) != GBLCTL_RFRST_ON );

     /* Start by sending a dummy write */
    while( ! ( stMcAspRegs->u32SRCTL0 & 0x10 ) );  // Check for Tx ready
	stMcAspRegs->u32XBUF0 = 0;

    /* Play Tone */
    for ( u16Seconds = 0 ; u16Seconds < aic3104_HP_TEST_DURATION ; u16Seconds++ )
    {
        for ( u16MilliSec = 0 ; u16MilliSec < 1000 ; u16MilliSec++ )
        {
            for ( u16Sample = 0 ; u16Sample < 48 ; u16Sample++ )
            {
                /* Send a sample to the left channel */
                while ( ! ( stMcAspRegs->u32SRCTL0 & 0x10 ) );
                    stMcAspRegs->u32XBUF0 = (sinetable[u16Sample] << 16);
                /* Send a sample to the right channel */
                while ( ! ( stMcAspRegs->u32SRCTL0 & 0x10 ) );
                    stMcAspRegs->u32XBUF0 = (sinetable[u16Sample] << 16);
			}
        }
    }

    /* Close Codec */ 
    aic3104RegSet( aic3104_PAGESELECT, 0 ); // Select Page 0
    aic3104RegSet( aic3104_RESET, 0x80 );   // Reset the aic3104
    
    /* Close McASP */
    stMcAspRegs->u32SRCTL0 = 0; // Serializers
    stMcAspRegs->u32SRCTL1 = 0;
    stMcAspRegs->u32GBLCTL = 0;  // Global Reset
    *(UINT32 *)0X48050044 &= ~(1 << 10);
       *(UINT32 *)0X48050044 |= 0x400;
    delay(1500);

	return (u32RetVal);
}
