From: Ivan Date: Mon, 24 Apr 2017 18:44:42 +0000 (+0900) Subject: s5j/i2s: Add device driver for I2S X-Git-Tag: 1.1_Public_Release~331^2~33 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=759e54d9c37cfbf5d09bd05a7d7176f44aba3995;p=rtos%2Ftinyara.git s5j/i2s: Add device driver for I2S I2S supports only 48KHz Stereo playback. I2S supports both read and write channels. Change-Id: Iba94830758e5b90a56fe33e33333f3709d6e2823 Signed-off-by: Ivan Signed-off-by: Bongryul Lee Signed-off-by: Junhwan Park --- diff --git a/os/arch/arm/src/s5j/Kconfig b/os/arch/arm/src/s5j/Kconfig index 979a95d..374d667 100644 --- a/os/arch/arm/src/s5j/Kconfig +++ b/os/arch/arm/src/s5j/Kconfig @@ -26,6 +26,7 @@ config S5J_S5JT200 default n select S5J_HAVE_ADC select S5J_HAVE_I2C + select S5J_HAVE_I2S select S5J_HAVE_MCT select S5J_HAVE_PWM0 select S5J_HAVE_PWM1 @@ -58,6 +59,10 @@ config S5J_HAVE_I2C bool default n +config S5J_HAVE_I2S + bool + default n + config S5J_HAVE_MCT bool default n @@ -147,6 +152,39 @@ config S5J_I2C default n depends on S5J_HAVE_I2C +config S5J_I2S + bool "I2S" + default n + depends on S5J_HAVE_I2S + select I2S + select AUDIO + select S5J_DMA + +config S5J_I2S_RX + bool "S5J I2S RX support" + default n + depends on S5J_I2S + +config S5J_I2S_TX_P + bool "S5J I2S TX Prime support" + default n + depends on S5J_I2S + +config S5J_I2S_TX_S + bool "S5J I2S TX Secondary support" + default n + depends on S5J_I2S + +config S5J_I2S_MAXINFLIGHT + int "I2S queue size" + default 16 + depends on S5J_I2S + ---help--- + This is the total number of transfers, both RX and TX, that can be + enqueue before the caller is required to wait. This setting + determines the number certain queue data structures that will be + pre-allocated. + config S5J_MCT bool default n diff --git a/os/arch/arm/src/s5j/Make.defs b/os/arch/arm/src/s5j/Make.defs index eb1af77..c023f53 100644 --- a/os/arch/arm/src/s5j/Make.defs +++ b/os/arch/arm/src/s5j/Make.defs @@ -169,6 +169,10 @@ CHIP_CSRCS += s5j_rtc_lowerhalf.c endif endif +ifeq ($(CONFIG_S5J_I2S),y) +CHIP_CSRCS += s5j_i2s.c +endif + ifeq ($(CONFIG_S5J_SSS),y) VPATH += chip/sss CHIP_CSRCS += sss_driver_io.c diff --git a/os/arch/arm/src/s5j/chip/s5jt200_i2s.h b/os/arch/arm/src/s5j/chip/s5jt200_i2s.h new file mode 100755 index 0000000..6ec8e92 --- /dev/null +++ b/os/arch/arm/src/s5j/chip/s5jt200_i2s.h @@ -0,0 +1,166 @@ +/**************************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ +/************************************************************************************ + * arch/arm/src/s5j//chip/s5jt200_i2s.h + * + * Copyright (C) 2013-2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. Neither the name NuttX 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. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_S5JT200_CHIP_S5J_I2S_H +#define __ARCH_ARM_SRC_S5JT200_CHIP_S5J_I2S_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include "chip/s5j_memorymap.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#define S5J_I2S_MAXPERCLK 26000000 /* Maximum peripheral clock frequency */ + +/* I2S Register Offsets *************************************************************/ + +#define S5J_I2S_CON 0x0000 /* I2S interface control */ +#define S5J_I2S_MOD 0x0004 /* I2S interface mode */ +#define S5J_I2S_FIC 0x0008 /* I2S interface TxFIFO and Rx FIFO control */ +#define S5J_I2S_PSR 0x000C /* I2S interface clock divider control */ +#define S5J_I2S_TXD 0x0010 /* I2S interface transmit sound data */ +#define S5J_I2S_RXD 0x0014 /* I2S interface receive data */ +#define S5J_I2S_FICS 0x0018 /* I2S interface secondary TxFIFO_S Control */ +#define S5J_I2S_TXDS 0x001C /* I2S interface transmit secondary data */ +#define S5J_I2S_VER 0x0044 /* I2S version */ + +/* I2S Register Bit Definitions *****************************************************/ + +#define I2S_CR_SW_RST_SHIFT (31) +#define I2S_CR_SW_RST_MASK (1 << I2S_CR_SW_RST_SHIFT) +#define I2S_CR_SW_RST_RELEASE (1 << I2S_CR_SW_RST_SHIFT) + +#define I2S_CR_FRXOFSTATUS (1 << 26) +#define I2S_CR_FRXOFINTEN (1 << 25) +#define I2S_CR_FTXSUR STATUS (1 << 24) +#define I2S_CR_FTXSURINTEN (1 << 23) +#define I2S_CR_FTXSEMPT (1 << 22) +#define I2S_CR_FTXSFULL (1 << 21) +#define I2S_CR_TXSDMAPAUSE (1 << 20) +#define I2S_CR_TXSDMACTIVE (1 << 18) +#define I2S_CR_FTXURSTATUS (1 << 17) +#define I2S_CR_FTXURINTEN (1 << 16) +#define I2S_CR_LRI (1 << 11) +#define I2S_CR_FTX0EMPT (1 << 10) +#define I2S_CR_FRXEMPT (1 << 9) +#define I2S_CR_FTX0FULL (1 << 8) +#define I2S_CR_FRXFULL (1 << 7) +#define I2S_CR_TXDMAPAUSE (1 << 6) +#define I2S_CR_RXDMAPAUSE (1 << 5) +#define I2S_CR_TXCHPAUSE (1 << 4) +#define I2S_CR_RXCHPAUSE (1 << 3) +#define I2S_CR_TXDMACTIVE (1 << 2) +#define I2S_CR_RXDMACTIVE (1 << 1) +#define I2S_CR_I2SACTIVE (1 << 0) + +/* MOD register */ +#define I2S_MOD_OP_CLK_MASK (3 << 30) +#define I2S_MOD_OP_CLK_PCLK (3 << 30) + +#define BLC_8BIT (1) +#define BLC_16BIT (0) +#define BLC_24BIT (2) + +#define I2S_MOD_BLC_S_MASK (3 << 26) +#define I2S_MOD_BLC_S(x) ((x & 3) << 26) +#define I2S_MOD_BLC_P_MASK (3 << 24) +#define I2S_MOD_BLC_P(x) ((x & 3) << 24) +#define I2S_MOD_LRP (1 << 15) +#define I2S_MOD_BLC_MASK (3 << 13) +#define I2S_MOD_BLC(x) ((x & 3) << 13) +#define I2S_MOD_CDCLKCON_IN (1 << 12) +#define I2S_MOD_MSS_SLAVE (1 << 11) +#define I2S_MOD_RCLKSRC (1 << 10) + +#define TXR_TX 0 +#define TXR_RX 1 +#define TXR_TXRX 2 +#define I2S_MOD_TXR_MASK (3 << 8) +#define I2S_MOD_TXR(x) ((x & 3) << 8) + +#define I2S_MOD_SDF_MASK (3 << 6) +#define I2S_MOD_SDF_I2S (0 << 6) +#define I2S_MOD_SDF_MSB_Jstf (1 << 6) +#define I2S_MOD_SDF_LSB_Jstf (2 << 6) + +#define RFS_256 (0x0) +#define RFS_192 (0x7) + +#define BFS_64 (0x4) + +#define I2S_MOD_RFS_MASK (7 << 3) +#define I2S_MOD_RFS(x) ((x & 7) << 3) +#define I2S_MOD_BFS_MASK (7 << 0) +#define I2S_MOD_BFS(x) ((x & 7) << 0) + +/* FIC register */ +#define I2S_FIC_TFLUSH (1 << 15) +#define I2S_FIC_FTX0CNT (0x7F << 8) +#define I2S_FIC_RFLUSH (1 << 7) +#define I2S_FIC_FRXCNT (0x7F << 0) + +#define I2S_PSR_PSREN (1 << 15) +#define I2S_PSR_PSVAL (0x3F << 8) + +#define I2S_FICS_TXDMA_TH_EN (1 << 31) +#define I2S_FICS_TXDMA_TH (0x3F << 24) +#define I2S_FICS_RXDMA_TH_EN (1 << 23) +#define I2S_FICS_RXDMA_TH (0x3F << 16) +#define I2S_FICS_TFLUSHS (1 << 15) +#define I2S_FICS_FTXSCNT (0x7F << 8) + +#endif /* __ARCH_ARM_SRC_S5JT200_CHIP_S5J_I2S_H */ diff --git a/os/arch/arm/src/s5j/s5j_i2s.c b/os/arch/arm/src/s5j/s5j_i2s.c new file mode 100644 index 0000000..3ee950f --- /dev/null +++ b/os/arch/arm/src/s5j/s5j_i2s.c @@ -0,0 +1,2063 @@ +/**************************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ +/**************************************************************************** + * arch/arm/src/sama5/s5j_i2s.c + * + * Copyright (C) 2013-2014, 2016 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * + * 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. Neither the name NuttX 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "up_internal.h" +#include "up_arch.h" +#include "cache.h" + +#include "chip.h" +#include "chip/s5jt200_i2s.h" +#include "s5j_dma.h" +#include "s5j_vclk.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ + +#ifndef CONFIG_SCHED_WORKQUEUE +#error Work queue support is required (CONFIG_SCHED_WORKQUEUE) +#endif + +#ifndef CONFIG_AUDIO +#error CONFIG_AUDIO required by this driver +#endif + +#ifndef CONFIG_S5J_I2S_MAXINFLIGHT +#define CONFIG_S5J_I2S_MAXINFLIGHT 16 +#endif + +/* Assume no RX/TX support until we learn better */ + +#undef I2S_HAVE_RX +#undef I2S_HAVE_TX_P +#undef I2S_HAVE_TX_S + +#if defined(CONFIG_S5J_I2S) + +/* The I2S can handle most any bit width from 2 to 32. However, the DMA + * logic here is constrained to byte, half-word, and word sizes. + */ + +/* Check for I2S RX support */ + +#if defined(CONFIG_S5J_I2S_RX) +#define I2S_HAVE_RX 1 +#endif + +/* Check for I2S TX support */ + +#if defined(CONFIG_S5J_I2S_TX_P) +#define I2S_HAVE_TX_P 1 +#endif + +#if defined(CONFIG_S5J_I2S_TX_S) +#define I2S_HAVE_TX_S 1 +#endif + +#ifndef CONFIG_S5J_I2S_DATALEN +#define CONFIG_S5J_I2S_DATALEN 16 +#endif + +#ifndef CONFIG_S5J_I2S_SAMPLERATE +#define CONFIG_S5J_I2S_SAMPLERATE 1 +#endif + +#endif + +#ifdef CONFIG_DEBUG +#define CONFIG_DEBUG_FEATURES +#endif + +/* DMA configuration */ + +#if !defined(CONFIG_I2S_TXP_DMACH) || !defined(CONFIG_I2S_TXS_DMACH) || !defined(CONFIG_I2S_RX_DMACH) +#undef CONFIG_I2S_TXP_DMACH +#undef CONFIG_I2S_TXS_DMACH +#undef CONFIG_I2S_RX_DMACH + +#define CONFIG_I2S_TXP_DMACH 0 +#define CONFIG_I2S_TXS_DMACH 1 +#define CONFIG_I2S_RX_DMACH 2 +#endif + +#if defined(I2S_HAVE_RX) || defined(I2S_HAVE_TX_P) || defined(I2S_HAVE_TX_S) + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* I2S buffer container */ + +struct s5j_buffer_s { + struct s5j_buffer_s *flink; /* Supports a singly linked list */ + i2s_callback_t callback; /* Function to call when the transfer completes */ + dma_task *dmatask; /* DMA transfer task structure */ + uint32_t timeout; /* The timeout value to use with DMA transfers */ + void *arg; /* The argument to be returned with the callback */ + struct ap_buffer_s *apb; /* The audio buffer */ + int result; /* The result of the transfer */ +}; + +/* This structure describes the state of one receiver or transmitter transport */ + +struct s5j_transport_s { + DMA_HANDLE dma; /* I2S DMA handle */ + WDOG_ID dog; /* Watchdog that handles DMA timeouts */ + sq_queue_t pend; /* A queue of pending transfers */ + sq_queue_t act; /* A queue of active transfers */ + sq_queue_t done; /* A queue of completed transfers */ + struct work_s work; /* Supports worker thread operations */ +}; + +/* The state of the one I2S peripheral */ + +struct s5j_i2s_s { + struct i2s_dev_s dev; /* Externally visible I2S interface */ + uintptr_t base; /* I2S controller register base address */ + sem_t exclsem; /* Assures mutually exclusive acess to I2S */ + uint8_t datalen; /* Data width (8, 16, or 32) */ + uint8_t rx_datalen; /* Data width (8, 16, or 32) */ + uint8_t txp_datalen; /* Data width (8, 16, or 32) */ + uint8_t txs_datalen; /* Data width (8, 16, or 32) */ + + uint8_t rxenab: 1; /* True: RX transfers enabled */ + uint8_t txpenab: 1; /* True: TX primary transfers enabled */ + uint8_t txsenab: 1; /* True: TX secondary transfers enabled */ + + uint32_t samplerate; /* Not actually needed in slave mode */ + +#ifdef I2S_HAVE_RX + struct s5j_transport_s rx; /* RX transport state */ +#endif +#ifdef I2S_HAVE_TX_P + struct s5j_transport_s txp; /* TX primary transport state */ +#endif +#ifdef I2S_HAVE_TX_S + struct s5j_transport_s txs; /* TX secodary transport state */ +#endif + + /* Pre-allocated pool of buffer containers */ + + sem_t bufsem_tx; /* Buffer wait semaphore */ + struct s5j_buffer_s *freelist_tx; /* A list a free buffer containers */ + struct s5j_buffer_s containers_tx[CONFIG_S5J_I2S_MAXINFLIGHT]; + + sem_t bufsem_rx; /* Buffer wait semaphore */ + struct s5j_buffer_s *freelist_rx; /* A list a free buffer containers */ + struct s5j_buffer_s containers_rx[CONFIG_S5J_I2S_MAXINFLIGHT]; + /* Debug stuff */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register helpers */ + +#ifdef CONFIG_S5J_I2S_DUMPBUFFERS +#define i2s_init_buffer(b,s) memset(b, 0x55, s); +#define i2s_dump_buffer(m,b,s) lib_dumpbuffer(m,b,s) +#else +#define i2s_init_buffer(b,s) +#define i2s_dump_buffer(m,b,s) +#endif + +/* Semaphore helpers */ + +static void i2s_exclsem_take(struct s5j_i2s_s *priv); +#define i2s_exclsem_give(priv) sem_post(&priv->exclsem) + +static void i2s_bufsem_rx_take(struct s5j_i2s_s *priv); +#define i2s_bufsem_rx_give(priv) sem_post(&priv->bufsem_rx) + +static void i2s_bufsem_tx_take(struct s5j_i2s_s *priv); +#define i2s_bufsem_tx_give(priv) sem_post(&priv->bufsem_tx) + +/* Buffer container helpers */ + +static struct s5j_buffer_s *i2s_buf_rx_allocate(struct s5j_i2s_s *priv); +static void i2s_buf_rx_free(struct s5j_i2s_s *priv, struct s5j_buffer_s *bfcontainer); +static void i2s_buf_rx_initialize(struct s5j_i2s_s *priv); + +static struct s5j_buffer_s *i2s_buf_tx_allocate(struct s5j_i2s_s *priv); +static void i2s_buf_tx_free(struct s5j_i2s_s *priv, struct s5j_buffer_s *bfcontainer); +static void i2s_buf_tx_initialize(struct s5j_i2s_s *priv); + +#ifdef I2S_HAVE_RX +static void i2s_rxdma_timeout(int argc, uint32_t arg); +static int i2s_rxdma_setup(struct s5j_i2s_s *priv); +static void i2s_rx_worker(void *arg); +static void i2s_rx_schedule(struct s5j_i2s_s *priv, int result); +static void i2s_rxdma_callback(DMA_HANDLE handle, void *arg, int result); +#endif +#ifdef I2S_HAVE_TX_P +static void i2s_txpdma_timeout(int argc, uint32_t arg); +static int i2s_txpdma_setup(struct s5j_i2s_s *priv); +static void i2s_txp_worker(void *arg); +static void i2s_txp_schedule(struct s5j_i2s_s *priv, int result); +static void i2s_txpdma_callback(DMA_HANDLE handle, void *arg, int result); +#endif + +/* I2S methods (and close friends) */ + +static int i2s_checkwidth(struct s5j_i2s_s *priv, int bits); + +static uint32_t i2s_rxdatawidth(struct i2s_dev_s *dev, int bits); +static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, uint32_t timeout); + +static uint32_t i2s_samplerate(struct i2s_dev_s *dev, uint32_t rate); + +static uint32_t i2s_txdatawidth(struct i2s_dev_s *dev, int bits); +static int i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, uint32_t timeout); + +/* Initialization */ + +#ifdef I2S_HAVE_RX +static int i2s_rx_configure(struct s5j_i2s_s *priv); +#endif +#ifdef I2S_HAVE_TX_P +static int i2s_txp_configure(struct s5j_i2s_s *priv); +#endif +static uint32_t i2s_bitrate(struct s5j_i2s_s *priv); +static int i2s_dma_allocate(struct s5j_i2s_s *priv); +static void i2s_dma_free(struct s5j_i2s_s *priv); + +static int i2s_configure(struct s5j_i2s_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* I2S device operations */ + +static const struct i2s_ops_s g_i2sops = { + /* Receiver methods */ + + .i2s_rxsamplerate = i2s_samplerate, + .i2s_rxdatawidth = i2s_rxdatawidth, + .i2s_receive = i2s_receive, + + /* Transmitter methods */ + + .i2s_txsamplerate = i2s_samplerate, + .i2s_txdatawidth = i2s_txdatawidth, + .i2s_send = i2s_send, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: i2s_exclsem_take + * + * Description: + * Take the exclusive access semaphore handling any exceptional conditions + * + * Input Parameters: + * priv - A reference to the I2S peripheral state + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void i2s_exclsem_take(struct s5j_i2s_s *priv) +{ + int ret; + + /* Wait until we successfully get the semaphore. EINTR is the only + * expected 'failure' (meaning that the wait for the semaphore was + * interrupted by a signal. + */ + + do { + ret = sem_wait(&priv->exclsem); + DEBUGASSERT(ret == 0 || errno == EINTR); + } while (ret < 0); +} + +/**************************************************************************** + * Name: i2s_bufsem_take + * + * Description: + * Take the buffer semaphore handling any exceptional conditions + * + * Input Parameters: + * priv - A reference to the I2S peripheral state + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void i2s_bufsem_rx_take(struct s5j_i2s_s *priv) +{ + int ret; + + /* Wait until we successfully get the semaphore. EINTR is the only + * expected 'failure' (meaning that the wait for the semaphore was + * interrupted by a signal. + */ + + do { + ret = sem_wait(&priv->bufsem_rx); + DEBUGASSERT(ret == 0 || errno == EINTR); + } while (ret < 0); +} + +static void i2s_bufsem_tx_take(struct s5j_i2s_s *priv) +{ + int ret; + + /* Wait until we successfully get the semaphore. EINTR is the only + * expected 'failure' (meaning that the wait for the semaphore was + * interrupted by a signal. + */ + + do { + ret = sem_wait(&priv->bufsem_tx); + DEBUGASSERT(ret == 0 || errno == EINTR); + } while (ret < 0); +} + +/**************************************************************************** + * Name: i2s_buf_allocate + * + * Description: + * Allocate a buffer container by removing the one at the head of the + * free list + * + * Input Parameters: + * priv - i2s state instance + * + * Returned Value: + * A non-NULL pointer to the allocate buffer container on success; NULL if + * there are no available buffer containers. + * + * Assumptions: + * The caller does NOT have exclusive access to the I2S state structure. + * That would result in a deadlock! + * + ****************************************************************************/ + +static struct s5j_buffer_s *i2s_buf_rx_allocate(struct s5j_i2s_s *priv) +{ + struct s5j_buffer_s *bfcontainer; + irqstate_t flags; + + /* Set aside a buffer container. By doing this, we guarantee that we will + * have at least one free buffer container. + */ + + i2s_bufsem_rx_take(priv); + + /* Get the buffer from the head of the free list */ + + flags = irqsave(); + bfcontainer = priv->freelist_rx; + ASSERT(bfcontainer); + + /* Unlink the buffer from the freelist */ + + priv->freelist_rx = bfcontainer->flink; + irqrestore(flags); + + return bfcontainer; +} + +static struct s5j_buffer_s *i2s_buf_tx_allocate(struct s5j_i2s_s *priv) +{ + struct s5j_buffer_s *bfcontainer; + irqstate_t flags; + + /* Set aside a buffer container. By doing this, we guarantee that we will + * have at least one free buffer container. + */ + + i2s_bufsem_tx_take(priv); + + /* Get the buffer from the head of the free list */ + + flags = irqsave(); + bfcontainer = priv->freelist_tx; + ASSERT(bfcontainer); + + /* Unlink the buffer from the freelist */ + + priv->freelist_tx = bfcontainer->flink; + irqrestore(flags); + + return bfcontainer; +} + +/**************************************************************************** + * Name: i2s_buf_free + * + * Description: + * Free buffer container by adding it to the head of the free list + * + * Input Parameters: + * priv - I2S state instance + * bfcontainer - The buffer container to be freed + * + * Returned Value: + * None + * + * Assumptions: + * The caller has exclusive access to the I2S state structure + * + ****************************************************************************/ + +static void i2s_buf_rx_free(struct s5j_i2s_s *priv, struct s5j_buffer_s *bfcontainer) +{ + irqstate_t flags; + + /* Put the buffer container back on the free list */ + + flags = irqsave(); + bfcontainer->flink = priv->freelist_rx; + priv->freelist_rx = bfcontainer; + irqrestore(flags); + + /* Wake up any threads waiting for a buffer container */ + + i2s_bufsem_rx_give(priv); +} + +static void i2s_buf_tx_free(struct s5j_i2s_s *priv, struct s5j_buffer_s *bfcontainer) +{ + irqstate_t flags; + + /* Put the buffer container back on the free list */ + + flags = irqsave(); + bfcontainer->flink = priv->freelist_tx; + priv->freelist_tx = bfcontainer; + irqrestore(flags); + + /* Wake up any threads waiting for a buffer container */ + + i2s_bufsem_tx_give(priv); +} + +/**************************************************************************** + * Name: i2s_buf_initialize + * + * Description: + * Initialize the buffer container allocator by adding all of the + * pre-allocated buffer containers to the free list + * + * Input Parameters: + * priv - I2S state instance + * + * Returned Value: + * None + * + * Assumptions: + * Called early in I2S initialization so that there are no issues with + * concurrency. + * + ****************************************************************************/ + +static void i2s_buf_rx_initialize(struct s5j_i2s_s *priv) +{ + int i; + + priv->freelist_rx = NULL; + sem_init(&priv->bufsem_rx, 0, CONFIG_S5J_I2S_MAXINFLIGHT); + + for (i = 0; i < CONFIG_S5J_I2S_MAXINFLIGHT; i++) { + i2s_buf_rx_free(priv, &priv->containers_rx[i]); + } +} + +static void i2s_buf_tx_initialize(struct s5j_i2s_s *priv) +{ + int i; + + priv->freelist_tx = NULL; + sem_init(&priv->bufsem_tx, 0, CONFIG_S5J_I2S_MAXINFLIGHT); + + for (i = 0; i < CONFIG_S5J_I2S_MAXINFLIGHT; i++) { + i2s_buf_tx_free(priv, &priv->containers_tx[i]); + } +} + +/**************************************************************************** + * Name: i2s_rxdma_timeout + * + * Description: + * The RX watchdog timeout without completion of the RX DMA. + * + * Input Parameters: + * argc - The number of arguments (should be 1) + * arg - The argument (state structure reference cast to uint32_t) + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static void i2s_rxdma_timeout(int argc, uint32_t arg) +{ + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)arg; + DEBUGASSERT(priv != NULL); + + /* Cancel the DMA */ + + s5j_dmastop(priv->rx.dma); + + /* Then schedule completion of the transfer to occur on the worker thread. + * NOTE: s5j_dmastop() will call the DMA complete callback with an error + * of -EINTR. So the following is just insurance and should have no + * effect if the worker is already schedule. + */ + + i2s_rx_schedule(priv, -ETIMEDOUT); +} +#endif + +/**************************************************************************** + * Name: i2s_rxdma_setup + * + * Description: + * Setup and initiate the next RX DMA transfer + * + * Input Parameters: + * priv - I2S state instance + * + * Returned Value: + * OK on success; a negated errno value on failure + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static int i2s_rxdma_setup(struct s5j_i2s_s *priv) +{ + struct s5j_buffer_s *bfcontainer; + struct ap_buffer_s *apb; + uint32_t timeout; + bool notimeout; + int ret; + dma_task *dmatask = 0; + + /* If there is already an active transmission in progress, then bail + * returning success. + */ + + if (!sq_empty(&priv->rx.act)) { + return OK; + } + + /* If there are no pending transfer, then bail returning success */ + + if (sq_empty(&priv->rx.pend)) { + return OK; + } + + /* Loop, adding each pending DMA */ + + timeout = 0; + notimeout = false; + + do { + /* Remove the pending RX transfer at the head of the RX pending queue. */ + + bfcontainer = (struct s5j_buffer_s *)sq_remfirst(&priv->rx.pend); + DEBUGASSERT(bfcontainer && bfcontainer->apb); + + apb = bfcontainer->apb; + dmatask = bfcontainer->dmatask; + /* No data received yet */ + + apb->nbytes = 0; + apb->curbyte = 0; + + dmatask->dst = (void *)apb->samp; + dmatask->src = (void *)(priv->base + S5J_I2S_RXD); + dmatask->size = apb->nmaxbytes; + dmatask->callback = i2s_rxdma_callback; + dmatask->arg = priv; + + /* Configure the RX DMA task */ + + s5j_dmasetup(priv->rx.dma, dmatask); + + /* Increment the DMA timeout */ + + if (bfcontainer->timeout > 0) { + timeout += bfcontainer->timeout; + } else { + notimeout = true; + } + + /* Add the container to the list of active DMAs */ + + sq_addlast((sq_entry_t *) bfcontainer, &priv->rx.act); + + /* Invalidate the data cache so that nothing gets flush into the + * DMA buffer after starting the DMA transfer. + */ + + arch_invalidate_dcache((uintptr_t) dmatask->dst, (uintptr_t)(dmatask->dst + apb->nmaxbytes)); + + } + + /* FLUSH RX FIFO */ + putreg32(I2S_FIC_RFLUSH, priv->base + S5J_I2S_FIC); + putreg32(0, priv->base + S5J_I2S_FIC); + + /* Enable Receiver */ + modifyreg32(priv->base + S5J_I2S_CON, 0, I2S_CR_RXDMACTIVE); + modifyreg32(priv->base + S5J_I2S_CON, 0, I2S_CR_I2SACTIVE); + + /* Start the DMA, saving the container as the current active transfer */ + + s5j_dmastart(priv->rx.dma, dmatask); + + /* Start a watchdog to catch DMA timeouts */ + + if (!notimeout) { + ret = wd_start(priv->rx.dog, timeout, (wdentry_t) i2s_rxdma_timeout, 1, (uint32_t) priv); + + /* Check if we have successfully started the watchdog timer. Note + * that we do nothing in the case of failure to start the timer. We + * are already committed to the DMA anyway. Let's just hope that the + * DMA does not hang. + */ + + if (ret < 0) { + lldbg("ERROR: wd_start failed: %d\n", errno); + } + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: i2s_rx_worker + * + * Description: + * RX transfer done worker + * + * Input Parameters: + * arg - the I2S device instance cast to void* + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static void i2s_rx_worker(void *arg) +{ + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)arg; + struct s5j_buffer_s *bfcontainer; + struct ap_buffer_s *apb; + irqstate_t flags; + + DEBUGASSERT(priv); + + /* When the transfer was started, the active buffer containers were removed + * from the rx.pend queue and saved in the rx.act queue. We get here when the + * DMA is finished... either successfully, with a DMA error, or with a DMA + * timeout. + * + * In any case, the buffer containers in rx.act will be moved to the end + * of the rx.done queue and rx.act queue will be emptied before this worker + * is started. + * + * REVISIT: Normal DMA callback processing should restart the DMA + * immediately to avoid audio artifacts at the boundaries between DMA + * transfers. Unfortunately, the DMA callback occurs at the interrupt + * level and we cannot call dma_setup() from the interrupt level. + * So we have to start the next DMA here. + */ + + llvdbg("rx.act.head=%p rx.done.head=%p\n", priv->rx.act.head, priv->rx.done.head); + + /* Check if the DMA is IDLE */ + + if (sq_empty(&priv->rx.act)) { + + /* Then start the next DMA. This must be done with interrupts + * disabled. + */ + + flags = irqsave(); + (void)i2s_rxdma_setup(priv); + irqrestore(flags); + } + + /* Process each buffer in the rx.done queue */ + + while (sq_peek(&priv->rx.done) != NULL) { + /* Remove the buffer container from the rx.done queue. NOTE that + * interrupts must be enabled to do this because the rx.done queue is + * also modified from the interrupt level. + */ + + flags = irqsave(); + bfcontainer = (struct s5j_buffer_s *)sq_remfirst(&priv->rx.done); + irqrestore(flags); + + DEBUGASSERT(bfcontainer && bfcontainer->apb && bfcontainer->callback); + apb = bfcontainer->apb; + + /* If the DMA was successful, then update the number of valid bytes in + * the audio buffer. + */ + + if (bfcontainer->result == OK) { + apb->nbytes = apb->nmaxbytes; + } + + i2s_dump_buffer("Received", apb->samp, apb->nbytes); + + /* Perform the RX transfer done callback */ + + bfcontainer->callback(&priv->dev, apb, bfcontainer->arg, bfcontainer->result); + + /* Release our reference on the audio buffer. This may very likely + * cause the audio buffer to be freed. + */ + + apb_free(apb); + + /* And release the buffer container */ + + i2s_buf_rx_free(priv, bfcontainer); + } + +} +#endif + +/**************************************************************************** + * Name: i2s_rx_schedule + * + * Description: + * An RX DMA completion or timeout has occurred. Schedule processing on + * the working thread. + * + * Input Parameters: + * handle - The DMA handler + * arg - A pointer to the chip select struction + * result - The result of the DMA transfer + * + * Returned Value: + * None + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static void i2s_rx_schedule(struct s5j_i2s_s *priv, int result) +{ + struct s5j_buffer_s *bfcontainer; + int ret; + + /* Upon entry, the transfer(s) that just completed are the ones in the + * priv->rx.act queue. NOTE: In certain conditions, this function may + * be called an additional time, hence, we can't assert this to be true. + * For example, in the case of a timeout, this function will be called by + * both indirectly via the s5j_dmastop() logic and directly via the + * i2s_rxdma_timeout() logic. + */ + + /* Move all entries from the rx.act queue to the rx.done queue */ + + while (!sq_empty(&priv->rx.act)) { + /* Remove the next buffer container from the rx.act list */ + + bfcontainer = (struct s5j_buffer_s *)sq_remfirst(&priv->rx.act); + + /* Report the result of the transfer */ + + bfcontainer->result = result; + + /* Add the completed buffer container to the tail of the rx.done queue */ + + sq_addlast((sq_entry_t *) bfcontainer, &priv->rx.done); + } + + /* If the worker has completed running, then reschedule the working thread. + * REVISIT: There may be a race condition here. So we do nothing is the + * worker is not available. + */ + + if (work_available(&priv->rx.work)) { + /* Schedule the TX DMA done processing to occur on the worker thread. */ + + ret = work_queue(HPWORK, &priv->rx.work, i2s_rx_worker, priv, 0); + if (ret != 0) { + lldbg("ERROR: Failed to queue RX work: %d\n", ret); + } + } +} +#endif + +/**************************************************************************** + * Name: i2s_rxdma_callback + * + * Description: + * This callback function is invoked at the completion of the I2S RX DMA. + * + * Input Parameters: + * handle - The DMA handler + * arg - A pointer to the chip select struction + * result - The result of the DMA transfer + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_RX +static void i2s_rxdma_callback(DMA_HANDLE handle, void *arg, int result) +{ + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)arg; + DEBUGASSERT(priv != NULL); + + /* Cancel the watchdog timeout */ + + (void)wd_cancel(priv->rx.dog); + + /* REVISIT: We would like to the next DMA started here so that we do not + * get audio glitches at the boundaries between DMA transfers. + * Unfortunately, we cannot call s5j_dmasetup() from an interrupt handler! + */ + + /* Then schedule completion of the transfer to occur on the worker thread */ + + i2s_rx_schedule(priv, result); +} +#endif + +/**************************************************************************** + * Name: i2s_txpdma_timeout + * + * Description: + * The Primary TX watchdog timeout without completion of the TX DMA. + * + * Input Parameters: + * argc - The number of arguments (should be 1) + * arg - The argument (state structure reference cast to uint32_t) + * + * Returned Value: + * None + * + * Assumptions: + * Always called from the interrupt level with interrupts disabled. + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX_P +static void i2s_txpdma_timeout(int argc, uint32_t arg) +{ + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)arg; + DEBUGASSERT(priv != NULL); + + /* Cancel the DMA */ + + s5j_dmastop(priv->txp.dma); + + /* Then schedule completion of the transfer to occur on the worker thread. + * NOTE: s5j_dmastop() will call the DMA complete callback with an error + * of -EINTR. So the following is just insurance and should have no + * effect if the worker is already schedule. + */ + + i2s_txp_schedule(priv, -ETIMEDOUT); +} +#endif + +/**************************************************************************** + * Name: i2s_txpdma_setup + * + * Description: + * Setup and initiate the next TX DMA transfer + * + * Input Parameters: + * priv - I2S state instance + * + * Returned Value: + * OK on success; a negated errno value on failure + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX_P +static int i2s_txpdma_setup(struct s5j_i2s_s *priv) +{ + struct s5j_buffer_s *bfcontainer; + struct ap_buffer_s *apb; + uint32_t timeout; + bool notimeout; + int ret; + dma_task *dmatask = 0; + dmavdbg("Entry\n"); + + /* If there is already an active transmission in progress, then bail + * returning success. + */ + + if (!sq_empty(&priv->txp.act)) { + return OK; + } + + /* If there are no pending transfer, then bail returning success */ + + if (sq_empty(&priv->txp.pend)) { + return OK; + } + + /* Loop, adding each pending DMA */ + + timeout = 0; + notimeout = false; + + do { + /* Remove the pending TX transfer at the head of the TX pending queue. */ + + bfcontainer = (struct s5j_buffer_s *)sq_remfirst(&priv->txp.pend); + DEBUGASSERT(bfcontainer && bfcontainer->apb); + + apb = bfcontainer->apb; + dmatask = bfcontainer->dmatask; + /* Get the transfer information, accounting for any data offset */ + + //dmatask->dst = (void *)((((int)apb->samp) & ~3) + 4); + dmatask->dst = (void *)(priv->base + S5J_I2S_TXD); + dmatask->src = (void *)&apb->samp[apb->curbyte]; + dmatask->size = apb->nbytes - apb->curbyte; + dmatask->callback = i2s_txpdma_callback; + dmatask->arg = priv; + + /* Configure the TX DMA task */ + + s5j_dmasetup(priv->txp.dma, dmatask); + + /* Increment the DMA timeout */ + + if (bfcontainer->timeout > 0) { + timeout += bfcontainer->timeout; + } else { + notimeout = true; + } + + /* Add the container to the list of active DMAs */ + + sq_addlast((sq_entry_t *) bfcontainer, &priv->txp.act); + + /* Flush the data cache so that everything is in the physical memory + * before starting the DMA. + */ + + arch_clean_dcache((uintptr_t) dmatask->src, (uintptr_t)(dmatask->src + dmatask->size)); + } + + /* Start the DMA, saving the container as the current active transfer */ + s5j_dmastart(priv->txp.dma, dmatask); + + /* Enable transmitter */ + modifyreg32(priv->base + S5J_I2S_CON, 0, I2S_CR_TXDMACTIVE); + modifyreg32(priv->base + S5J_I2S_CON, 0, I2S_CR_I2SACTIVE); + + /* Start a watchdog to catch DMA timeouts */ + + if (!notimeout) { + ret = wd_start(priv->txp.dog, timeout, (wdentry_t) i2s_txpdma_timeout, 1, (uint32_t) priv); + + /* Check if we have successfully started the watchdog timer. Note + * that we do nothing in the case of failure to start the timer. We + * are already committed to the DMA anyway. Let's just hope that the + * DMA does not hang. + */ + + if (ret < 0) { + lldbg("ERROR: wd_start failed: %d\n", errno); + } + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: i2s_txp_worker + * + * Description: + * Primary TX transfer done worker + * + * Input Parameters: + * arg - the I2S device instance cast to void* + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX_P +static void i2s_txp_worker(void *arg) +{ + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)arg; + struct s5j_buffer_s *bfcontainer; + irqstate_t flags; + + DEBUGASSERT(priv); + + /* When the transfer was started, the active buffer containers were removed + * from the txp.pend queue and saved in the txp.act queue. We get here when the + * DMA is finished... either successfully, with a DMA error, or with a DMA + * timeout. + * + * In any case, the buffer containers in txp.act will be moved to the end + * of the txp.done queue and txp.act will be emptied before this worker is + * started. + * + * REVISIT: Normal DMA callback processing should restart the DMA + * immediately to avoid audio artifacts at the boundaries between DMA + * transfers. Unfortunately, the DMA callback occurs at the interrupt + * level and we cannot call dma_setup() from the interrupt level. + * So we have to start the next DMA here. + */ + + llvdbg("txp.act.head=%p txp.done.head=%p\n", priv->txp.act.head, priv->txp.done.head); + + /* Check if the DMA is IDLE */ + + if (sq_empty(&priv->txp.act)) { + + /* Then start the next DMA. This must be done with interrupts + * disabled. + */ + + flags = irqsave(); + (void)i2s_txpdma_setup(priv); + irqrestore(flags); + } + + /* Process each buffer in the tx.done queue */ + + while (sq_peek(&priv->txp.done) != NULL) { + /* Remove the buffer container from the tx.done queue. NOTE that + * interrupts must be enabled to do this because the tx.done queue is + * also modified from the interrupt level. + */ + + flags = irqsave(); + bfcontainer = (struct s5j_buffer_s *)sq_remfirst(&priv->txp.done); + irqrestore(flags); + + /* Perform the TX transfer done callback */ + + DEBUGASSERT(bfcontainer && bfcontainer->callback); + bfcontainer->callback(&priv->dev, bfcontainer->apb, bfcontainer->arg, bfcontainer->result); + + /* Release our reference on the audio buffer. This may very likely + * cause the audio buffer to be freed. + */ + + apb_free(bfcontainer->apb); + + /* And release the buffer container */ + + i2s_buf_tx_free(priv, bfcontainer); + } + +} +#endif + +/**************************************************************************** + * Name: i2s_txp_schedule + * + * Description: + * A TX DMA completion or timeout has occurred. Schedule processing on + * the working thread. + * + * Input Parameters: + * handle - The DMA handler + * arg - A pointer to the chip select struction + * result - The result of the DMA transfer + * + * Returned Value: + * None + * + * Assumptions: + * - Interrupts are disabled + * - The TX timeout has been canceled. + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX_P +static void i2s_txp_schedule(struct s5j_i2s_s *priv, int result) +{ + struct s5j_buffer_s *bfcontainer; + int ret; + + /* Upon entry, the transfer(s) that just completed are the ones in the + * priv->tx.act queue. NOTE: In certain conditions, this function may + * be called an additional time, hence, we can't assert this to be true. + * For example, in the case of a timeout, this function will be called by + * both indirectly via the s5j_dmastop() logic and directly via the + * i2s_txdma_timeout() logic. + */ + + /* Move all entries from the tx.act queue to the tx.done queue */ + + while (!sq_empty(&priv->txp.act)) { + /* Remove the next buffer container from the tx.act list */ + + bfcontainer = (struct s5j_buffer_s *)sq_remfirst(&priv->txp.act); + + /* Report the result of the transfer */ + + bfcontainer->result = result; + + /* Add the completed buffer container to the tail of the tx.done queue */ + + sq_addlast((sq_entry_t *) bfcontainer, &priv->txp.done); + } + + /* If the worker has completed running, then reschedule the working thread. + * REVISIT: There may be a race condition here. So we do nothing is the + * worker is not available. + */ + + if (work_available(&priv->txp.work)) { + /* Schedule the TX DMA done processing to occur on the worker thread. */ + + ret = work_queue(HPWORK, &priv->txp.work, i2s_txp_worker, priv, 0); + if (ret != 0) { + lldbg("ERROR: Failed to queue TX primary work: %d\n", ret); + } + } +} +#endif + +/**************************************************************************** + * Name: i2s_txpdma_callback + * + * Description: + * This callback function is invoked at the completion of the I2S TX DMA. + * + * Input Parameters: + * handle - The DMA handler + * arg - A pointer to the chip select struction + * result - The result of the DMA transfer + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef I2S_HAVE_TX_P +static void i2s_txpdma_callback(DMA_HANDLE handle, void *arg, int result) +{ + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)arg; + DEBUGASSERT(priv != NULL); + + /* Cancel the watchdog timeout */ + + (void)wd_cancel(priv->txp.dog); + + /* REVISIT: We would like to the next DMA started here so that we do not + * get audio glitches at the boundaries between DMA transfers. + * Unfortunately, we cannot call s5j_dmasetup() from an interrupt handler! + */ + + /* Then schedule completion of the transfer to occur on the worker thread */ + + i2s_txp_schedule(priv, result); +} +#endif + +/**************************************************************************** + * Name: i2s_checkwidth + * + * Description: + * Check for a valid bit width. The I2S is capable of handling most any + * bit width from 2 to 32, but the DMA logic in this driver is constrained + * to 8-, 16-, and 32-bit data widths + * + * Input Parameters: + * dev - Device-specific state data + * rate - The I2S sample rate in samples (not bits) per second + * + * Returned Value: + * Returns the resulting bitrate + * + ****************************************************************************/ + +static int i2s_checkwidth(struct s5j_i2s_s *priv, int bits) +{ + /* The I2S can handle most any bit width from 2 to 32. However, the DMA + * logic here is constrained to byte, half-word, and word sizes. + */ + + switch (bits) { + case 8: + break; + + case 16: + break; + + case 32: + break; + + default: + lldbg("ERROR: Unsupported or invalid data width: %d\n", bits); + return (bits < 2 || bits > 32) ? -EINVAL : -ENOSYS; + } + + /* Save the new data width */ + priv->datalen = bits; + return OK; +} + +/**************************************************************************** + * Name: i2s_bitrate + * + * Description: + * In master mode supposed to set bit rate. But now, in slave mode, + * we just calculate it. Still we set all bit related regs. + * + * Input Parameter: + * priv - I2S device structure (only the sample rate and data length is + * needed at this point). + * + * Returned Value: + * The current bitrate + * + ****************************************************************************/ + +static uint32_t i2s_bitrate(struct s5j_i2s_s *priv) +{ + uint32_t bitrate; + uint32_t regval; + DEBUGASSERT(priv && priv->samplerate > 0 && priv->datalen > 0); + + switch (priv->datalen) { + case 8: + regval = BLC_8BIT; + break; + + case 16: + regval = BLC_16BIT; + break; + + case 32: + regval = BLC_24BIT; + break; + + default: + lldbg("ERROR: Unsupported or invalid datalen: %d\n", priv->datalen); + return -EINVAL; + } + + modifyreg32(priv->base + S5J_I2S_MOD, I2S_MOD_BLC_S_MASK, I2S_MOD_BLC_S(regval)); + modifyreg32(priv->base + S5J_I2S_MOD, I2S_MOD_BLC_P_MASK, I2S_MOD_BLC_P(regval)); + modifyreg32(priv->base + S5J_I2S_MOD, I2S_MOD_BLC_MASK, I2S_MOD_BLC(regval)); + + /* Even in slave mode it recommended to set. Set common values */ + modifyreg32(priv->base + S5J_I2S_MOD, I2S_MOD_RFS_MASK, I2S_MOD_RFS(RFS_192)); + modifyreg32(priv->base + S5J_I2S_MOD, I2S_MOD_BFS_MASK, I2S_MOD_BFS(BFS_64)); + + bitrate = priv->samplerate * priv->datalen; + return bitrate; +} + +/**************************************************************************** + * Name: i2s_rxdatawidth + * + * Description: + * Set the I2S RX data width. The RX bitrate is determined by + * sample_rate * data_width. + * + * Input Parameters: + * dev - Device-specific state data + * width - The I2S data with in bits. + * + * Returned Value: + * Returns the resulting bitrate + * + ****************************************************************************/ + +static uint32_t i2s_rxdatawidth(struct i2s_dev_s *dev, int bits) +{ + +#ifdef I2S_HAVE_RX + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)dev; + int ret; + + DEBUGASSERT(priv && bits > 1); + + /* Check if this is a bit width that we are configured to handle */ + + ret = i2s_checkwidth(priv, bits); + if (ret < 0) { + lldbg("ERROR: i2s_checkwidth failed: %d\n", ret); + return 0; + } + + /* Reconfigure the RX DMA (and TX DMA if applicable) */ + return i2s_bitrate(priv); +#endif + + return 0; +} + +/**************************************************************************** + * Name: i2s_receive + * + * Description: + * Receive a block of data from I2S. + * + * Input Parameters: + * dev - Device-specific state data + * apb - A pointer to the audio buffer in which to recieve data + * callback - A user provided callback function that will be called at + * the completion of the transfer. The callback will be + * performed in the context of the worker thread. + * arg - An opaque argument that will be provided to the callback + * when the transfer complete + * timeout - The timeout value to use. The transfer will be canceled + * and an ETIMEDOUT error will be reported if this timeout + * elapsed without completion of the DMA transfer. Units + * are system clock ticks. Zero means no timeout. + * + * Returned Value: + * OK on success; a negated errno value on failure. NOTE: This function + * only enqueues the transfer and returns immediately. Success here only + * means that the transfer was enqueued correctly. + * + * When the transfer is complete, a 'result' value will be provided as + * an argument to the callback function that will indicate if the transfer + * failed. + * + ****************************************************************************/ + +static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callback_t callback, void *arg, uint32_t timeout) +{ + + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)dev; +#ifdef I2S_HAVE_RX + struct s5j_buffer_s *bfcontainer; + irqstate_t flags; + int ret; +#endif + + llvdbg("apb=%p nmaxbytes=%d arg=%p timeout=%d\n", apb, apb->nmaxbytes, arg, timeout); + + i2s_init_buffer(apb->samp, apb->nmaxbytes); + +#ifdef I2S_HAVE_RX + /* Allocate a buffer container in advance */ + + bfcontainer = i2s_buf_rx_allocate(priv); + DEBUGASSERT(bfcontainer); + + /* Get exclusive access to the I2S driver data */ + + i2s_exclsem_take(priv); + + /* Has the RX channel been enabled? */ + + if (!priv->rxenab) { + lldbg("ERROR: I2S has no receiver\n"); + ret = -EAGAIN; + goto errout_with_exclsem; + } + + /* Add a reference to the audio buffer */ + + apb_reference(apb); + + /* Initialize the buffer container structure */ + + bfcontainer->callback = (void *)callback; + bfcontainer->timeout = timeout; + bfcontainer->arg = arg; + bfcontainer->apb = apb; + bfcontainer->result = -EBUSY; + + /* Add the buffer container to the end of the RX pending queue */ + + flags = irqsave(); + sq_addlast((sq_entry_t *) bfcontainer, &priv->rx.pend); + + /* Then start the next transfer. If there is already a transfer in progess, + * then this will do nothing. + */ + + ret = i2s_rxdma_setup(priv); + DEBUGASSERT(ret == OK); + irqrestore(flags); + i2s_exclsem_give(priv); + return OK; + +errout_with_exclsem: + i2s_exclsem_give(priv); + i2s_buf_rx_free(priv, bfcontainer); + return ret; + +#else + lldbg("ERROR: I2S has no receiver\n"); + UNUSED(priv); + return -ENOSYS; +#endif + +} + +/**************************************************************************** + * Name: i2s_samplerate + * + * Description: + * Set the I2S sample rate. + * + * Input Parameters: + * dev - Device-specific state data + * rate - The I2S sample rate in samples (not bits) per second + * + * Returned Value: + * Returns the resulting bitrate + * + ****************************************************************************/ + +static uint32_t i2s_samplerate(struct i2s_dev_s *dev, uint32_t rate) +{ + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)dev; + DEBUGASSERT(priv && priv->samplerate > 0 && rate > 0); + + /* Check if the receiver is driven by the MCK/2 */ + + priv->samplerate = rate; + return i2s_bitrate(priv); +} + +/**************************************************************************** + * Name: i2s_txdatawidth + * + * Description: + * Set the I2S TX data width. The TX bitrate is determined by + * sample_rate * data_width. + * + * Input Parameters: + * dev - Device-specific state data + * width - The I2S data with in bits. + * + * Returned Value: + * Returns the resulting bitrate + * + ****************************************************************************/ + +static uint32_t i2s_txdatawidth(struct i2s_dev_s *dev, int bits) +{ +#ifdef I2S_HAVE_TX_P + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)dev; + int ret; + + DEBUGASSERT(priv && bits > 1); + + /* Check if this is a bit width that we are configured to handle */ + + ret = i2s_checkwidth(priv, bits); + if (ret < 0) { + lldbg("ERROR: i2s_checkwidth failed: %d\n", ret); + return 0; + } + + /* Reconfigure the RX DMA (and TX DMA if applicable) */ + + return i2s_bitrate(priv); +#endif + + return 0; +} + +/**************************************************************************** + * Name: i2s_send + * + * Description: + * Send a block of data on I2S. + * + * Input Parameters: + * dev - Device-specific state data + * apb - A pointer to the audio buffer from which to send data + * callback - A user provided callback function that will be called at + * the completion of the transfer. The callback will be + * performed in the context of the worker thread. + * arg - An opaque argument that will be provided to the callback + * when the transfer complete + * timeout - The timeout value to use. The transfer will be canceled + * and an ETIMEDOUT error will be reported if this timeout + * elapsed without completion of the DMA transfer. Units + * are system clock ticks. Zero means no timeout. + * + * Returned Value: + * OK on success; a negated errno value on failure. NOTE: This function + * only enqueues the transfer and returns immediately. Success here only + * means that the transfer was enqueued correctly. + * + * When the transfer is complete, a 'result' value will be provided as + * an argument to the callback function that will indicate if the transfer + * failed. + * + ****************************************************************************/ + +static int i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callback_t callback, void *arg, uint32_t timeout) +{ + + struct s5j_i2s_s *priv = (struct s5j_i2s_s *)dev; +#ifdef I2S_HAVE_TX_P + struct s5j_buffer_s *bfcontainer; + irqstate_t flags; + int ret; +#endif + + DEBUGASSERT(priv && apb); + + llvdbg("apb=%p nbytes=%d arg=%p timeout=%d\n", apb, apb->nbytes - apb->curbyte, arg, timeout); + + i2s_dump_buffer("Sending", &apb->samp[apb->curbyte], apb->nbytes - apb->curbyte); + +#ifdef I2S_HAVE_TX_P + /* Allocate a buffer container in advance */ + + bfcontainer = i2s_buf_tx_allocate(priv); + DEBUGASSERT(bfcontainer); + + /* Get exclusive access to the I2S driver data */ + + i2s_exclsem_take(priv); + + /* Has the TX channel been enabled? */ + + if (!priv->txpenab) { + lldbg("ERROR: I2S has no transmitter\n"); + ret = -EAGAIN; + goto errout_with_exclsem; + } + + /* Add a reference to the audio buffer */ + + apb_reference(apb); + + /* Initialize the buffer container structure */ + + bfcontainer->callback = (void *)callback; + bfcontainer->timeout = timeout; + bfcontainer->arg = arg; + bfcontainer->apb = apb; + bfcontainer->result = -EBUSY; + + /* Add the buffer container to the end of the TX pending queue */ + + flags = irqsave(); + sq_addlast((sq_entry_t *) bfcontainer, &priv->txp.pend); + + /* Then start the next transfer. If there is already a transfer in progess, + * then this will do nothing. + */ + + ret = i2s_txpdma_setup(priv); + DEBUGASSERT(ret == OK); + irqrestore(flags); + i2s_exclsem_give(priv); + return OK; + +errout_with_exclsem: + i2s_exclsem_give(priv); + i2s_buf_tx_free(priv, bfcontainer); + return ret; + +#else + lldbg("ERROR: I2S has no transmitter\n"); + UNUSED(priv); + return -ENOSYS; +#endif +} + +/**************************************************************************** + * Name: i2s_rx/tx_configure + * + * Description: + * Configure the i2s receiver and transmitter. + * + * Input Parameters: + * priv - Fully initialized i2s device structure. + * + * Returned Value: + * OK is returned on failure. A negated errno value is returned on failure. + * + ****************************************************************************/ + +static int i2s_rx_cfg_clr(struct s5j_i2s_s *priv) +{ + int i; + + for (i = 0; i < CONFIG_S5J_I2S_MAXINFLIGHT; i++) { + if (priv->containers_rx[i].dmatask != NULL) { + s5j_dmatask_free(priv->containers_rx[i].dmatask); + } + priv->containers_rx[i].dmatask = NULL; + } + + priv->rxenab = 0; + return 0; +} + +static int i2s_rx_configure(struct s5j_i2s_s *priv) +{ + int i; + + /* Here I initialie dma task structures for RX channel */ + for (i = 0; i < CONFIG_S5J_I2S_MAXINFLIGHT; i++) { + priv->containers_rx[i].dmatask = dma_task_p2m_sb_4B_x256_alloc(DMA_I2S_RX); + if (priv->containers_rx[i].dmatask == NULL) { + goto err; + } + } + + priv->rxenab = 1; + return OK; + +err: + i2s_rx_cfg_clr(priv); + + return -ENOMEM; +} + +static int i2s_txp_cfg_clr(struct s5j_i2s_s *priv) +{ + int i; + + for (i = 0; i < CONFIG_S5J_I2S_MAXINFLIGHT; i++) { + if (priv->containers_tx[i].dmatask != NULL) { + s5j_dmatask_free(priv->containers_tx[i].dmatask); + } + priv->containers_tx[i].dmatask = NULL; + } + + priv->txpenab = 0; + return 0; +} + +static int i2s_txp_configure(struct s5j_i2s_s *priv) +{ + int i; + + /* Here I initialize dma task structures for TXP channel */ + for (i = 0; i < CONFIG_S5J_I2S_MAXINFLIGHT; i++) { + priv->containers_tx[i].dmatask = dma_task_m2p_sb_4B_x256_alloc(DMA_I2S_TX); + + if (priv->containers_tx[i].dmatask == NULL) { + goto err; + } + } + + priv->txpenab = 1; + return OK; + +err: + i2s_txp_cfg_clr(priv); + return -ENOMEM; +} + +static int i2s_txs_configure(struct s5j_i2s_s *priv) +{ + priv->txsenab = 0; + return OK; +} + +/**************************************************************************** + * Name: i2s_dma_allocate + * + * Description: + * Allocate I2S DMA channels + * + * Input Parameters: + * priv - Partially initialized I2S device structure. This function + * will complete the DMA specific portions of the initialization + * + * Returned Value: + * OK on success; A negated errno value on failure. + * + ****************************************************************************/ + +static int i2s_dma_allocate(struct s5j_i2s_s *priv) +{ +#ifdef I2S_HAVE_RX + if (priv->rxenab) { + /* Allocate an RX DMA channel */ + + priv->rx.dma = s5j_dmachannel(CONFIG_I2S_RX_DMACH, "pdma"); + if (!priv->rx.dma) { + lldbg("ERROR: Failed to allocate the RX DMA channel\n"); + goto errout; + } + + /* Create a watchdog time to catch RX DMA timeouts */ + + priv->rx.dog = wd_create(); + if (!priv->rx.dog) { + lldbg("ERROR: Failed to create the RX DMA watchdog\n"); + goto errout; + } + } +#endif + +#ifdef I2S_HAVE_TX_P + if (priv->txpenab) { + /* Allocate a TX DMA channel */ + + priv->txp.dma = s5j_dmachannel(CONFIG_I2S_TXP_DMACH, "pdma"); + if (!priv->txp.dma) { + lldbg("ERROR: Failed to allocate the TXP DMA channel\n"); + goto errout; + } + + /* Create a watchdog time to catch TX DMA timeouts */ + + priv->txp.dog = wd_create(); + if (!priv->txp.dog) { + lldbg("ERROR: Failed to create the TXP DMA watchdog\n"); + goto errout; + } + } +#endif + +#ifdef I2S_HAVE_TX_S + if (priv->txsenab) { + /* Allocate a TX DMA channel */ + + priv->txs.dma = s5j_dmachannel(CONFIG_I2S_TXS_DMACH, "pdma"); + if (!priv->txs.dma) { + lldbg("ERROR: Failed to allocate the TX DMA channel\n"); + goto errout; + } + + /* Create a watchdog time to catch TX DMA timeouts */ + + priv->txs.dog = wd_create(); + if (!priv->txs.dog) { + lldbg("ERROR: Failed to create the TX DMA watchdog\n"); + goto errout; + } + } +#endif + + /* Success exit */ + + return OK; + + /* Error exit */ + +errout: + i2s_dma_free(priv); + return -ENOMEM; +} + +/**************************************************************************** + * Name: i2s_dma_free + * + * Description: + * Release DMA-related resources allocated by i2s_dma_allocate() + * + * Input Parameters: + * priv - Partially initialized I2S device structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void i2s_dma_free(struct s5j_i2s_s *priv) +{ +#ifdef I2S_HAVE_TX_P + if (priv->txp.dog) { + wd_delete(priv->txp.dog); + priv->txp.dog = NULL; + } + + if (priv->txp.dma) { + s5j_dmafree(priv->txp.dma); + priv->txp.dma = NULL; + } +#endif + +#ifdef I2S_HAVE_TX_S + if (priv->txs.dog) { + wd_delete(priv->txs.dog); + priv->txs.dog = NULL; + } + + if (priv->txs.dma) { + s5j_dmafree(priv->txs.dma); + priv->txs.dma = NULL; + } +#endif + +#ifdef i2s_HAVE_RX + if (priv->rx.dog) { + wd_delete(priv->rx.dog); + priv->rx.dog = NULL; + } + + if (priv->rx.dma) { + s5j_dmafree(priv->rx.dma); + priv->rx.dma = NULL; + } +#endif +} + +/**************************************************************************** + * Name: i2s_configure + * + * Description: + * Configure i2s + * + * Input Parameters: + * priv - Partially initialized I2S device structure. These functions + * will complete the I2S specific portions of the initialization + * + * Returned Value: + * configure status + * + ****************************************************************************/ + +static int i2s_configure(struct s5j_i2s_s *priv) +{ + unsigned int reg; + int ret; + + /* GPIO initialisation */ + + priv->base = S5J_I2S_BASE; + + /* Reset I2S */ + putreg32(0, priv->base + S5J_I2S_CON); + modifyreg32(priv->base + S5J_I2S_CON, I2S_CR_SW_RST_MASK, + I2S_CR_SW_RST_RELEASE); + + /* Set Mode */ + reg = I2S_MOD_OP_CLK_PCLK | I2S_MOD_CDCLKCON_IN | I2S_MOD_MSS_SLAVE | + I2S_MOD_RCLKSRC | I2S_MOD_TXR(TXR_TXRX) | I2S_MOD_RFS(RFS_256) | + I2S_MOD_BFS(BFS_64); + putreg32(reg, priv->base + S5J_I2S_MOD); + + ret = s5j_configgpio(GPIO_I2S0_BCLK); + if (ret < 0) { + return ret; + } + ret = s5j_configgpio(GPIO_I2S0_LRCK); + if (ret < 0) { + return ret; + } + ret = s5j_configgpio(GPIO_I2S0_SDO); + if (ret < 0) { + return ret; + } + ret = s5j_configgpio(GPIO_I2S0_SDI); + if (ret < 0) { + return ret; + } + + cal_clk_mux(i2s_mux, i2s_bclk); + + /* We are always slave here ... Below code should set priv structure */ + /* Set priv structure here */ + priv->datalen = CONFIG_S5J_I2S_DATALEN; + priv->samplerate = CONFIG_S5J_I2S_SAMPLERATE; + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: + * + * Description: + * Initialize the selected i2s port + * + * Input Parameter: + * none + * + * Returned Value: + * Valid i2s device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +struct i2s_dev_s *s5j_i2s_initialize(void) +{ + struct s5j_i2s_s *priv; + int ret; + + /* Allocate a new state structure for this chip select. NOTE that there + * is no protection if the same chip select is used in two different + * chip select structures. + */ + priv = (struct s5j_i2s_s *)zalloc(sizeof(struct s5j_i2s_s)); + if (!priv) { + lldbg("ERROR: Failed to allocate a chip select structure\n"); + return NULL; + } + + /* Initialize the I2S priv device structure */ + sem_init(&priv->exclsem, 0, 1); + priv->dev.ops = &g_i2sops; + + /* Initialize buffering */ + i2s_buf_rx_initialize(priv); + i2s_buf_tx_initialize(priv); + + /* configure i2s port */ + ret = i2s_configure(priv); + +#ifdef I2S_HAVE_RX + /* Configure the receiver */ + + ret = i2s_rx_configure(priv); + if (ret < 0) { + lldbg("ERROR: Failed to configure the receiver: %d\n", ret); + goto errout_with_clocking; + } +#endif + +#ifdef I2S_HAVE_TX_P + /* Configure the transmitter */ + + ret = i2s_txp_configure(priv); + if (ret < 0) { + lldbg("ERROR: Failed to configure the primary transmitter: %d\n", ret); + goto errout_with_clocking; + } +#endif + +#ifdef I2S_HAVE_TX_S + ret = i2s_txs_configure(priv); + if (ret < 0) { + lldbg("ERROR: Failed to configure the secondary transmitter: %d\n", ret); + goto errout_with_clocking; + } +#endif + + /* Allocate DMA channels */ + ret = i2s_dma_allocate(priv); + if (ret < 0) { + goto errout_with_alloc; + } + + /* Success exit */ + return &priv->dev; + + /* Failure exits */ + +errout_with_alloc: + i2s_rx_cfg_clr(priv); + i2s_txp_cfg_clr(priv); + +errout_with_clocking: + + sem_destroy(&priv->exclsem); + kmm_free(priv); + return NULL; +} + +#endif /* I2S_HAVE_RX || I2S_HAVE_TX_P I2S_HAVE_TX_S */ diff --git a/os/arch/arm/src/s5j/s5j_i2s.h b/os/arch/arm/src/s5j/s5j_i2s.h new file mode 100755 index 0000000..2057e35 --- /dev/null +++ b/os/arch/arm/src/s5j/s5j_i2s.h @@ -0,0 +1,125 @@ +/**************************************************************************** + * + * Copyright 2017 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ +/************************************************************************************ + * arch/arm/src/sama5/sam_ssc.h + * + * Copyright (C) 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. Neither the name NuttX 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. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_S5JT200_S5J_I2S_H +#define __ARCH_ARM_SRC_S5JT200_S5J_I2S_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include + +#include +#include + +#include "s5j_gpio.h" +#include "chip.h" +#include "chip/s5j_i2s.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/************************************************************************************ + * Inline Functions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" { +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +#define enter_critical_section() 0 +#define leave_critical_section(x) + +/**************************************************************************** + * Name: s5j_i2s_initialize + * + * Description: + * Initialize the selected I2S port. + * + * Input Parameter: + * Port number (for hardware that has mutiple I2S interfaces) + * + * Returned Value: + * Valid I2S device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +FAR struct i2s_dev_s *s5j_i2s_initialize(void); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_S5JT200_S5J_I2S_H */