|  | /* | 
|  | * Copyright (c) 2019 Intel Corporation | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT ti_tlv320dac | 
|  |  | 
|  | #include <errno.h> | 
|  |  | 
|  | #include <zephyr/sys/util.h> | 
|  |  | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/drivers/i2c.h> | 
|  | #include <zephyr/drivers/gpio.h> | 
|  | #include <zephyr/sys/util.h> | 
|  | #include <zephyr/audio/codec.h> | 
|  | #include "tlv320dac310x.h" | 
|  |  | 
|  | #define LOG_LEVEL CONFIG_AUDIO_CODEC_LOG_LEVEL | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(tlv320dac310x); | 
|  |  | 
|  | #define CODEC_OUTPUT_VOLUME_MAX		0 | 
|  | #define CODEC_OUTPUT_VOLUME_MIN		(-78 * 2) | 
|  |  | 
|  | struct codec_driver_config { | 
|  | struct i2c_dt_spec bus; | 
|  | struct gpio_dt_spec reset_gpio; | 
|  | }; | 
|  |  | 
|  | struct codec_driver_data { | 
|  | struct reg_addr	reg_addr_cache; | 
|  | }; | 
|  |  | 
|  | static struct codec_driver_config codec_device_config = { | 
|  | .bus		= I2C_DT_SPEC_INST_GET(0), | 
|  | .reset_gpio	= GPIO_DT_SPEC_INST_GET(0, reset_gpios), | 
|  | }; | 
|  |  | 
|  | static struct codec_driver_data codec_device_data; | 
|  |  | 
|  | 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_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 enum osr_multiple codec_get_osr_multiple(audio_dai_cfg_t *cfg); | 
|  | static void codec_configure_output(const struct device *dev); | 
|  | static int codec_set_output_volume(const struct device *dev, 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_initialize(const struct device *dev) | 
|  | { | 
|  | const struct codec_driver_config *const dev_cfg = dev->config; | 
|  |  | 
|  | if (!device_is_ready(dev_cfg->bus.bus)) { | 
|  | LOG_ERR("I2C device not ready"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | if (!gpio_is_ready_dt(&dev_cfg->reset_gpio)) { | 
|  | LOG_ERR("GPIO device not ready"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int codec_configure(const struct device *dev, | 
|  | struct audio_codec_cfg *cfg) | 
|  | { | 
|  | const struct codec_driver_config *const dev_cfg = dev->config; | 
|  | int ret; | 
|  |  | 
|  | if (cfg->dai_type != AUDIO_DAI_TYPE_I2S) { | 
|  | LOG_ERR("dai_type must be AUDIO_DAI_TYPE_I2S"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* Configure reset GPIO, and set the line to inactive, which will also | 
|  | * de-assert the reset line and thus enable the codec. | 
|  | */ | 
|  | gpio_pin_configure_dt(&dev_cfg->reset_gpio, GPIO_OUTPUT_INACTIVE); | 
|  |  | 
|  | codec_soft_reset(dev); | 
|  |  | 
|  | ret = codec_configure_clocks(dev, cfg); | 
|  | if (ret == 0) { | 
|  | ret = codec_configure_dai(dev, &cfg->dai_cfg); | 
|  | } | 
|  | if (ret == 0) { | 
|  | ret = codec_configure_filters(dev, &cfg->dai_cfg); | 
|  | } | 
|  | 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) | 
|  | { | 
|  | /* mute DAC channels */ | 
|  | codec_write_reg(dev, VOL_CTRL_ADDR, VOL_CTRL_MUTE_DEFAULT); | 
|  | } | 
|  |  | 
|  | static void codec_unmute_output(const struct device *dev) | 
|  | { | 
|  | /* unmute DAC channels */ | 
|  | codec_write_reg(dev, VOL_CTRL_ADDR, VOL_CTRL_UNMUTE_DEFAULT); | 
|  | } | 
|  |  | 
|  | static int codec_set_property(const struct device *dev, | 
|  | audio_property_t property, | 
|  | audio_channel_t channel, | 
|  | audio_property_value_t val) | 
|  | { | 
|  | /* individual channel control not currently supported */ | 
|  | if (channel != AUDIO_CHANNEL_ALL) { | 
|  | LOG_ERR("channel %u invalid. must be AUDIO_CHANNEL_ALL", | 
|  | channel); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | switch (property) { | 
|  | case AUDIO_PROPERTY_OUTPUT_VOLUME: | 
|  | return codec_set_output_volume(dev, val.vol); | 
|  |  | 
|  | case AUDIO_PROPERTY_OUTPUT_MUTE: | 
|  | if (val.mute) { | 
|  | codec_mute_output(dev); | 
|  | } else { | 
|  | codec_unmute_output(dev); | 
|  | } | 
|  | 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_soft_reset(const struct device *dev) | 
|  | { | 
|  | /* soft reset the DAC */ | 
|  | 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) | 
|  | { | 
|  | int dac_clk, mod_clk; | 
|  | struct i2s_config *i2s; | 
|  | int osr, osr_min, osr_max; | 
|  | enum osr_multiple osr_multiple; | 
|  | int mdac, ndac, bclk_div, mclk_div; | 
|  |  | 
|  | i2s = &cfg->dai_cfg.i2s; | 
|  | LOG_DBG("MCLK %u Hz PCM Rate: %u Hz", cfg->mclk_freq, | 
|  | i2s->frame_clk_freq); | 
|  |  | 
|  | if (cfg->mclk_freq <= DAC_PROC_CLK_FREQ_MAX) { | 
|  | /* use MCLK frequency as the DAC processing clock */ | 
|  | ndac = 1; | 
|  | } else { | 
|  | ndac = cfg->mclk_freq / DAC_PROC_CLK_FREQ_MAX; | 
|  | } | 
|  |  | 
|  | dac_clk = cfg->mclk_freq / ndac; | 
|  |  | 
|  | /* determine OSR Multiple based on PCM rate */ | 
|  | osr_multiple = codec_get_osr_multiple(&cfg->dai_cfg); | 
|  |  | 
|  | /* | 
|  | * calculate MOD clock such that it is an integer multiple of | 
|  | * cfg->i2s.frame_clk_freq and | 
|  | * DAC_MOD_CLK_FREQ_MIN <= MOD clock <= DAC_MOD_CLK_FREQ_MAX | 
|  | */ | 
|  | osr_min = (DAC_MOD_CLK_FREQ_MIN + i2s->frame_clk_freq - 1) / | 
|  | i2s->frame_clk_freq; | 
|  | osr_max = DAC_MOD_CLK_FREQ_MAX / i2s->frame_clk_freq; | 
|  |  | 
|  | /* round mix and max values to the required multiple */ | 
|  | osr_max = (osr_max / osr_multiple) * osr_multiple; | 
|  | osr_min = DIV_ROUND_UP(osr_min, osr_multiple); | 
|  |  | 
|  | osr = osr_max; | 
|  | while (osr >= osr_min) { | 
|  | mod_clk = i2s->frame_clk_freq * osr; | 
|  |  | 
|  | /* calculate mdac */ | 
|  | mdac = dac_clk / mod_clk; | 
|  |  | 
|  | /* check if mdac is an integer */ | 
|  | if ((mdac * mod_clk) == dac_clk) { | 
|  | /* found suitable dividers */ | 
|  | break; | 
|  | } | 
|  | osr -= osr_multiple; | 
|  | } | 
|  |  | 
|  | /* check if suitable value was found */ | 
|  | if (osr < osr_min) { | 
|  | LOG_ERR("Unable to find suitable mdac and osr values"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | LOG_DBG("Processing freq: %u Hz Modulator freq: %u Hz", | 
|  | dac_clk, mod_clk); | 
|  | LOG_DBG("NDAC: %u MDAC: %u OSR: %u", ndac, mdac, osr); | 
|  |  | 
|  | if (i2s->options & I2S_OPT_BIT_CLK_MASTER) { | 
|  | bclk_div = osr * mdac / (i2s->word_size * 2U); /* stereo */ | 
|  | if ((bclk_div * i2s->word_size * 2) != (osr * 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 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)((osr >> 8) & OSR_MSB_MASK)); | 
|  | codec_write_reg(dev, OSR_LSB_ADDR, (uint8_t)(osr & OSR_LSB_MASK)); | 
|  |  | 
|  | 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 proc_block proc_blk; | 
|  |  | 
|  | /* determine decimation filter type */ | 
|  | if (cfg->i2s.frame_clk_freq >= AUDIO_PCM_RATE_192K) { | 
|  | proc_blk = PRB_P18_DECIMATION_C; | 
|  | LOG_INF("PCM Rate: %u Filter C PRB P18 selected", | 
|  | cfg->i2s.frame_clk_freq); | 
|  | } else if (cfg->i2s.frame_clk_freq >= AUDIO_PCM_RATE_96K) { | 
|  | proc_blk = PRB_P10_DECIMATION_B; | 
|  | LOG_INF("PCM Rate: %u Filter B PRB P10 selected", | 
|  | cfg->i2s.frame_clk_freq); | 
|  | } else { | 
|  | proc_blk = PRB_P25_DECIMATION_A; | 
|  | LOG_INF("PCM Rate: %u Filter A PRB P25 selected", | 
|  | cfg->i2s.frame_clk_freq); | 
|  | } | 
|  |  | 
|  | codec_write_reg(dev, PROC_BLK_SEL_ADDR, PROC_BLK_SEL(proc_blk)); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static enum osr_multiple codec_get_osr_multiple(audio_dai_cfg_t *cfg) | 
|  | { | 
|  | enum osr_multiple osr; | 
|  |  | 
|  | if (cfg->i2s.frame_clk_freq >= AUDIO_PCM_RATE_192K) { | 
|  | osr = OSR_MULTIPLE_2; | 
|  | } else if (cfg->i2s.frame_clk_freq >= AUDIO_PCM_RATE_96K) { | 
|  | osr = OSR_MULTIPLE_4; | 
|  | } else { | 
|  | osr = OSR_MULTIPLE_8; | 
|  | } | 
|  |  | 
|  | LOG_INF("PCM Rate: %u OSR Multiple: %u", cfg->i2s.frame_clk_freq, | 
|  | osr); | 
|  | return osr; | 
|  | } | 
|  |  | 
|  | 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 Headphone */ | 
|  | val = OUTPUT_ROUTING_HPL | OUTPUT_ROUTING_HPR; | 
|  | codec_write_reg(dev, OUTPUT_ROUTING_ADDR, val); | 
|  |  | 
|  | /* enable volume control on Headphone 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)); | 
|  |  | 
|  | /* set headphone outputs as line-out */ | 
|  | codec_write_reg(dev, HEADPHONE_DRV_CTRL_ADDR, HEADPHONE_DRV_LINEOUT); | 
|  |  | 
|  | /* unmute headphone drivers */ | 
|  | codec_write_reg(dev, HPL_DRV_GAIN_CTRL_ADDR, HPX_DRV_UNMUTE); | 
|  | codec_write_reg(dev, HPR_DRV_GAIN_CTRL_ADDR, HPX_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); | 
|  | } | 
|  |  | 
|  | static int codec_set_output_volume(const struct device *dev, int vol) | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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)); | 
|  | 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, 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, IF_CTRL1_ADDR, &val); | 
|  | codec_read_reg(dev, BCLK_DIV_ADDR, &val); | 
|  | codec_read_reg(dev, OVF_FLAG_ADDR, &val); | 
|  | codec_read_reg(dev, 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, 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, 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, | 
|  | }; | 
|  |  | 
|  | DEVICE_DT_INST_DEFINE(0, codec_initialize, NULL, &codec_device_data, | 
|  | &codec_device_config, POST_KERNEL, | 
|  | CONFIG_AUDIO_CODEC_INIT_PRIORITY, &codec_driver_api); |