
//------------------------------------------------------------------------------
// Copyright (c) 2012 by Silicon Laboratories. 
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Silicon Laboratories End User 
// License Agreement which accompanies this distribution, and is available at
// http://developer.silabs.com/legal/version/v10/License_Agreement_v10.htm
// Original content and implementation provided by Silicon Laboratories.
//------------------------------------------------------------------------------
// library
#include <si32McuComponent.h>
// hal
#include <si32_device.h>
// application
#include "myApplication.h"
#include "myI2S0.h"

//------------------------------------------------------------------------------
si32IisAPortalObject _my_iis0_portal = si32IisAPortalObject();
si32IisAPortalObject* my_iis0_portal = &_my_iis0_portal;

//------------------------------------------------------------------------------
void myI2S0_initialize_memory()
{
  si32LogPrologue();
  
  si32IisAPortalObject_initialize(my_iis0_portal,
                                  SI32_PORTAL_OBJECT_CAPABILITY_FULL_DUPLEX
                                  |SI32_PORTAL_OBJECT_CAPABILITY_TRANSMIT
                                  |SI32_PORTAL_OBJECT_CAPABILITY_RECEIVE,
                                  SI32_I2S_0);
  
  si32IisAPortalObject_set_configuration(my_iis0_portal,
                                         SI32_PORTAL_OBJECT_CONFIGURATION_FULL_DUPLEX
                                         |SI32_PORTAL_OBJECT_CONFIGURATION_TRANSMIT
                                         |SI32_PORTAL_OBJECT_CONFIGURATION_RECEIVE);
  
  si32LogEpilogue();
}

//------------------------------------------------------------------------------
void si32IisAPortalObject_interleaved_tx32_rx16_isr(
  obj* self 
  )
{
  si32Assert(is_si32IisAPortalObject(self));
  si32IisAPortalObject* my = self;

  bool txing = NO;
  bool rxing = NO;
  bool txd = NO;
  bool rxd = NO;

  txing = SI32_I2S_A_is_tx_fifo_watermark_interrupt_enabled(my->iisAPortalObject.module)
    && SI32_I2S_A_is_tx_fifo_watermark_interrupt_pending(my->iisAPortalObject.module);

  rxing = SI32_I2S_A_is_rx_fifo_watermark_interrupt_enabled(my->iisAPortalObject.module)
    && SI32_I2S_A_is_rx_fifo_watermark_interrupt_pending(my->iisAPortalObject.module);

  if (txing || rxing)
  {
    uint32* txaddr = (uint32*)&my->portalObject.tx_data[my->portalObject.tx_actual];
    uint16* rxaddr = (uint16*)&my->portalObject.rx_data[my->portalObject.rx_actual];  
    while (txing || rxing)
    {
      txing = txing 
        && (SI32_I2S_A_get_tx_fifo_count(my->iisAPortalObject.module) < 8) 
        && (my->portalObject.tx_actual < my->portalObject.tx_desired);
      if (txing)
      {
        probeHi(1);
        SI32_I2S_A_write_tx_fifo_u32(my->iisAPortalObject.module, 
                                     *txaddr);
        my->portalObject.tx_actual += 4;
        txaddr++;
        txd = YES;
        probeLo(1);
      }   
      rxing = rxing
        && SI32_I2S_A_get_rx_fifo_count(my->iisAPortalObject.module) 
        && (my->portalObject.rx_actual < my->portalObject.rx_desired);
      if (rxing)
      {
        probeHi(3);
        *rxaddr = (uint16)SI32_I2S_A_read_rx_fifo_u32(my->iisAPortalObject.module);
        my->portalObject.rx_actual += 2;
        rxaddr++;
        rxd = YES;
        probeLo(3);
      }
    }
    if (txd && (my->portalObject.tx_actual >= my->portalObject.tx_desired))
    {
      probeHi(2);
      // disable the transmitter.
      SI32_I2S_A_disable_tx_fifo_watermark_interrupt(my->iisAPortalObject.module);
      //SI32_I2S_A_disable_tx(my->iisAPortalObject.module);
      
      // complete.    
      my->portalObject.status_mask &= ~SI32_PORTAL_OBJECT_STATUS_TRANSMITTING;
      my->portalObject.tx_result = SI32_PORTAL_OBJECT_RESULT_SUCCESS;
    
      // make the callback at interrupt dispatch level.
      my->portalObject.tx_callback(// result
                                   my->portalObject.tx_result,
                                   // actual
                                   my->portalObject.tx_actual,
                                   // context0
                                   my->portalObject.tx_context0,
                                   // context1
                                   my->portalObject.tx_context1);
      probeLo(2);
    }             
    if (rxd && (my->portalObject.rx_actual >= my->portalObject.rx_desired))
    {
      probeHi(4);
      // disable the receiver.
      SI32_I2S_A_disable_rx_fifo_watermark_interrupt(my->iisAPortalObject.module);
      //SI32_I2S_A_disable_rx(my->iisAPortalObject.module);
      
      // complete.    
      my->portalObject.status_mask &= ~SI32_PORTAL_OBJECT_STATUS_RECEIVING;
      my->portalObject.rx_result = SI32_PORTAL_OBJECT_RESULT_SUCCESS;


      // make the callback at interrupt dispatch level.
      my->portalObject.rx_callback(// result
                                   my->portalObject.rx_result,
                                   // actual
                                   my->portalObject.rx_actual,
                                   // context0
                                   my->portalObject.rx_context0,
                                   // context1
                                   my->portalObject.rx_context1);
      probeLo(4);
    }   
              
    #if 1
    // ensure no tx underrun is occurring.  expect to see one at initial startup, or when muted.
    static uint32 underrun = 0;
    if (SI32_I2S_A_is_tx_fifo_underflow_interrupt_pending(my->iisAPortalObject.module))
    {
      SI32_I2S_A_clear_tx_fifo_underflow_interrupt(my->iisAPortalObject.module);   
      underrun++;
    }
    //si32Assert(underrun < 20);
    #endif

    #if 1
    // ensure no rx overrun is occurring.  expect to see one at end of stream.
    static uint32 overrun = 0;
    if (SI32_I2S_A_is_rx_fifo_overflow_interrupt_pending(my->iisAPortalObject.module))
    {
      SI32_I2S_A_clear_rx_fifo_overflow_interrupt(my->iisAPortalObject.module);   
      overrun++;
    }
    //si32Assert(overrun < 20);
    #endif
  }
}

//------------------------------------------------------------------------------
static void si32IisAPortalObject_sequential_tx32_rx16_isr(
  obj* self 
  )
{
  si32Assert(is_si32IisAPortalObject(self));
  si32IisAPortalObject* my = self;

  if (SI32_I2S_A_is_tx_fifo_watermark_interrupt_enabled(my->iisAPortalObject.module)
   && SI32_I2S_A_is_tx_fifo_watermark_interrupt_pending(my->iisAPortalObject.module))
  {
    #if 1
    // ensure no tx underrun is occurring.  expect to see one at initial startup, or when muted.
    static uint32 underrun = 0;
    if (SI32_I2S_A_is_tx_fifo_underflow_interrupt_pending(my->iisAPortalObject.module))
    {
      SI32_I2S_A_clear_tx_fifo_underflow_interrupt(my->iisAPortalObject.module);   
      underrun++;
    }
    //si32Assert(underrun < 20);
    #endif

    // fill the fifo.  it is 8x32bit words deep.
    uint32* addr = (uint32*)&my->portalObject.tx_data[my->portalObject.tx_actual];
    while ((SI32_I2S_A_get_tx_fifo_count(my->iisAPortalObject.module) < 8) 
        && (my->portalObject.tx_actual < my->portalObject.tx_desired))
    {
      probeHi(1);
      SI32_I2S_A_write_tx_fifo_u32(my->iisAPortalObject.module, 
                                   *addr);
      my->portalObject.tx_actual += 4;
      addr++;
      probeLo(1);
    }   
    if (my->portalObject.tx_actual >= my->portalObject.tx_desired)
    {
      probeHi(2);
      // disable the transmitter.
      SI32_I2S_A_disable_tx_fifo_watermark_interrupt(my->iisAPortalObject.module);
      //SI32_I2S_A_disable_tx(my->iisAPortalObject.module);
      
      // complete.    
      my->portalObject.status_mask &= ~SI32_PORTAL_OBJECT_STATUS_TRANSMITTING;
      my->portalObject.tx_result = SI32_PORTAL_OBJECT_RESULT_SUCCESS;
    
      // make the callback at interrupt dispatch level.
      my->portalObject.tx_callback(// result
                                   my->portalObject.tx_result,
                                   // actual
                                   my->portalObject.tx_actual,
                                   // context0
                                   my->portalObject.tx_context0,
                                   // context1
                                   my->portalObject.tx_context1);
      probeLo(2);
    }             
  } 
  if (SI32_I2S_A_is_rx_fifo_watermark_interrupt_enabled(my->iisAPortalObject.module)
   && SI32_I2S_A_is_rx_fifo_watermark_interrupt_pending(my->iisAPortalObject.module))
  {
    #if 1
    // ensure no rx overrun is occurring.  expect to see one at end of stream.
    static uint32 overrun = 0;
    if (SI32_I2S_A_is_rx_fifo_overflow_interrupt_pending(my->iisAPortalObject.module))
    {
      SI32_I2S_A_clear_rx_fifo_overflow_interrupt(my->iisAPortalObject.module);   
      overrun++;
    }
    //si32Assert(overrun < 20);
    #endif

    // drain the fifo.
    uint16* addr = (uint16*)&my->portalObject.rx_data[my->portalObject.rx_actual];  
    while (SI32_I2S_A_get_rx_fifo_count(my->iisAPortalObject.module) 
        && (my->portalObject.rx_actual < my->portalObject.rx_desired))
    {
      probeHi(3);
      uint32 word = SI32_I2S_A_read_rx_fifo_u32(my->iisAPortalObject.module);
      *addr = (uint16)word;
      my->portalObject.rx_actual += 2;
      addr++;
      probeLo(3);
    }
    if (my->portalObject.rx_actual >= my->portalObject.rx_desired)
    {
      probeHi(4);
      // disable the receiver.
      SI32_I2S_A_disable_rx_fifo_watermark_interrupt(my->iisAPortalObject.module);
      //SI32_I2S_A_disable_rx(my->iisAPortalObject.module);
      
      // complete.    
      my->portalObject.status_mask &= ~SI32_PORTAL_OBJECT_STATUS_RECEIVING;
      my->portalObject.rx_result = SI32_PORTAL_OBJECT_RESULT_SUCCESS;

      // make the callback at interrupt dispatch level.
      my->portalObject.rx_callback(// result
                                   my->portalObject.rx_result,
                                   // actual
                                   my->portalObject.rx_actual,
                                   // context0
                                   my->portalObject.rx_context0,
                                   // context1
                                   my->portalObject.rx_context1);
      probeLo(4);
    }             
  } 
}

//------------------------------------------------------------------------------
void I2S0TX_IRQHandler(void)
{
  //si32IisAPortalObject_isr(my_iis0_portal);
  si32IisAPortalObject_sequential_tx32_rx16_isr(my_iis0_portal);
  //si32IisAPortalObject_interleaved_tx32_rx16_isr(my_iis0_portal);
}

//------------------------------------------------------------------------------
void I2S0RX_IRQHandler(void)
{
  //si32IisAPortalObject_isr(my_iis0_portal);
  si32IisAPortalObject_sequential_tx32_rx16_isr(my_iis0_portal);
  //si32IisAPortalObject_interleaved_tx32_rx16_isr(my_iis0_portal);
}

//------------------------------------------------------------------------------
void myI2S0_enter_default_mode()
{
  // TBD
}

//------------------------------------------------------------------------------
void myI2S0_enter_active_mode()
{
  // Need 34 clocks per sample (16bit + 1 delay)
  // Bump to 40 clocks yields a bitclock of 1.92Mhz
  // 48Khz LR = 1.92Mhz bitclock = 12.288Mhz codec clock
  // Fiis = Fapb / (INTDIV + (FRACDIV / 256))
  // 1.92Mhz = 48Mhz /25
  // INTDIV = 25
  // FRACKDIV = 0
  SI32_I2S_A_set_clock_divider(SI32_I2S_0, /*int*/25, /*frac*/0);   
  SI32_I2S_A_enable_clock_divider(SI32_I2S_0);
  SI32_I2S_A_update_clock_divider(SI32_I2S_0);
  while (SI32_I2S_0->CLKCONTROL.CLKUPD);
 
  SI32_I2S_A_select_tx_frame_sync_as_internal_generator(SI32_I2S_0);
  SI32_I2S_A_select_tx_clock_source_as_divider_out(SI32_I2S_0);
  SI32_I2S_A_select_tx_dclk_pin_as_output(SI32_I2S_0);
  SI32_I2S_A_start_tx_clock(SI32_I2S_0);

  SI32_I2S_A_select_tx_left_justified_data(SI32_I2S_0);
  SI32_I2S_A_select_tx_data_nth_rising_edge(SI32_I2S_0, 2);
  SI32_I2S_A_select_tx_16bits_per_mono_sample(SI32_I2S_0);
  SI32_I2S_A_select_tx_unused_data_fill_zeros(SI32_I2S_0);
  SI32_I2S_A_enable_tx(SI32_I2S_0);

  SI32_I2S_A_select_rx_frame_sync_as_external_pin(SI32_I2S_0);
  SI32_I2S_A_select_rx_clock_source_as_pin_in(SI32_I2S_0);
  SI32_I2S_A_select_rx_dclk_pin_as_input(SI32_I2S_0);
  SI32_I2S_A_start_rx_clock(SI32_I2S_0);

  SI32_I2S_A_select_rx_left_justified_data(SI32_I2S_0);
  SI32_I2S_A_select_rx_data_nth_rising_edge(SI32_I2S_0, 2);
  SI32_I2S_A_select_rx_16bits_per_mono_sample(SI32_I2S_0);
  SI32_I2S_A_enable_rx(SI32_I2S_0);

  SI32_I2S_A_set_frame_sync_duty_cycle(SI32_I2S_0,
                                       20, 
                                       20);
  SI32_I2S_A_enable_frame_sync(SI32_I2S_0);
 
  SI32_I2S_A_set_fifo_watermarks(SI32_I2S_0, 
                                 7, 
                                 1);

  NVIC_ClearPendingIRQ(I2S0RX_IRQn);
  NVIC_EnableIRQ(I2S0RX_IRQn);
  NVIC_ClearPendingIRQ(I2S0TX_IRQn);
  NVIC_EnableIRQ(I2S0TX_IRQn);
}

//---eof------------------------------------------------------------------------
