Загрузка данных
static int wm8960_apply_properties(const struct device *dev)
{
/**
* Set VU = 1 for all input and output channels, VU takes effect for the whole
* channel pair.
*/
wm8960_update_reg(dev, WM8960_REG_LOUT1, WM8960_REGVAL_OUT_VOL(1, 0, 0),
WM8960_REGMASK_OUT_VU);
wm8960_update_reg(dev, WM8960_REG_LINVOL, WM8960_REGVAL_IN_VOL(1, 0, 0, 0),
WM8960_REGMASK_IN_VU);
return 0;
}
static void wm8960_read_reg(const struct device *dev, uint8_t reg, uint16_t *val)
{
if (reg >= WM8960_REG_CACHEREGNUM)
return;
*val = reg_cache[reg];
}
static void wm8960_write_reg(const struct device *dev, uint8_t reg, uint16_t val)
{
const struct wm8960_driver_config *const dev_cfg = DEV_CFG(dev);
uint8_t data[4];
int ret;
if (reg >= WM8960_REG_CACHEREGNUM)
return;
reg_cache[reg] = val;
/* data is reversed */
data[0] = (reg << 1) | (uint8_t)((val >> 8U) & 0x0001U);
data[1] = (uint8_t)(val & 0xff);
ret = i2c_write(dev_cfg->i2c.bus, data, 2, dev_cfg->i2c.addr);
if (ret != 0) {
LOG_ERR("i2c write to codec error %d", ret);
}
LOG_INF("REG:%#02x VAL:%#02x", reg, val);
}
static void wm8960_update_reg(const struct device *dev, uint8_t reg, uint16_t mask, uint16_t val)
{
uint16_t reg_val = 0;
uint16_t new_value = 0;
wm8960_read_reg(dev, reg, ®_val);
LOG_INF("read %#x = %x", reg, reg_val);
new_value = (reg_val & ~mask) | (val & mask);
LOG_INF("write %#x = %x", reg, new_value);
wm8960_write_reg(dev, reg, new_value);
}
static void wm8960_soft_reset(const struct device *dev)
{
wm8960_write_reg(dev, WM8960_REG_RESET, 0x00);
}
static void wm8960_set_module(const struct device *dev, wm8960_module_t module, bool isEnabled)
{
switch (module)
{
case kWM8960_ModuleADC:
wm8960_update_reg(dev, WM8960_REG_POWER1, WM8960_POWER1_ADCL_MASK, ((uint16_t)isEnabled << WM8960_POWER1_ADCL_SHIFT));
wm8960_update_reg(dev, WM8960_REG_POWER1, WM8960_POWER1_ADCR_MASK,((uint16_t)isEnabled << WM8960_POWER1_ADCR_SHIFT));
break;
case kWM8960_ModuleDAC:
wm8960_update_reg(dev, WM8960_REG_POWER2, WM8960_POWER2_DACL_MASK,((uint16_t)isEnabled << WM8960_POWER2_DACL_SHIFT));
wm8960_update_reg(dev, WM8960_REG_POWER2, WM8960_POWER2_DACR_MASK,((uint16_t)isEnabled << WM8960_POWER2_DACR_SHIFT));
break;
case kWM8960_ModuleVREF:
wm8960_update_reg(dev, WM8960_REG_POWER1, WM8960_POWER1_VREF_MASK,((uint16_t)isEnabled << WM8960_POWER1_VREF_SHIFT));
break;
case kWM8960_ModuleLineIn:
wm8960_update_reg(dev, WM8960_REG_POWER1, WM8960_POWER1_AINL_MASK,((uint16_t)isEnabled << WM8960_POWER1_AINL_SHIFT));
wm8960_update_reg(dev, WM8960_REG_POWER1, WM8960_POWER1_AINR_MASK,((uint16_t)isEnabled << WM8960_POWER1_AINR_SHIFT));
wm8960_update_reg(dev, WM8960_REG_POWER3, WM8960_POWER3_LMIC_MASK,((uint16_t)isEnabled << WM8960_POWER3_LMIC_SHIFT));
wm8960_update_reg(dev, WM8960_REG_POWER3, WM8960_POWER3_RMIC_MASK,((uint16_t)isEnabled << WM8960_POWER3_RMIC_SHIFT));
break;
case kWM8960_ModuleLineOut:
wm8960_update_reg(dev, WM8960_REG_POWER2, WM8960_POWER2_LOUT1_MASK,((uint16_t)isEnabled << WM8960_POWER2_LOUT1_SHIFT));
wm8960_update_reg(dev, WM8960_REG_POWER2, WM8960_POWER2_ROUT1_MASK,((uint16_t)isEnabled << WM8960_POWER2_ROUT1_SHIFT));
break;
case kWM8960_ModuleMICB:
wm8960_update_reg(dev, WM8960_REG_POWER1, WM8960_POWER1_MICB_MASK,((uint16_t)isEnabled << WM8960_POWER1_MICB_SHIFT));
break;
case kWM8960_ModuleSpeaker:
wm8960_update_reg(dev, WM8960_REG_POWER2, WM8960_POWER2_SPKL_MASK,((uint16_t)isEnabled << WM8960_POWER2_SPKL_SHIFT));
wm8960_update_reg(dev, WM8960_REG_POWER2, WM8960_POWER2_SPKR_MASK,((uint16_t)isEnabled << WM8960_POWER2_SPKR_SHIFT));
wm8960_write_reg(dev, WM8960_REG_CLASSD1, 0xF7);
break;
case kWM8960_ModuleOMIX:
wm8960_update_reg(dev, WM8960_REG_POWER3, WM8960_POWER3_LOMIX_MASK,((uint16_t)isEnabled << WM8960_POWER3_LOMIX_SHIFT));
wm8960_update_reg(dev, WM8960_REG_POWER3, WM8960_POWER3_ROMIX_MASK,((uint16_t)isEnabled << WM8960_POWER3_ROMIX_SHIFT));
break;
default:
break;
}
}
static void wm8960_set_data_route(const struct device *dev, audio_route_t route)
{
switch (route)
{
case AUDIO_ROUTE_BYPASS:
/* Bypass means from line-in to HP*/
/* Left LINPUT3 to left output mixer, LINPUT3 left output mixer volume = 0dB */
wm8960_write_reg(dev, WM8960_REG_LOUTMIX, 0x80);
/* Right RINPUT3 to right output mixer, RINPUT3 right output mixer volume = 0dB */
wm8960_write_reg(dev, WM8960_REG_ROUTMIX, 0x80);
break;
case AUDIO_ROUTE_PLAYBACK:
/* Data route I2S_IN-> DAC-> HP */
/* Left DAC to left output mixer, LINPUT3 left output mixer volume = 0dB */
wm8960_write_reg(dev, WM8960_REG_LOUTMIX, 0x100);
/* Right DAC to right output mixer, RINPUT3 right output mixer volume = 0dB */
wm8960_write_reg(dev, WM8960_REG_ROUTMIX, 0x100);
wm8960_write_reg(dev, WM8960_REG_POWER3, 0x0C);
/* Set power for DAC */
wm8960_set_module(dev, kWM8960_ModuleDAC, true);
wm8960_set_module(dev, kWM8960_ModuleOMIX, true);
wm8960_set_module(dev, kWM8960_ModuleLineOut, true);
break;
case AUDIO_ROUTE_PLAYBACK_CAPTURE:
/* Left DAC to left output mixer, LINPUT3 left output mixer volume = 0dB */
wm8960_write_reg(dev, WM8960_REG_LOUTMIX, 0x100);
/* Right DAC to right output mixer, RINPUT3 right output mixer volume = 0dB */
wm8960_write_reg(dev, WM8960_REG_ROUTMIX, 0x100);
wm8960_write_reg(dev, WM8960_REG_POWER3, 0x3C);
wm8960_set_module(dev, kWM8960_ModuleDAC, true);
wm8960_set_module(dev, kWM8960_ModuleADC, true);
wm8960_set_module(dev, kWM8960_ModuleLineIn, true);
wm8960_set_module(dev, kWM8960_ModuleOMIX, true);
wm8960_set_module(dev, kWM8960_ModuleLineOut, true);
break;
case AUDIO_ROUTE_CAPTURE:
/* LINE_IN->ADC->I2S_OUT */
/* Left and right input boost, LIN3BOOST and RIN3BOOST = 0dB */
wm8960_write_reg(dev, WM8960_REG_POWER3, 0x30);
/* Power up ADC and AIN */
wm8960_set_module(dev, kWM8960_ModuleLineIn, true);
wm8960_set_module(dev, kWM8960_ModuleADC, true);
break;
default:
break;
}
}
static void wm8960_set_play(const struct device *dev, uint32_t playSource)
{
if (((uint32_t)kWM8960_PlaySourcePGA & playSource) != 0U)
{
wm8960_update_reg(dev, WM8960_REG_BYPASS1, 0x80U, 0x80U);
wm8960_update_reg(dev, WM8960_REG_BYPASS2, 0x80U, 0x80U);
wm8960_update_reg(dev, WM8960_REG_LOUTMIX, 0x180U, 0U);
wm8960_update_reg(dev, WM8960_REG_ROUTMIX, 0x180U, 0U);
}
if ((playSource & (uint32_t)kWM8960_PlaySourceDAC) != 0U)
{
wm8960_update_reg(dev, WM8960_REG_BYPASS1, 0x80U, 0x00U);
wm8960_update_reg(dev, WM8960_REG_BYPASS2, 0x80U, 0x00U);
wm8960_update_reg(dev, WM8960_REG_LOUTMIX, 0x180U, 0x100U);
wm8960_update_reg(dev, WM8960_REG_ROUTMIX, 0x180U, 0x100U);
}
if ((playSource & (uint32_t)kWM8960_PlaySourceInput) != 0U)
{
wm8960_update_reg(dev, WM8960_REG_BYPASS1, 0x80U, 0x0U);
wm8960_update_reg(dev, WM8960_REG_BYPASS2, 0x80U, 0x0U);
wm8960_update_reg(dev, WM8960_REG_LOUTMIX, 0x180U, 0x80U);
wm8960_update_reg(dev, WM8960_REG_ROUTMIX, 0x180U, 0x80U);
}
}
static void wm8960_set_internal_PllConfig(const struct device *dev, uint32_t inputMclk, uint32_t outputClk, uint32_t sampleRate, uint32_t bitWidth)
{
uint32_t pllF2 = outputClk * 4U, pllPrescale = 0U, sysclkDiv = 1U, pllR = 0, pllN = 0, pllK = 0U, fracMode = 0U;
/* disable PLL power */
wm8960_update_reg(dev, WM8960_REG_POWER2, 1U, 0U);
wm8960_update_reg(dev, WM8960_REG_CLOCK1, 7U, 0U);
pllN = pllF2 / inputMclk;
if (pllN < WM8960_PLL_N_MIN_VALUE)
{
inputMclk >>= 1U;
pllPrescale = 1;
pllN = pllF2 / inputMclk;
if (pllN < WM8960_PLL_N_MIN_VALUE)
{
sysclkDiv = 2U;
pllN = (pllF2 * sysclkDiv) / inputMclk;
}
}
if ((pllN < WM8960_PLL_N_MIN_VALUE) || (pllN > WM8960_PLL_N_MAX_VALUE))
{
LOG_ERR("Invalid pllN value = %u\n",pllN);
return;
}
pllR = (uint32_t)(((uint64_t)pllF2 * sysclkDiv * 1000U) / (inputMclk / 1000U));
pllK = (uint32_t)(((1UL << 24U) * ((uint64_t)pllR - (uint64_t)pllN * 1000U * 1000U)) / 1000U / 1000U);
if (pllK != 0U)
{
fracMode = 1U;
}
wm8960_write_reg(dev, WM8960_REG_PLL1,((uint16_t)fracMode << 5U) | ((uint16_t)pllPrescale << 4U) | ((uint16_t)pllN & 0xFU));
wm8960_write_reg(dev, WM8960_REG_PLL2, (uint16_t)(pllK >> 16U) & 0xFFU);
wm8960_write_reg(dev, WM8960_REG_PLL3, (uint16_t)(pllK >> 8U) & 0xFFU);
wm8960_write_reg(dev, WM8960_REG_PLL4, (uint16_t)pllK & 0xFFU);
/* enable PLL power */
wm8960_update_reg(dev, WM8960_REG_POWER2, 1U, 1U);
wm8960_update_reg(dev, WM8960_REG_CLOCK1, 7U, (uint16_t)(((sysclkDiv == 1U ? 0U : sysclkDiv) << 1U) | 1U));
}