drivers: audio: Add the wm8904 driver
Add driver for the Wolfson WM8904 audio codec. Signed-off-by: Hake Huang <hake.huang@oss.nxp.com> Signed-off-by: Yves Vandervennet <yves.vandervennet@nxp.com> Signed-off-by: Vit Stanicek <vit.stanicek@nxp.com>
This commit is contained in:
parent
5d8b5c0188
commit
db1cb43a9d
6 changed files with 871 additions and 0 deletions
|
@ -9,3 +9,4 @@ zephyr_library_sources_ifdef(CONFIG_AUDIO_DMIC_NRFX_PDM dmic_nrfx_pdm.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_AUDIO_TAS6422DAC tas6422dac.c)
|
zephyr_library_sources_ifdef(CONFIG_AUDIO_TAS6422DAC tas6422dac.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_SHELL codec_shell.c)
|
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_SHELL codec_shell.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_AUDIO_DMIC_MCUX dmic_mcux.c)
|
zephyr_library_sources_ifdef(CONFIG_AUDIO_DMIC_MCUX dmic_mcux.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_WM8904 wm8904.c)
|
||||||
|
|
|
@ -37,6 +37,7 @@ source "subsys/logging/Kconfig.template.log_config"
|
||||||
|
|
||||||
source "drivers/audio/Kconfig.tas6422dac"
|
source "drivers/audio/Kconfig.tas6422dac"
|
||||||
source "drivers/audio/Kconfig.tlv320dac"
|
source "drivers/audio/Kconfig.tlv320dac"
|
||||||
|
source "drivers/audio/Kconfig.wm8904"
|
||||||
|
|
||||||
endif # AUDIO_CODEC
|
endif # AUDIO_CODEC
|
||||||
|
|
||||||
|
|
9
drivers/audio/Kconfig.wm8904
Normal file
9
drivers/audio/Kconfig.wm8904
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# Copyright 2024 NXP
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config AUDIO_CODEC_WM8904
|
||||||
|
bool "Wolfson WM8904 codec support"
|
||||||
|
select I2C
|
||||||
|
depends on DT_HAS_WOLFSON_WM8904_ENABLED
|
||||||
|
help
|
||||||
|
Enable support for the Wolfson WM8904 codec
|
681
drivers/audio/wm8904.c
Normal file
681
drivers/audio/wm8904.c
Normal file
|
@ -0,0 +1,681 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 NXP
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/drivers/i2c.h>
|
||||||
|
#include <zephyr/drivers/clock_control.h>
|
||||||
|
#include <zephyr/audio/codec.h>
|
||||||
|
#include <zephyr/devicetree/clocks.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(wolfson_wm8904);
|
||||||
|
|
||||||
|
#include "wm8904.h"
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT wolfson_wm8904
|
||||||
|
|
||||||
|
struct wm8904_driver_config {
|
||||||
|
struct i2c_dt_spec i2c;
|
||||||
|
int clock_source;
|
||||||
|
const struct device *mclk_dev;
|
||||||
|
clock_control_subsys_t mclk_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DEV_CFG(dev) ((const struct wm8904_driver_config *const)dev->config)
|
||||||
|
|
||||||
|
static void wm8904_write_reg(const struct device *dev, uint8_t reg, uint16_t val);
|
||||||
|
static void wm8904_read_reg(const struct device *dev, uint8_t reg, uint16_t *val);
|
||||||
|
static void wm8904_update_reg(const struct device *dev, uint8_t reg, uint16_t mask, uint16_t val);
|
||||||
|
static void wm8904_soft_reset(const struct device *dev);
|
||||||
|
|
||||||
|
static void wm8904_configure_output(const struct device *dev);
|
||||||
|
|
||||||
|
static void wm8904_configure_input(const struct device *dev);
|
||||||
|
|
||||||
|
static int wm8904_protocol_config(const struct device *dev, audio_dai_type_t dai_type)
|
||||||
|
{
|
||||||
|
wm8904_protocol_t proto;
|
||||||
|
|
||||||
|
switch (dai_type) {
|
||||||
|
case AUDIO_DAI_TYPE_I2S:
|
||||||
|
proto = kWM8904_ProtocolI2S;
|
||||||
|
break;
|
||||||
|
case AUDIO_DAI_TYPE_LEFT_JUSTIFIED:
|
||||||
|
proto = kWM8904_ProtocolLeftJustified;
|
||||||
|
break;
|
||||||
|
case AUDIO_DAI_TYPE_RIGHT_JUSTIFIED:
|
||||||
|
proto = kWM8904_ProtocolRightJustified;
|
||||||
|
break;
|
||||||
|
case AUDIO_DAI_TYPE_PCMA:
|
||||||
|
proto = kWM8904_ProtocolPCMA;
|
||||||
|
break;
|
||||||
|
case AUDIO_DAI_TYPE_PCMB:
|
||||||
|
proto = kWM8904_ProtocolPCMB;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_1, (0x0003U | (1U << 4U)), (uint16_t)proto);
|
||||||
|
|
||||||
|
LOG_DBG("Codec protocol: %#x", proto);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_audio_fmt_config(const struct device *dev, audio_dai_cfg_t *cfg, uint32_t mclk)
|
||||||
|
{
|
||||||
|
wm8904_sample_rate_t wm_sample_rate;
|
||||||
|
uint32_t fs;
|
||||||
|
uint16_t wmfs_ratio;
|
||||||
|
uint16_t mclkDiv;
|
||||||
|
uint16_t word_size = cfg->i2s.word_size;
|
||||||
|
|
||||||
|
switch (cfg->i2s.frame_clk_freq) {
|
||||||
|
case 8000:
|
||||||
|
wm_sample_rate = kWM8904_SampleRate8kHz;
|
||||||
|
break;
|
||||||
|
case 11025:
|
||||||
|
wm_sample_rate = kWM8904_SampleRate11025Hz;
|
||||||
|
break;
|
||||||
|
case 12000:
|
||||||
|
wm_sample_rate = kWM8904_SampleRate12kHz;
|
||||||
|
break;
|
||||||
|
case 16000:
|
||||||
|
wm_sample_rate = kWM8904_SampleRate16kHz;
|
||||||
|
break;
|
||||||
|
case 22050:
|
||||||
|
wm_sample_rate = kWM8904_SampleRate22050Hz;
|
||||||
|
break;
|
||||||
|
case 24000:
|
||||||
|
wm_sample_rate = kWM8904_SampleRate24kHz;
|
||||||
|
break;
|
||||||
|
case 32000:
|
||||||
|
wm_sample_rate = kWM8904_SampleRate32kHz;
|
||||||
|
break;
|
||||||
|
case 44100:
|
||||||
|
wm_sample_rate = kWM8904_SampleRate44100Hz;
|
||||||
|
break;
|
||||||
|
case 48000:
|
||||||
|
wm_sample_rate = kWM8904_SampleRate48kHz;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_WRN("Invalid codec sample rate: %d", cfg->i2s.frame_clk_freq);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wm8904_read_reg(dev, WM8904_REG_CLK_RATES_0, &mclkDiv);
|
||||||
|
fs = (mclk >> (mclkDiv & 0x1U)) / cfg->i2s.frame_clk_freq;
|
||||||
|
|
||||||
|
switch (fs) {
|
||||||
|
case 64:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio64X;
|
||||||
|
break;
|
||||||
|
case 128:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio128X;
|
||||||
|
break;
|
||||||
|
case 192:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio192X;
|
||||||
|
break;
|
||||||
|
case 256:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio256X;
|
||||||
|
break;
|
||||||
|
case 384:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio384X;
|
||||||
|
break;
|
||||||
|
case 512:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio512X;
|
||||||
|
break;
|
||||||
|
case 768:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio768X;
|
||||||
|
break;
|
||||||
|
case 1024:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio1024X;
|
||||||
|
break;
|
||||||
|
case 1408:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio1408X;
|
||||||
|
break;
|
||||||
|
case 1536:
|
||||||
|
wmfs_ratio = kWM8904_FsRatio1536X;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_WRN("Invalid Fs ratio: %d", fs);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable SYSCLK */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_CLK_RATES_2, 0x00);
|
||||||
|
|
||||||
|
/* Set Clock ratio and sample rate */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_CLK_RATES_1,
|
||||||
|
((wmfs_ratio) << 10U) | (uint16_t)(wm_sample_rate));
|
||||||
|
|
||||||
|
switch (cfg->i2s.word_size) {
|
||||||
|
case 16:
|
||||||
|
word_size = 0;
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
word_size = 1;
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
word_size = 2;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
word_size = 3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERR("Word size %d bits not supported; falling back to 16 bits",
|
||||||
|
cfg->i2s.word_size);
|
||||||
|
word_size = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Set bit resolution */
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_1, (0x000CU), ((uint16_t)(word_size) << 2U));
|
||||||
|
|
||||||
|
/* Enable SYSCLK */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_CLK_RATES_2, 0x1007);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_out_update(
|
||||||
|
const struct device *dev,
|
||||||
|
audio_channel_t channel,
|
||||||
|
uint16_t val,
|
||||||
|
uint16_t mask
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (channel) {
|
||||||
|
case AUDIO_CHANNEL_FRONT_LEFT:
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT2_LEFT, mask, val);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUDIO_CHANNEL_FRONT_RIGHT:
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT2_RIGHT, mask, val);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUDIO_CHANNEL_HEADPHONE_LEFT:
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT1_LEFT, mask, val);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUDIO_CHANNEL_HEADPHONE_RIGHT:
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT1_RIGHT, mask, val);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUDIO_CHANNEL_ALL:
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT1_LEFT, mask, val);
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT1_RIGHT, mask, val);
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT2_LEFT, mask, val);
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT2_RIGHT, mask, val);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_out_volume_config(const struct device *dev, audio_channel_t channel, int volume)
|
||||||
|
{
|
||||||
|
/* Set volume values with VU = 0 */
|
||||||
|
const uint16_t val = WM8904_REGVAL_OUT_VOL(0, 0, 1, volume);
|
||||||
|
const uint16_t mask = WM8904_REGMASK_OUT_VU
|
||||||
|
| WM8904_REGMASK_OUT_ZC
|
||||||
|
| WM8904_REGMASK_OUT_VOL;
|
||||||
|
|
||||||
|
return wm8904_out_update(dev, channel, val, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_out_mute_config(const struct device *dev, audio_channel_t channel, bool mute)
|
||||||
|
{
|
||||||
|
const uint16_t val = WM8904_REGVAL_OUT_VOL(mute, 0, 0, 0);
|
||||||
|
const uint16_t mask = WM8904_REGMASK_OUT_MUTE;
|
||||||
|
|
||||||
|
return wm8904_out_update(dev, channel, val, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_in_update(
|
||||||
|
const struct device *dev,
|
||||||
|
audio_channel_t channel,
|
||||||
|
uint16_t mask,
|
||||||
|
uint16_t val
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (channel) {
|
||||||
|
case AUDIO_CHANNEL_FRONT_LEFT:
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_LEFT_IN_0, mask, val);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUDIO_CHANNEL_FRONT_RIGHT:
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_RIGHT_IN_0, mask, val);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case AUDIO_CHANNEL_ALL:
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_LEFT_IN_0, mask, val);
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_ANALOG_RIGHT_IN_0, mask, val);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_in_volume_config(const struct device *dev, audio_channel_t channel, int volume)
|
||||||
|
{
|
||||||
|
const uint16_t val = WM8904_REGVAL_IN_VOL(0, volume);
|
||||||
|
const uint16_t mask = WM8904_REGMASK_IN_MUTE;
|
||||||
|
|
||||||
|
return wm8904_in_update(dev, channel, val, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_in_mute_config(const struct device *dev, audio_channel_t channel, bool mute)
|
||||||
|
{
|
||||||
|
const uint16_t val = WM8904_REGVAL_IN_VOL(mute, 0);
|
||||||
|
const uint16_t mask = WM8904_REGMASK_IN_MUTE;
|
||||||
|
|
||||||
|
return wm8904_in_update(dev, channel, val, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_route_input(const struct device *dev, audio_channel_t channel, uint32_t input)
|
||||||
|
{
|
||||||
|
if (input < 1 || input > 3) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t val = WM8904_REGVAL_INSEL(0, input - 1, input - 1, 0);
|
||||||
|
uint8_t mask = WM8904_REGMASK_INSEL_CMENA
|
||||||
|
| WM8904_REGMASK_INSEL_IP_SEL_P
|
||||||
|
| WM8904_REGMASK_INSEL_IP_SEL_N
|
||||||
|
| WM8904_REGMASK_INSEL_MODE;
|
||||||
|
uint8_t reg;
|
||||||
|
|
||||||
|
switch (channel) {
|
||||||
|
case AUDIO_CHANNEL_FRONT_LEFT:
|
||||||
|
reg = WM8904_REG_ANALOG_LEFT_IN_1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIO_CHANNEL_FRONT_RIGHT:
|
||||||
|
reg = WM8904_REG_ANALOG_RIGHT_IN_1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wm8904_update_reg(dev, reg, mask, val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm8904_set_master_clock(const struct device *dev, audio_dai_cfg_t *cfg, uint32_t sysclk)
|
||||||
|
{
|
||||||
|
uint32_t sampleRate = cfg->i2s.frame_clk_freq;
|
||||||
|
uint32_t bitWidth = cfg->i2s.word_size;
|
||||||
|
uint32_t bclk = sampleRate * bitWidth * 2U;
|
||||||
|
uint32_t bclkDiv = 0U;
|
||||||
|
uint16_t audioInterface = 0U;
|
||||||
|
uint16_t sysclkDiv = 0U;
|
||||||
|
|
||||||
|
wm8904_read_reg(dev, WM8904_REG_CLK_RATES_0, &sysclkDiv);
|
||||||
|
sysclk = sysclk >> (sysclkDiv & 0x1U);
|
||||||
|
LOG_DBG("Codec sysclk: %d", sysclk);
|
||||||
|
|
||||||
|
if ((sysclk / bclk > 48U) || (bclk / sampleRate > 2047U) || (bclk / sampleRate < 8U)) {
|
||||||
|
LOG_ERR("Invalid BCLK clock divider configured.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wm8904_read_reg(dev, WM8904_REG_AUDIO_IF_2, &audioInterface);
|
||||||
|
|
||||||
|
audioInterface &= ~(uint16_t)0x1FU;
|
||||||
|
bclkDiv = (sysclk * 10U) / bclk;
|
||||||
|
LOG_INF("blk %d", bclk);
|
||||||
|
|
||||||
|
switch (bclkDiv) {
|
||||||
|
case 10:
|
||||||
|
audioInterface |= 0U;
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
audioInterface |= 1U;
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
/* Avoid MISRA 16.4 violation */
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
/* Avoid MISRA 16.4 violation */
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
/* Avoid MISRA 16.4 violation */
|
||||||
|
break;
|
||||||
|
case 50:
|
||||||
|
audioInterface |= (uint16_t)bclkDiv / 10U;
|
||||||
|
break;
|
||||||
|
case 55:
|
||||||
|
audioInterface |= 6U;
|
||||||
|
break;
|
||||||
|
case 60:
|
||||||
|
audioInterface |= 7U;
|
||||||
|
break;
|
||||||
|
case 80:
|
||||||
|
audioInterface |= 8U;
|
||||||
|
break;
|
||||||
|
case 100:
|
||||||
|
/* Avoid MISRA 16.4 violation */
|
||||||
|
break;
|
||||||
|
case 110:
|
||||||
|
/* Avoid MISRA 16.4 violation */
|
||||||
|
break;
|
||||||
|
case 120:
|
||||||
|
audioInterface |= (uint16_t)bclkDiv / 10U - 1U;
|
||||||
|
break;
|
||||||
|
case 160:
|
||||||
|
audioInterface |= 12U;
|
||||||
|
break;
|
||||||
|
case 200:
|
||||||
|
audioInterface |= 13U;
|
||||||
|
break;
|
||||||
|
case 220:
|
||||||
|
audioInterface |= 14U;
|
||||||
|
break;
|
||||||
|
case 240:
|
||||||
|
audioInterface |= 15U;
|
||||||
|
break;
|
||||||
|
case 250:
|
||||||
|
audioInterface |= 16U;
|
||||||
|
break;
|
||||||
|
case 300:
|
||||||
|
audioInterface |= 17U;
|
||||||
|
break;
|
||||||
|
case 320:
|
||||||
|
audioInterface |= 18U;
|
||||||
|
break;
|
||||||
|
case 440:
|
||||||
|
audioInterface |= 19U;
|
||||||
|
break;
|
||||||
|
case 480:
|
||||||
|
audioInterface |= 20U;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERR("invalid audio interface for wm8904 %d", bclkDiv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bclk divider */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_AUDIO_IF_2, audioInterface);
|
||||||
|
/* bclk direction output */
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_1, 1U << 6U, 1U << 6U);
|
||||||
|
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_GPIO_CONTROL_4, 0x8FU, 1U);
|
||||||
|
/* LRCLK direction and divider */
|
||||||
|
audioInterface = (uint16_t)((1UL << 11U) | (bclk / sampleRate));
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_3, 0xFFFU, audioInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_configure(const struct device *dev, struct audio_codec_cfg *cfg)
|
||||||
|
{
|
||||||
|
uint16_t value;
|
||||||
|
const struct wm8904_driver_config *const dev_cfg = DEV_CFG(dev);
|
||||||
|
|
||||||
|
if (cfg->dai_type >= AUDIO_DAI_TYPE_INVALID) {
|
||||||
|
LOG_ERR("dai_type not supported");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wm8904_soft_reset(dev);
|
||||||
|
|
||||||
|
if (cfg->dai_route == AUDIO_ROUTE_BYPASS) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MCLK_INV=0, SYSCLK_SRC=0, TOCLK_RATE=0, OPCLK_ENA=1,
|
||||||
|
* CLK_SYS_ENA=1, CLK_DSP_ENA=1, TOCLK_ENA=1
|
||||||
|
*/
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_CLK_RATES_2, 0x000F);
|
||||||
|
|
||||||
|
/* WSEQ_ENA=1, WSEQ_WRITE_INDEX=0_0000 */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_WRT_SEQUENCER_0, 0x0100);
|
||||||
|
|
||||||
|
/* WSEQ_ABORT=0, WSEQ_START=1, WSEQ_START_INDEX=00_0000 */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_WRT_SEQUENCER_3, 0x0100);
|
||||||
|
|
||||||
|
do {
|
||||||
|
wm8904_read_reg(dev, WM8904_REG_WRT_SEQUENCER_4, &value);
|
||||||
|
} while (((value & 1U) != 0U));
|
||||||
|
|
||||||
|
/* TOCLK_RATE_DIV16=0, TOCLK_RATE_x4=1, SR_MODE=0, MCLK_DIV=1
|
||||||
|
* (Required for MMCs: SGY, KRT see erratum CE000546)
|
||||||
|
*/
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_CLK_RATES_0, 0xA45F);
|
||||||
|
|
||||||
|
/* INL_ENA=1, INR ENA=1 */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_POWER_MGMT_0, 0x0003);
|
||||||
|
|
||||||
|
/* HPL_PGA_ENA=1, HPR_PGA_ENA=1 */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_POWER_MGMT_2, 0x0003);
|
||||||
|
|
||||||
|
/* DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1 */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_POWER_MGMT_6, 0x000F);
|
||||||
|
|
||||||
|
/* ADC_OSR128=1 */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_ANALOG_ADC_0, 0x0001);
|
||||||
|
|
||||||
|
/* DACL_DATINV=0, DACR_DATINV=0, DAC_BOOST=00, LOOPBACK=0, AIFADCL_SRC=0,
|
||||||
|
* AIFADCR_SRC=1, AIFDACL_SRC=0, AIFDACR_SRC=1, ADC_COMP=0, ADC_COMPMODE=0,
|
||||||
|
* DAC_COMP=0, DAC_COMPMODE=0
|
||||||
|
*/
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_AUDIO_IF_0, 0x0050);
|
||||||
|
|
||||||
|
/* DAC_MONO=0, DAC_SB_FILT-0, DAC_MUTERATE=0, DAC_UNMUTE RAMP=0,
|
||||||
|
* DAC_OSR128=1, DAC_MUTE=0, DEEMPH=0 (none)
|
||||||
|
*/
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_DAC_DIG_1, 0x0040);
|
||||||
|
|
||||||
|
/* Enable DC servos for headphone out */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_DC_SERVO_0, 0x0003);
|
||||||
|
|
||||||
|
/* HPL_RMV_SHORT=1, HPL_ENA_OUTP=1, HPL_ENA_DLY=1, HPL_ENA=1,
|
||||||
|
* HPR_RMV_SHORT=1, HPR_ENA_OUTP=1, HPR_ENA_DLY=1, HPR_ENA=1
|
||||||
|
*/
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_ANALOG_HP_0, 0x00FF);
|
||||||
|
|
||||||
|
/* CP_DYN_PWR=1 */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_CLS_W_0, 0x0001);
|
||||||
|
|
||||||
|
/* CP_ENA=1 */
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_CHRG_PUMP_0, 0x0001);
|
||||||
|
|
||||||
|
wm8904_protocol_config(dev, cfg->dai_type);
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_CLK_RATES_2, (uint16_t)(1UL << 14U),
|
||||||
|
(uint16_t)(dev_cfg->clock_source));
|
||||||
|
|
||||||
|
if (dev_cfg->clock_source == 0) {
|
||||||
|
int err = clock_control_on(dev_cfg->mclk_dev, dev_cfg->mclk_name);
|
||||||
|
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("MCLK clock source enable fail: %d", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = clock_control_get_rate(dev_cfg->mclk_dev, dev_cfg->mclk_name,
|
||||||
|
&cfg->mclk_freq);
|
||||||
|
if (err < 0) {
|
||||||
|
LOG_ERR("MCLK clock source freq acquire fail: %d", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wm8904_audio_fmt_config(dev, &cfg->dai_cfg, cfg->mclk_freq);
|
||||||
|
|
||||||
|
if ((cfg->dai_cfg.i2s.options & I2S_OPT_FRAME_CLK_MASTER) == I2S_OPT_FRAME_CLK_MASTER) {
|
||||||
|
wm8904_set_master_clock(dev, &cfg->dai_cfg, cfg->mclk_freq);
|
||||||
|
} else {
|
||||||
|
/* BCLK/LRCLK default direction input */
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_1, 1U << 6U, 0U);
|
||||||
|
wm8904_update_reg(dev, WM8904_REG_AUDIO_IF_3, (uint16_t)(1UL << 11U), 0U);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cfg->dai_route) {
|
||||||
|
case AUDIO_ROUTE_PLAYBACK:
|
||||||
|
wm8904_configure_output(dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIO_ROUTE_CAPTURE:
|
||||||
|
wm8904_configure_input(dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AUDIO_ROUTE_PLAYBACK_CAPTURE:
|
||||||
|
wm8904_configure_output(dev);
|
||||||
|
wm8904_configure_input(dev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm8904_start_output(const struct device *dev)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm8904_stop_output(const struct device *dev)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_set_property(const struct device *dev, audio_property_t property,
|
||||||
|
audio_channel_t channel, audio_property_value_t val)
|
||||||
|
{
|
||||||
|
switch (property) {
|
||||||
|
case AUDIO_PROPERTY_OUTPUT_VOLUME:
|
||||||
|
return wm8904_out_volume_config(dev, channel, val.vol);
|
||||||
|
|
||||||
|
case AUDIO_PROPERTY_OUTPUT_MUTE:
|
||||||
|
return wm8904_out_mute_config(dev, channel, val.mute);
|
||||||
|
|
||||||
|
case AUDIO_PROPERTY_INPUT_VOLUME:
|
||||||
|
return wm8904_in_volume_config(dev, channel, val.vol);
|
||||||
|
|
||||||
|
case AUDIO_PROPERTY_INPUT_MUTE:
|
||||||
|
return wm8904_in_mute_config(dev, channel, val.mute);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wm8904_apply_properties(const struct device *dev)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set VU = 1 for all output channels, VU takes effect for the whole
|
||||||
|
* channel pair.
|
||||||
|
*/
|
||||||
|
wm8904_update_reg(
|
||||||
|
dev,
|
||||||
|
WM8904_REG_ANALOG_OUT1_LEFT,
|
||||||
|
WM8904_REGVAL_OUT_VOL(0, 1, 0, 0),
|
||||||
|
WM8904_REGMASK_OUT_MUTE
|
||||||
|
);
|
||||||
|
wm8904_update_reg(dev,
|
||||||
|
WM8904_REG_ANALOG_OUT2_LEFT,
|
||||||
|
WM8904_REGVAL_OUT_VOL(0, 1, 0, 0),
|
||||||
|
WM8904_REGMASK_OUT_MUTE
|
||||||
|
);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm8904_write_reg(const struct device *dev, uint8_t reg, uint16_t val)
|
||||||
|
{
|
||||||
|
const struct wm8904_driver_config *const dev_cfg = DEV_CFG(dev);
|
||||||
|
uint8_t data[3];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* data is reversed */
|
||||||
|
data[0] = reg;
|
||||||
|
data[1] = (val >> 8) & 0xff;
|
||||||
|
data[2] = val & 0xff;
|
||||||
|
|
||||||
|
ret = i2c_write(dev_cfg->i2c.bus, data, 3, dev_cfg->i2c.addr);
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_ERR("i2c write to codec error %d", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("REG:%02u VAL:%#02x", reg, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm8904_read_reg(const struct device *dev, uint8_t reg, uint16_t *val)
|
||||||
|
{
|
||||||
|
const struct wm8904_driver_config *const dev_cfg = DEV_CFG(dev);
|
||||||
|
uint16_t value;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_write_read(dev_cfg->i2c.bus, dev_cfg->i2c.addr, ®, sizeof(reg), &value,
|
||||||
|
sizeof(value));
|
||||||
|
if (ret == 0) {
|
||||||
|
*val = (value >> 8) & 0xff;
|
||||||
|
*val += ((value & 0xff) << 8);
|
||||||
|
/* update cache*/
|
||||||
|
LOG_DBG("REG:%02u VAL:%#02x", reg, *val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm8904_update_reg(const struct device *dev, uint8_t reg, uint16_t mask, uint16_t val)
|
||||||
|
{
|
||||||
|
uint16_t reg_val = 0;
|
||||||
|
uint16_t new_value = 0;
|
||||||
|
|
||||||
|
if (reg == 0x19) {
|
||||||
|
LOG_DBG("try write mask %#x val %#x", mask, val);
|
||||||
|
}
|
||||||
|
wm8904_read_reg(dev, reg, ®_val);
|
||||||
|
if (reg == 0x19) {
|
||||||
|
LOG_DBG("read %#x = %x", reg, reg_val);
|
||||||
|
}
|
||||||
|
new_value = (reg_val & ~mask) | (val & mask);
|
||||||
|
if (reg == 0x19) {
|
||||||
|
LOG_DBG("write %#x = %x", reg, new_value);
|
||||||
|
}
|
||||||
|
wm8904_write_reg(dev, reg, new_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm8904_soft_reset(const struct device *dev)
|
||||||
|
{
|
||||||
|
wm8904_write_reg(dev, WM8904_REG_RESET, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm8904_configure_output(const struct device *dev)
|
||||||
|
{
|
||||||
|
wm8904_out_volume_config(dev, AUDIO_CHANNEL_ALL, WM8904_OUTPUT_VOLUME_DEFAULT);
|
||||||
|
wm8904_out_mute_config(dev, AUDIO_CHANNEL_ALL, false);
|
||||||
|
|
||||||
|
wm8904_apply_properties(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm8904_configure_input(const struct device *dev)
|
||||||
|
{
|
||||||
|
wm8904_route_input(dev, AUDIO_CHANNEL_FRONT_LEFT, 2);
|
||||||
|
wm8904_route_input(dev, AUDIO_CHANNEL_FRONT_RIGHT, 2);
|
||||||
|
|
||||||
|
wm8904_in_volume_config(dev, AUDIO_CHANNEL_ALL, WM8904_INPUT_VOLUME_DEFAULT);
|
||||||
|
wm8904_in_mute_config(dev, AUDIO_CHANNEL_ALL, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct audio_codec_api wm8904_driver_api = {
|
||||||
|
.configure = wm8904_configure,
|
||||||
|
.start_output = wm8904_start_output,
|
||||||
|
.stop_output = wm8904_stop_output,
|
||||||
|
.set_property = wm8904_set_property,
|
||||||
|
.apply_properties = wm8904_apply_properties,
|
||||||
|
.route_input = wm8904_route_input
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WM8904_INIT(n) \
|
||||||
|
static const struct wm8904_driver_config wm8904_device_config_##n = { \
|
||||||
|
.i2c = I2C_DT_SPEC_INST_GET(n), \
|
||||||
|
.clock_source = DT_INST_PROP_OR(n, clk_source, 0), \
|
||||||
|
.mclk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(n, mclk)), \
|
||||||
|
.mclk_name = (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(n, mclk, name)}; \
|
||||||
|
\
|
||||||
|
DEVICE_DT_INST_DEFINE(n, NULL, NULL, NULL, &wm8904_device_config_##n, \
|
||||||
|
POST_KERNEL, CONFIG_AUDIO_CODEC_INIT_PRIORITY, &wm8904_driver_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(WM8904_INIT)
|
154
drivers/audio/wm8904.h
Normal file
154
drivers/audio/wm8904.h
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 NXP
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_INCLUDE_CODEC_WM8904_H_
|
||||||
|
#define ZEPHYR_INCLUDE_CODEC_WM8904_H_
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Definitions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#define WM8904_REG_RESET (0x00)
|
||||||
|
#define WM8904_REG_ANALOG_ADC_0 (0x0A)
|
||||||
|
#define WM8904_REG_POWER_MGMT_0 (0x0C)
|
||||||
|
#define WM8904_REG_POWER_MGMT_2 (0x0E)
|
||||||
|
#define WM8904_REG_POWER_MGMT_3 (0x0F)
|
||||||
|
#define WM8904_REG_POWER_MGMT_6 (0x12)
|
||||||
|
#define WM8904_REG_CLK_RATES_0 (0x14)
|
||||||
|
#define WM8904_REG_CLK_RATES_1 (0x15)
|
||||||
|
#define WM8904_REG_CLK_RATES_2 (0x16)
|
||||||
|
#define WM8904_REG_AUDIO_IF_0 (0x18)
|
||||||
|
#define WM8904_REG_AUDIO_IF_1 (0x19)
|
||||||
|
#define WM8904_REG_AUDIO_IF_2 (0x1A)
|
||||||
|
#define WM8904_REG_AUDIO_IF_3 (0x1B)
|
||||||
|
#define WM8904_REG_DAC_DIG_1 (0x21)
|
||||||
|
#define WM8904_REG_DAC_DIG_0 (0x27)
|
||||||
|
#define WM8904_REG_ANALOG_LEFT_IN_0 (0x2C)
|
||||||
|
#define WM8904_REG_ANALOG_RIGHT_IN_0 (0x2D)
|
||||||
|
#define WM8904_REG_ANALOG_LEFT_IN_1 (0x2E)
|
||||||
|
#define WM8904_REG_ANALOG_RIGHT_IN_1 (0x2F)
|
||||||
|
#define WM8904_REG_ANALOG_OUT1_LEFT (0x39)
|
||||||
|
#define WM8904_REG_ANALOG_OUT1_RIGHT (0x3A)
|
||||||
|
#define WM8904_REG_ANALOG_OUT12_ZC (0x3D)
|
||||||
|
#define WM8904_REG_DC_SERVO_0 (0x43)
|
||||||
|
#define WM8904_REG_ANALOG_HP_0 (0x5A)
|
||||||
|
#define WM8904_REG_CHRG_PUMP_0 (0x62)
|
||||||
|
#define WM8904_REG_CLS_W_0 (0x68)
|
||||||
|
#define WM8904_REG_WRT_SEQUENCER_0 (0x6C)
|
||||||
|
#define WM8904_REG_WRT_SEQUENCER_3 (0x6F)
|
||||||
|
#define WM8904_REG_WRT_SEQUENCER_4 (0x70)
|
||||||
|
#define WM8904_REG_DAC_DIGITAL_VOLUME_LEFT (0x1E)
|
||||||
|
#define WM8904_REG_DAC_DIGITAL_VOLUME_RIGHT (0x1F)
|
||||||
|
#define WM8904_REG_ADC_DIGITAL_VOLUME_LEFT (0x24)
|
||||||
|
#define WM8904_REG_ADC_DIGITAL_VOLUME_RIGHT (0x25)
|
||||||
|
#define WM8904_REG_ANALOG_OUT2_LEFT (0x3B)
|
||||||
|
#define WM8904_REG_ANALOG_OUT2_RIGHT (0x3C)
|
||||||
|
#define WM8904_REG_GPIO_CONTROL_4 (0x7C)
|
||||||
|
/* FLL control register */
|
||||||
|
#define WM8904_REG_FLL_CONTROL_1 (0x74)
|
||||||
|
#define WM8904_REG_FLL_CONTROL_2 (0x75)
|
||||||
|
#define WM8904_REG_FLL_CONTROL_3 (0x76)
|
||||||
|
#define WM8904_REG_FLL_CONTROL_4 (0x77)
|
||||||
|
#define WM8904_REG_FLL_CONTROL_5 (0x78)
|
||||||
|
/* GPIO control register */
|
||||||
|
#define WM8904_REG_GPIO_CONTROL_1 (0x79)
|
||||||
|
#define WM8904_REG_GPIO_CONTROL_2 (0x7A)
|
||||||
|
#define WM8904_REG_GPIO_CONTROL_3 (0x7B)
|
||||||
|
#define WM8904_REG_GPIO_CONTROL_4 (0x7C)
|
||||||
|
/* fll nco */
|
||||||
|
#define WM8904_REG_FLL_NCO_TEST_0 (0xF7U)
|
||||||
|
#define WM8904_REG_FLL_NCO_TEST_1 (0xF8U)
|
||||||
|
|
||||||
|
#define WM8904_OUTPUT_VOLUME_MIN (0b000000)
|
||||||
|
#define WM8904_OUTPUT_VOLUME_MAX (0b111111)
|
||||||
|
#define WM8904_OUTPUT_VOLUME_DEFAULT (0b101101)
|
||||||
|
#define WM8904_INPUT_VOLUME_MIN (0b00000)
|
||||||
|
#define WM8904_INPUT_VOLUME_MAX (0b11111)
|
||||||
|
#define WM8904_INPUT_VOLUME_DEFAULT (0b00101)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WM8904_ANALOG_OUT1_LEFT, WM8904_ANALOG_OUT1_RIGHT (headphone outs),
|
||||||
|
* WM8904_ANALOG_OUT2_LEFT, WM8904_ANALOG_OUT2_RIGHT (line outs):
|
||||||
|
* [8] - MUTE: Output mute
|
||||||
|
* [7] - VU: Volume update, works for entire channel pair
|
||||||
|
* [6] - ZC: Zero-crossing enable
|
||||||
|
* [5:0] - VOL: 6-bit volume value
|
||||||
|
*/
|
||||||
|
#define WM8904_REGVAL_OUT_VOL(mute, vu, zc, vol) \
|
||||||
|
(((mute & 0b1) << 8) | (vu & 0b1) << 7 | (zc & 0b1) << 6 | (vol & 0b000111111))
|
||||||
|
#define WM8904_REGMASK_OUT_MUTE 0b100000000
|
||||||
|
#define WM8904_REGMASK_OUT_VU 0b010000000
|
||||||
|
#define WM8904_REGMASK_OUT_ZC 0b001000000
|
||||||
|
#define WM8904_REGMASK_OUT_VOL 0b000111111
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WM8904_ANALOG_LEFT_IN_0, WM8904_ANALOG_RIGHT_IN_0:
|
||||||
|
* [7] - MUTE: Input mute
|
||||||
|
* [4:0] - VOL: 5 bit volume value
|
||||||
|
*/
|
||||||
|
#define WM8904_REGVAL_IN_VOL(mute, vol) \
|
||||||
|
((mute & 0b1) << 7 | (vol & 0b00011111))
|
||||||
|
#define WM8904_REGMASK_IN_MUTE 0b10000000
|
||||||
|
#define WM8904_REGMASK_IN_VOLUME 0b00011111
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WM8904_ANALOG_LEFT_IN_1, WM8904_ANALOG_RIGHT_IN_1:
|
||||||
|
* [6] - INx_CM_ENA: Common-mode rejection enable (N/A for single-mode)
|
||||||
|
* [5:4] - x_IP_SEL_N: Inverting input selection
|
||||||
|
* [3:2] - x_IP_SEL_P: Non-inverting input selection
|
||||||
|
* [1:0] - x_MODE: Input mode
|
||||||
|
*/
|
||||||
|
#define WM8904_REGVAL_INSEL(cm, nin, pin, mode) \
|
||||||
|
(((cm) & 0b1) << 6) | (((nin) & 0b11) << 4)
|
||||||
|
#define WM8904_REGMASK_INSEL_CMENA 0b01000000
|
||||||
|
#define WM8904_REGMASK_INSEL_IP_SEL_N 0b00110000
|
||||||
|
#define WM8904_REGMASK_INSEL_IP_SEL_P 0b00001100
|
||||||
|
#define WM8904_REGMASK_INSEL_MODE 0b00000011
|
||||||
|
|
||||||
|
|
||||||
|
/*!@brief WM8904 maximum volume */
|
||||||
|
#define WM8904_MAP_HEADPHONE_LINEOUT_MAX_VOLUME 0x3FU
|
||||||
|
#define WM8904_DAC_MAX_VOLUME 0xC0U
|
||||||
|
|
||||||
|
|
||||||
|
/*! @brief The audio data transfer protocol. */
|
||||||
|
typedef enum _wm8904_protocol {
|
||||||
|
kWM8904_ProtocolI2S = 0x2, /*!< I2S type */
|
||||||
|
kWM8904_ProtocolLeftJustified = 0x1, /*!< Left justified mode */
|
||||||
|
kWM8904_ProtocolRightJustified = 0x0, /*!< Right justified mode */
|
||||||
|
kWM8904_ProtocolPCMA = 0x3, /*!< PCM A mode */
|
||||||
|
kWM8904_ProtocolPCMB = 0x3 | (1 << 4), /*!< PCM B mode */
|
||||||
|
} wm8904_protocol_t;
|
||||||
|
|
||||||
|
/*! @brief The SYSCLK / fs ratio. */
|
||||||
|
typedef enum _wm8904_fs_ratio {
|
||||||
|
kWM8904_FsRatio64X = 0x0, /*!< SYSCLK is 64 * sample rate * frame width */
|
||||||
|
kWM8904_FsRatio128X = 0x1, /*!< SYSCLK is 128 * sample rate * frame width */
|
||||||
|
kWM8904_FsRatio192X = 0x2, /*!< SYSCLK is 192 * sample rate * frame width */
|
||||||
|
kWM8904_FsRatio256X = 0x3, /*!< SYSCLK is 256 * sample rate * frame width */
|
||||||
|
kWM8904_FsRatio384X = 0x4, /*!< SYSCLK is 384 * sample rate * frame width */
|
||||||
|
kWM8904_FsRatio512X = 0x5, /*!< SYSCLK is 512 * sample rate * frame width */
|
||||||
|
kWM8904_FsRatio768X = 0x6, /*!< SYSCLK is 768 * sample rate * frame width */
|
||||||
|
kWM8904_FsRatio1024X = 0x7, /*!< SYSCLK is 1024 * sample rate * frame width */
|
||||||
|
kWM8904_FsRatio1408X = 0x8, /*!< SYSCLK is 1408 * sample rate * frame width */
|
||||||
|
kWM8904_FsRatio1536X = 0x9 /*!< SYSCLK is 1536 * sample rate * frame width */
|
||||||
|
} wm8904_fs_ratio_t;
|
||||||
|
|
||||||
|
|
||||||
|
/*! @brief Sample rate. */
|
||||||
|
typedef enum _wm8904_sample_rate {
|
||||||
|
kWM8904_SampleRate8kHz = 0x0, /*!< 8 kHz */
|
||||||
|
kWM8904_SampleRate12kHz = 0x1, /*!< 12kHz */
|
||||||
|
kWM8904_SampleRate16kHz = 0x2, /*!< 16kHz */
|
||||||
|
kWM8904_SampleRate24kHz = 0x3, /*!< 24kHz */
|
||||||
|
kWM8904_SampleRate32kHz = 0x4, /*!< 32kHz */
|
||||||
|
kWM8904_SampleRate48kHz = 0x5, /*!< 48kHz */
|
||||||
|
kWM8904_SampleRate11025Hz = 0x6, /*!< 11.025kHz */
|
||||||
|
kWM8904_SampleRate22050Hz = 0x7, /*!< 22.05kHz */
|
||||||
|
kWM8904_SampleRate44100Hz = 0x8 /*!< 44.1kHz */
|
||||||
|
} wm8904_sample_rate_t;
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_INCLUDE_CODEC_WM8904_H_ */
|
25
dts/bindings/audio/wolfson,wm8904.yaml
Normal file
25
dts/bindings/audio/wolfson,wm8904.yaml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Copyright 2024 NXP
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: WM8904 audio codec
|
||||||
|
|
||||||
|
include: [i2c-device.yaml]
|
||||||
|
|
||||||
|
compatible: "wolfson,wm8904"
|
||||||
|
|
||||||
|
properties:
|
||||||
|
reg:
|
||||||
|
required: true
|
||||||
|
clock-source:
|
||||||
|
type: string
|
||||||
|
default: "MCLK"
|
||||||
|
description: |
|
||||||
|
Codec's internal clock signal (SYSCLK) source selection. These options
|
||||||
|
are available:
|
||||||
|
- "MCLK": WM8904's MCLK pin (supplied by the host)
|
||||||
|
- "FLL": WM8904's FLL facility, can be free-running
|
||||||
|
The "MCLK" option is default, as this clock signal is usually supplied
|
||||||
|
by the host.
|
||||||
|
enum:
|
||||||
|
- "MCLK"
|
||||||
|
- "FLL"
|
Loading…
Add table
Add a link
Reference in a new issue