diff --git a/arch/arm/src/stm32f7/stm32_rcc.h b/arch/arm/src/stm32f7/stm32_rcc.h index 7aff6e03430..9a221c4ba85 100644 --- a/arch/arm/src/stm32f7/stm32_rcc.h +++ b/arch/arm/src/stm32f7/stm32_rcc.h @@ -82,8 +82,8 @@ extern uint32_t _vectors[]; /* See stm32_vectors.S */ * Name: stm32_mco1config * * Description: - * Selects the clock source to output on MCO1 pin (PA8). PA8 should be configured in - * alternate function mode. + * Selects the clock source to output on MCO1 pin (PA8). PA8 should be configured + * in alternate function mode. * * Input Parameters: * source - One of the definitions for the RCC_CFGR_MCO1 definitions from @@ -103,7 +103,7 @@ static inline void stm32_mco1config(uint32_t source, uint32_t div) uint32_t regval; regval = getreg32(STM32_RCC_CFGR); - regval &= ~(RCC_CFGR_MCO1_MASK|RCC_CFGR_MCO1PRE_MASK); + regval &= ~(RCC_CFGR_MCO1_MASK | RCC_CFGR_MCO1PRE_MASK); regval |= (source | div); putreg32(regval, STM32_RCC_CFGR); } @@ -112,8 +112,8 @@ static inline void stm32_mco1config(uint32_t source, uint32_t div) * Name: stm32_mco2config * * Description: - * Selects the clock source to output on MCO2 pin (PC9). PC9 should be configured in - * alternate function mode. + * Selects the clock source to output on MCO2 pin (PC9). PC9 should be configured + * in alternate function mode. * * Input Parameters: * source - One of the definitions for the RCC_CFGR_MCO2 definitions from @@ -133,7 +133,7 @@ static inline void stm32_mco2config(uint32_t source, uint32_t div) uint32_t regval; regval = getreg32(STM32_RCC_CFGR); - regval &= ~(RCC_CFGR_MCO2_MASK|RCC_CFGR_MCO2PRE_MASK); + regval &= ~(RCC_CFGR_MCO2_MASK | RCC_CFGR_MCO2PRE_MASK); regval |= (source | div); putreg32(regval, STM32_RCC_CFGR); } @@ -223,44 +223,44 @@ void stm32_clockenable(void); void stm32_rcc_enablelse(void); -/**************************************************************************** +/************************************************************************************ * Name: stm32_rcc_enablelsi * * Description: * Enable the Internal Low-Speed (LSI) RC Oscillator. * - ****************************************************************************/ + ************************************************************************************/ void stm32_rcc_enablelsi(void); -/**************************************************************************** +/************************************************************************************ * Name: stm32_rcc_disablelsi * * Description: * Disable the Internal Low-Speed (LSI) RC Oscillator. * - ****************************************************************************/ + ************************************************************************************/ void stm32_rcc_disablelsi(void); #if defined(CONFIG_STM32F7_STM32F76XX) || defined(CONFIG_STM32F7_STM32F77XX) -/**************************************************************************** +/************************************************************************************ * Name: stm32f7x9_rcc_dsisrcphy * * Description: * Set DSI clock source to DSI PHY * - ****************************************************************************/ + ************************************************************************************/ void stm32f7x9_rcc_dsisrcphy(void); -/**************************************************************************** +/************************************************************************************ * Name: stm32f7x9_rcc_dsisrcpllr * * Description: * Set DSI clock source to PLLR * - ****************************************************************************/ + ************************************************************************************/ void stm32f7x9_rcc_dsisrcpllr(void); #endif diff --git a/arch/arm/src/stm32f7/stm32_sai.c b/arch/arm/src/stm32f7/stm32_sai.c index 7d83d55028b..1dfe08a9c9b 100644 --- a/arch/arm/src/stm32f7/stm32_sai.c +++ b/arch/arm/src/stm32f7/stm32_sai.c @@ -236,10 +236,8 @@ struct stm32f7_sai_s #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 */ @@ -704,51 +702,6 @@ static void sai_dump_regs(struct stm32f7_sai_s *priv, const char *msg) } #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); - i2sinfo("PLLSAICFGR = %08x\n", pll_sai_cfgr); - - uint32_t pllsain = (pll_sai_cfgr & RCC_PLLSAICFGR_PLLSAIN_MASK) >> - RCC_PLLSAICFGR_PLLSAIN_SHIFT; - i2sinfo("\t\tPLLSAICFGR PLLSAIN[14:6] = %d\n", pllsain); - uint32_t pllsaip = (pll_sai_cfgr & RCC_PLLSAICFGR_PLLSAIP_MASK) >> - RCC_PLLSAICFGR_PLLSAIP_SHIFT; - i2sinfo("\t\tPLLSAICFGR PLLSAIP[17:16] = %d\n", pllsaip); - uint32_t pllsaiq = (pll_sai_cfgr & RCC_PLLSAICFGR_PLLSAIQ_MASK) >> - RCC_PLLSAICFGQ_PLLSAIP_SHIFT; - i2sinfo("\t\tPLLSAICFGR PLLSAIQ[27:24] = %d\n", pllsaiq); - - uint32_t pllsair = (pll_sai_cfgr & RCC_PLLSAICFGR_PLLSAIR_MASK) >> - RCC_PLLSAICFGR_PLLSAIP_SHIFT; - i2sinfo("\t\tPLLSAICFGR PLLSAIR[30:28] = %d\n", pllsair); -#endif -} -#endif - /**************************************************************************** * Name: sai_exclsem_take * diff --git a/arch/arm/src/stm32f7/stm32f74xx75xx_rcc.c b/arch/arm/src/stm32f7/stm32f74xx75xx_rcc.c index 397c6ce937f..833f826e066 100644 --- a/arch/arm/src/stm32f7/stm32f74xx75xx_rcc.c +++ b/arch/arm/src/stm32f7/stm32f74xx75xx_rcc.c @@ -241,7 +241,7 @@ static inline void rcc_enableahb1(void) regval |= RCC_AHB1ENR_OTGHSEN; #endif -#endif /* CONFIG_STM32F7_OTGFSHS */ +#endif /* CONFIG_STM32F7_OTGFSHS */ putreg32(regval, STM32_RCC_AHB1ENR); /* Enable peripherals */ } @@ -857,7 +857,8 @@ static void stm32_stdclockconfig(void) /* Wait until the PLL source is used as the system clock source */ - while ((getreg32(STM32_RCC_CFGR) & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_PLL) + while ((getreg32(STM32_RCC_CFGR) + & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_PLL) { } @@ -1001,7 +1002,3 @@ static inline void rcc_enableperipherals(void) rcc_enableapb1(); rcc_enableapb2(); } - -/**************************************************************************** - * Public Functions - ****************************************************************************/ diff --git a/boards/arm/stm32f7/stm32f746g-disco/configs/audio/defconfig b/boards/arm/stm32f7/stm32f746g-disco/configs/audio/defconfig new file mode 100644 index 00000000000..79b719cc1ac --- /dev/null +++ b/boards/arm/stm32f7/stm32f746g-disco/configs/audio/defconfig @@ -0,0 +1,82 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_FPU is not set +# CONFIG_SPI_CALLBACK is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="stm32f746g-disco" +CONFIG_ARCH_BOARD_STM32F746G_DISCO=y +CONFIG_ARCH_BUTTONS=y +CONFIG_ARCH_CHIP="stm32f7" +CONFIG_ARCH_CHIP_STM32F746NG=y +CONFIG_ARCH_CHIP_STM32F7=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARMV7M_DCACHE=y +CONFIG_ARMV7M_DCACHE_WRITETHROUGH=y +CONFIG_ARMV7M_DTCM=y +CONFIG_ARMV7M_ICACHE=y +CONFIG_AUDIO_I2S=y +CONFIG_AUDIO_WM8994=y +CONFIG_BOARD_LATE_INITIALIZE=y +CONFIG_BOARD_LOOPSPERMSEC=43103 +CONFIG_BUILTIN=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_AUDIO=y +CONFIG_DEBUG_AUDIO_ERROR=y +CONFIG_DEBUG_AUDIO_INFO=y +CONFIG_DEBUG_AUDIO_WARN=y +CONFIG_DEBUG_ERROR=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_I2C=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_MEMCARD=y +CONFIG_DEBUG_MEMCARD_ERROR=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_DEBUG_WARN=y +CONFIG_DRIVERS_AUDIO=y +CONFIG_FS_PROCFS=y +CONFIG_FS_PROCFS_REGISTER=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_I2C=y +CONFIG_INTELHEX_BINARY=y +CONFIG_MAX_TASKS=16 +CONFIG_MMCSD=y +CONFIG_MM_REGIONS=3 +CONFIG_NFILE_DESCRIPTORS=8 +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_DISABLE_IFUPDOWN=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=245760 +CONFIG_RAM_START=0x20010000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_LPWORK=y +CONFIG_SCHED_WAITPID=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_SPI=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_STM32F7_DMA2=y +CONFIG_STM32F7_I2C3=y +CONFIG_STM32F7_SAI2=y +CONFIG_STM32F7_SAI2_A=y +CONFIG_STM32F7_SAI2_B=y +CONFIG_STM32F7_SAI2_B_SYNC_WITH_A=y +CONFIG_STM32F7_USART1=y +CONFIG_SYSLOG_CHAR=y +CONFIG_SYSLOG_DEVPATH="/dev/ttyS0" +CONFIG_SYSTEM_NSH=y +CONFIG_SYSTEM_VI=y +CONFIG_TASK_NAME_SIZE=0 +CONFIG_USART1_SERIAL_CONSOLE=y +CONFIG_USER_ENTRYPOINT="nsh_main" +CONFIG_WM8994_REGDUMP=y diff --git a/boards/arm/stm32f7/stm32f746g-disco/include/board.h b/boards/arm/stm32f7/stm32f746g-disco/include/board.h index bbe0bdfa0c0..b59daeeb60a 100644 --- a/boards/arm/stm32f7/stm32f746g-disco/include/board.h +++ b/boards/arm/stm32f7/stm32f746g-disco/include/board.h @@ -1,7 +1,7 @@ -/**************************************************************************** +/***************************************************************************************** * boards/arm/stm32f7/stm32f746g-disco/include/board.h * - * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Copyright (C) 2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -31,14 +31,14 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - ****************************************************************************/ + *****************************************************************************************/ #ifndef __BOARDS_ARM_STM32F7_STM32F746G_DISCO_INCLUDE_BOARD_H #define __BOARDS_ARM_STM32F7_STM32F746G_DISCO_INCLUDE_BOARD_H -/**************************************************************************** +/***************************************************************************************** * Included Files - ****************************************************************************/ + *****************************************************************************************/ #include @@ -48,11 +48,11 @@ /* No not include STM32 F7 header files here. */ -/**************************************************************************** +/***************************************************************************************** * Pre-processor Definitions - ****************************************************************************/ + *****************************************************************************************/ -/* Clocking *****************************************************************/ +/* Clocking */ /* The STM32F7 Discovery board provides the following clock sources: * @@ -154,15 +154,22 @@ /* Configure factors for PLLSAI clock */ +#define CONFIG_STM32F7_PLLSAI 1 #define STM32_RCC_PLLSAICFGR_PLLSAIN RCC_PLLSAICFGR_PLLSAIN(BOARD_LTDC_PLLSAIN) #define STM32_RCC_PLLSAICFGR_PLLSAIP RCC_PLLSAICFGR_PLLSAIP(2) #define STM32_RCC_PLLSAICFGR_PLLSAIQ RCC_PLLSAICFGR_PLLSAIQ(2) #define STM32_RCC_PLLSAICFGR_PLLSAIR RCC_PLLSAICFGR_PLLSAIR(BOARD_LTDC_PLLSAIR) +/* SAIx input frequency = 25 / M * N / Q / P + * 25000000 / 25 * 192 / 2 / 1 + */ +#define STM32F7_SAI1_FREQUENCY (96000000) +#define STM32F7_SAI2_FREQUENCY (96000000) + /* Configure Dedicated Clock Configuration Register */ #define STM32_RCC_DCKCFGR1_PLLI2SDIVQ RCC_DCKCFGR1_PLLI2SDIVQ(1) -#define STM32_RCC_DCKCFGR1_PLLSAIDIVQ RCC_DCKCFGR1_PLLSAIDIVQ(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) @@ -172,6 +179,7 @@ /* Configure factors for PLLI2S clock */ +#define CONFIG_STM32F7_PLLI2S 1 #define STM32_RCC_PLLI2SCFGR_PLLI2SN RCC_PLLI2SCFGR_PLLI2SN(192) #define STM32_RCC_PLLI2SCFGR_PLLI2SP RCC_PLLI2SCFGR_PLLI2SP(2) #define STM32_RCC_PLLI2SCFGR_PLLI2SQ RCC_PLLI2SCFGR_PLLI2SQ(2) @@ -253,7 +261,7 @@ #define BOARD_FLASH_WAITSTATES 7 -/* LED definitions **********************************************************/ +/* LED definitions */ /* The STM32F746G-DISCO board has numerous LEDs but only one, LD1 located * near the reset button, that can be controlled by software (LD2 is a power @@ -306,7 +314,7 @@ #define LED_ASSERTION 2 /* LD1=no change */ #define LED_PANIC 3 /* LD1=flashing */ -/* Button definitions *******************************************************/ +/* Button definitions */ /* The STM32F7 Discovery supports one button: * Pushbutton B1, labelled "User", is connected to GPIO PI11. @@ -317,7 +325,7 @@ #define NUM_BUTTONS 1 #define BUTTON_USER_BIT (1 << BUTTON_USER) -/* Alternate function pin selections ****************************************/ +/* Alternate function pin selections */ /* USART6: * @@ -361,12 +369,13 @@ /* I2C - There is a FT5336 TouchPanel on I2C3 using these pins: */ -#define GPIO_I2C3_SCL GPIO_I2C3_SCL_2 -#define GPIO_I2C3_SDA GPIO_I2C3_SDA_2 +#define ADJ_SLEW_RATE(p) (((p) & ~GPIO_SPEED_MASK) | (GPIO_SPEED_100MHz)) +#define GPIO_I2C3_SCL ADJ_SLEW_RATE(GPIO_I2C3_SCL_2) +#define GPIO_I2C3_SDA ADJ_SLEW_RATE(GPIO_I2C3_SDA_2) #define GPIO_TP_INT (GPIO_INPUT|GPIO_FLOAT|GPIO_EXTI|GPIO_PORTI|GPIO_PIN13) -#define FT5x06_I2C_ADDRESS 0x38 +#define FT5x06_I2C_ADDRESS (0x70 >> 1) /* The STM32 F7 connects to a SMSC LAN8742A PHY using these pins: * @@ -394,7 +403,7 @@ #define GPIO_ETH_RMII_TXD0 GPIO_ETH_RMII_TXD0_2 #define GPIO_ETH_RMII_TXD1 GPIO_ETH_RMII_TXD1_2 -/* LCD definitions **********************************************************/ +/* LCD definitions */ #define BOARD_LTDC_WIDTH 480 #define BOARD_LTDC_HEIGHT 272 @@ -513,4 +522,21 @@ # define STM32_SDMMC_SDXFR_CLKDIV (2 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT) #endif -#endif /* __BOARDS_ARM_STM32F7_STM32F746G_DISCO_INCLUDE_BOARD_H */ +/* SAI2 pinset */ +#if defined(CONFIG_STM32F7_SAI2) && defined(CONFIG_STM32F7_SAI2_A) +#define GPIO_SAI2_SD_A GPIO_SAI2_SD_A_2 +#define GPIO_SAI2_FS_A GPIO_SAI2_FS_A_2 +#define GPIO_SAI2_SCK_A GPIO_SAI2_SCK_A_2 +#define GPIO_SAI2_MCLK_A GPIO_SAI2_MCLK_A_2 +#define GPIO_SAI2_SD_B GPIO_SAI2_SD_B_4 + +#define DMACHAN_SAI2_A DMAMAP_SAI2_A +#define DMACHAN_SAI2_B DMAMAP_SAI2_B + +#else + +#define GPIO_SAI1_SD_B GPIO_SAI1_SD_B_1 +#define DMACHAN_SAI1_B DMAMAP_SAI1_B + +#endif +#endif diff --git a/boards/arm/stm32f7/stm32f746g-disco/src/Makefile b/boards/arm/stm32f7/stm32f746g-disco/src/Makefile index f0e4e1a083a..806bcac4dcc 100644 --- a/boards/arm/stm32f7/stm32f746g-disco/src/Makefile +++ b/boards/arm/stm32f7/stm32f746g-disco/src/Makefile @@ -1,7 +1,7 @@ ############################################################################ # boards/arm/stm32f7/stm32f746g-disco/src/Makefile # -# Copyright (C) 2015 Gregory Nutt. All rights reserved. +# Copyright (C) 2019 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -83,4 +83,8 @@ ifeq ($(CONFIG_STM32F7_SDMMC), y) CSRCS += stm32_sdmmc.c endif +ifeq ($(CONFIG_AUDIO_WM8994), y) +CSRCS += stm32_wm8994.c +endif + include $(TOPDIR)/boards/Board.mk diff --git a/boards/arm/stm32f7/stm32f746g-disco/src/stm32_bringup.c b/boards/arm/stm32f7/stm32f746g-disco/src/stm32_bringup.c index ed2eb41fa10..8ace173ccfc 100644 --- a/boards/arm/stm32f7/stm32f746g-disco/src/stm32_bringup.c +++ b/boards/arm/stm32f7/stm32f746g-disco/src/stm32_bringup.c @@ -154,6 +154,14 @@ int stm32_bringup(void) } #endif +#ifdef CONFIG_AUDIO_WM8994 + ret = stm32_wm8994_initialize(0); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_wm8994_initialize failed: %d\n", ret); + } +#endif + UNUSED(ret); /* May not be used */ return OK; } diff --git a/boards/arm/stm32f7/stm32f746g-disco/src/stm32_wm8994.c b/boards/arm/stm32f7/stm32f746g-disco/src/stm32_wm8994.c new file mode 100644 index 00000000000..3fd994d2976 --- /dev/null +++ b/boards/arm/stm32f7/stm32f746g-disco/src/stm32_wm8994.c @@ -0,0 +1,216 @@ +/**************************************************************************** + * boards/arm/stm32f7/stm32f746g-disco/src/stm32_wm8994.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "stm32f746g-disco.h" +#include "stm32_i2c.h" +#include "stm32_sai.h" + +#define HAVE_WM8994 +#define WM8994_I2C_ADDRESS (0x34 >> 1) +#define WM8994_I2C_BUS 3 +#define WM8994_SAI_BUS 2 + +#ifdef HAVE_WM8994 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct stm32_mwinfo_s +{ + /* Standard ADAU1961 interface */ + + struct wm8994_lower_s lower; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int wm8994_attach(FAR const struct wm8994_lower_s *lower, + wm8994_handler_t isr, FAR void *arg) +{ + audinfo("TODO\n"); + return 0; +} + +static bool wm8994_enable(FAR const struct wm8994_lower_s *lower, + bool enable) +{ + audinfo("TODO\n"); + return 0; +} + +#if 0 +static void wm8994_hw_reset(FAR const struct wm8994_lower_s *lower) +{ + audinfo("TODO\n"); +} +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* A reference to a structure of this type must be passed to the ADAU1961 + * driver. This structure provides information about the configuration + * of the ADAU1961 and provides some board-specific hooks. + * + * Memory for this structure is provided by the caller. It is not copied + * by the driver and is presumed to persist while the driver is active. + */ + +static struct stm32_mwinfo_s g_wm8994 = +{ + .lower = + { + .address = WM8994_I2C_ADDRESS, + .frequency = I2C_SPEED_FAST, /* 100 kHz */ + .mclk = 12000000, /* see MCO1 configuration */ + .attach = wm8994_attach, + .enable = wm8994_enable, + } +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_wm8994_initialize + * + * Description: + * This function is called by platform-specific, setup logic to configure + * and register the ADAU1961 device. This function will register the driver + * as /dev/audio/pcm[x] where x is determined by the minor device number. + * + * Input Parameters: + * minor - The input device minor number + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int stm32_wm8994_initialize(int minor) +{ + FAR struct audio_lowerhalf_s *wm8994; + FAR struct i2c_master_s *i2c; + FAR struct i2s_dev_s *i2s; + static bool initialized = false; + char devname[12]; + int ret; + + audinfo("minor %d\n", minor); + DEBUGASSERT(minor >= 0 && minor <= 25); + + /* Initialize the CODEC if we have not already done so */ + + if (!initialized) + { + /* Configure MC01 to drive the master clock of the CODEC at 8MHz */ + + /* stm32_configgpio(GPIO_MCO1); + * stm32_mco1config(RCC_CFGR_MCO1_HSE, RCC_CFGR_MCO1PRE_NONE); + */ + + /* Get an instance of the I2C interface for the CODEC */ + + i2c = stm32_i2cbus_initialize(WM8994_I2C_BUS); + if (!i2c) + { + auderr("stm32_i2cbus_initialize failed\n"); + ret = -ENODEV; + goto error; + } + + /* Get an instance of the I2S interface for the CODEC data streams */ + + i2s = stm32_sai_initialize(WM8994_SAI_BUS); + if (!i2s) + { + auderr("stm32_sai_initialize failed\n"); + ret = -ENODEV; + goto error; + } + + /* Now we can use these I2C and I2S interfaces to initialize the + * CODEC which will return an audio interface. + */ + + wm8994 = wm8994_initialize(i2c, i2s, &g_wm8994.lower); + if (!wm8994) + { + auderr("wm8994_initialize failed\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); + if (ret < 0) + { + auderr("failed to register /dev/%s device: %d\n", devname, ret); + goto error; + } + +#endif + + /* Now we are initialized */ + + initialized = true; + } + + return OK; + + /* Error exits. Unfortunately there is no mechanism in place now to + * recover resources from most errors on initialization failures. + */ + +error: + return ret; +} + +#endif /* HAVE_WM8994 */ diff --git a/boards/arm/stm32f7/stm32f746g-disco/src/stm32f746g-disco.h b/boards/arm/stm32f7/stm32f746g-disco/src/stm32f746g-disco.h index f5c0d492d28..3f31914a263 100644 --- a/boards/arm/stm32f7/stm32f746g-disco/src/stm32f746g-disco.h +++ b/boards/arm/stm32f7/stm32f746g-disco/src/stm32f746g-disco.h @@ -1,4 +1,4 @@ -/**************************************************************************** +/******************************************************************************* * boards/arm/stm32f7/stm32f746g-disco/src/stm32f746g-disco.h * * Copyright (C) 2015, 2017 Gregory Nutt. All rights reserved. @@ -31,22 +31,22 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - ****************************************************************************/ + *******************************************************************************/ #ifndef __BOARDS_ARM_STM32F7_STM32F746G_DISCO_SRC_STM32F746G_DISCO__H #define __BOARDS_ARM_STM32F7_STM32F746G_DISCO_SRC_STM32F746G_DISCO__H -/**************************************************************************** +/******************************************************************************* * Included Files - ****************************************************************************/ + *******************************************************************************/ #include #include #include -/**************************************************************************** +/******************************************************************************* * Pre-processor Definitions - ****************************************************************************/ + *******************************************************************************/ /* procfs File System */ @@ -64,7 +64,7 @@ #undef HAVE_SDIO #endif -/* STM32F736G Discovery GPIOs ***********************************************/ +/* STM32F736G Discovery GPIOs */ /* The STM32F746G-DISCO board has numerous LEDs but only one, * LD1 located near the reset button, that can be controlled by software @@ -120,17 +120,13 @@ #define SDIO_SLOTNO 0 #define SDIO_MINOR 0 -/**************************************************************************** +/******************************************************************************* * Public data - ****************************************************************************/ + *******************************************************************************/ #ifndef __ASSEMBLY__ -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** +/******************************************************************************* * Name: stm32_bringup * * Description: @@ -142,70 +138,72 @@ * CONFIG_BOARD_LATE_INITIALIZE=n && CONFIG_LIB_BOARDCTL=y : * Called from the NSH library * - ****************************************************************************/ + *******************************************************************************/ int stm32_bringup(void); -/**************************************************************************** +/******************************************************************************* * Name: stm32_adc_setup * * Description: * Initialize ADC and register the ADC driver. * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_ADC int stm32_adc_setup(void); #endif -/**************************************************************************** +/******************************************************************************* * Name: stm32_spidev_initialize * * Description: - * Called to configure SPI chip select GPIO pins for the stm32f746g-disco board. + * Called to configure SPI chip select GPIO pins for the + * stm32f746g-disco board. * - ****************************************************************************/ + *******************************************************************************/ void weak_function stm32_spidev_initialize(void); -/**************************************************************************** +/******************************************************************************* * Name: arch_sporadic_initialize * * Description: - * This configuration has been used for evaluating the NuttX sporadic scheduler. + * This configuration has been used for evaluating the NuttX sporadic + * scheduler. * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_SPORADIC_INSTRUMENTATION void arch_sporadic_initialize(void); #endif -/**************************************************************************** +/******************************************************************************* * Name: stm32_enablefmc * * Description: * enable clocking to the FMC module * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_STM32F7_FMC void stm32_enablefmc(void); #endif -/**************************************************************************** +/******************************************************************************* * Name: stm32_disablefmc * * Description: * disable clocking to the FMC module * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_STM32F7_FMC void stm32_disablefmc(void); #endif -/**************************************************************************** +/******************************************************************************* * Name: stm32_tsc_setup * * Description: @@ -220,7 +218,7 @@ void stm32_disablefmc(void); * Zero is returned on success. Otherwise, a negated errno value is * returned to indicate the nature of the failure. * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_INPUT_FT5X06 int stm32_tsc_setup(int minor); @@ -234,6 +232,10 @@ int stm32_n25qxxx_setup(void); int stm32_sdio_initialize(void); #endif +#ifdef CONFIG_AUDIO_WM8994 +int stm32_wm8994_initialize(int minor); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_ARM_STM32F7_STM32F746G_DISCO_SRC_STM32F746G_DISCO_H */ diff --git a/drivers/audio/Kconfig b/drivers/audio/Kconfig index a5182fa6bec..1d492b5c55e 100644 --- a/drivers/audio/Kconfig +++ b/drivers/audio/Kconfig @@ -234,6 +234,67 @@ config WM8776_SWAP_HPOUT endif # AUDIO_WM8776 +config AUDIO_WM8994 + bool "WM8994 audio chip" + default n + depends on AUDIO + ---help--- + Select to enable support for the WM8994 Audio codec by Wolfson + Microelectonics. This chip is a lower level audio chip.. basically + an exotic D-to-A. It includes no built-in support for audio CODECS + The WM8994 provides: + + - Low power consumption + - High SNR + - Stereo digital microphone input + - Digital Dynamic Range Controller (compressor / limiter) + - Digital sidetone mixing + - Ground-referenced headphone driver + - Ground-referenced line outputs + + NOTE: This driver also depends on both I2C and I2S support although + that dependency is not explicit here. + +if AUDIO_WM8994 + +config WM8994_INITVOLUME + int "WM8994 initial volume setting" + default 250 + +config WM8994_INFLIGHT + int "WM8994 maximum in-flight audio buffers" + default 2 + +config WM8994_MSG_PRIO + int "WM8994 message priority" + default 1 + +config WM8994_BUFFER_SIZE + int "WM8994 preferred buffer size" + default 8192 + +config WM8994_NUM_BUFFERS + int "WM8994 preferred number of buffers" + default 4 + +config WM8994_WORKER_STACKSIZE + int "WM8994 worker thread stack size" + default 768 + +config WM8994_REGDUMP + bool "WM8994 register dump" + default n + ---help--- + Enable logic to dump the contents of all WM8994 registers. + +config WM8994_CLKDEBUG + bool "WM8994 clock analysis" + default n + ---help--- + Enable logic to analyze WM8994 clock configuation. + +endif # AUDIO_WM8994 + config AUDIO_WM8904 bool "WM8904 audio chip" default n diff --git a/drivers/audio/Make.defs b/drivers/audio/Make.defs index cb8bb6e7dad..e6decebfacd 100644 --- a/drivers/audio/Make.defs +++ b/drivers/audio/Make.defs @@ -65,6 +65,17 @@ ifeq ($(CONFIG_AUDIO_CS4344),y) CSRCS += cs4344.c endif +ifeq ($(CONFIG_AUDIO_WM8994),y) +CSRCS += wm8994.c +ifeq ($(CONFIG_WM8994_REGDUMP),y) +CSRCS += wm8994_debug.c +else +ifeq ($(CONFIG_WM8994_CLKDEBUG),y) +CSRCS += wm8994_debug.c +endif +endif +endif + ifeq ($(CONFIG_AUDIO_WM8904),y) CSRCS += wm8904.c ifeq ($(CONFIG_WM8904_REGDUMP),y) diff --git a/drivers/audio/wm8994.c b/drivers/audio/wm8994.c new file mode 100644 index 00000000000..1a957233def --- /dev/null +++ b/drivers/audio/wm8994.c @@ -0,0 +1,2011 @@ +/*********************************************************************************** + * drivers/audio/wm8994.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ***********************************************************************************/ + +/*********************************************************************************** + * Included Files + ***********************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8994.h" + +/* Pre-processor Definitions */ + +/* Maximum number of retries */ + +#define MAX_RETRIES 3 + +#define WM8994_OUTPUT_DEVICE_SPEAKER ((uint16_t)0x0001) +#define WM8994_OUTPUT_DEVICE_HEADPHONE ((uint16_t)0x0002) +#define WM8994_OUTPUT_DEVICE_BOTH ((uint16_t)0x0003) +#define WM8994_OUTPUT_DEVICE_AUTO ((uint16_t)0x0004) + +#define WM8994_INPUT_DEVICE_DIGITAL_MICROPHONE_1 ((uint16_t)0x0100) +#define WM8994_INPUT_DEVICE_DIGITAL_MICROPHONE_2 ((uint16_t)0x0200) +#define WM8994_INPUT_DEVICE_INPUT_LINE_1 ((uint16_t)0x0300) +#define WM8994_INPUT_DEVICE_INPUT_LINE_2 ((uint16_t)0x0400) +#define WM8994_INPUT_DEVICE_DIGITAL_MIC1_MIC2 ((uint16_t)0x0800) + +#define WM8994_DEFAULT_OUTPUT_DEVICE (WM8994_OUTPUT_DEVICE_SPEAKER) +#define WM8994_DEFAULT_INPUT_DEVICE (WM8994_INPUT_DEVICE_DIGITAL_MIC1_MIC2) +#define WM8994_STARTUP_MODE_COLD (1) + +/*********************************************************************************** + * Private Function Prototypes + ***********************************************************************************/ + +#if !defined(CONFIG_WM8994_REGDUMP) && !defined(CONFIG_WM8994_CLKDEBUG) +static +#endif + uint16_t wm8994_readreg(FAR struct wm8994_dev_s *priv, + uint16_t regaddr); +static void wm8994_writereg(FAR struct wm8994_dev_s *priv, + uint16_t regaddr, uint16_t regval); +static void wm8994_takesem(sem_t *sem); +#define wm8994_givesem(s) nxsem_post(s) + +#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME +static inline uint16_t wm8994_scalevolume(uint16_t volume, b16_t scale); +#endif + +/* Audio lower half methods (and close friends) */ + +static int wm8994_getcaps(FAR struct audio_lowerhalf_s *dev, int type, + FAR struct audio_caps_s *caps); +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_configure(FAR struct audio_lowerhalf_s *dev, + FAR void *session, FAR const struct audio_caps_s *caps); +#else +static int wm8994_configure(FAR struct audio_lowerhalf_s *dev, + FAR const struct audio_caps_s *caps); +#endif +static int wm8994_shutdown(FAR struct audio_lowerhalf_s *dev); +static void wm8994_senddone(FAR struct i2s_dev_s *i2s, + FAR struct ap_buffer_s *apb, FAR void *arg, int result); +static void wm8994_returnbuffers(FAR struct wm8994_dev_s *priv); +static int wm8994_sendbuffer(FAR struct wm8994_dev_s *priv); + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_start(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#else +static int wm8994_start(FAR struct audio_lowerhalf_s *dev); +#endif +#ifndef CONFIG_AUDIO_EXCLUDE_STOP +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_stop(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#else +static int wm8994_stop(FAR struct audio_lowerhalf_s *dev); +#endif +#endif +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_pause(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +static int wm8994_resume(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#else +static int wm8994_pause(FAR struct audio_lowerhalf_s *dev); +static int wm8994_resume(FAR struct audio_lowerhalf_s *dev); +#endif +#endif +static int wm8994_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb); +static int wm8994_cancelbuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb); +static int wm8994_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, + unsigned long arg); +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_reserve(FAR struct audio_lowerhalf_s *dev, + FAR void **session); +#else +static int wm8994_reserve(FAR struct audio_lowerhalf_s *dev); +#endif +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_release(FAR struct audio_lowerhalf_s *dev, + FAR void *session); +#else +static int wm8994_release(FAR struct audio_lowerhalf_s *dev); +#endif + +/* Interrupt handling an worker thread */ + +#ifdef WM8994_USE_FFLOCK_INT +static void wm8994_interrupt_work(FAR void *arg); +static int wm8994_interrupt(FAR const struct wm8994_lower_s *lower, + FAR void *arg); +#endif + +static void *wm8994_workerthread(pthread_addr_t pvarg); + +/* Initialization */ + +static void wm8994_audio_output(FAR struct wm8994_dev_s *priv); +static void wm8994_audio_input(FAR struct wm8994_dev_s *priv); +#if 0 /* Not used */ +static void wm8994_audio_input(FAR struct wm8994_dev_s *priv); +#endif +#ifdef WM8994_USE_FFLOCK_INT +static void wm8994_configure_ints(FAR struct wm8994_dev_s *priv); +#else +# define wm8994_configure_ints(p) +#endif +static void wm8994_hw_reset(FAR struct wm8994_dev_s *priv); + +/* Private Data */ + +static const struct audio_ops_s g_audioops = +{ + wm8994_getcaps, /* getcaps */ + wm8994_configure, /* configure */ + wm8994_shutdown, /* shutdown */ + wm8994_start, /* start */ +#ifndef CONFIG_AUDIO_EXCLUDE_STOP + wm8994_stop, /* stop */ +#endif +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME + wm8994_pause, /* pause */ + wm8994_resume, /* resume */ +#endif + NULL, /* allocbuffer */ + NULL, /* freebuffer */ + wm8994_enqueuebuffer, /* enqueue_buffer */ + wm8994_cancelbuffer, /* cancel_buffer */ + wm8994_ioctl, /* ioctl */ + NULL, /* read */ + NULL, /* write */ + wm8994_reserve, /* reserve */ + wm8994_release /* release */ +}; + +/* Private Functions */ + +/* Name: wm8994_readreg + * + * Description: + * Read the specified 16-bit register from the WM8994 device. + * + */ + +#if !defined(CONFIG_WM8994_REGDUMP) && !defined(CONFIG_WM8994_CLKDEBUG) +static +#endif +uint16_t wm8994_readreg(FAR struct wm8994_dev_s *priv, uint16_t regaddr) +{ + int retries; + + /* Try up to three times to read the register */ + + for (retries = 1; retries <= MAX_RETRIES; retries++) + { + struct i2c_msg_s msg[2]; + uint8_t data[2]; + uint16_t buffer = ((regaddr >> 8) & 0xff) | ((regaddr << 8) & 0xff00); + int ret; + + /* Set up to write the address */ + + msg[0].frequency = priv->lower->frequency; + msg[0].addr = priv->lower->address; + msg[0].flags = 0; + msg[0].buffer = (uint8_t *)&buffer; + msg[0].length = 2; + + /* Followed by the read data */ + + msg[1].frequency = priv->lower->frequency; + msg[1].addr = priv->lower->address; + msg[1].flags = I2C_M_READ; + msg[1].buffer = data; + msg[1].length = 2; + + /* Read the register data. The returned value is the number messages + * completed. + */ + + ret = I2C_TRANSFER(priv->i2c, msg, 2); + if (ret < 0) + { +#ifdef CONFIG_I2C_RESET + /* Perhaps the I2C bus is locked up? Try to shake the bus free. + * Don't bother with the reset if this was the last attempt. + */ + + if (retries < MAX_RETRIES) + { + audwarn("WARNING: I2C_TRANSFER failed: %d ... Resetting\n", ret); + + ret = I2C_RESET(priv->i2c); + if (ret < 0) + { + auderr("ERROR: I2C_RESET failed: %d\n", ret); + break; + } + } +#else + auderr("ERROR: I2C_TRANSFER failed: %d\n", ret); +#endif + } + else + { + uint16_t regval; + + /* The I2C transfer was successful... break out of the loop and + * return the value read. + */ + + regval = ((uint16_t)data[0] << 8) | (uint16_t)data[1]; + audinfo("Read: %02x -> %04x\n", regaddr, regval); + return regval; + } + + audinfo("retries=%d regaddr=%02x\n", retries, regaddr); + } + + /* No error indication is returned on a failure... just return zero */ + + return 0; +} + +/* Name: wm8994_writereg + * + * Description: + * Write the specified 16-bit register to the WM8994 device. + * + */ + +static void wm8994_writereg(FAR struct wm8994_dev_s *priv, uint16_t regaddr, + uint16_t regval) +{ + struct i2c_config_s config; + int retries; + + /* Setup up the I2C configuration */ + + config.frequency = priv->lower->frequency; + config.address = priv->lower->address; + config.addrlen = 7; + + /* Try up to three times to read the register */ + + for (retries = 1; retries <= MAX_RETRIES; retries++) + { + uint8_t data[4]; + int ret; + + /* Set up the data to write */ + + data[0] = regaddr >> 8; + data[1] = regaddr & 0xff; + data[2] = regval >> 8; + data[3] = regval & 0xff; + + /* Read the register data. The returned value is the number messages + * completed. + */ + + ret = i2c_write(priv->i2c, &config, data, 3); + if (ret < 0) + { +#ifdef CONFIG_I2C_RESET + /* Perhaps the I2C bus is locked up? Try to shake the bus free. + * Don't bother with the reset if this was the last attempt. + */ + + if (retries < MAX_RETRIES) + { + audwarn("WARNING: i2c_write failed: %d ... Resetting\n", ret); + + ret = I2C_RESET(priv->i2c); + if (ret < 0) + { + auderr("ERROR: I2C_RESET failed: %d\n", ret); + break; + } + } +#else + auderr("ERROR: I2C_TRANSFER failed: %d\n", ret); +#endif + } + else + { + /* The I2C transfer was successful... break out of the loop and + * return the value read. + */ + + audinfo("Write: %02x <- %04x\n", regaddr, regval); + return; + } + + audinfo("retries=%d regaddr=%02x\n", retries, regaddr); + } +} + +/* Name: wm8994_takesem + * + * Description: + * Take a semaphore count, handling the nasty EINTR return if we are + * interrupted by a signal. + * + */ + +static void wm8994_takesem(sem_t *sem) +{ + int ret; + + do + { + ret = nxsem_wait(sem); + DEBUGASSERT(ret == 0 || ret == -EINTR); + } + while (ret == -EINTR); +} + +/* Name: wm8994_scalevolume + * + * Description: + * Set the right and left volume values in the WM8994 device based + * on the current volume and balance settings. + * + */ + +#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME +static inline uint16_t wm8994_scalevolume(uint16_t volume, b16_t scale) +{ + return b16toi((b16_t)volume * scale); +} +#endif + +/* Name: wm8994_setbitrate + * + * Description: + * Program the FLL to achieve the requested bitrate (fout). Given: + * + * samprate - Samples per second + * nchannels - Number of channels of data + * bpsamp - Bits per sample + * + * Then + * fout = samprate * nchannels * bpsamp + * + * For example: + * samplerate = 11,025 samples/sec + * nchannels = 1 + * bpsamp = 16 bits + * + * Then + * fout = 11025 samples/sec * 1 * 16 bits/sample = 176.4 bits/sec + * + * The clocking is configured like this: + * MCLK is the FLL source clock + * Fref is the scaled down version of MCLK + * Fvco is the output frequency from the FLL + * Fout is the final output from the FLL that drives the SYSCLK + * SYSCLK can be divided down to generate the BCLK + * + * The FLL output frequency is generated at that fout by: + * + * Fout = (Fvco / FLL_OUTDIV) + * + * The FLL operating frequency is set according to: + * + * Fvco = Fref * N.K * FLL_RATIO + * + * Where Fref is the input frequency frequency as determined by + * FLL_CLK_REF_DIV. Fvco must be in the range of 90-100MHz. + * + * As an example: + * FLL_CLK_REF_DIV = 16 + * FLL_OUTDIV = 8 + * N.K = 187.25 + * FLL_RATIO=16 + * Fref =32,768 + * + * Fvco = 32,768 * 187.25 / 16 = 383,488 Hz + * Fout = 383,488 / 8 = 47,936 Hz (approx. 48Khz) + * + */ + +static void wm8994_setbitrate(FAR struct wm8994_dev_s *priv) +{ + DEBUGASSERT(priv && priv->lower); + + /* TODO */ +} + +/* Name: wm8994_getcaps + * + * Description: + * Get the audio device capabilities + * + */ + +static int wm8994_getcaps(FAR struct audio_lowerhalf_s *dev, int type, + FAR struct audio_caps_s *caps) +{ + /* Validate the structure */ + + DEBUGASSERT(caps && caps->ac_len >= sizeof(struct audio_caps_s)); + audinfo("type=%d ac_type=%d\n", type, caps->ac_type); + return 0; +} + +/* Name: wm8994_configure + * + * Description: + * Configure the audio device for the specified mode of operation. + * + */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_configure(FAR struct audio_lowerhalf_s *dev, + FAR void *session, + FAR const struct audio_caps_s *caps) +#else +static int wm8994_configure(FAR struct audio_lowerhalf_s *dev, + FAR const struct audio_caps_s *caps) +#endif +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)dev; + int ret = OK; + + DEBUGASSERT(priv != NULL && caps != NULL); + audinfo("ac_type: %d\n", caps->ac_type); + + /* TODO */ + + return ret; +} + +/* Name: wm8994_shutdown + * + * Description: + * Shutdown the WM8994 chip and put it in the lowest power state possible. + * + */ + +static int wm8994_shutdown(FAR struct audio_lowerhalf_s *dev) +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)dev; + + DEBUGASSERT(priv); + + /* First disable interrupts */ + + WM8994_DISABLE(priv->lower); + + /* Now issue a software reset. This puts all WM8994 registers back in + * their default state. + */ + + wm8994_hw_reset(priv); + return OK; +} + +/* Name: wm8994_senddone + * + * Description: + * This is the I2S callback function that is invoked when the transfer + * completes. + * + */ + +static void wm8994_senddone(FAR struct i2s_dev_s *i2s, + FAR struct ap_buffer_s *apb, FAR void *arg, + int result) +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)arg; + struct audio_msg_s msg; + irqstate_t flags; + int ret; + + DEBUGASSERT(i2s && priv && priv->running && apb); + audinfo("apb=%p inflight=%d result=%d\n", apb, priv->inflight, result); + + /* We do not place any restriction on the context in which this function + * is called. It may be called from an interrupt handler. Therefore, the + * doneq and in-flight values might be accessed from the interrupt level. + * Not the best design. But we will use interrupt controls to protect + * against that possibility. + */ + + flags = enter_critical_section(); + + /* Add the completed buffer to the end of our doneq. We do not yet + * decrement the reference count. + */ + + dq_addlast((FAR dq_entry_t *)apb, &priv->doneq); + + /* And decrement the number of buffers in-flight */ + + DEBUGASSERT(priv->inflight > 0); + priv->inflight--; + + /* Save the result of the transfer */ + + /* REVISIT: This can be overwritten */ + + priv->result = result; + leave_critical_section(flags); + + /* Now send a message to the worker thread, informing it that there are + * buffers in the done queue that need to be cleaned up. + */ + + msg.msg_id = AUDIO_MSG_COMPLETE; + ret = nxmq_send(priv->mq, (FAR const char *)&msg, sizeof(msg), + CONFIG_WM8994_MSG_PRIO); + if (ret < 0) + { + auderr("ERROR: nxmq_send failed: %d\n", ret); + } +} + +/* Name: wm8994_returnbuffers + * + * Description: + * This function is called after the complete of one or more data + * transfers. This function will empty the done queue and release our + * reference to each buffer. + * + */ + +static void wm8994_returnbuffers(FAR struct wm8994_dev_s *priv) +{ + FAR struct ap_buffer_s *apb; + irqstate_t flags; + + /* The doneq and in-flight values might be accessed from the interrupt + * level in some implementations. Not the best design. But we will + * use interrupt controls to protect against that possibility. + */ + + flags = enter_critical_section(); + while (dq_peek(&priv->doneq) != NULL) + { + /* Take the next buffer from the queue of completed transfers */ + + apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->doneq); + leave_critical_section(flags); + + audinfo("Returning: apb=%p curbyte=%d nbytes=%d flags=%04x\n", + apb, apb->curbyte, apb->nbytes, apb->flags); + + /* Are we returning the final buffer in the stream? */ + + if ((apb->flags & AUDIO_APB_FINAL) != 0) + { + /* Both the pending and the done queues should be empty and there + * should be no buffers in-flight. + */ + + DEBUGASSERT(dq_empty(&priv->doneq) && dq_empty(&priv->pendq) && + priv->inflight == 0); + + /* Set the terminating flag. This will, eventually, cause the + * worker thread to exit (if it is not already terminating). + */ + + audinfo("Terminating\n"); + priv->terminating = true; + } + + /* Release our reference to the audio buffer */ + + apb_free(apb); + + /* Send the buffer back up to the previous level. */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION + priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL); +#else + priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK); +#endif + flags = enter_critical_section(); + } + + leave_critical_section(flags); +} + +/* Name: wm8994_sendbuffer + * + * Description: + * Start the transfer an audio buffer to the WM8994 via I2S. This + * will not wait for the transfer to complete but will return immediately. + * the wmd8994_senddone called will be invoked when the transfer + * completes, stimulating the worker thread to call this function again. + * + */ + +static int wm8994_sendbuffer(FAR struct wm8994_dev_s *priv) +{ + FAR struct ap_buffer_s *apb; + irqstate_t flags; + uint32_t timeout; + int shift; + int ret = OK; + + /* Loop while there are audio buffers to be sent and we have few than + * CONFIG_WM8994_INFLIGHT then "in-flight" + * + * The 'inflight' value might be modified from the interrupt level in some + * implementations. We will use interrupt controls to protect against + * that possibility. + * + * The 'pendq', on the other hand, is protected via a semaphore. Let's + * hold the semaphore while we are busy here and disable the interrupts + * only while accessing 'inflight'. + */ + + wm8994_takesem(&priv->pendsem); + while (priv->inflight < CONFIG_WM8994_INFLIGHT && + dq_peek(&priv->pendq) != NULL && !priv->paused) + { + /* Take next buffer from the queue of pending transfers */ + + apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq); + audinfo("Sending apb=%p, size=%d inflight=%d\n", + apb, apb->nbytes, priv->inflight); + + /* Increment the number of buffers in-flight before sending in order + * to avoid a possible race condition. + */ + + flags = enter_critical_section(); + priv->inflight++; + leave_critical_section(flags); + + /* Send the entire audio buffer via I2S. What is a reasonable timeout + * to use? This would depend on the bit rate and size of the buffer. + * + * Samples in the buffer (samples): + * = buffer_size * 8 / bpsamp samples + * Sample rate (samples/second): + * = samplerate * nchannels + * Expected transfer time (seconds): + * = (buffer_size * 8) / bpsamp / samplerate / nchannels + * + * We will set the timeout about twice that. + * + * NOTES: + * - The multiplier of 8 becomes 16000 for 2x and units of + * milliseconds. + * - 16000 is a approximately 16384 (1 << 14), bpsamp is either + * (1 << 3) or (1 << 4), and nchannels is either (1 << 0) or + * (1 << 1). So this can be simplifies to (milliseconds): + * + * = (buffer_size << shift) / samplerate + */ + + shift = (priv->bpsamp == 8) ? 14 - 3 : 14 - 4; + shift -= (priv->nchannels > 1) ? 1 : 0; + + timeout = MSEC2TICK(((uint32_t)(apb->nbytes - apb->curbyte) << shift) / + (uint32_t)priv->samprate); + + ret = I2S_SEND(priv->i2s, apb, wm8994_senddone, priv, timeout); + if (ret < 0) + { + auderr("ERROR: I2S_SEND failed: %d\n", ret); + break; + } + } + + wm8994_givesem(&priv->pendsem); + return ret; +} + +/* Name: wm8994_start + * + * Description: + * Start the configured operation (audio streaming, volume enabled, etc.). + * + */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_start(FAR struct audio_lowerhalf_s *dev, FAR void *session) +#else +static int wm8994_start(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)dev; + struct sched_param sparam; + struct mq_attr attr; + pthread_attr_t tattr; + FAR void *value; + int ret; + + audinfo("Entry\n"); + + /* Exit reduced power modes of operation */ + + /* REVISIT */ + + /* Create a message queue for the worker thread */ + + snprintf(priv->mqname, sizeof(priv->mqname), "/tmp/%p", priv); + + attr.mq_maxmsg = 16; + attr.mq_msgsize = sizeof(struct audio_msg_s); + attr.mq_curmsgs = 0; + attr.mq_flags = 0; + + priv->mq = mq_open(priv->mqname, O_RDWR | O_CREAT, 0644, &attr); + if (priv->mq == NULL) + { + /* Error creating message queue! */ + + auderr("ERROR: Couldn't allocate message queue\n"); + return -ENOMEM; + } + + /* Join any old worker thread we had created to prevent a memory leak */ + + if (priv->threadid != 0) + { + audinfo("Joining old thread\n"); + pthread_join(priv->threadid, &value); + } + + /* Start our thread for sending data to the device */ + + pthread_attr_init(&tattr); + sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 3; + (void)pthread_attr_setschedparam(&tattr, &sparam); + (void)pthread_attr_setstacksize(&tattr, CONFIG_WM8994_WORKER_STACKSIZE); + + audinfo("Starting worker thread\n"); + ret = pthread_create(&priv->threadid, &tattr, wm8994_workerthread, + (pthread_addr_t)priv); + if (ret != OK) + { + auderr("ERROR: pthread_create failed: %d\n", ret); + } + else + { + pthread_setname_np(priv->threadid, "wm8994"); + audinfo("Created worker thread\n"); + } + + return ret; +} + +/* Name: wm8994_stop + * + * Description: Stop the configured operation (audio streaming, volume + * disabled, etc.). + * + */ + +#ifndef CONFIG_AUDIO_EXCLUDE_STOP +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session) +#else +static int wm8994_stop(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)dev; + struct audio_msg_s term_msg; + FAR void *value; + + /* Send a message to stop all audio streaming */ + + term_msg.msg_id = AUDIO_MSG_STOP; + term_msg.u.data = 0; + (void)nxmq_send(priv->mq, (FAR const char *)&term_msg, sizeof(term_msg), + CONFIG_WM8994_MSG_PRIO); + + /* Join the worker thread */ + + pthread_join(priv->threadid, &value); + priv->threadid = 0; + + /* Enter into a reduced power usage mode */ + + /* REVISIT: */ + + return OK; +} +#endif + +/* Name: wm8994_pause + * + * Description: Pauses the playback. + * + */ + +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session) +#else +static int wm8994_pause(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)dev; + + if (priv->running && !priv->paused) + { + /* Disable interrupts to prevent us from suppling any more data */ + + priv->paused = true; + WM8994_DISABLE(priv->lower); + } + + return OK; +} +#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */ + +/* Name: wm8994_resume + * + * Description: Resumes the playback. + * + */ + +#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_resume(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int wm8994_resume(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)dev; + + if (priv->running && priv->paused) + { + priv->paused = false; + + /* Enable interrupts to allow sampling data */ + + wm8994_sendbuffer(priv); +#ifdef WM8994_USE_FFLOCK_INT + WM8994_ENABLE(priv->lower); +#endif + } + + return OK; +} +#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */ + +/* Name: wm8994_enqueuebuffer + * + * Description: Enqueue an Audio Pipeline Buffer for playback/ processing. + * + */ + +static int wm8994_enqueuebuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb) +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)dev; + struct audio_msg_s term_msg; + int ret; + + audinfo("Enqueueing: apb=%p curbyte=%d nbytes=%d flags=%04x\n", + apb, apb->curbyte, apb->nbytes, apb->flags); + + /* Take a reference on the new audio buffer */ + + apb_reference(apb); + + /* Add the new buffer to the tail of pending audio buffers */ + + wm8994_takesem(&priv->pendsem); + apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED; + dq_addlast(&apb->dq_entry, &priv->pendq); + wm8994_givesem(&priv->pendsem); + + /* Send a message to the worker thread indicating that a new buffer has been + * enqueued. If mq is NULL, then the playing has not yet started. In that + * case we are just "priming the pump" and we don't need to send any message. + */ + + ret = OK; + if (priv->mq != NULL) + { + term_msg.msg_id = AUDIO_MSG_ENQUEUE; + term_msg.u.data = 0; + + ret = nxmq_send(priv->mq, (FAR const char *)&term_msg, + sizeof(term_msg), CONFIG_WM8994_MSG_PRIO); + if (ret < 0) + { + auderr("ERROR: nxmq_send failed: %d\n", ret); + } + } + + return ret; +} + +/** Name: wm8994_cancelbuffer + * + * Description: Called when an enqueued buffer is being cancelled. + * + */ + +static int wm8994_cancelbuffer(FAR struct audio_lowerhalf_s *dev, + FAR struct ap_buffer_s *apb) +{ + audinfo("apb=%p\n", apb); + return OK; +} + +/* Name: wm8994_ioctl + * + * Description: Perform a device ioctl + * + */ + +static int wm8994_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ +#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS + FAR struct ap_buffer_info_s *bufinfo; +#endif + + /* Deal with ioctls passed from the upper-half driver */ + + switch (cmd) + { + /* Check for AUDIOIOC_HWRESET ioctl. This ioctl is passed straight + * through from the upper-half audio driver. + */ + + case AUDIOIOC_HWRESET: + { + /* REVISIT: Should we completely re-initialize the chip? We + * can't just issue a software reset; that would puts all WM8994 + * registers back in their default state. + */ + + audinfo("AUDIOIOC_HWRESET:\n"); + } + break; + + /* Report our preferred buffer size and quantity */ + +#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS + case AUDIOIOC_GETBUFFERINFO: + { + audinfo("AUDIOIOC_GETBUFFERINFO:\n"); + bufinfo = (FAR struct ap_buffer_info_s *) arg; + bufinfo->buffer_size = CONFIG_WM8994_BUFFER_SIZE; + bufinfo->nbuffers = CONFIG_WM8994_NUM_BUFFERS; + } + break; +#endif + + default: + audinfo("Ignored\n"); + break; + } + + return OK; +} + +/* Name: wm8994_reserve + * + * Description: Reserves a session (the only one we have). + * + */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_reserve(FAR struct audio_lowerhalf_s *dev, + FAR void **session) +#else +static int wm8994_reserve(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *) dev; + int ret = OK; + + /* Borrow the APBQ semaphore for thread sync */ + + wm8994_takesem(&priv->pendsem); + if (priv->reserved) + { + ret = -EBUSY; + } + else + { + /* Initialize the session context */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION + *session = NULL; +#endif + priv->inflight = 0; + priv->running = false; + priv->paused = false; +#ifndef CONFIG_AUDIO_EXCLUDE_STOP + priv->terminating = false; +#endif + priv->reserved = true; + } + + wm8994_givesem(&priv->pendsem); + + return ret; +} + +/* Name: wm8994_release + * + * Description: Releases the session (the only one we have). + * + */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION +static int wm8994_release(FAR struct audio_lowerhalf_s *dev, + FAR void *session) +#else +static int wm8994_release(FAR struct audio_lowerhalf_s *dev) +#endif +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)dev; + void *value; + + /* Join any old worker thread we had created to prevent a memory leak */ + + if (priv->threadid != 0) + { + pthread_join(priv->threadid, &value); + priv->threadid = 0; + } + + /* Borrow the APBQ semaphore for thread sync */ + + wm8994_takesem(&priv->pendsem); + + /* Really we should free any queued buffers here */ + + priv->reserved = false; + wm8994_givesem(&priv->pendsem); + + return OK; +} + +/* Name: wm8994_interrupt_work + * + * Description: + * WM8994 interrupt actions cannot be performed in the interrupt handler + * because I2C access is not possible in that context. Instead, all I2C + * operations are deferred to the work queue. + * + * Assumptions: + * WM8994 interrupts were disabled in the interrupt handler. + * + */ + +#ifdef WM8994_USE_FFLOCK_INT +static void wm8994_interrupt_work(FAR void *arg) +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)arg; + uint16_t regval; + + DEBUGASSERT(priv && priv->lower); + + /* Sample the interrupt status */ + + /* regval = wm8994_readreg(priv, WM8994_INT_STATUS); */ + + audinfo("INT_STATUS: %04x\n", regval); + + /* Check for the FLL lock interrupt. We are sloppy here since at + * present, only the FLL lock interrupt is used. + */ + + DEBUGASSERT((regval & WM8994_FLL_LOCK_INT) != 0 && !priv->locked); + UNUSED(regval); + + priv->locked = true; + + /* Clear all pending interrupts by write 1's to the interrupt status + * register. + * + * REVISIT: Since I2C is slow and not atomic with respect to WM8994 event, + * could this not cause the lost of interrupts? + */ + + /* wm8994_writereg(priv, WM8994_INT_STATUS, WM8994_ALL_INTS); */ + + /* Disable further FLL lock interrupts. We are sloppy here since at + * present, only the FLL lock interrupt is used. + */ + + /* wm8994_writereg(priv, WM8994_INT_MASK, WM8994_ALL_INTS); */ + +#ifdef WM8994_USE_FFLOCK_INT + /* Re-enable WM8994 interrupts */ + + WM8994_ENABLE(priv->lower); +#endif +} +#endif + +/* Name: wm8994_interrupt + * + * Description: + * This is the ISR that services the GPIO1/IRQ pin from the WM8994. It + * signals WM8994 events such FLL lock. + * + */ + +#ifdef WM8994_USE_FFLOCK_INT +static int wm8994_interrupt(FAR const struct wm8994_lower_s *lower, + FAR void *arg) +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)arg; + int ret; + + DEBUGASSERT(lower && priv); + + /* Disable further interrupts and perform all interrupt related activities + * on the work thread. There is nothing that we can do from the interrupt + * handler because we cannot perform I2C operations here. + */ + + WM8994_DISABLE(priv->lower); + + DEBUGASSERT(work_available(&priv->work)); + ret = work_queue(LPWORK, &priv->work, wm8994_interrupt_work, priv, 0); + if (ret < 0) + { + auderr("ERROR: Failed to schedule work\n"); + } + + return OK; +} +#endif + +/* Name: wm8994_workerthread + * + * This is the thread that feeds data to the chip and keeps the audio + * stream going. + * + */ + +static void *wm8994_workerthread(pthread_addr_t pvarg) +{ + FAR struct wm8994_dev_s *priv = (struct wm8994_dev_s *) pvarg; + struct audio_msg_s msg; + FAR struct ap_buffer_s *apb; + int msglen; + unsigned int prio; + + audinfo("Entry\n"); + +#ifndef CONFIG_AUDIO_EXCLUDE_STOP + priv->terminating = false; +#endif + + /* Mark ourself as running and make sure that WM8994 interrupts are + * enabled. + */ + + priv->running = true; +#ifdef WM8994_USE_FFLOCK_INT + WM8994_ENABLE(priv->lower); +#endif + + /* Loop as long as we are supposed to be running and as long as we have + * buffers in-flight. + */ + + while (priv->running || priv->inflight > 0) + { + /* Check if we have been asked to terminate. e have to check if we + * still have buffers in-flight. If we do, then we can't stop until + * birds come back to roost. + */ + + if (priv->terminating && priv->inflight <= 0) + { + /* We are IDLE. Break out of the loop and exit. */ + + break; + } + else + { + /* Check if we can send more audio buffers to the WM8994 */ + + wm8994_sendbuffer(priv); + } + + /* Wait for messages from our message queue */ + + msglen = nxmq_receive(priv->mq, (FAR char *)&msg, sizeof(msg), &prio); + + /* Handle the case when we return with no message */ + + if (msglen < sizeof(struct audio_msg_s)) + { + auderr("ERROR: Message too small: %d\n", msglen); + continue; + } + + /* Process the message */ + + switch (msg.msg_id) + { + /* The ISR has requested more data. We will catch this case at + * the top of the loop. + */ + + case AUDIO_MSG_DATA_REQUEST: + audinfo("AUDIO_MSG_DATA_REQUEST\n"); + break; + + /* Stop the playback */ + +#ifndef CONFIG_AUDIO_EXCLUDE_STOP + case AUDIO_MSG_STOP: + + /* Indicate that we are terminating */ + + audinfo("AUDIO_MSG_STOP: Terminating\n"); + priv->terminating = true; + break; +#endif + + /* We have a new buffer to send. We will catch this case at + * the top of the loop. + */ + + case AUDIO_MSG_ENQUEUE: + audinfo("AUDIO_MSG_ENQUEUE\n"); + break; + + /* We will wake up from the I2S callback with this message */ + + case AUDIO_MSG_COMPLETE: + audinfo("AUDIO_MSG_COMPLETE\n"); + wm8994_returnbuffers(priv); + break; + + default: + auderr("ERROR: Ignoring message ID %d\n", msg.msg_id); + break; + } + } + + /* Reset the WM8994 hardware */ + + wm8994_hw_reset(priv); + + /* Return any pending buffers in our pending queue */ + + wm8994_takesem(&priv->pendsem); + while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL) + { + /* Release our reference to the buffer */ + + apb_free(apb); + + /* Send the buffer back up to the previous level. */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION + priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL); +#else + priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK); +#endif + } + + wm8994_givesem(&priv->pendsem); + + /* Return any pending buffers in our done queue */ + + wm8994_returnbuffers(priv); + + /* Close the message queue */ + + mq_close(priv->mq); + mq_unlink(priv->mqname); + priv->mq = NULL; + + /* Send an AUDIO_MSG_COMPLETE message to the client */ + +#ifdef CONFIG_AUDIO_MULTI_SESSION + priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL); +#else + priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK); +#endif + + audinfo("Exit\n"); + return NULL; +} + +/* Name: wm8994_audio_output + * + * Description: + * Initialize and configure the WM8994 device as an audio output device. + * + * Input Parameters: + * priv - A reference to the driver state structure + * + * Returned Value: + * None. No failures are detected. + * + */ + +static void wm8994_audio_output(FAR struct wm8994_dev_s *priv) +{ + /* TODO */ +} + +/* Name: wm8994_audio_input + * + * Description: + * Initialize and configure the WM8994 device as an audio input device. + * + * Input Parameters: + * priv - A reference to the driver state structure + * + * Returned Value: + * None. No failures are detected. + * + */ + +static void wm8994_audio_input(FAR struct wm8994_dev_s *priv) +{ +} + +/* Name: wm8994_audio_input + * + * Description: + * Initialize and configure the WM8994 device as an audio output device + * (Right input only). wm8994_audio_output() must be called first, this + * function then modifies the configuration to support audio input. + * + * Input Parameters: + * priv - A reference to the driver state structure + * + * Returned Value: + * None. No failures are detected. + * + */ + +#if 0 /* Not used */ +static void wm8994_audio_input(FAR struct wm8994_dev_s *priv) +{ + /* Analogue Left Input 0 */ + + wm8994_writereg(priv, WM8994_ANA_LEFT_IN0, WM8994_INMUTE); + + /* Analogue Right Input 0 */ + + wm8994_writereg(priv, WM8994_ANA_RIGHT_IN0, WM8994_IN_VOL(5)); + + /* Analogue Left Input 1 */ + + wm8994_writereg(priv, WM8994_ANA_LEFT_IN1, 0); + + /* Analogue Right Input 1 */ + + wm8994_writereg(priv, WM8994_ANA_RIGHT_IN1, WM8994_IP_SEL_N_IN2L); +} +#endif + +/* Name: wm8994_configure_ints + * + * Description: + * Configure the GPIO/IRQ interrupt + * + * Input Parameters: + * priv - A reference to the driver state structure + * + * Returned Value: + * None + * + */ + +#ifdef WM8994_USE_FFLOCK_INT +static void wm8994_configure_ints(FAR struct wm8994_dev_s *priv) +{ + uint16_t regval; + + /* Configure GPIO1 as an IRQ + * + * WM8994_GPIO1_PU=0 : No pull-up + * WM8994_GPIO1_PD=1 : Pulled-down + * WM8994_GPIO1_SEL_IRQ : Configured as IRQ + */ + + /* Attach our handler to the GPIO1/IRQ interrupt */ + + WM8994_ATTACH(lower, wm8994_interrupt, priv); + + /* Configure interrupts. wm8994_setbitrate() depends on FLL interrupts. */ +} +#endif + +/* Name: wm8994_hw_reset + * + * Description: + * Reset and re-initialize the WM8994 + * + * Input Parameters: + * priv - A reference to the driver state structure + * + * Returned Value: + * None + * + */ + +static void wm8994_hw_reset(FAR struct wm8994_dev_s *priv) +{ + /* Put audio output back to its initial configuration */ + + priv->samprate = WM8994_DEFAULT_SAMPRATE; + priv->nchannels = WM8994_DEFAULT_NCHANNELS; + priv->bpsamp = WM8994_DEFAULT_BPSAMP; +#if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) && !defined(CONFIG_AUDIO_EXCLUDE_BALANCE) + priv->balance = b16HALF; /* Center balance */ +#endif + + /* Software reset. This puts all WM8994 registers back in their + * default state. + */ + + /* 0x0000 = 0 */ + + wm8994_writereg(priv, WM8994_SWRST, 0); + + /* wm8994 Errata Work-Arounds */ + + /* copy code from STM32Cube_FW_F7_V1.15.0 */ + + wm8994_writereg(priv, 0x102, 0x0003); + wm8994_writereg(priv, 0x817, 0x0000); + wm8994_writereg(priv, 0x102, 0x0000); + uint16_t regval; + + /* regval=0x006c */ + + regval = WM8994_VMID_RAMP_SOFT_FAST_START | WM8994_VMID_BUF_ENA + | WM8994_STARTUP_BIAS_ENA; + + /* 0x39 = 0x006c */ + + wm8994_writereg(priv, WM8994_ANTI_POP2, regval); + + if (WM8994_DEFAULT_INPUT_DEVICE > 0) + regval = 0x0013; + else + regval = 0x0003; + + /* 0x01 = 0x0013 */ + + wm8994_writereg(priv, WM8994_PM1, regval); + up_mdelay(50); + + /* Configure the WM8994 hardware as an audio output device */ + + wm8994_audio_output(priv); + { + switch (WM8994_DEFAULT_OUTPUT_DEVICE) + { + case WM8994_OUTPUT_DEVICE_SPEAKER: + + /* regval = 0x0c0c */ + + /* regval = WM8994_AIF1_DAC2L_ENA | WM8994_AIF1_DAC2R_ENA + * | WM8994_DAC2L_ENA | WM8994_DAC2R_ENA; + */ + + regval = 0x0c0c; + wm8994_writereg(priv, WM8994_PM5, regval); /* 0x05 */ + + /* regval = 0x0000 */ + + regval = 0; + + /* 0x601 = 0x0000 */ + + wm8994_writereg(priv, WM8994_DAC1_LEFT_MIXER_ROUTING, regval); + + regval = 0; /* regval = 0x0000 */ + + /* 0x602 = 0x0000 */ + + wm8994_writereg(priv, WM8994_DAC1_RIGHT_MIXER_ROUTING, regval); + + regval = WM8994_AIF1DAC2L_TO_DAC2L_ENA; /* regval = 0x0002 */ + + /* 0x604=0x0002 */ + + wm8994_writereg(priv, WM8994_DAC2_LEFT_MIXER_ROUTING, regval); + + regval = WM8994_AIF1DAC2R_TO_DAC2R_ENA; /* regval = 0x0002 */ + + /* 0x605=0x0002 */ + + wm8994_writereg(priv, WM8994_DAC2_RIGHT_MIXER_ROUTING, regval); + break; + case WM8994_OUTPUT_DEVICE_HEADPHONE: + /* regval = WM8994_AIF1_DAC1L_ENA | WM8994_AIF1_DAC1R_ENA + * | WM8994_DAC1L_ENA | WM8994_DAC1R_ENA; + */ + + regval = 0x0303; + wm8994_writereg(priv, WM8994_PM5, regval); /* 0x05 = 0x0303 */ + + regval = WM8994_AIF1DAC1L_TO_DAC1L_ENA; + wm8994_writereg(priv, WM8994_DAC1_LEFT_MIXER_ROUTING, regval); /* 0x601=0x0001 */ + + regval = WM8994_AIF1DAC1R_TO_DAC1R_ENA; + wm8994_writereg(priv, WM8994_DAC1_RIGHT_MIXER_ROUTING, regval); /* 0x602=0x0001 */ + + regval = 0; + wm8994_writereg(priv, WM8994_DAC2_LEFT_MIXER_ROUTING, regval); /* 0x604=0x0000 */ + + regval = 0; + + /* 0x605=0x0000 */ + + wm8994_writereg(priv, WM8994_DAC2_RIGHT_MIXER_ROUTING, regval); + break; + case WM8994_OUTPUT_DEVICE_BOTH: + if (WM8994_DEFAULT_INPUT_DEVICE == WM8994_INPUT_DEVICE_DIGITAL_MIC1_MIC2) + { + wm8994_writereg(priv, 0x005, 0x0303 | 0x0c0c); + wm8994_writereg(priv, 0x601, 0x0003); + wm8994_writereg(priv, 0x602, 0x0003); + wm8994_writereg(priv, 0x604, 0x0003); + wm8994_writereg(priv, 0x605, 0x0003); + } + else + { + wm8994_writereg(priv, 0x005, 0x0303 | 0x0c0c); + wm8994_writereg(priv, 0x601, 0x0001); + wm8994_writereg(priv, 0x602, 0x0001); + wm8994_writereg(priv, 0x604, 0x0002); + wm8994_writereg(priv, 0x605, 0x0002); + } + + break; + case WM8994_OUTPUT_DEVICE_AUTO: + wm8994_writereg(priv, 0x005, 0x0303); + wm8994_writereg(priv, 0x601, 0x0001); + wm8994_writereg(priv, 0x602, 0x0001); + wm8994_writereg(priv, 0x604, 0x0000); + wm8994_writereg(priv, 0x605, 0x0000); + break; + default: + break; + } + } + + /* Configure the WM8994 hardware as an audio input device */ + + wm8994_audio_input(priv); + switch (WM8994_DEFAULT_INPUT_DEVICE) + { + case WM8994_INPUT_DEVICE_DIGITAL_MICROPHONE_2: + wm8994_writereg(priv, 0x004, 0x0c30); + wm8994_writereg(priv, 0x450, 0x00db); + wm8994_writereg(priv, 0x002, 0x6000); + wm8994_writereg(priv, 0x608, 0x0002); + wm8994_writereg(priv, 0x700, 0x000b); + break; + case WM8994_INPUT_DEVICE_INPUT_LINE_1: + wm8994_writereg(priv, 0x028, 0x0011); + wm8994_writereg(priv, 0x029, 0x0035); + wm8994_writereg(priv, 0x02a, 0x0035); + wm8994_writereg(priv, 0x004, 0x0303); + wm8994_writereg(priv, 0x440, 0x00db); + wm8994_writereg(priv, 0x002, 0x6350); + wm8994_writereg(priv, 0x606, 0x0002); + wm8994_writereg(priv, 0x607, 0x0002); + wm8994_writereg(priv, 0x700, 0x000d); + break; + case WM8994_INPUT_DEVICE_DIGITAL_MICROPHONE_1: + wm8994_writereg(priv, 0x004, 0x030c); + wm8994_writereg(priv, 0x440, 0x00db); + wm8994_writereg(priv, 0x002, 0x6350); + wm8994_writereg(priv, 0x606, 0x0002); + wm8994_writereg(priv, 0x607, 0x0002); + wm8994_writereg(priv, 0x700, 0x000d); + break; + case WM8994_INPUT_DEVICE_DIGITAL_MIC1_MIC2: + wm8994_writereg(priv, 0x004, 0x0f3c); + wm8994_writereg(priv, 0x450, 0x00db); + wm8994_writereg(priv, 0x440, 0x00db); + wm8994_writereg(priv, 0x002, 0x63a0); + wm8994_writereg(priv, 0x606, 0x0002); + wm8994_writereg(priv, 0x607, 0x0002); + wm8994_writereg(priv, 0x608, 0x0002); + wm8994_writereg(priv, 0x609, 0x0002); + wm8994_writereg(priv, 0x700, 0x000d); + break; + case WM8994_INPUT_DEVICE_INPUT_LINE_2: + default: + break; + } + + { + switch (WM8994_DEFAULT_SAMPRATE) + { + case WM8994_AUDIO_FREQUENCY_8K: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_8K; + + /* 0x210 = 0x0003 */ + + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); + break; + case WM8994_AUDIO_FREQUENCY_16K: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_16K; + + /* 0x210 = 0x0033 */ + + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); + break; + case WM8994_AUDIO_FREQUENCY_22_050K: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_22K; + + /* 0x210 = 0x0063 */ + + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); + break; + #if 0 + case WM8994_AUDIO_FREQUENCY_48K: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_24K; + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); /* 0x210 = 0x0083 */ + break; + #endif + case WM8994_AUDIO_FREQUENCY_32K: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_32K; + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); /* 0x210 = 0x00a3 */ + break; + case WM8994_AUDIO_FREQUENCY_44_100K: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_44K; + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); /* 0x210 = 0x0013 */ + break; + case WM8994_AUDIO_FREQUENCY_48K: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_48K; + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); /* 0x210 = 0x0043 */ + break; + #if 0 + case WM8994_AUDIO_FREQUENCY_44_100K: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_88K; + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); /* 0x210 = 0x0073 */ + break; + #endif + case WM8994_AUDIO_FREQUENCY_96K: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_96K; + + /* 0x210 = 0x00a3 */ + + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); + break; + default: + regval = WM8994_AIF1CLK_RATE_2 | WM8994_AIF1_SR_48K; + + /* 0x210 = 0x0083 */ + + wm8994_writereg(priv, WM8994_AIF1_RATE, regval); + break; + } + + if (WM8994_DEFAULT_INPUT_DEVICE == WM8994_INPUT_DEVICE_DIGITAL_MIC1_MIC2) + + /* regval = 0x4018 */ + + regval = WM8994_AIF1ADCR_RIGHT_ADC | WM8994_AIF1_WL_16BITS + | WM8994_AIF1_FMT_I2S; + else + + /* regval = 0x4010 */ + + regval = WM8994_AIF1ADCR_RIGHT_ADC | WM8994_AIF1_WL_16BITS + | WM8994_AIF1_FMT_DSP; + + /* 0x300 = */ + + wm8994_writereg(priv, WM8994_AIF1_CTL1, regval); + + regval = WM8994_AIF1_TRI_NORMAL | WM8994_AIF1_MSTR_SLAVE_MODE + | WM8994_AIF1_CLK_FRC_NORMAL | WM8994_AIF1_LRCLK_FRC_NORMAL; + + /* 0x302 = 0x0000 */ + + wm8994_writereg(priv, WM8994_AIF1_MASTER_SLAVE, regval); + + regval = WM8994_AIF1DSPCLK_ENA | WM8994_SYSDSPCLK_ENA + | WM8994_SYSCLK_SRC_AIF1CLK; + + /* 0x208 = 0x000a */ + + wm8994_writereg(priv, WM8994_CLK1, regval); + + regval = WM8994_AIF1CLK_ENA; + + /* 0x200 = 0x0001 */ + + wm8994_writereg(priv, WM8994_AIF1_CLK1, regval); + + if (WM8994_DEFAULT_OUTPUT_DEVICE == WM8994_OUTPUT_DEVICE_HEADPHONE) + { + regval = WM8994_DAC1L_TO_HPOUT1L_DAC1L; + + /* 0x2d = 0x0100 */ + + wm8994_writereg(priv, WM8994_OUTPUT_MIXER1, regval); + + regval = 0; + wm8994_writereg(priv, WM8994_OUTPUT_MIXER2, regval); /* 0x2e = 0x0100 */ + + if (WM8994_STARTUP_MODE_COLD) + { + regval = 0x8100; + wm8994_writereg(priv, WM8994_WR_CTL_SEQ1, regval); + + /* 0x110 = regval */ + + up_mdelay(300); + } + else + { + regval = 0x8108; + wm8994_writereg(priv, WM8994_WR_CTL_SEQ1, regval); /* 0x110 = regval */ + up_mdelay(50); + } + + regval = 0; + wm8994_writereg(priv, WM8994_AIF1_DAC1_FILTERS1, regval); /* 0x420 = 0x0000 */ + } + + regval = 0; + wm8994_writereg(priv, WM8994_PM3, regval); /* 0x03 = 0x0300 */ + + regval = 0; + wm8994_writereg(priv, WM8994_SPKMIXL_ATT, regval); /* 0x22 = 0x0000 */ + + regval = 0; + wm8994_writereg(priv, WM8994_SPKMIXR_ATT, regval); /* 0x23 = 0x0000 */ + + regval = 0; + wm8994_writereg(priv, WM8994_SPEAKER_MIXER, regval); /* 0x36 = 0x0300 */ + + regval = 0; + wm8994_writereg(priv, WM8994_PM1, regval); /* 0x01 = 0x3003 */ + + if (WM8994_DEFAULT_INPUT_DEVICE == WM8994_INPUT_DEVICE_DIGITAL_MIC1_MIC2) + regval = 0x0205; + else + regval = 0x0005; + wm8994_writereg(priv, WM8994_CLASS_W_1, regval); /* 0x51 = regval */ + + priv->power_mgnt_reg_1 |= 0x0303 | 0x3003; + regval = priv->power_mgnt_reg_1; + wm8994_writereg(priv, WM8994_PM1, regval); /* 0x01 = power_mgnt_reg_1 */ + + regval = 0; + wm8994_writereg(priv, WM8994_ANA_HP1, regval); /* 0x60 = 0x0022 */ + + regval = 0; + wm8994_writereg(priv, WM8994_CHARGE_PUMP1, regval); /* 0x4c = 0x9F25 */ + + up_mdelay(15); + + regval = 0; + wm8994_writereg(priv, WM8994_OUTPUT_MIXER1, regval); /* 0x2d = 0x0001 */ + + regval = 0; + wm8994_writereg(priv, 0x2e, regval); /* 0x2e = 0x0001 */ + + regval = 0; + wm8994_writereg(priv, 0x03, regval); /* 0x03 = 0x0030 | 0x0300 */ + + regval = 0x0033; + wm8994_writereg(priv, 0x54, regval); /* 0x54 = 0x0033 */ + + up_mdelay(257); + + regval = 0x00ee; + wm8994_writereg(priv, 0x60, 0x00ee); /* 0x60 = 0x00ee */ + + regval = 0x00c0; + wm8994_writereg(priv, 0x610, regval); /* 0x610 = 0x00c0 */ + + regval = 0x00c0; + wm8994_writereg(priv, 0x611, regval); /* 0x611 = 0x00c0 */ + + regval = 0x0010; + wm8994_writereg(priv, 0x420, regval); /* 0x420 = 0x0010 */ + + regval = 0x00c0; + wm8994_writereg(priv, 0x612, regval); /* 0x612 = 0x00c0 */ + + regval = 0x00c0; + wm8994_writereg(priv, 0x613, regval); /* 0x613 = 0x00c0 */ + + regval = 0x0010; + wm8994_writereg(priv, 0x422, regval); /* 0x422 = 0x0010 */ + + if ((WM8994_DEFAULT_INPUT_DEVICE == + WM8994_INPUT_DEVICE_DIGITAL_MICROPHONE_1) + || (WM8994_DEFAULT_INPUT_DEVICE == + WM8994_INPUT_DEVICE_DIGITAL_MICROPHONE_2)) + { + priv->power_mgnt_reg_1 |= 0x0013; + wm8994_writereg(priv, 0x01, priv->power_mgnt_reg_1); /* 0x01 = power_mgnt_reg_1 */ + + regval = 0x0002; + wm8994_writereg(priv, 0x620, 0x0002); /* 0x620 = 0x0002 */ + + regval = 0x3800; + wm8994_writereg(priv, 0x411, 0x3800); /* 0x411 = 0x3800 */ + } + else if (WM8994_DEFAULT_INPUT_DEVICE == + WM8994_INPUT_DEVICE_DIGITAL_MIC1_MIC2) + { + priv->power_mgnt_reg_1 |= 0x0013; + wm8994_writereg(priv, 0x01, priv->power_mgnt_reg_1); /* 0x01 = power_mgnt_reg_1 */ + + regval = 0x0002; + wm8994_writereg(priv, 0x620, regval); /* 0x620 = 0x0002; */ + + regval = 0x1800; + wm8994_writereg(priv, 0x410, regval); /* 0x410 = 0x1800 */ + + regval = 0x1800; + wm8994_writereg(priv, 0x411, regval); /* 0x411 = 0x1800 */ + } + else if ((WM8994_DEFAULT_INPUT_DEVICE == + WM8994_INPUT_DEVICE_INPUT_LINE_1) + || (WM8994_DEFAULT_INPUT_DEVICE == + WM8994_INPUT_DEVICE_INPUT_LINE_2)) + + { + regval = 0x000b; + wm8994_writereg(priv, 0x18, regval); /* 0x18 = 0x000b */ + + regval = 0x000b; + wm8994_writereg(priv, 0x1a, regval); /* 0x1A = 0x000B */ + + regval = 0x1800; + wm8994_writereg(priv, 0x410, regval); /* 0x410 = 0x1800 */ + } + } + + /* Configure interrupts */ + + wm8994_configure_ints(priv); + + /* Configure the FLL and the LRCLK */ + + wm8994_setbitrate(priv); + + /* Dump some information and return the device instance */ + + wm8994_dump_registers(&priv->dev, "After configuration"); + wm8994_clock_analysis(&priv->dev, "After configuration"); +} + +/*********************************************************************************** + * Public Functions + ***********************************************************************************/ + +/* Name: wm8994_initialize + * + * Description: + * Initialize the WM8994 device. + * + * Input Parameters: + * i2c - An I2C driver instance + * i2s - An I2S driver instance + * lower - Persistent board configuration data + * + * Returned Value: + * A new lower half audio interface for the WM8994 device is returned on + * success; NULL is returned on failure. + * + */ + +FAR struct audio_lowerhalf_s * + wm8994_initialize(FAR struct i2c_master_s *i2c, FAR struct i2s_dev_s *i2s, + FAR const struct wm8994_lower_s *lower) +{ + FAR struct wm8994_dev_s *priv; + uint16_t regval; + + /* Sanity check */ + + DEBUGASSERT(i2c && i2s && lower); + + /* Allocate a WM8994 device structure */ + + priv = (FAR struct wm8994_dev_s *)kmm_zalloc(sizeof(struct wm8994_dev_s)); + if (priv) + { + /* Initialize the WM8994 device structure. Since we used kmm_zalloc, + * only the non-zero elements of the structure need to be initialized. + */ + + priv->dev.ops = &g_audioops; + priv->lower = lower; + priv->i2c = i2c; + priv->i2s = i2s; + + nxsem_init(&priv->pendsem, 0, 1); + dq_init(&priv->pendq); + dq_init(&priv->doneq); + + /* Verify that WM8994 is present and available on this I2C */ + + regval = wm8994_readreg(priv, WM8994_ID); + + if (regval != WM8994_SW_RST_DEV_ID1) + { + auderr("ERROR: WM8994 not found: ID=%04x\n", regval); + goto errout_with_dev; + } + + /* Software reset. This puts all WM8994 registers back in their + * default state. + */ + + wm8994_writereg(priv, WM8994_SWRST, 0); + wm8994_dump_registers(&priv->dev, "After reset"); + + /* chip revison */ + + audinfo("wm8994 chip revison: %d\n", + wm8994_readreg(priv, WM8994_CHIP_REV)); + + /* Reset and reconfigure the WM8994 hardwaqre */ + + wm8994_hw_reset(priv); + return &priv->dev; + } + + return NULL; + +errout_with_dev: + nxsem_destroy(&priv->pendsem); + kmm_free(priv); + return NULL; +} diff --git a/drivers/audio/wm8994.h b/drivers/audio/wm8994.h new file mode 100644 index 00000000000..fc9539f70cc --- /dev/null +++ b/drivers/audio/wm8994.h @@ -0,0 +1,1635 @@ +/*************************************************************************************************** + * drivers/audio/wm8994.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ***************************************************************************************************/ + +#ifndef __DRIVERS_AUDIO_WM8994_H +#define __DRIVERS_AUDIO_WM8994_H + +/*************************************************************************************************** + * Included Files + ***************************************************************************************************/ + +#include +#include + +#include +#include + +#include +#include + +#ifdef CONFIG_AUDIO + +/*************************************************************************************************** + * Pre-processor Definitions + ***************************************************************************************************/ + +/* So far, I have not been able to get FLL lock interrupts. Worse, I have + * been able to get the FLL to claim that it is locked at all even when + * polling. What am I doing wrong? + * + * Hmmm.. seems unnecessary anyway + */ + +#undef WM8994_USE_FFLOCK_INT +#undef WM8994_USE_FFLOCK_POLL + +/* Registers Addresses */ + +#define WM8994_SWRST 0x00 /* SW Reset and ID */ +#define WM8994_ID 0x00 /* SW Reset and ID */ + +#define WM8994_PM1 0x01 /* Power Mangement */ +#define WM8994_PM2 0x02 /* Power Mangement */ +#define WM8994_PM3 0x03 /* Power Mangement */ +#define WM8994_PM4 0x04 /* Power Mangement */ +#define WM8994_PM5 0x05 /* Power Mangement */ +#define WM8994_PM6 0x06 /* Power Mangement */ + +#define WM8994_INPUT_MIXER1 0x15 /* Input Mixer (1) */ + +#define WM8994_LEFTLINE_12_VOL 0x18 /* Left Line Input 1&2 Volume */ +#define WM8994_LEFTLINE_34_VOL 0x19 /* Left Line Input 3&4 Volume */ +#define WM8994_RIGHTLINE_12_VOL 0x1A /* Right Line Input 1&2 Volume */ +#define WM8994_RIGHTLINE_34_VOL 0x1B /* Right Line Input 3&4 Volume */ + +#define WM8994_LEFT_OUTPUT_VOL 0x1C /* Left Output Volume */ +#define WM8994_RIGHT_OUTPUT_VOL 0x1D /* Right Output Volume */ +#define WM8994_LINE_OUTPUTS_VOL 0x1E /* Line Outputs Volume */ +#define WM8994_HPOUT2_VOL 0x1F /* HPOUT2 Volume */ +#define WM8994_LEFT_OPGA_VOL 0x20 /* Left OPGA Volume */ +#define WM8994_RIGHT_OPGA_VOL 0x21 /* Right OPGA Volume */ + +#define WM8994_SPKMIXL_ATT 0x22 /* SPKMIXL Attenuation */ +#define WM8994_SPKMIXR_ATT 0x23 /* SPKMIXR Attenuation */ +#define WM8994_SPKOUT_MIXERS 0x24 /* SPKOUT Mixers */ + +#define WM8994_CLASS_D 0x25 /* ClassD */ +#define WM8994_SPEAKER_VOL_LEFT 0x26 /* Speaker Volume left */ +#define WM8994_SPEAKER_VOL_RIGHT 0x27 /* Speaker Volume right */ + +#define WM8994_INPUT_MIXER2 0x28 /* Input Mixer (2) */ +#define WM8994_INPUT_MIXER3 0x29 /* Input Mixer (3) */ +#define WM8994_INPUT_MIXER4 0x2A /* Input Mixer (4) */ +#define WM8994_INPUT_MIXER5 0x2B /* Input Mixer (5) */ +#define WM8994_INPUT_MIXER6 0x2C /* Input Mixer (6) */ + +#define WM8994_OUTPUT_MIXER1 0x2D /* Output Mixer (1) */ +#define WM8994_OUTPUT_MIXER2 0x2E /* Output Mixer (2) */ +#define WM8994_OUTPUT_MIXER3 0x2F /* Output Mixer (3) */ +#define WM8994_OUTPUT_MIXER4 0x30 /* Output Mixer (4) */ +#define WM8994_OUTPUT_MIXER5 0x31 /* Output Mixer (5) */ +#define WM8994_OUTPUT_MIXER6 0x32 /* Output Mixer (6) */ + +#define WM8994_HPOUT2_MIXER 0x33 /* HPOUT2 Mixer */ + +#define WM8994_LINE_MIXER1 0x34 /* Line Mixer (1) */ +#define WM8994_LINE_MIXER2 0x35 /* Line Mixer (2) */ + +#define WM8994_SPEAKER_MIXER 0x36 /* Speaker Mixer */ + +#define WM8994_ADDITIONAL_CTL 0x37 /* Additional Control */ +#define WM8994_ANTI_POP1 0x38 /* AntiPOP (1) */ +#define WM8994_ANTI_POP2 0x39 /* AntiPOP (2) */ + +#define WM8994_MIC_BIAS 0x3A /* MICBIAS */ + +#define WM8994_LDO_1 0x3B /* LDO 1 */ +#define WM8994_LDO_2 0x3C /* LDO 2 */ + +#define WM8994_CHARGE_PUMP1 0x4C /* Charge Pump 1 */ +#define WM8994_CHARGE_PUMP2 0x4D /* Charge Pump 2 */ + +#define WM8994_CLASS_W_1 0x51 /* Class W (1) */ + +#define WM8994_DC_SERVO1 0x54 /* DC Servo (1) */ +#define WM8994_DC_SERVO2 0x55 /* DC Servo (2) */ +#define WM8994_DC_SERVO_RB 0x58 /* DC Servo Readback */ +#define WM8994_DC_SERVO4 0x59 /* DC Servo (4) */ + +#define WM8994_ANA_HP1 0x60 /* Analogue HP (1) */ +#define WM8994_CHIP_REV 0x100 /* Chip Revision */ + +#define WM8994_CTL_IF 0x101 /* Control Interface */ +#define WM8994_WR_CTL_SEQ1 0x110 /* Write Sequencer Ctrl (1) */ +#define WM8994_WR_CTL_SEQ2 0x111 /* Write Sequencer Ctrl (2) */ + +#define WM8994_AIF1_CLK1 0x200 /* AIF1 Clocking (1) */ +#define WM8994_AIF1_CLK2 0x201 /* AIF1 Clocking (2) */ +#define WM8994_AIF2_CLK1 0x204 /* AIF2 Clocking (1) */ +#define WM8994_AIF2_CLK2 0x205 /* AIF2 Clocking (2) */ +#define WM8994_CLK1 0x208 /* Clocking (1) */ +#define WM8994_CLK2 0x209 /* Clocking (2) */ + +#define WM8994_AIF1_RATE 0x210 /* AIF1 Rate */ +#define WM8994_AIF2_RATE 0x211 /* AIF2 Rate */ +#define WM8994_RATE_STATUS 0x212 /* Rate Status */ + +#define WM8994_PLL1_CTL1 0x220 /* PLL1 Control (1) */ +#define WM8994_PLL1_CTL2 0x221 /* PLL1 Control (2) */ +#define WM8994_PLL1_CTL3 0x222 /* PLL1 Control (3) */ +#define WM8994_PLL1_CTL4 0x223 /* PLL1 Control (4) */ +#define WM8994_PLL1_CTL5 0x224 /* PLL1 Control (5) */ + +#define WM8994_PLL2_CTL1 0x240 /* PLL2 Control (1) */ +#define WM8994_PLL2_CTL2 0x241 /* PLL2 Control (2) */ +#define WM8994_PLL2_CTL3 0x242 /* PLL2 Control (3) */ +#define WM8994_PLL2_CTL4 0x243 /* PLL2 Control (4) */ +#define WM8994_PLL2_CTL5 0x244 /* PLL2 Control (5) */ + +#define WM8994_AIF1_CTL1 0x300 /* AIF1 Control (1) */ +#define WM8994_AIF1_CTL2 0x301 /* AIF1 Control (2) */ + +#define WM8994_AIF1_MASTER_SLAVE 0x302 /* AIF1 Master/Slave */ +#define WM8994_AIF1_BCLK 0x303 /* AIF1 BCLK */ +#define WM8994_AIF1_ADC_LRCLK 0x304 /* AIF1 ADC LRCLK */ +#define WM8994_AIF1_DAC_LRCLK 0x305 /* AIF1 DAC LRCLK */ +#define WM8994_AIF1_DAC_DATA 0x306 /* AIF1 DAC DATA */ +#define WM8994_AIF1_ADC_DATA 0x307 /* AIF1 ADC DATA */ + +#define WM8994_AIF2_CTL1 0x310 /* AIF2 Control (1) */ +#define WM8994_AIF2_CTL2 0x311 /* AIF2 Control (2) */ +#define WM8994_AIF2_MASTER_SLAVE 0x312 /* AIF2 Master/Slave */ +#define WM8994_AIF2_BCLK 0x313 /* AIF2 BCLK */ +#define WM8994_AIF2_ADC_LRCLK 0x314 /* AIF2 ADC LRCLK */ +#define WM8994_AIF2_DAC_LRCLK 0x315 /* AIF2 DAC LRCLK */ +#define WM8994_AIF2_DAC_DATA 0x316 /* AIF2 DAC DATA */ +#define WM8994_AIF2_ADC_DATA 0x317 /* AIF2 ADC DATA */ + +#define WM8994_AIF1_ADC1_LEFT_VOL 0x400 /* AIF1 ADC1 Left Volume */ +#define WM8994_AIF1_ADC1_RIGHT_VOL 0x401 /* AIF1 ADC1 Right Volume */ +#define WM8994_AIF1_DAC1_LEFT_VOL 0x402 /* AIF1 DAC1 Left Volume */ +#define WM8994_AIF1_DAC1_RIGHT_VOL 0x403 /* AIF1 DAC1 Right Volume */ + +#define WM8994_AIF1_ADC2_LEFT_VOL 0x404 /* AIF1 ADC2 Left Volume */ +#define WM8994_AIF1_ADC2_RIGHT_VOL 0x405 /* AIF1 ADC2 Right Volume */ +#define WM8994_AIF1_DAC2_LEFT_VOL 0x406 /* AIF1 DAC2 Left Volume */ +#define WM8994_AIF1_DAC2_RIGHT_VOL 0x407 /* AIF1 DAC2 Right Volume */ + +#define WM8994_AIF1_ADC1_FILTERS 0x410 /* AIF1 ADC1 Filters */ +#define WM8994_AIF1_ADC2_FILTERS 0x411 /* AIF1 ADC2 Filters */ + +#define WM8994_AIF1_DAC1_FILTERS1 0x420 /* AIF1 DAC1 Filters (1) */ +#define WM8994_AIF1_DAC1_FILTERS2 0x421 /* AIF1 DAC1 Filters (2) */ +#define WM8994_AIF1_DAC2_FILTERS1 0x422 /* AIF1 DAC2 Filters (1) */ +#define WM8994_AIF1_DAC2_FILTERS2 0x423 /* AIF1 DAC2 Filters (2) */ + +#define WM8994_AIF1_DRC1_1 0x440 /* AIF1 DRC1 (1) */ +#define WM8994_AIF1_DRC1_2 0x441 /* AIF1 DRC1 (2) */ +#define WM8994_AIF1_DRC1_3 0x442 /* AIF1 DRC1 (3) */ +#define WM8994_AIF1_DRC1_4 0x443 /* AIF1 DRC1 (4) */ +#define WM8994_AIF1_DRC1_5 0x444 /* AIF1 DRC1 (5) */ + +#define WM8994_AIF1_DRC2_1 0x450 /* AIF1 DRC2 (1) */ +#define WM8994_AIF1_DRC2_2 0x451 /* AIF1 DRC2 (2) */ +#define WM8994_AIF1_DRC2_3 0x452 /* AIF1 DRC2 (3) */ +#define WM8994_AIF1_DRC2_4 0x453 /* AIF1 DRC2 (4) */ +#define WM8994_AIF1_DRC2_5 0x454 /* AIF1 DRC2 (5) */ + +#define WM8994_AIF1_DAC1_EQ_GAINS_1 0x480 /* AIF1 DAC1 EQ Gains (1) */ +#define WM8994_AIF1_DAC1_EQ_GAINS_2 0x481 /* AIF1 DAC1 EQ Gains (2) */ +#define WM8994_AIF1_DAC1_EQ_BAND_1A 0x482 /* AIF1 DAC1 EQ Band 1 A */ +#define WM8994_AIF1_DAC1_EQ_BAND_1B 0x483 /* AIF1 DAC1 EQ Band 1 B */ +#define WM8994_AIF1_DAC1_EQ_BAND_1PG 0x484 /* AIF1 DAC1 EQ Band 1 PG */ +#define WM8994_AIF1_DAC1_EQ_BAND_2A 0x485 /* AIF1 DAC1 EQ Band 2 A */ +#define WM8994_AIF1_DAC1_EQ_BAND_2B 0x486 /* AIF1 DAC1 EQ Band 2 B */ +#define WM8994_AIF1_DAC1_EQ_BAND_2C 0x487 /* AIF1 DAC1 EQ Band 2 C */ +#define WM8994_AIF1_DAC1_EQ_BAND_2PG 0x488 /* AIF1 DAC1 EQ Band 2 PG */ +#define WM8994_AIF1_DAC1_EQ_BAND_3A 0x489 /* AIF1 DAC1 EQ Band 3 A */ +#define WM8994_AIF1_DAC1_EQ_BAND_3B 0x48A /* AIF1 DAC1 EQ Band 3 B */ +#define WM8994_AIF1_DAC1_EQ_BAND_3C 0x48B /* AIF1 DAC1 EQ Band 3 C */ +#define WM8994_AIF1_DAC1_EQ_BAND_3PG 0x48C /* AIF1 DAC1 EQ Band 3 PG */ +#define WM8994_AIF1_DAC1_EQ_BAND_4A 0x48D /* AIF1 DAC1 EQ Band 4 A */ +#define WM8994_AIF1_DAC1_EQ_BAND_4B 0x48E /* AIF1 DAC1 EQ Band 4 B */ +#define WM8994_AIF1_DAC1_EQ_BAND_4C 0x48F /* AIF1 DAC1 EQ Band 4 C */ +#define WM8994_AIF1_DAC1_EQ_BAND_4PG 0x490 /* AIF1 DAC1 EQ Band 4 PG */ +#define WM8994_AIF1_DAC1_EQ_BAND_5A 0x491 /* AIF1 DAC1 EQ Band 5 A */ +#define WM8994_AIF1_DAC1_EQ_BAND_5B 0x492 /* AIF1 DAC1 EQ Band 5 B */ +#define WM8994_AIF1_DAC1_EQ_BAND_5PG 0x493 /* AIF1 DAC1 EQ Band 5 PG */ + +#define WM8994_AIF1_DAC2_EQ_GAINS_1 0x4A0 /* AIF1 DAC2 EQ Gains (1) */ +#define WM8994_AIF1_DAC2_EQ_GAINS_2 0x4A1 /* AIF1 DAC2 EQ Gains (2) */ +#define WM8994_AIF1_DAC2_EQ_BAND_1A 0x4A2 /* AIF1 DAC2 EQ Band 1 A */ +#define WM8994_AIF1_DAC2_EQ_BAND_1B 0x4A3 /* AIF1 DAC2 EQ Band 1 B */ +#define WM8994_AIF1_DAC2_EQ_BAND_1PG 0x4A4 /* AIF1 DAC2 EQ Band 1 PG */ +#define WM8994_AIF1_DAC2_EQ_BAND_2A 0x4A5 /* AIF1 DAC2 EQ Band 2 A */ +#define WM8994_AIF1_DAC2_EQ_BAND_2B 0x4A6 /* AIF1 DAC2 EQ Band 2 B */ +#define WM8994_AIF1_DAC2_EQ_BAND_2C 0x4A7 /* AIF1 DAC2 EQ Band 2 C */ +#define WM8994_AIF1_DAC2_EQ_BAND_2PG 0x4A8 /* AIF1 DAC2 EQ Band 2 PG */ +#define WM8994_AIF1_DAC2_EQ_BAND_3A 0x4A9 /* AIF1 DAC2 EQ Band 3 A */ +#define WM8994_AIF1_DAC2_EQ_BAND_3B 0x4AA /* AIF1 DAC2 EQ Band 3 B */ +#define WM8994_AIF1_DAC2_EQ_BAND_3C 0x4AB /* AIF1 DAC2 EQ Band 3 C */ +#define WM8994_AIF1_DAC2_EQ_BAND_3PG 0x4AC /* AIF1 DAC2 EQ Band 3 PG */ +#define WM8994_AIF1_DAC2_EQ_BAND_4A 0x4AD /* AIF1 DAC2 EQ Band 4 A */ +#define WM8994_AIF1_DAC2_EQ_BAND_4B 0x4AE /* AIF1 DAC2 EQ Band 4 B */ +#define WM8994_AIF1_DAC2_EQ_BAND_4C 0x4AF /* AIF1 DAC2 EQ Band 4 C */ +#define WM8994_AIF1_DAC2_EQ_BAND_4PG 0x4B0 /* AIF1 DAC2 EQ Band 4 PG */ +#define WM8994_AIF1_DAC2_EQ_BAND_5A 0x4B1 /* AIF1 DAC2 EQ Band 5 A */ +#define WM8994_AIF1_DAC2_EQ_BAND_5B 0x4B2 /* AIF1 DAC2 EQ Band 5 B */ +#define WM8994_AIF1_DAC2_EQ_BAND_5PG 0x4B3 /* AIF1 DAC2 EQ Band 5 PG */ + +/* AIF2 */ +#define WM8994_AIF2_ADC1_LEFT_VOL 0x500 /* AIF2 ADC1 Left Volume */ +#define WM8994_AIF2_ADC1_RIGHT_VOL 0x501 /* AIF2 ADC1 Right Volume */ +#define WM8994_AIF2_DAC1_LEFT_VOL 0x502 /* AIF2 DAC1 Left Volume */ +#define WM8994_AIF2_DAC1_RIGHT_VOL 0x503 /* AIF2 DAC1 Right Volume */ + +#define WM8994_AIF2_ADC_FILTERS 0x510 /* AIF2 ADC Filters */ +#define WM8994_AIF2_DAC_FILTERS1 0x520 /* AIF2 DAC Filters (1) */ +#define WM8994_AIF2_DAC_FILTERS2 0x521 /* AIF2 DAC Filters (2) */ + +#define WM8994_AIF2_DRC_1 0x540 /* AIF2 DRC (1) */ +#define WM8994_AIF2_DRC_2 0x541 /* AIF2 DRC (2) */ +#define WM8994_AIF2_DRC_3 0x542 /* AIF2 DRC (3) */ +#define WM8994_AIF2_DRC_4 0x543 /* AIF2 DRC (4) */ +#define WM8994_AIF2_DRC_5 0x544 /* AIF2 DRC (5) */ + +#define WM8994_AIF2_EQ_GAINS_1 0x580 /* AIF2 EQ Gains (1) */ +#define WM8994_AIF2_EQ_GAINS_2 0x581 /* AIF2 EQ Gains (2) */ +#define WM8994_AIF2_EQ_BAND_1A 0x582 /* AIF2 EQ Band 1 A */ +#define WM8994_AIF2_EQ_BAND_1B 0x583 /* AIF2 EQ Band 1 B */ +#define WM8994_AIF2_EQ_BAND_1PG 0x584 /* AIF2 EQ Band 1 PG */ +#define WM8994_AIF2_EQ_BAND_2A 0x585 /* AIF2 EQ Band 2 A */ +#define WM8994_AIF2_EQ_BAND_2B 0x586 /* AIF2 EQ Band 2 B */ +#define WM8994_AIF2_EQ_BAND_2C 0x587 /* AIF2 EQ Band 2 C */ +#define WM8994_AIF2_EQ_BAND_2PG 0x588 /* AIF2 EQ Band 2 PG */ +#define WM8994_AIF2_EQ_BAND_3A 0x589 /* AIF2 EQ Band 3 A */ +#define WM8994_AIF2_EQ_BAND_3B 0x58A /* AIF2 EQ Band 3 B */ +#define WM8994_AIF2_EQ_BAND_3C 0x58B /* AIF2 EQ Band 3 C */ +#define WM8994_AIF2_EQ_BAND_3PG 0x58C /* AIF2 EQ Band 3 PG */ +#define WM8994_AIF2_EQ_BAND_4A 0x58D /* AIF2 EQ Band 4 A */ +#define WM8994_AIF2_EQ_BAND_4B 0x58E /* AIF2 EQ Band 4 B */ +#define WM8994_AIF2_EQ_BAND_4C 0x58F /* AIF2 EQ Band 4 C */ +#define WM8994_AIF2_EQ_BAND_4PG 0x490 /* AIF2 EQ Band 4 PG */ +#define WM8994_AIF2_EQ_BAND_5A 0x591 /* AIF2 EQ Band 5 A */ +#define WM8994_AIF2_EQ_BAND_5B 0x592 /* AIF2 EQ Band 5 B */ +#define WM8994_AIF2_EQ_BAND_5PG 0x593 /* AIF2 EQ Band 5 PG */ + +#define WM8994_DAC1_MIXER_VOLS 0x600 /* DAC1 Mixer Volumes */ +#define WM8994_DAC1_LEFT_MIXER_ROUTING 0x601 /* DAC1 Left Mixer Routing */ +#define WM8994_DAC1_RIGHT_MIXER_ROUTING 0x602 /* DAC1 Right Mixer Routing */ + +#define WM8994_DAC2_MIXER_VOLS 0x603 /* DAC2 Mixer Volumes */ +#define WM8994_DAC2_LEFT_MIXER_ROUTING 0x604 /* DAC2 Left Mixer Routing */ +#define WM8994_DAC2_RIGHT_MIXER_ROUTING 0x605 /* DAC2 Right Mixer Routing */ + +#define WM8994_ADC1_LEFT_MIXER_ROUTING 0x606 /* ADC1 Left Mixer Routing */ +#define WM8994_ADC1_RIGHT_MIXER_ROUTING 0x607 /* ADC1 Right Mixer Routing */ + +#define WM8994_ADC2_LEFT_MIXER_ROUTING 0x608 /* ADC2 Left Mixer Routing */ +#define WM8994_ADC2_RIGHT_MIXER_ROUTING 0x609 /* ADC2 Right Mixer Routing */ + +#define WM8994_DAC1_LEFT_VOL 0x610 /* DAC1 Left Volume */ +#define WM8994_DAC1_RIGHT_VOL 0x611 /* DAC1 Right Volume */ + +#define WM8994_DAC2_LEFT_VOL 0x612 /* DAC2 Left Volume */ +#define WM8994_DAC2_RIGHT_VOL 0x613 /* DAC2 Right Volume */ + +#define WM8994_DAC_SOFT_MUTE 0x614 /* DAC Softmute */ + +#define WM8994_OVER_SAMPLING 0x620 /* Oversampling */ +#define WM8994_SIDE_TONE 0x621 /* Sidetone */ + +#define WM8994_GPIO1 0x700 /* GPIO 1 */ +#define WM8994_GPIO2 0x701 /* GPIO 2 */ +#define WM8994_GPIO3 0x702 /* GPIO 3 */ +#define WM8994_GPIO4 0x703 /* GPIO 4 */ +#define WM8994_GPIO5 0x704 /* GPIO 5 */ +#define WM8994_GPIO6 0x705 /* GPIO 6 */ +#define WM8994_GPIO7 0x706 /* GPIO 7 */ +#define WM8994_GPIO8 0x707 /* GPIO 8 */ +#define WM8994_GPIO9 0x708 /* GPIO 9 */ +#define WM8994_GPIO10 0x709 /* GPIO 10 */ +#define WM8994_GPIO11 0x70A /* GPIO 11 */ + +#define WM8994_PULL_CTL1 0x720 /* Pull Control (1) */ +#define WM8994_PULL_CTL2 0x721 /* Pull Control (2) */ + +#define WM8994_INT_STATUS1 0x730 /* Interrupt Status 1 */ +#define WM8994_INT_STATUS2 0x731 /* Interrupt Status 2 */ +#define WM8994_INT_RAW_STATUS2 0x732 /* Interrupt Raw Status 2 */ +#define WM8994_INT_STATUS1_MASK 0x738 /* Interrupt Status 1 Mask */ +#define WM8994_INT_STATUS2_MASK 0x739 /* Interrupt Status 2 Mask */ +#define WM8994_INT_CTL 0x740 /* Interrupt Control */ +#define WM8994_INT_DEBOUNCE 0x748 /* IRQ Debounce */ + +#if 0 +#define WM8994_WR_SEQ0 0x3000 /* Write Sequencer 0 */ +#define WM8994_WR_SEQ1 0x3001 /* Write Sequencer 1 */ +#define WM8994_WR_SEQ2 0x3002 /* Write Sequencer 2 */ +#define WM8994_WR_SEQ3 0x3003 /* Write Sequencer 3 */ + +#define WM8994_WR_SEQ508 0x31FC /* Write Sequencer 508 */ +#define WM8994_WR_SEQ509 0x31FD /* Write Sequencer 509 */ +#define WM8994_WR_SEQ510 0x31FE /* Write Sequencer 510 */ +#define WM8994_WR_SEQ511 0x31FF /* Write Sequencer 511 */ +#endif + +#define WM8994_WR_SEQ(x) (0x3000+(x)) + +#define WM8994_WR_SEQ_NUM (511) + +#define WM8994_REGISTER_COUNT 736 +#define WM8994_MAX_REGISTER 0x31FF +#define WM8994_MAX_CACHED_REGISTER 0x749 + +/* Field Definitions. + */ + +/* R0 (0x00) - Software Reset + */ + +#define WM8994_SW_RESET (0) /* Bits 0-15: SW_RESET - [15:0] */ + +/* R1 (0x01) - Power Management (1) + */ + +#define WM8994_BIAS_ENA (1 << 0) /* Bit 0: Enables the Normal bias current generator (for all analogue functions */ +#define WM8994_BIAS_ENA_DISABLE (0) /* Diabled */ +#define WM8994_BIAS_ENA_ENABLE WM8994_BIAS_ENA /* Enabled */ +#define WM8994_VMID_SEL_SHITF (1) /* Bits 1-2: VMID Divider Enable and Select */ +#define WM8994_VMID_SEL_DISABLE (0 << WM8994_VMID_SEL_SHIFT) /* VMID disabled (for OFF mode) */ +#define WM8994_VMID_SEL_2x40K (1 << WM8994_VMID_SEL_SHIFT) /* 2*40k divider (for normal operation */ +#define WM8994_VMID_SEL_2x240K (2 << WM8994_VMID_SEL_SHIFT) /* 2*240k divider (for low power standby*/ + /* Bit 3: Reserved */ + +#define WM8994_MICB1_ENA (1 << 4) /* Bit 4; Microphone Bias 1 Enable */ +#define WM8994_MICB1_ENA_DISABLE (0) /* Disabled */ +#define WM8994_MICB1_ENA_ENABLE (WM8994_MICB1_ENA) /* Enabled */ +#define WM8994_MICB2_ENA (1 << 5) /* Bit 5; Microphone Bias 2 Enable */ +#define WM8994_MICB2_ENA_DISABLE (0) /* Disabled */ +#define WM8994_MICB2_ENA_ENABLE (WM8994_MICB2_ENA) /* Enabled */ + /* Bits 6-7: Reserved */ +#define WM8994_HPOUT1R_ENA (1 << 8) /* Bit 8: Enables HPOUT1R input stage */ +#define WM8994_HPOUT1R_ENA_DISABLE (0) /* Disabled */ +#define WM8994_HPOUT1R_ENA_ENABLE (WM8994_HPOUT1R_ENA) /* Enabled */ +#define WM8994_HPOUT1L_ENA (1 << 9) /* Bit 9: Enables HPOUT1L input stage */ +#define WM8994_HPOUT1L_ENA_DISABLE (0) /* Disabled */ +#define WM8994_HPOUT1L_ENA_ENABLE (WM8994_HPOUT1L_ENA) /* Enabled */ + /* Bit 10: Reserved */ +#define WM8994_HPOUT2_ENA (1 << 11) /* Bit 11: Enables HPOUT2 input stage */ +#define WM8994_HPOUT2_ENA_DISABLE (0) /* Disabled */ +#define WM8994_HPOUT2_ENA_ENABLE (WM8994_HPOUT2_ENA) /* Enabled */ +#define WM8994_SPKOUTL_ENA (1 << 12) /* Bit 12: SPKMIXL Mixer, SPKLVOL PGA and SPKOUTL Output Enable */ +#define WM8994_SPKOUTL_ENA_DISABLE (0) /* Disabled */ +#define WM8994_SPKOUTL_ENA_ENABLE (WM8994_SPKOUTL_ENA) /* Enabled */ +#define WM8994_SPKOUTR_ENA (1 << 13) /* Bit 13: SPKMIXR Mixer, SPKRVOL PGA and SPKOUTR Output Enable */ +#define WM8994_SPKOUTR_ENA_DISABLE (0) /* Disabled */ +#define WM8994_SPKOUTR_ENA_ENABLE (WM8994_SPKOUTR_ENA) /* Enabled */ + /* Bits 14-15: Reserved */ + +/* R2 (0x02) - Power Management (2) + */ + + /* Bits 0-3: Reserved */ + +#define WM8994_IN1R_ENA (1 << 4) /* Bit 4: IN1R Input PGA Enable */ +#define WM8994_IN1R_ENA_DISABLE (0) /* Disabled */ +#define WM8994_IN1R_ENA_ENABLE (WM8994_IN1R_ENA) /* Enabled */ +#define WM8994_IN2R_ENA (1 << 5) /* Bit 5: IN2R Input PGA Enable */ +#define WM8994_IN2R_ENA_DISABLE (0) /* Disabled */ +#define WM8994_IN2R_ENA_ENABLE (WM8994_IN2R_ENA) /* Enabled */ +#define WM8994_IN1L_ENA (1 << 6) /* Bit 6: IN1L Input PGA Enable */ +#define WM8994_IN1L_ENA_DISABLE (0) /* Disabled */ +#define WM8994_IN1L_ENA_ENABLE (WM8994_IN1L_ENA) /* Enabled */ +#define WM8994_IN2L_ENA (1 << 7) /* Bit 7: IN2L Input PGA Enable */ +#define WM8994_IN2L_ENA_DISABLE (0) /* Disabled */ +#define WM8994_IN2L_ENA_ENABLE (WM8994_IN2L_ENA) /* Enabled */ +#define WM8994_MIXINR_ENA (1 << 8) /* Bit 8: Right Input Mixer Enable */ +#define WM8994_MIXINR_ENA_DISABLE (0) /* Disabled */ +#define WM8994_MIXINR_ENA_ENABLE (WM8994_MIXINR_ENA) /* Enabled */ +#define WM8994_MIXINL_ENA (1 << 9) /* Bit 9: Left Input Mixer Enable */ +#define WM8994_MIXINL_ENA_DISABLE (0) /* Disabled */ +#define WM8994_MIXINL_ENA_ENABLE (WM8994_MIXINL_ENA) /* Enabled */ + /* Bit 10: Reserved */ +#define WM8994_OPCLK_ENA (1 << 11) /* Bit 11: GPIO Clock Output(OPCLK) Enable */ +#define WM8994_OPCLK_ENA_DISABLE (0) /* Disabled */ +#define WM8994_OPCLK_ENA_ENABLE (WM8994_OPCLK_ENA) /* Enabled */ + /* Bit 12: Reserved */ +#define WM8994_TSHUT_OPDIS (1 << 13) /* Bit 13: Thermal shutdown control */ +#define WM8994_TSHUT_OPDIS_DISABLE (0) /* Disabled */ +#define WM8994_TSHUT_OPDIS_ENABLE (WM8994_TSHUT_OPDIS) /* Enabled */ +#define WM8994_TSHUT_ENA (1 << 14) /* Bit 14: Thermal sensor enable */ +#define WM8994_TSHUT_ENA_DISABLE (0) /* Disabled */ +#define WM8994_TSHUT_ENA_ENABLE (WM8994_TSHUT_ENA) /* Enabled */ + /* Bit 15: Reserved */ + +/* R3 (0x03) - Power Management (3) + */ + + /* Bits 0-3: Reserved */ +#define WM8994_MIXOUTR_ENA (1 << 4) /* Bit 4: MIXOUTR Right Output Mixer Enable */ +#define WM8994_MIXOUTR_ENA_DISABLE (0) /* Disabled */ +#define WM8994_MIXOUTR_ENA_ENABLE (WM8994_MIXOUTR_ENA) /* Enabled */ +#define WM8994_MIXOUTL_ENA (1 << 5) /* Bit 5: MIXOUTL Left Output Mixer Enable */ +#define WM8994_MIXOUTL_ENA_DISABLE (0) /* Disabled */ +#define WM8994_MIXOUTL_ENA_ENABLE (WM8994_MIXOUTL_ENA) /* Enabled */ +#define WM8994_MIXOUTRVOL_ENA (1 << 6) /* Bit 6: MIXOUTR Right Volume Control Enable */ +#define WM8994_MIXOUTRVOL_ENA_DISABLE (0) /* Disabled */ +#define WM8994_MIXOUTRVOL_ENA_ENABLE (WM8994_MIXOUTRVOL_ENA) /* Enabled */ +#define WM8994_MIXOUTLVOL_ENA (1 << 7) /* Bit 7: MIXOUTL Left Volume Control Enable */ +#define WM8994_MIXOUTLVOL_ENA_DISABLE (0) /* Disabled */ +#define WM8994_MIXOUTLVOL_ENA_ENABLE (WM8994_MIXOUTLVOL_ENA) /* Enabled */ +#define WM8994_SPKLVOL_ENA (1 << 8) /* Bit 8: SPKMIXL Mixer and SPKLVOL PGA Enable */ +#define WM8994_SPKLVOL_ENA_DISABLE (0) /* Disabled */ +#define WM8994_SPKLVOL_ENA_ENABLE (WM8994_SPKLVOL_ENA) /* Enabled */ +#define WM8994_SPKRVOL_ENA (1 << 9) /* Bit 9: SPKMIXR Mixer and SPKRVOL PGA Enable */ +#define WM8994_SPKRVOL_ENA_DISABLE (0) /* Disabled */ +#define WM8994_SPKRVOL_ENA_ENABLE (WM8994_SPKRVOL_ENA) /* Enabled */ +#define WM8994_LINEOUT2P_ENA (1 << 10) /* Bit 10: LINEOUT2P Line Out and LINEOUT2PMIX Enable */ +#define WM8994_LINEOUT2P_ENA_DISABLE (0) /* Disabled */ +#define WM8994_LINEOUT2P_ENA_ENABLE (WM8994_LINEOUT2P_ENA) /* Enabled */ +#define WM8994_LINEOUT2N_ENA (1 << 11) /* Bit 11: LINEOUT2N Line Out and LINEOUT2NMIX Enable */ +#define WM8994_LINEOUT2N_ENA_DISABLE (0) /* Disabled */ +#define WM8994_LINEOUT2N_ENA_ENABLE (WM8994_LINEOUT2N_ENA) /* Enabled */ +#define WM8994_LINEOUT1P_ENA (1 << 12) /* Bit 12: LINEOUT1P Line Out and LINEOUT1PMIX Enable */ +#define WM8994_LINEOUT1P_ENA_DISABLE (0) /* Disabled */ +#define WM8994_LINEOUT1P_ENA_ENABLE (WM8994_LINEOUT1P_ENA) /* Enabled */ +#define WM8994_LINEOUT1N_ENA (1 << 13) /* Bit 13: LINEOUT1N Line Out and LINEOUT1NMIX Enable */ +#define WM8994_LINEOUT1N_ENA_DISABLE (0) /* Disabled */ +#define WM8994_LINEOUT1N_ENA_ENABLE (WM8994_LINEOUT1N_ENA) /* Enabled */ + /* Bits 14-15: Reserved */ + +/* R4 (0x04) - Power Management (4) + */ + +#define WM8994_ADCR_ENA (1 << 0) /* Bit 0: Right ADC Enable */ +#define WM8994_ADCR_ENA_DISABLE (0) /* Disabled */ +#define WM8994_ADCR_ENA_ENABLE (WM8994_ADCR_ENA) /* Enabled */ +#define WM8994_ADCL_ENA (1 << 1) /* Bit 1: Left ADC Enable */ +#define WM8994_ADCL_ENA_DISABLE (0) /* Disabled */ +#define WM8994_ADCL_ENA_ENABLE (WM8994_ADCL_ENA) /* Enabled */ +#define WM8994_DMIC1R_ENA (1 << 2) /* Bit 2: Digital microphone DMICDAT1 Right channel enable */ +#define WM8994_DMIC1R_ENA_DISABLE (0) /* Disabled */ +#define WM8994_DMIC1R_ENA_ENABLE (WM8994_DMIC1R_ENA) /* Enabled */ +#define WM8994_DMIC1L_ENA (1 << 3) /* Bit 3: Digital microphone DMICDAT1 Left channel enable */ +#define WM8994_DMIC1L_ENA_DISABLE (0) /* Disabled */ +#define WM8994_DMIC1L_ENA_ENABLE (WM8994_DMIC1L_ENA) /* Enabled */ +#define WM8994_DMIC2R_ENA (1 << 4) /* Bit 4: Digital microphone DMICDAT2 Right channel enable */ +#define WM8994_DMIC2R_ENA_DISABLE (0) /* Disabled */ +#define WM8994_DMIC2R_ENA_ENABLE (WM8994_DMIC2R_ENA) /* Enabled */ +#define WM8994_DMIC2L_ENA (1 << 5) /* Bit 5: Digital microphone DMICDAT2 Left channel enable */ +#define WM8994_DMIC2L_ENA_DISABLE (0) /* Disabled */ +#define WM8994_DMIC2L_ENA_ENABLE (WM8994_DMIC2L_ENA) /* Enabled */ + /* Bits 6-7: Reserved */ +#define WM8994_AIF1ADC1R_ENA (1 << 8) /* Bit 8: Enable AIF1ADC1(Right) output path (AIF1, Timeslot 0) */ +#define WM8994_AIF1ADC1R_ENA_DISABLE (0) /* Disabled */ +#define WM8994_AIF1ADC1R_ENA_ENABLE (WM8994_AIF1ADC1R_ENA) /* Enabled */ +#define WM8994_AIF1ADC1L_ENA (1 << 9) /* Bit 9: Enable AIF1ADC1(Left) output path (AIF1, Timeslot 0) */ +#define WM8994_AIF1ADC1L_ENA_DISABLE (0) /* Disabled */ +#define WM8994_AIF1ADC1L_ENA_ENABLE (WM8994_AIF1ADC1L_ENA) /* Enabled */ +#define WM8994_AIF1ADC2R_ENA (1 << 10) /* Bit 10: Enable AIF1ADC2(Right) output path (AIF1, Timeslot 1) */ +#define WM8994_AIF1ADC2R_ENA_DISABLE (0) /* Disabled */ +#define WM8994_AIF1ADC2R_ENA_ENABLE (WM8994_AIF1ADC2L_ENA) /* Enabled */ +#define WM8994_AIF1ADC2L_ENA (1 << 11) /* Bit 11: Enable AIF1ADC2(Left) output path (AIF1, Timeslot 1) */ +#define WM8994_AIF1ADC2L_ENA_DISABLE (0) /* Disabled */ +#define WM8994_AIF1ADC2L_ENA_ENABLE (WM8994_AIF1ADC2L_ENA) /* Enabled */ +#define WM8994_AIF2ADCR_ENA (1 << 12) /* Bit 12: Enable AIF2ADC(Right) output path */ +#define WM8994_AIF2ADCR_ENA_DISABLE (0) /* Disabled */ +#define WM8994_AIF2ADCR_ENA_ENABLE (WM8994_AIF2ADCL_ENA) /* Enabled */ +#define WM8994_AIF2ADCL_ENA (1 << 13) /* Bit 13: Enable AIF2ADC(Left) output path */ +#define WM8994_AIF2ADCL_ENA_DISABLE (0) /* Disabled */ +#define WM8994_AIF2ADCL_ENA_ENABLE (WM8994_AIF2ADCL_ENA) /* Enabled */ + /* Bits 14-15: Reserved */ + +/* R5 (0x05) - Power Management (5) + */ +#define WM8994_DAC1R_ENA (1 << 0) /* Bit 0: Right DAC1 Enable */ +#define WM8994_DAC1L_ENA (1 << 1) /* Bit 1: Left DAC1 Enable */ +#define WM8994_DAC2R_ENA (1 << 2) /* Bit 2: Right DAC2 Enable */ +#define WM8994_DAC2L_ENA (1 << 3) /* Bit 3: Left DAC2 Enable */ +#define WM8994_AIF1DAC1R_ENA (1 << 8) /* Bit 8: Enable AIF1DAC1(Right) input path (AIF1, Timeslot 0) */ +#define WM8994_AIF1DAC1L_ENA (1 << 9) /* Bit 9: Enable AIF1DAC1(Left) input path (AIF1, Timeslot 0) */ +#define WM8994_AIF1DAC2R_ENA (1 << 10) /* Bit 10: Enable AIF1DAC2(Right) input path (AIF1, Timeslot 1) */ +#define WM8994_AIF1DAC2L_ENA (1 << 11) /* Bit 11: Enable AIF1DAC2(Left) input path (AIF1, Timeslot 1) */ +#define WM8994_AIF2DACR_ENA (1 << 12) /* Bit 12: Enable AIF2DAC(Right) input path */ +#define WM8994_AIF2DACL_ENA (1 << 13) /* Bit 13: Enable AIF2DAC(Left) input path */ + /* Bits 14-15: Reserved */ + +/* R6 (0x06) - Power Management (6) + */ +#define WM8994_AIF1_DACDAT_SRC (1 << 0) /* Bit 0: AIF1 DACDAT Source Select */ +#define WM8994_AIF1_DACDAT_SRC_DACDAT1 (0) /* DACDAT1 */ +#define WM8994_AIF1_DACDAT_SRC_GPIO8_DACDAT3 (WM8994_AIF1_DACDAT_SRC) /* GPIO8/DACDAT3 */ +#define WM8994_AIF2_DACDAT_SRC (1 << 1) /* Bit 1: AIF2 DACDAT Source Select */ +#define WM8994_AIF2_DACDAT_SRC_GPIO5_DACDAT1 (0) /* GPIO5/DACDAT2 */ +#define WM8994_AIF2_DACDAT_SRC_GPIO8_DACDAT3 (WM8994_AIF2_DACDAT_SRC) /* GPIO8/DACDAT3 */ +#define WM8994_AIF2_ADCDAT_SRC (1 << 2) /* Bit 2: GPIO7/ADCDAT Source Select */ +#define WM8994_AIF2_ADCDAT_SRC_ADCDAT2 (0) /* AIF2 ADCDAT2 */ +#define WM8994_AIF2_ADCDAT_SRC_GPIO8_DACDAT3 (WM8994_AIF2_ADCDAT_SRC) /* GPIO8/DACDAT3 */ +#define WM8994_AIF3_ADCDAT_SRC (1 << 3) /* Bits 3-4: GPIO9/ADCDAT3 Source Select */ +#define WM8994_AIF3_ADCDAR_SRC_AIF1_ADCDAT1 (0) /* AIF1 ADCDAT1 */ +#define WM8994_AIF3_ADCDAR_SRC_AIF2_ADCDAT1 (1 << 3) /* AIF2 ADCDAT2 */ +#define WM8994_AIF3_ADCDAR_SRC_GPIO5_DACDAT2 (2 << 3) /* GPIO5/DACDAT2 */ +#define WM8994_AIF3_TRI (1 << 5) /* Bit 5: AIF3 Audio Interface tri-state */ +#define WM8994_AIF3_TRI_NO (0) /* AIF3 pins operate normally */ +#define WM8994_AIF3_TRI_YES (WM8994_AIF3_TRI) /* Tri-State all AIF3 interface pins */ + +/* R21 (0x15) - Input Mixer (1) + */ + + /* Bits 0-5: Reserved */ +#define WM8994_INPUTS_CLAMP (1 << 6) /* Bit 6: Input pad VMID clamp */ +#define WM8994_INPUTS_CLAMP_DE_ACTIVATED (0) /* Clamp de-activated */ +#define WM8994_INPUTS_CLAMP_ACTIVATED (WM8994_INPUTS_CLAMP) /* Clamp activated */ +#define WM8994_IN1LP_MIXINL_BOOST (1 << 7) /* Bit 7: IN1LP Pin (PGA Bypass) to MIXINL Gain Boost. The bit seletcs the maximum gain setting of the IN1LP_MIXINL_VOL register. */ +#define WM8994_IN1LP_MIXINL_BOOST_P_6dB (0) /* Maximum gain is +6dB */ +#define WM8994_IN1LP_MIXINL_BOOST_P_15dB (WM8994_IN1LP_MIXINL_BOOST) /* Maximu gain is +15dB */ +#define WM8994_IN1RP_MIXINR_BOOST (1 << 8) /* Bit 8: IN1RP Pin (PGA Bypass) to MIXINR Gain Boost. The bit seletcs the maximum gain setting of the IN1RP_MIXINR_VOL register. */ +#define WM8994_IN1RP_MIXINR_BOOST_P_6dB (0) /* Maximum gain is +6dB */ +#define WM8994_IN1RP_MIXINR_BOOST_P_15dB (WM8994_IN1RP_MIXINR_BOOST) /* Maximu gain is +15dB */ + /* Bits 9-15: Reserved */ + +/* R24 (0x18) - Left Line Input 1&2 Volume + */ + +#define WM8994_IN1L_VOL (0) /* Bits 0-4: IN1L Volume */ +#define WM8994_IN1L_VOL_MIN (0 << 0) /* -16.5dB */ +#define WM8994_IN1L_VOL_DEFAULT (11 << 0) /* -16.5dB to +30dB in 1.5dB steps */ +#define WM8994_IN1L_VOL_MAX (31 << 0) /* +30dB */ + /* Bit 5: Reserved */ +#define WM8994_IN1L_ZC (1 << 6) /* Bit 6: IN1L PGA Zero Cross Dectector */ +#define WM8994_IN1L_ZC_NO (0) /* Change gain immediately */ +#define WM8994_IN1L_ZC_YES (WM8994_IN1L_ZC) /* Change gain on zero cross only */ +#define WM8994_IN1L_MUTE (1 << 7) /* Bit 7: IN1L PGA Mute */ +#define WM8994_IN1L_MUTE_DISABLE (0) /* Disabled */ +#define WM8994_IN1L_MUTE_ENABLE (WM8994_IN1L_MUTE) /* Enabled */ +#define WM8994_IN1_VU (1 << 8) /* Bit 8: Input PGA Voluem Update. Writing a 1 to this bit cause IN1L and IN1R input PGA volumes to updated simultaneously */ + + /* Bits 9-15: Reserved */ + +/* R25 (0x19) - Left Line Input 3&4 Volume + */ + +#define WM8994_IN2L_VOL_SHIFT (0) /* Bits 0-4: IN2L Volume */ +#define WM8994_IN2L_VOL_MIN (0 << WM8994_IN2L_VOL_SHIFT) /* -16.5dB */ +#define WM8994_IN2L_VOL_DEFAULT (11 << WM8994_IN2L_VOL_SHIFT) /* -16.5dB to +30dB in 1.5dB steps */ +#define WM8994_IN2L_VOL_MAX (31 << WM8994_IN2L_VOL_SHIFT) /* +30dB */ + /* Bit 5: Reserved */ +#define WM8994_IN2L_ZC (1 << 6) /* Bit 6: IN2L PGA Zero Cross Dectector */ +#define WM8994_IN2L_ZC_NO (0) /* Change gain immediately */ +#define WM8994_IN2L_ZC_YES (WM8994_IN2L_ZC) /* Change gain on zero cross only */ +#define WM8994_IN2L_MUTE (1 << 7) /* Bit 7: IN2L PGA Mute */ +#define WM8994_IN2L_MUTE_DISABLE (0) /* Disabled */ +#define WM8994_IN2L_MUTE_ENABLE (WM8994_IN2L_MUTE) /* Enabled */ +#define WM8994_IN2_VU (1 << 8) /* Bit 8: Input PGA Voluem Update. Writing a 1 to this bit cause IN2L and IN2R input PGA volumes to updated simultaneously */ + + /* Bits 9-15: Reserved */ + +/* R26 (0x1A) - Right Line Input 1&2 Volume + */ + +#define WM8994_IN1R_VOL_SHIFT (0) /* Bits 0-4: IN1R Volume */ +#define WM8994_IN1R_VOL_MIN (0 << WM8994_IN1R_VOL_SHIFT) /* -16.5dB */ +#define WM8994_IN1R_VOL_DEFAULT (11 << WM8994_IN1R_VOL_SHIFT) /* -16.5dB to +30dB in 1.5dB steps */ +#define WM8994_IN1R_VOL_MAX (31 << WM8994_IN1R_VOL_SHIFT) /* +30dB */ + /* Bit 5: Reserved */ +#define WM8994_IN1R_ZC_SHIFT (6) /* Bit 6: IN1R PGA Zero Cross Dectector */ +#define WM8994_IN1R_ZC_NO (0) /* Change gain immediately */ +#define WM8994_IN1R_ZC_YES (1 << WM8994_IN1R_ZC_SHIFT) /* Change gain on zero cross only */ +#define WM8994_IN1R_MUTE_SHIFT (7) /* Bit 7: IN1R PGA Mute */ +#define WM8994_IN1R_MUTE_DISABLE (0) /* Disabled */ +#define WM8994_IN1R_MUTE_ENABLE (WM8994_IN1R_MUTE_SHIFT) /* Enabled */ +#if 0 +#define WM8994_IN1_VU (1 << 8) /* Bit 8: Input PGA Voluem Update. Writing a 1 to this bit cause IN1L and IN1R input PGA volumes to updated simultaneously */ +#endif + /* Bits 9-15: Reserved */ + +/* R27 (0x1B) - Right Line Input 3&4 Volume + */ + +#define WM8994_IN2R_VOL_SHIFT (0) /* Bits 0-4: IN2R Volume */ +#define WM8994_IN2R_VOL_MIN (0 << WM8994_IN2R_VOL_SHIFT) /* -16.5dB */ +#define WM8994_IN2R_VOL_DEFAULT (11 << WM8994_IN2R_VOL_SHIFT) /* -16.5dB to +30dB in 1.5dB steps */ +#define WM8994_IN2R_VOL_MAX (31 << WM8994_IN2R_VOL_SHIFT) /* +30dB */ + /* Bit 5: Reserved */ +#define WM8994_IN2R_ZC_SHIFT (6) /* Bit 6: IN2R PGA Zero Cross Dectector */ +#define WM8994_IN2R_ZC_NO (0) /* Change gain immediately */ +#define WM8994_IN2R_ZC_YES (1 << WM8994_IN2R_ZC_SHIFT) /* Change gain on zero cross only */ +#define WM8994_IN2R_MUTE_SHIFT (7) /* Bit 7: IN2R PGA Mute */ +#define WM8994_IN2R_MUTE_DISABLE (0) /* Disabled */ +#define WM8994_IN2R_MUTE_ENABLED (1 << WM8994_IN2R_MUTE_SHIFT) /* Enabled */ +#if 0 +#define WM8994_IN2_VU (1 << 8) /* Bit 8: Input PGA Voluem Update. Writing a 1 to this bit cause IN2L and IN2R input PGA volumes to updated simultaneously */ +#endif + +/* R28 (0x1C) - Left Output Volume + */ + +#define WM8994_HPOUT1L_VOL_SHIFT (0) /* Bits 0-5: HPOUT1LVOL (Left Headphone Output PGA) Volume */ +#define WM8994_HPOUT1L_VOL_MIN (0 << WM8994_HPOUT1L_VOL_SHIFT) /* -57dB */ +#define WM8994_HPOUT1L_VOL_DEFAULT (0 << WM8994_HPOUT1L_VOL_SHIFT) /* -57dB to +6dB in 1 dB steps*/ +#define WM8994_HPOUT1L_VOL_MAX (0x3F << WM8994_HPOUT1L_VOL_SHIFT) /* +6dB */ +#define WM8994_HPOUT1L_MUTE_N_SHIFT (6) /* Bit 6: HPOUT1LVOL (Left Headphone Output PGA) Mute */ +#define WM8994_HPOUT1L_MUTE_N_YES (0) /* Mute */ +#define WM8994_HPOUT1L_MUTE_N_NO (1 << WM8994_HPOUT1L_MUTE_N_SHIFT) /* Un-Mute */ +#define WM8994_HPOUT1L_ZC_SHIFT (7) /* Bit 7: HPOUT1LVOL (Left Headphone Output PGA) Zero Cross */ +#define WM8994_HPOUT1L_ZC_DIABLED (0) /* Zero cross disabled */ +#define WM8994_HPOUT1L_ZC_ENABLED (WM8994_HPOUT1L_ZC_SHIFT) /* Zero cross enabled */ +#define WM8994_HPOUT1_VU_SHIFT (8) /* Bit 8: Headphone Output PGA Volume Update */ +#define WM8994_HPOUT1_VU_DISABLE (0) +#define WM8994_HPOUT1_VU_ENABLED (WM8994_HPOUT1L_VU_SHIFT) /* Writing a 1 to this bit will update HPOUT1LVOL and + * HPOUT1RVOL volumes simultaneously */ + +/* R29 (0x1D) - Right Output Volume + */ + +#define WM8994_HPOUT1R_VOL_SHIFT (0) /* Bits 0-5: HPOUT1RVOL (Right Headphone Output PGA) Volume */ +#define WM8994_HPOUT1R_VOL_MIN (0 << WM8994_HPOUT1R_VOL_SHIFT) /* -57dB */ +#define WM8994_HPOUT1R_VOL_DEFAULT (0 << WM8994_HPOUT1R_VOL_SHIFT) /* -57dB to +6dB in 1 dB steps*/ +#define WM8994_HPOUT1R_VOL_MAX (0x3F << WM8994_HPOUT1R_VOL_SHIFT) /* +6dB */ +#define WM8994_HPOUT1R_MUTE_N_SHIFT (6) /* Bit 6: HPOUT1RVOL (Left Headphone Output PGA) Mute */ +#define WM8994_HPOUT1R_MUTE_N_YES (0) /* Mute */ +#define WM8994_HPOUT1R_MUTE_N_NO (1 << WM8994_HPOUT1R_MUTE_N_SHIFT) /* Un-Mute */ +#define WM8994_HPOUT1R_ZC_SHIFT (7) /* Bit 7: HPOUT1RVOL (Left Headphone Output PGA) Zero Cross */ +#define WM8994_HPOUT1R_ZC_DIABLED (0) /* Zero cross disabled */ +#define WM8994_HPOUT1R_ZC_ENABLED (WM8994_HPOUT1R_ZC_SHIFT) /* Zero cross enabled */ +#if 0 +#define WM8994_HPOUT1_VU_SHIFT (1 << 8) /* Bit 8: Headphone Output PGA Volume Update */ +#define WM8994_HPOUT1_VU_DISABLE (0) +#define WM8994_HPOUT1_VU_ENABLED (WM8994_HPOUT1L_VU_SHIFT) /* Writing a 1 to this bit will update HPOUT1LVOL and + * HPOUT1RVOL volumes simultaneously */ +#endif + +/* R30 (0x1E) - Line Outputs Volume + */ + +#define WM8994_LINEOUT2_VOL_SHIFT (0) /* LINEOUT2 Line Output Volume */ +#define WM8994_LINEOUT2_VOL_0dB (0) /* 0dB */ +#define WM8994_LINEOUT2_VOL_n6dB (1 << WM8994_LINEOUT2_VOL_SHIFT) /* -6dB */ +#define WM8994_LINEOUT2P_MUTE (1) /* LINEOUT2P Line Output Mute */ + +/* R31 (0x1F) - HPOUT2 Volume + */ + +/* R32 (0x20) - Left OPGA Volume + */ + +/* R33 (0x21) - Right OPGA Volume + */ + +/* R34 (0x22) - SPKMIXL Attenuation + */ + +/* R35 (0x23) - SPKMIXR Attenuation + */ + +/* R36 (0x24) - SPKOUT Mixers + */ + +/* R37 (0x25) - ClassD + */ + +/* R38 (0x26) - Speaker Volume Left + */ + +/* R39 (0x27) - Speaker Volume Right + */ + +/* R40 (0x28) - Input Mixer (2) + */ + +/* R41 (0x29) - Input Mixer (3) + */ + +/* R42 (0x2A) - Input Mixer (4) + */ + +/* R43 (0x2B) - Input Mixer (5) + */ + +/* R44 (0x2C) - Input Mixer (6) + */ + +/* R45 (0x2D) - Output Mixer (1) + */ + +#define WM8994_DAC1L_TO_MIXOUTL (1 << 0) /* Bit 0: Left DAC1 to MIXOUTL Mute */ +#define WM8994_DAC1L_TO_MIXOUTL_MUTE (0) /* Mute */ +#define WM8994_DAC1L_TO_MIXOUTL_UNMUTE (WM8994_DAC1L_TO_MIXOUTL) /* Un-mute */ +#define WM8994_IN2LP_TO_MIXOUTL (1 << 1) /* Bit 1: IN2LP to MIXOUTL Mute */ +#define WM8994_IN2LP_TO_MIXOUTL_MUTE (0) /* Mute */ +#define WM8994_IN2LP_TO_MIXOUTL_UNMUTE (WM8994_IN2LP_TO_MIXOUTL) /* Un-mute */ +#define WM8994_IN1L_TO_MIXOUTL (1 << 2) /* Bit 2: IN1L PGA Output to MIXOUTL Mute */ +#define WM8994_IN1L_TO_MIXOUTL_MUTE (0) /* Mute */ +#define WM8994_IN1L_TO_MIXOUTL_UNMUTE (WM8994_IN1L_TO_MIXOUTL_MUTE) /* Un-mute */ +#define WM8994_IN1R_TO_MIXOUTL (1 << 3) /* Bit 3: IN1R PGA Output to MIXOUTL Mute */ +#define WM8994_IN1R_TO_MIXOUTL_MUTE (0) /* Mute */ +#define WM8994_IN1R_TO_MIXOUTL_UNMUTE (WM8994_IN1R_TO_MIXOUTL_MUTE) /* Un-mute */ +#define WM8994_IN2LN_TO_MIXOUTL (1 << 4) /* Bit 4: IN2LN to MIXOUTL Mute */ +#define WM8994_IN2LN_TO_MIXOUTL_MUTE (0) /* Mute */ +#define WM8994_IN2LN_TO_MIXOUTL_UNMUTE (WM8994_IN2LN_TO_MIXOUTL) /* Un-mute */ +#define WM8994_IN2RN_TO_MIXOUTL (1 << 5) /* Bit 5: IN2RN to MIXOUTL Mute */ +#define WM8994_IN2RN_TO_MIXOUTL_MUTE (0) /* Mute */ +#define WM8994_IN2RN_TO_MIXOUTL_UNMUTE (WM8994_IN2RN_TO_MIXOUTL) /* Un-mute */ +#define WM8994_MIXINL_TO_MIXOUTL (1 << 6) /* Bit 6: MIXINL Output(Left ADC bypass) to MIXOUTL Mute */ +#define WM8994_MIXINL_TO_MIXOUTL_MUTE (0) /* mute */ +#define WM8994_MIXINL_TO_MIXOUTL_UNMUTE (WM8994_MIXINL_TO_MIXOUTL) /* Un-mute */ +#define WM8994_MIXINR_TO_MIXOUTL (1 << 7) /* Bit 7: MIXINR Output(Left ADC bypass) to MIXOUTL Mute */ +#define WM8994_MIXINR_TO_MIXOUTL_MUTE (0) /* mute */ +#define WM8994_MIXINR_TO_MIXOUTL_UNMUTE (WM8994_MIXINR_TO_MIXOUTL) /* Un-mute */ +#define WM8994_DAC1L_TO_HPOUT1L (1 << 8) /* Bit 8: HPOUT1LVOL(Left Headphone Output PGA) Input Select */ +#define WM8994_DAC1L_TO_HPOUT1L_MIXOUTL (0) /* MIXOUTL */ +#define WM8994_DAC1L_TO_HPOUT1L_DAC1L (WM8994_DAC1L_TO_HPOUT1L) /* DAC1L */ + /* Bits 9-15: Reserved */ + +/* R46 (0x2E) - Output Mixer (2) + */ + +/* R47 (0x2F) - Output Mixer (3) + */ + +/* R48 (0x30) - Output Mixer (4) + */ + +/* R49 (0x31) - Output Mixer (5) + */ + +/* R50 (0x32) - Output Mixer (6) + */ + +/* R51 (0x33) - HPOUT2 Mixer + */ + +/* R52 (0x34) - Line Mixer (1) + */ + +/* R53 (0x35) - Line Mixer (2) + */ + +/* R54 (0x36) - Speaker Mixer + */ + +/* R55 (0x37) - Additional Control + */ + +/* R56 (0x38) - AntiPOP (1) + */ + +/* R57 (0x39) - AntiPOP (2) + */ + + /* Bits 8-15: Reserved */ +#define WM8994_MICB2_DISCH (1 << 8) /* Bit 7: Microphone Bias 2 Discharge */ +#define WM8994_MICB2_DISCH_FLOAT (0) /* MICBIAS2 floating when disabled */ +#define WM8994_MICB2_DISCH_DISCHARGED WM8994_MICB2_DISCH /* MICBIAS2 disharged when disabled */ +#define WM8994_MICB1_DISCH (1 << 7) /* Bit 7: Microphone Bias 1 Discharge */ +#define WM8994_MICB1_DISCH_FLOAT (0) /* MICBIAS1 floating when disabled */ +#define WM8994_MICB1_DISCH_DISCHARGED WM8994_MICB1_DISCH /* MICBIAS1 disharged when disabled */ + +#define WM8994_VMID_DISCH (1 << 0) /* Bit 0:Connects VMID to ground */ +#define WM8994_VMID_DISCH_DISABLE (0) /* Disabled */ +#define WM8994_VMID_DISCH_ENABLE WM8994_VMID_DISCH /* Enabled */ +#define WM8994_BIAS_SRC (1 << 1) /* Bit 1: Selects the bias current source */ +# define WM8994_BIAS_SRC_NORMAL_BIAS (0) /* Normal bias */ +# define WM8994_BIAS_SRC_STARTUP_BIAS WM8994_BIAS_SRC /* Start-Up bias */ +#define WM8994_STARTUP_BIAS_ENA (1 << 2) /* Bit 2: Enables the Start-Up bias current generator */ +#define WM8994_VMID_BUF_ENA (1 << 3) /* Bit 3: VMID Buffer Enable */ +#define WM8994_VMID_RAMP_SHIFT (5) /* Bits 5-6: VMID soft start enable/slew rate control */ +#define WM8994_VMID_RAMP_MASK (3 << WM8994_VMID_RAMP_SHIFT) +#define WM8994_VMID_RAMP_NORMAL_SLOW_START (0 << WM8994_VMID_RAMP_SHIFT) /* Normal slow start */ +#define WM8994_VMID_RAMP_NORMAL_FAST_START (1 << WM8994_VMID_RAMP_SHIFT) /* Normal fast start */ +#define WM8994_VMID_RAMP_SOFT_SLOW_START (2 << WM8994_VMID_RAMP_SHIFT) /* Soft slow start */ +#define WM8994_VMID_RAMP_SOFT_FAST_START (3 << WM8994_VMID_RAMP_SHIFT) /* Soft fast start */ + +/* R58 (0x3A) - MICBIAS + */ + +/* R59 (0x3B) - LDO 1 + */ + +/* R60 (0x3C) - LDO 2 + */ + +/* R61 (0x3D) - MICBIAS1 + */ + +/* R62 (0x3E) - MICBIAS2 + */ + +/* R210 (0xD2) - Mic Detect 3 + */ + +/* R76 (0x4C) - Charge Pump (1) + */ + +/* R77 (0x4D) - Charge Pump (2) + */ + +/* R81 (0x51) - Class W (1) + */ + +/* R84 (0x54) - DC Servo (1) + */ + +/* R85 (0x55) - DC Servo (2) + */ + +/* R87 (0x57) - DC Servo (4) + */ + +/* R88 (0x58) - DC Servo Readback + */ + +/* R96 (0x60) - Analogue HP (1) + */ + +/* R208 (0xD0) - Mic Detect 1 + */ + +/* R209 (0xD1) - Mic Detect 2 + */ + +/* R210 (0xD2) - Mic Detect 3 + */ + +/* R256 (0x100) - Chip Revision + */ + +/** R257 (0x101) - Control Interface + */ + +/* R272 (0x110) - Write Sequencer Ctrl (1) + */ + +/* R273 (0x111) - Write Sequencer Ctrl (2) + */ + +/* R512 (0x200) - AIF1 Clocking (1) + */ + +#define WM8994_AIF1CLK_ENA (1 << 0) /* Bit 0: AIF1CLK Enable */ +#define WM8994_AIF1CLK_DIV (1 << 1) /* Bit 1: AIF1CLK Divider */ +#define WM8994_AIF1CLK_DIV_0 (0) /* AIF1CLK/1 */ +#define WM8994_AIF1CLK_DIV_1 (WM8994_AIF1CLK_DIV) /* AIF1CLK/2 */ +#define WM8994_AIF1CLK_INV (1 << 2) /* Bit 2: AIF1CLK Invert */ +#define WM8994_AIF1CLK_INV_NOT (0) /* AIF1CLK not inverted */ +#define WM8994_AIF1CLK_INV_YES (WM8994_AIF1CLK_INV) /* AIF1CLK inverted */ +#define WM8994_AIF1CLK_SRC_SHIFT (3) /* Bit 3-4: AIF1CLK Source Select */ +#define WM8994_AIF1CLK_SRC_MASK (3 << WM8994_AIF1CLK_SRC_SHIFT) +#define WM8994_AIF1CLK_SRC_MCLK1 (0 << WM8994_AIF1CLK_SRC_SHIFT) /* MCLK1 */ +#define WM8994_AIF1CLK_SRC_MCLK2 (1 << WM8994_AIF1CLK_SRC_SHIFT) /* MCLK2 */ +#define WM8994_AIF1CLK_SRC_PLL1 (2 << WM8994_AIF1CLK_SRC_SHIFT) /* PLL1 */ +#define WM8994_AIF1CLK_SRC_PLL2 (3 << WM8994_AIF1CLK_SRC_SHIFT) /* PLL2 */ + /* Bits 5-15: Reserved */ + +/* R513 (0x201) - AIF1 Clocking (2) + */ + +/* R516 (0x204) - AIF2 Clocking (1) + */ + +/* R517 (0x205) - AIF2 Clocking (2) + */ + +/* R520 (0x208) - Clocking (1) + */ + +#define WM8994_SYSCLK_SRC (1 << 0) /* Bit 0: SYSCLK Source Select */ +#define WM8994_SYSCLK_SRC_AIF1CLK (0) /* AIF1CLK */ +#define WM8994_SYSCLK_SRC_AIF2CLK (WM8994_SYSCLK_SRC) /* AIF2CLK */ +#define WM8994_SYSDSPCLK_ENA (1 << 1) /* Bit 1: Digital Mixing Processor Clock Enable */ +#define WM8994_AIF2DSPCLK_ENA (1 << 2) /* Bit 2: AIF2 Processor Clock Enable */ +#define WM8994_AIF1DSPCLK_ENA (1 << 3) /* Bit 3: AIF1 Processor Clock Enable */ +#define WM8994_TOCLK_ENA (1 << 4) /* Bit 4: Slow Clock(TOCLK) Enable */ + /* Bits 5-15: Reserved */ + +/* R521 (0x209) - Clocking (2) + */ + +/* R528 (0x210) - AIF1 Rate + */ + +#define WM8994_AIF1CLK_RATE_SHIFT (0) /* Bits 0-3: Selects the AIF1CLK/fs ratio */ +#define WM8994_AIF1CLK_RATE_MASK (0xf << WM8994_AIF1CLK_RATE_SHIFT) +#define WM8994_AIF1CLK_RATE_0 (0 << WM8994_AIF1CLK_RATE_SHIFT) /* Reserved */ +#define WM8994_AIF1CLK_RATE_1 (1 << WM8994_AIF1CLK_RATE_SHIFT) /* 128 */ +#define WM8994_AIF1CLK_RATE_2 (2 << WM8994_AIF1CLK_RATE_SHIFT) /* 192 */ +#define WM8994_AIF1CLK_RATE_3 (3 << WM8994_AIF1CLK_RATE_SHIFT) /* 256 */ +#define WM8994_AIF1CLK_RATE_4 (4 << WM8994_AIF1CLK_RATE_SHIFT) /* 384 */ +#define WM8994_AIF1CLK_RATE_5 (5 << WM8994_AIF1CLK_RATE_SHIFT) /* 512 */ +#define WM8994_AIF1CLK_RATE_6 (6 << WM8994_AIF1CLK_RATE_SHIFT) /* 768 */ +#define WM8994_AIF1CLK_RATE_7 (7 << WM8994_AIF1CLK_RATE_SHIFT) /* 1024 */ +#define WM8994_AIF1CLK_RATE_8 (8 << WM8994_AIF1CLK_RATE_SHIFT) /* 1408 */ +#define WM8994_AIF1CLK_RATE_9 (9 << WM8994_AIF1CLK_RATE_SHIFT) /* 1536 */ +#define WM8994_AIF1_SR_SHIFT (4) /* Bits 4-7: Selects the AIF1 Sample Rate (fs) */ +#define WM8994_AIF1_SR_MASK (0xf << WM8994_AIF1CLK_RATE_SHIFT) +#define WM8994_AIF1_SR_8K (0 << WM8994_AIF1CLK_RATE_SHIFT) /* 8kHz */ +#define WM8994_AIF1_SR_11K (1 << WM8994_AIF1CLK_RATE_SHIFT) /* 11.025kHz */ +#define WM8994_AIF1_SR_12K (2 << WM8994_AIF1CLK_RATE_SHIFT) /* 12kHz */ +#define WM8994_AIF1_SR_16K (3 << WM8994_AIF1CLK_RATE_SHIFT) /* 16kHz */ +#define WM8994_AIF1_SR_22K (4 << WM8994_AIF1CLK_RATE_SHIFT) /* 22.05kHz */ +#define WM8994_AIF1_SR_24K (5 << WM8994_AIF1CLK_RATE_SHIFT) /* 24kHz */ +#define WM8994_AIF1_SR_32K (6 << WM8994_AIF1CLK_RATE_SHIFT) /* 32kHz */ +#define WM8994_AIF1_SR_44K (7 << WM8994_AIF1CLK_RATE_SHIFT) /* 44.1kHz */ +#define WM8994_AIF1_SR_48K (8 << WM8994_AIF1CLK_RATE_SHIFT) /* 48kHz */ +#define WM8994_AIF1_SR_88K (9 << WM8994_AIF1CLK_RATE_SHIFT) /* 88.2kHz */ +#define WM8994_AIF1_SR_96K (10 << WM8994_AIF1CLK_RATE_SHIFT) /* 96kHz */ + /* Bits 8-15: Reserved */ + +/* R529 (0x211) - AIF2 Rate + */ + +/* R530 (0x212) - Rate Status + */ + +/* R544 (0x220) - FLL1 Control (1) + */ + +/* R545 (0x221) - FLL1 Control (2) + */ + +/* R546 (0x222) - FLL1 Control (3) + */ + +/* R547 (0x223) - FLL1 Control (4) + */ + +/* R548 (0x224) - FLL1 Control (5) + */ + +/* R550 (0x226) - FLL1 EFS 1 + */ + +/* R551 (0x227) - FLL1 EFS 2 + */ + +/* R576 (0x240) - FLL2 Control (1) + */ + +/* R577 (0x241) - FLL2 Control (2) + */ + +/* R578 (0x242) - FLL2 Control (3) + */ + +/* R579 (0x243) - FLL2 Control (4) + */ + +/* R580 (0x244) - FLL2 Control (5) + */ + +/* R582 (0x246) - FLL2 EFS 1 + */ + +/* R583 (0x247) - FLL2 EFS 2 + */ + +/* R768 (0x300) - AIF1 Control (1) + */ + + /* Bits 0-2: Reserved */ +#define WM8994_AIF1_FMT_SHIFT (3) /* Bits 3-4: AIF1 Digital Audio Interface Format */ +#define WM8994_AIF1_FMT_MASK (3 << WM8994_AIF1_FMT_SHIFT) +#define WM8994_AIF1_FMT_RIGHT (0 << WM8994_AIF1_FMT_SHIFT) /* Right justified */ +#define WM8994_AIF1_FMT_LEFT (1 << WM8994_AIF1_FMT_SHIFT) /* Left justified */ +#define WM8994_AIF1_FMT_I2S (2 << WM8994_AIF1_FMT_SHIFT) /* I2S Format */ +#define WM8994_AIF1_FMT_DSP (3 << WM8994_AIF1_FMT_SHIFT) /* DSP Mode */ +#define WM8994_AIF1_WL_SHIFT (5) /* Bits 5-6: AIF1 Digital Audio Interface Word Length */ +#define WM8994_AIF1_WL_MASK (3 << WM8994_AIF1_WL_SHIFT) +#define WM8994_AIF1_WL_16BITS (0 << WM8994_AIF1_WL_SHIFT) /* 16 bits */ +#define WM8994_AIF1_WL_20BITS (1 << WM8994_AIF1_WL_SHIFT) /* 20 bits */ +#define WM8994_AIF1_WL_24BITS (2 << WM8994_AIF1_WL_SHIFT) /* 24 bits */ +#define WM8994_AIF1_WL_32BITS (3 << WM8994_AIF1_WL_SHIFT) /* 32 bits */ +#define WM8994_AIF1ADC_TDM (1 << 13) /* Bit 13: AIF1 transmit (ADC) TDM control */ +#define WM8994_AIF1ADC_TDM_0 (0) /* ADCDAT1 driver logic '0' when not transmit data */ +#define WM8994_AIF1ADC_TDM_TRI (WM8994_AIF1ADC_TDM) /* ADCDAT1 is tri-stated when not transmit data */ +#define WM8994_AIF1ADCR_SRC (1 << 14) /* Bit 14: AIF1 Right Audio Interface Source */ +#define WM8994_AIF1ADCR_LEFT_ADC (0) /* Left ADC data is output on right channel */ +#define WM8994_AIF1ADCR_RIGHT_ADC (WM8994_AIF1ADCR_SRC) /* Right ADC data is output on right channel */ +#define WM8994_AIF1ADCL_SRC (1 << 15) /* Bit 15: AIF1 Left Audio Interface Source */ +#define WM8994_AIF1ADCL_LEFT_ADC (0) /* Left ADC data is output on left channel */ +#define WM8994_AIF1ADCL_RIGHT_ADC (WM8994_AIF1ADCL_SRC) /* Right ADC data is output on left channel */ + +/* R769 (0x301) - AIF1 Control (2) + */ + +/* R770 (0x302) - AIF1 Master/Slave + */ + + /* Bits 0-11: Reserved */ +#define WM8994_AIF1_LRCLK_FRC (1 << 12) /* Bit 12: Forces LRCLK1 and ADCLRCLK1 to enabled when all AIF1 audio channels are disabled */ +#define WM8994_AIF1_LRCLK_FRC_NORMAL (0) /* Normal */ +#define WM8994_AIF1_LRCLK_FRC_YES (WM8994_AIF1_LRCLK_FRC) /* LRCLK1 and ADCLRCLK1 always enabled in Master Mode */ +#define WM8994_AIF1_CLK_FRC (1 << 13) /* Bit 13: Forces BCLK1, LRCLK1 and ADCLRCLK1 to enabled when all AIF1 audio channels are disabled */ +#define WM8994_AIF1_CLK_FRC_NORMAL (0) /* Normal */ +#define WM8994_AIF1_CLK_FRC_YES (WM8994_AIF1_CLK_FRC) /* BLCK1, LRCLK1 and ADCLRCLK1 always enabled in Master Mode */ +#define WM8994_AIF1_MSTR (1 << 14) /* Bit 14: AIF1 Audio Interface Master Mode Select */ +#define WM8994_AIF1_MSTR_SLAVE_MODE (0) /* Slave Mode */ +#define WM8994_AIF1_MSTR_MASTER_MODE (WM8994_AIF1_MSTR) /* Master Mode */ +#define WM8994_AIF1_TRI (1 << 15) /* Bit 15: AIF1 Audio Interface tri-state */ +#define WM8994_AIF1_TRI_NORMAL (0) /* AIF1 pins operate normally */ +#define WM8994_AIF1_TRI_TRI (WM8994_AIF1_TRI) /* Tri-state all AIF1 interface pins */ + +/* R771 (0x303) - AIF1 BCLK + */ + +/* R772 (0x304) - AIF1ADC LRCLK + */ + +/* R773 (0x305) - AIF1DAC LRCLK + */ + +/* R774 (0x306) - AIF1DAC Data + */ + +/* R775 (0x307) - AIF1ADC Data + */ + +/* R784 (0x310) - AIF2 Control (1) + */ + +/* R785 (0x311) - AIF2 Control (2) + */ + +/* R786 (0x312) - AIF2 Master/Slave + */ + +/* R787 (0x313) - AIF2 BCLK + */ + +/* R788 (0x314) - AIF2ADC LRCLK + */ + +/* R789 (0x315) - AIF2DAC LRCLK + */ + +/* R790 (0x316) - AIF2DAC Data + */ + +/* R791 (0x317) - AIF2ADC Data + */ + +/* R800 (0x320) - AIF3 Control (1) + */ + +/* R801 (0x321) - AIF3 Control (2) + */ + +/* R802 (0x322) - AIF3DAC Data + */ + +/* R803 (0x323) - AIF3ADC Data + */ + +/* R1024 (0x400) - AIF1 ADC1 Left Volume + */ + +/* R1025 (0x401) - AIF1 ADC1 Right Volume + */ + +/* R1026 (0x402) - AIF1 DAC1 Left Volume + */ + +/* R1027 (0x403) - AIF1 DAC1 Right Volume + */ + +/* R1028 (0x404) - AIF1 ADC2 Left Volume + */ + +/* R1029 (0x405) - AIF1 ADC2 Right Volume + */ + +/* R1030 (0x406) - AIF1 DAC2 Left Volume + */ + +/* R1031 (0x407) - AIF1 DAC2 Right Volume + */ + +/* R1040 (0x410) - AIF1 ADC1 Filters + */ + +/* R1041 (0x411) - AIF1 ADC2 Filters + */ + +#define WM8994_AIF1ADC2_HPF_CUT_MASK 0x6000 /* AIF1ADC2_HPF_CUT - [14:13] */ +#define WM8994_AIF1ADC2_HPF_CUT_SHIFT 13 /* AIF1ADC2_HPF_CUT - [14:13] */ +#define WM8994_AIF1ADC2_HPF_CUT_WIDTH 2 /* AIF1ADC2_HPF_CUT - [14:13] */ +#define WM8994_AIF1ADC2L_HPF 0x1000 /* AIF1ADC2L_HPF */ +#define WM8994_AIF1ADC2L_HPF_MASK 0x1000 /* AIF1ADC2L_HPF */ +#define WM8994_AIF1ADC2L_HPF_SHIFT 12 /* AIF1ADC2L_HPF */ +#define WM8994_AIF1ADC2L_HPF_WIDTH 1 /* AIF1ADC2L_HPF */ +#define WM8994_AIF1ADC2R_HPF 0x0800 /* AIF1ADC2R_HPF */ +#define WM8994_AIF1ADC2R_HPF_MASK 0x0800 /* AIF1ADC2R_HPF */ +#define WM8994_AIF1ADC2R_HPF_SHIFT 11 /* AIF1ADC2R_HPF */ +#define WM8994_AIF1ADC2R_HPF_WIDTH 1 /* AIF1ADC2R_HPF */ + +/* R1056 (0x420) - AIF1 DAC1 Filters (1) + */ + +/* R1057 (0x421) - AIF1 DAC1 Filters (2) + */ + +/* R1058 (0x422) - AIF1 DAC2 Filters (1) + */ + +/* R1059 (0x423) - AIF1 DAC2 Filters (2) + */ + +/* R1072 (0x430) - AIF1 DAC1 Noise Gate + */ + +/* R1073 (0x431) - AIF1 DAC2 Noise Gate + */ + +/* R1088 (0x440) - AIF1 DRC1 (1) + */ + +/* R1089 (0x441) - AIF1 DRC1 (2) + */ + +/* R1090 (0x442) - AIF1 DRC1 (3) + */ + +/* R1091 (0x443) - AIF1 DRC1 (4) + */ + +/* R1092 (0x444) - AIF1 DRC1 (5) + */ + +/* R1104 (0x450) - AIF1 DRC2 (1) + */ + +/* R1105 (0x451) - AIF1 DRC2 (2) + */ + +/* R1106 (0x452) - AIF1 DRC2 (3) + */ + +/* R1107 (0x453) - AIF1 DRC2 (4) + */ + +/* R1108 (0x454) - AIF1 DRC2 (5) + */ + +/* R1152 (0x480) - AIF1 DAC1 EQ Gains (1) + */ + +/* R1153 (0x481) - AIF1 DAC1 EQ Gains (2) + */ + +/* R1154 (0x482) - AIF1 DAC1 EQ Band 1 A + */ + +/* R1155 (0x483) - AIF1 DAC1 EQ Band 1 B + */ + +/* R1156 (0x484) - AIF1 DAC1 EQ Band 1 PG + */ + +/* R1157 (0x485) - AIF1 DAC1 EQ Band 2 A + */ + +/* R1158 (0x486) - AIF1 DAC1 EQ Band 2 B + */ + +/* R1159 (0x487) - AIF1 DAC1 EQ Band 2 C + */ + +/* R1160 (0x488) - AIF1 DAC1 EQ Band 2 PG + */ + +/* R1161 (0x489) - AIF1 DAC1 EQ Band 3 A + */ + +/* R1162 (0x48A) - AIF1 DAC1 EQ Band 3 B + */ + +/* R1163 (0x48B) - AIF1 DAC1 EQ Band 3 C + */ + +/* R1164 (0x48C) - AIF1 DAC1 EQ Band 3 PG + */ + +/* R1165 (0x48D) - AIF1 DAC1 EQ Band 4 A + */ + +/* R1166 (0x48E) - AIF1 DAC1 EQ Band 4 B + */ + +/* R1167 (0x48F) - AIF1 DAC1 EQ Band 4 C + */ + +/* R1168 (0x490) - AIF1 DAC1 EQ Band 4 PG + */ + +/* R1169 (0x491) - AIF1 DAC1 EQ Band 5 A + */ + +/* R1170 (0x492) - AIF1 DAC1 EQ Band 5 B + */ + +/* R1171 (0x493) - AIF1 DAC1 EQ Band 5 PG + */ + +/* R1184 (0x4A0) - AIF1 DAC2 EQ Gains (1) + */ + +/* R1185 (0x4A1) - AIF1 DAC2 EQ Gains (2) + */ + +/* R1186 (0x4A2) - AIF1 DAC2 EQ Band 1 A + */ + +/* R1187 (0x4A3) - AIF1 DAC2 EQ Band 1 B + */ + +/* R1188 (0x4A4) - AIF1 DAC2 EQ Band 1 PG + */ + +/* R1189 (0x4A5) - AIF1 DAC2 EQ Band 2 A + */ + +/* R1190 (0x4A6) - AIF1 DAC2 EQ Band 2 B + */ + +/* R1191 (0x4A7) - AIF1 DAC2 EQ Band 2 C + */ + +/* R1192 (0x4A8) - AIF1 DAC2 EQ Band 2 PG + */ + +/* R1193 (0x4A9) - AIF1 DAC2 EQ Band 3 A + */ + +/* R1194 (0x4AA) - AIF1 DAC2 EQ Band 3 B + */ + +/* R1195 (0x4AB) - AIF1 DAC2 EQ Band 3 C + */ + +/* R1196 (0x4AC) - AIF1 DAC2 EQ Band 3 PG + */ + +/* R1197 (0x4AD) - AIF1 DAC2 EQ Band 4 A + */ + +/* R1198 (0x4AE) - AIF1 DAC2 EQ Band 4 B + */ + +/* R1199 (0x4AF) - AIF1 DAC2 EQ Band 4 C + */ + +/* R1200 (0x4B0) - AIF1 DAC2 EQ Band 4 PG + */ + +/* R1201 (0x4B1) - AIF1 DAC2 EQ Band 5 A + */ + +/* R1202 (0x4B2) - AIF1 DAC2 EQ Band 5 B + */ + +/* R1203 (0x4B3) - AIF1 DAC2 EQ Band 5 PG + */ + +/* R1280 (0x500) - AIF2 ADC Left Volume + */ + +/* R1281 (0x501) - AIF2 ADC Right Volume + */ + +/* R1282 (0x502) - AIF2 DAC Left Volume + */ + +/* R1283 (0x503) - AIF2 DAC Right Volume + */ + +/* R1296 (0x510) - AIF2 ADC Filters + */ + +/* R1312 (0x520) - AIF2 DAC Filters (1) + */ + +/* R1313 (0x521) - AIF2 DAC Filters (2) + */ + +/* R1328 (0x530) - AIF2 DAC Noise Gate + */ + +/* R1344 (0x540) - AIF2 DRC (1) + */ + +/* R1345 (0x541) - AIF2 DRC (2) + */ + +/* R1346 (0x542) - AIF2 DRC (3) + */ + +/* R1347 (0x543) - AIF2 DRC (4) + */ + +/* R1348 (0x544) - AIF2 DRC (5) + */ + +/* R1408 (0x580) - AIF2 EQ Gains (1) + */ + +/* R1409 (0x581) - AIF2 EQ Gains (2) + */ + +/* R1410 (0x582) - AIF2 EQ Band 1 A + */ + +/* R1411 (0x583) - AIF2 EQ Band 1 B + */ + +/* R1412 (0x584) - AIF2 EQ Band 1 PG + */ + +/* R1413 (0x585) - AIF2 EQ Band 2 A + */ + +/* R1414 (0x586) - AIF2 EQ Band 2 B + */ + +/* R1415 (0x587) - AIF2 EQ Band 2 C + */ + +/* R1416 (0x588) - AIF2 EQ Band 2 PG + */ + +/* R1417 (0x589) - AIF2 EQ Band 3 A + */ + +/* R1418 (0x58A) - AIF2 EQ Band 3 B + */ + +/* R1419 (0x58B) - AIF2 EQ Band 3 C + */ + +/* R1420 (0x58C) - AIF2 EQ Band 3 PG + */ + +/* R1421 (0x58D) - AIF2 EQ Band 4 A + */ + +/* R1422 (0x58E) - AIF2 EQ Band 4 B + */ + +/* R1423 (0x58F) - AIF2 EQ Band 4 C + */ + +/* R1424 (0x590) - AIF2 EQ Band 4 PG + */ + +/* R1425 (0x591) - AIF2 EQ Band 5 A + */ + +/* R1426 (0x592) - AIF2 EQ Band 5 B + */ + +/* R1427 (0x593) - AIF2 EQ Band 5 PG + */ + +/* R1536 (0x600) - DAC1 Mixer Volumes + */ + +/* R1537 (0x601) - DAC1 Left Mixer Routing + */ + +#define WM8994_AIF1DAC1L_TO_DAC1L_ENA (1 << 0) /* Bit 0: Enable AIF1(Timeslot 0, Left) to DAC1L */ +#define WM8994_AIF1DAC2L_TO_DAC1L_ENA (1 << 1) /* Bit 1: Enable AIF1(Timeslot 1, Left) to DAC1L */ +#define WM8994_AIF2DACL_TO_DAC1L_ENA (1 << 2) /* Bit 2: Enable AIF2(Left) to DAC1L */ + /* Bit 3: Reserved */ +#define WM8994_ADCL_TO_DAC1L_ENA (1 << 4) /* Bit 4: Enable Sidetone STL to DAC1L */ +#define WM8994_ADCR_TO_DAC1L_ENA (1 << 5) /* Bit 5: Enable Sidetone STR to DAC1L */ + /* Bits 6-15: Reserved */ + +/* R1538 (0x602) - DAC1 Right Mixer Routing + */ + +#define WM8994_AIF1DAC1R_TO_DAC1R_ENA (1 << 0) /* Bit 0: Enable AIF1(Timeslot 0, Right) to DAC1R */ +#define WM8994_AIF1DAC2R_TO_DAC1R_ENA (1 << 1) /* Bit 1: Enable AIF1(Timeslot 1, Right) to DAC1R */ +#define WM8994_AIF2DACR_TO_DAC1R_ENA (1 << 2) /* Bit 2: Enable AIF2(Right) to DAC1R */ + /* Bit 3: Reserved */ +#define WM8994_ADCL_TO_DAC1R_ENA (1 << 4) /* Bit 4: Enable Sidetone STL to DAC1R */ +#define WM8994_ADCR_TO_DAC1R_ENA (1 << 5) /* Bit 5: Enable Sidetone STR to DAC1R */ + /* Bits 6-15: Reserved */ + +/* R1539 (0x603) - DAC2 Mixer Volumes + */ + +/* R1540 (0x604) - DAC2 Left Mixer Routing + */ + +#define WM8994_AIF1DAC1L_TO_DAC2L_ENA (1 << 0) /* Bit 0: Enable AIF1(Timeslot 0, Left) to DAC2L */ +#define WM8994_AIF1DAC2L_TO_DAC2L_ENA (1 << 1) /* Bit 1: Enable AIF1(Timeslot 1, Left) to DAC2L */ +#define WM8994_AIF2DACL_TO_DAC2L_ENA (1 << 2) /* Bit 2: Enable AIF2(Left) to DAC2L */ + /* Bit 3: Reserved */ +#define WM8994_ADCL_TO_DAC2L_ENA (1 << 4) /* Bit 4: Enable Sidetone STL to DAC2L */ +#define WM8994_ADCR_TO_DAC2L_ENA (1 << 5) /* Bit 5: Enable Sidetone STR to DAC2L */ + /* Bits 6-15: Reserved */ + +/* R1541 (0x605) - DAC2 Right Mixer Routing + */ + +#define WM8994_AIF1DAC1R_TO_DAC2R_ENA (1 << 0) /* Bit 0: Enable AIF1(Timeslot 0, Right) to DAC2R */ +#define WM8994_AIF1DAC2R_TO_DAC2R_ENA (1 << 1) /* Bit 1: Enable AIF1(Timeslot 1, Right) to DAC2R */ +#define WM8994_AIF2DACR_TO_DAC2R_ENA (1 << 2) /* Bit 2: Enable AIF2(Right) to DAC2R */ + /* Bit 3: Reserved */ +#define WM8994_ADCL_TO_DAC2R_ENA (1 << 4) /* Bit 4: Enable Sidetone STL to DAC2R */ +#define WM8994_ADCR_TO_DAC2R_ENA (1 << 5) /* Bit 5: Enable Sidetone STR to DAC2R */ + /* Bits 6-15: Reserved */ + +/* R1542 (0x606) - AIF1 ADC1 Left Mixer Routing + */ + +/* R1543 (0x607) - AIF1 ADC1 Right Mixer Routing + */ + +/* R1544 (0x608) - AIF1 ADC2 Left Mixer Routing + */ + +/* R1545 (0x609) - AIF1 ADC2 Right mixer Routing + */ + +/* R1552 (0x610) - DAC1 Left Volume + */ + +/* R1553 (0x611) - DAC1 Right Volume + */ + +/* R1554 (0x612) - DAC2 Left Volume + */ + +/* R1555 (0x613) - DAC2 Right Volume + */ + +/* R1556 (0x614) - DAC Softmute + */ + +/* R1568 (0x620) - Oversampling + */ + +/* R1569 (0x621) - Sidetone + */ + +/* R1797 (0x705) - JACKDET Ctrl + */ + +/* R1824 (0x720) - Pull Control (1) + */ + +/* R1825 (0x721) - Pull Control (2) + */ + +/* R1840 (0x730) - Interrupt Status 1 + */ + +/* R1841 (0x731) - Interrupt Status 2 + */ + +/* R1842 (0x732) - Interrupt Raw Status 2 + */ + +/* R1848 (0x738) - Interrupt Status 1 Mask + */ + +/* R1849 (0x739) - Interrupt Status 2 Mask + */ + +/* R1856 (0x740) - Interrupt Control + */ + +/* R1864 (0x748) - IRQ Debounce + */ + +/* Register Default Values */ + +/* Registers have some undocumented bits set on power up. These probably + * should be retained on writes (?). + */ + +/* Register Bit Definitions */ + +/* 0x00 SW Reset and ID */ + +#define WM8994_SW_RST_DEV_ID1 0x8994 + +#define WM8994_AUDIO_FREQUENCY_8K 8000UL +#define WM8994_AUDIO_FREQUENCY_11_025K 11025UL +#define WM8994_AUDIO_FREQUENCY_16K 16000UL +#define WM8994_AUDIO_FREQUENCY_22_050K 22050UL +#define WM8994_AUDIO_FREQUENCY_32K 32000UL +#define WM8994_AUDIO_FREQUENCY_44_100K 44100UL +#define WM8994_AUDIO_FREQUENCY_48K 48000UL +#define WM8994_AUDIO_FREQUENCY_96K 96000UL +#define WM8994_AUDIO_FREQUENCY_192K 192000UL + +#define WM8994_DEFAULT_SAMPRATE (WM8994_AUDIO_FREQUENCY_48K) + +#define WM8994_DEFAULT_NCHANNELS 0 +#define WM8994_DEFAULT_BPSAMP 0 +#define WM8994_BCLK_MAXDIV 30 +#define WM8994_NFLLRATIO 30 + +/* Commonly defined and redefined macros */ + +#ifndef MIN +# define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +# define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +/*************************************************************************************************** + * Public Types + ***************************************************************************************************/ + +struct wm8994_dev_s +{ + /* We are an audio lower half driver (We are also the upper "half" of + * the WM8994 driver with respect to the board lower half driver). + * + * Terminology: Our "lower" half audio instances will be called dev for the + * publicly visible version and "priv" for the version that only this driver + * knows. From the point of view of this driver, it is the board lower + * "half" that is referred to as "lower". + */ + + struct audio_lowerhalf_s dev; /* WM8994 audio lower half (this device) */ + + /* Our specific driver data goes here */ + + const FAR struct wm8994_lower_s *lower; /* Pointer to the board lower functions */ + FAR struct i2c_master_s *i2c; /* I2C driver to use */ + FAR struct i2s_dev_s *i2s; /* I2S driver to use */ + struct dq_queue_s pendq; /* Queue of pending buffers to be sent */ + struct dq_queue_s doneq; /* Queue of sent buffers to be returned */ + mqd_t mq; /* Message queue for receiving messages */ + char mqname[16]; /* Our message queue name */ + pthread_t threadid; /* ID of our thread */ + uint32_t bitrate; /* Actual programmed bit rate */ + sem_t pendsem; /* Protect pendq */ +#ifdef WM8994_USE_FFLOCK_INT + struct work_s work; /* Interrupt work */ +#endif + uint16_t samprate; /* Configured samprate (samples/sec) */ +#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME +#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE + uint16_t balance; /* Current balance level (b16) */ +#endif /* CONFIG_AUDIO_EXCLUDE_BALANCE */ + uint8_t volume; /* Current volume level {0..63} */ +#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */ + uint8_t nchannels; /* Number of channels (1 or 2) */ + uint8_t bpsamp; /* Bits per sample (8 or 16) */ + volatile uint8_t inflight; /* Number of audio buffers in-flight */ +#ifdef WM8994_USE_FFLOCK_INT + volatile bool locked; /* FLL is locked */ +#endif + bool running; /* True: Worker thread is running */ + bool paused; /* True: Playing is paused */ + bool mute; /* True: Output is muted */ +#ifndef CONFIG_AUDIO_EXCLUDE_STOP + bool terminating; /* True: Stop requested */ +#endif + bool reserved; /* True: Device is reserved */ + volatile int result; /* The result of the last transfer */ + uint16_t power_mgnt_reg_1; +}; + +/*************************************************************************************************** + * Public Data + ***************************************************************************************************/ + +#ifdef CONFIG_WM8994_CLKDEBUG +extern const uint8_t g_sysclk_scaleb1[WM8994_BCLK_MAXDIV + 1]; +extern const uint8_t g_fllratio[WM8994_NFLLRATIO]; +#endif + +/*************************************************************************************************** + * Public Function Prototypes + ***************************************************************************************************/ + +/*************************************************************************************************** + * Name: wm8994_readreg + * + * Description: + * Read the specified 16-bit register from the WM8994 device. + * + ***************************************************************************************************/ + +#if defined(CONFIG_WM8994_REGDUMP) || defined(CONFIG_WM8994_CLKDEBUG) +struct wm8994_dev_s; +uint16_t wm8994_readreg(FAR struct wm8994_dev_s *priv, uint16_t regaddr); +#endif + +#endif /* CONFIG_AUDIO */ +#endif /* __DRIVERS_AUDIO_WM8994_H */ diff --git a/drivers/audio/wm8994_debug.c b/drivers/audio/wm8994_debug.c new file mode 100644 index 00000000000..3386136d0a3 --- /dev/null +++ b/drivers/audio/wm8994_debug.c @@ -0,0 +1,543 @@ +/***************************************************************************************** + * drivers/audio/wm8994_debug.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + *****************************************************************************************/ + +/***************************************************************************************** + * Included Files + *****************************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "wm8994.h" + +/***************************************************************************************** + * Pre-processor Definitions + *****************************************************************************************/ + +/***************************************************************************************** + * Private Types + *****************************************************************************************/ + +#ifdef CONFIG_WM8994_REGDUMP +struct wb8994_regdump_s +{ + FAR const char *regname; + uint16_t regaddr; +}; +#endif + +/***************************************************************************************** + * Private Function Prototypes + *****************************************************************************************/ + +/***************************************************************************************** + * Private Data + *****************************************************************************************/ + +#ifdef CONFIG_WM8994_REGDUMP +static const struct wb8994_regdump_s g_wm8994_debug[] = +{ + {"ID", WM8994_ID}, + {"PM1", WM8994_PM1}, + {"PM2", WM8994_PM2}, + {"PM3", WM8994_PM3}, + {"PM4", WM8994_PM4}, + {"PM5", WM8994_PM5}, + {"PM6", WM8994_PM6}, + {"INPUT_MIXER1", WM8994_INPUT_MIXER1}, + {"LEFTLINE_12_VOL", WM8994_LEFTLINE_12_VOL}, + {"LEFTLINE_34_VOL", WM8994_LEFTLINE_34_VOL}, + {"RIGHTLINE_12_VOL", WM8994_RIGHTLINE_12_VOL}, + {"RIGHTLINE_34_VOL", WM8994_RIGHTLINE_34_VOL}, + {"LEFT_OUTPUT_VOL", WM8994_LEFT_OUTPUT_VOL}, + {"RIGHT_OUTPUT_VOL", WM8994_RIGHT_OUTPUT_VOL}, + {"LINE_OUTPUTS_VOL", WM8994_LINE_OUTPUTS_VOL}, + {"HPOUT2_VOL", WM8994_HPOUT2_VOL}, + {"LEFT_OPGA_VOL", WM8994_LEFT_OPGA_VOL}, + {"RIGHT_OPGA_VOL", WM8994_RIGHT_OPGA_VOL}, + {"SPKMIXL_ATT", WM8994_SPKMIXL_ATT}, + {"SPKMIXR_ATT", WM8994_SPKMIXR_ATT}, + {"SPKOUT_MIXERS", WM8994_SPKOUT_MIXERS}, + {"CLASS_D", WM8994_CLASS_D}, + {"SPEAKER_VOL_LEFT", WM8994_SPEAKER_VOL_LEFT}, + {"SPEAKER_VOL_RIGHT", WM8994_SPEAKER_VOL_RIGHT}, + {"INPUT_MIXER2", WM8994_INPUT_MIXER2}, + {"INPUT_MIXER3", WM8994_INPUT_MIXER3}, + {"INPUT_MIXER4", WM8994_INPUT_MIXER4}, + {"INPUT_MIXER5", WM8994_INPUT_MIXER5}, + {"INPUT_MIXER6", WM8994_INPUT_MIXER6}, + {"OUTPUT_MIXER1", WM8994_OUTPUT_MIXER1}, + {"OUTPUT_MIXER2", WM8994_OUTPUT_MIXER2}, + {"OUTPUT_MIXER3", WM8994_OUTPUT_MIXER3}, + {"OUTPUT_MIXER4", WM8994_OUTPUT_MIXER4}, + {"OUTPUT_MIXER5", WM8994_OUTPUT_MIXER5}, + {"OUTPUT_MIXER6", WM8994_OUTPUT_MIXER6}, + {"HPOUT2_MIXER", WM8994_HPOUT2_MIXER}, + {"LINE_MIXER1", WM8994_LINE_MIXER1}, + {"LINE_MIXER2", WM8994_LINE_MIXER2}, + {"SPEAKER_MIXER", WM8994_SPEAKER_MIXER}, + {"ADDITIONAL_CTL", WM8994_ADDITIONAL_CTL}, + {"ANTI_POP1", WM8994_ANTI_POP1}, + {"ANTI_POP2", WM8994_ANTI_POP2}, + {"MIC_BIAS", WM8994_MIC_BIAS}, + {"LDO_1", WM8994_LDO_1}, + {"LDO_2", WM8994_LDO_2}, + {"CHARGE_PUMP1", WM8994_CHARGE_PUMP1}, + {"CHARGE_PUMP2", WM8994_CHARGE_PUMP2}, + {"CLASS_W_1", WM8994_CLASS_W_1}, + {"DC_SERVO1", WM8994_DC_SERVO1}, + {"DC_SERVO2", WM8994_DC_SERVO2}, + {"DC_SERVO_BB", WM8994_DC_SERVO_RB}, + {"DC_SERVO4", WM8994_DC_SERVO4}, + {"ANA_HP1", WM8994_ANA_HP1}, + {"CHIP_REV", WM8994_CHIP_REV}, + {"CTL_IF", WM8994_CTL_IF}, + {"WR_CTL_SEQ1", WM8994_WR_CTL_SEQ1}, + {"WR_CTL_SEQ2", WM8994_WR_CTL_SEQ2}, + {"AIF1_CLK1", WM8994_AIF1_CLK1}, + {"AIF1_CLK2", WM8994_AIF1_CLK2}, + {"AIF2_CLK1", WM8994_AIF2_CLK1}, + {"AIF2_CLK2", WM8994_AIF2_CLK2}, + {"CLK1", WM8994_CLK1}, + {"CLK2", WM8994_CLK2}, + {"AIF1_RATE", WM8994_AIF1_RATE}, + {"AIF2_RATE", WM8994_AIF2_RATE}, + {"RATE_STATUS", WM8994_RATE_STATUS}, + {"PLL1_CTL1", WM8994_PLL1_CTL1}, + {"PLL1_CTL2", WM8994_PLL1_CTL2}, + {"PLL1_CTL3", WM8994_PLL1_CTL3}, + {"PLL1_CTL4", WM8994_PLL1_CTL4}, + {"PLL1_CTL5", WM8994_PLL1_CTL5}, + {"PLL2_CTL1", WM8994_PLL2_CTL1}, + {"PLL2_CTL2", WM8994_PLL2_CTL2}, + {"PLL2_CTL3", WM8994_PLL2_CTL3}, + {"PLL2_CTL4", WM8994_PLL2_CTL4}, + {"PLL2_CTL5", WM8994_PLL2_CTL5}, + {"AIF1_CTL1", WM8994_AIF1_CTL1}, + {"AIF1_CTL2", WM8994_AIF1_CTL2}, + {"AIF1_MASTER_SLAVE", WM8994_AIF1_MASTER_SLAVE}, + {"AIF1_BCLK", WM8994_AIF1_BCLK}, + {"AIF1_ADC_LRCLK", WM8994_AIF1_ADC_LRCLK}, + {"AIF1_DAC_LRCLK", WM8994_AIF1_DAC_LRCLK}, + {"AIF1_DAC_DATA", WM8994_AIF1_DAC_DATA}, + {"AIF1_ADC_DATA", WM8994_AIF1_ADC_DATA}, + {"AIF2_CTL1", WM8994_AIF2_CTL1}, + {"AIF2_CTL2", WM8994_AIF2_CTL2}, + {"AIF2_MASTER_SLAVE", WM8994_AIF2_MASTER_SLAVE}, + {"AIF2_BCLKK", WM8994_AIF2_BCLK}, + {"AIF2_ADC_LRCLK", WM8994_AIF2_ADC_LRCLK}, + {"AIF2_DAC_LRCLK", WM8994_AIF2_DAC_LRCLK}, + {"AIF2_DAC_DATA", WM8994_AIF2_DAC_DATA}, + {"AIF2_ADC_DATA", WM8994_AIF2_ADC_DATA}, + {"AIF1_ADC1_LEFT_VOL", WM8994_AIF1_ADC1_LEFT_VOL}, + {"AIF1_ADC1_RIGHT_VOL", WM8994_AIF1_ADC1_RIGHT_VOL}, + {"AIF1_DAC1_LEFT_VOL", WM8994_AIF1_DAC1_LEFT_VOL}, + {"AIF1_DAC1_RIGHT_VOL", WM8994_AIF1_DAC1_RIGHT_VOL}, + {"AIF1_ADC2_LEFT_VOL", WM8994_AIF1_ADC2_LEFT_VOL}, + {"AIF1_ADC2_RIGHT_VOL", WM8994_AIF1_ADC2_RIGHT_VOL}, + {"AIF1_DAC2_LEFT_VOL", WM8994_AIF1_DAC2_LEFT_VOL}, + {"AIF1_DAC2_RIGHT_VOL", WM8994_AIF1_DAC2_RIGHT_VOL}, + {"AIF1_ADC1_FILTERS", WM8994_AIF1_ADC1_FILTERS}, + {"AIF1_ADC2_FILTERS", WM8994_AIF1_ADC2_FILTERS}, + {"AIF1_DAC1_FILTERS1", WM8994_AIF1_DAC1_FILTERS1}, + {"AIF1_DAC1_FILTERS2", WM8994_AIF1_DAC1_FILTERS2}, + {"AIF1_DAC2_FILTERS1", WM8994_AIF1_DAC2_FILTERS1}, + {"AIF1_DAC2_FILTERS2", WM8994_AIF1_DAC2_FILTERS2}, + {"AIF1_DRC1_1", WM8994_AIF1_DRC1_1}, + {"AIF1_DRC1_2", WM8994_AIF1_DRC1_2}, + {"AIF1_DRC1_3", WM8994_AIF1_DRC1_3}, + {"AIF1_DRC1_4", WM8994_AIF1_DRC1_4}, + {"AIF1_DRC1_5", WM8994_AIF1_DRC1_5}, + {"AIF1_DRC2_1", WM8994_AIF1_DRC2_1}, + {"AIF1_DRC2_2", WM8994_AIF1_DRC2_2}, + {"AIF1_DRC2_3", WM8994_AIF1_DRC2_3}, + {"AIF1_DRC2_4", WM8994_AIF1_DRC2_4}, + {"AIF1_DRC2_5", WM8994_AIF1_DRC2_5}, + {"AIF1_DAC1_EQ_GAINS_1", WM8994_AIF1_DAC1_EQ_GAINS_1}, + {"AIF1_DAC1_EQ_GAINS_2", WM8994_AIF1_DAC1_EQ_GAINS_2}, + {"AIF1_DAC1_EQ_BAND_1A", WM8994_AIF1_DAC1_EQ_BAND_1A}, + {"AIF1_DAC1_EQ_BAND_1B", WM8994_AIF1_DAC1_EQ_BAND_1B}, + {"AIF1_DAC1_EQ_BAND_1PG", WM8994_AIF1_DAC1_EQ_BAND_1PG}, + {"AIF1_DAC1_EQ_BAND_2A", WM8994_AIF1_DAC1_EQ_BAND_2A}, + {"AIF1_DAC1_EQ_BAND_2B", WM8994_AIF1_DAC1_EQ_BAND_2B}, + {"AIF1_DAC1_EQ_BAND_2C", WM8994_AIF1_DAC1_EQ_BAND_2C}, + {"AIF1_DAC1_EQ_BAND_2PG", WM8994_AIF1_DAC1_EQ_BAND_2PG}, + {"AIF1_DAC1_EQ_BAND_3A", WM8994_AIF1_DAC1_EQ_BAND_3A}, + {"AIF1_DAC1_EQ_BAND_3B", WM8994_AIF1_DAC1_EQ_BAND_3B}, + {"AIF1_DAC1_EQ_BAND_3C", WM8994_AIF1_DAC1_EQ_BAND_3C}, + {"AIF1_DAC1_EQ_BAND_3PG", WM8994_AIF1_DAC1_EQ_BAND_3PG}, + {"AIF1_DAC1_EQ_BAND_4A", WM8994_AIF1_DAC1_EQ_BAND_4A}, + {"AIF1_DAC1_EQ_BAND_4B", WM8994_AIF1_DAC1_EQ_BAND_4B}, + {"AIF1_DAC1_EQ_BAND_4C", WM8994_AIF1_DAC1_EQ_BAND_4C}, + {"AIF1_DAC1_EQ_BAND_4PG", WM8994_AIF1_DAC1_EQ_BAND_4PG}, + {"AIF1_DAC1_EQ_BAND_5A", WM8994_AIF1_DAC1_EQ_BAND_5A}, + {"AIF1_DAC1_EQ_BAND_5B", WM8994_AIF1_DAC1_EQ_BAND_5B}, + {"AIF1_DAC1_EQ_BAND_5PG", WM8994_AIF1_DAC1_EQ_BAND_5PG}, + {"AIF1_DAC2_EQ_GAINS_1", WM8994_AIF1_DAC2_EQ_GAINS_1}, + {"AIF1_DAC2_EQ_GAINS_2", WM8994_AIF1_DAC2_EQ_GAINS_2}, + {"AIF1_DAC2_EQ_BAND_1A", WM8994_AIF1_DAC2_EQ_BAND_1A}, + {"AIF1_DAC2_EQ_BAND_1B", WM8994_AIF1_DAC2_EQ_BAND_1B}, + {"AIF1_DAC2_EQ_BAND_1PG", WM8994_AIF1_DAC2_EQ_BAND_1PG}, + {"AIF1_DAC2_EQ_BAND_2A", WM8994_AIF1_DAC2_EQ_BAND_2A}, + {"AIF1_DAC2_EQ_BAND_2B", WM8994_AIF1_DAC2_EQ_BAND_2B}, + {"AIF1_DAC2_EQ_BAND_2C", WM8994_AIF1_DAC2_EQ_BAND_2C}, + {"AIF1_DAC2_EQ_BAND_2PG", WM8994_AIF1_DAC2_EQ_BAND_2PG}, + {"AIF1_DAC2_EQ_BAND_3A", WM8994_AIF1_DAC2_EQ_BAND_3A}, + {"AIF1_DAC2_EQ_BAND_3B", WM8994_AIF1_DAC2_EQ_BAND_3B}, + {"AIF1_DAC2_EQ_BAND_3C", WM8994_AIF1_DAC2_EQ_BAND_3C}, + {"AIF1_DAC2_EQ_BAND_3PG", WM8994_AIF1_DAC2_EQ_BAND_3PG}, + {"AIF1_DAC2_EQ_BAND_4A", WM8994_AIF1_DAC2_EQ_BAND_4A}, + {"AIF1_DAC2_EQ_BAND_4B", WM8994_AIF1_DAC2_EQ_BAND_4B}, + {"AIF1_DAC2_EQ_BAND_4C", WM8994_AIF1_DAC2_EQ_BAND_4C}, + {"AIF1_DAC2_EQ_BAND_4PG", WM8994_AIF1_DAC2_EQ_BAND_4PG}, + {"AIF1_DAC2_EQ_BAND_5A", WM8994_AIF1_DAC2_EQ_BAND_5A}, + {"AIF1_DAC2_EQ_BAND_5B", WM8994_AIF1_DAC2_EQ_BAND_5B}, + {"AIF1_DAC2_EQ_BAND_5PG", WM8994_AIF1_DAC2_EQ_BAND_5PG}, + {"AIF2_ADC1_LEFT_VOL", WM8994_AIF2_ADC1_LEFT_VOL}, + {"AIF2_ADC1_RIGHT_VOL", WM8994_AIF2_ADC1_RIGHT_VOL}, + {"AIF2_DAC1_LEFT_VOL", WM8994_AIF2_DAC1_LEFT_VOL}, + {"AIF2_DAC1_RIGHT_VOL", WM8994_AIF2_DAC1_RIGHT_VOL}, + {"AIF2_ADC_FILTERS", WM8994_AIF2_ADC_FILTERS}, + {"AIF2_DAC_FILTERS1", WM8994_AIF2_DAC_FILTERS1}, + {"AIF2_DAC_FILTERS2", WM8994_AIF2_DAC_FILTERS2}, + {"AIF2_DRC_1", WM8994_AIF2_DRC_1}, + {"AIF2_DRC_2", WM8994_AIF2_DRC_2}, + {"AIF2_DRC_3", WM8994_AIF2_DRC_3}, + {"AIF2_DRC_4", WM8994_AIF2_DRC_4}, + {"AIF2_DRC_5", WM8994_AIF2_DRC_5}, + {"AIF2_EQ_GAINS_1", WM8994_AIF2_EQ_GAINS_1}, + {"AIF2_EQ_GAINS_2", WM8994_AIF2_EQ_GAINS_2}, + {"AIF2_EQ_BAND_1A", WM8994_AIF2_EQ_BAND_1A}, + {"AIF2_EQ_BAND_1B", WM8994_AIF2_EQ_BAND_1B}, + {"AIF2_EQ_BAND_1PG", WM8994_AIF2_EQ_BAND_1PG}, + {"AIF2_EQ_BAND_2A", WM8994_AIF2_EQ_BAND_2A}, + {"AIF2_EQ_BAND_2B", WM8994_AIF2_EQ_BAND_2B}, + {"AIF2_EQ_BAND_2C", WM8994_AIF2_EQ_BAND_2C}, + {"AIF2_EQ_BAND_2PG", WM8994_AIF2_EQ_BAND_2PG}, + {"AIF2_EQ_BAND_3A", WM8994_AIF2_EQ_BAND_3A}, + {"AIF2_EQ_BAND_3B", WM8994_AIF2_EQ_BAND_3B}, + {"AIF2_EQ_BAND_3C", WM8994_AIF2_EQ_BAND_3C}, + {"AIF2_EQ_BAND_3PG", WM8994_AIF2_EQ_BAND_3PG}, + {"AIF2_EQ_BAND_4A", WM8994_AIF2_EQ_BAND_4A}, + {"AIF2_EQ_BAND_4B", WM8994_AIF2_EQ_BAND_4B}, + {"AIF2_EQ_BAND_4C", WM8994_AIF2_EQ_BAND_4C}, + {"AIF2_EQ_BAND_4PG", WM8994_AIF2_EQ_BAND_4PG}, + {"AIF2_EQ_BAND_5A", WM8994_AIF2_EQ_BAND_5A}, + {"AIF2_EQ_BAND_5B", WM8994_AIF2_EQ_BAND_5B}, + {"AIF2_EQ_BAND_5PG", WM8994_AIF2_EQ_BAND_5PG}, + {"DAC1_MIXER_VOLS", WM8994_DAC1_MIXER_VOLS}, + {"DAC1_LEFT_MIXER_ROUTING", WM8994_DAC1_LEFT_MIXER_ROUTING}, + {"DAC1_RIGHT_MIXER_ROUTING", WM8994_DAC1_RIGHT_MIXER_ROUTING}, + {"DAC2_MIXER_VOLS", WM8994_DAC2_MIXER_VOLS}, + {"DAC2_LEFT_MIXER_ROUTING", WM8994_DAC2_LEFT_MIXER_ROUTING}, + {"DAC2_RIGHT_MIXER_ROUTING", WM8994_DAC2_RIGHT_MIXER_ROUTING}, + {"ADC1_LEFT_MIXER_ROUTING", WM8994_ADC1_LEFT_MIXER_ROUTING}, + {"ADC1_RIGHT_MIXER_ROUTING", WM8994_ADC1_RIGHT_MIXER_ROUTING}, + {"ADC2_LEFT_MIXER_ROUTING", WM8994_ADC2_LEFT_MIXER_ROUTING}, + {"ADC2_RIGHT_MIXER_ROUTING", WM8994_ADC2_RIGHT_MIXER_ROUTING}, + {"DAC1_LEFT_VOL", WM8994_DAC1_LEFT_VOL}, + {"DAC1_RIGHT_VOL", WM8994_DAC1_RIGHT_VOL}, + {"DAC2_LEFT_VOL", WM8994_DAC2_LEFT_VOL}, + {"DAC2_RIGHT_VOL", WM8994_DAC2_RIGHT_VOL}, + {"DAC_SOFT_MUTE", WM8994_DAC_SOFT_MUTE}, + {"OVER_SAMPLING", WM8994_OVER_SAMPLING}, + {"SIDE_TONE", WM8994_SIDE_TONE}, + {"GPIO1", WM8994_GPIO1}, + {"GPIO2", WM8994_GPIO2}, + {"GPIO3", WM8994_GPIO3}, + {"GPIO4", WM8994_GPIO4}, + {"GPIO5", WM8994_GPIO5}, + {"GPIO6", WM8994_GPIO6}, + {"GPIO7", WM8994_GPIO7}, + {"GPIO8", WM8994_GPIO8}, + {"GPIO9", WM8994_GPIO9}, + {"GPIO10", WM8994_GPIO10}, + {"GPIO11", WM8994_GPIO11}, + {"PULL_CTL1", WM8994_PULL_CTL1}, + {"PULL_CTL2", WM8994_PULL_CTL2}, + {"INT_STATUS1", WM8994_INT_STATUS1}, + {"INT_STATUS2", WM8994_INT_STATUS2}, + {"INT_RAW_STATUS2", WM8994_INT_RAW_STATUS2}, + {"INT_STATUS1_MASK", WM8994_INT_STATUS1_MASK}, + {"INT_STATUS2_MASK", WM8994_INT_STATUS2_MASK}, + {"INT_CTL", WM8994_INT_CTL}, + {"INT_DEBOUNCE", WM8994_INT_DEBOUNCE}, +}; + +# define WM8994_NREGISTERS (sizeof(g_wm8994_debug)/sizeof(struct wb8994_regdump_s)) +#endif /* CONFIG_WM8994_REGDUMP */ + +/***************************************************************************************** + * Private Functions + *****************************************************************************************/ + +/***************************************************************************************** + * Public Functions + *****************************************************************************************/ + +/***************************************************************************************** + * Name: wm8994_dump_registers + * + * Description: + * Dump the contents of all WM8994 registers to the syslog device + * + * Input Parameters: + * dev - The device instance returned by wm8994_initialize + * + * Returned Value: + * None. + * + *****************************************************************************************/ + +#ifdef CONFIG_WM8994_REGDUMP +void wm8994_dump_registers(FAR struct audio_lowerhalf_s *dev, + FAR const char *msg) +{ + int i; + + syslog(LOG_INFO, "WM8994 Registers: %s\n", msg); + for (i = 0; i < WM8994_NREGISTERS; i++) + { + syslog(LOG_INFO, "%24s[%04x]: %04x\n", + g_wm8994_debug[i].regname, g_wm8994_debug[i].regaddr, + wm8994_readreg((FAR struct wm8994_dev_s *)dev, + g_wm8994_debug[i].regaddr)); + } +} +#endif /* CONFIG_WM8994_REGDUMP */ + +/***************************************************************************************** + * Name: wm8994_clock_analysis + * + * Description: + * Analyze the settings in the clock chain and dump to syslog. + * + * Input Parameters: + * dev - The device instance returned by wm8994_initialize + * + * Returned Value: + * None. + * + *****************************************************************************************/ + +#ifdef CONFIG_WM8994_CLKDEBUG +void wm8994_clock_analysis(FAR struct audio_lowerhalf_s *dev, + FAR const char *msg) +{ + FAR struct wm8994_dev_s *priv = (FAR struct wm8994_dev_s *)dev; + uint32_t sysclk; + uint32_t bclk; + uint32_t lrclk; + uint16_t regval; + unsigned int tmp; + double ftmp; + + syslog(LOG_INFO, "WM8994 Clock Analysis: %s\n", msg); + DEBUGASSERT(priv && priv->lower); + syslog(LOG_INFO, " MCLK: %lu Hz\n", + (unsigned long)priv->lower->mclk); + + /* Is the SYSCLK source the FLL? Or MCK? */ + + regval = wm8994_readreg(priv, WM8994_CLKRATE2); + if ((regval & WM8994_SYSCLK_SRC) == WM8994_SYSCLK_SRCMCLK) + { + /* The SYSCLK divider bypasses the FLL and takes its input + * directly from MCLK. + */ + + sysclk = priv->lower->mclk; + syslog(LOG_INFO, " SYSCLK Source: MCLK (%s)\n", + (regval & WM8994_MCLK_INV) != 0 ? "inverted" : "not inverted"); + } + else + { + uint32_t fref; + uint32_t fvco; + uint32_t fout; + unsigned int outdiv; + unsigned int frndx; + unsigned int flln; + unsigned int fllk; + unsigned int fratio; + double nk; + + /* Assume that the Fref input to the FLL is MCLK */ + + fref = priv->lower->mclk; + + /* Now get the real FLL input source */ + + regval = wm8994_readreg(priv, WM8994_FLL_CTRL5); + switch (regval & WM8994_FLL_CLK_REF_SRC_MASK) + { + case WM8994_FLL_CLK_REF_SRC_MCLK: + syslog(LOG_INFO, " FLL Source: MCLK\n"); + break; + + case WM8994_FLL_CLK_REF_SRC_BCLK: + syslog(LOG_INFO, " ERROR: FLL source is BCLK: %04x\n", + regval); + break; + + case WM8994_FLL_CLK_REF_SRC_LRCLK: + syslog(LOG_INFO, " ERROR: FLL source is LRCLK: %04x\n", + regval); + break; + + default: + syslog(LOG_INFO, " ERROR: Unrecognized FLL source: %04x\n", + regval); + } + + syslog(LOG_INFO, " Fref: %lu Hz (before divider)\n", + fref); + switch (regval & WM8994_FLL_CLK_REF_DIV_MASK) + { + case WM8994_FLL_CLK_REF_DIV1: + syslog(LOG_INFO, " FLL_CLK_REF_DIV: 1\n"); + break; + + case WM8994_FLL_CLK_REF_DIV2: + syslog(LOG_INFO, " FLL_CLK_REF_DIV: 2\n"); + fref >>= 1; + break; + + case WM8994_FLL_CLK_REF_DIV4: + syslog(LOG_INFO, " FLL_CLK_REF_DIV: 4\n"); + fref >>= 2; + break; + + case WM8994_FLL_CLK_REF_DIV8: + syslog(LOG_INFO, " FLL_CLK_REF_DIV: 8\n"); + fref >>= 3; + break; + } + + syslog(LOG_INFO, " Fref: %lu Hz (after divider)\n", fref); + + regval = wm8994_readreg(priv, WM8994_FLL_CTRL2); + frndx = (regval & WM8994_FLL_FRATIO_MASK) >> WM8994_FLL_FRATIO_SHIFT; + tmp = (regval & WM8994_FLL_CTRL_RATE_MASK) >> WM8994_FLL_CTRL_RATE_SHIFT; + outdiv = ((regval & WM8994_FLL_OUTDIV_MASK) >> WM8994_FLL_OUTDIV_SHIFT) + 1; + + syslog(LOG_INFO, " FLL_CTRL_RATE: Fvco / %u\n", tmp + 1); + + regval = wm8904_readreg(priv, WM8994_FLL_CTRL4); + flln = (regval & WM8994_FLL_N_MASK) >> WM8994_FLL_N_SHIFT; + tmp = (regval & WM8994_FLL_GAIN_MASK) >> WM8994_FLL_GAIN_SHIFT; + + syslog(LOG_INFO, " FLL_GAIN: %u\n", (1 << tmp)); + + fllk = wm8994_readreg(priv, WM8994_FLL_CTRL3); + nk = (double)flln + ((double)fllk / 65536.0); + fratio = g_fllratio[frndx]; + + syslog(LOG_INFO, " FLL_FRATIO: %u\n", fratio); + syslog(LOG_INFO, " FLL_OUTDIV: %u\n", outdiv); + syslog(LOG_INFO, " FLL_N.K: %u.%05u\n", flln, fllk); + + ftmp = nk * (double)fref * (double)fratio; + fvco = (uint32_t)ftmp; + + syslog(LOG_INFO, " Fvco: %lu Hz\n", (unsigned long)fvco); + + fout = fvco / outdiv; + syslog(LOG_INFO, " Fout: %lu Hz\n", (unsigned long)fout); + + regval = wm8994_readreg(priv, WM8994_FLL_CTRL1); + + syslog(LOG_INFO, " FLL_FRACN_ENA: %s\n", + (regval & WM8994_FLL_FRACN_ENA) != 0 ? "Enabled" : "Disabled"); + syslog(LOG_INFO, " FLL_OSC_ENA: %s\n", + (regval & WM8994_FLL_OSC_ENA) != 0 ? "Enabled" : "Disabled"); + syslog(LOG_INFO, " FLL_ENA: %s\n", + (regval & WM8994_FLL_ENA) != 0 ? "Enabled" : "Disabled"); + + if ((regval & WM8994_FLL_ENA) == 0) + { + syslog(LOG_INFO, " No SYSCLK\n"); + return; + } + + sysclk = fout; + } + + syslog(LOG_INFO, " SYSCLK: %lu Hz (before divider)\n", + (unsigned long)sysclk); + + regval = wm8994_readreg(priv, WM8994_CLKRATE0); + if ((regval & WM8994_MCLK_DIV) == WM8994_MCLK_DIV1) + { + syslog(LOG_INFO, " MCLK_DIV: 1\n"); + } + else + { + syslog(LOG_INFO, " MCLK_DIV: 2\n"); + sysclk >>= 1; + } + + syslog(LOG_INFO, " SYSCLK: %lu (after divider)\n", (unsigned long)sysclk); + + regval = wm8994_readreg(priv, WM8994_CLKRATE2); + + syslog(LOG_INFO, " CLK_SYS_ENA: %s\n", + (regval & WM8994_CLK_SYS_ENA) != 0 ? "Enabled" : "Disabled"); + + if ((regval & WM8994_CLK_SYS_ENA) == 0) + { + syslog(LOG_INFO, " No SYSCLK\n"); + return; + } + + regval = wm8994_readreg(priv, WM8994_AIF2); + tmp = (regval & WM8994_BCLK_DIV_MASK) >> WM8994_BCLK_DIV_SHIFT; + tmp = g_sysclk_scaleb1[tmp]; + ftmp = (double)tmp / 2.0; + + syslog(LOG_INFO, " BCLK_DIV: SYSCLK / %u.%01u\n", + (unsigned int)(tmp >> 1), (unsigned int)(5 * (tmp & 1))); + + bclk = (uint32_t)(sysclk / ftmp); + + syslog(LOG_INFO, " BCLK: %lu Hz\n", (unsigned long)bclk); + + regval = wm8994_readreg(priv, WM8994_AIF1); + syslog(LOG_INFO, " BCLK_DIR: %s\n", + (regval & WM8994_BCLK_DIR) != 0 ? "Output" : "Input"); + + regval = wm8994_readreg(priv, WM8994_AIF3); + tmp = (regval & WM8994_LRCLK_RATE_MASK) >> WM8994_LRCLK_RATE_SHIFT; + + lrclk = bclk / tmp; + + syslog(LOG_INFO, " LRCLK_RATE: BCLK / %lu\n", (unsigned long)tmp); + syslog(LOG_INFO, " LRCLK: %lu Hz\n", (unsigned long)lrclk); + syslog(LOG_INFO, " LRCLK_DIR: %s\n", + (regval & WM8994_LRCLK_DIR) != 0 ? "Output" : "Input"); +} +#endif /* CONFIG_WM8994_CLKDEBUG */ diff --git a/include/nuttx/audio/wm8994.h b/include/nuttx/audio/wm8994.h new file mode 100644 index 00000000000..cf42da295bf --- /dev/null +++ b/include/nuttx/audio/wm8994.h @@ -0,0 +1,260 @@ +/**************************************************************************** + * include/nuttx/audio/wm8994.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_AUDIO_WM8994_H +#define __INCLUDE_NUTTX_AUDIO_WM8994_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include + +#ifdef CONFIG_AUDIO_WM8994 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************ + * + * CONFIG_AUDIO_WM8994 - Enables WM8994 support + * CONFIG_WM8994_INITVOLUME - The initial volume level in the range {0..1000} + * CONFIG_WM8994_INFLIGHT - Maximum number of buffers that the WM8994 driver + * will send to the I2S driver before any have completed. + * CONFIG_WM8994_MSG_PRIO - Priority of messages sent to the WM8994 worker + * thread. + * CONFIG_WM8994_BUFFER_SIZE - Preferred buffer size + * CONFIG_WM8994_NUM_BUFFERS - Preferred number of buffers + * CONFIG_WM8994_WORKER_STACKSIZE - Stack size to use when creating the + * WM8994 worker thread. + * CONFIG_WM8994_REGDUMP - Enable logic to dump all WM8994 registers to + * the SYSLOG device. + */ + +/* Pre-requisites */ + +#ifndef CONFIG_AUDIO +# error CONFIG_AUDIO is required for audio subsystem support +#endif + +#ifndef CONFIG_I2S +# error CONFIG_I2S is required by the WM8994 driver +#endif + +#ifndef CONFIG_I2C +# error CONFIG_I2C is required by the WM8994 driver +#endif + +#ifndef CONFIG_SCHED_WORKQUEUE +# error CONFIG_SCHED_WORKQUEUE is required by the WM8994 driver +#endif + +/* Default configuration values */ + +#ifndef CONFIG_WM8994_INITVOLUME +# define CONFIG_WM8994_INITVOLUME 250 +#endif + +#ifndef CONFIG_WM8994_INFLIGHT +# define CONFIG_WM8994_INFLIGHT 2 +#endif + +#if CONFIG_WM8994_INFLIGHT > 255 +# error CONFIG_WM8994_INFLIGHT must fit in a uint8_t +#endif + +#ifndef CONFIG_WM8994_MSG_PRIO +# define CONFIG_WM8994_MSG_PRIO 1 +#endif + +#ifndef CONFIG_WM8994_BUFFER_SIZE +# define CONFIG_WM8994_BUFFER_SIZE 8192 +#endif + +#ifndef CONFIG_WM8994_NUM_BUFFERS +# define CONFIG_WM8994_NUM_BUFFERS 4 +#endif + +#ifndef CONFIG_WM8994_WORKER_STACKSIZE +# define CONFIG_WM8994_WORKER_STACKSIZE 768 +#endif + +/* Helper macros ************************************************************/ + +#define WM8994_ATTACH(s,isr,arg) ((s)->attach(s,isr,arg)) +#define WM8994_DETACH(s) ((s)->attach(s,NULL,NULL)) +#define WM8994_ENABLE(s) ((s)->enable(s,true)) +#define WM8994_DISABLE(s) ((s)->enable(s,false)) +#define WM8994_RESTORE(s,e) ((s)->enable(s,e)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This is the type of the WM8994 interrupt handler. The lower level code + * will intercept the interrupt and provide the upper level with the private + * data that was provided when the interrupt was attached. + */ + +struct wm8994_lower_s; /* Forward reference. Defined below */ + +typedef CODE int (*wm8994_handler_t)(FAR const struct wm8994_lower_s *lower, + FAR void *arg); + +/* A reference to a structure of this type must be passed to the WM8994 + * driver. This structure provides information about the configuration + * of the WM8994 and provides some board-specific hooks. + * + * Memory for this structure is provided by the caller. It is not copied + * by the driver and is presumed to persist while the driver is active. + */ + +struct wm8994_lower_s +{ + /* I2C characterization */ + + uint32_t frequency; /* Initial I2C frequency */ + uint8_t address; /* 7-bit I2C address (only bits 0-6 used) */ + + /* Clocking is provided via MCLK. The WM8994 driver will need to know + * the frequency of MCLK in order to generate the correct bitrates. + */ + + uint32_t mclk; /* W8994 Master clock frequency */ + + /* IRQ/GPIO access callbacks. These operations all hidden behind + * callbacks to isolate the WM8994 driver from differences in GPIO + * interrupt handling by varying boards and MCUs. If possible, + * interrupts should be configured on both rising and falling edges + * so that contact and loss-of-contact events can be detected. + * + * attach - Attach or detach the WM8994 interrupt handler to the GPIO + * interrupt + * enable - Enable or disable the GPIO interrupt. Returns the + * previous interrupt state. + */ + + CODE int (*attach)(FAR const struct wm8994_lower_s *lower, + wm8994_handler_t isr, FAR void *arg); + CODE bool (*enable)(FAR const struct wm8994_lower_s *lower, bool enable); +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: wm8994_initialize + * + * Description: + * Initialize the WM8994 device. + * + * Input Parameters: + * i2c - An I2C driver instance + * i2s - An I2S driver instance + * lower - Persistent board configuration data + * + * Returned Value: + * A new lower half audio interface for the WM8994 device is returned on + * success; NULL is returned on failure. + * + ****************************************************************************/ + +struct i2c_master_s; /* Forward reference. Defined in include/nuttx/i2c/i2c_master.h */ +struct i2s_dev_s; /* Forward reference. Defined in include/nuttx/audio/i2s.h */ +struct audio_lowerhalf_s; /* Forward reference. Defined in nuttx/audio/audio.h */ + +FAR struct audio_lowerhalf_s * + wm8994_initialize(FAR struct i2c_master_s *i2c, FAR struct i2s_dev_s *i2s, + FAR const struct wm8994_lower_s *lower); + +/**************************************************************************** + * Name: wm8994_dump_registers + * + * Description: + * Dump the contents of all WM8994 registers to the syslog device + * + * Input Parameters: + * dev - The device instance returned by wm8994_initialize + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_WM8994_REGDUMP +void wm8994_dump_registers(FAR struct audio_lowerhalf_s *dev, + FAR const char *msg); +#else + /* This eliminates the need for any conditional compilation in the + * including file. + */ + +# define wm8994_dump_registers(d,m) +#endif + +/**************************************************************************** + * Name: wm8994_clock_analysis + * + * Description: + * Analyze the settings in the clock chain and dump to syslog. + * + * Input Parameters: + * dev - The device instance returned by wm8994_initialize + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_WM8994_CLKDEBUG +void wm8994_clock_analysis(FAR struct audio_lowerhalf_s *dev, + FAR const char *msg); +#else + /* This eliminates the need for any conditional compilation in the + * including file. + */ + +# define wm8994_clock_analysis(d,m) +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_AUDIO_WM8994 */ +#endif /* __INCLUDE_NUTTX_AUDIO_WM8994_H */