diff --git a/TODO b/TODO index 712d183bfb0..f7016ae637a 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ -NuttX TODO List (Last updated September 27, 2013) +NuttX TODO List (Last updated November 7, 2013) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This file summarizes known NuttX bugs, limitations, inconsistencies with diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index eaba1681b02..50564476a9b 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -250,11 +250,13 @@ config SAMA5_SSC0 bool "Synchronous Serial Controller 0 (SSC0)" default n select I2S + depends on SAMA5_DMAC0 config SAMA5_SSC1 bool "Synchronous Serial Controller 1 (SSC1)" default n select I2S + depends on SAMA5_DMAC1 config SAMA5_CAN0 bool "CAN controller 0 (CAN0)" @@ -1378,27 +1380,23 @@ endif # SAMA5_TWI0 || SAMA5_TWI1 || SAMA5_TWI2 if SAMA5_SSC0 || SAMA5_SSC1 menu "SSC Configuration" -config SAMA5_SSC_DMA - bool "Enable SSC DMA" - default n - depends on (SAMA5_DMAC0 && SAMA5_SSC0) || (SAMA5_DMAC1 && SAMA5_SSC1) - ---help--- - Enable use of DMA in I2C tranfers - -config SAMA5_SSC_DMATHRESHOLD - int "DMA transfer threshold" - default 4 - depends on SAMA5_SSC_DMA - ---help--- - Small I2S transfers are better performed without using DMA. This - setting defines a threshold to select when small transfer should - be performed without using DMA. - if SAMA5_SSC0 comment "SSC0 Configuration" +config SAMA5_SSC0_MASTER + bool "Master mode" + default n + ---help--- + Selects master (vs. slave) mode data transfers + +config SAMA5_SSC0_DATALEN + int "Data width (bits)" + default 16 + ---help--- + Data width in bits. + config SAMA5_SSC0_RX - bool "Enable I2C receive" + bool "Enable I2C receiver" default n ---help--- Enable I2S receipt logic @@ -1407,9 +1405,9 @@ if SAMA5_SSC0_RX choice prompt "Receiver clock source" - default SAMA5_SSC0_RX_INTCLK + default SAMA5_SSC0_RX_MCKDIV -config SAMA5_SSC0_RX_EXTCLK +config SAMA5_SSC0_RX_RKINPUT bool "External Clock" ---help--- The SSC receiver clock is an external clock provided on the RK input @@ -1420,7 +1418,7 @@ config SAMA5_SSC0_RX_TXCLK ---help--- The SSC receiver clock is transmitter clock. -config SAMA5_SSC0_RX_INTCLK +config SAMA5_SSC0_RX_MCKDIV bool "MCK/2" ---help--- The SSC receiver clock is the MCK/2 divided by a up to 4095. @@ -1430,15 +1428,31 @@ endchoice # Receiver clock source config SAMA5_SSC0_RX_EXTFREQ int "External reciver clock frequency" default 100 - depends on SAMA5_SSC0_RX_EXTCLK + depends on SAMA5_SSC0_RX_RKINPUT ---help--- If the receiver clock is provided via a clock input on the RK pin, then the frequency of the receiver clock must be provided. +if !SAMA5_SSC0_RX_RKINPUT +choice + prompt "Receiver output clock" + default SAMA5_SSC0_RX_RKOUTPUT_NONE + +config SAMA5_SSC0_RX_RKOUTPUT_NONE + bool "None" + +config SAMA5_SSC0_RX_RKOUTPUT_CONT + bool "Continuous" + +config SAMA5_SSC0_RX_RKOUTPUT_XFR + bool "Only during transfers" + +endchoice # Receiver output clock +endif # !SAMA5_SSC0_RX_RKINPUT endif # SAMA5_SSC0_RX config SAMA5_SSC0_TX - bool "Enable I2C transmit" + bool "Enable I2C transmitter" default n ---help--- Enable I2S transmission logic @@ -1447,9 +1461,9 @@ if SAMA5_SSC0_TX choice prompt "Transmitter clock source" - default SAMA5_SSC0_TX_INTCLK + default SAMA5_SSC0_TX_MCKDIV -config SAMA5_SSC0_TX_EXTCLK +config SAMA5_SSC0_TX_TKINPUT bool "External Clock" ---help--- The SSC transmitter clock is an external clock provided on the TK input @@ -1460,7 +1474,7 @@ config SAMA5_SSC0_TX_RXCLK ---help--- The SSC transmitter clock is receiver clock. -config SAMA5_SSC0_TX_INTCLK +config SAMA5_SSC0_TX_MCKDIV bool "MCK/2" ---help--- The SSC transmitter clock is the MCK/2 divided by a up to 4095. @@ -1470,17 +1484,33 @@ endchoice # Transmitter clock source config SAMA5_SSC0_TX_EXTFREQ int "External transmitter clock frequency" default 100 - depends on SAMA5_SSC0_TX_EXTCLK + depends on SAMA5_SSC0_TX_TKINPUT ---help--- If the transmitter clock is provided via a clock input on the TK pin, then the frequency of the transmitter clock must be provided. +if !SAMA5_SSC0_TX_TKINPUT +choice + prompt "Transmitter output clock" + default SAMA5_SSC0_TX_TKOUTPUT_NONE + +config SAMA5_SSC0_TX_TKOUTPUT_NONE + bool "None" + +config SAMA5_SSC0_TX_TKOUTPUT_CONT + bool "Continuous" + +config SAMA5_SSC0_TX_TKOUTPUT_XFR + bool "Only during transfers" + +endchoice # Receiver output clock +endif # !SAMA5_SSC0_TX_TKINPUT endif # SAMA5_SSC0_TX -config SAMA5_SSC0_TX_INTCLK_FREQUENCY +config SAMA5_SSC0_MCKDIV_FREQUENCY int "Internal transmitter clock frequency" default 100 - depends on SAMA5_SSC0_RX_INTCLK || SAMA5_SSC0_TX_INTCLK + depends on SAMA5_SSC0_RX_MCKDIV || SAMA5_SSC0_TX_MCKDIV ---help--- If the either the receiver or transmitter clock is provided by MCK/2 divided down, then the target frequency must be provided. The SSC driver will @@ -1493,8 +1523,20 @@ endif # SAMA5_SSC0 if SAMA5_SSC1 comment "SSC1 Configuration" +config SAMA5_SSC1_MASTER + bool "Master mode" + default n + ---help--- + Selects master (vs. slave) mode data transfers + +config SAMA5_SSC1_DATALEN + int "Data width (bits)" + default 16 + ---help--- + Data width in bits. + config SAMA5_SSC1_RX - bool "Enable I2C receive" + bool "Enable I2C receiver" default n ---help--- Enable I2S receipt logic @@ -1503,9 +1545,9 @@ if SAMA5_SSC1_RX choice prompt "Receiver clock source" - default SAMA5_SSC1_RX_INTCLK + default SAMA5_SSC1_RX_MCKDIV -config SAMA5_SSC1_RX_EXTCLK +config SAMA5_SSC1_RX_RKINPUT bool "External Clock" ---help--- The SSC receiver clock is an external clock provided on the RK input @@ -1516,7 +1558,7 @@ config SAMA5_SSC1_RX_TXCLK ---help--- The SSC receiver clock is transmitter clock. -config SAMA5_SSC1_RX_INTCLK +config SAMA5_SSC1_RX_MCKDIV bool "MCK/2" ---help--- The SSC receiver clock is the MCK/2 divided by a up to 4095. @@ -1526,15 +1568,31 @@ endchoice # Receiver clock source config SAMA5_SSC1_RX_EXTFREQ int "External reciver clock frequency" default 100 - depends on SAMA5_SSC1_RX_EXTCLK + depends on SAMA5_SSC1_RX_RKINPUT ---help--- If the receiver clock is provided via a clock input on the RK pin, then the frequency of the receiver clock must be provided. +if !SAMA5_SSC1_RX_RKINPUT +choice + prompt "Receiver output clock" + default SAMA5_SSC1_RX_RKOUTPUT_NONE + +config SAMA5_SSC1_RX_RKOUTPUT_NONE + bool "None" + +config SAMA5_SSC1_RX_RKOUTPUT_CONT + bool "Continuous" + +config SAMA5_SSC1_RX_RKOUTPUT_XFR + bool "Only during transfers" + +endchoice # Receiver output clock +endif # !SAMA5_SSC1_RX_RKINPUT endif # SAMA5_SSC0_RX config SAMA5_SSC1_TX - bool "Enable I2C transmit" + bool "Enable I2C transmitter" default n ---help--- Enable I2S transmission logic @@ -1543,9 +1601,9 @@ if SAMA5_SSC1_TX choice prompt "Transmitter clock source" - default SAMA5_SSC1_TX_INTCLK + default SAMA5_SSC1_TX_MCKDIV -config SAMA5_SSC1_TX_EXTCLK +config SAMA5_SSC1_TX_TKINPUT bool "External Clock" ---help--- The SSC transmitter clock is an external clock provided on the TK input @@ -1556,7 +1614,7 @@ config SAMA5_SSC1_TX_RXCLK ---help--- The SSC transmitter clock is receiver clock. -config SAMA5_SSC1_TX_INTCLK +config SAMA5_SSC1_TX_MCKDIV bool "MCK/2" ---help--- The SSC transmitter clock is the MCK/2 divided by a up to 4095. @@ -1566,17 +1624,33 @@ endchoice # Transmitter clock source config SAMA5_SSC1_TX_EXTFREQ int "External transmitter clock frequency" default 100 - depends on SAMA5_SSC1_TX_EXTCLK + depends on SAMA5_SSC1_TX_TKINPUT ---help--- If the transmitter clock is provided via a clock input on the TK pin, then the frequency of the transmitter clock must be provided. +if !SAMA5_SSC1_TX_TKINPUT +choice + prompt "Transmitter output clock" + default SAMA5_SSC1_TX_TKOUTPUT_NONE + +config SAMA5_SSC1_TX_TKOUTPUT_NONE + bool "None" + +config SAMA5_SSC1_TX_TKOUTPUT_CONT + bool "Continuous" + +config SAMA5_SSC1_TX_TKOUTPUT_XFR + bool "Only during transfers" + +endchoice # Receiver output clock +endif # !SAMA5_SSC1_TX_TKINPUT endif # SAMA5_SSC1_TX -config SAMA5_SSC1_TX_INTCLK_FREQUENCY +config SAMA5_SSC1_MCKDIV_FREQUENCY int "Internal transmitter clock frequency" default 100 - depends on SAMA5_SSC1_RX_INTCLK || SAMA5_SSC1_TX_INTCLK + depends on SAMA5_SSC1_RX_MCKDIV || SAMA5_SSC1_TX_MCKDIV ---help--- If the either the receiver or transmitter clock is provided by MCK/2 divided down, then the target frequency must be provided. The SSC driver will @@ -1588,7 +1662,7 @@ endif # SAMA5_SSC1 config SAMA5_SSC_DMADEBUG bool "SSC DMA transfer debug" - depends on SAMA5_SSC_DMA && DEBUG && DEBUG_DMA + depends on DEBUG && DEBUG_DMA default n ---help--- Enable special debug instrumentation analyze SSC DMA data transfers. diff --git a/arch/arm/src/sama5/sam_ssc.c b/arch/arm/src/sama5/sam_ssc.c index 0cd024e478d..601a8879ca2 100644 --- a/arch/arm/src/sama5/sam_ssc.c +++ b/arch/arm/src/sama5/sam_ssc.c @@ -74,34 +74,40 @@ * Definitions ****************************************************************************/ /* Configuration ************************************************************/ -/* When SSC DMA is enabled, small DMA transfers will still be performed by - * polling logic. But we need a threshold value to determine what is small. - * That value is provided by CONFIG_SAMA5_SSC_DMATHRESHOLD. - */ -#ifndef CONFIG_SAMA5_SSC_DMATHRESHOLD -# define CONFIG_SAMA5_SSC_DMATHRESHOLD 4 +#if defined(CONFIG_SAMA5_SSC0) && !defined(CONFIG_SAMA5_DMAC0) +# error CONFIG_SAMA5_DMAC0 required by SSC0 #endif -#ifdef CONFIG_SAMA5_SSC_DMA - -# if defined(CONFIG_SAMA5_SSC0) && defined(CONFIG_SAMA5_DMAC0) -# define SAMA5_SSC0_DMA true -# else -# define SAMA5_SSC0_DMA false -# endif - -# if defined(CONFIG_SAMA5_SSC1) && defined(CONFIG_SAMA5_DMAC1) -# define SAMA5_SSC1_DMA true -# else -# define SAMA5_SSC1_DMA false -# endif +#if defined(CONFIG_SAMA5_SSC1) && !defined(CONFIG_SAMA5_DMAC1) +# error CONFIG_SAMA5_DMAC1 required by SSC1 #endif -#ifndef CONFIG_SAMA5_SSC_DMA -# undef CONFIG_SAMA5_SSC_DMADEBUG +#ifndef CONFIG_SAMA5_SSC0_DATALEN +# define CONFIG_SAMA5_SSC0_DATALEN 16 #endif +#if CONFIG_SAMA5_SSC0_DATALEN < 2 || CONFIG_SAMA5_SSC0_DATALEN > 32 +# error Invalid value for CONFIG_SAMA5_SSC0_DATALEN +#endif + +/* Check if we need to build RX and/or TX support */ + +#undef SSC_HAVE_RX +#undef SSC_HAVE_TX + +#if (defined(CONFIG_SAMA5_SSC0) && defined(CONFIG_SAMA5_SSC0_RX)) || \ + (defined(CONFIG_SAMA5_SSC1) && defined(CONFIG_SAMA5_SSC1_RX)) +# define SSC_HAVE_RX +#endif + +#if (defined(CONFIG_SAMA5_SSC0) && defined(CONFIG_SAMA5_SSC0_TX)) || \ + (defined(CONFIG_SAMA5_SSC1) && defined(CONFIG_SAMA5_SSC1_TX)) +# define SSC_HAVE_TX +#endif + +#define SSC_DATNB (1) /* Data number per frame */ + /* Clocking *****************************************************************/ /* Select MCU-specific settings * @@ -125,6 +131,21 @@ # error Cannot realize SSC input frequency #endif +/* Clock source definitions */ + +#define SSC_CLKSRC_NONE 0 /* No clock */ +#define SSC_CLKSRC_MCKDIV 1 /* Clock source is MCK divided down */ +#define SSC_CLKSRC_RXOUT 2 /* Transmitter clock source is the receiver clock */ +#define SSC_CLKSRC_TXOUT 2 /* Receiver clock source is the transmitter clock */ +#define SSC_CLKSRC_TKIN 3 /* Transmitter clock source is TK */ +#define SSC_CLKSRC_RKIN 3 /* Receiver clock source is RK */ + +/* Clock output definitions */ + +#define SSC_CLKOUT_NONE 0 /* No output clock */ +#define SSC_CLKOUT_CONT 1 /* Continuous */ +#define SSC_CLKOUT_XFER 2 /* Only output clock during transfers */ + /* DMA timeout. The value is not critical; we just don't want the system to * hang in the event that a DMA does not finish. This is set to */ @@ -179,17 +200,27 @@ struct sam_ssc_s struct i2s_dev_s dev; /* Externally visible I2S interface */ uint32_t base; /* SSC controller register base address */ sem_t exclsem; /* Assures mutually exclusive acess to SSC */ -#if defined(CONFIG_SAMA5_SSC0) || defined(CONFIG_SAMA5_SSC1) - uint8_t sscno; /* SSC controller number (0 or 1) */ -#endif - -#ifdef CONFIG_SAMA5_SSC_DMA + uint16_t master:1; /* True: Master mode transfers */ + uint16_t rx:1; /* True: RX transfers supported */ + uint16_t tx:1; /* True: TX transfers supported */ + uint16_t sscno:1; /* SSC controller number (0 or 1) */ + uint16_t rxclk:2; /* Receiver clock source. See SSC_CLKSRC_* definitions */ + uint16_t txclk:2; /* Transmitter clock source. See SSC_CLKSRC_* definitions */ + uint16_t rxout:2; /* Receiver clock output. See SSC_CLKOUT_* definitions */ + uint16_t txout:2; /* Transmitter clock output. See SSC_CLKOUT_* definitions */ + uint8_t datalen; /* Data width (2-32) */ uint8_t pid; /* Peripheral ID */ - bool candma; /* DMA is supported */ sem_t dmawait; /* Used to wait for DMA completion */ + uint32_t frequency; /* Target MCK frequency */ WDOG_ID dmadog; /* Watchdog that handles DMA timeouts */ int result; /* DMA result */ + +#ifdef SSC_HAVE_RX + uint32_t rxcmr; /* Value of CMR register for RX transfers */ DMA_HANDLE rxdma; /* SSC RX DMA handle */ +#endif +#ifdef SSC_HAVE_TX + uint32_t txcmr; /* Value of CMR register for TX transfers */ DMA_HANDLE txdma; /* SSC TX DMA handle */ #endif @@ -200,10 +231,13 @@ struct sam_ssc_s uint32_t regaddr; /* Last address */ uint32_t regval; /* Last value */ int count; /* Number of times */ +#endif /* CONFIG_SAMA5_SSC_REGDEBUG */ + +#if defined(CONFIG_SAMA5_SSC_DMADEBUG) && defined(SSC_HAVE_RX) + struct sam_dmaregs_s rxdmaregs[DMA_NSAMPLES]; #endif -#ifdef CONFIG_SAMA5_SSC_DMADEBUG - struct sam_dmaregs_s rxdmaregs[DMA_NSAMPLES]; +#if defined(CONFIG_SAMA5_SSC_DMADEBUG) && defined(SSC_HAVE_TX) struct sam_dmaregs_s txdmaregs[DMA_NSAMPLES]; #endif }; @@ -224,10 +258,9 @@ static bool ssc_checkreg(struct sam_ssc_s *priv, bool wr, uint32_t value, static inline uint32_t ssc_getreg(struct sam_ssc_s *priv, unsigned int offset); static inline void ssc_putreg(struct sam_ssc_s *priv, uint32_t value, unsigned int offset); -#ifdef CONFIG_SAMA5_SSC_DMA static inline uintptr_t ssc_physregaddr(struct sam_ssc_s *priv, unsigned int offset); -#endif + #if defined(CONFIG_DEBUG_I2S) && defined(CONFIG_DEBUG_VERBOSE) static void ssc_dumpregs(struct sam_ssc_s *priv, const char *msg); #else @@ -236,34 +269,40 @@ static void ssc_dumpregs(struct sam_ssc_s *priv, const char *msg); /* DMA support */ -#if defined(CONFIG_SAMA5_SSC_DMADEBUG) +#if defined(CONFIG_SAMA5_SSC_DMADEBUG) && defined(SSC_HAVE_RX) # define ssc_rxdma_sample(s,i) sam_dmasample((s)->rxdma, &(s)->rxdmaregs[i]) -# define ssc_txdma_sample(s,i) sam_dmasample((s)->txdma, &(s)->txdmaregs[i]) -static void ssc_dma_sampleinit(struct sam_ssc_s *priv); -static void ssc_dma_sampledone(struct sam_ssc_s *priv); +static void ssc_rxdma_sampleinit(struct sam_ssc_s *priv); +static void ssc_rxdma_sampledone(struct sam_ssc_s *priv); #else # define ssc_rxdma_sample(s,i) -# define ssc_txdma_sample(s,i) -# define ssc_dma_sampleinit(s) -# define ssc_dma_sampledone(s) +# define ssc_rxdma_sampleinit(s) +# define ssc_rxdma_sampledone(s) #endif -#ifdef CONFIG_SAMA5_SSC_DMA +#if defined(CONFIG_SAMA5_SSC_DMADEBUG) && defined(SSC_HAVE_TX) +# define ssc_txdma_sample(s,i) sam_dmasample((s)->txdma, &(s)->txdmaregs[i]) +static void ssc_txdma_sampleinit(struct sam_ssc_s *priv); +static void ssc_txdma_sampledone(struct sam_ssc_s *priv); + +#else +# define ssc_txdma_sample(s,i) +# define ssc_txdma_sampleinit(s) +# define ssc_txdma_sampledone(s) + +#endif + +#ifdef SSC_HAVE_RX static void ssc_rxcallback(DMA_HANDLE handle, void *arg, int result); +#endif +#ifdef SSC_HAVE_TX static void ssc_txcallback(DMA_HANDLE handle, void *arg, int result); #endif /* I2S methods */ static uint32_t ssc_frequency(FAR struct i2s_dev_s *dev, uint32_t frequency); -#ifdef CONFIG_SAMA5_SSC_DMA -static void ssc_send_nodma(FAR struct i2s_dev_s *dev, - FAR const void *buffer, size_t nbytes); -static void ssc_receive_nodma(FAR struct i2s_dev_s *dev, FAR void *buffer, - size_t nbytes); -#endif static void ssc_send(struct i2s_dev_s *dev, const void *buffer, size_t nbytes); static void ssc_receive(struct i2s_dev_s *dev, void *buffer, @@ -271,6 +310,12 @@ static void ssc_receive(struct i2s_dev_s *dev, void *buffer, /* Initialization */ +#ifdef SSC_HAVE_RX +static int ssc_rx_configure(struct sam_ssc_s *priv); +#endif +#ifdef SSC_HAVE_TX +static int ssc_tx_configure(struct sam_ssc_s *priv); +#endif #ifdef CONFIG_SAMA5_SSC0 static void ssc0_configure(struct sam_ssc_s *priv); #endif @@ -407,13 +452,11 @@ static inline void ssc_putreg(struct sam_ssc_s *priv, uint32_t value, * ****************************************************************************/ -#ifdef CONFIG_SAMA5_SSC_DMA static inline uintptr_t ssc_physregaddr(struct sam_ssc_s *priv, unsigned int offset) { return sam_physregaddr(priv->base + offset); } -#endif /**************************************************************************** * Name: ssc_dumpregs @@ -452,10 +495,10 @@ static void ssc_dumpregs(struct sam_ssc_s *priv, const char *msg) #endif /**************************************************************************** - * Name: ssc_dma_sampleinit + * Name: ssc_rxdma_sampleinit * * Description: - * Initialize sampling of DMA registers (if CONFIG_SAMA5_SSC_DMADEBUG) + * Initialize sampling of RX DMA registers (if CONFIG_SAMA5_SSC_DMADEBUG) * * Input Parameters: * priv - Chip select doing the DMA @@ -465,26 +508,51 @@ static void ssc_dumpregs(struct sam_ssc_s *priv, const char *msg) * ****************************************************************************/ -#ifdef CONFIG_SAMA5_SSC_DMADEBUG -static void ssc_dma_sampleinit(struct sam_ssc_s *priv) +#if defined(CONFIG_SAMA5_SSC_DMADEBUG) && defined(SSC_HAVE_RX) +static void ssc_rxdma_sampleinit(struct sam_ssc_s *priv, bool tx) { /* Put contents of register samples into a known state */ memset(priv->rxdmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); - memset(priv->txdmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); /* Then get the initial samples */ sam_dmasample(priv->rxdma, &priv->rxdmaregs[DMA_INITIAL]); +} +#endif + +/**************************************************************************** + * Name: ssc_txdma_sampleinit + * + * Description: + * Initialize sampling of TX DMA registers (if CONFIG_SAMA5_SSC_DMADEBUG) + * + * Input Parameters: + * priv - Chip select doing the DMA + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_SAMA5_SSC_DMADEBUG) && defined(SSC_HAVE_TX) +static void ssc_txdma_sampleinit(struct sam_ssc_s *priv) +{ + /* Put contents of register samples into a known state */ + + memset(priv->txdmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); + + /* Then get the initial samples */ + sam_dmasample(priv->txdma, &priv->txdmaregs[DMA_INITIAL]); } #endif /**************************************************************************** - * Name: ssc_dma_sampledone + * Name: ssc_rxdma_sampledone * * Description: - * Dump sampled DMA registers + * Dump sampled RX DMA registers * * Input Parameters: * priv - Chip select doing the DMA @@ -494,33 +562,26 @@ static void ssc_dma_sampleinit(struct sam_ssc_s *priv) * ****************************************************************************/ -#ifdef CONFIG_SAMA5_SSC_DMADEBUG -static void ssc_dma_sampledone(struct sam_ssc_s *priv) +#if defined(CONFIG_SAMA5_SSC_DMADEBUG) && defined(SSC_HAVE_RX) +static void ssc_rxdma_sampledone(struct sam_ssc_s *priv) { /* Sample the final registers */ sam_dmasample(priv->rxdma, &priv->rxdmaregs[DMA_END_TRANSFER]); - sam_dmasample(priv->txdma, &priv->txdmaregs[DMA_END_TRANSFER]); /* Then dump the sampled DMA registers */ /* Initial register values */ - sam_dmadump(priv->txdma, &priv->txdmaregs[DMA_INITIAL], - "TX: Initial Registers"); sam_dmadump(priv->rxdma, &priv->rxdmaregs[DMA_INITIAL], "RX: Initial Registers"); /* Register values after DMA setup */ - sam_dmadump(priv->txdma, &priv->txdmaregs[DMA_AFTER_SETUP], - "TX: After DMA Setup"); sam_dmadump(priv->rxdma, &priv->rxdmaregs[DMA_AFTER_SETUP], "RX: After DMA Setup"); /* Register values after DMA start */ - sam_dmadump(priv->txdma, &priv->txdmaregs[DMA_AFTER_START], - "TX: After DMA Start"); sam_dmadump(priv->rxdma, &priv->rxdmaregs[DMA_AFTER_START], "RX: After DMA Start"); @@ -532,11 +593,6 @@ static void ssc_dma_sampledone(struct sam_ssc_s *priv) * samples either, but we don't know for sure. */ - sam_dmadump(priv->txdma, &priv->txdmaregs[DMA_CALLBACK], - "TX: At DMA callback"); - - /* Register values at the end of the DMA */ - if (priv->result == -ETIMEDOUT) { sam_dmadump(priv->rxdma, &priv->rxdmaregs[DMA_TIMEOUT], @@ -550,6 +606,53 @@ static void ssc_dma_sampledone(struct sam_ssc_s *priv) sam_dmadump(priv->rxdma, &priv->rxdmaregs[DMA_END_TRANSFER], "RX: At End-of-Transfer"); +} +#endif + +/**************************************************************************** + * Name: ssc_txdma_sampledone + * + * Description: + * Dump sampled DMA registers + * + * Input Parameters: + * priv - Chip select doing the DMA + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_SAMA5_SSC_DMADEBUG) && defined(SSC_HAVE_TX) +static void ssc_txdma_sampledone(struct sam_ssc_s *priv) +{ + /* Sample the final registers */ + + sam_dmasample(priv->txdma, &priv->txdmaregs[DMA_END_TRANSFER]); + + /* Then dump the sampled DMA registers */ + /* Initial register values */ + + sam_dmadump(priv->txdma, &priv->txdmaregs[DMA_INITIAL], + "TX: Initial Registers"); + + /* Register values after DMA setup */ + + sam_dmadump(priv->txdma, &priv->txdmaregs[DMA_AFTER_SETUP], + "TX: After DMA Setup"); + + /* Register values after DMA start */ + + sam_dmadump(priv->txdma, &priv->txdmaregs[DMA_AFTER_START], + "TX: After DMA Start"); + + /* Register values at the time of the TX and RX DMA callbacks + * -OR- DMA timeout. + */ + + sam_dmadump(priv->txdma, &priv->txdmaregs[DMA_CALLBACK], + "TX: At DMA callback"); + sam_dmadump(priv->txdma, &priv->txdmaregs[DMA_END_TRANSFER], "TX: At End-of-Transfer"); } @@ -574,7 +677,6 @@ static void ssc_dma_sampledone(struct sam_ssc_s *priv) * ****************************************************************************/ -#ifdef CONFIG_SAMA5_SSC_DMA static void ssc_dmatimeout(int argc, uint32_t arg) { struct sam_ssc_s *priv = (struct sam_ssc_s *)arg; @@ -594,7 +696,6 @@ static void ssc_dmatimeout(int argc, uint32_t arg) sem_post(&priv->dmawait); } -#endif /**************************************************************************** * Name: ssc_rxcallback @@ -612,7 +713,6 @@ static void ssc_dmatimeout(int argc, uint32_t arg) * ****************************************************************************/ -#ifdef CONFIG_SAMA5_SSC_DMA static void ssc_rxcallback(DMA_HANDLE handle, void *arg, int result) { struct sam_ssc_s *priv = (struct sam_ssc_s *)arg; @@ -641,7 +741,6 @@ static void ssc_rxcallback(DMA_HANDLE handle, void *arg, int result) sem_post(&priv->dmawait); } -#endif /**************************************************************************** * Name: ssc_txcallback @@ -659,7 +758,6 @@ static void ssc_rxcallback(DMA_HANDLE handle, void *arg, int result) * ****************************************************************************/ -#ifdef CONFIG_SAMA5_SSC_DMA static void ssc_txcallback(DMA_HANDLE handle, void *arg, int result) { struct sam_ssc_s *priv = (struct sam_ssc_s *)arg; @@ -679,7 +777,6 @@ static void ssc_txcallback(DMA_HANDLE handle, void *arg, int result) priv->result = result; } } -#endif /**************************************************************************** * Name: ssc_frequency @@ -709,67 +806,6 @@ static uint32_t ssc_frequency(struct i2s_dev_s *dev, uint32_t frequency) return actual; } -/*************************************************************************** - * Name: ssc_send_nodma - * - * Description: - * Send a block of data on SSC without using DMA - * - * Input Parameters: - * dev - Device-specific state data - * buffer - A pointer to the buffer of data to be sent - * nbytes - the length of data to send from the buffer in number of bytes. - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef CONFIG_SAMA5_SSC_DMA -static void ssc_send_nodma(struct i2s_dev_s *dev, const void *buffer, - size_t nbytes) -#else -static void ssc_send(struct i2s_dev_s *dev, const void *buffer, size_t nbytes) -#endif -{ - struct sam_ssc_s *priv = (struct sam_ssc_s *)dev; - DEBUGASSERT(priv); - - i2svdbg("buffer=%p nbytes=%d\n", buffer, (int)nbytes); -#warning Missing logic -} - -/**************************************************************************** - * Name: ssc_receive_nodma - * - * Description: - * Receive a block of data from SSC without using DMA - * - * Input Parameters: - * dev - Device-specific state data - * buffer - A pointer to the buffer in which to recieve data - * nbytes - the length of data that can be received in the buffer in number - * of bytes. - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef CONFIG_SAMA5_SSC_DMA -static void ssc_receive_nodma(struct i2s_dev_s *dev, void *buffer, - size_t nbytes) -#else -static void ssc_receive(struct i2s_dev_s *dev, void *buffer, size_t nbytes) -#endif -{ - struct sam_ssc_s *priv = (struct sam_ssc_s *)dev; - DEBUGASSERT(priv); - - i2svdbg("buffer=%p nbytes=%d\n", buffer, (int)nbytes); -#warning Missing logic -} - /*************************************************************************** * Name: ssc_send * @@ -786,31 +822,27 @@ static void ssc_receive(struct i2s_dev_s *dev, void *buffer, size_t nbytes) * ****************************************************************************/ -#ifdef CONFIG_SAMA5_SSC_DMA static void ssc_send(struct i2s_dev_s *dev, const void *buffer, size_t nbytes) { struct sam_ssc_s *priv = (struct sam_ssc_s *)dev; - DEBUGASSERT(priv); i2svdbg("buffer=%p nbytes=%d\n", buffer, (int)nbytes); + DEBUGASSERT(priv); - /* If we are not configured to do DMA OR is this is a very small transfer, - * then defer the operation to ssc_send_nodma(). - */ +#ifdef SSC_HAVE_TX + /* Has the TX channel been configured? */ - if (!priv->candma || nbytes < CONFIG_SAMA5_SSC_DMATHRESHOLD) + if (!priv->tx) { - ssc_send_nodma(dev, buffer, nbytes); + i2sdbg("ERROR: SSC%d has no transmitter\n", priv->sscno); + return; } - /* Otherwise, perform the transfer using DMA */ - - else - { #warning Missing logic - } -} +#else + i2sdbg("ERROR: SSC%d has no transmitter\n", priv->sscno); #endif +} /**************************************************************************** * Name: ssc_receive @@ -829,29 +861,266 @@ static void ssc_send(struct i2s_dev_s *dev, const void *buffer, size_t nbytes) * ****************************************************************************/ -#ifdef CONFIG_SAMA5_SSC_DMA static void ssc_receive(struct i2s_dev_s *dev, void *buffer, size_t nbytes) { struct sam_ssc_s *priv = (struct sam_ssc_s *)dev; - /* If we are not configured to do DMA OR is this is a very small transfer, - * then defer the operation to ssc_send_nodma(). + i2svdbg("buffer=%p nbytes=%d\n", buffer, (int)nbytes); + DEBUGASSERT(priv); + +#ifdef SSC_HAVE_RX + /* Has the RX channel been configured? */ + + if (!priv->rx) + { + i2sdbg("ERROR: SSC%d has no receiver\n", priv->sscno); + return; + } + +#warning Missing logic +#else + i2sdbg("ERROR: SSC%d has no receiver\n", priv->sscno); +#endif +} + +/**************************************************************************** + * Name: ssc_rx/tx_configure + * + * Description: + * Configure the SSC receiver and transmitter. + * + * Input Parameters: + * priv - Fully initialized SSC device structure. + * + * Returned Value: + * OK is returned on failure. A negated errno value is returned on failure. + * + ****************************************************************************/ + +static int ssc_rx_configure(struct sam_ssc_s *priv) +{ +#ifdef SSC_HAVE_RX + uint32_t regval; + + /* RCMR settings */ + /* Configure the receiver input clock */ + + regval = 0; + switch (priv->rxclk) + { + case SSC_CLKSRC_RKIN: /* Receiver clock source is RK */ + regval = SSC_RCMR_CKS_RK; + break; + + case SSC_CLKSRC_TXOUT: /* Receiver clock source is the transmitter clock */ + regval = SSC_RCMR_CKS_TK; + break; + + case SSC_CLKSRC_MCKDIV: /* Clock source is MCK divided down */ + DEBUGASSERT(priv->frequency != 0); + regval = SSC_RCMR_CKS_MCK; + break; + + case SSC_CLKSRC_NONE: /* No clock */ + default: + i2sdbg("ERROR: No receiver clock\n"); + return -EINVAL; + } + + /* Configure the receiver output clock */ + + switch (priv->rxout) + { + case SSC_CLKOUT_CONT: /* Continuous */ + regval |= SSC_RCMR_CKO_CONT; + break; + + case SSC_CLKOUT_XFER: /* Only output clock during transfers */ + regval |= SSC_RCMR_CKO_TRANSFER; + break; + + case SSC_CLKOUT_NONE: /* No output clock */ + regval |= SSC_RCMR_CKO_NONE; + break; + + default: + i2sdbg("ERROR: Invalid clock output selection\n"); + return -EINVAL; + } + + /* REVISIT: Some of these settings will need to be configurable as well. + * Currently hardcoded to: + * + * SSC_RCMR_CKI Receive clock inversion + * SSC_RCMR_CKG_CONT No receive clock gating + * SSC_RCMR_START_EDGE Detection of any edge on RF signal + * SSC_RCMR_STOP Not selected + * SSC_RCMR_STTDLY(1) Receive start delay = 1 + * SSC_RCMR_PERIOD(0) Receive period divider = 0 */ - if (!priv->candma || nbytes < CONFIG_SAMA5_SSC_DMATHRESHOLD) + regval |= (SSC_RCMR_CKI | SSC_RCMR_CKG_CONT | SSC_RCMR_START_EDGE | + SSC_RCMR_STTDLY(1) | SSC_RCMR_PERIOD(0)); + ssc_putreg(priv, SAM_SSC_RCMR_OFFSET, regval); + + /* RFMR settings. Some of these settings will need to be configurable as well. + * Currently hardcoded to: + * + * SSC_RFMR_DATLEN(n) 'n' deterimined by configuration + * SSC_RFMR_LOOP Loop mode not selected + * SSC_RFMR_MSBF Most significant bit first + * SSC_RFMR_DATNB(n) Data number 'n' per frame (hard-coded) + * SSC_RFMR_FSLEN(0) Receive frame sync length = 0 + * SSC_RFMR_FSOS_NONE RF pin is always in input + * SSC_RFMR_FSEDGE_POS Positive frame sync edge detection + * SSC_RFMR_FSLENEXT(0) FSLEN field extension = 0 + */ + + regval = (SSC_RFMR_DATLEN(CONFIG_SAMA5_SSC0_DATALEN - 1) | SSC_RFMR_MSBF | + SSC_RFMR_DATNB(SSC_DATNB - 1) | SSC_RFMR_FSLEN(0) | + SSC_RFMR_FSOS_NONE | SSC_RFMR_FSLENEXT(0)); + ssc_putreg(priv, SAM_SSC_RFMR_OFFSET, regval); + +#else + ssc_putreg(priv, SAM_SSC_RCMR_OFFSET, 0); + ssc_putreg(priv, SAM_SSC_RFMR_OFFSET, 0); + +#endif + + /* Disable the receiver */ + + ssc_putreg(priv, SAM_SSC_CR_OFFSET, SSC_CR_RXDIS); + return OK; +} + +static int ssc_tx_configure(struct sam_ssc_s *priv) +{ +#ifdef SSC_HAVE_TX + uint32_t regval; + + /* TCMR settings */ + /* Configure the transmitter input clock */ + + regval = 0; + switch (priv->txclk) { - ssc_send_nodma(dev, buffer, nbytes); + case SSC_CLKSRC_TKIN: /* Transmitter clock source is TK */ + regval = SSC_TCMR_CKS_TK; + break; + + case SSC_CLKSRC_RXOUT: /* Transmitter clock source is the receiver clock */ + regval = SSC_TCMR_CKS_RK; + break; + + case SSC_CLKSRC_MCKDIV: /* Clock source is MCK divided down */ + DEBUGASSERT(priv->frequency != 0); + regval = SSC_TCMR_CKS_MCK; + break; + + case SSC_CLKSRC_NONE: /* No clock */ + default: + i2sdbg("ERROR: No transmitter clock\n"); + return -EINVAL; } - /* Otherwise, perform the transfer using DMA */ + /* Configure the receiver output clock */ + switch (priv->txout) + { + case SSC_CLKOUT_CONT: /* Continuous */ + regval |= SSC_TCMR_CKO_CONT; + break; + + case SSC_CLKOUT_XFER: /* Only output clock during transfers */ + regval |= SSC_TCMR_CKO_TRANSFER; + break; + + case SSC_CLKOUT_NONE: /* No output clock */ + regval |= SSC_TCMR_CKO_NONE; + break; + + default: + i2sdbg("ERROR: Invalid clock output selection\n"); + return -EINVAL; + } + + /* REVISIT: Some of these settings will need to be configurable as well. + * Currently hardcoded to: + * + * SSC_RCMR_CKI No transmitter clock inversion + * SSC_RCMR_CKG_CONT No transmit clock gating + * SSC_TCMR_STTDLY(1) Receive start delay = 1 + * + * If master: + * SSC_TCMR_START_FALLING Detection of a falling edge on TF signal + * SSC_TCMR_PERIOD(n) 'n' depends on the datawidth + * + * If slave: + * SSC_TCMR_START_EDGE Detection of any edge on TF signal + * SSC_TCMR_PERIOD(0) Receive period divider = 0 + */ + + if (priv->master) + { + regval |= (SSC_TCMR_CKG_CONT | SSC_TCMR_START_FALLING | SSC_TCMR_STTDLY(1) | + SSC_TCMR_PERIOD(((CONFIG_SAMA5_SSC0_DATALEN * SSC_DATNB) / 2) - 1)); + } else { -#warning Missing logic + regval |= (SSC_TCMR_CKG_CONT | SSC_TCMR_START_EDGE | SSC_TCMR_STTDLY(1) | + SSC_TCMR_PERIOD(0)); } -} + + ssc_putreg(priv, SAM_SSC_TCMR_OFFSET, regval); + + /* RFMR settings. Some of these settings will need to be configurable as well. + * Currently hardcoded to: + * + * SSC_TFMR_DATLEN(n) 'n' deterimined by configuration + * SSC_TFMR_DATDEF Data default = 0 + * SSC_TFMR_MSBF Most significant bit first + * SSC_TFMR_DATNB(n) Data number 'n' per frame (hard-coded) + * SSC_TFMR_FSDEN Frame sync data is not enabled + * SSC_TFMR_FSLENEXT(0) FSLEN field extension = 0 + * + * If master: + * SSC_TFMR_FSLEN(n) Receive frame sync length depends on data width + * SSC_TFMR_FSOS_NEGATIVE Negative pulse TF output + * + * If slave: + * SSC_TFMR_FSLEN(0) Receive frame sync length = 0 + * SSC_TFMR_FSOS_NONE TF is an output + */ + + if (priv->master) + { + regval = (SSC_TFMR_DATLEN(CONFIG_SAMA5_SSC0_DATALEN - 1) | + SSC_TFMR_MSBF | SSC_TFMR_DATNB(SSC_DATNB - 1) | + SSC_TFMR_FSLEN(CONFIG_SAMA5_SSC0_DATALEN - 1) | + SSC_TFMR_FSOS_NEGATIVE | SSC_TFMR_FSLENEXT(0)); + } + else + { + regval = (SSC_TFMR_DATLEN(CONFIG_SAMA5_SSC0_DATALEN - 1) | + SSC_TFMR_MSBF | SSC_TFMR_DATNB(SSC_DATNB - 1) | + SSC_TFMR_FSLEN(0) | SSC_TFMR_FSOS_NONE | + SSC_TFMR_FSLENEXT(0)); + } + + ssc_putreg(priv, SAM_SSC_TFMR_OFFSET, regval); + +#else + ssc_putreg(priv, SAM_SSC_TCMR_OFFSET, 0); + ssc_putreg(priv, SAM_SSC_TFMR_OFFSET, 0); + #endif + /* Disable the transmitter */ + + ssc_putreg(priv, SAM_SSC_CR_OFFSET, SSC_CR_TXDIS); + return OK; +} + /**************************************************************************** * Name: ssc0/1_configure * @@ -859,8 +1128,8 @@ static void ssc_receive(struct i2s_dev_s *dev, void *buffer, size_t nbytes) * Configure SSC0 and/or SSC1 * * Input Parameters: - * i2c - Partially initialized I2C device structure. These functions - * will compolete the SSC specific portions of the initialization + * priv - Partially initialized I2C device structure. These functions + * will compolete the SSC specific portions of the initialization * * Returned Value: * None @@ -870,75 +1139,256 @@ static void ssc_receive(struct i2s_dev_s *dev, void *buffer, size_t nbytes) #ifdef CONFIG_SAMA5_SSC0 static void ssc0_configure(struct sam_ssc_s *priv) { - /* Enable clocking to the SSC0 peripheral */ - - sam_ssc0_enableclk(); - /* Configure multiplexed pins as connected on the board. Chip * select pins must be selected by board-specific logic. */ #ifdef CONFIG_SAMA5_SSC0_RX + priv->rx = true; + + /* Configure the receiver data (RD) and receiver frame synchro (RF) pins */ + sam_configpio(PIO_SSC0_RD); sam_configpio(PIO_SSC0_RF); - sam_configpio(PIO_SSC0_RK); -#ifdef CONFIG_SAMA5_SSC0_RX_EXTCLK + +#if defined(CONFIG_SAMA5_SSC0_RX_RKINPUT) + /* Configure the RK pin only if we are using an external clock to drive + * the receiver clock. + * + * REVISIT: The SSC is also capable of generated the receiver clock + * output on the RK pin. + */ + sam_configpio(PIO_SSC0_RK); /* External clock received on the RK I/O pad */ + priv->rxclk = SSC_CLKSRC_RKIN; + +#elif defined(CONFIG_SAMA5_SSC0_RX_TXCLK) + priv->rxclk = SSC_CLKSRC_TXOUT; + +#elif defined(CONFIG_SAMA5_SSC0_RX_MCKDIV) + priv->rxclk = SSC_CLKSRC_MCKDIV; + +#else + priv->rxclk = SSC_CLKSRC_NONE; + #endif + + /* Remember the configured RX clock output */ + +#if defined(CONFIG_SAMA5_SSC0_RX_RKOUTPUT_CONT) + priv->rxout = SSC_CLKOUT_CONT; /* Continuous */ +#elif defined(CONFIG_SAMA5_SSC0_RX_RKOUTPUT_XFR) + priv->rxout = SSC_CLKOUT_XFER; /* Only output clock during transfers */ +#else /* if defined(CONFIG_SAMA5_SSC0_RX_RKOUTPUT_NONE) */ + priv->rxout = SSC_CLKOUT_NONE; /* No output clock */ +#endif + +#else + priv->rx = false; + priv->rxclk = SSC_CLKSRC_NONE; /* No input clock */ + priv->rxout = SSC_CLKOUT_NONE; /* No output clock */ + #endif /* CONFIG_SAMA5_SSC0_RX */ #ifdef CONFIG_SAMA5_SSC0_TX + priv->tx = true; + + /* Configure the transmitter data (TD) and transmitter frame synchro (TF) + * pins + */ + sam_configpio(PIO_SSC0_TD); sam_configpio(PIO_SSC0_TF); -#ifdef CONFIG_SAMA5_SSC0_TX_EXTCLK + +#if defined(CONFIG_SAMA5_SSC0_TX_TKINPUT) + /* Configure the TK pin only if we are using an external clock to drive + * the transmitter clock. + * + * REVISIT: The SSC is also capable of generated the transmitter clock + * output on the TK pin. + */ + sam_configpio(PIO_SSC0_TK); /* External clock received on the TK I/O pad */ + priv->txclk = SSC_CLKSRC_TKIN; + +#elif defined(CONFIG_SAMA5_SSC0_TX_RXCLK) + priv->txclk = SSC_CLKSRC_RXOUT; + +#elif defined(CONFIG_SAMA5_SSC0_TX_MCKDIV) + priv->txclk = SSC_CLKSRC_MCKDIV; + +#else + priv->txclk = SSC_CLKSRC_NONE; + #endif + + /* Remember the configured TX clock output */ + +#if defined(CONFIG_SAMA5_SSC0_TX_TKOUTPUT_CONT) + priv->txout = SSC_CLKOUT_CONT; /* Continuous */ +#elif defined(CONFIG_SAMA5_SSC0_TX_TKOUTPUT_XFR) + priv->txout = SSC_CLKOUT_XFER; /* Only output clock during transfers */ +#else /* if defined(CONFIG_SAMA5_SSC0_TX_TKOUTPUT_NONE) */ + priv->txout = SSC_CLKOUT_NONE; /* No output clock */ +#endif + +#else + priv->tx = false; + priv->txclk = SSC_CLKSRC_NONE; /* No input clock */ + priv->txout = SSC_CLKOUT_NONE; /* No output clock */ + #endif /* CONFIG_SAMA5_SSC0_TX */ + /* Does the receiver or transmitter need to have the MCK divider set up? */ + +#if (defined(CONFIG_SAMA5_SSC0_RX) && defined(CONFIG_SAMA5_SSC0_RX_MCKDIV)) || \ + (defined(CONFIG_SAMA5_SSC0_TX) && defined(CONFIG_SAMA5_SSC0_TX_MCKDIV)) + + priv->frequency = CONFIG_SAMA5_SSC0_MCKDIV_FREQUENCY; + +#else + priv->frequency = 0; + +#endif + /* Configure driver state specific to this SSC peripheral */ - priv->base = SAM_SSC0_VBASE; -#ifdef CONFIG_SAMA5_SSC_DMA - priv->candma = SAMA5_SSC1_DMA; - priv->pid = SAM_PID_SSC0; + priv->base = SAM_SSC0_VBASE; +#ifdef CONFIG_SAMA5_SSC0_MASTER + priv->master = true; +#else + priv->master = false; #endif + priv->datalen = CONFIG_SAMA5_SSC0_DATALEN; + priv->pid = SAM_PID_SSC0; } #endif #ifdef CONFIG_SAMA5_SSC1 static void ssc1_configure(struct sam_ssc_s *priv) { - /* Enable clocking to the SSC1 peripheral */ - - sam_ssc1_enableclk(); - /* Configure multiplexed pins as connected on the board. Chip * select pins must be selected by board-specific logic. */ #ifdef CONFIG_SAMA5_SSC1_RX + priv->rx = true; + + /* Configure the receiver data (RD) and receiver frame synchro (RF) pins */ + sam_configpio(PIO_SSC1_RD); sam_configpio(PIO_SSC1_RF); -#ifdef CONFIG_SAMA5_SSC1_RX_EXTCLK + +#ifdef CONFIG_SAMA5_SSC1_RX_RKINPUT + /* Configure the RK pin only if we are using an external clock to drive + * the receiver clock. + * + * REVISIT: The SSC is also capable of generated the receiver clock + * output on the RK pin. + */ + sam_configpio(PIO_SSC1_RK); /* External clock received on the RK I/O pad */ + priv->rxclk = SSC_CLKSRC_RKIN; + +#elif defined(CONFIG_SAMA5_SSC1_RX_TXCLK) + priv->rxclk = SSC_CLKSRC_TXOUT; + +#elif defined(CONFIG_SAMA5_SSC1_RX_MCKDIV) + priv->rxclk = SSC_CLKSRC_MCKDIV; + +#else + priv->rxclk = SSC_CLKSRC_NONE; + #endif + + /* Remember the configured RX clock output */ + +#if defined(CONFIG_SAMA5_SSC1_RX_RKOUTPUT_CONT) + priv->rxout = SSC_CLKOUT_CONT; /* Continuous */ +#elif defined(CONFIG_SAMA5_SSC1_RX_RKOUTPUT_XFR) + priv->rxout = SSC_CLKOUT_XFER; /* Only output clock during transfers */ +#else /* if defined(CONFIG_SAMA5_SSC1_RX_RKOUTPUT_NONE) */ + priv->rxout = SSC_CLKOUT_NONE; /* No output clock */ +#endif + +#else + priv->rx = false; + priv->rxclk = SSC_CLKSRC_NONE; /* No input clock */ + priv->rxout = SSC_CLKOUT_NONE; /* No output clock */ + #endif /* CONFIG_SAMA5_SSC1_RX */ #ifdef CONFIG_SAMA5_SSC1_TX + priv->tx = true; + + /* Configure the transmitter data (TD) and transmitter frame synchro (TF) + * pins + */ + sam_configpio(PIO_SSC1_TD); sam_configpio(PIO_SSC1_TF); -#ifdef CONFIG_SAMA5_SSC1_TX_EXTCLK + +#if defined(CONFIG_SAMA5_SSC1_TX_TKINPUT) + /* Configure the TK pin only if we are using an external clock to drive + * the transmitter clock. + * + * REVISIT: The SSC is also capable of generated the transmitter clock + * output on the TK pin. + */ + sam_configpio(PIO_SSC1_TK); /* External clock received on the TK I/O pad */ + priv->txclk = SSC_CLKSRC_TKIN; + +#elif defined(CONFIG_SAMA5_SSC1_TX_RXCLK) + priv->txclk = SSC_CLKSRC_RXOUT; + +#elif defined(CONFIG_SAMA5_SSC1_TX_MCKDIV) + priv->txclk = SSC_CLKSRC_MCKDIV; + +#else + priv->txclk = SSC_CLKSRC_NONE; + #endif + + /* Remember the configured TX clock output */ + +#if defined(CONFIG_SAMA5_SSC1_TX_TKOUTPUT_CONT) + priv->txout = SSC_CLKOUT_CONT; /* Continuous */ +#elif defined(CONFIG_SAMA5_SSC1_TX_TKOUTPUT_XFR) + priv->txout = SSC_CLKOUT_XFER;/* Only output clock during transfers */ +#else /* if defined(CONFIG_SAMA5_SSC1_TX_TKOUTPUT_NONE) */ + priv->txout = SSC_CLKOUT_NONE; /* No output clock */ +#endif + +#else + priv->tx = false; + priv->txclk = SSC_CLKSRC_NONE; /* No input clock */ + priv->txout = SSC_CLKOUT_NONE; /* No output clock */ + #endif /* CONFIG_SAMA5_SSC1_TX */ + /* Does the receiver or transmitter need to have the MCK divider set up? */ + +#if (defined(CONFIG_SAMA5_SSC1_RX) && defined(CONFIG_SAMA5_SSC1_RX_MCKDIV)) || \ + (defined(CONFIG_SAMA5_SSC1_TX) && defined(CONFIG_SAMA5_SSC1_TX_MCKDIV)) + + priv->frequency = CONFIG_SAMA5_SSC1_MCKDIV_FREQUENCY; + +#else + priv->frequency = 0; + +#endif + /* Configure driver state specific to this SSC peripheral */ - priv->base = SAM_SSC1_VBASE; -#ifdef CONFIG_SAMA5_SSC_DMA - priv->candma = SAMA5_SSC1_DMA; - priv->pid = SAM_PID_SSC1; + priv->base = SAM_SSC1_VBASE; +#ifdef CONFIG_SAMA5_SSC1_MASTER + priv->master = true; +#else + priv->master = false; #endif + priv->datalen = CONFIG_SAMA5_SSC1_DATALEN; + priv->pid = SAM_PID_SSC1; } #endif @@ -964,6 +1414,8 @@ struct i2s_dev_s *sam_ssc_initialize(int port) { struct sam_ssc_s *priv; irqstate_t flags; + uint32_t regval; + int ret; /* The support SAM parts have only a single SSC port */ @@ -1011,33 +1463,34 @@ struct i2s_dev_s *sam_ssc_initialize(int port) goto errout_with_alloc; } -#ifdef CONFIG_SAMA5_SSC_DMA - /* Pre-allocate DMA channels. These allocations exploit that fact that + /* Allocate DMA channels. These allocations exploit that fact that * SSC0 is managed by DMAC0 and SSC1 is managed by DMAC1. Hence, * the SSC number (port) is the same as the DMAC number. */ - if (priv->candma) +#ifdef SSC_HAVE_RX + if (priv->rx) { priv->rxdma = sam_dmachannel(port, 0); if (!priv->rxdma) { i2sdbg("ERROR: Failed to allocate the RX DMA channel\n"); - priv->candma = false; + goto errout_with_alloc; } } +#endif - if (priv->candma) +#ifdef SSC_HAVE_TX + if (priv->tx) { priv->txdma = sam_dmachannel(port, 0); if (!priv->txdma) { i2sdbg("ERROR: Failed to allocate the TX DMA channel\n"); - sam_dmafree(priv->rxdma); - priv->rxdma = NULL; - priv->candma = false; + goto errout_with_rxdma; } } +#endif /* Initialize the SSC semaphore that is used to wake up the waiting * thread when the DMA transfer completes. @@ -1049,15 +1502,80 @@ struct i2s_dev_s *sam_ssc_initialize(int port) priv->dmadog = wd_create(); DEBUGASSERT(priv->dmadog); -#endif /* Initialize I2S hardware */ + /* Set the maximum SSC peripheral clock frequency */ + + regval = PMC_PCR_PID(priv->pid) | PMC_PCR_CMD | SSC_PCR_DIV | PMC_PCR_EN; + putreg32(regval, SAM_PMC_PCR); + + /* Reset, disable receiver & transmitter */ + + ssc_putreg(priv, SAM_SSC_CR_OFFSET, SSC_CR_RXDIS | SSC_CR_TXDIS | SSC_CR_SWRST); + + /* Configure MCK/2 divider */ + + if (priv->frequency > 0) + { + regval = SSC_FREQUENCY / (2 * priv->frequency); + } + else + { + regval = 0; + } + + ssc_putreg(priv, SAM_SSC_CMR_OFFSET, regval); + + /* Enable peripheral clocking */ + + sam_enableperiph1(priv->pid); + + /* Configure the receiver */ + + ret = ssc_rx_configure(priv); + if (ret < 0) + { + i2sdbg("ERROR: Failed to configure the receiver: %d\n", ret); + goto errout_with_clocking; + } + + /* Configure the transmitter */ + + ret = ssc_tx_configure(priv); + if (ret < 0) + { + i2sdbg("ERROR: Failed to configure the transmitter: %d\n", ret); + goto errout_with_clocking; + } + + /* Configure DMA */ #warning "Missing logic" irqrestore(flags); ssc_dumpregs(priv, "After initialization"); + /* Success exit */ + return &priv->dev; + /* Failure exits */ + +errout_with_clocking: + sam_disableperiph1(priv->pid); +#ifdef SSC_HAVE_TX + if (priv->txdma) + { + sam_dmafree(priv->txdma); + } + +errout_with_rxdma: +#endif +#ifdef SSC_HAVE_RX + if (priv->rxdma) + { + sam_dmafree(priv->rxdma); + } +#endif + errout_with_alloc: sem_destroy(&priv->exclsem); kfree(priv);