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:
parent
5977279f0e
commit
476770e9fd
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user