rt-thread-official/bsp/lpc55sxx/Libraries/drivers/drv_sound_wm8904.c

1124 lines
32 KiB
C

/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-03-12 Vandoul the first version
*/
#include <rtthread.h>
#include <drivers/audio.h>
#include <drivers/i2c.h>
#include "drv_sound_wm8904.h"
/**
* @brief object of wm8904.
*/
struct drv_sound_wm8904{
struct rt_i2c_bus_device *i2c_bus;
rt_device_t i2s_bus;
int i2c_addr;
};
rt_err_t wm8904_write_register(struct drv_sound_wm8904 *dev, rt_uint8_t reg, rt_uint16_t value)
{
struct rt_i2c_msg msg[2];
rt_uint8_t buf[2];
buf[0] = (value>>8)&0xFF;
buf[1] = value&0xFF;
msg[0].addr = dev->i2c_addr;
msg[0].buf = &reg;
msg[0].flags = RT_I2C_WR;
msg[0].len = 1;
msg[1].addr = dev->i2c_addr;
msg[1].buf = buf;
msg[1].flags = RT_I2C_WR|RT_I2C_NO_START;
msg[1].len = 2;
if(rt_i2c_transfer(dev->i2c_bus, msg, 2) != 2)
{
return -RT_ERROR;
}
return RT_EOK;
}
rt_err_t wm8904_read_register(struct drv_sound_wm8904 *dev, rt_uint8_t reg, rt_uint16_t *value)
{
struct rt_i2c_msg msg[2];
rt_uint8_t buf[2];
msg[0].addr = dev->i2c_addr;
msg[0].buf = &reg;
msg[0].flags = RT_I2C_WR;
msg[0].len = 1;
msg[1].addr = dev->i2c_addr;
msg[1].buf = buf;
msg[1].flags = RT_I2C_RD|RT_I2C_NO_START;
msg[1].len = 2;
if(rt_i2c_transfer(dev->i2c_bus, msg, 2) != 2)
{
return -RT_ERROR;
}
return RT_EOK;
}
rt_err_t wm8904_modify_register(struct drv_sound_wm8904 *dev, rt_uint8_t reg, rt_uint16_t mask, rt_uint16_t value)
{
rt_uint16_t reg_value;
rt_err_t ret;
ret = wm8904_read_register(dev, reg, &reg_value);
if(ret != RT_EOK)
{
return ret;
}
reg_value &= (uint16_t)~mask;
reg_value |= value;
return wm8904_write_register(dev, reg, reg_value);
}
static rt_err_t wm8904_wait_on_write_sequencer(struct drv_sound_wm8904 *dev)
{
rt_err_t ret;
rt_uint16_t value;
do {
ret = wm8904_read_register(dev, WM8904_WRT_SEQUENCER_4, &value);
}while((ret == RT_EOK) && ((value & 1U) != 0u));
return ret;
}
static rt_err_t wm8904_updateformat(struct drv_sound_wm8904 *dev, wm8904_fs_ratio_t fs_ratio, wm8904_sample_rate_t sample_rate, wm8904_bit_width_t bit_width)
{
rt_err_t err = RT_EOK;
/** Disable SYSCLK */
err = wm8904_write_register(dev, WM8904_CLK_RATES_2, 0x00);
if(err != RT_EOK)
{
return err;
}
/** Set clock ratio and sample rate */
err = wm8904_write_register(dev, WM8904_CLK_RATES_1, (rt_uint16_t)((rt_uint16_t)fs_ratio << 10U) | (rt_uint16_t)sample_rate);
if(err != RT_EOK)
{
return err;
}
/** Set bit resolution. */
err = wm8904_modify_register(dev, WM8904_AUDIO_IF_1, (0x000CU), (rt_uint16_t)bit_width << 2U);
if(err != RT_EOK)
{
return err;
}
/** Enable SYSCLK */
err = wm8904_write_register(dev, WM8904_CLK_RATES_2, 0x1007);
if(err != RT_EOK)
{
return err;
}
return RT_EOK;
}
static rt_err_t wm8904_i2c_bus_init(struct rt_i2c_bus_device *i2c_bus)
{
(void)i2c_bus;
return RT_EOK;
}
static rt_err_t wm8904_i2s_bus_init(rt_device_t i2s_bus)
{
(void)i2s_bus;
return RT_EOK;
}
const static uint16_t wm8904_init_list[][2] = {
/* TOCLK_RATE_DIV16=0, TOCLK_RATE_x4=1, SR_MODE=0, MCLK_DIV=1
* (Required for MMCs: SGY, KRT see erratum CE000546) */
{WM8904_CLK_RATES_0, 0xA45F},
/* INL_ENA=1, INR ENA=1 */
{WM8904_POWER_MGMT_0, 0x0003},
/* HPL_PGA_ENA=1, HPR_PGA_ENA=1 */
{WM8904_POWER_MGMT_2, 0x0003},
/* DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1 */
{WM8904_POWER_MGMT_6, 0x000F},
/* ADC_OSR128=1 */
{WM8904_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_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_DAC_DIG_1, 0x0040},
/* LINMUTE=0, LIN_VOL=0_0101 */
{WM8904_ANALOG_LEFT_IN_0, 0x0005},
/* RINMUTE=0, RIN VOL=0_0101 LINEOUTL RMV SHORT-1, LINEOUTL ENA_OUTP=1,
* LINEOUTL_ENA_DLY=1, LINEOUTL_ENA=1, LINEOUTR_RMV_SHORT-1,
* LINEOUTR_ENA_OUTP=1 */
{WM8904_ANALOG_RIGHT_IN_0, 0x0005},
/* HPOUTL_MUTE=0, HPOUT_VU=0, HPOUTLZC=0, HPOUTL_VOL=10_1101 */
{WM8904_ANALOG_OUT1_LEFT, 0x00AD},
/* HPOUTR_MUTE=0, HPOUT_VU=0, HPOUTRZC=0, HPOUTR_VOL=10_1101 */
{WM8904_ANALOG_OUT1_RIGHT, 0x00AD},
/* Enable DC servos for headphone out */
{WM8904_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_ANALOG_HP_0, 0x00FF},
/* CP_DYN_PWR=1 */
{WM8904_CLS_W_0, 0x0001},
/* CP_ENA=1 */
{WM8904_CHRG_PUMP_0, 0x0001},
/* TOCLK_RATE_DIV16=0, TOCLK_RATE_x4=1, SR_MODE=0, MCLK_DIV=1
* (Required for MMCs: SGY, KRT see erratum CE000546) */
{WM8904_CLK_RATES_0, 0xA45F},
/* INL_ENA=1, INR ENA=1 */
{WM8904_POWER_MGMT_0, 0x0003},
/* HPL_PGA_ENA=1, HPR_PGA_ENA=1 */
{WM8904_POWER_MGMT_2, 0x0003},
/* DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1 */
{WM8904_POWER_MGMT_6, 0x000F},
/* ADC_OSR128=1 */
{WM8904_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_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_DAC_DIG_1, 0x0040},
/* LINMUTE=0, LIN_VOL=0_0101 */
{WM8904_ANALOG_LEFT_IN_0, 0x0005},
/* RINMUTE=0, RIN VOL=0_0101 LINEOUTL RMV SHORT-1, LINEOUTL ENA_OUTP=1,
* LINEOUTL_ENA_DLY=1, LINEOUTL_ENA=1, LINEOUTR_RMV_SHORT-1,
* LINEOUTR_ENA_OUTP=1 */
{WM8904_ANALOG_RIGHT_IN_0, 0x0005},
/* HPOUTL_MUTE=0, HPOUT_VU=0, HPOUTLZC=0, HPOUTL_VOL=10_1101 */
{WM8904_ANALOG_OUT1_LEFT, 0x00AD},
/* HPOUTR_MUTE=0, HPOUT_VU=0, HPOUTRZC=0, HPOUTR_VOL=10_1101 */
{WM8904_ANALOG_OUT1_RIGHT, 0x00AD},
/* Enable DC servos for headphone out */
{WM8904_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_ANALOG_HP_0, 0x00FF},
/* CP_DYN_PWR=1 */
{WM8904_CLS_W_0, 0x0001},
/* CP_ENA=1 */
{WM8904_CHRG_PUMP_0, 0x0001},
};
rt_err_t wm8904_init(struct drv_sound_wm8904 *dev, struct wm8904_config *config)
{
rt_err_t ret;
dev->i2c_bus = rt_i2c_bus_device_find(config->i2c_bus_name);
if(dev->i2c_bus == RT_NULL)
{
return -RT_EINVAL;
}
dev->i2s_bus = rt_device_find(config->i2s_bus_name);
if(dev->i2s_bus == RT_NULL)
{
return -RT_EINVAL;
}
wm8904_i2c_bus_init(dev->i2c_bus);
wm8904_i2s_bus_init(dev->i2s_bus);
ret = wm8904_write_register(dev, WM8904_RESET, 0x0000);
if(ret != RT_EOK)
{
return ret;
}
/* MCLK_INV=0, SYSCLK_SRC=0, TOCLK_RATE=0, OPCLK_ENA=1,
* CLK_SYS_ENA=1, CLK_DSP_ENA=1, TOCLK_ENA=1 */
ret = wm8904_write_register(dev, WM8904_CLK_RATES_2, 0x000F);
if(ret != RT_EOK)
{
return ret;
}
/* WSEQ_ENA=1, WSEQ_WRITE_INDEX=0_0000 */
ret = wm8904_write_register(dev, WM8904_WRT_SEQUENCER_0, 0x0100);
if(ret != RT_EOK)
{
return ret;
}
/* WSEQ_ABORT=0, WSEQ_START=1, WSEQ_START_INDEX=00_0000 */
ret = wm8904_write_register(dev, WM8904_WRT_SEQUENCER_3, 0x0100);
if(ret != RT_EOK)
{
return ret;
}
/* WSEQ_ENA=1, WSEQ_SRITE_INDEX=0_0000 */
ret = wm8904_write_register(dev, WM8904_WRT_SEQUENCER_0, 0x0100);
if(ret != RT_EOK)
{
return ret;
}
ret = wm8904_wait_on_write_sequencer(dev);
if(ret != RT_EOK)
{
return ret;
}
for(int i=0; i<sizeof(wm8904_init_list)/sizeof(wm8904_init_list[0]); i++)
{
ret = wm8904_write_register(dev, wm8904_init_list[i][0], wm8904_init_list[i][1]);
if(ret != RT_EOK)
{
return ret;
}
}
return ret;
}
rt_err_t wm8904_deinit(struct drv_sound_wm8904 *dev)
{
return wm8904_write_register(dev, WM8904_RESET, 0x0000);
}
rt_err_t wm8904_set_master_clock(struct drv_sound_wm8904 *dev, rt_uint32_t sysclk, rt_uint32_t sample_rate, rt_uint32_t bit_width)
{
rt_uint32_t bclk = sample_rate * bit_width * 2U;
rt_uint32_t bclk_div = 0U;
rt_uint16_t audio_interface = 0U;
rt_err_t err = RT_EOK;
rt_uint16_t sysclk_div = 0;
err = wm8904_read_register(dev, WM8904_CLK_RATES_0, &sysclk_div);
sysclk = sysclk >> (sysclk_div & 0x1U);
if((sysclk / bclk > 48U) || (bclk / sample_rate > 2047U) || (bclk / sample_rate < 8U))
{
return -RT_EINVAL;
}
err = wm8904_read_register(dev, WM8904_AUDIO_IF_2, &audio_interface);
if(err != RT_EOK)
{
return err;
}
audio_interface &= ~(rt_uint16_t)0x1FU;
bclk_div = (sysclk * 10U) / bclk;
switch(bclk_div)
{
case 10:
audio_interface |= 0U;
break;
case 15:
audio_interface |= 1U;
break;
case 20:
audio_interface |= 2U;
break;
case 30:
audio_interface |= 3U;
break;
case 40:
audio_interface |= 4U;
break;
case 50:
audio_interface |= 5U;
break;
case 55:
audio_interface |= 6U;
break;
case 60:
audio_interface |= 7U;
break;
case 80:
audio_interface |= 8U;
break;
case 100:
audio_interface |= 9U;
break;
case 110:
audio_interface |= 10U;
break;
case 120:
audio_interface |= 11U;
break;
case 160:
audio_interface |= 12U;
break;
case 200:
audio_interface |= 13U;
break;
case 220:
audio_interface |= 14U;
break;
case 240:
audio_interface |= 15U;
break;
case 250:
audio_interface |= 16U;
break;
case 300:
audio_interface |= 17U;
break;
case 320:
audio_interface |= 18U;
break;
case 440:
audio_interface |= 19U;
break;
case 480:
audio_interface |= 20U;
break;
default:
err = -RT_EINVAL;
break;
}
if(err != RT_EOK)
{
return err;
}
/** bclk divider */
err = wm8904_write_register(dev, WM8904_AUDIO_IF_2, audio_interface);
if(err != RT_EOK)
{
return err;
}
err = wm8904_modify_register(dev, WM8904_GPIO_CONTROL_4, 0x8FU, 1U);
if(err != RT_EOK)
{
return err;
}
/** LRCLK direction and divider */
audio_interface = (rt_uint16_t)((1UL << 11U) | (bclk / sample_rate));
err = wm8904_modify_register(dev, WM8904_AUDIO_IF_3, 0xFFFU, audio_interface);
if(err != RT_EOK)
{
return err;
}
return RT_EOK;
}
rt_err_t wm8904_set_fll_config(struct drv_sound_wm8904 *dev, wm8904_fll_config_t *config)
{
RT_ASSERT(dev != RT_NULL);
RT_ASSERT(config != RT_NULL);
rt_uint32_t reference_clock = config->ref_clock_hz;
rt_uint32_t input_divider = 0U;
rt_uint32_t fvco = 0U, output_div = 0U, ratio = 0U;
rt_uint32_t n = 0U, k = 0U;
/* it is recommended that the highest possible frequency - within the 13.5MHz limit - should be selected */
if(reference_clock < 13500000U)
{
input_divider = 0;
}
else if(reference_clock / 2U < 13500000U)
{
input_divider = 1;
}
else if(reference_clock / 4U < 13500000U)
{
input_divider = 2;
}
else
{
input_divider = 3;
}
if(reference_clock / (1UL << input_divider) > 13500000)
{
return -RT_EINVAL;
}
reference_clock = reference_clock / (1UL << input_divider);
for (output_div = 4U; output_div <= 64U; output_div++)
{
fvco = output_div * config->output_clock_hz;
if ((fvco >= 90000000U) && (fvco <= 100000000U))
{
break;
}
}
if(reference_clock <= 64000U)
{
ratio = 4U;
}
else if(reference_clock <= 128000U)
{
ratio = 3U;
}
else if(reference_clock <= 256000U)
{
ratio = 2U;
}
else if(reference_clock <= 1000000U)
{
ratio = 1U;
}
else
{
ratio = 0U;
}
n = fvco / ((ratio + 1U) * reference_clock);
k = (rt_uint32_t)((rt_uint64_t)fvco * 1000000U) / ((ratio + 1U) * reference_clock);
if(n != 0U)
{
k = k - n * 1000000U;
}
k = (rt_uint32_t)((rt_uint64_t)k * 65536U) / 1000000U;
/* configure WM8904 */
if (wm8904_modify_register(dev, WM8904_FLL_CONTROL_1, 7U, 4U) != RT_EOK)
{
return -RT_ERROR;
}
/* configure WM8904 */
if (wm8904_modify_register(dev, WM8904_FLL_CONTROL_2, 0x3F07U, (rt_uint16_t)(((output_div - 1U) << 8U) | ratio)) != RT_EOK)
{
return -RT_ERROR;
}
if (wm8904_write_register(dev, WM8904_FLL_CONTROL_3, (rt_uint16_t)k) != RT_EOK)
{
return -RT_ERROR;
}
if (wm8904_modify_register(dev, WM8904_FLL_CONTROL_4, 0x7FE0U, (rt_uint16_t)(n << 5U)) != RT_EOK)
{
return -RT_ERROR;
}
if (wm8904_write_register(dev, WM8904_FLL_CONTROL_5, (rt_uint16_t)((input_divider << 3U) | (rt_uint16_t)config->source)) != RT_EOK)
{
return -RT_ERROR;
}
if (wm8904_modify_register(dev, WM8904_FLL_CONTROL_1, 1U, 1) != RT_EOK)
{
return -RT_ERROR;
}
/** enable GPIO1 output fll output clock */
if (wm8904_write_register(dev, WM8904_GPIO_CONTROL_1, (rt_uint16_t)9U) != RT_EOK)
{
return -RT_ERROR;
}
return RT_EOK;
}
rt_err_t wm8904_set_protocol(struct drv_sound_wm8904 *dev, wm8904_protocol_t p)
{
return wm8904_modify_register(dev, WM8904_AUDIO_IF_1, (0x0003U | (1U << 4U)), (rt_uint16_t)p);
}
rt_err_t wm8904_select_lrc_polarity(struct drv_sound_wm8904 *dev, rt_uint32_t polarity)
{
return wm8904_modify_register(dev, WM8904_AUDIO_IF_1, 0x0010U, (rt_uint16_t)polarity);
}
rt_err_t wm8904_enable_dac_time_slot(struct drv_sound_wm8904 *dev, wm8904_timeslot_t timeslot)
{
return wm8904_modify_register(dev, WM8904_AUDIO_IF_1, ((rt_uint16_t)3U << 12U), (((rt_uint16_t)1U << 13U) | ((rt_uint16_t)timeslot << 12U)));
}
rt_err_t wm8904_enable_adc_time_slot(struct drv_sound_wm8904 *dev, wm8904_timeslot_t timeslot)
{
return wm8904_modify_register(dev, WM8904_AUDIO_IF_1, ((rt_uint16_t)3U << 10U), (((rt_uint16_t)1U << 11U) | ((rt_uint16_t)timeslot << 10U)));
}
rt_err_t wm8904_check_audio_format(struct drv_sound_wm8904 *dev, wm8904_audio_format_t *format, rt_uint32_t mclk_freq)
{
RT_ASSERT((dev != RT_NULL) && (format != RT_NULL));
rt_err_t err = RT_EOK;
rt_uint16_t mclk_div = 0U;
rt_uint32_t sample_rate = 0U;
rt_uint32_t fs_ratio = 0;
wm8904_sample_rate_t reg_sample_rate = format->sampleRate;
err = wm8904_read_register(dev, WM8904_CLK_RATES_0, &mclk_div);
if(err != RT_EOK)
{
return err;
}
switch(format->sampleRate)
{
case WM8904_SAMPLERATE_8kHz:
sample_rate = 8000;
break;
case WM8904_SAMPLERATE_12kHz :
sample_rate = 12000;
break;
case WM8904_SAMPLERATE_16kHz :
sample_rate = 16000;
break;
case WM8904_SAMPLERATE_24kHz :
sample_rate = 24000;
break;
case WM8904_SAMPLERATE_32kHz :
sample_rate = 32000;
break;
case WM8904_SAMPLERATE_48kHz :
sample_rate = 48000;
break;
case WM8904_SAMPLERATE_11025Hz:
sample_rate = 11025;
reg_sample_rate = WM8904_SAMPLERATE_12kHz;
break;
case WM8904_SAMPLERATE_22050Hz:
sample_rate = 22050;
reg_sample_rate = WM8904_SAMPLERATE_24kHz;
break;
case WM8904_SAMPLERATE_44100Hz:
sample_rate = 44100;
reg_sample_rate = WM8904_SAMPLERATE_48kHz;
break;
default:
err = -RT_EINVAL;
break;
}
if(err != RT_EOK)
{
return err;
}
fs_ratio = (mclk_freq >> (mclk_div & 0x1U)) / sample_rate;
switch(fs_ratio)
{
case 64:
format->fsRatio = WM8904_FSRATIO_64X;
break;
case 128 :
format->fsRatio = WM8904_FSRATIO_128X;
break;
case 192 :
format->fsRatio = WM8904_FSRATIO_192X;
break;
case 256 :
format->fsRatio = WM8904_FSRATIO_256X;
break;
case 384 :
format->fsRatio = WM8904_FSRATIO_384X;
break;
case 512 :
format->fsRatio = WM8904_FSRATIO_512X;
break;
case 768 :
format->fsRatio = WM8904_FSRATIO_768X;
break;
case 1024:
format->fsRatio = WM8904_FSRATIO_1024X;
break;
case 1408:
format->fsRatio = WM8904_FSRATIO_1408X;
break;
case 1536:
format->fsRatio = WM8904_FSRATIO_1536X;
break;
default:
err = -RT_EINVAL;
break;
}
if(err != RT_EOK)
{
return err;
}
return wm8904_updateformat(dev, format->fsRatio, reg_sample_rate, format->bitWidth);
}
rt_err_t wm8904_set_audio_format(struct drv_sound_wm8904 *dev, rt_uint32_t sysclk, rt_uint32_t sample_rate, rt_uint32_t bit_width)
{
rt_uint32_t ratio = 0;
rt_err_t err = RT_EOK;
wm8904_bit_width_t reg_bit_width = WM8904_BITWIDTH_32;
wm8904_sample_rate_t reg_sample_reate = WM8904_SAMPLERATE_48kHz;
wm8904_fs_ratio_t reg_fsratio = WM8904_FSRATIO_1536X;
rt_uint16_t temp_reg = 0U;
err = wm8904_read_register(dev, WM8904_CLK_RATES_0, &temp_reg);
if(err != RT_EOK)
{
return err;
}
switch(sample_rate)
{
case 8000:
sample_rate = WM8904_SAMPLERATE_8kHz;
break;
case 11025:
sample_rate = WM8904_SAMPLERATE_12kHz;
break;
case 12000:
sample_rate = WM8904_SAMPLERATE_12kHz;
break;
case 16000:
sample_rate = WM8904_SAMPLERATE_16kHz;
break;
case 22050:
sample_rate = WM8904_SAMPLERATE_24kHz;
break;
case 24000:
sample_rate = WM8904_SAMPLERATE_24kHz;
break;
case 32000:
sample_rate = WM8904_SAMPLERATE_32kHz;
break;
case 44100:
sample_rate = WM8904_SAMPLERATE_48kHz;
break;
case 48000:
sample_rate = WM8904_SAMPLERATE_48kHz;
break;
default:
err = -RT_EINVAL;
break;
}
if(err != RT_EOK)
{
return err;
}
switch(bit_width)
{
case 16:
reg_bit_width = WM8904_BITWIDTH_16;
break;
case 20:
reg_bit_width = WM8904_BITWIDTH_20;
break;
case 24:
reg_bit_width = WM8904_BITWIDTH_24;
break;
case 32:
reg_bit_width = WM8904_BITWIDTH_32;
break;
default:
err = -RT_EINVAL;
break;
}
if(err != RT_EOK)
{
return err;
}
ratio = (sysclk >> (temp_reg & 0x1U)) / sample_rate;
switch(ratio)
{
case 64:
reg_fsratio = WM8904_FSRATIO_64X;
break;
case 128 :
reg_fsratio = WM8904_FSRATIO_128X;
break;
case 192 :
reg_fsratio = WM8904_FSRATIO_192X;
break;
case 256 :
reg_fsratio = WM8904_FSRATIO_256X;
break;
case 384 :
reg_fsratio = WM8904_FSRATIO_384X;
break;
case 512 :
reg_fsratio = WM8904_FSRATIO_512X;
break;
case 768 :
reg_fsratio = WM8904_FSRATIO_768X;
break;
case 1024:
reg_fsratio = WM8904_FSRATIO_1024X;
break;
case 1408:
reg_fsratio = WM8904_FSRATIO_1408X;
break;
case 1536:
reg_fsratio = WM8904_FSRATIO_1536X;
break;
default:
err = -RT_EINVAL;
break;
}
if(err != RT_EOK)
{
return err;
}
err = wm8904_updateformat(dev, reg_fsratio, reg_sample_reate, reg_bit_width);
if(err != RT_EOK)
{
return err;
}
err = wm8904_read_register(dev, WM8904_AUDIO_IF_1, &temp_reg);
if(err != RT_EOK)
{
return err;
}
if((temp_reg & (1UL << 6U)) != 0)
{
err = wm8904_set_master_clock(dev, sysclk, sample_rate, bit_width);
}
return err;
}
rt_err_t wm8904_set_volume(struct drv_sound_wm8904 *dev, rt_uint16_t volume_left, rt_uint16_t volume_right)
{
RT_ASSERT(volume_left <= WM8904_MAP_HEADPHONE_LINEOUT_MAX_VOLUME);
RT_ASSERT(volume_right <= WM8904_MAP_HEADPHONE_LINEOUT_MAX_VOLUME);
rt_err_t err = RT_EOK;
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_LEFT, 0x1BF, volume_left);
if(err != RT_EOK)
{
return err;
}
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_RIGHT, 0x1BF, ((rt_uint16_t)volume_right | 0x0080U));
if(err != RT_EOK)
{
return err;
}
return RT_EOK;
}
rt_err_t wm8904_set_mute(struct drv_sound_wm8904 *dev, rt_bool_t mute_left, rt_bool_t mute_right)
{
rt_err_t err = RT_EOK;
rt_uint16_t left = (rt_uint16_t)(mute_left ? 0x0100U : 0x0000U);
rt_uint16_t right = (rt_uint16_t)(mute_right ? 0x0100U : 0x0000U);
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_LEFT, 0x0100U, left);
if(err != RT_EOK)
{
return err;
}
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_RIGHT, 0x0180U, ((rt_uint16_t)right | 0x0080U));
if(err != RT_EOK)
{
return err;
}
return err;
}
rt_err_t wm8904_set_channel_volume(struct drv_sound_wm8904 *dev, rt_uint32_t channel, rt_uint32_t volume)
{
RT_ASSERT(volume <= WM8904_MAP_HEADPHONE_LINEOUT_MAX_VOLUME);
rt_err_t err = RT_EOK;
/* headphone left channel 0x1BF means unmute the OUT and reset the OUT volume update bit and volume range fields */
if((channel & (rt_uint32_t)WM8904_HEADPHONE_LEFT) != 0U)
{
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_LEFT, 0x1BFU, (rt_uint16_t)volume | 0x80U);
}
/* headphone right channel */
if((channel & (rt_uint32_t)WM8904_HEADPHONE_RIGHT) != 0U)
{
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_RIGHT, 0x1BFU, (rt_uint16_t)volume | 0x80U);
}
/* line out left channel */
if((channel & (rt_uint32_t)WM8904_LINEOUT_LEFT) != 0U)
{
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT2_LEFT, 0x1BFU, (rt_uint16_t)volume | 0x80U);
}
/* line out right channel */
if((channel & (rt_uint32_t)WM8904_LINEOUT_RIGHT) != 0U)
{
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT2_RIGHT, 0x1BFU, (rt_uint16_t)volume | 0x80U);
}
return err;
}
rt_err_t wm8904_set_channel_mute(struct drv_sound_wm8904 *dev, rt_uint32_t channel, rt_bool_t is_mute)
{
rt_err_t err = RT_EOK;
rt_uint16_t reg_value = 0U, reg_mask = 0U;
reg_value = is_mute ? 0x180U : 0x80U;
reg_mask = 0x100U;
/* headphone left channel */
if((channel & (rt_uint32_t)WM8904_HEADPHONE_LEFT) != 0U)
{
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_LEFT, reg_mask, reg_value);
}
/* headphone right channel */
if((channel & (rt_uint32_t)WM8904_HEADPHONE_RIGHT) != 0U)
{
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_RIGHT, reg_mask, reg_value);
}
/* line out left channel */
if((channel & (rt_uint32_t)WM8904_LINEOUT_LEFT) != 0U)
{
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT2_LEFT, reg_mask, reg_value);
}
/* line out right channel */
if((channel & (rt_uint32_t)WM8904_LINEOUT_RIGHT) != 0U)
{
err = wm8904_modify_register(dev, WM8904_ANALOG_OUT2_RIGHT, reg_mask, reg_value);
}
return err;
}
rt_err_t wm8904_enable_dac_volume(struct drv_sound_wm8904 *dev, rt_uint8_t volume)
{
rt_err_t err = RT_EOK;
err = wm8904_write_register(dev, WM8904_DAC_DIGITAL_VOLUME_LEFT, (rt_uint16_t)(volume | 0x100UL));
if(err == RT_EOK)
{
err = wm8904_write_register(dev, WM8904_DAC_DIGITAL_VOLUME_RIGHT, (rt_uint16_t)(volume | 0x100UL));
}
return err;
}
rt_err_t wm8904_set_module_power(struct drv_sound_wm8904 *dev, wm8904_module_t module, rt_bool_t is_enabled)
{
rt_uint8_t reg_addr = 0, reg_bit_mask = 0U, reg_value = 0U;
rt_err_t err = RT_EOK;
switch(module)
{
case WM8904_MODULE_ADC:
reg_addr = WM8904_POWER_MGMT_6;
reg_bit_mask = 3U;
reg_value = is_enabled ? 3U : 0U;
break;
case WM8904_MODULE_DAC:
reg_addr = WM8904_POWER_MGMT_6;
reg_bit_mask = 0xCU;
reg_value = is_enabled ? 0xCU : 0U;
break;
case WM8904_MODULE_PGA:
reg_addr = WM8904_POWER_MGMT_0;
reg_bit_mask = 3U;
reg_value = is_enabled ? 3U : 0U;
break;
case WM8904_MODULE_HEADPHONE:
reg_addr = WM8904_POWER_MGMT_2;
reg_bit_mask = 3U;
reg_value = is_enabled ? 3U : 0U;
break;
case WM8904_MODULE_LINEOUT:
reg_addr = WM8904_POWER_MGMT_3;
reg_bit_mask = 3U;
reg_value = is_enabled ? 3U : 0U;
break;
default:
err = -RT_EINVAL;
break;
}
if(err == RT_EOK)
{
err = wm8904_modify_register(dev, reg_addr, reg_bit_mask, reg_value);
}
return err;
}
rt_err_t wm8904_set_record(struct drv_sound_wm8904 *dev, rt_uint32_t record_source)
{
rt_uint8_t reg_left_addr = WM8904_ANALOG_LEFT_IN_1, reg_right_addr = WM8904_ANALOG_RIGHT_IN_1;
rt_uint16_t reg_left_value = 0U, reg_right_value = 0U, reg_bit_mask = 0U;
rt_err_t err = RT_EOK;
switch(record_source)
{
case WM8904_RECORD_SOURCE_DIFFERENTIAL_LINE:
reg_left_value = 1U;
reg_right_value = 1U;
reg_bit_mask = 0x3FU;
break;
case WM8904_RECORD_SOURCE_DIFFERENTIAL_MIC:
reg_left_value = 2U;
reg_right_value = 2U;
reg_bit_mask = 0x3FU;
break;
case WM8904_RECORD_SOURCE_LINE_INPUT:
reg_left_value = 0U;
reg_right_value = 0U;
reg_bit_mask = 0x3FU;
break;
case WM8904_RECORD_SOURCE_DIGITAL_MIC:
reg_left_value = 1U << 12;
reg_left_addr = WM8904_DAC_DIG_0;
reg_right_addr = 0U;
reg_bit_mask = 1U << 12;
break;
default:
err = -RT_EINVAL;
break;
}
if(err != RT_EOK)
{
return err;
}
err = wm8904_modify_register(dev, reg_left_addr, reg_bit_mask, reg_left_value);
if((err == RT_EOK) && (reg_right_addr != 0U))
{
err = wm8904_modify_register(dev, reg_right_addr, reg_bit_mask, reg_right_value);
}
return err;
}
rt_err_t wm8904_set_record_channel(struct drv_sound_wm8904 *dev, rt_uint32_t left_record_channel, rt_uint32_t right_record_channel)
{
rt_uint8_t reg_left_addr = WM8904_ANALOG_LEFT_IN_1, reg_right_addr = WM8904_ANALOG_RIGHT_IN_1;
rt_uint16_t reg_left_value = 0U, reg_right_value = 0U, reg_bit_mask;
rt_err_t err = RT_EOK;
rt_uint8_t left_positive_channel = 0U, left_negative_channel = 0U, right_positive_channel = 0U, right_negative_channel = 0U;
if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE1) != 0U)
{
left_positive_channel = 0U;
}
else if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE2) != 0U)
{
left_positive_channel = 1U;
}
else
{
left_positive_channel = 2U;
}
if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE1) != 0U)
{
left_negative_channel = 0U;
}
else if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE2) != 0U)
{
left_negative_channel = 1U;
}
else if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE3) != 0U)
{
left_negative_channel = 2U;
}
else
{
left_negative_channel = left_positive_channel;
}
if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE1) != 0U)
{
right_positive_channel = 0U;
}
else if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE2) != 0U)
{
right_positive_channel = 1U;
}
else
{
right_positive_channel = 2U;
}
if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE1) != 0U)
{
right_negative_channel = 0U;
}
else if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE2) != 0U)
{
right_negative_channel = 1U;
}
else if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE3) != 0U)
{
right_negative_channel = 2U;
}
else
{
right_negative_channel = right_positive_channel;
}
reg_left_value = (((rt_uint16_t)left_negative_channel & 3U) << 4U) | (((rt_uint16_t)left_positive_channel & 3U) << 2U);
reg_right_value = (((rt_uint16_t)right_negative_channel & 3U) << 4U) | (((rt_uint16_t)right_positive_channel & 3U) << 2U);
reg_bit_mask = 0x3CU;
err = wm8904_modify_register(dev, reg_left_addr, reg_bit_mask, reg_left_value);
if(err == RT_EOK)
{
return wm8904_modify_register(dev, reg_right_addr, reg_bit_mask, reg_right_value);
}
return -RT_ERROR;
}
rt_err_t wm8904_set_play(struct drv_sound_wm8904 *dev, rt_uint32_t play_source)
{
rt_uint16_t reg_value = 0U, reg_bit_mask = 0xFU;
/* source from PGA */
if(play_source == (rt_uint32_t)WM8904_PLAY_SOURCE_PGA)
{
reg_value |= (3U << 2U) | 3U;
} else
/* source from DAC */
if(play_source == (rt_uint32_t)WM8904_PLAY_SOURCE_DAC)
{
reg_value &= (rt_uint16_t) ~((3U << 2U) | 3U);
}
return wm8904_modify_register(dev, WM8904_ANALOG_OUT12_ZC, reg_bit_mask, reg_value);
}
/*******************************************************************************************/
rt_err_t wm8904_audio_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
return RT_EOK;
}
rt_err_t wm8904_audio_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
return RT_EOK;
}
rt_err_t wm8904_audio_init(struct rt_audio_device *audio)
{
return RT_EOK;
}
rt_err_t wm8904_audio_start(struct rt_audio_device *audio, int stream)
{
return RT_EOK;
}
rt_err_t wm8904_audio_stop(struct rt_audio_device *audio, int stream)
{
return RT_EOK;
}
rt_ssize_t wm8904_audio_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size)
{
return 0;
}
void wm8904_audio_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
}
/**
*
*/
struct rt_audio_ops wm8904_audio_ops =
{
.getcaps = wm8904_audio_getcaps,
.configure = wm8904_audio_configure,
.init = wm8904_audio_init,
.start = wm8904_audio_start,
.stop = wm8904_audio_stop,
.transmit = wm8904_audio_transmit,
.buffer_info = wm8904_audio_buffer_info,
};