From 498fb6d15fa447f22bd4f4cd98065cc3b6f01997 Mon Sep 17 00:00:00 2001 From: OSer Date: Mon, 2 Dec 2019 01:55:29 +0000 Subject: [PATCH] Merged in OSer916/nuttx/stm32f746g-disco_sai (pull request #1081) boards/arm/stm32f7/stm32f746g-disco: add SAI driver Approved-by: Gregory Nutt --- arch/arm/src/stm32f7/Kconfig | 111 ++ arch/arm/src/stm32f7/Make.defs | 4 + arch/arm/src/stm32f7/hardware/stm32_sai.h | 259 +++ .../stm32f7/hardware/stm32f74xx75xx_pinmap.h | 70 +- arch/arm/src/stm32f7/stm32_sai.c | 1732 +++++++++++++++++ arch/arm/src/stm32f7/stm32_sai.h | 94 + 6 files changed, 2235 insertions(+), 35 deletions(-) create mode 100644 arch/arm/src/stm32f7/hardware/stm32_sai.h create mode 100644 arch/arm/src/stm32f7/stm32_sai.c create mode 100644 arch/arm/src/stm32f7/stm32_sai.h diff --git a/arch/arm/src/stm32f7/Kconfig b/arch/arm/src/stm32f7/Kconfig index b20f9c57071..7b96ea8f824 100644 --- a/arch/arm/src/stm32f7/Kconfig +++ b/arch/arm/src/stm32f7/Kconfig @@ -732,6 +732,8 @@ config STM32F7_STM32F746XX select STM32F7_HAVE_CAN2 select STM32F7_HAVE_SPI4 select STM32F7_HAVE_EXTERNAL_ULPI + select STM32F7_HAVE_SAI1 + select STM32F7_HAVE_SAI2 config STM32F7_STM32F756XX bool @@ -1236,6 +1238,15 @@ config STM32F7_HAVE_EXTERNAL_ULPI bool default n +config STM32F7_HAVE_SAI1 + bool + default n + +config STM32F7_HAVE_SAI2 + bool + default n + + # These "hidden" settings are the OR of individual peripheral selections # indicating that the general capability is required. @@ -1491,12 +1502,48 @@ config STM32F7_RNG config STM32F7_SAI1 bool "SAI1" default n + depends on STM32F7_HAVE_SAI1 + +config STM32F7_SAI1_A + bool "SAI1 Block A" + default n + select AUDIO + select I2S + select SCHED_HPWORK select STM32F7_SAI + depends on STM32F7_SAI1 + +config STM32F7_SAI1_B + bool "SAI1 Block B" + default n + select AUDIO + select I2S + select SCHED_HPWORK + select STM32F7_SAI + depends on STM32F7_SAI1 config STM32F7_SAI2 bool "SAI2" default n + select STM32F7_HAVE_SAI2 + +config STM32F7_SAI2_A + bool "SAI2 Block A" + default n + select AUDIO + select I2S + select SCHED_HPWORK select STM32F7_SAI + depends on STM32F7_SAI2 + +config STM32F7_SAI2_B + bool "SAI2 Block B" + default n + select AUDIO + select I2S + select SCHED_HPWORK + select STM32F7_SAI + depends on STM32F7_SAI2 config STM32F7_SDMMC1 bool "SDMMC1" @@ -5763,4 +5810,68 @@ endchoice endmenu +menu "SAI Configuration" + depends on STM32F7_SAI + +choice + prompt "Operation mode" + default STM32F7_SAI_DMA + ---help--- + Select the operation mode the SAI driver should use. + +config STM32F7_SAI_POLLING + bool "Polling" + ---help--- + The SAI registers are polled for events. + +config STM32F7_SAI_INTERRUPTS + bool "Interrupt" + ---help--- + Select to enable interrupt driven SAI support. + +config STM32F7_SAI_DMA + bool "DMA" + ---help--- + Use DMA to improve SAI transfer performance. + +endchoice # Operation mode + +choice + prompt "SAI1 synchronization enable" + default STM32F7_SAI1_BOTH_ASYNC + depends on STM32F7_SAI1_A && STM32F7_SAI1_B + ---help--- + Select the synchronization mode of the SAI sub-blocks + +config STM32F7_SAI1_BOTH_ASYNC + bool "Both asynchronous" + +config STM32F7_SAI1_A_SYNC_WITH_B + bool "Block A is synchronous with Block B" + +config STM32F7_SAI1_B_SYNC_WITH_A + bool "Block B is synchronous with Block A" + +endchoice # SAI1 synchronization enable + +choice + prompt "SAI2 synchronization enable" + default STM32F7_SAI2_BOTH_ASYNC + depends on STM32F7_SAI2_A && STM32F7_SAI2_B + ---help--- + Select the synchronization mode of the SAI sub-blocks + +config STM32F7_SAI2_BOTH_ASYNC + bool "Both asynchronous" + +config STM32F7_SAI2_A_SYNC_WITH_B + bool "Block A is synchronous with Block B" + +config STM32F7_SAI2_B_SYNC_WITH_A + bool "Block B is synchronous with Block A" + +endchoice # SAI2 synchronization enable + +endmenu + endif # ARCH_CHIP_STM32F7 diff --git a/arch/arm/src/stm32f7/Make.defs b/arch/arm/src/stm32f7/Make.defs index 506d830d582..c0d8a25d2d7 100644 --- a/arch/arm/src/stm32f7/Make.defs +++ b/arch/arm/src/stm32f7/Make.defs @@ -237,6 +237,10 @@ ifeq ($(CONFIG_STM32F7_CAN),y) CHIP_CSRCS += stm32_can.c endif +ifeq ($(CONFIG_STM32F7_SAI),y) +CHIP_CSRCS += stm32_sai.c +endif + ifeq ($(CONFIG_PWM),y) CHIP_CSRCS += stm32_pwm.c endif diff --git a/arch/arm/src/stm32f7/hardware/stm32_sai.h b/arch/arm/src/stm32f7/hardware/stm32_sai.h new file mode 100644 index 00000000000..076075b5973 --- /dev/null +++ b/arch/arm/src/stm32f7/hardware/stm32_sai.h @@ -0,0 +1,259 @@ +/************************************************************************************ + * arch/arm/src/stm32f7/hardware/stm32_sai.h + * + * Copyright (C) 2019 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_STM32F7_HARDWARE_STM32_SAI_H +#define __ARCH_ARM_SRC_STM32F7_HARDWARE_STM32_SAI_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include "chip.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/* Register Offsets *****************************************************************/ + +#define STM32F7_SAI_GCR_OFFSET 0x0000 /* SAI Global Configuration Register */ + +#define STM32F7_SAI_A_OFFSET 0x0004 +#define STM32F7_SAI_B_OFFSET 0x0024 + +#define STM32F7_SAI_CR1_OFFSET 0x0000 /* SAI Configuration Register 1 A */ +#define STM32F7_SAI_CR2_OFFSET 0x0004 /* SAI Configuration Register 2 A */ +#define STM32F7_SAI_FRCR_OFFSET 0x0008 /* SAI Frame Configuration Register A */ +#define STM32F7_SAI_SLOTR_OFFSET 0x000c /* SAI Slot Register A */ +#define STM32F7_SAI_IM_OFFSET 0x0010 /* SAI Interrupt Mask Register 2 A */ +#define STM32F7_SAI_SR_OFFSET 0x0014 /* SAI Status Register A */ +#define STM32F7_SAI_CLRFR_OFFSET 0x0018 /* SAI Clear Flag Register A */ +#define STM32F7_SAI_DR_OFFSET 0x001c /* SAI Data Register A */ + +/* Register Addresses ***************************************************************/ + +#define STM32F7_SAI1_GCR (STM32_SAI1_BASE+STM32F7_SAI_GCR_OFFSET) + +#define STM32F7_SAI1_A_BASE (STM32_SAI1_BASE+STM32F7_SAI_A_OFFSET) +#define STM32F7_SAI1_B_BASE (STM32_SAI1_BASE+STM32F7_SAI_B_OFFSET) + +#define STM32F7_SAI1_ACR1 (STM32F7_SAI1_A_BASE+STM32F7_SAI_CR1_OFFSET) +#define STM32F7_SAI1_ACR2 (STM32F7_SAI1_A_BASE+STM32F7_SAI_CR2_OFFSET) +#define STM32F7_SAI1_AFRCR (STM32F7_SAI1_A_BASE+STM32F7_SAI_FRCR_OFFSET) +#define STM32F7_SAI1_ASLOTR (STM32F7_SAI1_A_BASE+STM32F7_SAI_SLOTR_OFFSET) +#define STM32F7_SAI1_AIM (STM32F7_SAI1_A_BASE+STM32F7_SAI_IM_OFFSET) +#define STM32F7_SAI1_ASR (STM32F7_SAI1_A_BASE+STM32F7_SAI_SR_OFFSET) +#define STM32F7_SAI1_ACLRFR (STM32F7_SAI1_A_BASE+STM32F7_SAI_CLRFR_OFFSET) +#define STM32F7_SAI1_ADR (STM32F7_SAI1_A_BASE+STM32F7_SAI_DR_OFFSET) + +#define STM32F7_SAI1_BCR1 (STM32F7_SAI1_B_BASE+STM32F7_SAI_CR1_OFFSET) +#define STM32F7_SAI1_BCR2 (STM32F7_SAI1_B_BASE+STM32F7_SAI_CR2_OFFSET) +#define STM32F7_SAI1_BFRCR (STM32F7_SAI1_B_BASE+STM32F7_SAI_FRCR_OFFSET) +#define STM32F7_SAI1_BSLOTR (STM32F7_SAI1_B_BASE+STM32F7_SAI_SLOTR_OFFSET) +#define STM32F7_SAI1_BIM (STM32F7_SAI1_B_BASE+STM32F7_SAI_IM_OFFSET) +#define STM32F7_SAI1_BSR (STM32F7_SAI1_B_BASE+STM32F7_SAI_SR_OFFSET) +#define STM32F7_SAI1_BCLRFR (STM32F7_SAI1_B_BASE+STM32F7_SAI_CLRFR_OFFSET) +#define STM32F7_SAI1_BDR (STM32F7_SAI1_B_BASE+STM32F7_SAI_DR_OFFSET) + +#define STM32F7_SAI2_GCR (STM32_SAI2_BASE+STM32F7_SAI_GCR_OFFSET) + +#define STM32F7_SAI2_A_BASE (STM32_SAI2_BASE+STM32F7_SAI_A_OFFSET) +#define STM32F7_SAI2_B_BASE (STM32_SAI2_BASE+STM32F7_SAI_B_OFFSET) + +#define STM32F7_SAI2_ACR1 (STM32F7_SAI2_A_BASE+STM32F7_SAI_CR1_OFFSET) +#define STM32F7_SAI2_ACR2 (STM32F7_SAI2_A_BASE+STM32F7_SAI_CR2_OFFSET) +#define STM32F7_SAI2_AFRCR (STM32F7_SAI2_A_BASE+STM32F7_SAI_FRCR_OFFSET) +#define STM32F7_SAI2_ASLOTR (STM32F7_SAI2_A_BASE+STM32F7_SAI_SLOTR_OFFSET) +#define STM32F7_SAI2_AIM (STM32F7_SAI2_A_BASE+STM32F7_SAI_IM_OFFSET) +#define STM32F7_SAI2_ASR (STM32F7_SAI2_A_BASE+STM32F7_SAI_SR_OFFSET) +#define STM32F7_SAI2_ACLRFR (STM32F7_SAI2_A_BASE+STM32F7_SAI_CLRFR_OFFSET) +#define STM32F7_SAI2_ADR (STM32F7_SAI2_A_BASE+STM32F7_SAI_DR_OFFSET) + +#define STM32F7_SAI2_BCR1 (STM32F7_SAI2_B_BASE+STM32F7_SAI_CR1_OFFSET) +#define STM32F7_SAI2_BCR2 (STM32F7_SAI2_B_BASE+STM32F7_SAI_CR2_OFFSET) +#define STM32F7_SAI2_BFRCR (STM32F7_SAI2_B_BASE+STM32F7_SAI_FRCR_OFFSET) +#define STM32F7_SAI2_BSLOTR (STM32F7_SAI2_B_BASE+STM32F7_SAI_SLOTR_OFFSET) +#define STM32F7_SAI2_BIM (STM32F7_SAI2_B_BASE+STM32F7_SAI_IM_OFFSET) +#define STM32F7_SAI2_BSR (STM32F7_SAI2_B_BASE+STM32F7_SAI_SR_OFFSET) +#define STM32F7_SAI2_BCLRFR (STM32F7_SAI2_B_BASE+STM32F7_SAI_CLRFR_OFFSET) +#define STM32F7_SAI2_BDR (STM32F7_SAI2_B_BASE+STM32F7_SAI_DR_OFFSET) + +/* Register Bitfield Definitions ****************************************************/ + +/* SAI Global Configuration Register */ + +#define SAI_GCR_SYNCIN_SHIFT (0) /* Bits 0-1: Synchronization inputs */ +#define SAI_GCR_SYNCIN_MASK (3 << SAI_GCR_SYNCIN_SHIFT) +# define SAI_GCR_SYNCIN(n) ((uint32_t)(n) << SAI_GCR_SYNCIN_SHIFT) + /* Bits 2-3: Reserved */ +#define SAI_GCR_SYNCOUT_SHIFT (4) /* Bits 4-5: Synchronization outputs */ +#define SAI_GCR_SYNCOUT_MASK (3 << SAI_GCR_SYNCOUT_SHIFT) +# define SAI_GCR_SYNCOUT ((uint32_t)(n) << SAI_GCR_SYNCOUT_SHIFT) + /* Bits 6-31: Reserved */ + +/* SAI Configuration Register 1 */ + +#define SAI_CR1_MODE_SHIFT (0) /* Bits 0-1: SAI audio block mode */ +#define SAI_CR1_MODE_MASK (3 << SAI_CR1_MODE_SHIFT) +# define SAI_CR1_MODE_MASTER_TX (0 << SAI_CR1_MODE_SHIFT) /* Master transmitter */ +# define SAI_CR1_MODE_MASTER_RX (1 << SAI_CR1_MODE_SHIFT) /* Master receiver */ +# define SAI_CR1_MODE_SLAVE_TX (2 << SAI_CR1_MODE_SHIFT) /* Slave transmitter */ +# define SAI_CR1_MODE_SLAVE_RX (3 << SAI_CR1_MODE_SHIFT) /* Slave receiver */ +#define SAI_CR1_PRTCFG_SHIFT (2) /* Bits 2-3: Protocol configuration */ +#define SAI_CR1_PRTCFG_MASK (3 << SAI_CR1_PRTCFG_SHIFT) +# define SAI_CR1_PRTCFG_FREE (0 << SAI_CR1_PRTCFG_SHIFT) /* Free protocol */ +# define SAI_CR1_PRTCFG_SPDIF (1 << SAI_CR1_PRTCFG_SHIFT) /* SPDIF protocol */ +# define SAI_CR1_PRTCFG_AC97 (2 << SAI_CR1_PRTCFG_SHIFT) /* AC97 protocol */ + /* Bit 4: Reserved */ +#define SAI_CR1_DS_SHIFT (5) /* Bits 5-7: Data size */ +#define SAI_CR1_DS_MASK (7 << SAI_CR1_DS_SHIFT) +# define SAI_CR1_DS_8BITS (2 << SAI_CR1_DS_SHIFT) /* 8 bits */ +# define SAI_CR1_DS_10BITS (3 << SAI_CR1_DS_SHIFT) /* 10 bits */ +# define SAI_CR1_DS_16BITS (4 << SAI_CR1_DS_SHIFT) /* 16 bits */ +# define SAI_CR1_DS_20BITS (5 << SAI_CR1_DS_SHIFT) /* 20 bits */ +# define SAI_CR1_DS_24BITS (6 << SAI_CR1_DS_SHIFT) /* 24 bits */ +# define SAI_CR1_DS_32BITS (7 << SAI_CR1_DS_SHIFT) /* 32 bits */ +#define SAI_CR1_LSBFIRST (1 << 8) /* Bit 8: Least significant bit first */ +#define SAI_CR1_CKSTR (1 << 9) /* Bit 9: Clock strobing edge */ +#define SAI_CR1_SYNCEN_SHIFT (10) /* Bits 10-11: Synchronization enable */ +#define SAI_CR1_SYNCEN_MASK (3 << SAI_CR1_SYNCEN_SHIFT) +# define SAI_CR1_SYNCEN_ASYNCH (0 << SAI_CR1_SYNCEN_SHIFT) /* Asynchronous mode */ +# define SAI_CR1_SYNCEN_INTERNAL (1 << SAI_CR1_SYNCEN_SHIFT) /* Synchronous with other internal sub-block */ +# define SAI_CR1_SYNCEN_EXTERNAL (2 << SAI_CR1_SYNCEN_SHIFT) /* Aynchronous with external SAI peripheral */ +#define SAI_CR1_MONO (1 << 12) /* Bit 12: Mono mode */ +#define SAI_CR1_OUTDRIV (1 << 13) /* Bit 13: Output drive */ + /* Bits 14-15: Reserved */ +#define SAI_CR1_SAIEN (1 << 16) /* Bit 16: Audio block enable */ +#define SAI_CR1_DMAEN (1 << 17) /* Bit 17: DMA enable */ + /* Bit 18: Reserved */ +#define SAI_CR1_NODIV (1 << 19) /* Bit 19: No divider */ +#define SAI_CR1_MCKDIV_SHIFT (20) /* Bits 20-23: Master clock divider */ +#define SAI_CR1_MCKDIV_MASK (15 << SAI_CR1_MCKDIV_SHIFT) +# define SAI_CR1_MCKDIV(n) ((uint32_t)(n) << SAI_CR1_MCKDIV_SHIFT) + /* Bits 24-31: Reserved */ + +/* SAI Configuration Register 2 */ + +#define SAI_CR2_FTH_SHIFT (0) /* Bits 0-2: FIFO threshold */ +#define SAI_CR2_FTH_MASK (7 << SAI_CR2_FTH_SHIFT) +# define SAI_CR2_FTH_EMPTY (0 << SAI_CR2_FTH_SHIFT) /* FIFO empty */ +# define SAI_CR2_FTH_1QF (1 << SAI_CR2_FTH_SHIFT) /* 1/4 FIFO */ +# define SAI_CR2_FTH_HF (2 << SAI_CR2_FTH_SHIFT) /* 1/2 FIFO */ +# define SAI_CR2_FTH_3QF (3 << SAI_CR2_FTH_SHIFT) /* 3/4 FIFO */ +# define SAI_CR2_FTH_FULL (4 << SAI_CR2_FTH_SHIFT) /* FIFO full */ +#define SAI_CR2_FFLUSH (1 << 3) /* Bit 3: FIFO flush */ +#define SAI_CR2_TRIS (1 << 4) /* Bit 4: Tristate management on data line */ +#define SAI_CR2_MUTE (1 << 5) /* Bit 5: Mute */ +#define SAI_CR2_MUTEVAL (1 << 6) /* Bit 6: Mute value */ +#define SAI_CR2_MUTECNT_SHIFT (7) /* Bits 7-12: Mute counter */ +#define SAI_CR2_MUTECNT_MASK (0x3f << SAI_CR2_MUTECNT_SHIFT) +# define SAI_CR2_MUTECNT(n) ((uint32_t)(n) << SAI_CR2_MUTECNT_SHIFT) +#define SAI_CR2_CPL (1 << 13) /* Bit 13: Complement */ +#define SAI_CR2_COMP_SHIFT (14) /* Bits 14-15: Companding mode */ +#define SAI_CR2_COMP_MASK (3 << SAI_CR2_COMP_SHIFT) +# define SAI_CR2_COMP_NONE (0 << SAI_CR2_COMP_SHIFT) /* No companding algorithm */ +# define SAI_CR2_COMP_ULAW (2 << SAI_CR2_COMP_SHIFT) /* μ-Law algorithm */ +# define SAI_CR2_COMP_ALAW (3 << SAI_CR2_COMP_SHIFT) /* A-Law algorithm */ + /* Bits 16-31: Reserved */ + +/* SAI Frame Configuration Register */ + +#define SAI_FRCR_FRL_SHIFT (0) /* Bits 0-7: Frame length */ +#define SAI_FRCR_FRL_MASK (0xff << SAI_FRCR_FRL_SHIFT) +# define SAI_FRCR_FRL(n) ((uint32_t)((n) - 1) << SAI_FRCR_FRL_SHIFT) +#define SAI_FRCR_FSALL_SHIFT (8) /* Bits 8-14: Frame synchronization active level length */ +#define SAI_FRCR_FSALL_MASK (0x7f << SAI_FRCR_FSALL_SHIFT) +# define SAI_FRCR_FSALL(n) ((uint32_t)((n) - 1) << SAI_FRCR_FSALL_SHIFT) +#define SAI_FRCR_FSDEF (1 << 16) /* Bit 16: Frame synchronization definition */ +# define SAI_FRCR_FSDEF_SF (0) /* FS signal is a start frame signal */ +# define SAI_FRCR_FSDEF_CHID SAI_FRCR_FSDEF /* FS signal is a start of frame + channel side ID */ +#define SAI_FRCR_FSPOL (1 << 17) /* Bit 17: Frame synchronization polarity */ +# define SAI_FRCR_FSPOL_LOW (0) /* FS is active low */ +# define SAI_FRCR_FSPOL_HIGH SAI_FRCR_FSPOL /* FS is active high */ +#define SAI_FRCR_FSOFF (1 << 18) /* Bit 18: Frame synchronization offset */ +# define SAI_FRCR_FSOFF_FB (0) /* FS on first bit of slot 0 */ +# define SAI_FRCR_FSOFF_BFB SAI_FRCR_FSOFF /* FS one bit before first bit of slot 0 */ + /* Bits 19-31: Reserved */ + +/* SAI Slot Register */ + +#define SAI_SLOTR_FBOFF_SHIFT (0) /* Bits 0-4: First bit offset */ +#define SAI_SLOTR_FBOFF_MASK (31 << SAI_SLOTR_FBOFF_SHIFT) +# define SAI_SLOTR_FBOFF(n) ((uint32_t)(n) << SAI_SLOTR_FBOFF_SHIFT) + /* Bit 5: Reserved */ +#define SAI_SLOTR_SLOTSZ_SHIFT (6) /* Bits 6-7: Slot size */ +#define SAI_SLOTR_SLOTSZ_MASK (3 << SAI_SLOTR_SLOTSZ_SHIFT) +# define SAI_SLOTR_SLOTSZ_DATA (0 << SAI_SLOTR_SLOTSZ_SHIFT) /* Same as data size */ +# define SAI_SLOTR_SLOTSZ_16BIT (1 << SAI_SLOTR_SLOTSZ_SHIFT) /* 16-bit */ +# define SAI_SLOTR_SLOTSZ_32BIT (2 << SAI_SLOTR_SLOTSZ_SHIFT) /* 32-bit */ +#define SAI_SLOTR_NBSLOT_SHIFT (8) /* Bits 8-11: Number of slots in an audio frame */ +#define SAI_SLOTR_NBSLOT_MASK (15 << SAI_SLOTR_NBSLOT_SHIFT) +# define SAI_SLOTR_NBSLOT(n) ((uint32_t)((n) - 1) << SAI_SLOTR_NBSLOT_SHIFT) + /* Bits 12-15: Reserved */ +#define SAI_SLOTR_SLOTEN_SHIFT (16) /* Bits 16-31: Slot enable */ +#define SAI_SLOTR_SLOTEN_MASK (0xffff << SAI_SLOTR_SLOTEN_SHIFT) +# define SAI_SLOTR_SLOTEN(n) ((uint32_t)(n) << SAI_SLOTR_SLOTEN_SHIFT) +# define SAI_SLOTR_SLOTEN_0 (1 << 16) /* Bit 16: Slot 0 Enabled */ +# define SAI_SLOTR_SLOTEN_1 (1 << 17) /* Bit 17: Slot 1 Enabled */ +# define SAI_SLOTR_SLOTEN_2 (1 << 18) /* Bit 18: Slot 2 Enabled */ +# define SAI_SLOTR_SLOTEN_3 (1 << 19) /* Bit 19: Slot 3 Enabled */ +# define SAI_SLOTR_SLOTEN_4 (1 << 20) /* Bit 20: Slot 4 Enabled */ +# define SAI_SLOTR_SLOTEN_5 (1 << 21) /* Bit 21: Slot 5 Enabled */ +# define SAI_SLOTR_SLOTEN_6 (1 << 22) /* Bit 22: Slot 6 Enabled */ +# define SAI_SLOTR_SLOTEN_7 (1 << 23) /* Bit 23: Slot 7 Enabled */ +# define SAI_SLOTR_SLOTEN_8 (1 << 24) /* Bit 24: Slot 8 Enabled */ +# define SAI_SLOTR_SLOTEN_9 (1 << 25) /* Bit 25: Slot 9 Enabled */ +# define SAI_SLOTR_SLOTEN_10 (1 << 26) /* Bit 26: Slot 10 Enabled */ +# define SAI_SLOTR_SLOTEN_11 (1 << 27) /* Bit 27: Slot 11 Enabled */ +# define SAI_SLOTR_SLOTEN_12 (1 << 28) /* Bit 28: Slot 12 Enabled */ +# define SAI_SLOTR_SLOTEN_13 (1 << 29) /* Bit 29: Slot 13 Enabled */ +# define SAI_SLOTR_SLOTEN_14 (1 << 30) /* Bit 30: Slot 14 Enabled */ +# define SAI_SLOTR_SLOTEN_15 (1 << 31) /* Bit 31: Slot 15 Enabled */ + +/* SAI Interrupt Mask Register 2, SAI Status Register, and SAI Clear Flag Register */ + +#define SAI_INT_OVRUDR (1 << 0) /* Bit 0: Overrun/underrun interrupt */ +#define SAI_INT_MUTEDET (1 << 1) /* Bit 1: Mute detection interrupt */ +#define SAI_INT_WCKCFG (1 << 2) /* Bit 2: Wrong clock configuration interrupt */ +#define SAI_INT_FREQ (1 << 3) /* Bit 3: FIFO request interrupt (not CLRFFR) */ +#define SAI_INT_CNRDY (1 << 4) /* Bit 4: Codec not ready interrupt (AC97). */ +#define SAI_INT_AFSDET (1 << 5) /* Bit 5: Anticipated frame synchronization detection interrupt */ +#define SAI_INT_LFSDET (1 << 6) /* Bit 6: Late frame synchronization detection interrupt */ + /* Bits 7-31: Reserved */ + +/* SAI Data Register (32-bit data) */ + +#endif /* __ARCH_ARM_SRC_STM32F7_HARDWARE_STM32_SAI_H */ diff --git a/arch/arm/src/stm32f7/hardware/stm32f74xx75xx_pinmap.h b/arch/arm/src/stm32f7/hardware/stm32f74xx75xx_pinmap.h index d5262f25d74..70e2e109d65 100644 --- a/arch/arm/src/stm32f7/hardware/stm32f74xx75xx_pinmap.h +++ b/arch/arm/src/stm32f7/hardware/stm32f74xx75xx_pinmap.h @@ -780,41 +780,41 @@ /* Serial Audio Interface */ -#define GPIO_SAI1_FS_A (GPIO_ALT|GPIO_AF6|GPIO_PORTE|GPIO_PIN4) -#define GPIO_SAI1_FS_B (GPIO_ALT|GPIO_AF6|GPIO_PORTF|GPIO_PIN9) -#define GPIO_SAI1_MCLK_A (GPIO_ALT|GPIO_AF6|GPIO_PORTE|GPIO_PIN2) -#define GPIO_SAI1_MCLK_B (GPIO_ALT|GPIO_AF6|GPIO_PORTF|GPIO_PIN7) -#define GPIO_SAI1_SCK_A (GPIO_ALT|GPIO_AF6|GPIO_PORTE|GPIO_PIN5) -#define GPIO_SAI1_SCK_B (GPIO_ALT|GPIO_AF6|GPIO_PORTF|GPIO_PIN8) -#define GPIO_SAI1_SD_A_1 (GPIO_ALT|GPIO_AF6|GPIO_PORTB|GPIO_PIN2) -#define GPIO_SAI1_SD_A_2 (GPIO_ALT|GPIO_AF6|GPIO_PORTC|GPIO_PIN1) -#define GPIO_SAI1_SD_A_3 (GPIO_ALT|GPIO_AF6|GPIO_PORTD|GPIO_PIN6) -#define GPIO_SAI1_SD_A_4 (GPIO_ALT|GPIO_AF6|GPIO_PORTE|GPIO_PIN6) -#define GPIO_SAI1_SD_B_1 (GPIO_ALT|GPIO_AF6|GPIO_PORTE|GPIO_PIN3) -#define GPIO_SAI1_SD_B_2 (GPIO_ALT|GPIO_AF6|GPIO_PORTF|GPIO_PIN6) -#define GPIO_SAI2_FS_A_1 (GPIO_ALT|GPIO_AF10|GPIO_PORTD|GPIO_PIN12) -#define GPIO_SAI2_FS_A_2 (GPIO_ALT|GPIO_AF10|GPIO_PORTI|GPIO_PIN7) -#define GPIO_SAI2_FS_B_1 (GPIO_ALT|GPIO_AF10|GPIO_PORTE|GPIO_PIN13) -#define GPIO_SAI2_FS_B_2 (GPIO_ALT|GPIO_AF10|GPIO_PORTG|GPIO_PIN9) -#define GPIO_SAI2_FS_B_3 (GPIO_ALT|GPIO_AF8|GPIO_PORTA|GPIO_PIN12) -#define GPIO_SAI2_FS_B_4 (GPIO_ALT|GPIO_AF8|GPIO_PORTC|GPIO_PIN0) -#define GPIO_SAI2_MCLK_A_1 (GPIO_ALT|GPIO_AF10|GPIO_PORTE|GPIO_PIN0) -#define GPIO_SAI2_MCLK_A_2 (GPIO_ALT|GPIO_AF10|GPIO_PORTI|GPIO_PIN4) -#define GPIO_SAI2_MCLK_B_1 (GPIO_ALT|GPIO_AF10|GPIO_PORTA|GPIO_PIN1) -#define GPIO_SAI2_MCLK_B_2 (GPIO_ALT|GPIO_AF10|GPIO_PORTE|GPIO_PIN14) -#define GPIO_SAI2_MCLK_B_3 (GPIO_ALT|GPIO_AF10|GPIO_PORTE|GPIO_PIN6) -#define GPIO_SAI2_MCLK_B_4 (GPIO_ALT|GPIO_AF10|GPIO_PORTH|GPIO_PIN3) -#define GPIO_SAI2_SCK_A_1 (GPIO_ALT|GPIO_AF10|GPIO_PORTD|GPIO_PIN13) -#define GPIO_SAI2_SCK_A_2 (GPIO_ALT|GPIO_AF10|GPIO_PORTI|GPIO_PIN5) -#define GPIO_SAI2_SCK_B_1 (GPIO_ALT|GPIO_AF10|GPIO_PORTE|GPIO_PIN12) -#define GPIO_SAI2_SCK_B_2 (GPIO_ALT|GPIO_AF10|GPIO_PORTH|GPIO_PIN2) -#define GPIO_SAI2_SCK_B_3 (GPIO_ALT|GPIO_AF8|GPIO_PORTA|GPIO_PIN2) -#define GPIO_SAI2_SD_A_1 (GPIO_ALT|GPIO_AF10|GPIO_PORTD|GPIO_PIN11) -#define GPIO_SAI2_SD_A_2 (GPIO_ALT|GPIO_AF10|GPIO_PORTI|GPIO_PIN6) -#define GPIO_SAI2_SD_B_1 (GPIO_ALT|GPIO_AF10|GPIO_PORTA|GPIO_PIN0) -#define GPIO_SAI2_SD_B_2 (GPIO_ALT|GPIO_AF10|GPIO_PORTE|GPIO_PIN11) -#define GPIO_SAI2_SD_B_3 (GPIO_ALT|GPIO_AF10|GPIO_PORTF|GPIO_PIN11) -#define GPIO_SAI2_SD_B_4 (GPIO_ALT|GPIO_AF10|GPIO_PORTG|GPIO_PIN10) +#define GPIO_SAI1_FS_A (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN4) +#define GPIO_SAI1_FS_B (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTF|GPIO_PIN9) +#define GPIO_SAI1_MCLK_A (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN2) +#define GPIO_SAI1_MCLK_B (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTF|GPIO_PIN7) +#define GPIO_SAI1_SCK_A (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN5) +#define GPIO_SAI1_SCK_B (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTF|GPIO_PIN8) +#define GPIO_SAI1_SD_A_1 (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTB|GPIO_PIN2) +#define GPIO_SAI1_SD_A_2 (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTC|GPIO_PIN1) +#define GPIO_SAI1_SD_A_3 (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTD|GPIO_PIN6) +#define GPIO_SAI1_SD_A_4 (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN6) +#define GPIO_SAI1_SD_B_1 (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN3) +#define GPIO_SAI1_SD_B_2 (GPIO_ALT|GPIO_AF6|GPIO_SPEED_100MHz|GPIO_PORTF|GPIO_PIN6) +#define GPIO_SAI2_FS_A_1 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTD|GPIO_PIN12) +#define GPIO_SAI2_FS_A_2 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTI|GPIO_PIN7) +#define GPIO_SAI2_FS_B_1 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN13) +#define GPIO_SAI2_FS_B_2 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTG|GPIO_PIN9) +#define GPIO_SAI2_FS_B_3 (GPIO_ALT|GPIO_AF8|GPIO_SPEED_100MHz|GPIO_PORTA|GPIO_PIN12) +#define GPIO_SAI2_FS_B_4 (GPIO_ALT|GPIO_AF8|GPIO_SPEED_100MHz|GPIO_PORTC|GPIO_PIN0) +#define GPIO_SAI2_MCLK_A_1 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN0) +#define GPIO_SAI2_MCLK_A_2 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTI|GPIO_PIN4) +#define GPIO_SAI2_MCLK_B_1 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTA|GPIO_PIN1) +#define GPIO_SAI2_MCLK_B_2 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN14) +#define GPIO_SAI2_MCLK_B_3 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN6) +#define GPIO_SAI2_MCLK_B_4 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTH|GPIO_PIN3) +#define GPIO_SAI2_SCK_A_1 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTD|GPIO_PIN13) +#define GPIO_SAI2_SCK_A_2 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTI|GPIO_PIN5) +#define GPIO_SAI2_SCK_B_1 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN12) +#define GPIO_SAI2_SCK_B_2 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTH|GPIO_PIN2) +#define GPIO_SAI2_SCK_B_3 (GPIO_ALT|GPIO_AF8|GPIO_SPEED_100MHz|GPIO_PORTA|GPIO_PIN2) +#define GPIO_SAI2_SD_A_1 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTD|GPIO_PIN11) +#define GPIO_SAI2_SD_A_2 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTI|GPIO_PIN6) +#define GPIO_SAI2_SD_B_1 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTA|GPIO_PIN0) +#define GPIO_SAI2_SD_B_2 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTE|GPIO_PIN11) +#define GPIO_SAI2_SD_B_3 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTF|GPIO_PIN11) +#define GPIO_SAI2_SD_B_4 (GPIO_ALT|GPIO_AF10|GPIO_SPEED_100MHz|GPIO_PORTG|GPIO_PIN10) /* SD/MMC * diff --git a/arch/arm/src/stm32f7/stm32_sai.c b/arch/arm/src/stm32f7/stm32_sai.c new file mode 100644 index 00000000000..c8b779f745d --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_sai.c @@ -0,0 +1,1732 @@ +/**************************************************************************** + * arch/arm/src/stm32f7/stm32_sai.c + * + * Copyright (C) 2013-2014, 2019 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * Copyright (c) 2016 Motorola Mobility, LLC. All rights reserved. + * + * 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 "chip.h" +#include "up_arch.h" + +#include "stm32_dma.h" +#include "stm32_gpio.h" +#include "stm32_sai.h" + +#ifdef CONFIG_STM32F7_SAI + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#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_I2S +# error CONFIG_I2S required by this driver +#endif + +#ifdef CONFIG_STM32F7_SAI_POLLING +# error "Polling SAI not yet supported" +#endif + +#ifdef CONFIG_STM32F7_SAI_INTERRUPTS +# error "Interrupt driven SAI not yet supported" +#endif + +#ifndef CONFIG_STM32F7_SAI_DEFAULT_SAMPLERATE +# define CONFIG_STM32F7_SAI_DEFAULT_SAMPLERATE (48000) +#endif + +#ifndef CONFIG_STM32F7_SAI_DEFAULT_DATALEN +# define CONFIG_STM32F7_SAI_DEFAULT_DATALEN (16) +#endif + +#ifndef CONFIG_STM32F7_SAI_MAXINFLIGHT +# define CONFIG_STM32F7_SAI_MAXINFLIGHT (16) +#endif + +#ifdef CONFIG_STM32F7_SAI1 +#ifndef STM32F7_SAI1_FREQUENCY +# error "Please define STM32F7_SAI1_FREQUENCY in board.h" +#endif +#endif + +#ifdef CONFIG_STM32F7_SAI2 +#ifndef STM32F7_SAI2_FREQUENCY +# error "Please define STM32F7_SAI1_FREQUENCY in board.h" +#endif +#endif + +#ifdef CONFIG_STM32F7_SAI_DMA +/* SAI DMA priority */ + +# if defined(CONFIG_STM32F7_SAI_DMAPRIO) +# define SAI_DMA_PRIO CONFIG_STM32F7_SAI_DMAPRIO +# else +# define SAI_DMA_PRIO DMA_SCR_PRIVERYHI +# endif + +# if (SAI_DMA_PRIO & ~DMA_SCR_PL_MASK) != 0 +# error "Illegal value for CONFIG_STM32F7_SAI_DMAPRIO" +# endif + +/* DMA channel/stream configuration register settings. The following + * must be selected. The DMA driver will select the remaining fields. + * + * - 32-bit DMA + * - Memory increment + * - Direction (memory-to-peripheral, peripheral-to-memory) + * - Memory burst size (F4 only) + */ + +/* STM32 stream configuration register (SCR) settings base settings sans + * priority. + */ + +# define SAI_RXDMA8_CONFIG (DMA_SCR_PFCTRL | DMA_SCR_DIR_P2M|DMA_SCR_MINC | \ + DMA_SCR_PSIZE_8BITS | DMA_SCR_MSIZE_8BITS | \ + DMA_SCR_PBURST_INCR4 | DMA_SCR_MBURST_INCR4) +# define SAI_RXDMA16_CONFIG (DMA_SCR_PFCTRL | DMA_SCR_DIR_P2M|DMA_SCR_MINC | \ + DMA_SCR_PSIZE_16BITS | DMA_SCR_MSIZE_16BITS | \ + DMA_SCR_PBURST_INCR4 | DMA_SCR_MBURST_INCR4) + +# define SAI_RXDMA32_CONFIG (DMA_SCR_PFCTRL | DMA_SCR_DIR_P2M|DMA_SCR_MINC | \ + DMA_SCR_PSIZE_32BITS | DMA_SCR_MSIZE_32BITS | \ + DMA_SCR_PBURST_INCR4 | DMA_SCR_MBURST_INCR4) +# define SAI_TXDMA8_CONFIG (DMA_SCR_PFCTRL | DMA_SCR_DIR_M2P | DMA_SCR_MINC | \ + DMA_SCR_PSIZE_32BITS | DMA_SCR_MSIZE_32BITS | \ + DMA_SCR_PBURST_INCR4 | DMA_SCR_MBURST_INCR4) +# define SAI_TXDMA16_CONFIG (DMA_SCR_PFCTRL | DMA_SCR_DIR_M2P | DMA_SCR_MINC | \ + DMA_SCR_PSIZE_32BITS | DMA_SCR_MSIZE_32BITS | \ + DMA_SCR_PBURST_INCR4 | DMA_SCR_MBURST_INCR4) + +# define SAI_TXDMA32_CONFIG (DMA_SCR_PFCTRL | DMA_SCR_DIR_M2P | DMA_SCR_MINC | \ + DMA_SCR_PSIZE_32BITS | DMA_SCR_MSIZE_32BITS | \ + DMA_SCR_PBURST_INCR4 | DMA_SCR_MBURST_INCR4) + +#endif + +#ifdef DMAMAP_SAI1 + +/* SAI DMA Channel/Stream selection. There + * are multiple DMA stream options that must be dis-ambiguated in the board.h + * file. + */ + +# define SAI1_DMACHAN DMAMAP_SAI1 +#endif + +#ifdef DMAMAP_SAI2 + +/* SAI DMA Channel/Stream selection. There + * are multiple DMA stream options that must be dis-ambiguated in the board.h + * file. + */ + +# define SAI2_DMACHAN DMAMAP_SAI2 +#endif + + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* I2S buffer container */ + +struct sai_buffer_s +{ + struct sai_buffer_s *flink; /* Supports a singly linked list */ + i2s_callback_t callback; /* Function to call when the transfer completes */ + uint32_t timeout; /* The timeout value to use with 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 */ +}; + +/* The state of the one SAI peripheral */ + +struct stm32f7_sai_s +{ + struct i2s_dev_s dev; /* Externally visible I2S interface */ + uintptr_t base; /* SAI block register base address */ + sem_t exclsem; /* Assures mutually exclusive acess to SAI */ + uint32_t frequency; /* SAI clock frequency */ + uint32_t syncen; /* Synchronization setting */ +#ifdef CONFIG_STM32F7_SAI_DMA + uint16_t dma_ch; /* DMA channel number */ + DMA_HANDLE dma; /* DMA channel handle */ + uint32_t dma_ccr; /* DMA control register */ +#endif + uint8_t datalen; /* Data width */ + uint32_t samplerate; /* Data sample rate */ + uint8_t rxenab:1; /* True: RX transfers enabled */ + uint8_t txenab:1; /* True: TX transfers enabled */ + WDOG_ID dog; /* Watchdog that handles 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 */ + + /* Pre-allocated pool of buffer containers */ + + sem_t bufsem; /* Buffer wait semaphore */ + struct sai_buffer_s *freelist; /* A list a free buffer containers */ + struct sai_buffer_s containers[CONFIG_STM32F7_SAI_MAXINFLIGHT]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_I2S_INFO +static void sai_dump_regs(struct stm32f7_sai_s *priv, const char *msg); +static void rcc_dump_regs(const char *msg); +#else +# define sai_dump_regs(s,m) +# define rcc_dump_regs(m) +#endif + +/* Semaphore helpers */ + +static void sai_exclsem_take(struct stm32f7_sai_s *priv); +#define sai_exclsem_give(priv) nxsem_post(&priv->exclsem) + +static void sai_bufsem_take(struct stm32f7_sai_s *priv); +#define sai_bufsem_give(priv) nxsem_post(&priv->bufsem) + +/* Buffer container helpers */ + +static struct sai_buffer_s * + sai_buf_allocate(struct stm32f7_sai_s *priv); +static void sai_buf_free(struct stm32f7_sai_s *priv, + struct sai_buffer_s *bfcontainer); +static void sai_buf_initialize(struct stm32f7_sai_s *priv); + +/* DMA support */ + +#ifdef CONFIG_STM32F7_SAI_DMA +static void sai_schedule(struct stm32f7_sai_s *priv, int result); +static void sai_dma_callback(DMA_HANDLE handle, uint8_t isr, void *arg); +#endif + +/* I2S methods */ + +static uint32_t sai_samplerate(struct i2s_dev_s *dev, uint32_t rate); +static uint32_t sai_datawidth(struct i2s_dev_s *dev, int bits); +static int sai_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, uint32_t timeout); +static int sai_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, + uint32_t timeout); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* I2S device operations */ + +static const struct i2s_ops_s g_i2sops = +{ + /* Receiver methods */ + + .i2s_rxsamplerate = sai_samplerate, + .i2s_rxdatawidth = sai_datawidth, + .i2s_receive = sai_receive, + + /* Transmitter methods */ + + .i2s_txsamplerate = sai_samplerate, + .i2s_txdatawidth = sai_datawidth, + .i2s_send = sai_send, +}; + +/* SAI1 state */ + +#ifdef CONFIG_STM32F7_SAI1_A +static struct stm32f7_sai_s g_sai1a_priv = +{ + .dev.ops = &g_i2sops, + .base = STM32F7_SAI1_A_BASE, + .frequency = STM32F7_SAI1_FREQUENCY, +#ifdef CONFIG_STM32F7_SAI1_A_SYNC_WITH_B + .syncen = SAI_CR1_SYNCEN_INTERNAL, +#else + .syncen = SAI_CR1_SYNCEN_ASYNCH, +#endif +#ifdef CONFIG_STM32F7_SAI_DMA + .dma_ch = DMACHAN_SAI1_A, +#endif + .datalen = CONFIG_STM32F7_SAI_DEFAULT_DATALEN, + .samplerate = CONFIG_STM32F7_SAI_DEFAULT_SAMPLERATE, +}; +#endif + +#ifdef CONFIG_STM32F7_SAI1_B +static struct stm32f7_sai_s g_sai1b_priv = +{ + .dev.ops = &g_i2sops, + .base = STM32F7_SAI1_B_BASE, + .frequency = STM32F7_SAI1_FREQUENCY, +#ifdef CONFIG_STM32F7_SAI1_B_SYNC_WITH_A + .syncen = SAI_CR1_SYNCEN_INTERNAL, +#else + .syncen = SAI_CR1_SYNCEN_ASYNCH, +#endif +#ifdef CONFIG_STM32F7_SAI_DMA + .dma_ch = DMACHAN_SAI1_B, +#endif + .datalen = CONFIG_STM32F7_SAI_DEFAULT_DATALEN, + .samplerate = CONFIG_STM32F7_SAI_DEFAULT_SAMPLERATE, +}; +#endif + +/* SAI2 state */ + +#ifdef CONFIG_STM32F7_SAI2_A +static struct stm32f7_sai_s g_sai2a_priv = +{ + .dev.ops = &g_i2sops, + .base = STM32F7_SAI2_A_BASE, + .frequency = STM32F7_SAI2_FREQUENCY, +#ifdef CONFIG_STM32F7_SAI2_A_SYNC_WITH_B + .syncen = SAI_CR1_SYNCEN_INTERNAL, +#else + .syncen = SAI_CR1_SYNCEN_ASYNCH, +#endif +#ifdef CONFIG_STM32F7_SAI_DMA + .dma_ch = DMACHAN_SAI2_A, +#endif + .datalen = CONFIG_STM32F7_SAI_DEFAULT_DATALEN, + .samplerate = CONFIG_STM32F7_SAI_DEFAULT_SAMPLERATE, +}; +#endif + +#ifdef CONFIG_STM32F7_SAI2_B +static struct stm32f7_sai_s g_sai2b_priv = +{ + .dev.ops = &g_i2sops, + .base = STM32F7_SAI2_B_BASE, + .frequency = STM32F7_SAI2_FREQUENCY, +#ifdef CONFIG_STM32F7_SAI2_B_SYNC_WITH_A + .syncen = SAI_CR1_SYNCEN_INTERNAL, +#else + .syncen = SAI_CR1_SYNCEN_ASYNCH, +#endif +#ifdef CONFIG_STM32F7_SAI_DMA + .dma_ch = DMACHAN_SAI2_B, +#endif + .datalen = CONFIG_STM32F7_SAI_DEFAULT_DATALEN, + .samplerate = CONFIG_STM32F7_SAI_DEFAULT_SAMPLERATE, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sai_getbitrate + * + * Description: + * Get the currently configured bitrate + * + * Input Parameters: + * priv - private SAI device structure + * + * Returned Value: + * The current bitrate + * + ****************************************************************************/ + +static inline uint32_t sai_getbitrate(struct stm32f7_sai_s *priv) +{ + /* Calculate the bitrate in Hz */ + + return priv->samplerate * priv->datalen; +} + +/**************************************************************************** + * Name: sai_getreg + * + * Description: + * Get the contents of the SAI register at offset + * + * Input Parameters: + * priv - private SAI device structure + * offset - offset to the register of interest + * + * Returned Value: + * The contents of the 32-bit register + * + ****************************************************************************/ + +static inline uint32_t sai_getreg(struct stm32f7_sai_s *priv, uint8_t offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: sai_putreg + * + * Description: + * Write a 16-bit value to the SAI register at offset + * + * Input Parameters: + * priv - private SAI device structure + * offset - offset to the register of interest + * value - the 32-bit value to be written + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void sai_putreg(struct stm32f7_sai_s *priv, uint8_t offset, + uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/************************************************************************************ + * Name: sai_modifyreg + * + * Description: + * Clear and set bits in the SAI register at offset + * + * Input Parameters: + * priv - private SAI device structure + * offset - offset to the register of interest + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ************************************************************************************/ + +static void sai_modifyreg(struct stm32f7_sai_s *priv, uint8_t offset, + uint32_t clrbits, uint32_t setbits) +{ + uint32_t regval; + + regval = sai_getreg(priv, offset); + regval &= ~clrbits; + regval |= setbits; + sai_putreg(priv, offset, regval); +} + +/**************************************************************************** + * Name: sai_dump_regs + * + * Description: + * Dump the contents of all SAI block registers + * + * Input Parameters: + * priv - The SAI block controller to dump + * msg - Message to print before the register data + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_I2S_INFO +static void sai_dump_regs(struct stm32f7_sai_s *priv, const char *msg) +{ + if (msg) + i2sinfo("%s\n", msg); + +#if 0 + i2sinfo("CR1:%08x CR2:%08x FRCR:%08x SLOTR:%08x\n", + sai_getreg(priv, STM32F7_SAI_CR1_OFFSET), + sai_getreg(priv, STM32F7_SAI_CR2_OFFSET), + sai_getreg(priv, STM32F7_SAI_FRCR_OFFSET), + sai_getreg(priv, STM32F7_SAI_SLOTR_OFFSET)); + i2sinfo(" IM:%08x SR:%08x CLRFR:%08x\n", + sai_getreg(priv, STM32F7_SAI_IM_OFFSET), + sai_getreg(priv, STM32F7_SAI_SR_OFFSET), + sai_getreg(priv, STM32F7_SAI_CLRFR_OFFSET)); +#else // more information + /*********************GCR*********************/ +#ifdef CONFIG_STM32F7_SAI1 + uint32_t gcr = getreg32(STM32F7_SAI1_GCR); + i2sinfo("GCR: *%08x = %08x\n", STM32F7_SAI1_GCR, gcr); +#else + uint32_t gcr = getreg32(STM32F7_SAI2_GCR); + i2sinfo("GCR: *%08x = %08x\n", STM32F7_SAI2_GCR, gcr); +#endif + + /********************* CR1 *******************/ + uint32_t cr1 = sai_getreg(priv, STM32F7_SAI_CR1_OFFSET); + i2sinfo("CR1: *%08x = %08x\n", STM32F7_SAI_CR1_OFFSET, cr1); + + uint32_t mode = (cr1 & SAI_CR1_MODE_MASK) >> SAI_CR1_MODE_SHIFT; + const char *mode_string[] = { "Master transmitter", + "Master receiver", + "Slave transmitter", + "Slave receiver" + }; + i2sinfo("\t\tCR1: MODE[1:0] = %s\n", mode_string[mode]); + + uint32_t prtcfg = (cr1 & SAI_CR1_PRTCFG_MASK) >> SAI_CR1_PRTCFG_SHIFT; + const char *prtcfg_string[] = { "Free protocol", + "SPDIF protocol", + "AC'97 protocol", + "Reserved" + }; + i2sinfo("\t\tCR1: PRTCFG[3:2] = %s\n", prtcfg_string[prtcfg]); + + uint32_t ds = (cr1 & SAI_CR1_DS_MASK) >> SAI_CR1_DS_SHIFT; + const char *ds_string[] = { "Reserved", + "Reserved", + "8 Bits", + "10 Bits", + "16 Bits", + "20 Bits", + "24 Bits", + "32 Bits" + }; + i2sinfo("\t\tCR1: DS[7:5] = %s\n", ds_string[ds]); + + uint32_t lsbfirst = cr1 & SAI_CR1_LSBFIRST; + i2sinfo("\t\tCR1: LSBFIRST[8] = %s\n", lsbfirst ? "Data are transferred with LSB first" + : "Data are transferred with MSB first"); + uint32_t ckstr = cr1 & SAI_CR1_CKSTR; + i2sinfo("\t\tCR1: CKSTR[9] = %s\n", ckstr ? "SCK falling edge" + : "SCK rising edge"); + + uint32_t syncen = (cr1 & SAI_CR1_SYNCEN_MASK) >> SAI_CR1_SYNCEN_SHIFT; + const char *syncen_string[] = { "audio sub-block in asynchronous mode", + "audio sub-block in asynchronous with the other internal audio sub-block", + "audio sub-block in synchronous with an external SAI embedded peripheral", + "Reserved" + }; + i2sinfo("\t\tCR1: SYNCEN[11:10] = %s\n", syncen_string[syncen]); + + uint32_t mono = cr1 & SAI_CR1_MONO; + i2sinfo("\t\tCR1: MONO[12] = %s\n", mono ? "Mono mode" + : "Stereo mode"); + uint32_t outdriv = cr1 & SAI_CR1_OUTDRIV; + i2sinfo("\t\tCR1: OUTDRIV[13] = %s\n", outdriv ? "Audio block output driven immediately after the setting of this bit" + : "Audio block output driven when SAIEN is set"); + uint32_t saien = cr1 & SAI_CR1_SAIEN; + i2sinfo("\t\tCR1: SAIEN[16] = %s\n", saien ? "SAI audio block enabled" + : "SAI audio block disabled"); + uint32_t dmaen = cr1 & SAI_CR1_DMAEN; + i2sinfo("\t\tCR1: DMAEN[17] = %s\n", dmaen ? "DMA enabled" + : "DMA disabled"); + uint32_t nodiv = cr1 & SAI_CR1_NODIV; + i2sinfo("\t\tCR1: NODIV[19] = %s\n", nodiv ? "No divider used in the clock generator" + : "Master clock generator is enabled"); + uint32_t mckdiv = (cr1 & SAI_CR1_MCKDIV_MASK) >> SAI_CR1_MCKDIV_SHIFT; + i2sinfo("\t\tCR1: MCKDIV[23:20] = %d\n", mckdiv); + + /*************************CR2**************************/ + uint32_t cr2 = sai_getreg(priv, STM32F7_SAI_CR2_OFFSET); + i2sinfo("CR2: *%08x = %08x\n", STM32F7_SAI_CR2_OFFSET, cr2); + uint32_t fth = (cr2 & SAI_CR2_FTH_MASK) >> SAI_CR2_FTH_SHIFT; + const char *fth_string[] = { "FIFO empty", + "1/4 FIFO", + "1/2 FIFO", + "3/4 FIFO", + "FIFO full", + "Reserved", + "Reserved", + "Reserved" + }; + i2sinfo("\t\tCR2: FTH[2:0] = %s\n", fth_string[fth]); + uint32_t fflush = cr2 & SAI_CR2_FFLUSH; + i2sinfo("\t\tCR2: FFLUSH[3] = %s\n", fflush ? "FIFO flush" + : "no FIFO flush"); + + uint32_t tris = cr2 & SAI_CR2_TRIS; + i2sinfo("\t\tCR2: TRIS[4] = %s\n", tris ? "SD output line is release (HI-Z)" + : "SD output line is still driven by the SAI when a slot is inactive"); + uint32_t mute = cr2 & SAI_CR2_MUTE; + i2sinfo("\t\tCR2: MUTE[5] = %s\n", mute ? "Mute mode enabled" + : "No mute mode"); + + uint32_t muteval = cr2 & SAI_CR2_MUTEVAL; + i2sinfo("\t\tCR2: MUTEVAL[6] = %s\n", muteval ? "Last values are sent during the mute mode" + : "Bit value 0 is sent during the mute mode"); + + uint32_t mutecnt = (cr2 & SAI_CR2_MUTECNT_MASK) >> SAI_CR2_MUTECNT_SHIFT; + i2sinfo("\t\tCR2: MUTECNT[12:7] = %d\n", mutecnt); + + uint32_t cpl = cr2 & SAI_CR2_CPL; + i2sinfo("\t\tCR2: CPL[13] = %s\n", cpl ? "1's complement represention" + : "2's complement represention"); + uint32_t comp = (cr2 & SAI_CR2_COMP_MASK) >> SAI_CR2_COMP_SHIFT; + const char *comp_string[] = { "No companding algorithm", + "Reserved", + "u-Law algorithm", + "A-Law algorithm" + }; + i2sinfo("\t\tCR2: COMP[15:14] = %s\n", comp_string[comp]); + + /**********************FRCR*****************************/ + uint32_t frcr = sai_getreg(priv, STM32F7_SAI_FRCR_OFFSET); + i2sinfo("FRCR: *%08x = %08x\n", STM32F7_SAI_FRCR_OFFSET, frcr); + + uint32_t frl = (frcr & SAI_FRCR_FRL_MASK) >> SAI_FRCR_FRL_SHIFT; + i2sinfo("\t\tFRCR: FRL[7:0] = %d\n", frl); + + uint32_t fsall = (frcr & SAI_FRCR_FSALL_MASK) >> SAI_FRCR_FSALL_SHIFT; + i2sinfo("\t\tFRCR: FSALL[14:8] = %d\n", fsall+1); + + uint32_t fsdef = frcr & SAI_FRCR_FSDEF; + i2sinfo("\t\tFRCR: FSDEF[16] = %s\n", fsdef ? "FS signal is a start of frame + channel side ID" + : "FS signal is a start frame signal"); + + uint32_t fspol = frcr & SAI_FRCR_FSPOL; + i2sinfo("\t\tFRCR: FSPOL[17] = %s\n", fspol ? "FS is active high" + : "FS is active low"); + uint32_t fsoff = frcr & SAI_FRCR_FSOFF; + i2sinfo("\t\tFRCR: FSOFF[18] = %s\n", fsoff ? "FS one bit before first bit of slot 0" + : "FS on first bit of slot 0"); + /*******************SLOTR****************************/ + uint32_t slotr = sai_getreg(priv, STM32F7_SAI_SLOTR_OFFSET); + i2sinfo("SLOTR: *%08x = %08x\n", STM32F7_SAI_SLOTR_OFFSET, slotr); + + uint32_t fboff = (slotr & SAI_SLOTR_FBOFF_MASK) >> SAI_SLOTR_FBOFF_SHIFT; + i2sinfo("\t\tSLOTR: FBOFF[4:0] = %d\n", fboff); + + uint32_t slotsz = (slotr & SAI_SLOTR_SLOTSZ_MASK) >> SAI_SLOTR_SLOTSZ_SHIFT; + const char *slotsz_string[] = { "Same as data size", + "16-bit", + "32-bit", + "Reserved" + }; + i2sinfo("\t\tSLOTR: SLOTSZ[7:6] = %s\n", slotsz_string[slotsz]); + + uint32_t nbslot = (slotr & SAI_SLOTR_NBSLOT_MASK) >> SAI_SLOTR_NBSLOT_SHIFT; + i2sinfo("\t\tSLOTR: NBSLOT[11:8] = %d\n", nbslot+1); + + uint32_t sloten = (slotr & SAI_SLOTR_SLOTEN_MASK) >> SAI_SLOTR_SLOTEN_SHIFT; + i2sinfo("\t\tSLOTR: SLOTEN[31:16] = %08x\n", sloten+1); + +#endif +} +#endif + +/**************************************************************************** + * Name: rcc_dump_regs + * + * Description: + * Dump the contents of all rcc block registers + * + * Input Parameters: + * msg - Message to print before the register data + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_I2S_INFO +static void rcc_dump_regs(const char *msg) +{ + if (msg) + i2sinfo("%s\n", msg); + +#if 0 + /**************RCC_PLLSAICFGR******************/ + uint32_t pll_sai_cfgr = getreg32(STM32_RCC_PLLSAICFGR); + _info("PLLSAICFGR = %08x\n", pll_sai_cfgr); + + uint32_t pllsain = (pll_sai_cfgr & RCC_PLLSAICFGR_PLLSAIN_MASK) >> RCC_PLLSAICFGR_PLLSAIN_SHIFT; + _info("\t\tPLLSAICFGR PLLSAIN[14:6] = %d\n", pllsain); + uint32_t pllsaip = (pll_sai_cfgr & RCC_PLLSAICFGR_PLLSAIP_MASK) >> RCC_PLLSAICFGR_PLLSAIP_SHIFT; + _info("\t\tPLLSAICFGR PLLSAIP[17:16] = %d\n", pllsaip); + uint32_t pllsaiq = (pll_sai_cfgr & RCC_PLLSAICFGR_PLLSAIQ_MASK) >> RCC_PLLSAICFGQ_PLLSAIP_SHIFT; + _info("\t\tPLLSAICFGR PLLSAIQ[27:24] = %d\n", pllsaiq); + + uint32_t pllsair = (pll_sai_cfgr & RCC_PLLSAICFGR_PLLSAIR_MASK) >> RCC_PLLSAICFGR_PLLSAIP_SHIFT; + _info("\t\tPLLSAICFGR PLLSAIR[30:28] = %d\n", pllsair); + + + +#endif + +} +#endif + + +/**************************************************************************** + * Name: sai_exclsem_take + * + * Description: + * Take the exclusive access semaphore handling any exceptional conditions + * + * Input Parameters: + * priv - A reference to the SAI peripheral state + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sai_exclsem_take(struct stm32f7_sai_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 = nxsem_wait(&priv->exclsem); + DEBUGASSERT(ret == 0 || ret == -EINTR); + } + while (ret == -EINTR); +} + +/**************************************************************************** + * Name: sai_mckdivider + * + * Description: + * Setup the master clock divider based on the currently selected data width + * and the sample rate + * + * Input Parameters: + * priv - SAI device structure (only the sample rate and frequency are + * needed at this point). + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sai_mckdivider(struct stm32f7_sai_s *priv) +{ + uint32_t mckdiv; + uint32_t tmpval; + + DEBUGASSERT(priv && priv->samplerate > 0 && priv->frequency > 0); + + /* Configure Master Clock using the following formula: + * MCLK_x = SAI_CK_x / (MCKDIV[3:0] * 2) with MCLK_x = 256 * FS + * FS = SAI_CK_x / (MCKDIV[3:0] * 2) * 256 + * MCKDIV[3:0] = SAI_CK_x / FS * 512 + */ + + /* (freq*10) to keep Significant digits */ + tmpval = (priv->frequency * 10) / (priv->samplerate * 2 * 256); + mckdiv = tmpval / 10; + + /* Round result to the nearest integer */ + if ((tmpval % 10) > 8) + { + mckdiv += 1; + } + + sai_modifyreg(priv, STM32F7_SAI_CR1_OFFSET, SAI_CR1_MCKDIV_MASK, + mckdiv << SAI_CR1_MCKDIV_SHIFT); +} + +/**************************************************************************** + * Name: sai_timeout + * + * Description: + * The watchdog timeout without completion of the transfer. + * + * 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. + * + ****************************************************************************/ + +static void sai_timeout(int argc, uint32_t arg) +{ + struct stm32f7_sai_s *priv = (struct stm32f7_sai_s *)arg; + DEBUGASSERT(priv != NULL); + +#ifdef CONFIG_STM32F7_SAI_DMA + /* Cancel the DMA */ + + stm32_dmastop(priv->dma); +#endif + + /* Then schedule completion of the transfer to occur on the worker thread. */ + + sai_schedule(priv, -ETIMEDOUT); +} + +/**************************************************************************** + * Name: sai_dma_setup + * + * Description: + * Setup and initiate the next DMA transfer + * + * Input Parameters: + * priv - SAI state instance + * + * Returned Value: + * OK on success; a negated errno value on failure + * + * Assumptions: + * Interrupts are disabled + * + ****************************************************************************/ + +#ifdef CONFIG_STM32F7_SAI_DMA +static int sai_dma_setup(struct stm32f7_sai_s *priv) +{ + struct sai_buffer_s *bfcontainer; + struct ap_buffer_s *apb; + uintptr_t samp; + apb_samp_t nbytes; + size_t ntransfers = 0; + int ret; + + /* If there is already an active transmission in progress, then bail + * returning success. + */ + + if (!sq_empty(&priv->act)) + { + return OK; + } + + /* If there are no pending transfer, then bail returning success */ + + if (sq_empty(&priv->pend)) + { + priv->txenab = priv->rxenab = false; + return OK; + } + + /* Remove the pending transfer at the head of the pending queue. */ + + bfcontainer = (struct sai_buffer_s *)sq_remfirst(&priv->pend); + DEBUGASSERT(bfcontainer && bfcontainer->apb); + + apb = bfcontainer->apb; + + /* Get the transfer information, accounting for any data offset */ + + samp = (uintptr_t)&apb->samp[apb->curbyte]; + + /* Configure the DMA */ + + if (priv->txenab) + { + nbytes = apb->nbytes - apb->curbyte; + + switch (priv->datalen) + { + case 8: + priv->dma_ccr = SAI_TXDMA8_CONFIG; + ntransfers = nbytes; + break; + + case 16: + priv->dma_ccr = SAI_TXDMA16_CONFIG; + DEBUGASSERT((nbytes & 0x1) == 0); + ntransfers = nbytes >> 1; + break; + + case 32: + priv->dma_ccr = SAI_TXDMA32_CONFIG; + DEBUGASSERT((nbytes & 0x3) == 0); + ntransfers = nbytes >> 2; + break; + } + } + else if (priv->rxenab) + { + nbytes = apb->nmaxbytes - apb->curbyte; + + switch (priv->datalen) + { + case 8: + priv->dma_ccr = SAI_RXDMA8_CONFIG; + ntransfers = nbytes; + break; + + case 16: + priv->dma_ccr = SAI_RXDMA16_CONFIG; + DEBUGASSERT((nbytes & 0x1) == 0); + ntransfers = nbytes >> 1; + break; + + case 32: + priv->dma_ccr = SAI_RXDMA32_CONFIG; + DEBUGASSERT((nbytes & 0x3) == 0); + ntransfers = nbytes >> 2; + break; + } + } + + DEBUGASSERT(ntransfers > 0); + + stm32_dmasetup(priv->dma, priv->base + STM32F7_SAI_DR_OFFSET, + samp, ntransfers, priv->dma_ccr); + + /* Add the container to the list of active DMAs */ + + sq_addlast((sq_entry_t *)bfcontainer, &priv->act); + + /* Start the DMA, saving the container as the current active transfer */ + + stm32_dmastart(priv->dma, sai_dma_callback, priv, false); + + /* Enable the transmitter */ + + sai_modifyreg(priv, STM32F7_SAI_CR1_OFFSET, 0, SAI_CR1_SAIEN); + + /* Start a watchdog to catch DMA timeouts */ + + if (bfcontainer->timeout > 0) + { + ret = wd_start(priv->dog, bfcontainer->timeout, (wdentry_t)sai_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) + { + i2serr("ERROR: wd_start failed: %d\n", ret); + } + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: sai_worker + * + * Description: + * Transfer done worker + * + * Input Parameters: + * arg - the SAI device instance cast to void* + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sai_worker(void *arg) +{ + struct stm32f7_sai_s *priv = (struct stm32f7_sai_s *)arg; + struct sai_buffer_s *bfcontainer; + irqstate_t flags; + + DEBUGASSERT(priv); + + /* When the transfer was started, the active buffer containers were removed + * from the pend queue and saved in the act queue. We get here when the + * transfer is finished... either successfully, with an error, or with a + * timeout. + * + * In any case, the buffer containers in act will be moved to the end + * of the done queue and act will be emptied before this worker is + * started. + */ + + i2sinfo("act.head=%p done.head=%p\n", priv->act.head, priv->done.head); + + /* Check if IDLE */ + + if (sq_empty(&priv->act)) + { + /* Then start the next transfer. This must be done with interrupts + * disabled. + */ + + flags = enter_critical_section(); +#ifdef CONFIG_STM32F7_SAI_DMA + (void)sai_dma_setup(priv); +#endif + leave_critical_section(flags); + } + + /* Process each buffer in the done queue */ + + while (sq_peek(&priv->done) != NULL) + { + /* Remove the buffer container from the done queue. NOTE that + * interupts must be enabled to do this because the done queue is + * also modified from the interrupt level. + */ + + flags = enter_critical_section(); + bfcontainer = (struct sai_buffer_s *)sq_remfirst(&priv->done); + leave_critical_section(flags); + + /* Perform the 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 */ + + sai_buf_free(priv, bfcontainer); + } +} + +/**************************************************************************** + * Name: sai_schedule + * + * Description: + * An transfer completion or timeout has occurred. Schedule processing on + * the working thread. + * + * Input Parameters: + * priv - SAI state instance + * result - The result of the transfer + * + * Returned Value: + * None + * + * Assumptions: + * - Interrupts are disabled + * - The timeout has been canceled. + * + ****************************************************************************/ + +static void sai_schedule(struct stm32f7_sai_s *priv, int result) +{ + struct sai_buffer_s *bfcontainer; + int ret; + + /* Move all entries from the act queue to the done queue */ + + while (!sq_empty(&priv->act)) + { + /* Remove the next buffer container from the act list */ + + bfcontainer = (struct sai_buffer_s *)sq_remfirst(&priv->act); + + /* Report the result of the transfer */ + + bfcontainer->result = result; + + /* Add the completed buffer container to the tail of the done queue */ + + sq_addlast((sq_entry_t *)bfcontainer, &priv->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->work)) + { + /* Schedule the done processing to occur on the worker thread. */ + + ret = work_queue(HPWORK, &priv->work, sai_worker, priv, 0); + if (ret != 0) + { + i2serr("ERROR: Failed to queue work: %d\n", ret); + } + } +} + +/**************************************************************************** + * Name: sai_dma_callback + * + * Description: + * This callback function is invoked at the completion of the SAI DMA. + * + * Input Parameters: + * handle - The DMA handler + * isr - The interrupt status of the DMA transfer + * arg - A pointer to the SAI state instance + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_STM32F7_SAI_DMA +static void sai_dma_callback(DMA_HANDLE handle, uint8_t isr, void *arg) +{ + struct stm32f7_sai_s *priv = (struct stm32f7_sai_s *)arg; + DEBUGASSERT(priv); + + /* Cancel the watchdog timeout */ + + (void)wd_cancel(priv->dog); + + /* Then schedule completion of the transfer to occur on the worker thread */ + + sai_schedule(priv, (isr & DMA_STREAM_TEIF_BIT) ? -EIO : OK); +} +#endif + +/**************************************************************************** + * Name: sai_samplerate + * + * Description: + * Set the I2S RX/TX 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 sai_samplerate(struct i2s_dev_s *dev, uint32_t rate) +{ + struct stm32f7_sai_s *priv = (struct stm32f7_sai_s *)dev; + + DEBUGASSERT(priv && rate > 0); + + /* Save the new sample rate and update the divider */ + + priv->samplerate = rate; + sai_mckdivider(priv); + + return sai_getbitrate(priv); +} + +/**************************************************************************** + * Name: sai_datawidth + * + * Description: + * Set the I2S data width. The 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 sai_datawidth(struct i2s_dev_s *dev, int bits) +{ + struct stm32f7_sai_s *priv = (struct stm32f7_sai_s *)dev; + uint32_t setbits; + + DEBUGASSERT(priv && bits >= 8); + + switch (bits) + { + case 8: + setbits = SAI_CR1_DS_8BITS; + break; + + case 16: + setbits = SAI_CR1_DS_16BITS; + break; + + case 32: + setbits = SAI_CR1_DS_32BITS; + break; + + default: + i2serr("ERROR: Unsupported or invalid data width: %d\n", bits); + return 0; + } + + sai_modifyreg(priv, STM32F7_SAI_CR1_OFFSET, SAI_CR1_DS_MASK, setbits); + + sai_modifyreg(priv, STM32F7_SAI_FRCR_OFFSET, + SAI_FRCR_FSALL_MASK | SAI_FRCR_FRL_MASK, + SAI_FRCR_FSALL(bits) | SAI_FRCR_FRL(bits * 2)); + + /* Save the new data width */ + + priv->datalen = bits; + + return sai_getbitrate(priv); +} + +/**************************************************************************** + * Name: sai_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 receive 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 sai_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, uint32_t timeout) +{ + struct stm32f7_sai_s *priv = (struct stm32f7_sai_s *)dev; + struct sai_buffer_s *bfcontainer; + uint32_t mode; + irqstate_t flags; + int ret; + + DEBUGASSERT(priv && apb); + i2sinfo("apb=%p nbytes=%d arg=%p timeout=%d\n", + apb, apb->nbytes - apb->curbyte, arg, timeout); + + /* Allocate a buffer container in advance */ + + bfcontainer = sai_buf_allocate(priv); + DEBUGASSERT(bfcontainer); + + /* Get exclusive access to the SAI driver data */ + + sai_exclsem_take(priv); + + /* Verify not already TX'ing */ + + if (priv->txenab) + { + i2serr("ERROR: SAI has no receiver\n"); + ret = -EAGAIN; + goto errout_with_exclsem; + } + + mode = priv->syncen ? SAI_CR1_MODE_SLAVE_RX : SAI_CR1_MODE_MASTER_RX; + sai_modifyreg(priv, STM32F7_SAI_CR1_OFFSET, SAI_CR1_MODE_MASK, mode); + priv->rxenab = true; + + /* 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 pending queue */ + + flags = enter_critical_section(); + sq_addlast((sq_entry_t *)bfcontainer, &priv->pend); + + /* Then start the next transfer. If there is already a transfer in progess, + * then this will do nothing. + */ + +#ifdef CONFIG_STM32F7_SAI_DMA + ret = sai_dma_setup(priv); +#endif + DEBUGASSERT(ret == OK); + leave_critical_section(flags); + sai_exclsem_give(priv); + return OK; + +errout_with_exclsem: + sai_exclsem_give(priv); + sai_buf_free(priv, bfcontainer); + return ret; +} + +/**************************************************************************** + * Name: sai_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 sai_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb, + i2s_callback_t callback, void *arg, uint32_t timeout) +{ + struct stm32f7_sai_s *priv = (struct stm32f7_sai_s *)dev; + struct sai_buffer_s *bfcontainer; + uint32_t mode; + irqstate_t flags; + int ret; + + DEBUGASSERT(priv && apb); + i2sinfo("apb=%p nbytes=%d arg=%p timeout=%d\n", + apb, apb->nbytes - apb->curbyte, arg, timeout); + + /* Allocate a buffer container in advance */ + + bfcontainer = sai_buf_allocate(priv); + DEBUGASSERT(bfcontainer); + + /* Get exclusive access to the SAI driver data */ + + sai_exclsem_take(priv); + + /* Verify not already RX'ing */ + + if (priv->rxenab) + { + i2serr("ERROR: SAI has no transmitter\n"); + ret = -EAGAIN; + goto errout_with_exclsem; + } + + mode = priv->syncen ? SAI_CR1_MODE_SLAVE_TX : SAI_CR1_MODE_MASTER_TX; + sai_modifyreg(priv, STM32F7_SAI_CR1_OFFSET, SAI_CR1_MODE_MASK, mode); + priv->txenab = true; + + /* 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 pending queue */ + + flags = enter_critical_section(); + sq_addlast((sq_entry_t *)bfcontainer, &priv->pend); + + /* Then start the next transfer. If there is already a transfer in progess, + * then this will do nothing. + */ + +#ifdef CONFIG_STM32F7_SAI_DMA + ret = sai_dma_setup(priv); +#endif + DEBUGASSERT(ret == OK); + leave_critical_section(flags); + sai_exclsem_give(priv); + return OK; + +errout_with_exclsem: + sai_exclsem_give(priv); + sai_buf_free(priv, bfcontainer); + return ret; +} + +/**************************************************************************** + * Name: sai_bufsem_take + * + * Description: + * Take the buffer semaphore handling any exceptional conditions + * + * Input Parameters: + * priv - A reference to the SAI peripheral state + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sai_bufsem_take(struct stm32f7_sai_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 = nxsem_wait(&priv->bufsem); + DEBUGASSERT(ret == 0 || ret == -EINTR); + } + while (ret == -EINTR); +} + +/**************************************************************************** + * Name: sai_buf_allocate + * + * Description: + * Allocate a buffer container by removing the one at the head of the + * free list + * + * Input Parameters: + * priv - SAI 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 SAI state structure. + * That would result in a deadlock! + * + ****************************************************************************/ + +static struct sai_buffer_s *sai_buf_allocate(struct stm32f7_sai_s *priv) +{ + struct sai_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. + */ + + sai_bufsem_take(priv); + + /* Get the buffer from the head of the free list */ + + flags = enter_critical_section(); + bfcontainer = priv->freelist; + DEBUGASSERT(bfcontainer); + + /* Unlink the buffer from the freelist */ + + priv->freelist = bfcontainer->flink; + leave_critical_section(flags); + return bfcontainer; +} + +/**************************************************************************** + * Name: sai_buf_free + * + * Description: + * Free buffer container by adding it to the head of the free list + * + * Input Parameters: + * priv - SAI state instance + * bfcontainer - The buffer container to be freed + * + * Returned Value: + * None + * + * Assumptions: + * The caller has exclusive access to the SAI state structure + * + ****************************************************************************/ + +static void sai_buf_free(struct stm32f7_sai_s *priv, struct sai_buffer_s *bfcontainer) +{ + irqstate_t flags; + + /* Put the buffer container back on the free list */ + + flags = enter_critical_section(); + bfcontainer->flink = priv->freelist; + priv->freelist = bfcontainer; + leave_critical_section(flags); + + /* Wake up any threads waiting for a buffer container */ + + sai_bufsem_give(priv); +} + +/**************************************************************************** + * Name: sai_buf_initialize + * + * Description: + * Initialize the buffer container allocator by adding all of the + * pre-allocated buffer containers to the free list + * + * Input Parameters: + * priv - SAI state instance + * + * Returned Value: + * None + * + * Assumptions: + * Called early in SAI initialization so that there are no issues with + * concurrency. + * + ****************************************************************************/ + +static void sai_buf_initialize(struct stm32f7_sai_s *priv) +{ + int i; + + priv->freelist = NULL; + nxsem_init(&priv->bufsem, 0, CONFIG_STM32F7_SAI_MAXINFLIGHT); + + for (i = 0; i < CONFIG_STM32F7_SAI_MAXINFLIGHT; i++) + { + sai_buf_free(priv, &priv->containers[i]); + } +} + +/**************************************************************************** + * Name: sai_portinitialize + * + * Description: + * Initialize the selected SAI port in its default state + * + * Input Parameters: + * priv - private SAI device structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void sai_portinitialize(struct stm32f7_sai_s *priv) +{ + sai_dump_regs(priv, "Before initialization"); + + nxsem_init(&priv->exclsem, 0, 1); + + /* Create a watchdog timer to catch transfer timeouts */ + + priv->dog = wd_create(); + DEBUGASSERT(priv->dog); + + /* Initialize buffering */ + + sai_buf_initialize(priv); + + /* Configure the master clock divider */ + + sai_mckdivider(priv); + + /* Configure the data width */ + + sai_datawidth((struct i2s_dev_s *)priv, CONFIG_STM32F7_SAI_DEFAULT_DATALEN); + +#ifdef CONFIG_STM32F7_SAI_DMA + /* Get DMA channel */ + + priv->dma = stm32_dmachannel(priv->dma_ch); + DEBUGASSERT(priv->dma); + + sai_modifyreg(priv, STM32F7_SAI_CR1_OFFSET, 0, SAI_CR1_DMAEN); +#endif + + sai_modifyreg(priv, STM32F7_SAI_CR1_OFFSET, SAI_CR1_SYNCEN_MASK, priv->syncen); + + sai_modifyreg(priv, STM32F7_SAI_CR1_OFFSET, 0, SAI_CR1_OUTDRIV); + + + sai_modifyreg(priv, STM32F7_SAI_CR2_OFFSET, SAI_CR2_FTH_MASK, SAI_CR2_FTH_1QF); + + sai_modifyreg(priv, STM32F7_SAI_FRCR_OFFSET, + SAI_FRCR_FSDEF | SAI_FRCR_FSPOL | SAI_FRCR_FSOFF, + SAI_FRCR_FSDEF_CHID | SAI_FRCR_FSPOL_LOW | SAI_FRCR_FSOFF_BFB); + + sai_modifyreg(priv, STM32F7_SAI_SLOTR_OFFSET, + SAI_SLOTR_NBSLOT_MASK | SAI_SLOTR_SLOTEN_MASK, + SAI_SLOTR_NBSLOT(2) | SAI_SLOTR_SLOTEN_0 | SAI_SLOTR_SLOTEN_1); + + sai_modifyreg(priv, STM32F7_SAI_CR1_OFFSET, 0, SAI_CR1_SAIEN); + sai_dump_regs(priv, "After initialization"); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_sai_initialize + * + * Description: + * Initialize the selected SAI block + * + * Input Parameters: + * intf - I2S interface number (identifying the "logical" SAI interface) + * + * Returned Value: + * Valid I2S device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct i2s_dev_s *stm32_sai_initialize(int intf) +{ + struct stm32f7_sai_s *priv; + irqstate_t flags; + + flags = enter_critical_section(); + + switch (intf) + { +#ifdef CONFIG_STM32F7_SAI1_A + case SAI1_BLOCK_A: + { + i2sinfo("SAI1 Block A Selected\n"); + priv = &g_sai1a_priv; + + stm32_configgpio(GPIO_SAI1_SD_A); +# ifndef CONFIG_STM32F7_SAI1_A_SYNC_WITH_B + stm32_configgpio(GPIO_SAI1_FS_A); + stm32_configgpio(GPIO_SAI1_SCK_A); + stm32_configgpio(GPIO_SAI1_MCLK_A); +# endif + break; + } +#endif +#ifdef CONFIG_STM32F7_SAI1_B + case SAI1_BLOCK_B: + { + i2sinfo("SAI1 Block B Selected\n"); + priv = &g_sai1b_priv; + + stm32_configgpio(GPIO_SAI1_SD_B); +# ifndef CONFIG_STM32F7_SAI1_B_SYNC_WITH_A + stm32_configgpio(GPIO_SAI1_FS_B); + stm32_configgpio(GPIO_SAI1_SCK_B); + stm32_configgpio(GPIO_SAI1_MCLK_B); +# endif + break; + } +#endif +#ifdef CONFIG_STM32F7_SAI2_A + case SAI2_BLOCK_A: + { + i2sinfo("SAI2 Block A Selected\n"); + priv = &g_sai2a_priv; + + stm32_configgpio(GPIO_SAI2_SD_A); +# ifndef CONFIG_STM32F7_SAI2_A_SYNC_WITH_B + stm32_configgpio(GPIO_SAI2_FS_A); + stm32_configgpio(GPIO_SAI2_SCK_A); + stm32_configgpio(GPIO_SAI2_MCLK_A); +# endif + break; + } +#endif +#ifdef CONFIG_STM32F7_SAI2_B + case SAI2_BLOCK_B: + { + i2sinfo("SAI2 Block B Selected\n"); + priv = &g_sai2b_priv; + + stm32_configgpio(GPIO_SAI2_SD_B); +# ifndef CONFIG_STM32F7_SAI2_B_SYNC_WITH_A + stm32_configgpio(GPIO_SAI2_FS_B); + stm32_configgpio(GPIO_SAI2_SCK_B); + stm32_configgpio(GPIO_SAI2_MCLK_B); +# endif + break; + } +#endif + default: + { + i2sinfo("No SAI interface defined\n"); + goto err; + } + } + + sai_portinitialize(priv); + leave_critical_section(flags); + + return &priv->dev; + +err: + leave_critical_section(flags); + return NULL; +} + +#endif diff --git a/arch/arm/src/stm32f7/stm32_sai.h b/arch/arm/src/stm32f7/stm32_sai.h new file mode 100644 index 00000000000..e4264fea61b --- /dev/null +++ b/arch/arm/src/stm32f7/stm32_sai.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * arch/arm/src/stm32f7/stm32_sai.h + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Copyright (c) 2016 Motorola Mobility, LLC. All rights reserved. + * + * 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_STM32F7_STM32_SAI_H +#define __ARCH_ARM_SRC_STM32F7_STM32_SAI_H + +/****************************************************************************** + * Included Files + ******************************************************************************/ + +#include + +#include "chip.h" +#include "hardware/stm32_sai.h" + +#include + +/****************************************************************************** + * Pre-processor definitions + ******************************************************************************/ + +#define SAI1_BLOCK_A 0 +#define SAI1_BLOCK_B 1 +#define SAI2_BLOCK_A 2 +#define SAI2_BLOCK_B 3 + +/****************************************************************************** + * Public Function Prototypes + ******************************************************************************/ + +#ifndef __ASSEMBLY__ +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: stm32_sai_initialize + * + * Description: + * Initialize the selected SAI block + * + * Input Parameters: + * intf - I2S interface number (identifying the "logical" SAI interface) + * + * Returned Value: + * Valid I2S device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct i2s_dev_s *stm32_sai_initialize(int intf); + +#undef EXTERN +#ifdef __cplusplus +} +#endif +#endif /* __ASSEMBLY__ */ + +#endif /* __ARCH_ARM_SRC_STM32F7_STM32_SAI_H */