/**
 * \file
 *
 * \brief Synchronous Serial Controller (SSC) driver for SAM.
 *
 * Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * 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
 * EXPRESSLY AND SPECIFICALLY 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.
 *
 * \asf_license_stop
 *
 */

#ifndef SSC_H_INCLUDED
#define SSC_H_INCLUDED

#include "../chip.h"

/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
extern "C" {
#endif
/**INDENT-ON**/
/// @endcond

//! Receive stop selection.
#define SSC_RX_STOP_COMPARE_0        0
#define SSC_RX_STOP_COMPARE_0_1      1

//! Compare register ID.
#define COMPARE_ID0                  0
#define COMPARE_ID1                  1

//! SSC module default timeout. */
#define SSC_DEFAULT_TIMEOUT          10000

//! \brief SSC driver return codes.
enum ssc_return_code {
	SSC_RC_OK = 0,              //!< OK
	SSC_RC_YES = 0,             //!< Yes
	SSC_RC_NO = 1,              //!< No
	SSC_RC_ERROR = 1,           //!< General error
	SSC_RC_INVALID = 0xFFFFFFFF //!< Parameter invalid
};

//! Data frame structure.
typedef struct {
	//! Data bits length per transfer, should be 0 to 31.
	uint32_t ul_datlen;
	//! Bit sequence LSBF or MSBF.
	//! For receiver configuration, SSC_RFMR_MSBF or 0.
	//! For transmitter configuration, SSC_TFMR_MSBF or 0.
	uint32_t ul_msbf;
	//! Data number per frame, should be 0 to 15.
	uint32_t ul_datnb;
	//! Frame Sync. length should be 0 to 15.
	uint32_t ul_fslen;
	//! Frame Sync. length extension field, should be 0 to 15.
	uint32_t ul_fslen_ext;
	//! Frame Sync. output selection.
	//! For receiver configuration, one of SSC_RFMR_FSOS_NONE, SSC_RFMR_FSOS_NEGATIVE, SSC_RFMR_FSOS_POSITIVE,
	//! SSC_RFMR_FSOS_LOW, SSC_RFMR_FSOS_HIGH or SSC_RFMR_FSOS_TOGGLING.
	//! For transmitter configuration, one of SSC_TFMR_FSOS_NONE, SSC_TFMR_FSOS_NEGATIVE, SSC_TFMR_FSOS_POSITIVE
	//! SSC_TFMR_FSOS_LOW, SSC_TFMR_FSOS_HIGH, SSC_TFMR_FSOS_TOGGLING,
	uint32_t ul_fsos;
	//! Frame Sync. edge detection.
	//! For receiver configuration, SSC_RFMR_FSEDGE_POSITIVE or SSC_RFMR_FSEDGE_NEGATIVE.
	//! For transmitter configuration, SSC_TFMR_FSEDGE_POSITIVE or SSC_TFMR_FSEDGE_NEGATIVE.
	uint32_t ul_fsedge;
} data_frame_opt_t;

//! Clock mode structure.
typedef struct {
	//! Communication clock selection.
	//! For receiver configuration, one of SSC_RCMR_CKS_MCK, SSC_RCMR_CKS_TK or SSC_RCMR_CKS_RK.
	//! For transmitter configuration, one of SSC_TCMR_CKS_MCK, SSC_TCMR_CKS_TK or SSC_TCMR_CKS_RK.
	uint32_t ul_cks;
	//! Communication clock output mode selection.
	//! For receiver configuration, one of SSC_RCMR_CKO_NONE, SSC_RCMR_CKO_CONTINUOUS or SSC_RCMR_CKO_TRANSFER.
	//! For transmitter configuration, one of SSC_TCMR_CKO_NONE, SSC_TCMR_CKO_CONTINUOUS or SSC_TCMR_CKO_TRANSFER.
	uint32_t ul_cko;
	//! Communication clock inversion.
	//! For receiver configuration, SSC_RCMR_CKI or 0.
	//! For transmitter configuration, SSC_TCMR_CKI or 0.
	uint32_t ul_cki;
	//! Communication clock gating selection.
	//! For receiver configuration, one of SSC_RCMR_CKG_NONE, SSC_RCMR_CKG_CONTINUOUS and SSC_RCMR_CKG_TRANSFER.
	//! For transmitter configuration, one of SSC_TCMR_CKG_NONE, SSC_TCMR_CKG_CONTINUOUS and SSC_TCMR_CKG_TRANSFER.
	uint32_t ul_ckg;
	//! Period divider selection, should be 0 to 255.
	uint32_t ul_period;
	//! Communication start delay, should be 0 to 255.
	uint32_t ul_sttdly;
	//! Communication start selection.
	//! For receiver configuration, one of SSC_RCMR_START_CONTINUOUS, SSC_RCMR_START_TRANSMIT, SSC_RCMR_START_RF_LOW,
	//! SSC_RCMR_START_RF_HIGH, SSC_RCMR_START_RF_FALLING, SSC_RCMR_START_RF_RISING, SSC_RCMR_START_RF_LEVEL,
	//! SSC_RCMR_START_RF_EDGE or SSC_RCMR_START_CMP_0.
	//! For transmitter configuration, one of SSC_TCMR_START_CONTINUOUS, SSC_TCMR_START_TRANSMIT, SSC_TCMR_START_RF_LOW,
	//! SSC_TCMR_START_RF_HIGH, SSC_TCMR_START_RF_FALLING, SSC_TCMR_START_RF_RISING, SSC_TCMR_START_RF_LEVEL,
	//! SSC_TCMR_START_RF_EDGE or SSC_TCMR_START_CMP_0.
	uint32_t ul_start_sel;
} clock_opt_t;

//! SSC working role in I2S mode.
#define SSC_I2S_MASTER_OUT    (1 << 0) //! Working mode for transmitter as master.
#define SSC_I2S_MASTER_IN     (1 << 1) //! Working mode for receiver as master.
#define SSC_I2S_SLAVE_OUT     (1 << 2) //! Working mode for transmitter as slave.
#define SSC_I2S_SLAVE_IN      (1 << 3) //! Working mode for receiver as slave.

//! Bit for SSC Audio channel left.
#define SSC_AUDIO_CH_LEFT     (1 << 0)
//! Bit for SSC Audio channel right.
#define SSC_AUDIO_CH_RIGHT    (1 << 1)
//! SSC Audio Channel modes.
enum {
	//! Mono, left channel enabled.
	SSC_AUDIO_MONO_LEFT = (SSC_AUDIO_CH_LEFT),
	//! Mono, right channel enabled.
	SSC_AUDIO_MONO_RIGHT = (SSC_AUDIO_CH_RIGHT),
	//! Stereo, two channels.
	SSC_AUDIO_STERO = (SSC_AUDIO_CH_LEFT | SSC_AUDIO_CH_RIGHT)
};

uint32_t ssc_set_clock_divider(Ssc *p_ssc, uint32_t ul_bitclock, uint32_t ul_mck);
void ssc_i2s_set_transmitter(Ssc *p_ssc, uint32_t ul_mode,
		uint32_t ul_cks, uint32_t ul_ch_mode, uint32_t ul_datlen);
void ssc_i2s_set_receiver(Ssc *p_ssc, uint32_t ul_mode,
		uint32_t ul_cks, uint32_t ul_ch_mode, uint32_t ul_datlen);
void ssc_reset(Ssc *p_ssc);
void ssc_enable_rx(Ssc *p_ssc);
void ssc_disable_rx(Ssc *p_ssc);
void ssc_enable_tx(Ssc *p_ssc);
void ssc_disable_tx(Ssc *p_ssc);
void ssc_set_normal_mode(Ssc *p_ssc);
void ssc_set_loop_mode(Ssc *p_ssc);
void ssc_set_rx_stop_selection(Ssc *p_ssc, uint32_t ul_sel);
void ssc_set_td_default_level(Ssc *p_ssc, uint32_t ul_level);
void ssc_enable_tx_frame_sync_data(Ssc *p_ssc);
void ssc_disable_tx_frame_sync_data(Ssc *p_ssc);
void ssc_set_receiver(Ssc *p_ssc, clock_opt_t *p_rx_clk_opt, data_frame_opt_t *p_rx_data_frame);
void ssc_set_transmitter(Ssc *p_ssc, clock_opt_t *p_tx_clk_opt, data_frame_opt_t *p_tx_data_frame);
void ssc_set_rx_compare(Ssc *p_ssc, uint32_t ul_id, uint32_t ul_value);
uint32_t ssc_get_rx_compare(Ssc *p_ssc, uint32_t ul_id);
void ssc_enable_interrupt(Ssc *p_ssc, uint32_t ul_sources);
void ssc_disable_interrupt(Ssc *p_ssc, uint32_t ul_sources);
uint32_t ssc_get_interrupt_mask(Ssc *p_ssc);
uint32_t ssc_get_status(Ssc *p_ssc);
uint32_t ssc_is_tx_ready(Ssc *p_ssc);
uint32_t ssc_is_tx_empty(Ssc *p_ssc);
uint32_t ssc_is_rx_ready(Ssc *p_ssc);
uint32_t ssc_is_tx_enabled(Ssc *p_ssc);
uint32_t ssc_is_rx_enabled(Ssc *p_ssc);
#if (defined _SAM3S_) || (defined _SAM4S_)
uint32_t ssc_is_rx_buf_end(Ssc *p_ssc);
uint32_t ssc_is_tx_buf_end(Ssc *p_ssc);
uint32_t ssc_is_rx_buf_full(Ssc *p_ssc);
uint32_t ssc_is_tx_buf_empty(Ssc *p_ssc);
Pdc *ssc_get_pdc_base(Ssc *p_ssc);
#endif
uint32_t ssc_write(Ssc *p_ssc, uint32_t ul_frame);
uint32_t ssc_read(Ssc *p_ssc, uint32_t *ul_data);
void ssc_write_sync_data(Ssc *p_ssc, uint32_t ul_frame);
uint32_t ssc_read_sync_data(Ssc *p_ssc);
#if ((defined _SAM3XA_) || (defined _SAM3U_))
void *ssc_get_tx_access(Ssc *p_ssc);
void *ssc_get_rx_access(Ssc *p_ssc);
#endif
void ssc_set_writeprotect(Ssc *p_ssc, uint32_t ul_enable);
uint32_t ssc_get_writeprotect_status(Ssc *p_ssc);

/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
}
#endif
/**INDENT-ON**/
/// @endcond

#endif /* SSC_H_INCLUDED */