/* * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include "es8311.h" #include "driver/gpio.h" #include "esp_log.h" #include "esp_err.h" #include "esp_check.h" #include "es8311_reg.h" #include "esp32-hal-i2c.h" typedef struct { unsigned int port; uint16_t dev_addr; } es8311_dev_t; /* * Clock coefficient structure */ struct _coeff_div { uint32_t mclk; /* mclk frequency */ uint32_t rate; /* sample rate */ uint8_t pre_div; /* the pre divider with range from 1 to 8 */ uint8_t pre_multi; /* the pre multiplier with 0: 1x, 1: 2x, 2: 4x, 3: 8x selection */ uint8_t adc_div; /* adcclk divider */ uint8_t dac_div; /* dacclk divider */ uint8_t fs_mode; /* double speed or single speed, =0, ss, =1, ds */ uint8_t lrck_h; /* adclrck divider and daclrck divider */ uint8_t lrck_l; uint8_t bclk_div; /* sclk divider */ uint8_t adc_osr; /* adc osr */ uint8_t dac_osr; /* dac osr */ }; /* codec hifi mclk clock divider coefficients */ static const struct _coeff_div coeff_div[] = { /*!port, es->dev_addr, write_buf, sizeof(write_buf), 1000); } static inline esp_err_t es8311_read_reg(es8311_handle_t dev, uint8_t reg_addr, uint8_t *reg_value) { es8311_dev_t *es = (es8311_dev_t *) dev; size_t readCount = 0; return i2cWriteReadNonStop(es->port, es->dev_addr, ®_addr, 1, reg_value, 1, 1000, &readCount); } /* * look for the coefficient in coeff_div[] table */ static int get_coeff(uint32_t mclk, uint32_t rate) { for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) { if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) { return i; } } return -1; } esp_err_t es8311_sample_frequency_config(es8311_handle_t dev, int mclk_frequency, int sample_frequency) { uint8_t regv; /* Get clock coefficients from coefficient table */ int coeff = get_coeff(mclk_frequency, sample_frequency); if (coeff < 0) { ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_frequency, mclk_frequency); return ESP_ERR_INVALID_ARG; } const struct _coeff_div *const selected_coeff = &coeff_div[coeff]; /* register 0x02 */ ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG02, ®v), TAG, "I2C read/write error"); regv &= 0x07; regv |= (selected_coeff->pre_div - 1) << 5; regv |= selected_coeff->pre_multi << 3; ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG02, regv), TAG, "I2C read/write error"); /* register 0x03 */ const uint8_t reg03 = (selected_coeff->fs_mode << 6) | selected_coeff->adc_osr; ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG03, reg03), TAG, "I2C read/write error"); /* register 0x04 */ ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG04, selected_coeff->dac_osr), TAG, "I2C read/write error"); /* register 0x05 */ const uint8_t reg05 = ((selected_coeff->adc_div - 1) << 4) | (selected_coeff->dac_div - 1); ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG05, reg05), TAG, "I2C read/write error"); /* register 0x06 */ ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG06, ®v), TAG, "I2C read/write error"); regv &= 0xE0; if (selected_coeff->bclk_div < 19) { regv |= (selected_coeff->bclk_div - 1) << 0; } else { regv |= (selected_coeff->bclk_div) << 0; } ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG06, regv), TAG, "I2C read/write error"); /* register 0x07 */ ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG07, ®v), TAG, "I2C read/write error"); regv &= 0xC0; regv |= selected_coeff->lrck_h << 0; ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG07, regv), TAG, "I2C read/write error"); /* register 0x08 */ ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG08, selected_coeff->lrck_l), TAG, "I2C read/write error"); return ESP_OK; } static esp_err_t es8311_clock_config(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, es8311_resolution_t res) { uint8_t reg06; uint8_t reg01 = 0x3F; // Enable all clocks int mclk_hz; /* Select clock source for internal MCLK and determine its frequency */ if (clk_cfg->mclk_from_mclk_pin) { mclk_hz = clk_cfg->mclk_frequency; } else { mclk_hz = clk_cfg->sample_frequency * (int)res * 2; reg01 |= BIT(7); // Select BCLK (a.k.a. SCK) pin } if (clk_cfg->mclk_inverted) { reg01 |= BIT(6); // Invert MCLK pin } ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG01, reg01), TAG, "I2C read/write error"); ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_CLK_MANAGER_REG06, ®06), TAG, "I2C read/write error"); if (clk_cfg->sclk_inverted) { reg06 |= BIT(5); } else { reg06 &= ~BIT(5); } ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_CLK_MANAGER_REG06, reg06), TAG, "I2C read/write error"); /* Configure clock dividers */ return es8311_sample_frequency_config(dev, mclk_hz, clk_cfg->sample_frequency); } static esp_err_t es8311_resolution_config(const es8311_resolution_t res, uint8_t *reg) { switch (res) { case ES8311_RESOLUTION_16: *reg |= (3 << 2); break; case ES8311_RESOLUTION_18: *reg |= (2 << 2); break; case ES8311_RESOLUTION_20: *reg |= (1 << 2); break; case ES8311_RESOLUTION_24: *reg |= (0 << 2); break; case ES8311_RESOLUTION_32: *reg |= (4 << 2); break; default: return ESP_ERR_INVALID_ARG; } return ESP_OK; } static esp_err_t es8311_fmt_config(es8311_handle_t dev, const es8311_resolution_t res_in, const es8311_resolution_t res_out) { uint8_t reg09 = 0; // SDP In uint8_t reg0a = 0; // SDP Out ESP_LOGI(TAG, "ES8311 in Slave mode and I2S format"); uint8_t reg00; ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_RESET_REG00, ®00), TAG, "I2C read/write error"); reg00 &= 0xBF; ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, reg00), TAG, "I2C read/write error"); // Slave serial port - default /* Setup SDP In and Out resolution */ es8311_resolution_config(res_in, ®09); es8311_resolution_config(res_out, ®0a); ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SDPIN_REG09, reg09), TAG, "I2C read/write error"); ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SDPOUT_REG0A, reg0a), TAG, "I2C read/write error"); return ESP_OK; } esp_err_t es8311_microphone_config(es8311_handle_t dev, bool digital_mic) { uint8_t reg14 = 0x1A; // enable analog MIC and max PGA gain /* PDM digital microphone enable or disable */ if (digital_mic) { reg14 |= BIT(6); } es8311_write_reg(dev, ES8311_ADC_REG17, 0xC8); // Set ADC gain @todo move this to ADC config section return es8311_write_reg(dev, ES8311_SYSTEM_REG14, reg14); } esp_err_t es8311_init(es8311_handle_t dev, const es8311_clock_config_t *const clk_cfg, const es8311_resolution_t res_in, const es8311_resolution_t res_out) { ESP_RETURN_ON_FALSE( (clk_cfg->sample_frequency >= 8000) && (clk_cfg->sample_frequency <= 96000), ESP_ERR_INVALID_ARG, TAG, "ES8311 init needs frequency in interval [8000; 96000] Hz" ); if (!clk_cfg->mclk_from_mclk_pin) { ESP_RETURN_ON_FALSE(res_out == res_in, ESP_ERR_INVALID_ARG, TAG, "Resolution IN/OUT must be equal if MCLK is taken from SCK pin"); } /* Reset ES8311 to its default */ ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x1F), TAG, "I2C read/write error"); ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x00), TAG, "I2C read/write error"); ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_RESET_REG00, 0x80), TAG, "I2C read/write error"); // Power-on command /* Setup clock: source, polarity and clock dividers */ ESP_RETURN_ON_ERROR(es8311_clock_config(dev, clk_cfg, res_out), TAG, ""); /* Setup audio format (fmt): master/slave, resolution, I2S */ ESP_RETURN_ON_ERROR(es8311_fmt_config(dev, res_in, res_out), TAG, ""); ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG0D, 0x01), TAG, "I2C read/write error"); // Power up analog circuitry - NOT default ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG0E, 0x02), TAG, "I2C read/write error"); // Enable analog PGA, enable ADC modulator - NOT default ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG12, 0x00), TAG, "I2C read/write error"); // power-up DAC - NOT default ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_SYSTEM_REG13, 0x10), TAG, "I2C read/write error"); // Enable output to HP drive - NOT default ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_ADC_REG1C, 0x6A), TAG, "I2C read/write error"); // ADC Equalizer bypass, cancel DC offset in digital domain ESP_RETURN_ON_ERROR(es8311_write_reg(dev, ES8311_DAC_REG37, 0x08), TAG, "I2C read/write error"); // Bypass DAC equalizer - NOT default return ESP_OK; } void es8311_delete(es8311_handle_t dev) { free(dev); } esp_err_t es8311_voice_volume_set(es8311_handle_t dev, int volume, int *volume_set) { if (volume < 0) { volume = 0; } else if (volume > 100) { volume = 100; } int reg32; if (volume == 0) { reg32 = 0; } else { reg32 = ((volume) * 256 / 100) - 1; } // provide user with real volume set if (volume_set != NULL) { *volume_set = volume; } return es8311_write_reg(dev, ES8311_DAC_REG32, reg32); } esp_err_t es8311_voice_volume_get(es8311_handle_t dev, int *volume) { uint8_t reg32; ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG32, ®32), TAG, "I2C read/write error"); if (reg32 == 0) { *volume = 0; } else { *volume = ((reg32 * 100) / 256) + 1; } return ESP_OK; } esp_err_t es8311_voice_mute(es8311_handle_t dev, bool mute) { uint8_t reg31; ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG31, ®31), TAG, "I2C read/write error"); if (mute) { reg31 |= BIT(6) | BIT(5); } else { reg31 &= ~(BIT(6) | BIT(5)); } return es8311_write_reg(dev, ES8311_DAC_REG31, reg31); } esp_err_t es8311_microphone_gain_set(es8311_handle_t dev, es8311_mic_gain_t gain_db) { return es8311_write_reg(dev, ES8311_ADC_REG16, gain_db); // ADC gain scale up } esp_err_t es8311_voice_fade(es8311_handle_t dev, const es8311_fade_t fade) { uint8_t reg37; ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_DAC_REG37, ®37), TAG, "I2C read/write error"); reg37 &= 0x0F; reg37 |= (fade << 4); return es8311_write_reg(dev, ES8311_DAC_REG37, reg37); } esp_err_t es8311_microphone_fade(es8311_handle_t dev, const es8311_fade_t fade) { uint8_t reg15; ESP_RETURN_ON_ERROR(es8311_read_reg(dev, ES8311_ADC_REG15, ®15), TAG, "I2C read/write error"); reg15 &= 0x0F; reg15 |= (fade << 4); return es8311_write_reg(dev, ES8311_ADC_REG15, reg15); } void es8311_register_dump(es8311_handle_t dev) { for (int reg = 0; reg < 0x4A; reg++) { uint8_t value; ESP_ERROR_CHECK(es8311_read_reg(dev, reg, &value)); printf("REG:%02x: %02x", reg, value); } } es8311_handle_t es8311_create(const unsigned int port, const uint16_t dev_addr) { es8311_dev_t *sensor = (es8311_dev_t *) calloc(1, sizeof(es8311_dev_t)); sensor->port = port; sensor->dev_addr = dev_addr; return (es8311_handle_t) sensor; }