Added functionality for Audio support with the STM32F746 Discoboard

In particular additions to wm8994.h and filled functionality into
wm8994.c.

Resolved a few more remarks from review.
This commit is contained in:
okayserh 2022-04-30 11:10:12 +02:00 committed by Alan Carvalho de Assis
parent 5977279f0e
commit 476770e9fd
6 changed files with 1801 additions and 624 deletions

View File

@ -63,6 +63,7 @@
#include "stm32_dma.h"
#include "stm32_gpio.h"
#include "stm32_sai.h"
#include "stm32_pwr.h"
#ifdef CONFIG_STM32F7_SAI
@ -146,41 +147,19 @@
# 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 | \
# define SAI_TXDMA8_CONFIG (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 | \
# define SAI_TXDMA16_CONFIG (DMA_SCR_DIR_M2P | DMA_SCR_MINC | \
DMA_SCR_PSIZE_16BITS | DMA_SCR_MSIZE_16BITS | \
DMA_SCR_PBURST_INCR4 | DMA_SCR_MBURST_INCR4)
# define SAI_TXDMA32_CONFIG (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
/****************************************************************************
@ -204,6 +183,10 @@ struct sai_buffer_s
struct stm32f7_sai_s
{
struct i2s_dev_s dev; /* Externally visible I2S interface */
/* Callback for changes in sample rate */
stm32_sai_sampleratecb_t sampleratecb;
uintptr_t base; /* SAI block register base address */
sem_t exclsem; /* Assures mutually exclusive access to SAI */
uint32_t frequency; /* SAI clock frequency */
@ -1151,6 +1134,17 @@ static uint32_t sai_samplerate(struct i2s_dev_s *dev, uint32_t rate)
DEBUGASSERT(priv && rate > 0);
/* Call callback to change system clock (needed for STM32F746 Disco) */
if (priv->sampleratecb != NULL)
{
priv->frequency = priv->sampleratecb(dev, rate);
}
else
{
i2sinfo("No Sample Rate CB set!\n");
}
/* Save the new sample rate and update the divider */
priv->samplerate = rate;
@ -1638,7 +1632,8 @@ static void sai_portinitialize(struct stm32f7_sai_s *priv)
*
****************************************************************************/
struct i2s_dev_s *stm32_sai_initialize(int intf)
struct i2s_dev_s *stm32_sai_initialize(int intf,
stm32_sai_sampleratecb_t sampleratecb)
{
struct stm32f7_sai_s *priv;
irqstate_t flags;
@ -1652,6 +1647,7 @@ struct i2s_dev_s *stm32_sai_initialize(int intf)
{
i2sinfo("SAI1 Block A Selected\n");
priv = &g_sai1a_priv;
priv->sampleratecb = sampleratecb;
stm32_configgpio(GPIO_SAI1_SD_A);
# ifndef CONFIG_STM32F7_SAI1_A_SYNC_WITH_B
@ -1668,6 +1664,7 @@ struct i2s_dev_s *stm32_sai_initialize(int intf)
{
i2sinfo("SAI1 Block B Selected\n");
priv = &g_sai1b_priv;
priv->sampleratecb = sampleratecb;
stm32_configgpio(GPIO_SAI1_SD_B);
# ifndef CONFIG_STM32F7_SAI1_B_SYNC_WITH_A
@ -1684,6 +1681,7 @@ struct i2s_dev_s *stm32_sai_initialize(int intf)
{
i2sinfo("SAI2 Block A Selected\n");
priv = &g_sai2a_priv;
priv->sampleratecb = sampleratecb;
stm32_configgpio(GPIO_SAI2_SD_A);
# ifndef CONFIG_STM32F7_SAI2_A_SYNC_WITH_B
@ -1700,6 +1698,7 @@ struct i2s_dev_s *stm32_sai_initialize(int intf)
{
i2sinfo("SAI2 Block B Selected\n");
priv = &g_sai2b_priv;
priv->sampleratecb = sampleratecb;
stm32_configgpio(GPIO_SAI2_SD_B);
# ifndef CONFIG_STM32F7_SAI2_B_SYNC_WITH_A

View File

@ -69,6 +69,9 @@ extern "C"
#define EXTERN extern
#endif
typedef uint32_t (*stm32_sai_sampleratecb_t)(struct i2s_dev_s *dev,
uint32_t rate);
/****************************************************************************
* Name: stm32_sai_initialize
*
@ -83,7 +86,8 @@ extern "C"
*
****************************************************************************/
struct i2s_dev_s *stm32_sai_initialize(int intf);
struct i2s_dev_s *stm32_sai_initialize(int intf,
stm32_sai_sampleratecb_t sampleratecb);
#undef EXTERN
#ifdef __cplusplus

View File

@ -158,8 +158,8 @@
#define STM32_RCC_DCKCFGR1_PLLI2SDIVQ RCC_DCKCFGR1_PLLI2SDIVQ(1)
#define STM32_RCC_DCKCFGR1_PLLSAIDIVQ RCC_DCKCFGR1_PLLSAIDIVQ(0)
#define STM32_RCC_DCKCFGR1_PLLSAIDIVR RCC_DCKCFGR1_PLLSAIDIVR(1)
#define STM32_RCC_DCKCFGR1_SAI1SRC RCC_DCKCFGR1_SAI1SEL(0)
#define STM32_RCC_DCKCFGR1_SAI2SRC RCC_DCKCFGR1_SAI2SEL(0)
#define STM32_RCC_DCKCFGR1_SAI1SRC RCC_DCKCFGR1_SAI1SEL(1)
#define STM32_RCC_DCKCFGR1_SAI2SRC RCC_DCKCFGR1_SAI2SEL(1)
#define STM32_RCC_DCKCFGR1_TIMPRESRC 0
#define STM32_RCC_DCKCFGR1_DFSDM1SRC 0
#define STM32_RCC_DCKCFGR1_ADFSDM1SRC 0

View File

@ -33,13 +33,18 @@
#include <nuttx/irq.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/audio/i2s.h>
#include <nuttx/audio/pcm.h>
#include <nuttx/audio/wm8994.h>
#include <arch/board/board.h>
#include "chip.h"
#include "stm32f746g-disco.h"
#include "stm32_i2c.h"
#include "stm32_sai.h"
#include "stm32_pwr.h"
#include "stm32_rcc.h"
#define HAVE_WM8994
#define WM8994_I2C_ADDRESS (0x34 >> 1)
@ -112,6 +117,88 @@ static struct stm32_mwinfo_s g_wm8994 =
* Public Functions
****************************************************************************/
static uint32_t stm32_wm8994_sampleratecb(struct i2s_dev_s *dev,
uint32_t rate)
{
uint32_t frequency = 0;
uint32_t regval = 0;
uint32_t mask_divq = 0;
uint32_t mask_i2s = 0;
/* Table 210 in "STM32F75xxx and STM32F74xxx advanced Arm(r)-based 32-bit
* MCUs - Reference manual" suggests the use of specific sai_x_ker_ck
* frequencies for common audio sample frequencies:
* For 44.1, 22.05, 11.025 KHz, 44.1 kHz * 256 = 11289600 Hz
* For 192, 96, 48, 32, 16, 8 KHz, 192 kHz * 256 = 49152000 Hz
*
* The below configurations use 429000000 / 19 / 2 = 1128473.x
* as approximation for the first group of sample frequencies and
* 344000000 / 1 / 7 = 49142857.x as approximation for the second
* group of sample frequencies
*/
if ((rate == 44100) || (rate == 22050) || (rate == 11025))
{
/* Division factor has 1 offset! (i.e. 0 = /1, 1 = /2, etc. */
mask_divq = RCC_DCKCFGR1_PLLI2SDIVQ(18);
mask_i2s = RCC_PLLI2SCFGR_PLLI2SN(429) |
RCC_PLLI2SCFGR_PLLI2SQ(2);
frequency = 11289473;
}
else
{
/* Division factor has 1 offset! (i.e. 0 = /1, 1 = /2, etc. */
mask_divq = RCC_DCKCFGR1_PLLI2SDIVQ(0);
mask_i2s = RCC_PLLI2SCFGR_PLLI2SN(344) |
RCC_PLLI2SCFGR_PLLI2SQ(7);
frequency = 49142857;
}
/* Check if i2s PLL is already in correct configuration */
if ((getreg32(STM32_RCC_DCKCFGR1) &
(RCC_DCKCFGR1_PLLI2SDIVQ_MASK)) != mask_divq)
{
/* Disable the PLLI2S */
regval = getreg32(STM32_RCC_CR);
regval &= ~(RCC_CR_PLLI2SON);
putreg32 (regval, STM32_RCC_CR);
while ((getreg32(STM32_RCC_CR) & RCC_CR_PLLI2SON))
{
}
/* Set PLLI2S Configuration */
regval = getreg32(STM32_RCC_DCKCFGR1);
regval &= ~(RCC_DCKCFGR1_PLLI2SDIVQ_MASK);
regval |= mask_divq;
putreg32(regval, STM32_RCC_DCKCFGR1);
regval = getreg32(STM32_RCC_PLLI2SCFGR);
regval &= ~(RCC_PLLI2SCFGR_PLLI2SN_MASK
| RCC_PLLI2SCFGR_PLLI2SQ_MASK);
regval |= mask_i2s;
putreg32(regval, STM32_RCC_PLLI2SCFGR);
/* Enable the PLLI2S */
regval = getreg32(STM32_RCC_CR);
regval |= (RCC_CR_PLLI2SON);
putreg32 (regval, STM32_RCC_CR);
while (!(getreg32(STM32_RCC_CR) & RCC_CR_PLLI2SRDY))
{
}
}
audinfo("i2s PLL configured for samplerate %lu\n", rate);
return frequency;
}
/****************************************************************************
* Name: stm32_wm8994_initialize
*
@ -132,6 +219,7 @@ static struct stm32_mwinfo_s g_wm8994 =
int stm32_wm8994_initialize(int minor)
{
struct audio_lowerhalf_s *wm8994;
struct audio_lowerhalf_s *pcm;
struct i2c_master_s *i2c;
struct i2s_dev_s *i2s;
static bool initialized = false;
@ -163,7 +251,7 @@ int stm32_wm8994_initialize(int minor)
/* Get an instance of the I2S interface for the CODEC data streams */
i2s = stm32_sai_initialize(WM8994_SAI_BUS);
i2s = stm32_sai_initialize(WM8994_SAI_BUS, stm32_wm8994_sampleratecb);
if (!i2s)
{
auderr("stm32_sai_initialize failed\n");
@ -183,21 +271,32 @@ int stm32_wm8994_initialize(int minor)
goto error;
}
/* No we can embed the WM8994/I2C/I2S conglomerate into a PCM decoder
* instance so that we will have a PCM front end for the the WM8994
* driver.
*/
pcm = pcm_decode_initialize(wm8994);
if (pcm == NULL)
{
auderr("ERROR: Failed create the PCM decoder\n");
ret = -ENODEV;
goto error;
}
/* Create a device name */
snprintf(devname, 12, "pcm%d", minor);
#if 0
/* Finally, we can register the ADAU1961/I2C/I2S audio device. */
ret = audio_register(devname, wm8994);
ret = audio_register(devname, pcm);
if (ret < 0)
{
auderr("failed to register /dev/%s device: %d\n", devname, ret);
goto error;
}
#endif
/* Now we are initialized */
initialized = true;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff