blob: c0b70a0fefe82610fed840a8119017e9e667cb00 [file] [log] [blame]
/*
* Copyright 2025 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/sys/util.h>
#include <zephyr/logging/log.h>
#include "da7212.h"
LOG_MODULE_REGISTER(dlg_da7212, CONFIG_AUDIO_CODEC_LOG_LEVEL);
#define DT_DRV_COMPAT dlg_da7212
#define DEV_CFG(dev) ((const struct da7212_driver_config *const)dev->config)
struct da7212_driver_config {
struct i2c_dt_spec i2c;
int clock_source;
const struct device *mclk_dev;
clock_control_subsys_t mclk_name;
};
static inline void da7212_write_reg(const struct device *dev, uint8_t reg, uint8_t val)
{
const struct da7212_driver_config *const dev_cfg = DEV_CFG(dev);
uint8_t data[2] = {reg, val};
int ret;
ret = i2c_write(dev_cfg->i2c.bus, data, sizeof(data), dev_cfg->i2c.addr);
if (ret != 0) {
LOG_ERR("i2c write to codec error %d (reg 0x%02x)", ret, reg);
}
LOG_DBG("REG:%02u VAL:%#02x", reg, val);
}
static inline void da7212_read_reg(const struct device *dev, uint8_t reg, uint8_t *val)
{
const struct da7212_driver_config *const dev_cfg = DEV_CFG(dev);
int ret;
ret = i2c_write_read(dev_cfg->i2c.bus, dev_cfg->i2c.addr,
&reg, sizeof(reg), val, sizeof(*val));
if (ret != 0) {
LOG_ERR("i2c read from codec error %d (reg 0x%02x)", ret, reg);
} else {
LOG_DBG("REG:%02u VAL:%#02x", reg, *val);
}
}
static inline void da7212_update_reg(const struct device *dev, uint8_t reg,
uint8_t mask, uint8_t val)
{
uint8_t cur, newv;
da7212_read_reg(dev, reg, &cur);
LOG_DBG("read %#x = %x", reg, cur);
/* Apply mask to update only selected bits */
newv = (cur & (uint8_t)~mask) | (val & mask);
LOG_DBG("write %#x = %x", reg, newv);
da7212_write_reg(dev, reg, newv);
}
static void da7212_soft_reset(const struct device *dev)
{
da7212_write_reg(dev, DIALOG7212_CIF_CTRL,
(uint8_t)DIALOG7212_CIF_CTRL_CIF_REG_SOFT_RESET_MASK);
}
static int da7212_clock_mode_config(const struct device *dev, audio_dai_cfg_t *cfg)
{
uint8_t val = 0;
/* Master mode => DAI_CLK_EN = 1 (BCLK/WCLK output).
* Slave mode => DAI_CLK_EN = 0 (BCLK/WCLK input)
*/
if ((cfg->i2s.options & I2S_OPT_FRAME_CLK_SLAVE) == 0) {
da7212_update_reg(dev, DIALOG7212_DAI_CLK_MODE,
DIALOG7212_DAI_CLK_EN_MASK, DIALOG7212_DAI_CLK_EN_MASK);
/* DAI master mode BCLK number per WCLK period */
switch (cfg->i2s.word_size) {
case 16:
val = DIALOG7212_DAI_BCLKS_PER_WCLK_BCLK32;
break;
case 32:
val = DIALOG7212_DAI_BCLKS_PER_WCLK_BCLK64;
break;
case 64:
val = DIALOG7212_DAI_BCLKS_PER_WCLK_BCLK128;
break;
case 128:
val = DIALOG7212_DAI_BCLKS_PER_WCLK_BCLK256;
break;
default:
LOG_ERR("Word size %d not supported", cfg->i2s.word_size);
return -EINVAL;
}
da7212_update_reg(dev, DIALOG7212_DAI_CLK_MODE,
(uint8_t)DIALOG7212_DAI_BCLKS_PER_WCLK_MASK, val);
} else {
da7212_update_reg(dev, DIALOG7212_DAI_CLK_MODE,
DIALOG7212_DAI_CLK_EN_MASK, 0);
}
return 0;
}
static int da7212_dac_input_config(const struct device *dev, audio_route_t route)
{
if ((route == AUDIO_ROUTE_PLAYBACK) || (route == AUDIO_ROUTE_PLAYBACK_CAPTURE)) {
/* Route DAI input to DAC outputs (playback path) */
da7212_write_reg(dev, DIALOG7212_DIG_ROUTING_DAC,
(uint8_t)(DIALOG7212_DIG_ROUTING_DAC_R_RSC_DAC_R |
DIALOG7212_DIG_ROUTING_DAC_L_RSC_DAC_L));
} else {
/* Route ADC input to DAC outputs (bypass path) */
da7212_write_reg(dev, DIALOG7212_DIG_ROUTING_DAC,
(uint8_t)(DIALOG7212_DIG_ROUTING_DAC_R_RSC_ADC_R_OUTPUT |
DIALOG7212_DIG_ROUTING_DAC_L_RSC_ADC_L_OUTPUT));
}
return 0;
}
static int da7212_protocol_config(const struct device *dev, audio_dai_type_t dai_type)
{
uint8_t proto;
switch (dai_type) {
case AUDIO_DAI_TYPE_I2S:
proto = DIALOG7212_DAI_FORMAT_I2S_MODE;
break;
case AUDIO_DAI_TYPE_LEFT_JUSTIFIED:
proto = DIALOG7212_DAI_FORMAT_LEFT_JUSTIFIED;
break;
case AUDIO_DAI_TYPE_RIGHT_JUSTIFIED:
proto = DIALOG7212_DAI_FORMAT_RIGHT_JUSTIFIED;
break;
case AUDIO_DAI_TYPE_PCMA:
proto = DIALOG7212_DAI_FORMAT_DSP_MODE; /* Map to DSP mode */
break;
case AUDIO_DAI_TYPE_PCMB:
proto = DIALOG7212_DAI_FORMAT_DSP_MODE; /* Map to DSP mode */
break;
default:
return -EINVAL;
}
/* Keep DAI enabled flag set */
da7212_update_reg(dev, DIALOG7212_DAI_CTRL,
(uint8_t)(DIALOG7212_DAI_FORMAT_MASK), proto);
LOG_DBG("Codec protocol: %#x", proto);
return 0;
}
static int da7212_audio_format_config(const struct device *dev, audio_dai_cfg_t *cfg)
{
uint8_t val = 0;
/* Sample rate */
switch (cfg->i2s.frame_clk_freq) {
case 8000:
val = DIALOG7212_SR_8KHZ;
break;
case 11025:
val = DIALOG7212_SR_11_025KHZ;
break;
case 12000:
val = DIALOG7212_SR_12KHZ;
break;
case 16000:
val = DIALOG7212_SR_16KHZ;
break;
case 22050:
val = DIALOG7212_SR_22KHZ;
break;
case 24000:
val = DIALOG7212_SR_24KHZ;
break;
case 32000:
val = DIALOG7212_SR_32KHZ;
break;
case 44100:
val = DIALOG7212_SR_44_1KHZ;
break;
case 48000:
val = DIALOG7212_SR_48KHZ;
break;
case 88200:
val = DIALOG7212_SR_88_2KHZ;
break;
case 96000:
val = DIALOG7212_SR_96KHZ;
break;
default:
LOG_WRN("Invalid codec sample rate: %d", cfg->i2s.frame_clk_freq);
return -EINVAL;
}
da7212_write_reg(dev, DIALOG7212_SR, val);
/* Word length */
switch (cfg->i2s.word_size) {
case 16:
val = DIALOG7212_DAI_WORD_LENGTH_16B;
break;
case 20:
val = DIALOG7212_DAI_WORD_LENGTH_20B;
break;
case 24:
val = DIALOG7212_DAI_WORD_LENGTH_24B;
break;
case 32:
val = DIALOG7212_DAI_WORD_LENGTH_32B;
break;
default:
LOG_ERR("Word size %d not supported", cfg->i2s.word_size);
return -EINVAL;
}
da7212_update_reg(dev, DIALOG7212_DAI_CTRL,
(uint8_t)DIALOG7212_DAI_WORD_LENGTH_MASK, val);
return 0;
}
static int da7212_out_update(const struct device *dev, audio_channel_t channel,
uint8_t reg, uint8_t val)
{
switch (channel) {
case AUDIO_CHANNEL_FRONT_LEFT:
case AUDIO_CHANNEL_HEADPHONE_LEFT:
da7212_write_reg(dev, reg, val);
return 0;
case AUDIO_CHANNEL_FRONT_RIGHT:
case AUDIO_CHANNEL_HEADPHONE_RIGHT:
da7212_write_reg(dev, reg + 1U, val); /* R gain is next register for HP */
return 0;
case AUDIO_CHANNEL_ALL:
da7212_write_reg(dev, reg, val);
da7212_write_reg(dev, reg + 1U, val);
return 0;
default:
return -EINVAL;
}
}
static int da7212_out_volume_config(const struct device *dev,
audio_channel_t channel, int volume)
{
uint8_t vol = (uint8_t)CLAMP(volume, 0, DIALOG7212_HP_L_AMP_GAIN_STATUS_MASK);
/* DIALOG7212_HP_L_GAIN at 0x48, DIALOG7212_HP_R_GAIN at 0x49 */
return da7212_out_update(dev, channel, DIALOG7212_HP_L_GAIN, vol);
}
/* Mute by setting MUTE bit, keep HP_L_AMP_EN bit set */
static int da7212_out_mute_config(const struct device *dev,
audio_channel_t channel, bool mute)
{
/* Keep amp enabled while toggling mute:
* 0xC0 (EN|MUTE) to mute
* 0x80 (EN) to unmute
*/
uint8_t regValue = mute ? DIALOG7212_MUTE_MASK : DIALOG7212_UNMUTE_MASK;
switch (channel) {
case AUDIO_CHANNEL_FRONT_LEFT:
case AUDIO_CHANNEL_HEADPHONE_LEFT:
da7212_update_reg(dev, DIALOG7212_HP_L_CTRL, DIALOG7212_MUTE_MASK, regValue);
return 0;
case AUDIO_CHANNEL_FRONT_RIGHT:
case AUDIO_CHANNEL_HEADPHONE_RIGHT:
da7212_update_reg(dev, DIALOG7212_HP_R_CTRL, DIALOG7212_MUTE_MASK, regValue);
return 0;
case AUDIO_CHANNEL_ALL:
da7212_update_reg(dev, DIALOG7212_HP_L_CTRL, DIALOG7212_MUTE_MASK, regValue);
da7212_update_reg(dev, DIALOG7212_HP_R_CTRL, DIALOG7212_MUTE_MASK, regValue);
da7212_update_reg(dev, DIALOG7212_LINE_CTRL, DIALOG7212_MUTE_MASK, regValue);
return 0;
default:
return -EINVAL;
}
}
static int da7212_in_update(const struct device *dev, audio_channel_t channel,
uint8_t reg_gain, uint8_t gain)
{
switch (channel) {
case AUDIO_CHANNEL_FRONT_LEFT:
da7212_write_reg(dev, reg_gain, gain);
return 0;
case AUDIO_CHANNEL_FRONT_RIGHT:
da7212_write_reg(dev, reg_gain + 1U, gain);
return 0;
case AUDIO_CHANNEL_ALL:
da7212_write_reg(dev, reg_gain, gain);
da7212_write_reg(dev, reg_gain + 1U, gain);
return 0;
default:
return -EINVAL;
}
}
static int da7212_in_volume_config(const struct device *dev,
audio_channel_t channel, int volume)
{
uint8_t vol = (uint8_t)CLAMP(volume, 0, DIALOG7212_ADC_L_GAIN_STATUS_MASK);
/* DIALOG7212_ADC_L_GAIN at 0x36, DIALOG7212_ADC_R_GAIN at 0x37 */
return da7212_in_update(dev, channel, DIALOG7212_ADC_L_GAIN, vol);
}
static int da7212_in_mute_config(const struct device *dev,
audio_channel_t channel, bool mute)
{
uint8_t reg = (channel == AUDIO_CHANNEL_FRONT_RIGHT) ?
DIALOG7212_ADC_R_CTRL : DIALOG7212_ADC_L_CTRL;
/* Keep ADC enabled while toggling mute:
* 0xC0 (EN|MUTE) to mute
* 0x80 (EN) to unmute
*/
uint8_t regValue = mute ? DIALOG7212_MUTE_MASK : DIALOG7212_UNMUTE_MASK;
if (channel == AUDIO_CHANNEL_ALL) {
da7212_update_reg(dev, DIALOG7212_ADC_L_CTRL, DIALOG7212_MUTE_MASK, regValue);
da7212_update_reg(dev, DIALOG7212_ADC_R_CTRL, DIALOG7212_MUTE_MASK, regValue);
return 0;
}
da7212_update_reg(dev, reg, DIALOG7212_MUTE_MASK, regValue);
return 0;
}
static int da7212_route_input(const struct device *dev,
audio_channel_t channel, uint32_t input)
{
switch (channel) {
case AUDIO_CHANNEL_FRONT_LEFT:
da7212_write_reg(dev, DIALOG7212_MIXIN_L_SELECT,
DIALOG7212_MIXIN_L_SELECT_AUX_L_SEL_MASK);
break;
case AUDIO_CHANNEL_FRONT_RIGHT:
da7212_write_reg(dev, DIALOG7212_MIXIN_R_SELECT,
DIALOG7212_MIXIN_R_SELECT_AUX_R_SEL_MASK);
break;
case AUDIO_CHANNEL_ALL:
da7212_write_reg(dev, DIALOG7212_MIXIN_L_SELECT,
DIALOG7212_MIXIN_L_SELECT_AUX_L_SEL_MASK);
da7212_write_reg(dev, DIALOG7212_MIXIN_R_SELECT,
DIALOG7212_MIXIN_R_SELECT_AUX_R_SEL_MASK);
break;
default:
return -EINVAL;
}
return 0;
}
static inline void da7212_route_dac_to_mixout(const struct device *dev)
{
da7212_write_reg(dev, DIALOG7212_MIXOUT_L_SELECT,
(uint8_t)DIALOG7212_MIXOUT_L_SELECT_DAC_L_MASK);
da7212_write_reg(dev, DIALOG7212_MIXOUT_R_SELECT,
(uint8_t)DIALOG7212_MIXOUT_R_SELECT_DAC_R_MASK);
}
static int da7212_route_output(const struct device *dev,
audio_channel_t channel, uint32_t output)
{
/* Route DACs to mixers by default */
ARG_UNUSED(output);
da7212_route_dac_to_mixout(dev);
return 0;
}
static void da7212_configure_output(const struct device *dev)
{
/* Power charge pump */
da7212_write_reg(dev, DIALOG7212_CP_CTRL,
(uint8_t)(DIALOG7212_CP_CTRL_EN_MASK |
DIALOG7212_CP_CTRL_SMALL_SWIT_CH_FREQ_EN_MASK |
DIALOG7212_CP_CTRL_MCHANGE_OUTPUT |
DIALOG7212_CP_CTRL_MOD_CPVDD_1 |
DIALOG7212_CP_CTRL_ANALOG_VLL_LV_BOOSTS_CP));
/* Route DAC to MixOut */
da7212_route_dac_to_mixout(dev);
/* Enable DACs with ramp */
da7212_write_reg(dev, DIALOG7212_DAC_L_CTRL,
(uint8_t)(DIALOG7212_DAC_L_CTRL_DAC_EN_MASK |
DIALOG7212_DAC_L_CTRL_DAC_RAMP_EN_MASK));
da7212_write_reg(dev, DIALOG7212_DAC_R_CTRL,
(uint8_t)(DIALOG7212_DAC_R_CTRL_DAC_EN_MASK |
DIALOG7212_DAC_R_CTRL_DAC_RAMP_EN_MASK));
/* Enable HP amps, ZC and OE */
da7212_write_reg(dev, DIALOG7212_HP_L_CTRL,
(uint8_t)(DIALOG7212_HP_L_CTRL_AMP_EN_MASK |
DIALOG7212_HP_L_CTRL_AMP_RAMP_EN_MASK |
DIALOG7212_HP_L_CTRL_AMP_ZC_EN_MASK |
DIALOG7212_HP_L_CTRL_AMP_OE_MASK));
da7212_write_reg(dev, DIALOG7212_HP_R_CTRL,
(uint8_t)(DIALOG7212_HP_R_CTRL_AMP_EN_MASK |
DIALOG7212_HP_R_CTRL_AMP_RAMP_EN_MASK |
DIALOG7212_HP_R_CTRL_AMP_ZC_EN_MASK |
DIALOG7212_HP_R_CTRL_AMP_OE_MASK));
/* Enable MixOut amplifiers and mixing into HP */
da7212_write_reg(dev, DIALOG7212_MIXOUT_L_CTRL,
(uint8_t)(DIALOG7212_MIXOUT_L_CTRL_AMP_EN_MASK |
DIALOG7212_MIXOUT_L_CTRL_AMP_SOFT_MIX_EN_MASK |
DIALOG7212_MIXOUT_L_CTRL_AMP_MIX_EN_MASK));
da7212_write_reg(dev, DIALOG7212_MIXOUT_R_CTRL,
(uint8_t)(DIALOG7212_MIXOUT_R_CTRL_AMP_EN_MASK |
DIALOG7212_MIXOUT_R_CTRL_AMP_SOFT_MIX_EN_MASK |
DIALOG7212_MIXOUT_R_CTRL_AMP_MIX_EN_MASK));
/* Configure DAC gain to 0x67. */
da7212_write_reg(dev, DIALOG7212_DAC_L_GAIN, (uint8_t)DIALOG7212_DAC_DEFAULT_GAIN);
da7212_write_reg(dev, DIALOG7212_DAC_R_GAIN, (uint8_t)DIALOG7212_DAC_DEFAULT_GAIN);
/* Set default HP volume and unmute */
da7212_out_volume_config(dev, AUDIO_CHANNEL_ALL, DIALOG7212_HP_DEFAULT_GAIN);
da7212_out_mute_config(dev, AUDIO_CHANNEL_ALL, false);
}
static void da7212_configure_input(const struct device *dev)
{
/* Route AUX to MIXIN L/R (0x01 on both) */
da7212_write_reg(dev, DIALOG7212_MIXIN_L_SELECT,
(uint8_t)DIALOG7212_MIXIN_L_SELECT_AUX_L_SEL_MASK);
da7212_write_reg(dev, DIALOG7212_MIXIN_R_SELECT,
(uint8_t)DIALOG7212_MIXIN_R_SELECT_AUX_R_SEL_MASK);
/* Charge pump control: 0xFD */
da7212_write_reg(dev, DIALOG7212_CP_CTRL,
(uint8_t)(DIALOG7212_CP_CTRL_EN_MASK |
DIALOG7212_CP_CTRL_SMALL_SWIT_CH_FREQ_EN_MASK |
DIALOG7212_CP_CTRL_MCHANGE_OUTPUT |
DIALOG7212_CP_CTRL_MOD_CPVDD_1 |
DIALOG7212_CP_CTRL_ANALOG_VLL_LV_BOOSTS_CP));
/* AUX_L_CTRL: 0xB4 */
da7212_write_reg(dev, DIALOG7212_AUX_L_CTRL,
(uint8_t)(DIALOG7212_AUX_L_CTRL_AMP_EN_MASK |
DIALOG7212_AUX_L_CTRL_AMP_RAMP_EN_MASK |
DIALOG7212_AUX_L_CTRL_AMP_ZC_EN_MASK |
DIALOG7212_AUX_L_CTRL_AMP_ZC_SEL_INPUT_AUX_L_IF));
/* AUX_R_CTRL: 0xB0 */
da7212_write_reg(dev, DIALOG7212_AUX_R_CTRL,
(uint8_t)(DIALOG7212_AUX_R_CTRL_AMP_EN_MASK |
DIALOG7212_AUX_R_CTRL_AMP_RAMP_EN_MASK |
DIALOG7212_AUX_R_CTRL_AMP_ZC_EN_MASK));
/* MIC_1_CTRL: 0x04 */
da7212_write_reg(dev, DIALOG7212_MIC_1_CTRL,
(uint8_t)DIALOG7212_MIC_1_CTRL_AMP_IN_SEL_MIC_1_P);
da7212_write_reg(dev, DIALOG7212_MIC_2_CTRL,
(uint8_t)DIALOG7212_MIC_2_CTRL_AMP_IN_SEL_MIC_2_P);
/* MIXIN_L/R_CTRL: 0x88 */
da7212_write_reg(dev, DIALOG7212_MIXIN_L_CTRL,
(uint8_t)(DIALOG7212_MIXIN_L_CTRL_AMP_EN_MASK |
DIALOG7212_MIXIN_L_CTRL_AMP_MIX_EN_MASK));
da7212_write_reg(dev, DIALOG7212_MIXIN_R_CTRL,
(uint8_t)(DIALOG7212_MIXIN_R_CTRL_AMP_EN_MASK |
DIALOG7212_MIXIN_R_CTRL_AMP_MIX_EN_MASK));
/* ADC_L_CTRL: 0xA0 */
da7212_write_reg(dev, DIALOG7212_ADC_L_CTRL,
(uint8_t)(DIALOG7212_ADC_L_CTRL_ADC_EN_MASK |
DIALOG7212_ADC_L_CTRL_ADC_RAMP_EN_MASK));
da7212_write_reg(dev, DIALOG7212_ADC_R_CTRL,
(uint8_t)(DIALOG7212_ADC_R_CTRL_ADC_EN_MASK |
DIALOG7212_ADC_R_CTRL_ADC_RAMP_EN_MASK));
/* GAIN_RAMP_CTRL: 0x02 */
da7212_write_reg(dev, DIALOG7212_GAIN_RAMP_CTRL,
(uint8_t)DIALOG7212_GAIN_RAMP_CTRL_RATE_NR_MUL_16);
/* PC_COUNT: 0x02 */
da7212_write_reg(dev, DIALOG7212_PC_COUNT,
(uint8_t)DIALOG7212_PC_COUNT_RESYNC_MASK);
/* CP_DELAY: 0x95 */
da7212_write_reg(dev, DIALOG7212_CP_DELAY,
(uint8_t)(DIALOG7212_CP_DELAY_ON_OFF_LIMITER_AUT |
DIALOG7212_CP_DELAY_TAU_DELAY_4MS |
DIALOG7212_CP_DELAY_FCONTROL_0HZ_OR_1MHZ));
/* Set default ADC volume and unmute */
da7212_in_volume_config(dev, AUDIO_CHANNEL_ALL, DIALOG7212_HP_DEFAULT_GAIN);
da7212_in_mute_config(dev, AUDIO_CHANNEL_ALL, false);
}
static int da7212_configure(const struct device *dev, struct audio_codec_cfg *cfg)
{
const struct da7212_driver_config *const dev_cfg = DEV_CFG(dev);
if (cfg->dai_type >= AUDIO_DAI_TYPE_INVALID) {
LOG_ERR("dai_type not supported");
return -EINVAL;
}
if (cfg->dai_route == AUDIO_ROUTE_BYPASS) {
return 0;
}
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);
}
}
da7212_soft_reset(dev);
/* DAI right/left output stream comes from ADC right/left.
* Not used in AUDIO_ROUTE_PLAYBACK routing.
*/
da7212_write_reg(dev, DIALOG7212_DIG_ROUTING_DAI,
(uint8_t)(DIALOG7212_DIG_ROUTING_DAI_R_SRC_ADC_RIGHT |
DIALOG7212_DIG_ROUTING_DAI_L_SRC_ADC_LEFT));
/* Set default sample rate to 16kHz */
da7212_write_reg(dev, DIALOG7212_SR,
(uint8_t)DIALOG7212_SR_16KHZ);
/* Enable voltage reference and bias */
da7212_write_reg(dev, DIALOG7212_REFERENCES,
(uint8_t)DIALOG7212_REFERENCES_BIAS_EN_MASK);
/* Keep PLL disable, use MCLK as system clock. */
da7212_write_reg(dev, DIALOG7212_PLL_FRAC_TOP, 0);
da7212_write_reg(dev, DIALOG7212_PLL_FRAC_BOT, 0);
da7212_write_reg(dev, DIALOG7212_PLL_INTEGER,
DIALOG7212_PLL_FBDIV_INTEGER_RESET_VALUE);
da7212_write_reg(dev, DIALOG7212_PLL_CTRL, 0x0);
/* Set default clock mode to slave, BCLK number per WCLK = 64 */
da7212_write_reg(dev, DIALOG7212_DAI_CLK_MODE,
(uint8_t)DIALOG7212_DAI_BCLKS_PER_WCLK_BCLK64);
/* Enable DAI, set default word length to 16 bits, I2S format,
* output enabled
*/
da7212_write_reg(dev, DIALOG7212_DAI_CTRL,
(uint8_t)(DIALOG7212_DAI_EN_MASK |
DIALOG7212_DAI_OE_MASK |
DIALOG7212_DAI_WORD_LENGTH_16B |
DIALOG7212_DAI_FORMAT_I2S_MODE));
/* Route DAC to MixOut by default */
da7212_write_reg(dev, DIALOG7212_DIG_ROUTING_DAC,
(uint8_t)(DIALOG7212_DIG_ROUTING_DAC_R_RSC_DAC_R |
DIALOG7212_DIG_ROUTING_DAC_L_RSC_DAC_L));
/* Clock mode configuration */
da7212_clock_mode_config(dev, &cfg->dai_cfg);
/* DAC input configuration */
da7212_dac_input_config(dev, cfg->dai_route);
/* Protocol configuration */
da7212_protocol_config(dev, cfg->dai_type);
/* Sample rate, word length configuration */
da7212_audio_format_config(dev, &cfg->dai_cfg);
switch (cfg->dai_route) {
case AUDIO_ROUTE_PLAYBACK:
da7212_configure_output(dev);
break;
case AUDIO_ROUTE_CAPTURE:
da7212_configure_input(dev);
break;
case AUDIO_ROUTE_PLAYBACK_CAPTURE:
da7212_configure_output(dev);
da7212_configure_input(dev);
break;
default:
break;
}
return 0;
}
static void da7212_start_output(const struct device *dev)
{
ARG_UNUSED(dev);
}
static void da7212_stop_output(const struct device *dev)
{
ARG_UNUSED(dev);
}
static int da7212_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 da7212_out_volume_config(dev, channel, val.vol);
case AUDIO_PROPERTY_OUTPUT_MUTE:
return da7212_out_mute_config(dev, channel, val.mute);
case AUDIO_PROPERTY_INPUT_VOLUME:
return da7212_in_volume_config(dev, channel, val.vol);
case AUDIO_PROPERTY_INPUT_MUTE:
return da7212_in_mute_config(dev, channel, val.mute);
default:
break;
}
return -EINVAL;
}
static int da7212_apply_properties(const struct device *dev)
{
/* Nothing special: gains written immediately */
return 0;
}
static const struct audio_codec_api da7212_driver_api = {
.configure = da7212_configure,
.start_output = da7212_start_output,
.stop_output = da7212_stop_output,
.set_property = da7212_set_property,
.apply_properties = da7212_apply_properties,
.route_input = da7212_route_input,
.route_output = da7212_route_output,
};
#define DA7212_INIT(n) \
static const struct da7212_driver_config da7212_device_config_##n = { \
.i2c = I2C_DT_SPEC_INST_GET(n), \
.clock_source = DT_INST_ENUM_IDX(n, clock_source), \
.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, &da7212_device_config_##n, \
POST_KERNEL, CONFIG_AUDIO_CODEC_INIT_PRIORITY, &da7212_driver_api);
DT_INST_FOREACH_STATUS_OKAY(DA7212_INIT)