Browse Source
Add driver for the Texas Instruments TLV320AIC3110 audio codec. Signed-off-by: Florijan Plohl <florijan.plohl@norik.com>pull/84916/head
6 changed files with 902 additions and 0 deletions
@ -0,0 +1,10 @@ |
|||||||
|
# Copyright (c) 2025 PHYTEC America LLC |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
config AUDIO_TLV320AIC3110 |
||||||
|
bool "TLV320AIC3110 AIC support" |
||||||
|
default y |
||||||
|
depends on DT_HAS_TI_TLV320AIC3110_ENABLED |
||||||
|
select I2C |
||||||
|
help |
||||||
|
Enable TLV320AIC3110 support on the selected board |
@ -0,0 +1,582 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 PHYTEC America LLC |
||||||
|
* Author: Florijan Plohl <florijan.plohl@norik.com> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#define DT_DRV_COMPAT ti_tlv320aic3110 |
||||||
|
|
||||||
|
#include <errno.h> |
||||||
|
|
||||||
|
#include <zephyr/sys/util.h> |
||||||
|
|
||||||
|
#include <zephyr/device.h> |
||||||
|
#include <zephyr/drivers/i2c.h> |
||||||
|
#include <zephyr/sys/util.h> |
||||||
|
#include <zephyr/audio/codec.h> |
||||||
|
#include <zephyr/drivers/clock_control.h> |
||||||
|
#include "tlv320aic3110.h" |
||||||
|
|
||||||
|
#define LOG_LEVEL CONFIG_AUDIO_CODEC_LOG_LEVEL |
||||||
|
#include <zephyr/logging/log.h> |
||||||
|
LOG_MODULE_REGISTER(tlv320aic3110); |
||||||
|
|
||||||
|
#define CODEC_OUTPUT_VOLUME_MAX 0 |
||||||
|
#define CODEC_OUTPUT_VOLUME_MIN (-78 * 2) |
||||||
|
|
||||||
|
struct codec_driver_config { |
||||||
|
struct i2c_dt_spec bus; |
||||||
|
int clock_source; |
||||||
|
const struct device *mclk_dev; |
||||||
|
clock_control_subsys_t mclk_name; |
||||||
|
}; |
||||||
|
|
||||||
|
struct codec_driver_data { |
||||||
|
struct reg_addr reg_addr_cache; |
||||||
|
}; |
||||||
|
|
||||||
|
static void codec_write_reg(const struct device *dev, struct reg_addr reg, uint8_t val); |
||||||
|
static void codec_read_reg(const struct device *dev, struct reg_addr reg, uint8_t *val); |
||||||
|
static void codec_update_reg(const struct device *dev, struct reg_addr reg, uint8_t mask, |
||||||
|
uint8_t val); |
||||||
|
static void codec_soft_reset(const struct device *dev); |
||||||
|
static int codec_configure_dai(const struct device *dev, audio_dai_cfg_t *cfg); |
||||||
|
static int codec_configure_clocks(const struct device *dev, struct audio_codec_cfg *cfg); |
||||||
|
static int codec_configure_filters(const struct device *dev, audio_dai_cfg_t *cfg); |
||||||
|
static void codec_configure_output(const struct device *dev); |
||||||
|
static void codec_configure_input(const struct device *dev); |
||||||
|
static int codec_set_output_volume(const struct device *dev, audio_channel_t channel, int vol); |
||||||
|
|
||||||
|
#if (LOG_LEVEL >= LOG_LEVEL_DEBUG) |
||||||
|
static void codec_read_all_regs(const struct device *dev); |
||||||
|
#define CODEC_DUMP_REGS(dev) codec_read_all_regs((dev)) |
||||||
|
#else |
||||||
|
#define CODEC_DUMP_REGS(dev) |
||||||
|
#endif |
||||||
|
|
||||||
|
static int codec_configure(const struct device *dev, struct audio_codec_cfg *cfg) |
||||||
|
{ |
||||||
|
int ret; |
||||||
|
|
||||||
|
if (cfg->dai_type != AUDIO_DAI_TYPE_I2S) { |
||||||
|
LOG_ERR("dai_type must be AUDIO_DAI_TYPE_I2S"); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
codec_soft_reset(dev); |
||||||
|
|
||||||
|
ret = codec_configure_clocks(dev, cfg); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to configure clocks: %d", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
ret = codec_configure_dai(dev, &cfg->dai_cfg); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to configure DAI: %d", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
ret = codec_configure_filters(dev, &cfg->dai_cfg); |
||||||
|
if (ret) { |
||||||
|
LOG_ERR("Failed to configure filters: %d", ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
codec_configure_input(dev); |
||||||
|
codec_configure_output(dev); |
||||||
|
|
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
static void codec_start_output(const struct device *dev) |
||||||
|
{ |
||||||
|
/* powerup DAC channels */ |
||||||
|
codec_write_reg(dev, DATA_PATH_SETUP_ADDR, DAC_LR_POWERUP_DEFAULT); |
||||||
|
|
||||||
|
/* unmute DAC channels */ |
||||||
|
codec_write_reg(dev, VOL_CTRL_ADDR, VOL_CTRL_UNMUTE_DEFAULT); |
||||||
|
|
||||||
|
CODEC_DUMP_REGS(dev); |
||||||
|
} |
||||||
|
|
||||||
|
static void codec_stop_output(const struct device *dev) |
||||||
|
{ |
||||||
|
/* mute DAC channels */ |
||||||
|
codec_write_reg(dev, VOL_CTRL_ADDR, VOL_CTRL_MUTE_DEFAULT); |
||||||
|
|
||||||
|
/* powerdown DAC channels */ |
||||||
|
codec_write_reg(dev, DATA_PATH_SETUP_ADDR, DAC_LR_POWERDN_DEFAULT); |
||||||
|
} |
||||||
|
|
||||||
|
static void codec_mute_output(const struct device *dev, audio_channel_t channel, bool mute) |
||||||
|
{ |
||||||
|
/* mute channel */ |
||||||
|
switch (channel) { |
||||||
|
case AUDIO_CHANNEL_HEADPHONE_LEFT: |
||||||
|
/**/ |
||||||
|
codec_update_reg(dev, HPL_DRV_GAIN_CTRL_ADDR, HPX_DRV_UNMUTE, |
||||||
|
(mute << 2) | HPX_DRV_RESERVED); |
||||||
|
break; |
||||||
|
|
||||||
|
case AUDIO_CHANNEL_HEADPHONE_RIGHT: |
||||||
|
codec_update_reg(dev, HPR_DRV_GAIN_CTRL_ADDR, HPX_DRV_UNMUTE, |
||||||
|
(mute << 2) | HPX_DRV_RESERVED); |
||||||
|
break; |
||||||
|
|
||||||
|
case AUDIO_CHANNEL_FRONT_LEFT: |
||||||
|
codec_update_reg(dev, SPL_DRV_GAIN_CTRL_ADDR, SPX_DRV_UNMUTE, mute << 2); |
||||||
|
break; |
||||||
|
|
||||||
|
case AUDIO_CHANNEL_FRONT_RIGHT: |
||||||
|
codec_update_reg(dev, SPR_DRV_GAIN_CTRL_ADDR, SPX_DRV_UNMUTE, mute << 2); |
||||||
|
break; |
||||||
|
|
||||||
|
case AUDIO_CHANNEL_ALL: |
||||||
|
codec_update_reg(dev, HPL_DRV_GAIN_CTRL_ADDR, HPX_DRV_UNMUTE, |
||||||
|
(mute << 2) | HPX_DRV_RESERVED); |
||||||
|
codec_update_reg(dev, HPR_DRV_GAIN_CTRL_ADDR, HPX_DRV_UNMUTE, |
||||||
|
(mute << 2) | HPX_DRV_RESERVED); |
||||||
|
codec_update_reg(dev, SPL_DRV_GAIN_CTRL_ADDR, SPX_DRV_UNMUTE, mute << 2); |
||||||
|
codec_update_reg(dev, SPR_DRV_GAIN_CTRL_ADDR, SPX_DRV_UNMUTE, mute << 2); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
LOG_ERR("channel %u invalid.", channel); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static int codec_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 codec_set_output_volume(dev, channel, val.vol); |
||||||
|
|
||||||
|
case AUDIO_PROPERTY_OUTPUT_MUTE: |
||||||
|
codec_mute_output(dev, channel, !val.mute); |
||||||
|
return 0; |
||||||
|
|
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
static int codec_apply_properties(const struct device *dev) |
||||||
|
{ |
||||||
|
/* nothing to do because there is nothing cached */ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void codec_write_reg(const struct device *dev, struct reg_addr reg, uint8_t val) |
||||||
|
{ |
||||||
|
struct codec_driver_data *const dev_data = dev->data; |
||||||
|
const struct codec_driver_config *const dev_cfg = dev->config; |
||||||
|
|
||||||
|
/* set page if different */ |
||||||
|
if (dev_data->reg_addr_cache.page != reg.page) { |
||||||
|
i2c_reg_write_byte_dt(&dev_cfg->bus, 0, reg.page); |
||||||
|
dev_data->reg_addr_cache.page = reg.page; |
||||||
|
} |
||||||
|
|
||||||
|
i2c_reg_write_byte_dt(&dev_cfg->bus, reg.reg_addr, val); |
||||||
|
LOG_DBG("WR PG:%u REG:%02u VAL:0x%02x", reg.page, reg.reg_addr, val); |
||||||
|
} |
||||||
|
|
||||||
|
static void codec_read_reg(const struct device *dev, struct reg_addr reg, uint8_t *val) |
||||||
|
{ |
||||||
|
struct codec_driver_data *const dev_data = dev->data; |
||||||
|
const struct codec_driver_config *const dev_cfg = dev->config; |
||||||
|
|
||||||
|
/* set page if different */ |
||||||
|
if (dev_data->reg_addr_cache.page != reg.page) { |
||||||
|
i2c_reg_write_byte_dt(&dev_cfg->bus, 0, reg.page); |
||||||
|
dev_data->reg_addr_cache.page = reg.page; |
||||||
|
} |
||||||
|
|
||||||
|
i2c_reg_read_byte_dt(&dev_cfg->bus, reg.reg_addr, val); |
||||||
|
LOG_DBG("RD PG:%u REG:%02u VAL:0x%02x", reg.page, reg.reg_addr, *val); |
||||||
|
} |
||||||
|
|
||||||
|
static void codec_update_reg(const struct device *dev, struct reg_addr reg, uint8_t mask, |
||||||
|
uint8_t val) |
||||||
|
{ |
||||||
|
uint8_t reg_val; |
||||||
|
|
||||||
|
codec_read_reg(dev, reg, ®_val); |
||||||
|
reg_val = (reg_val & ~mask) | (val & mask); |
||||||
|
codec_write_reg(dev, reg, reg_val); |
||||||
|
} |
||||||
|
|
||||||
|
static void codec_soft_reset(const struct device *dev) |
||||||
|
{ |
||||||
|
/* soft reset the AIC */ |
||||||
|
codec_write_reg(dev, SOFT_RESET_ADDR, SOFT_RESET_ASSERT); |
||||||
|
} |
||||||
|
|
||||||
|
static int codec_configure_dai(const struct device *dev, audio_dai_cfg_t *cfg) |
||||||
|
{ |
||||||
|
uint8_t val; |
||||||
|
|
||||||
|
/* configure I2S interface */ |
||||||
|
val = IF_CTRL_IFTYPE(IF_CTRL_IFTYPE_I2S); |
||||||
|
if (cfg->i2s.options & I2S_OPT_BIT_CLK_MASTER) { |
||||||
|
val |= IF_CTRL_BCLK_OUT; |
||||||
|
} |
||||||
|
|
||||||
|
if (cfg->i2s.options & I2S_OPT_FRAME_CLK_MASTER) { |
||||||
|
val |= IF_CTRL_WCLK_OUT; |
||||||
|
} |
||||||
|
|
||||||
|
switch (cfg->i2s.word_size) { |
||||||
|
case AUDIO_PCM_WIDTH_16_BITS: |
||||||
|
val |= IF_CTRL_WLEN(IF_CTRL_WLEN_16); |
||||||
|
break; |
||||||
|
case AUDIO_PCM_WIDTH_20_BITS: |
||||||
|
val |= IF_CTRL_WLEN(IF_CTRL_WLEN_20); |
||||||
|
break; |
||||||
|
case AUDIO_PCM_WIDTH_24_BITS: |
||||||
|
val |= IF_CTRL_WLEN(IF_CTRL_WLEN_24); |
||||||
|
break; |
||||||
|
case AUDIO_PCM_WIDTH_32_BITS: |
||||||
|
val |= IF_CTRL_WLEN(IF_CTRL_WLEN_32); |
||||||
|
break; |
||||||
|
default: |
||||||
|
LOG_ERR("Unsupported PCM sample bit width %u", cfg->i2s.word_size); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
codec_write_reg(dev, IF_CTRL1_ADDR, val); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int codec_configure_clocks(const struct device *dev, struct audio_codec_cfg *cfg) |
||||||
|
{ |
||||||
|
struct i2s_config *i2s; |
||||||
|
const struct codec_driver_config *const dev_cfg = dev->config; |
||||||
|
uint32_t rate; |
||||||
|
int madc, nadc, aosr, mdac, ndac, dosr, bclk_div, mclk_div; |
||||||
|
int p, r, j, d; |
||||||
|
int i, ret; |
||||||
|
|
||||||
|
i2s = &cfg->dai_cfg.i2s; |
||||||
|
|
||||||
|
/* get MCLK rate */ |
||||||
|
if (dev_cfg->clock_source == 0) { |
||||||
|
ret = clock_control_get_rate(dev_cfg->mclk_dev, dev_cfg->mclk_name, |
||||||
|
&cfg->mclk_freq); |
||||||
|
if (ret < 0) { |
||||||
|
LOG_ERR("MCLK clock source freq acquire fail: %d", ret); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
LOG_DBG("MCLK %u Hz Sampling Rate: %u Hz", cfg->mclk_freq, i2s->frame_clk_freq); |
||||||
|
|
||||||
|
/* use pll div table to get all values */ |
||||||
|
rate = i2s->frame_clk_freq; |
||||||
|
for (i = 0; i < ARRAY_SIZE(pll_div_table); i++) { |
||||||
|
if (rate == pll_div_table[i].rate && cfg->mclk_freq == pll_div_table[i].mclk) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (i == ARRAY_SIZE(pll_div_table)) { |
||||||
|
LOG_ERR("Unable to find PLL dividers for MCLK %u Hz PCM Rate: %u Hz", |
||||||
|
cfg->mclk_freq, i2s->frame_clk_freq); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
p = pll_div_table[i].pll_p; |
||||||
|
r = 1; |
||||||
|
j = pll_div_table[i].pll_j; |
||||||
|
d = pll_div_table[i].pll_d; |
||||||
|
|
||||||
|
/* set the dividers */ |
||||||
|
codec_write_reg(dev, PLL_P_R_ADDR, (1 << 7) | PLL_P(p) | PLL_R(r)); |
||||||
|
codec_write_reg(dev, PLL_J_ADDR, j); |
||||||
|
codec_write_reg(dev, PLL_D_MSB_ADDR, (uint8_t)(d >> 8)); |
||||||
|
codec_write_reg(dev, PLL_D_LSB_ADDR, (uint8_t)(d & 0xFF)); |
||||||
|
|
||||||
|
madc = pll_div_table[i].madc; |
||||||
|
nadc = pll_div_table[i].nadc; |
||||||
|
aosr = pll_div_table[i].aosr; |
||||||
|
|
||||||
|
ndac = pll_div_table[i].ndac; |
||||||
|
mdac = pll_div_table[i].mdac; |
||||||
|
dosr = pll_div_table[i].dosr; |
||||||
|
|
||||||
|
LOG_DBG("PLLP: %u PLLJ: %u PLLD: %u", pll_div_table[i].pll_p, pll_div_table[i].pll_j, |
||||||
|
pll_div_table[i].pll_d); |
||||||
|
LOG_DBG("MDAC: %u NDAC: %u DOSR: %u", mdac, ndac, dosr); |
||||||
|
LOG_DBG("MADC: %u NADC: %u AOSR: %u", madc, nadc, aosr); |
||||||
|
|
||||||
|
if (i2s->options & I2S_OPT_BIT_CLK_MASTER) { |
||||||
|
bclk_div = dosr * mdac / (i2s->word_size * 2U); /* stereo */ |
||||||
|
if ((bclk_div * i2s->word_size * 2) != (dosr * mdac)) { |
||||||
|
LOG_ERR("Unable to generate BCLK %u from MCLK %u", |
||||||
|
i2s->frame_clk_freq * i2s->word_size * 2U, cfg->mclk_freq); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
LOG_DBG("I2S Master BCLKDIV: %u", bclk_div); |
||||||
|
codec_write_reg(dev, BCLK_DIV_ADDR, BCLK_DIV_POWER_UP | BCLK_DIV(bclk_div)); |
||||||
|
} |
||||||
|
|
||||||
|
/* Set clock gen mux and turn on PLL */ |
||||||
|
codec_write_reg(dev, CLOCK_GEN_MUX_ADDR, CLOCK_GEN_MUX_DEFAULT); |
||||||
|
codec_update_reg(dev, PLL_P_R_ADDR, PLL_POWER_UP, PLL_POWER_UP); |
||||||
|
|
||||||
|
/* set NDAC, then MDAC, followed by OSR */ |
||||||
|
codec_write_reg(dev, NDAC_DIV_ADDR, (uint8_t)(NDAC_DIV(ndac) | NDAC_POWER_UP_MASK)); |
||||||
|
codec_write_reg(dev, MDAC_DIV_ADDR, (uint8_t)(MDAC_DIV(mdac) | MDAC_POWER_UP_MASK)); |
||||||
|
codec_write_reg(dev, OSR_MSB_ADDR, (uint8_t)((dosr >> 8) & OSR_MSB_MASK)); |
||||||
|
codec_write_reg(dev, OSR_LSB_ADDR, (uint8_t)(dosr & OSR_LSB_MASK)); |
||||||
|
|
||||||
|
/* set NADC, MADC, OSR */ |
||||||
|
codec_write_reg(dev, NADC_DIV_ADDR, (uint8_t)(NADC_DIV(nadc) | NADC_POWER_UP_MASK)); |
||||||
|
codec_write_reg(dev, MADC_DIV_ADDR, (uint8_t)(MADC_DIV(madc) | MADC_POWER_UP_MASK)); |
||||||
|
codec_write_reg(dev, AOSR_ADDR, (uint8_t)aosr); |
||||||
|
|
||||||
|
if (i2s->options & I2S_OPT_BIT_CLK_MASTER) { |
||||||
|
codec_write_reg(dev, BCLK_DIV_ADDR, BCLK_DIV(bclk_div) | BCLK_DIV_POWER_UP); |
||||||
|
} |
||||||
|
|
||||||
|
/* calculate MCLK divider to get ~1MHz */ |
||||||
|
mclk_div = DIV_ROUND_UP(cfg->mclk_freq, 1000000); |
||||||
|
/* setup timer clock to be MCLK divided */ |
||||||
|
codec_write_reg(dev, TIMER_MCLK_DIV_ADDR, |
||||||
|
TIMER_MCLK_DIV_EN_EXT | TIMER_MCLK_DIV_VAL(mclk_div)); |
||||||
|
LOG_DBG("Timer MCLK Divider: %u", mclk_div); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int codec_configure_filters(const struct device *dev, audio_dai_cfg_t *cfg) |
||||||
|
{ |
||||||
|
enum dac_proc_block dac_proc_blk; |
||||||
|
enum adc_proc_block adc_proc_blk; |
||||||
|
|
||||||
|
/* determine decimation filter type */ |
||||||
|
if (cfg->i2s.frame_clk_freq >= AUDIO_PCM_RATE_192K) { |
||||||
|
dac_proc_blk = PRB_P17_DECIMATION_C; |
||||||
|
adc_proc_blk = PRB_R16_DECIMATION_C; |
||||||
|
LOG_INF("PCM Rate: %u Filter C PRB P17/R16 selected", cfg->i2s.frame_clk_freq); |
||||||
|
} else if (cfg->i2s.frame_clk_freq >= AUDIO_PCM_RATE_96K) { |
||||||
|
dac_proc_blk = PRB_P7_DECIMATION_B; |
||||||
|
adc_proc_blk = PRB_R10_DECIMATION_B; |
||||||
|
LOG_INF("PCM Rate: %u Filter B PRB P7/R10 selected", cfg->i2s.frame_clk_freq); |
||||||
|
} else { |
||||||
|
dac_proc_blk = PRB_P1_DECIMATION_A; |
||||||
|
adc_proc_blk = PRB_R4_DECIMATION_A; |
||||||
|
LOG_INF("PCM Rate: %u Filter A PRB P1/R4 selected", cfg->i2s.frame_clk_freq); |
||||||
|
} |
||||||
|
|
||||||
|
codec_write_reg(dev, DAC_PROC_BLK_SEL_ADDR, dac_proc_blk); |
||||||
|
codec_write_reg(dev, ADC_PROC_BLK_SEL_ADDR, adc_proc_blk); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void codec_configure_output(const struct device *dev) |
||||||
|
{ |
||||||
|
uint8_t val; |
||||||
|
|
||||||
|
/*
|
||||||
|
* set common mode voltage to 1.65V (half of AVDD) |
||||||
|
* AVDD is typically 3.3V |
||||||
|
*/ |
||||||
|
codec_read_reg(dev, HEADPHONE_DRV_ADDR, &val); |
||||||
|
val &= ~HEADPHONE_DRV_CM_MASK; |
||||||
|
val |= HEADPHONE_DRV_CM(CM_VOLTAGE_1P65) | HEADPHONE_DRV_RESERVED; |
||||||
|
codec_write_reg(dev, HEADPHONE_DRV_ADDR, val); |
||||||
|
|
||||||
|
/* enable pop removal on power down/up */ |
||||||
|
codec_read_reg(dev, HP_OUT_POP_RM_ADDR, &val); |
||||||
|
codec_write_reg(dev, HP_OUT_POP_RM_ADDR, val | HP_OUT_POP_RM_ENABLE); |
||||||
|
|
||||||
|
/* route DAC output to mixer */ |
||||||
|
val = OUTPUT_ROUTING_MIXER; |
||||||
|
codec_write_reg(dev, OUTPUT_ROUTING_ADDR, val); |
||||||
|
|
||||||
|
/* enable volume control on Headphone out and Speaker out */ |
||||||
|
codec_write_reg(dev, HPL_ANA_VOL_CTRL_ADDR, HPX_ANA_VOL(HPX_ANA_VOL_DEFAULT)); |
||||||
|
codec_write_reg(dev, HPR_ANA_VOL_CTRL_ADDR, HPX_ANA_VOL(HPX_ANA_VOL_DEFAULT)); |
||||||
|
codec_write_reg(dev, SPL_ANA_VOL_CTRL_ADDR, SPX_ANA_VOL(SPX_ANA_VOL_DEFAULT)); |
||||||
|
codec_write_reg(dev, SPR_ANA_VOL_CTRL_ADDR, SPX_ANA_VOL(SPX_ANA_VOL_DEFAULT)); |
||||||
|
|
||||||
|
/* unmute headphone and speaker drivers */ |
||||||
|
codec_write_reg(dev, HPL_DRV_GAIN_CTRL_ADDR, HPX_DRV_UNMUTE | HPX_DRV_RESERVED); |
||||||
|
codec_write_reg(dev, HPR_DRV_GAIN_CTRL_ADDR, HPX_DRV_UNMUTE | HPX_DRV_RESERVED); |
||||||
|
codec_write_reg(dev, SPL_DRV_GAIN_CTRL_ADDR, SPX_DRV_UNMUTE); |
||||||
|
codec_write_reg(dev, SPR_DRV_GAIN_CTRL_ADDR, SPX_DRV_UNMUTE); |
||||||
|
|
||||||
|
/* power up headphone drivers */ |
||||||
|
codec_read_reg(dev, HEADPHONE_DRV_ADDR, &val); |
||||||
|
val |= HEADPHONE_DRV_POWERUP | HEADPHONE_DRV_RESERVED; |
||||||
|
codec_write_reg(dev, HEADPHONE_DRV_ADDR, val); |
||||||
|
|
||||||
|
/* power up speaker drivers */ |
||||||
|
codec_read_reg(dev, SPEAKER_DRV_ADDR, &val); |
||||||
|
val |= SPEAKER_DRV_POWERUP | SPEAKER_DRV_RESERVED; |
||||||
|
codec_write_reg(dev, SPEAKER_DRV_ADDR, val); |
||||||
|
LOG_INF("Headphone driver and Class-D amplifier powered up"); |
||||||
|
} |
||||||
|
|
||||||
|
static void codec_configure_input(const struct device *dev) |
||||||
|
{ |
||||||
|
uint8_t val; |
||||||
|
|
||||||
|
/* power up ADC channel */ |
||||||
|
codec_write_reg(dev, MIC_ADC_CTRL_ADDR, MIC_ADC_POWERUP); |
||||||
|
|
||||||
|
/* set microphone bias - THIS SHOULD BE FIXED*/ |
||||||
|
codec_write_reg(dev, MIC_BIAS_ADDR, MICBIAS_DEFAULT); |
||||||
|
|
||||||
|
/* unmute microphone input */ |
||||||
|
codec_write_reg(dev, MIC_FCTRL_ADDR, MIC_FCTRL_DEFAULT); |
||||||
|
|
||||||
|
/* set PGA, D7 enables PGA control, D6-D0 sets volume */ |
||||||
|
codec_write_reg(dev, MIC_PGA_ADDR, MIC_PGA_VOL_DEFAULT); |
||||||
|
|
||||||
|
/* select both MIC inputs for PGA and their resistance */ |
||||||
|
val = MIC_PGAPI_L_DEFAULT | MIC_PGAPI_R_DEFAULT; |
||||||
|
codec_write_reg(dev, MIC_PGAPI_ADDR, val); |
||||||
|
LOG_INF("Microphone bias and PGA configured"); |
||||||
|
} |
||||||
|
|
||||||
|
static int codec_set_output_volume(const struct device *dev, audio_channel_t channel, int vol) |
||||||
|
{ |
||||||
|
struct reg_addr reg; |
||||||
|
uint8_t vol_val; |
||||||
|
int vol_index; |
||||||
|
uint8_t vol_array[] = {107, 108, 110, 113, 116, 120, 125, 128, 132, 138, 144}; |
||||||
|
|
||||||
|
if ((vol > CODEC_OUTPUT_VOLUME_MAX) || (vol < CODEC_OUTPUT_VOLUME_MIN)) { |
||||||
|
LOG_ERR("Invalid volume %d.%d dB", vol >> 1, ((uint32_t)vol & 1) ? 5 : 0); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
/* remove sign */ |
||||||
|
vol = -vol; |
||||||
|
|
||||||
|
/* if volume is near floor, set minimum */ |
||||||
|
if (vol > HPX_ANA_VOL_FLOOR) { |
||||||
|
vol_val = HPX_ANA_VOL_FLOOR; |
||||||
|
} else if (vol > HPX_ANA_VOL_LOW_THRESH) { |
||||||
|
/* lookup low volume values */ |
||||||
|
for (vol_index = 0; vol_index < ARRAY_SIZE(vol_array); vol_index++) { |
||||||
|
if (vol_array[vol_index] >= vol) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
vol_val = HPX_ANA_VOL_LOW_THRESH + vol_index + 1; |
||||||
|
} else { |
||||||
|
vol_val = (uint8_t)vol; |
||||||
|
} |
||||||
|
LOG_INF("Writing value to %u channel: %u", channel, vol_val); |
||||||
|
|
||||||
|
switch (channel) { |
||||||
|
case AUDIO_CHANNEL_HEADPHONE_LEFT: |
||||||
|
reg = HPL_ANA_VOL_CTRL_ADDR; |
||||||
|
break; |
||||||
|
|
||||||
|
case AUDIO_CHANNEL_HEADPHONE_RIGHT: |
||||||
|
reg = HPR_ANA_VOL_CTRL_ADDR; |
||||||
|
break; |
||||||
|
|
||||||
|
case AUDIO_CHANNEL_FRONT_LEFT: |
||||||
|
reg = SPL_ANA_VOL_CTRL_ADDR; |
||||||
|
break; |
||||||
|
|
||||||
|
case AUDIO_CHANNEL_FRONT_RIGHT: |
||||||
|
reg = SPR_ANA_VOL_CTRL_ADDR; |
||||||
|
break; |
||||||
|
|
||||||
|
case AUDIO_CHANNEL_ALL: |
||||||
|
codec_write_reg(dev, HPL_ANA_VOL_CTRL_ADDR, HPX_ANA_VOL(vol_val)); |
||||||
|
codec_write_reg(dev, HPR_ANA_VOL_CTRL_ADDR, HPX_ANA_VOL(vol_val)); |
||||||
|
codec_write_reg(dev, SPL_ANA_VOL_CTRL_ADDR, SPX_ANA_VOL(vol_val)); |
||||||
|
codec_write_reg(dev, SPR_ANA_VOL_CTRL_ADDR, SPX_ANA_VOL(vol_val)); |
||||||
|
return 0; |
||||||
|
|
||||||
|
default: |
||||||
|
LOG_ERR("channel %u invalid.", channel); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
codec_write_reg(dev, reg, HPX_ANA_VOL(vol_val)); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
#if (LOG_LEVEL >= LOG_LEVEL_DEBUG) |
||||||
|
static void codec_read_all_regs(const struct device *dev) |
||||||
|
{ |
||||||
|
uint8_t val; |
||||||
|
|
||||||
|
codec_read_reg(dev, SOFT_RESET_ADDR, &val); |
||||||
|
codec_read_reg(dev, PLL_P_R_ADDR, &val); |
||||||
|
codec_read_reg(dev, PLL_J_ADDR, &val); |
||||||
|
codec_read_reg(dev, PLL_D_MSB_ADDR, &val); |
||||||
|
codec_read_reg(dev, PLL_D_LSB_ADDR, &val); |
||||||
|
codec_read_reg(dev, NDAC_DIV_ADDR, &val); |
||||||
|
codec_read_reg(dev, MDAC_DIV_ADDR, &val); |
||||||
|
codec_read_reg(dev, OSR_MSB_ADDR, &val); |
||||||
|
codec_read_reg(dev, OSR_LSB_ADDR, &val); |
||||||
|
codec_read_reg(dev, NADC_DIV_ADDR, &val); |
||||||
|
codec_read_reg(dev, MADC_DIV_ADDR, &val); |
||||||
|
codec_read_reg(dev, AOSR_ADDR, &val); |
||||||
|
codec_read_reg(dev, IF_CTRL1_ADDR, &val); |
||||||
|
codec_read_reg(dev, BCLK_DIV_ADDR, &val); |
||||||
|
codec_read_reg(dev, OVF_FLAG_ADDR, &val); |
||||||
|
codec_read_reg(dev, DAC_PROC_BLK_SEL_ADDR, &val); |
||||||
|
codec_read_reg(dev, ADC_PROC_BLK_SEL_ADDR, &val); |
||||||
|
codec_read_reg(dev, DATA_PATH_SETUP_ADDR, &val); |
||||||
|
codec_read_reg(dev, VOL_CTRL_ADDR, &val); |
||||||
|
codec_read_reg(dev, L_DIG_VOL_CTRL_ADDR, &val); |
||||||
|
|
||||||
|
codec_read_reg(dev, MIC_PGA_ADDR, &val); |
||||||
|
|
||||||
|
codec_read_reg(dev, DRC_CTRL1_ADDR, &val); |
||||||
|
codec_read_reg(dev, L_BEEP_GEN_ADDR, &val); |
||||||
|
codec_read_reg(dev, R_BEEP_GEN_ADDR, &val); |
||||||
|
codec_read_reg(dev, BEEP_LEN_MSB_ADDR, &val); |
||||||
|
codec_read_reg(dev, BEEP_LEN_MIB_ADDR, &val); |
||||||
|
codec_read_reg(dev, BEEP_LEN_LSB_ADDR, &val); |
||||||
|
|
||||||
|
codec_read_reg(dev, HEADPHONE_DRV_ADDR, &val); |
||||||
|
codec_read_reg(dev, HP_OUT_POP_RM_ADDR, &val); |
||||||
|
codec_read_reg(dev, OUTPUT_ROUTING_ADDR, &val); |
||||||
|
codec_read_reg(dev, HPL_ANA_VOL_CTRL_ADDR, &val); |
||||||
|
codec_read_reg(dev, HPR_ANA_VOL_CTRL_ADDR, &val); |
||||||
|
codec_read_reg(dev, HPL_DRV_GAIN_CTRL_ADDR, &val); |
||||||
|
codec_read_reg(dev, HPR_DRV_GAIN_CTRL_ADDR, &val); |
||||||
|
codec_read_reg(dev, HEADPHONE_DRV_CTRL_ADDR, &val); |
||||||
|
|
||||||
|
codec_read_reg(dev, SPL_DRV_GAIN_CTRL_ADDR, &val); |
||||||
|
codec_read_reg(dev, SPR_DRV_GAIN_CTRL_ADDR, &val); |
||||||
|
|
||||||
|
codec_read_reg(dev, TIMER_MCLK_DIV_ADDR, &val); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
static const struct audio_codec_api codec_driver_api = { |
||||||
|
.configure = codec_configure, |
||||||
|
.start_output = codec_start_output, |
||||||
|
.stop_output = codec_stop_output, |
||||||
|
.set_property = codec_set_property, |
||||||
|
.apply_properties = codec_apply_properties, |
||||||
|
}; |
||||||
|
|
||||||
|
#define TLV320AIC3110_INIT(n) \ |
||||||
|
static const struct codec_driver_config codec_device_config_##n = { \ |
||||||
|
.bus = I2C_DT_SPEC_INST_GET(n), \ |
||||||
|
.clock_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, &codec_device_config_##n, POST_KERNEL, \ |
||||||
|
CONFIG_AUDIO_CODEC_INIT_PRIORITY, &codec_driver_api); |
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(TLV320AIC3110_INIT) |
@ -0,0 +1,300 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 PHYTEC America LLC |
||||||
|
* Author: Florijan Plohl <florijan.plohl@norik.com> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ZEPHYR_DRIVERS_AUDIO_TLV320DAC310X_H_ |
||||||
|
#define ZEPHYR_DRIVERS_AUDIO_TLV320DAC310X_H_ |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Register addresses */ |
||||||
|
#define PAGE_CONTROL_ADDR 0 |
||||||
|
|
||||||
|
/* Register addresses {page, address} and fields */ |
||||||
|
#define SOFT_RESET_ADDR (struct reg_addr){0, 1} |
||||||
|
#define SOFT_RESET_ASSERT (1) |
||||||
|
|
||||||
|
#define CLOCK_GEN_MUX_ADDR (struct reg_addr){0, 4} |
||||||
|
#define CLOCK_GEN_MUX_DEFAULT (0x3) |
||||||
|
|
||||||
|
#define PLL_P_R_ADDR (struct reg_addr){0, 5} |
||||||
|
#define PLL_POWER_UP BIT(7) |
||||||
|
#define PLL_POWER_UP_MASK BIT(7) |
||||||
|
#define PLL_P_MASK BIT_MASK(3) |
||||||
|
#define PLL_P(val) (((val) & PLL_P_MASK) << 4) |
||||||
|
#define PLL_R_MASK BIT_MASK(4) |
||||||
|
#define PLL_R(val) ((val) & PLL_R_MASK) |
||||||
|
|
||||||
|
#define PLL_J_ADDR (struct reg_addr){0, 6} |
||||||
|
|
||||||
|
#define PLL_D_MSB_ADDR (struct reg_addr){0, 7} |
||||||
|
#define PLL_D_LSB_ADDR (struct reg_addr){0, 8} |
||||||
|
|
||||||
|
#define NDAC_DIV_ADDR (struct reg_addr){0, 11} |
||||||
|
#define NDAC_POWER_UP BIT(7) |
||||||
|
#define NDAC_POWER_UP_MASK BIT(7) |
||||||
|
#define NDAC_DIV_MASK BIT_MASK(7) |
||||||
|
#define NDAC_DIV(val) ((val) & NDAC_DIV_MASK) |
||||||
|
|
||||||
|
#define MDAC_DIV_ADDR (struct reg_addr){0, 12} |
||||||
|
#define MDAC_POWER_UP BIT(7) |
||||||
|
#define MDAC_POWER_UP_MASK BIT(7) |
||||||
|
#define MDAC_DIV_MASK BIT_MASK(7) |
||||||
|
#define MDAC_DIV(val) ((val) & MDAC_DIV_MASK) |
||||||
|
|
||||||
|
#define OSR_MSB_ADDR (struct reg_addr){0, 13} |
||||||
|
#define OSR_MSB_MASK BIT_MASK(2) |
||||||
|
|
||||||
|
#define OSR_LSB_ADDR (struct reg_addr){0, 14} |
||||||
|
#define OSR_LSB_MASK BIT_MASK(8) |
||||||
|
|
||||||
|
#define NADC_DIV_ADDR (struct reg_addr){0, 18} |
||||||
|
#define NADC_POWER_UP BIT(7) |
||||||
|
#define NADC_POWER_UP_MASK BIT(7) |
||||||
|
#define NADC_DIV_MASK BIT_MASK(7) |
||||||
|
#define NADC_DIV(val) ((val) & NADC_DIV_MASK) |
||||||
|
|
||||||
|
#define MADC_DIV_ADDR (struct reg_addr){0, 19} |
||||||
|
#define MADC_POWER_UP BIT(7) |
||||||
|
#define MADC_POWER_UP_MASK BIT(7) |
||||||
|
#define MADC_DIV_MASK BIT_MASK(7) |
||||||
|
#define MADC_DIV(val) ((val) & MADC_DIV_MASK) |
||||||
|
|
||||||
|
#define AOSR_ADDR (struct reg_addr){0, 20} |
||||||
|
|
||||||
|
#define IF_CTRL1_ADDR (struct reg_addr){0, 27} |
||||||
|
#define IF_CTRL_IFTYPE_MASK BIT_MASK(1) |
||||||
|
#define IF_CTRL_IFTYPE_I2S 0 |
||||||
|
#define IF_CTRL_IFTYPE_DSP 1 |
||||||
|
#define IF_CTRL_IFTYPE_RJF 2 |
||||||
|
#define IF_CTRL_IFTYPE_LJF 3 |
||||||
|
#define IF_CTRL_IFTYPE(val) (((val) & IF_CTRL_IFTYPE_MASK) << 6) |
||||||
|
#define IF_CTRL_WLEN_MASK BIT_MASK(2) |
||||||
|
#define IF_CTRL_WLEN(val) (((val) & IF_CTRL_WLEN_MASK) << 4) |
||||||
|
#define IF_CTRL_WLEN_16 0 |
||||||
|
#define IF_CTRL_WLEN_20 1 |
||||||
|
#define IF_CTRL_WLEN_24 2 |
||||||
|
#define IF_CTRL_WLEN_32 3 |
||||||
|
#define IF_CTRL_BCLK_OUT BIT(3) |
||||||
|
#define IF_CTRL_WCLK_OUT BIT(2) |
||||||
|
#define IF_CTRL_DOUT_HIGH_Z BIT(0) |
||||||
|
|
||||||
|
#define BCLK_DIV_ADDR (struct reg_addr){0, 30} |
||||||
|
#define BCLK_DIV_POWER_UP BIT(7) |
||||||
|
#define BCLK_DIV_POWER_UP_MASK BIT(7) |
||||||
|
#define BCLK_DIV_MASK BIT_MASK(7) |
||||||
|
#define BCLK_DIV(val) ((val) & MDAC_DIV_MASK) |
||||||
|
|
||||||
|
#define OVF_FLAG_ADDR (struct reg_addr){0, 39} |
||||||
|
|
||||||
|
#define DAC_PROC_BLK_SEL_ADDR (struct reg_addr){0, 60} |
||||||
|
#define DAC_PROC_BLK_SEL_MASK BIT_MASK(5) |
||||||
|
#define DAC_PROC_BLK_SEL(val) ((val) & DAC_PROC_BLK_SEL_MASK) |
||||||
|
|
||||||
|
#define ADC_PROC_BLK_SEL_ADDR (struct reg_addr){0, 61} |
||||||
|
#define ADC_PROC_BLK_SEL_MASK BIT_MASK(5) |
||||||
|
#define ADC_PROC_BLK_SEL(val) ((val) & ADC_PROC_BLK_SEL_MASK) |
||||||
|
|
||||||
|
#define DATA_PATH_SETUP_ADDR (struct reg_addr){0, 63} |
||||||
|
#define DAC_LR_POWERUP_DEFAULT (BIT(7) | BIT(6) | BIT(4) | BIT(2)) |
||||||
|
#define DAC_LR_POWERDN_DEFAULT (BIT(4) | BIT(2)) |
||||||
|
|
||||||
|
#define VOL_CTRL_ADDR (struct reg_addr){0, 64} |
||||||
|
#define VOL_CTRL_UNMUTE_DEFAULT (0) |
||||||
|
#define VOL_CTRL_MUTE_DEFAULT (BIT(3) | BIT(2)) |
||||||
|
|
||||||
|
#define L_DIG_VOL_CTRL_ADDR (struct reg_addr){0, 65} |
||||||
|
#define DRC_CTRL1_ADDR (struct reg_addr){0, 68} |
||||||
|
#define L_BEEP_GEN_ADDR (struct reg_addr){0, 71} |
||||||
|
#define BEEP_GEN_EN_BEEP (BIT(7)) |
||||||
|
#define R_BEEP_GEN_ADDR (struct reg_addr){0, 72} |
||||||
|
#define BEEP_LEN_MSB_ADDR (struct reg_addr){0, 73} |
||||||
|
#define BEEP_LEN_MIB_ADDR (struct reg_addr){0, 74} |
||||||
|
#define BEEP_LEN_LSB_ADDR (struct reg_addr){0, 75} |
||||||
|
|
||||||
|
/* Page 1 registers */ |
||||||
|
/* Headphone driver registers */ |
||||||
|
#define HEADPHONE_DRV_ADDR (struct reg_addr){1, 31} |
||||||
|
#define HEADPHONE_DRV_POWERUP (BIT(7) | BIT(6)) |
||||||
|
#define HEADPHONE_DRV_CM_MASK (BIT_MASK(2) << 3) |
||||||
|
#define HEADPHONE_DRV_CM(val) (((val) << 3) & HEADPHONE_DRV_CM_MASK) |
||||||
|
#define HEADPHONE_DRV_RESERVED (BIT(2)) |
||||||
|
|
||||||
|
#define HP_OUT_POP_RM_ADDR (struct reg_addr){1, 33} |
||||||
|
#define HP_OUT_POP_RM_ENABLE (BIT(7)) |
||||||
|
|
||||||
|
#define OUTPUT_ROUTING_ADDR (struct reg_addr){1, 35} |
||||||
|
#define OUTPUT_ROUTING_HPL BIT(7) |
||||||
|
#define OUTPUT_ROUTING_HPR BIT(3) |
||||||
|
|
||||||
|
#define HPL_ANA_VOL_CTRL_ADDR (struct reg_addr){1, 36} |
||||||
|
#define HPR_ANA_VOL_CTRL_ADDR (struct reg_addr){1, 37} |
||||||
|
#define HPX_ANA_VOL_ENABLE BIT(7) |
||||||
|
#define HPX_ANA_VOL_MASK BIT_MASK(7) |
||||||
|
#define HPX_ANA_VOL(val) (((val) & HPX_ANA_VOL_MASK) | \ |
||||||
|
HPX_ANA_VOL_ENABLE) |
||||||
|
#define HPX_ANA_VOL_MAX (0) |
||||||
|
#define HPX_ANA_VOL_DEFAULT (0) |
||||||
|
#define HPX_ANA_VOL_MIN (127) |
||||||
|
#define HPX_ANA_VOL_MUTE (HPX_ANA_VOL_MIN | ~HPX_ANA_VOL_ENABLE) |
||||||
|
#define HPX_ANA_VOL_LOW_THRESH (105) |
||||||
|
#define HPX_ANA_VOL_FLOOR (144) |
||||||
|
|
||||||
|
#define HPL_DRV_GAIN_CTRL_ADDR (struct reg_addr){1, 40} |
||||||
|
#define HPR_DRV_GAIN_CTRL_ADDR (struct reg_addr){1, 41} |
||||||
|
#define HPX_DRV_UNMUTE (BIT(2)) |
||||||
|
#define HPX_DRV_RESERVED (BIT(1)) /* Write only 1 */ |
||||||
|
|
||||||
|
#define HEADPHONE_DRV_CTRL_ADDR (struct reg_addr){1, 44} |
||||||
|
#define HEADPHONE_DRV_LINEOUT (BIT(1) | BIT(2)) |
||||||
|
|
||||||
|
/* Class-D speaker amplifier registers */ |
||||||
|
#define SPEAKER_DRV_ADDR (struct reg_addr){1, 32} |
||||||
|
#define SPEAKER_DRV_POWERUP BIT(7) | BIT(6) |
||||||
|
#define SPEAKER_DRV_RESERVED BIT(2) | BIT(1) |
||||||
|
|
||||||
|
#define OUTPUT_ROUTING_MIXER BIT(6) | BIT(2) |
||||||
|
|
||||||
|
#define SPL_ANA_VOL_CTRL_ADDR (struct reg_addr){1, 38} |
||||||
|
#define SPR_ANA_VOL_CTRL_ADDR (struct reg_addr){1, 39} |
||||||
|
#define SPX_ANA_VOL_ENABLE BIT(7) |
||||||
|
#define SPX_ANA_VOL_MASK (BIT_MASK(7)) |
||||||
|
#define SPX_ANA_VOL(val) (((val) & SPX_ANA_VOL_MASK) | \ |
||||||
|
SPX_ANA_VOL_ENABLE) |
||||||
|
#define SPX_ANA_VOL_MAX (0) |
||||||
|
#define SPX_ANA_VOL_DEFAULT (0) |
||||||
|
#define SPX_ANA_VOL_MIN (127) |
||||||
|
#define SPX_ANA_VOL_MUTE (SPX_ANA_VOL_MIN | ~SPX_ANA_VOL_ENABLE) |
||||||
|
#define SPX_ANA_VOL_LOW_THRESH (105) |
||||||
|
#define SPX_ANA_VOL_FLOOR (144) |
||||||
|
|
||||||
|
#define SPL_DRV_GAIN_CTRL_ADDR (struct reg_addr){1, 42} |
||||||
|
#define SPR_DRV_GAIN_CTRL_ADDR (struct reg_addr){1, 43} |
||||||
|
#define SPX_DRV_UNMUTE (BIT(2)) |
||||||
|
#define SPX_DRV_RESERVED (BIT(1)) /* Write only 0 */ |
||||||
|
|
||||||
|
/* ADC Mic registers */ |
||||||
|
#define MIC_ADC_CTRL_ADDR (struct reg_addr){0, 81} |
||||||
|
#define MIC_ADC_POWERUP BIT(7) |
||||||
|
#define MIC_ADC_FLAG_ADDR (struct reg_addr){0, 36} |
||||||
|
|
||||||
|
#define MIC_FCTRL_ADDR (struct reg_addr){0, 82} |
||||||
|
#define MIC_FCTRL_DEFAULT 0x0 |
||||||
|
|
||||||
|
#define MIC_CCTRL_ADDR (struct reg_addr){0, 83} |
||||||
|
#define MIC_CCTRL_DEFAULT 0x0 |
||||||
|
|
||||||
|
#define MIC_BIAS_ADDR (struct reg_addr){1, 46} |
||||||
|
#define MICBIAS_DEFAULT (BIT(3) | BIT(1)) |
||||||
|
|
||||||
|
#define MIC_PGA_ADDR (struct reg_addr){1, 47} /* Missing in datasheet */ |
||||||
|
#define MIC_PGA_VOL_DEFAULT 0x0 /* 0.5 dB/bit, max volume: 59.5 dB */ |
||||||
|
#define MIC_PGAPI_ADDR (struct reg_addr){1, 48} |
||||||
|
#define MIC_PGAPI_L_DEFAULT (BIT(7) | BIT(6)) |
||||||
|
#define MIC_PGAPI_R_DEFAULT (BIT(5) | BIT(4)) |
||||||
|
#define MIC_PGAMI_ADDR (struct reg_addr){1, 49} |
||||||
|
#define MIC_ICM_ADDR (struct reg_addr){1, 50} |
||||||
|
|
||||||
|
/* Page 3 registers */ |
||||||
|
#define TIMER_MCLK_DIV_ADDR (struct reg_addr){3, 16} |
||||||
|
#define TIMER_MCLK_DIV_EN_EXT (BIT(7)) |
||||||
|
#define TIMER_MCLK_DIV_MASK (BIT_MASK(7)) |
||||||
|
#define TIMER_MCLK_DIV_VAL(val) ((val) & TIMER_MCLK_DIV_MASK) |
||||||
|
|
||||||
|
struct reg_addr { |
||||||
|
uint8_t page; /* page number */ |
||||||
|
uint8_t reg_addr; /* register address */ |
||||||
|
}; |
||||||
|
|
||||||
|
enum dac_proc_block { |
||||||
|
PRB_P1_DECIMATION_A = 1, |
||||||
|
PRB_P7_DECIMATION_B = 7, |
||||||
|
PRB_P17_DECIMATION_C = 17, |
||||||
|
}; |
||||||
|
|
||||||
|
enum adc_proc_block { |
||||||
|
PRB_R4_DECIMATION_A = 4, |
||||||
|
PRB_R10_DECIMATION_B = 10, |
||||||
|
PRB_R16_DECIMATION_C = 16, |
||||||
|
}; |
||||||
|
|
||||||
|
enum cm_voltage { |
||||||
|
CM_VOLTAGE_1P35 = 0, |
||||||
|
CM_VOLTAGE_1P5 = 1, |
||||||
|
CM_VOLTAGE_1P65 = 2, |
||||||
|
CM_VOLTAGE_1P8 = 3, |
||||||
|
}; |
||||||
|
|
||||||
|
struct tlv320aic3110_rate_divs { |
||||||
|
uint32_t mclk; |
||||||
|
uint32_t rate; |
||||||
|
uint8_t pll_p; |
||||||
|
uint8_t pll_j; |
||||||
|
uint16_t pll_d; |
||||||
|
uint8_t dosr; |
||||||
|
uint8_t ndac; |
||||||
|
uint8_t mdac; |
||||||
|
uint8_t aosr; |
||||||
|
uint8_t nadc; |
||||||
|
uint8_t madc; |
||||||
|
}; |
||||||
|
|
||||||
|
/* PLL configurations */ |
||||||
|
static const struct tlv320aic3110_rate_divs pll_div_table[] = { |
||||||
|
/* mclk rate pll: p j d dosr ndac mdac aors nadc madc */ |
||||||
|
/* 8k rate */ |
||||||
|
{12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2}, |
||||||
|
{24000000, 8000, 2, 8, 1920, 128, 48, 2, 128, 48, 2}, |
||||||
|
{25000000, 8000, 2, 7, 8643, 128, 48, 2, 128, 48, 2}, |
||||||
|
/* 11.025k rate */ |
||||||
|
{12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2}, |
||||||
|
{24000000, 11025, 2, 7, 5264, 128, 32, 2, 128, 32, 2}, |
||||||
|
{25000000, 11025, 2, 7, 2253, 128, 32, 2, 128, 32, 2}, |
||||||
|
/* 16k rate */ |
||||||
|
{12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2}, |
||||||
|
{24000000, 16000, 2, 8, 1920, 128, 24, 2, 128, 24, 2}, |
||||||
|
{25000000, 16000, 2, 7, 8643, 128, 24, 2, 128, 24, 2}, |
||||||
|
/* 22.05k rate */ |
||||||
|
{12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2}, |
||||||
|
{24000000, 22050, 2, 7, 5264, 128, 16, 2, 128, 16, 2}, |
||||||
|
{25000000, 22050, 2, 7, 2253, 128, 16, 2, 128, 16, 2}, |
||||||
|
/* 32k rate */ |
||||||
|
{12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2}, |
||||||
|
{24000000, 32000, 2, 8, 1920, 128, 12, 2, 128, 12, 2}, |
||||||
|
{25000000, 32000, 2, 7, 8643, 128, 12, 2, 128, 12, 2}, |
||||||
|
/* 44.1k rate */ |
||||||
|
{12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2}, |
||||||
|
{24000000, 44100, 2, 7, 5264, 128, 8, 2, 128, 8, 2}, |
||||||
|
{25000000, 44100, 2, 7, 2253, 128, 8, 2, 128, 8, 2}, |
||||||
|
/* 48k rate */ |
||||||
|
{12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2}, |
||||||
|
{24000000, 48000, 2, 8, 1920, 128, 8, 2, 128, 8, 2}, |
||||||
|
{25000000, 48000, 2, 7, 8643, 128, 8, 2, 128, 8, 2}, |
||||||
|
/* 88.2k rate */ |
||||||
|
{12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2}, |
||||||
|
{24000000, 88200, 2, 7, 5264, 64, 8, 2, 64, 8, 2}, |
||||||
|
{25000000, 88200, 2, 7, 2253, 64, 8, 2, 64, 8, 2}, |
||||||
|
/* 96k rate */ |
||||||
|
{12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2}, |
||||||
|
{24000000, 96000, 2, 8, 1920, 64, 8, 2, 64, 8, 2}, |
||||||
|
{25000000, 96000, 2, 7, 8643, 64, 8, 2, 64, 8, 2}, |
||||||
|
/* 176.4k rate */ |
||||||
|
{12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2}, |
||||||
|
{24000000, 176400, 2, 7, 5264, 32, 8, 2, 32, 8, 2}, |
||||||
|
{25000000, 176400, 2, 7, 2253, 32, 8, 2, 32, 8, 2}, |
||||||
|
/* 192k rate */ |
||||||
|
{12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2}, |
||||||
|
{24000000, 192000, 2, 8, 1920, 32, 8, 2, 32, 8, 2}, |
||||||
|
{25000000, 192000, 2, 7, 8643, 32, 8, 2, 32, 8, 2}, |
||||||
|
}; |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* ZEPHYR_DRIVERS_AUDIO_TLV320DAC310X_H_ */ |
Loading…
Reference in new issue