Загрузка данных


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, &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));
}