add some code

This commit is contained in:
2025-09-05 13:25:11 +08:00
parent 9ff0a99e7a
commit 3cf1229a85
8911 changed files with 2535396 additions and 0 deletions

View File

@@ -0,0 +1,367 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "aw88298_dac.h"
#include "aw88298_reg.h"
#include "esp_codec_dev_os.h"
#include "esp_log.h"
#define TAG "AW88298"
typedef struct {
audio_codec_if_t base;
const audio_codec_ctrl_if_t *ctrl_if;
const audio_codec_gpio_if_t *gpio_if;
bool is_open;
bool enabled;
int16_t reset_pin;
float hw_gain;
} audio_codec_aw88298_t;
/* The volume register mapped to decibel table can get from codec data-sheet
Volume control register 0x0C description:
0xC0 - '-96dB' ... 0x00 - '+0dB'
*/
static const esp_codec_dev_vol_range_t vol_range = {
.min_vol =
{
.vol = 0xC0,
.db_value = -96,
},
.max_vol =
{
.vol = 0,
.db_value = 0,
},
};
static int aw88298_write_reg(audio_codec_aw88298_t *codec, int reg, int value)
{
uint8_t write_data[2] = {(uint8_t) ((value & 0xFFFF) >> 8), (uint8_t) (value & 0xFF)};
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, write_data, 2);
}
static int aw88298_read_reg(audio_codec_aw88298_t *codec, int reg, int *value)
{
int ret = 0;
uint8_t read_data[2] = {0};
ret = codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, read_data, 2);
*value = ((int)read_data[0] << 8) | read_data[1];
return ret;
}
static int aw88298_set_bits_per_sample(audio_codec_aw88298_t *codec, uint8_t bits)
{
int ret = 0;
int dac_iface = 0;
ret = aw88298_read_reg(codec, AW88298_I2SCTRL_REG06, &dac_iface);
dac_iface &= ~(0xF0);
switch (bits) {
case 16:
default:
break;
case 24:
dac_iface |= 0x90;
break;
case 32:
dac_iface |= 0xE0;
break;
}
ret |= aw88298_write_reg(codec, AW88298_I2SCTRL_REG06, dac_iface);
ESP_LOGD(TAG, "Bits %d", bits);
return ret;
}
static int aw88298_config_sample(audio_codec_aw88298_t *codec, int sample_rate)
{
int ret = 0;
int dac_iface = 0;
ret = aw88298_read_reg(codec, AW88298_I2SCTRL_REG06, &dac_iface);
dac_iface &= ~(0x0F);
switch (sample_rate) {
case 8000:
break;
case 11025:
dac_iface |= 0x01;
break;
case 12000:
dac_iface |= 0x02;
break;
case 16000:
dac_iface |= 0x03;
break;
case 22050:
dac_iface |= 0x04;
break;
case 24000:
dac_iface |= 0x05;
break;
case 32000:
dac_iface |= 0x06;
break;
case 44100:
dac_iface |= 0x07;
break;
case 48000:
dac_iface |= 0x08;
break;
case 96000:
dac_iface |= 0x09;
break;
case 192000:
dac_iface |= 0x0A;
break;
default:
ESP_LOGE(TAG, "Sample rate(%d) can not support", sample_rate);
return ESP_CODEC_DEV_NOT_SUPPORT;
}
ESP_LOGD(TAG, "Current sample rate: %d", sample_rate);
ret |= aw88298_write_reg(codec, AW88298_I2SCTRL_REG06, dac_iface);
return ret;
}
static int aw88298_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
{
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
if (codec == NULL || codec->is_open == false) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = 0;
ret |= aw88298_set_bits_per_sample(codec, fs->bits_per_sample);
ret |= aw88298_config_sample(codec, fs->sample_rate);
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;;
}
static int aw88298_stop(audio_codec_aw88298_t *codec)
{
int ret = 0;
int data = 0;
ret |= aw88298_read_reg(codec, AW88298_SYSCTRL_REG04, &data);
data |= 0x03;
data &= ~(1 << 6);
ret |= aw88298_write_reg(codec, AW88298_SYSCTRL_REG04, data);
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;;
}
static int aw88298_start(audio_codec_aw88298_t *codec)
{
int ret = 0;
int data = 0;
ret |= aw88298_read_reg(codec, AW88298_SYSCTRL_REG04, &data);
data &= ~0x03;
data |= (1 << 6);
ret |= aw88298_write_reg(codec, AW88298_SYSCTRL_REG04, data);
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;;
}
static int aw88298_set_mute(const audio_codec_if_t *h, bool mute)
{
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
if (codec == NULL || codec->is_open == false) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int regv;
int ret = aw88298_read_reg(codec, AW88298_SYSCTRL2_REG05, &regv);
if (ret < 0) {
return ESP_CODEC_DEV_READ_FAIL;
}
if (mute) {
regv = regv | (1 << 4);
} else {
regv = regv & (~(1 << 4));
}
return aw88298_write_reg(codec, AW88298_SYSCTRL2_REG05, regv);
}
static int aw88298_set_vol(const audio_codec_if_t *h, float volume)
{
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
volume -= codec->hw_gain;
int reg = esp_codec_dev_vol_calc_reg(&vol_range, volume);
reg = (reg << 8) | 0x64;
int ret = aw88298_write_reg(codec, AW88298_HAGCCFG4_REG0C, reg);
ESP_LOGD(TAG, "Set volume reg:%x db:%.2f", reg, esp_codec_dev_vol_calc_db(&vol_range, reg >> 8));
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static void aw88298_reset(audio_codec_aw88298_t *codec, int16_t reset_pin)
{
if (reset_pin <= 0 || codec->gpio_if == NULL) {
return;
}
codec->gpio_if->setup(reset_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
codec->gpio_if->set(reset_pin, 0);
esp_codec_dev_sleep(10);
codec->gpio_if->set(reset_pin, 1);
esp_codec_dev_sleep(50);
}
static int aw88298_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
aw88298_codec_cfg_t *codec_cfg = (aw88298_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg == NULL || cfg_size != sizeof(aw88298_codec_cfg_t) || codec_cfg->ctrl_if == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = ESP_CODEC_DEV_OK;
codec->ctrl_if = codec_cfg->ctrl_if;
codec->gpio_if = codec_cfg->gpio_if;
codec->reset_pin = codec_cfg->reset_pin;
aw88298_reset(codec, codec_cfg->reset_pin);
ret |= aw88298_write_reg(codec, AW88298_RESET_REG00, 0x55aa); // Reset chip
ret |= aw88298_write_reg(codec, AW88298_SYSCTRL_REG04, 0x4040); // I2SEN=1 AMPPD=0 PWDN=0
ret |= aw88298_write_reg(codec, AW88298_SYSCTRL2_REG05, 0x0008); // RMSE=0 HAGCE=0 HDCCE=0 HMUTE=0
ret |= aw88298_write_reg(codec, AW88298_I2SCTRL_REG06, 0x3CC8); // I2SBCK=0 (BCK mode 16*2)
ret |= aw88298_write_reg(codec, AW88298_HAGCCFG4_REG0C, 0x3064); // volume setting
ret |= aw88298_write_reg(codec, AW88298_BSTCTRL2_REG61, 0x0673); // default:0x6673: BOOST mode disabled
if (ret != 0) {
return ESP_CODEC_DEV_WRITE_FAIL;
}
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int aw88298_close(const audio_codec_if_t *h)
{
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open) {
aw88298_stop(codec);
codec->is_open = false;
}
return ESP_CODEC_DEV_OK;
}
static int aw88298_enable(const audio_codec_if_t *h, bool enable)
{
int ret = ESP_CODEC_DEV_OK;
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (codec->enabled == enable) {
return ESP_CODEC_DEV_OK;
}
if (enable) {
if (codec->reset_pin > 0) {
codec->gpio_if->set(codec->reset_pin, 1);
esp_codec_dev_sleep(10);
}
ret = aw88298_start(codec);
} else {
ret = aw88298_stop(codec);
if (codec->reset_pin > 0) {
esp_codec_dev_sleep(10);
codec->gpio_if->set(codec->reset_pin, 0);
}
}
if (ret == ESP_CODEC_DEV_OK) {
codec->enabled = enable;
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
}
return ret;
}
static int aw88298_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return aw88298_write_reg(codec, reg, value);
}
static int aw88298_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return aw88298_read_reg(codec, reg, value);
}
static void aw88298_dump(const audio_codec_if_t *h)
{
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i <= 0x14; i++) {
if (i == 0x08 || i == 0x0D || i == 0x0E || i == 0x0F || i == 0x11) {
continue;
}
int value = 0;
int ret = aw88298_read_reg(codec, i, &value);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %04x", i, value);
}
for (int i = 0x60; i <= 0x61; i++) {
int value = 0;
int ret = aw88298_read_reg(codec, i, &value);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %04x", i, value);
}
}
const audio_codec_if_t *aw88298_codec_new(aw88298_codec_cfg_t *codec_cfg)
{
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_aw88298_t *codec = (audio_codec_aw88298_t *) calloc(1, sizeof(audio_codec_aw88298_t));
if (codec == NULL) {
ESP_LOGE(TAG, "No memory for instance");
return NULL;
}
codec->ctrl_if = codec_cfg->ctrl_if;
codec->base.open = aw88298_open;
codec->base.enable = aw88298_enable;
codec->base.set_fs = aw88298_set_fs;
codec->base.set_vol = aw88298_set_vol;
codec->base.mute = aw88298_set_mute;
codec->base.set_reg = aw88298_set_reg;
codec->base.get_reg = aw88298_get_reg;
codec->base.dump_reg = aw88298_dump;
codec->base.close = aw88298_close;
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(aw88298_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail");
free(codec);
return NULL;
}
return &codec->base;
} while (0);
}

View File

@@ -0,0 +1,86 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _AW88298_REG_H_
#define _AW88298_REG_H_
#ifdef __cplusplus
extern "C" {
#endif
/* AW88298 register space */
/*
* RESET Control
*/
#define AW88298_RESET_REG00 0x00
/*
* System Control
*/
#define AW88298_SYSST_REG01 0x01
/*
* System interrupt
*/
#define AW88298_SYSINT_REG02 0x02
#define AW88298_SYSINTM_REG03 0x03
/*
* System Control
*/
#define AW88298_SYSCTRL_REG04 0x04
#define AW88298_SYSCTRL2_REG05 0x05
/*
* I2S control and config
*/
#define AW88298_I2SCTRL_REG06 0x06
#define AW88298_I2SCFG1_REG07 0x07
/*
* HAGC config
*/
#define AW88298_HAGCCFG1_REG09 0x09
#define AW88298_HAGCCFG2_REG0A 0x0a
#define AW88298_HAGCCFG3_REG0B 0x0b
#define AW88298_HAGCCFG4_REG0C 0x0C
/*
* HAGC boost output voltage
*/
#define AW88298_HAGCST_REG10 0x10
/*
* Detected voltage of battery
*/
#define AW88298_VDD_REG12 0x12
/*
* Detected die temperature
*/
#define AW88298_TEMP_REG13 0x13
/*
* Detected voltage of PVDD
*/
#define AW88298_PVDD_REG14 0x14
/*
* Smart boost control
*/
#define AW88298_BSTCTRL1_REG60 0x60
#define AW88298_BSTCTRL2_REG61 0x61
/*
* Chip Information
*/
#define AW88298_CHIP_VERSION_REG00 0x00 // ID: 1852h
#ifdef __cplusplus
}
#endif
#endif /* _AW88298_REG_H_ */

View File

@@ -0,0 +1,386 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"
#include "esp_codec_dev_vol.h"
#include "cjc8910_codec.h"
#include "cjc8910_reg.h"
#define CODEC_MEM_CHECK(ptr) if (ptr == NULL) { \
ESP_LOGE(TAG, "Fail to alloc memory at %s:%d", __FUNCTION__, __LINE__); \
}
#define INVERT_SCLK_BIT (7)
#define INVERT_LR_BIT (4)
typedef enum {
PA_SETUP = 1 << 0,
PA_ENABLE = 1 << 1,
PA_DISABLE = 1 << 2,
} pa_setting_t;
typedef enum {
CJC8910_I2S_FMT_I2S = 0,
CJC8910_I2S_FMT_LEFT = 1,
CJC8910_I2S_FMT_DSP = 2,
} cjc8910_i2s_fmt_t;
typedef struct {
audio_codec_if_t base;
cjc8910_codec_cfg_t cfg;
float hw_gain;
bool is_open;
bool enabled;
} audio_codec_cjc8910_t;
static const char *TAG = "CJC8910";
static const esp_codec_dev_vol_range_t vol_range = {
.min_vol = {
.vol = 0x01,
.db_value = -97,
},
.max_vol = {
.vol = 0xFF,
.db_value = 30.0,
},
};
static int cjc8910_write_reg(audio_codec_cjc8910_t *codec, int reg, int value)
{
ESP_LOGD(TAG, "Write reg %d, value %d", reg, value);
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg, 1, &value, 1);
}
static int cjc8910_read_reg(audio_codec_cjc8910_t *codec, int reg, int *value)
{
*value = 0;
return codec->cfg.ctrl_if->read_reg(codec->cfg.ctrl_if, reg, 1, value, 1);
}
static int cjc8910_config_fmt(audio_codec_cjc8910_t *codec, cjc8910_i2s_fmt_t fmt)
{
int ret = ESP_CODEC_DEV_OK;
int format = 0;
ret = cjc8910_read_reg(codec, CJC8910_R7_AUDIO_INTERFACE, &format);
format &= ~0x03;
switch (fmt) {
case CJC8910_I2S_FMT_I2S:
format |= 0x02; // FORMAT[1:0] = 10 (standard I2S)
break;
case CJC8910_I2S_FMT_LEFT:
format |= 0x01; // FORMAT[1:0] = 01 (left justified)
break;
case CJC8910_I2S_FMT_DSP:
format |= 0x03; // FORMAT[1:0] = 11 (DSP/PCM)
break;
default:
format |= 0x02;
break;
}
ret |= cjc8910_write_reg(codec, CJC8910_R7_AUDIO_INTERFACE, format);
return ret;
}
static void cjc8910_pa_power(audio_codec_cjc8910_t *codec, pa_setting_t pa_setting)
{
int pa_pin = codec->cfg.pa_pin;
if (pa_pin == -1 || codec->cfg.gpio_if == NULL) {
return;
}
if (pa_setting & PA_SETUP) {
codec->cfg.gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
}
if (pa_setting & PA_ENABLE) {
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? false : true);
}
if (pa_setting & PA_DISABLE) {
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? true : false);
}
}
static void cjc8910_dump(const audio_codec_if_t *h)
{
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i < CJC8910_MAX_REGISTER; i++) {
int value = 0;
int ret = cjc8910_read_reg(codec, i, &value);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, value);
}
}
static int cjc8910_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
cjc8910_codec_cfg_t *codec_cfg = (cjc8910_codec_cfg_t *)cfg;
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(cjc8910_codec_cfg_t)) {
return ESP_CODEC_DEV_INVALID_ARG;
}
memcpy(&codec->cfg, cfg, sizeof(cjc8910_codec_cfg_t));
int regv;
int ret = ESP_CODEC_DEV_OK;
ret |= cjc8910_write_reg(codec, CJC8910_R15_RESET, 0x00); // Reset
ret |= cjc8910_write_reg(codec, CJC8910_R0_LEFT_INPUT_VOLUME, 0x17); // Audio input left channel volume
ret |= cjc8910_write_reg(codec, CJC8910_R2_LOUT1_VOLUME, 0x79); // Audio output letf channel1 volume
ret |= cjc8910_write_reg(codec, CJC8910_R5_ADC_DAC_CONTROL, 0x00); // ADC and DAC CONTROL
ret |= cjc8910_write_reg(codec, CJC8910_R7_AUDIO_INTERFACE, 0x0A); // Digital Audio interface format
ret |= cjc8910_write_reg(codec, CJC8910_R8_SAMPLE_RATE, 0x00); // Clock and Sample rate control
ret |= cjc8910_write_reg(codec, CJC8910_R10_LEFT_DAC_VOLUME, 0xC3); // Left channel DAC digital volume
ret |= cjc8910_write_reg(codec, CJC8910_R12_BASS_CONTROL, 0x0f); // BASS control
ret |= cjc8910_write_reg(codec, CJC8910_R13_TREBLE_CONTROL, 0x0f); // Treble control
ret |= cjc8910_write_reg(codec, CJC8910_R17_ALC1_CONTROL, 0x7B); // ALC1 control
ret |= cjc8910_write_reg(codec, CJC8910_R18_ALC2_CONTROL, 0x00); // ACL2 control
ret |= cjc8910_write_reg(codec, CJC8910_R19_ALC3_CONTROL, 0x00); // ALC3 control
ret |= cjc8910_write_reg(codec, CJC8910_R20_NOISE_GATE_CONTROL, 0x00); // Noise gate
ret |= cjc8910_write_reg(codec, CJC8910_R21_LEFT_ADC_VOLUME, 0xc3); // Left ADC digital volume
ret |= cjc8910_write_reg(codec, CJC8910_R23_ADDITIONAL_CONTROL1, 0x00); // Additional control 1
ret |= cjc8910_write_reg(codec, CJC8910_R24_ADDITIONAL_CONTROL2, 0x00); // Additional control 2
ret |= cjc8910_write_reg(codec, CJC8910_R27_ADDITIONAL_CONTROL3, 0x00); // Additional control 3
ret |= cjc8910_write_reg(codec, CJC8910_R32_ADCL_SIGNAL_PATH, 0x00); // ADC signal path control
ret |= cjc8910_write_reg(codec, CJC8910_R33_MIC, 0x0A); // MIC control
ret |= cjc8910_write_reg(codec, CJC8910_R34_AUX, 0x0A); // AUX control
ret |= cjc8910_write_reg(codec, CJC8910_R35_LEFT_OUT_MIX2_H, 0x00); // Left out Mix (2)
ret |= cjc8910_write_reg(codec, CJC8910_R37_ADC_PDN, 0x00); // Adc_pdn sel
ret |= cjc8910_write_reg(codec, CJC8910_R67_LOW_POWER_PLAYBACK, 0x00); // Low power playback
ret |= cjc8910_write_reg(codec, CJC8910_R25_PWR_MGMT1_H, 0xE8); // Power management1 and VMIDSEL
ret |= cjc8910_write_reg(codec, CJC8910_R26_PWR_MGMT2_H, 0x40); // Power management2 and DAC left power down
ret = cjc8910_read_reg(codec, CJC8910_R7_AUDIO_INTERFACE, &regv);
/* Only support codec in slave mode */
regv &= ~(1 << 6);
if (codec_cfg->invert_sclk) {
regv |= (1 << INVERT_SCLK_BIT);
} else {
regv &= ~(1 << INVERT_SCLK_BIT);
}
if (codec_cfg->invert_lr) {
regv |= (1 << INVERT_LR_BIT);
} else {
regv &= ~(1 << INVERT_LR_BIT);
}
ret |= cjc8910_write_reg(codec, CJC8910_R7_AUDIO_INTERFACE, regv);
if (ret != 0) {
return ESP_CODEC_DEV_WRITE_FAIL;
}
cjc8910_pa_power(codec, PA_SETUP | PA_ENABLE);
codec->is_open = true;
ESP_LOGI(TAG, "Codec is opened");
return ret;
}
static int cjc8910_set_vol(const audio_codec_if_t *h, float db_value)
{
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
int ret = ESP_CODEC_DEV_OK;
db_value -= codec->hw_gain;
uint8_t volume_reg_value = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
ESP_LOGD(TAG, "%s:set vol: %f, reg: %d", __func__, db_value, volume_reg_value);
ret |= cjc8910_write_reg(codec, CJC8910_R10_LEFT_DAC_VOLUME, volume_reg_value);
return ret;
}
static int cjc8910_set_mic_gain(const audio_codec_if_t *h, float db)
{
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
int ret = 0;
uint8_t volume_reg_value = esp_codec_dev_vol_calc_reg(&vol_range, db);
ret |= cjc8910_write_reg(codec, CJC8910_R21_LEFT_ADC_VOLUME, volume_reg_value);
ESP_LOGD(TAG, "Set mic gain: %f, reg: %d", db, volume_reg_value);
return ret;
}
static int cjc8910_set_bits_per_sample(audio_codec_cjc8910_t *codec, int bits)
{
int ret = ESP_CODEC_DEV_OK;
int audio_iface = 0;
ret |= cjc8910_read_reg(codec, CJC8910_R7_AUDIO_INTERFACE, &audio_iface);
// bit[3:2]: 11 = 32bit, 10 = 24bit, 01 = 20bit, 00 = 16bit
audio_iface &= ~0x0C;
switch (bits) {
case 16:
break;
case 20:
audio_iface |= 0x04;
break;
case 24:
audio_iface |= 0x08;
break;
case 32:
audio_iface |= 0x0C;
break;
default:
break;
}
ret |= cjc8910_write_reg(codec, CJC8910_R7_AUDIO_INTERFACE, audio_iface);
return ret;
}
static int cjc8910_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
{
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
int ret = ESP_CODEC_DEV_OK;
ret |= cjc8910_set_bits_per_sample(codec, fs->bits_per_sample);
ret |= cjc8910_config_fmt(codec, CJC8910_I2S_FMT_I2S);
ESP_LOGD(TAG, "Set sample_rate:%ld, channel:%d, bits_per_sample:%d", fs->sample_rate, fs->channel, fs->bits_per_sample);
return ret;
}
static int cjc8910_mute_output(const audio_codec_if_t *h, bool mute)
{
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
int ret = ESP_CODEC_DEV_OK;
int regv = 0;
ret |= cjc8910_read_reg(codec, CJC8910_R5_ADC_DAC_CONTROL, &regv);
if (mute) {
regv |= (1 << 3);
} else {
regv &= ~(1 << 3);
}
ret |= cjc8910_write_reg(codec, CJC8910_R5_ADC_DAC_CONTROL, regv);
ret |= cjc8910_read_reg(codec, CJC8910_R5_ADC_DAC_CONTROL, &regv);
ESP_LOGD(TAG, "Set cjc8910 output mute: %d", mute);
return ret;
}
static int cjc8910_close(const audio_codec_if_t *h)
{
int ret = ESP_CODEC_DEV_OK;
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
cjc8910_pa_power(codec, PA_DISABLE);
ret |= cjc8910_write_reg(codec, CJC8910_R15_RESET, 0x00);
codec->is_open = false;
return ret;
}
static int cjc8910_enable(const audio_codec_if_t *h, bool enable)
{
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (codec->enabled == enable) {
return ESP_CODEC_DEV_OK;
}
if (enable) {
cjc8910_pa_power(codec, PA_ENABLE);
cjc8910_mute_output(h, false);
} else {
cjc8910_pa_power(codec, PA_DISABLE);
cjc8910_mute_output(h, true);
}
codec->enabled = enable;
return ESP_CODEC_DEV_OK;
}
static int cjc8910_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return cjc8910_write_reg(codec, reg, value);
}
static int cjc8910_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return cjc8910_read_reg(codec, reg, value);
}
const audio_codec_if_t *cjc8910_codec_new(cjc8910_codec_cfg_t *codec_cfg)
{
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Invalid parameters,cfg: %p, ctrl_if: %p", codec_cfg, codec_cfg == NULL ? NULL : codec_cfg->ctrl_if);
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_cjc8910_t *codec = (audio_codec_cjc8910_t *)calloc(1, sizeof(audio_codec_cjc8910_t));
if (codec == NULL) {
CODEC_MEM_CHECK(codec);
return NULL;
}
codec->base.open = cjc8910_open;
codec->base.close = cjc8910_close;
codec->base.enable = cjc8910_enable;
codec->base.set_fs = cjc8910_set_fs;
codec->base.set_vol = cjc8910_set_vol;
codec->base.set_mic_gain = cjc8910_set_mic_gain;
codec->base.mute = cjc8910_mute_output;
codec->base.set_reg = cjc8910_set_reg;
codec->base.get_reg = cjc8910_get_reg;
codec->base.dump_reg = cjc8910_dump;
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(cjc8910_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail, ret: %d", ret);
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,86 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _CJC8910_REG_H_
#define _CJC8910_REG_H_
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @brief CJC8910_REGISTER NAME_REG_REGISTER ADDRESS
*/
/**
* @brief RESET
*/
#define CJC8910_R15_RESET 0x1E /* reset digital, analog, etc. */
/**
* @brief Clock Scheme Register definition
*/
#define CJC8910_R8_SAMPLE_RATE 0x10 /* clock and sample rate control */
/**
* @brief SDP - Serial Digital Port
*/
#define CJC8910_R7_AUDIO_INTERFACE 0x0E /* digital audio interface format */
/**
* @brief SYSTEM - System Control and Power Management
*/
#define CJC8910_R5_ADC_DAC_CONTROL 0x0A /* ADC and DAC control */
#define CJC8910_R23_ADDITIONAL_CONTROL1 0x2E /* Addtional control 1 */
#define CJC8910_R24_ADDITIONAL_CONTROL2 0x30 /* Addtional control 2 */
#define CJC8910_R27_ADDITIONAL_CONTROL3 0x36 /* Addtional control 3 */
#define CJC8910_R25_PWR_MGMT1_L 0x32 /* Power management1 and VMIDSEL (low byte) */
#define CJC8910_R25_PWR_MGMT1_H 0x33 /* Power management1 and VMIDSEL (high byte) */
#define CJC8910_R26_PWR_MGMT2_L 0x34 /* Power management2 and DAC left power down */
#define CJC8910_R26_PWR_MGMT2_H 0x35 /* Power management2 and DAC right power up */
#define CJC8910_R37_ADC_PDN 0x4A /* ADC power down control */
#define CJC8910_R67_LOW_POWER_PLAYBACK 0x86 /* Low power playback mode */
/**
* @brief ADC - Analog to Digital Converter
*/
#define CJC8910_R0_LEFT_INPUT_VOLUME 0x01 /* Audio input left channel volume */
#define CJC8910_R21_LEFT_ADC_VOLUME 0x2B /* Left ADC digital volume */
#define CJC8910_R32_ADCL_SIGNAL_PATH 0x41 /* ADC signal path control */
#define CJC8910_R33_MIC 0x42 /* Microphone control */
#define CJC8910_R34_AUX 0x44 /* Auxiliary input control */
/**
* @brief ALC - Automatic Level Control
*/
#define CJC8910_R17_ALC1_CONTROL 0x22 /* ALC control 1 */
#define CJC8910_R18_ALC2_CONTROL 0x24 /* ALC control 2 */
#define CJC8910_R19_ALC3_CONTROL 0x26 /* ALC control 3 */
#define CJC8910_R20_NOISE_GATE_CONTROL 0x28 /* Noise gate control */
/**
* @brief DAC - Digital to Analog Converter
*/
#define CJC8910_R2_LOUT1_VOLUME 0x05 /* Audio output left channel1 volume */
#define CJC8910_R10_LEFT_DAC_VOLUME 0x15 /* Left channel DAC digital volume */
#define CJC8910_R35_LEFT_OUT_MIX2_L 0x46 /* Left out mixer 2 (low byte) */
#define CJC8910_R35_LEFT_OUT_MIX2_H 0x47 /* Left out mixer 2 (high byte) */
/**
* @brief EQ - Equalizer Control
*/
#define CJC8910_R12_BASS_CONTROL 0x18 /* Bass control */
#define CJC8910_R13_TREBLE_CONTROL 0x1A /* Treble control */
/**
* @brief CJC8910 maximum register address
*/
#define CJC8910_MAX_REGISTER 0x55
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif // _CJC8910_REG_H_

View File

@@ -0,0 +1,621 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#include "es7210_adc.h"
#include "es7210_reg.h"
#include "es_common.h"
#include "esp_codec_dev_defaults.h"
#include "esp_codec_dev_vol.h"
#define I2S_DSP_MODE 0
#define ENABLE_TDM_MAX_NUM 3
#define TAG "ES7210"
typedef struct {
audio_codec_if_t base;
const audio_codec_ctrl_if_t *ctrl_if;
bool is_open;
bool enabled;
es7210_input_mics_t mic_select;
es7210_gain_value_t gain;
bool master_mode;
uint8_t off_reg;
uint16_t mclk_div;
} audio_codec_es7210_t;
/*
* Clock coefficient structure
*/
struct _coeff_div {
uint32_t mclk; /* mclk frequency */
uint32_t lrck; /* lrck */
uint8_t ss_ds;
uint8_t adc_div; /* adcclk divider */
uint8_t dll; /* dll_bypass */
uint8_t doubler; /* doubler enable */
uint8_t osr; /* adc osr */
uint8_t mclk_src; /* select mclk source */
uint32_t lrck_h; /* The high 4 bits of lrck */
uint32_t lrck_l; /* The low 8 bits of lrck */
};
/* Codec hifi mclk clock divider coefficients
* MEMBER REG
* mclk: 0x03
* lrck: standard
* ss_ds: --
* adc_div: 0x02
* dll: 0x06
* doubler: 0x02
* osr: 0x07
* mclk_src: 0x03
* lrckh: 0x04
* lrckl: 0x05
*/
static const struct _coeff_div coeff_div[] = {
// mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl
/* 8k */
{12288000, 8000, 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00},
{16384000, 8000, 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00},
{19200000, 8000, 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60},
{4096000, 8000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
/* 11.025k */
{11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
/* 12k */
{12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
{19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40},
/* 16k */
{4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
{19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80},
{16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
{12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00},
/* 22.05k */
{11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
/* 24k */
{12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
{19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20},
/* 32k */
{8192000, 32000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
{12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80},
{16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
{19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58},
/* 44.1k */
{11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
/* 48k */
{12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
{19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90},
/* 64k */
{16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
{19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c},
/* 88.2k */
{11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
/* 96k */
{12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
{19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8},
};
static int es7210_write_reg(audio_codec_es7210_t *codec, int reg, int value)
{
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
}
static int es7210_read_reg(audio_codec_es7210_t *codec, int reg, int *value)
{
*value = 0;
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, value, 1);
}
static int es7210_update_reg_bit(audio_codec_es7210_t *codec, uint8_t reg_addr, uint8_t update_bits, uint8_t data)
{
int regv = 0;
es7210_read_reg(codec, reg_addr, &regv);
regv = (regv & (~update_bits)) | (update_bits & data);
return es7210_write_reg(codec, reg_addr, regv);
}
static int get_coeff(uint32_t mclk, uint32_t lrck)
{
for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) {
if (coeff_div[i].lrck == lrck && coeff_div[i].mclk == mclk)
return i;
}
return -1;
}
static int es7210_config_sample(audio_codec_es7210_t *codec, int sample_fre)
{
if (codec->master_mode == false) {
return ESP_CODEC_DEV_OK;
}
int regv;
int coeff;
int mclk_fre = 0;
int ret = 0;
mclk_fre = sample_fre * codec->mclk_div;
coeff = get_coeff(mclk_fre, sample_fre);
if (coeff < 0) {
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre);
return ESP_FAIL;
}
/* Set clock parameters */
if (coeff >= 0) {
/* Set adc_div & doubler & dll */
ret |= es7210_read_reg(codec, ES7210_MAINCLK_REG02, &regv);
regv &= 0x00;
regv |= coeff_div[coeff].adc_div;
regv |= coeff_div[coeff].doubler << 6;
regv |= coeff_div[coeff].dll << 7;
ret |= es7210_write_reg(codec, ES7210_MAINCLK_REG02, regv);
/* Set osr */
regv = coeff_div[coeff].osr;
ret |= es7210_write_reg(codec, ES7210_OSR_REG07, regv);
/* Set lrck */
regv = coeff_div[coeff].lrck_h;
ret |= es7210_write_reg(codec, ES7210_LRCK_DIVH_REG04, regv);
regv = coeff_div[coeff].lrck_l;
ret |= es7210_write_reg(codec, ES7210_LRCK_DIVL_REG05, regv);
}
return ret;
}
static bool es7210_is_tdm_mode(audio_codec_es7210_t *codec)
{
uint16_t mic_num = 0;
for (int i = ES7210_INPUT_MIC1; i <= ES7210_INPUT_MIC4; i = i << 1) {
if (codec->mic_select & i) {
mic_num++;
}
}
return (mic_num >= ENABLE_TDM_MAX_NUM);
}
static int es7210_mic_select(audio_codec_es7210_t *codec, es7210_input_mics_t mic)
{
int ret = 0;
if (codec->mic_select & (ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4)) {
for (int i = 0; i < 4; i++) {
ret |= es7210_update_reg_bit(codec, ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00);
}
ret |= es7210_write_reg(codec, ES7210_MIC12_POWER_REG4B, 0xff);
ret |= es7210_write_reg(codec, ES7210_MIC34_POWER_REG4C, 0xff);
if (codec->mic_select & ES7210_INPUT_MIC1) {
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC1");
ret |= es7210_update_reg_bit(codec, ES7210_CLOCK_OFF_REG01, 0x0b, 0x00);
ret |= es7210_write_reg(codec, ES7210_MIC12_POWER_REG4B, 0x00);
ret |= es7210_update_reg_bit(codec, ES7210_MIC1_GAIN_REG43, 0x10, 0x10);
ret |= es7210_update_reg_bit(codec, ES7210_MIC1_GAIN_REG43, 0x0f, codec->gain);
}
if (codec->mic_select & ES7210_INPUT_MIC2) {
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC2");
ret |= es7210_update_reg_bit(codec, ES7210_CLOCK_OFF_REG01, 0x0b, 0x00);
ret |= es7210_write_reg(codec, ES7210_MIC12_POWER_REG4B, 0x00);
ret |= es7210_update_reg_bit(codec, ES7210_MIC2_GAIN_REG44, 0x10, 0x10);
ret |= es7210_update_reg_bit(codec, ES7210_MIC2_GAIN_REG44, 0x0f, codec->gain);
}
if (codec->mic_select & ES7210_INPUT_MIC3) {
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC3");
ret |= es7210_update_reg_bit(codec, ES7210_CLOCK_OFF_REG01, 0x15, 0x00);
ret |= es7210_write_reg(codec, ES7210_MIC34_POWER_REG4C, 0x00);
ret |= es7210_update_reg_bit(codec, ES7210_MIC3_GAIN_REG45, 0x10, 0x10);
ret |= es7210_update_reg_bit(codec, ES7210_MIC3_GAIN_REG45, 0x0f, codec->gain);
}
if (codec->mic_select & ES7210_INPUT_MIC4) {
ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC4");
ret |= es7210_update_reg_bit(codec, ES7210_CLOCK_OFF_REG01, 0x15, 0x00);
ret |= es7210_write_reg(codec, ES7210_MIC34_POWER_REG4C, 0x00);
ret |= es7210_update_reg_bit(codec, ES7210_MIC4_GAIN_REG46, 0x10, 0x10);
ret |= es7210_update_reg_bit(codec, ES7210_MIC4_GAIN_REG46, 0x0f, codec->gain);
}
} else {
ESP_LOGE(TAG, "Microphone selection error");
return ESP_FAIL;
}
if (es7210_is_tdm_mode(codec)) {
ret |= es7210_write_reg(codec, ES7210_SDP_INTERFACE2_REG12, 0x02);
ESP_LOGI(TAG, "Enable TDM mode");
} else {
ret |= es7210_write_reg(codec, ES7210_SDP_INTERFACE2_REG12, 0x00);
}
return ret;
}
static int es7210_config_fmt(audio_codec_es7210_t *codec, es_i2s_fmt_t fmt)
{
int ret = 0;
int adc_iface = 0;
ret = es7210_read_reg(codec, ES7210_SDP_INTERFACE1_REG11, &adc_iface);
adc_iface &= 0xfc;
switch (fmt) {
case ES_I2S_NORMAL:
ESP_LOGD(TAG, "ES7210 in I2S Format");
adc_iface |= 0x00;
break;
case ES_I2S_LEFT:
case ES_I2S_RIGHT:
ESP_LOGD(TAG, "ES7210 in LJ Format");
adc_iface |= 0x01;
break;
case ES_I2S_DSP:
if (I2S_DSP_MODE) {
ESP_LOGD(TAG, "ES7210 in DSP-A Format");
adc_iface |= 0x13;
} else {
ESP_LOGD(TAG, "ES7210 in DSP-B Format");
adc_iface |= 0x03;
}
break;
default:
adc_iface &= 0xfc;
break;
}
ret |= es7210_write_reg(codec, ES7210_SDP_INTERFACE1_REG11, adc_iface);
return ret;
}
static int es7210_set_bits(audio_codec_es7210_t *codec, uint8_t bits)
{
int ret = 0;
int adc_iface = 0;
ret = es7210_read_reg(codec, ES7210_SDP_INTERFACE1_REG11, &adc_iface);
adc_iface &= 0x1f;
switch (bits) {
case 16:
adc_iface |= 0x60;
break;
case 24:
adc_iface |= 0x00;
break;
case 32:
adc_iface |= 0x80;
break;
default:
adc_iface |= 0x60;
break;
}
ret |= es7210_write_reg(codec, ES7210_SDP_INTERFACE1_REG11, adc_iface);
ESP_LOGI(TAG, "Bits %d", bits);
return ret;
}
static int es7210_start(audio_codec_es7210_t *codec, uint8_t clock_reg_value)
{
int ret = 0;
ret |= es7210_write_reg(codec, ES7210_CLOCK_OFF_REG01, clock_reg_value);
ret |= es7210_write_reg(codec, ES7210_POWER_DOWN_REG06, 0x00);
ret |= es7210_write_reg(codec, ES7210_ANALOG_REG40, 0x43);
ret |= es7210_write_reg(codec, ES7210_MIC1_POWER_REG47, 0x08);
ret |= es7210_write_reg(codec, ES7210_MIC2_POWER_REG48, 0x08);
ret |= es7210_write_reg(codec, ES7210_MIC3_POWER_REG49, 0x08);
ret |= es7210_write_reg(codec, ES7210_MIC4_POWER_REG4A, 0x08);
ret |= es7210_mic_select(codec, codec->mic_select);
ret |= es7210_write_reg(codec, ES7210_ANALOG_REG40, 0x43);
ret |= es7210_write_reg(codec, ES7210_RESET_REG00, 0x71);
ret |= es7210_write_reg(codec, ES7210_RESET_REG00, 0x41);
return ret;
}
static int es7210_stop(audio_codec_es7210_t *codec)
{
int ret = 0;
ret |= es7210_write_reg(codec, ES7210_MIC1_POWER_REG47, 0xff);
ret |= es7210_write_reg(codec, ES7210_MIC2_POWER_REG48, 0xff);
ret |= es7210_write_reg(codec, ES7210_MIC3_POWER_REG49, 0xff);
ret |= es7210_write_reg(codec, ES7210_MIC4_POWER_REG4A, 0xff);
ret |= es7210_write_reg(codec, ES7210_MIC12_POWER_REG4B, 0xff);
ret |= es7210_write_reg(codec, ES7210_MIC34_POWER_REG4C, 0xff);
ret |= es7210_write_reg(codec, ES7210_ANALOG_REG40, 0xc0);
ret |= es7210_write_reg(codec, ES7210_CLOCK_OFF_REG01, 0x7f);
ret |= es7210_write_reg(codec, ES7210_POWER_DOWN_REG06, 0x07);
return ret;
}
static es7210_gain_value_t get_db(float db)
{
db += 0.5;
if (db < 33) {
int idx = db < 3 ? 0 : db / 3;
return GAIN_0DB + idx;
}
if (db < 34.5) {
return GAIN_30DB;
}
if (db < 36) {
return GAIN_34_5DB;
}
if (db < 37) {
return GAIN_36DB;
}
return GAIN_37_5DB;
}
static int _es7210_set_channel_gain(audio_codec_es7210_t *codec, uint16_t channel_mask, float db)
{
int ret = 0;
es7210_gain_value_t gain = get_db(db);
if ((codec->mic_select & ES7210_INPUT_MIC1) & (channel_mask & ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0))) {
ret |= es7210_update_reg_bit(codec, ES7210_MIC1_GAIN_REG43, 0x0f, gain);
}
if ((codec->mic_select & ES7210_INPUT_MIC2) & (channel_mask & ESP_CODEC_DEV_MAKE_CHANNEL_MASK(1))) {
ret |= es7210_update_reg_bit(codec, ES7210_MIC2_GAIN_REG44, 0x0f, gain);
}
if ((codec->mic_select & ES7210_INPUT_MIC3) & (channel_mask & ESP_CODEC_DEV_MAKE_CHANNEL_MASK(2))) {
ret |= es7210_update_reg_bit(codec, ES7210_MIC3_GAIN_REG45, 0x0f, gain);
}
if ((codec->mic_select & ES7210_INPUT_MIC4) & (channel_mask & ESP_CODEC_DEV_MAKE_CHANNEL_MASK(3))) {
ret |= es7210_update_reg_bit(codec, ES7210_MIC4_GAIN_REG46, 0x0f, gain);
}
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static int _es7210_set_mute(audio_codec_es7210_t *codec, bool mute)
{
int ret = 0;
if (mute) {
ret |= es7210_update_reg_bit(codec, 0x14, 0x03, 0x03);
ret |= es7210_update_reg_bit(codec, 0x15, 0x03, 0x03);
} else {
ret |= es7210_update_reg_bit(codec, 0x14, 0x03, 0x00);
ret |= es7210_update_reg_bit(codec, 0x15, 0x03, 0x00);
}
ESP_LOGI(TAG, "%s", mute ? "Muted" : "Unmuted");
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static int es7210_enable(const audio_codec_if_t *h, bool enable)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (enable == codec->enabled) {
return ESP_CODEC_DEV_OK;
}
int ret = 0;
if (enable) {
ret |= es7210_start(codec, codec->off_reg);
} else {
ret |= es7210_stop(codec);
}
if (ret == ESP_CODEC_DEV_OK) {
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
codec->enabled = enable;
}
return ret;
}
static int es7210_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
es7210_codec_cfg_t *codec_cfg = (es7210_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es7210_codec_cfg_t)) {
ESP_LOGE(TAG, "Wrong codec config");
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = 0;
codec->ctrl_if = codec_cfg->ctrl_if;
ret |= es7210_write_reg(codec, ES7210_RESET_REG00, 0xff);
ret |= es7210_write_reg(codec, ES7210_RESET_REG00, 0x41);
ret |= es7210_write_reg(codec, ES7210_CLOCK_OFF_REG01, 0x3f);
ret |= es7210_write_reg(codec, ES7210_TIME_CONTROL0_REG09, 0x30); /* Set chip state cycle */
ret |= es7210_write_reg(codec, ES7210_TIME_CONTROL1_REG0A, 0x30); /* Set power on state cycle */
ret |= es7210_write_reg(codec, ES7210_ADC12_HPF2_REG23, 0x2a); /* Quick setup */
ret |= es7210_write_reg(codec, ES7210_ADC12_HPF1_REG22, 0x0a);
ret |= es7210_write_reg(codec, ES7210_ADC34_HPF2_REG20, 0x0a);
ret |= es7210_write_reg(codec, ES7210_ADC34_HPF1_REG21, 0x2a);
if (ret != 0) {
ESP_LOGE(TAG, "Write register fail");
return ESP_CODEC_DEV_WRITE_FAIL;
}
if (codec_cfg->master_mode) {
ESP_LOGI(TAG, "Work in Master mode");
ret |= es7210_update_reg_bit(codec, ES7210_MODE_CONFIG_REG08, 0x01, 0x01);
/* Select clock source for internal mclk */
switch (codec_cfg->mclk_src) {
case ES7210_MCLK_FROM_PAD:
default:
ret |= es7210_update_reg_bit(codec, ES7210_MASTER_CLK_REG03, 0x80, 0x00);
break;
case ES7210_MCLK_FROM_CLOCK_DOUBLER:
ret |= es7210_update_reg_bit(codec, ES7210_MASTER_CLK_REG03, 0x80, 0x80);
break;
}
} else {
ESP_LOGI(TAG, "Work in Slave mode");
ret |= es7210_update_reg_bit(codec, ES7210_MODE_CONFIG_REG08, 0x01, 0x00);
}
/* Select power off analog, vdda = 3.3V, close vx20ff, VMID select 5KΩ start */
ret |= es7210_write_reg(codec, ES7210_ANALOG_REG40, 0x43);
ret |= es7210_write_reg(codec, ES7210_MIC12_BIAS_REG41, 0x70); /* Select 2.87v */
ret |= es7210_write_reg(codec, ES7210_MIC34_BIAS_REG42, 0x70); /* Select 2.87v */
ret |= es7210_write_reg(codec, ES7210_OSR_REG07, 0x20);
/* Set the frequency division coefficient and use dll except clock doubler, and need to set 0xc1 to clear the state */
ret |= es7210_write_reg(codec, ES7210_MAINCLK_REG02, 0xc1);
codec->mic_select = (es7210_input_mics_t) codec_cfg->mic_selected;
if (codec->mic_select == 0) {
codec->mic_select = ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2;
}
ret |= es7210_mic_select(codec, codec->mic_select);
ret |= _es7210_set_channel_gain(codec, 0xF, 30.0);
if (ret != 0) {
return ESP_CODEC_DEV_WRITE_FAIL;
}
codec->master_mode = codec_cfg->master_mode;
codec->mclk_div = codec_cfg->mclk_div;
if (codec->mclk_div == 0) {
codec->mclk_div = MCLK_DEFAULT_DIV;
}
es7210_read_reg(codec, ES7210_CLOCK_OFF_REG01, &ret);
if (ret >= 0) {
codec->off_reg = (uint8_t) ret;
}
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int es7210_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
int ret = 0;
uint8_t bits = fs->bits_per_sample;
// Use 2 channel to fetch TDM data
if (es7210_is_tdm_mode(codec) && fs->channel <= 2 && fs->channel_mask == 0) {
bits >>= 1;
}
ret |= es7210_set_bits(codec, bits);
ret |= es7210_config_sample(codec, fs->sample_rate);
ret |= es7210_config_fmt(codec, ES_I2S_NORMAL);
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static int es7210_set_gain(const audio_codec_if_t *h, float db)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return _es7210_set_channel_gain(codec, 0xF, db);
}
static int es7210_set_channel_gain(const audio_codec_if_t *h, uint16_t channel_mask, float db)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return _es7210_set_channel_gain(codec, channel_mask, db);
}
static int es7210_set_mute(const audio_codec_if_t *h, bool mute)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return _es7210_set_mute(codec, mute);
}
static int es7210_close(const audio_codec_if_t *h)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = 0;
if (codec->is_open) {
ret = es7210_enable(h, false);
codec->is_open = false;
}
return ret;
}
static int es7210_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es7210_write_reg(codec, reg, value);
}
static int es7210_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es7210_read_reg(codec, reg, value);
}
static void es7210_dump(const audio_codec_if_t *h)
{
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) h;
if (codec == NULL) {
return;
}
for (int i = 0; i <= 0x4E; i++) {
int reg = 0;
if (es7210_read_reg(codec, i, &reg) != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, reg);
}
}
const audio_codec_if_t *es7210_codec_new(es7210_codec_cfg_t *codec_cfg)
{
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_es7210_t *codec = (audio_codec_es7210_t *) calloc(1, sizeof(audio_codec_es7210_t));
CODEC_MEM_CHECK(codec);
if (codec == NULL) {
return NULL;
}
codec->base.open = es7210_open;
codec->base.enable = es7210_enable;
codec->base.set_fs = es7210_set_fs;
codec->base.set_mic_gain = es7210_set_gain;
codec->base.set_mic_channel_gain = es7210_set_channel_gain;
codec->base.mute_mic = es7210_set_mute;
codec->base.set_reg = es7210_set_reg;
codec->base.get_reg = es7210_get_reg;
codec->base.dump_reg = es7210_dump;
codec->base.close = es7210_close;
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es7210_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,82 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES7210_REG_H
#define _ES7210_REG_H
#ifdef __cplusplus
extern "C" {
#endif
#define ES7210_RESET_REG00 0x00 /* Reset control */
#define ES7210_CLOCK_OFF_REG01 0x01 /* Used to turn off the ADC clock */
#define ES7210_MAINCLK_REG02 0x02 /* Set ADC clock frequency division */
#define ES7210_MASTER_CLK_REG03 0x03 /* MCLK source $ SCLK division */
#define ES7210_LRCK_DIVH_REG04 0x04 /* lrck_divh */
#define ES7210_LRCK_DIVL_REG05 0x05 /* lrck_divl */
#define ES7210_POWER_DOWN_REG06 0x06 /* power down */
#define ES7210_OSR_REG07 0x07
#define ES7210_MODE_CONFIG_REG08 0x08 /* Set master/slave & channels */
#define ES7210_TIME_CONTROL0_REG09 0x09 /* Set Chip intial state period*/
#define ES7210_TIME_CONTROL1_REG0A 0x0A /* Set Power up state period */
#define ES7210_SDP_INTERFACE1_REG11 0x11 /* Set sample & fmt */
#define ES7210_SDP_INTERFACE2_REG12 0x12 /* Pins state */
#define ES7210_ADC_AUTOMUTE_REG13 0x13 /* Set mute */
#define ES7210_ADC34_MUTERANGE_REG14 0x14 /* Set mute range */
#define ES7210_ADC34_HPF2_REG20 0x20 /* HPF */
#define ES7210_ADC34_HPF1_REG21 0x21
#define ES7210_ADC12_HPF1_REG22 0x22
#define ES7210_ADC12_HPF2_REG23 0x23
#define ES7210_ANALOG_REG40 0x40 /* ANALOG Power */
#define ES7210_MIC12_BIAS_REG41 0x41
#define ES7210_MIC34_BIAS_REG42 0x42
#define ES7210_MIC1_GAIN_REG43 0x43
#define ES7210_MIC2_GAIN_REG44 0x44
#define ES7210_MIC3_GAIN_REG45 0x45
#define ES7210_MIC4_GAIN_REG46 0x46
#define ES7210_MIC1_POWER_REG47 0x47
#define ES7210_MIC2_POWER_REG48 0x48
#define ES7210_MIC3_POWER_REG49 0x49
#define ES7210_MIC4_POWER_REG4A 0x4A
#define ES7210_MIC12_POWER_REG4B 0x4B /* MICBias & ADC & PGA Power */
#define ES7210_MIC34_POWER_REG4C 0x4C
typedef enum {
ES7210_AD1_AD0_00 = 0x80,
ES7210_AD1_AD0_01 = 0x82,
ES7210_AD1_AD0_10 = 0x84,
ES7210_AD1_AD0_11 = 0x86
} es7210_address_t;
typedef enum {
ES7210_INPUT_MIC1 = 0x01,
ES7210_INPUT_MIC2 = 0x02,
ES7210_INPUT_MIC3 = 0x04,
ES7210_INPUT_MIC4 = 0x08
} es7210_input_mics_t;
typedef enum gain_value {
GAIN_0DB = 0,
GAIN_3DB,
GAIN_6DB,
GAIN_9DB,
GAIN_12DB,
GAIN_15DB,
GAIN_18DB,
GAIN_21DB,
GAIN_24DB,
GAIN_27DB,
GAIN_30DB,
GAIN_33DB,
GAIN_34_5DB,
GAIN_36DB,
GAIN_37_5DB,
} es7210_gain_value_t;
#ifdef __cplusplus
}
#endif
#endif /* _ES7210_H_ */

View File

@@ -0,0 +1,232 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "es7243_adc.h"
#include "esp_log.h"
#include "es_common.h"
#include "esp_codec_dev_os.h"
#define MCLK_PULSES_NUMBER (20)
#define TAG "ES7243"
typedef struct {
audio_codec_if_t base;
const audio_codec_ctrl_if_t *ctrl_if;
bool is_open;
bool enabled;
} audio_codec_es7243_t;
static int es7243_write_reg(audio_codec_es7243_t *codec, int reg, int value)
{
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
}
static int es7243_adc_set_voice_mute(audio_codec_es7243_t *codec, bool mute)
{
ESP_LOGI(TAG, "mute = %d", mute);
if (mute) {
es7243_write_reg(codec, 0x05, 0x1B);
} else {
es7243_write_reg(codec, 0x05, 0x13);
}
return 0;
}
static int es7243_adc_set_gain(audio_codec_es7243_t *codec, float db)
{
int ret = 0;
if (db <= 1) {
ret = es7243_write_reg(codec, 0x08, 0x11); // 1db
} else if (db <= 4) {
ret = es7243_write_reg(codec, 0x08, 0x13); // 3.5db
} else if (db <= 18) {
ret = es7243_write_reg(codec, 0x08, 0x21); // 18db
} else if (db < 21) {
ret = es7243_write_reg(codec, 0x08, 0x23); // 20.5db
} else if (db < 23) {
ret = es7243_write_reg(codec, 0x08, 0x06); // 22.5db
} else if (db < 25) {
ret = es7243_write_reg(codec, 0x08, 0x41); // 24.5db
} else {
ret = es7243_write_reg(codec, 0x08, 0x43); // 27db
}
ESP_LOGI(TAG, "Set DB %f", db);
return ret;
}
static int es7243_adc_enable(audio_codec_es7243_t *codec, bool enable)
{
int ret = ESP_CODEC_DEV_OK;
if (enable) {
// slave mode only
ret |= es7243_write_reg(codec, 0x00, 0x01);
ret |= es7243_write_reg(codec, 0x06, 0x00);
ret |= es7243_write_reg(codec, 0x05, 0x1B);
ret |= es7243_write_reg(codec, 0x01, 0x0C);
ret |= es7243_write_reg(codec, 0x08, 0x43);
ret |= es7243_write_reg(codec, 0x05, 0x13);
} else {
ret |= es7243_write_reg(codec, 0x06, 0x05);
ret |= es7243_write_reg(codec, 0x05, 0x1B);
ret |= es7243_write_reg(codec, 0x06, 0x5C);
ret |= es7243_write_reg(codec, 0x07, 0x3F);
ret |= es7243_write_reg(codec, 0x08, 0x4B);
ret |= es7243_write_reg(codec, 0x09, 0x9F);
}
ESP_LOGI(TAG, "Set enable %d", enable);
return ret;
}
static int es7243_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
es7243_codec_cfg_t *codec_cfg = (es7243_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es7243_codec_cfg_t)) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (es7243_adc_enable(codec, true)) {
ESP_LOGE(TAG, "Fail to write register");
return ESP_CODEC_DEV_WRITE_FAIL;
}
codec->enabled = true;
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int es7243_mute(const audio_codec_if_t *h, bool mute)
{
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es7243_adc_set_voice_mute(codec, mute);
}
static int es7243_enable(const audio_codec_if_t *h, bool enable)
{
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (codec->enabled == enable) {
return ESP_CODEC_DEV_OK;
}
int ret = es7243_adc_enable(codec, enable);
if (ret == ESP_CODEC_DEV_OK) {
codec->enabled = enable;
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
}
return ret;
}
static int es7243_set_gain(const audio_codec_if_t *h, float db)
{
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es7243_adc_set_gain(codec, db);
}
static int es7243_close(const audio_codec_if_t *h)
{
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open) {
codec->is_open = false;
}
return ESP_CODEC_DEV_OK;
}
static int es7243_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es7243_write_reg(codec, reg, value);
}
static int es7243_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, &value, 1);
}
static void es7243_dump(const audio_codec_if_t *h)
{
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i < 10; i++) {
int value = 0;
int ret = codec->ctrl_if->read_reg(codec->ctrl_if, i, 1, &value, 1);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, value);
}
}
const audio_codec_if_t *es7243_codec_new(es7243_codec_cfg_t *codec_cfg)
{
// verify param
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_es7243_t *codec = (audio_codec_es7243_t *) calloc(1, sizeof(audio_codec_es7243_t));
if (codec == NULL) {
CODEC_MEM_CHECK(codec);
return NULL;
}
codec->ctrl_if = codec_cfg->ctrl_if;
codec->base.open = es7243_open;
codec->base.enable = es7243_enable;
codec->base.set_mic_gain = es7243_set_gain;
codec->base.mute_mic = es7243_mute;
codec->base.set_reg = es7243_set_reg;
codec->base.get_reg = es7243_get_reg;
codec->base.dump_reg = es7243_dump;
codec->base.close = es7243_close;
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es7243_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Fail to open");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,276 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "es7243e_adc.h"
#include "esp_log.h"
#include "es_common.h"
#include "esp_codec_dev_os.h"
#define TAG "ES7243E"
typedef struct {
audio_codec_if_t base;
const audio_codec_ctrl_if_t *ctrl_if;
bool is_open;
bool enabled;
} audio_codec_es7243e_t;
static uint8_t get_db_reg(float db)
{
// reg: 12 - 34.5dB, 13 - 36dB, 14 - 37.5dB
db += 0.5;
if (db <= 33.0) {
return (uint8_t) db / 3;
}
if (db < 36.0) {
return 12;
}
if (db < 37.0) {
return 13;
}
return 14;
}
static int es7243e_write_reg(audio_codec_es7243e_t *codec, int reg, int value)
{
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
}
int es7243e_adc_enable(audio_codec_es7243e_t *codec, bool enable)
{
int ret = ESP_CODEC_DEV_OK;
if (enable) {
// slave mode only
ret |= es7243e_write_reg(codec, 0xF9, 0x00);
ret |= es7243e_write_reg(codec, 0x04, 0x01);
ret |= es7243e_write_reg(codec, 0x17, 0x01);
ret |= es7243e_write_reg(codec, 0x20, 0x10);
ret |= es7243e_write_reg(codec, 0x21, 0x10);
ret |= es7243e_write_reg(codec, 0x00, 0x80);
ret |= es7243e_write_reg(codec, 0x01, 0x3A);
ret |= es7243e_write_reg(codec, 0x16, 0x3F);
ret |= es7243e_write_reg(codec, 0x16, 0x00);
} else {
ret |= es7243e_write_reg(codec, 0x04, 0x02);
ret |= es7243e_write_reg(codec, 0x04, 0x01);
ret |= es7243e_write_reg(codec, 0xF7, 0x30);
ret |= es7243e_write_reg(codec, 0xF9, 0x01);
ret |= es7243e_write_reg(codec, 0x16, 0xFF);
ret |= es7243e_write_reg(codec, 0x17, 0x00);
ret |= es7243e_write_reg(codec, 0x01, 0x38);
ret |= es7243e_write_reg(codec, 0x20, 0x00);
ret |= es7243e_write_reg(codec, 0x21, 0x00);
ret |= es7243e_write_reg(codec, 0x00, 0x00);
ret |= es7243e_write_reg(codec, 0x00, 0x1E);
ret |= es7243e_write_reg(codec, 0x01, 0x30);
ret |= es7243e_write_reg(codec, 0x01, 0x00);
}
return ret;
}
static int es7243e_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
es7243e_codec_cfg_t *codec_cfg = (es7243e_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es7243e_codec_cfg_t)) {
return ESP_CODEC_DEV_INVALID_ARG;
}
codec->ctrl_if = codec_cfg->ctrl_if;
int ret = 0;
ret |= es7243e_write_reg(codec, 0x01, 0x3A);
ret |= es7243e_write_reg(codec, 0x00, 0x80);
ret |= es7243e_write_reg(codec, 0xF9, 0x00);
ret |= es7243e_write_reg(codec, 0x04, 0x02);
ret |= es7243e_write_reg(codec, 0x04, 0x01);
ret |= es7243e_write_reg(codec, 0xF9, 0x01);
ret |= es7243e_write_reg(codec, 0x00, 0x1E);
ret |= es7243e_write_reg(codec, 0x01, 0x00);
ret |= es7243e_write_reg(codec, 0x02, 0x00);
ret |= es7243e_write_reg(codec, 0x03, 0x20);
ret |= es7243e_write_reg(codec, 0x04, 0x01);
ret |= es7243e_write_reg(codec, 0x0D, 0x00);
ret |= es7243e_write_reg(codec, 0x05, 0x00);
ret |= es7243e_write_reg(codec, 0x06, 0x03); // SCLK=MCLK/4
ret |= es7243e_write_reg(codec, 0x07, 0x00); // LRCK=MCLK/256
ret |= es7243e_write_reg(codec, 0x08, 0xFF); // LRCK=MCLK/256
ret |= es7243e_write_reg(codec, 0x09, 0xCA);
ret |= es7243e_write_reg(codec, 0x0A, 0x85);
ret |= es7243e_write_reg(codec, 0x0B, 0x00);
ret |= es7243e_write_reg(codec, 0x0E, 0xBF);
ret |= es7243e_write_reg(codec, 0x0F, 0x80);
ret |= es7243e_write_reg(codec, 0x14, 0x0C);
ret |= es7243e_write_reg(codec, 0x15, 0x0C);
ret |= es7243e_write_reg(codec, 0x17, 0x02);
ret |= es7243e_write_reg(codec, 0x18, 0x26);
ret |= es7243e_write_reg(codec, 0x19, 0x77);
ret |= es7243e_write_reg(codec, 0x1A, 0xF4);
ret |= es7243e_write_reg(codec, 0x1B, 0x66);
ret |= es7243e_write_reg(codec, 0x1C, 0x44);
ret |= es7243e_write_reg(codec, 0x1E, 0x00);
ret |= es7243e_write_reg(codec, 0x1F, 0x0C);
ret |= es7243e_write_reg(codec, 0x20, 0x1A); // PGA gain +30dB
ret |= es7243e_write_reg(codec, 0x21, 0x1A);
ret |= es7243e_write_reg(codec, 0x00, 0x80); // Slave Mode
ret |= es7243e_write_reg(codec, 0x01, 0x3A);
ret |= es7243e_write_reg(codec, 0x16, 0x3F);
ret |= es7243e_write_reg(codec, 0x16, 0x00);
if (ret != 0 || es7243e_adc_enable(codec, true)) {
ESP_LOGI(TAG, "Fail to write register");
return ESP_CODEC_DEV_WRITE_FAIL;
}
codec->enabled = true;
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int es7243e_enable(const audio_codec_if_t *h, bool enable)
{
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (codec->enabled == enable) {
return ESP_CODEC_DEV_OK;
}
int ret = es7243e_adc_enable(codec, enable);
if (ret == ESP_CODEC_DEV_OK) {
codec->enabled = enable;
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
}
return ret;
}
static int es7243e_set_gain(const audio_codec_if_t *h, float db_value)
{
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
uint8_t reg = get_db_reg(db_value);
int ret = ESP_CODEC_DEV_OK;
ret |= es7243e_write_reg(codec, 0x20, 0x10 | reg);
ret |= es7243e_write_reg(codec, 0x21, 0x10 | reg);
return ret;
}
static int es7243e_set_mute(const audio_codec_if_t *h, bool mute)
{
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
int ret;
if (mute) {
ret = es7243e_write_reg(codec, 0x0B, 0xC0);
} else {
ret = es7243e_write_reg(codec, 0x0B, 0x00);
}
ESP_LOGD(TAG, "%s", mute ? "Muted" : "Unmuted");
return ret;
}
static int es7243e_close(const audio_codec_if_t *h)
{
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open) {
es7243e_adc_enable(codec, false);
codec->is_open = false;
}
return ESP_CODEC_DEV_OK;
}
static int es7243e_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es7243e_write_reg(codec, reg, value);
}
static int es7243e_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, &value, 1);
}
static void es7243e_dump(const audio_codec_if_t *h)
{
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i < 0xFF; i++) {
int value = 0;
int ret = codec->ctrl_if->read_reg(codec->ctrl_if, i, 1, &value, 1);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, value);
}
}
const audio_codec_if_t *es7243e_codec_new(es7243e_codec_cfg_t *codec_cfg)
{
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_es7243e_t *codec = (audio_codec_es7243e_t *) calloc(1, sizeof(audio_codec_es7243e_t));
if (codec == NULL) {
CODEC_MEM_CHECK(codec);
return NULL;
}
codec->base.open = es7243e_open;
codec->base.enable = es7243e_enable;
codec->base.mute_mic = es7243e_set_mute;
codec->base.set_mic_gain = es7243e_set_gain;
codec->base.set_reg = es7243e_set_reg;
codec->base.get_reg = es7243e_get_reg;
codec->base.dump_reg = es7243e_dump;
codec->base.close = es7243e_close;
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es7243e_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,294 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "es8156_dac.h"
#include "es8156_reg.h"
#include "esp_log.h"
#include "es_common.h"
#include "esp_codec_dev_os.h"
#define TAG "ES8156"
typedef struct {
audio_codec_if_t base;
const audio_codec_ctrl_if_t *ctrl_if;
const audio_codec_gpio_if_t *gpio_if;
int16_t pa_pin;
bool pa_reverted;
bool is_open;
bool enabled;
float hw_gain;
} audio_codec_es8156_t;
/* The volume register mapped to decibel table can get from codec data-sheet
Volume control register 0x14 description:
0x00 - '-95.5dB' ... 0xFF - '+32dB'
*/
static const esp_codec_dev_vol_range_t vol_range = {
.min_vol =
{
.vol = 0x0,
.db_value = -95.5,
},
.max_vol =
{
.vol = 0xFF,
.db_value = 32.0,
},
};
static int es8156_write_reg(audio_codec_es8156_t *codec, int reg, int value)
{
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
}
static int es8156_read_reg(audio_codec_es8156_t *codec, int reg, int *value)
{
*value = 0;
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, value, 1);
}
static int es8156_stop(audio_codec_es8156_t *codec)
{
int ret = 0;
ret = es8156_write_reg(codec, 0x14, 0x00);
ret |= es8156_write_reg(codec, 0x19, 0x02);
ret |= es8156_write_reg(codec, 0x21, 0x1F);
ret |= es8156_write_reg(codec, 0x22, 0x02);
ret |= es8156_write_reg(codec, 0x25, 0x21);
ret |= es8156_write_reg(codec, 0x25, 0xA1);
ret |= es8156_write_reg(codec, 0x18, 0x01);
ret |= es8156_write_reg(codec, 0x09, 0x02);
ret |= es8156_write_reg(codec, 0x09, 0x01);
ret |= es8156_write_reg(codec, 0x08, 0x00);
return ret;
}
static int es8156_start(audio_codec_es8156_t *codec)
{
int ret = 0;
ret |= es8156_write_reg(codec, 0x08, 0x3F);
ret |= es8156_write_reg(codec, 0x09, 0x00);
ret |= es8156_write_reg(codec, 0x18, 0x00);
ret |= es8156_write_reg(codec, 0x25, 0x20);
ret |= es8156_write_reg(codec, 0x22, 0x00);
ret |= es8156_write_reg(codec, 0x21, 0x3C);
ret |= es8156_write_reg(codec, 0x19, 0x20);
ret |= es8156_write_reg(codec, 0x14, 179);
return ret;
}
static int es8156_set_mute(const audio_codec_if_t *h, bool mute)
{
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
if (codec == NULL || codec->is_open == false) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int regv;
int ret = es8156_read_reg(codec, ES8156_DAC_MUTE_REG13, &regv);
if (ret < 0) {
return ESP_CODEC_DEV_READ_FAIL;
}
if (mute) {
regv = regv | BITS(1) | BITS(2);
} else {
regv = regv & (~(BITS(1) | BITS(2)));
}
return es8156_write_reg(codec, ES8156_DAC_MUTE_REG13, (uint8_t) regv);
}
static int es8156_set_vol(const audio_codec_if_t *h, float volume)
{
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
volume -= codec->hw_gain;
int reg = esp_codec_dev_vol_calc_reg(&vol_range, volume);
int ret = es8156_write_reg(codec, ES8156_VOLUME_CONTROL_REG14, reg);
ESP_LOGD(TAG, "Set volume reg:%x db:%f", reg, volume);
return (ret == 0) ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static void es8156_pa_power(audio_codec_es8156_t *codec, es_pa_setting_t pa_setting)
{
int16_t pa_pin = codec->pa_pin;
if (pa_pin == -1 || codec->gpio_if == NULL) {
return;
}
if (pa_setting & ES_PA_SETUP) {
codec->gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
}
if (pa_setting & ES_PA_ENABLE) {
codec->gpio_if->set(pa_pin, codec->pa_reverted ? false : true);
}
if (pa_setting & ES_PA_DISABLE) {
codec->gpio_if->set(pa_pin, codec->pa_reverted ? true : false);
}
}
static int es8156_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
es8156_codec_cfg_t *codec_cfg = (es8156_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg == NULL || cfg_size != sizeof(es8156_codec_cfg_t) || codec_cfg->ctrl_if == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = ESP_CODEC_DEV_OK;
codec->ctrl_if = codec_cfg->ctrl_if;
codec->gpio_if = codec_cfg->gpio_if;
codec->pa_pin = codec_cfg->pa_pin;
codec->pa_reverted = codec_cfg->pa_reverted;
ret |= es8156_write_reg(codec, 0x02, 0x04);
ret |= es8156_write_reg(codec, 0x20, 0x2A);
ret |= es8156_write_reg(codec, 0x21, 0x3C);
ret |= es8156_write_reg(codec, 0x22, 0x00);
ret |= es8156_write_reg(codec, 0x24, 0x07);
ret |= es8156_write_reg(codec, 0x23, 0x00);
ret |= es8156_write_reg(codec, 0x0A, 0x01);
ret |= es8156_write_reg(codec, 0x0B, 0x01);
ret |= es8156_write_reg(codec, 0x11, 0x00);
ret |= es8156_write_reg(codec, 0x14, 179); // volume 70%
ret |= es8156_write_reg(codec, 0x0D, 0x14);
ret |= es8156_write_reg(codec, 0x18, 0x00);
ret |= es8156_write_reg(codec, 0x08, 0x3F);
ret |= es8156_write_reg(codec, 0x00, 0x02);
ret |= es8156_write_reg(codec, 0x00, 0x03);
ret |= es8156_write_reg(codec, 0x25, 0x20);
if (ret != 0) {
return ESP_CODEC_DEV_WRITE_FAIL;
}
es8156_pa_power(codec, ES_PA_SETUP | ES_PA_ENABLE);
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int es8156_close(const audio_codec_if_t *h)
{
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open) {
es8156_stop(codec);
es8156_pa_power(codec, ES_PA_DISABLE);
codec->is_open = false;
}
return ESP_CODEC_DEV_OK;
}
static int es8156_enable(const audio_codec_if_t *h, bool enable)
{
int ret = ESP_CODEC_DEV_OK;
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (codec->enabled == enable) {
return ESP_CODEC_DEV_OK;
}
if (enable) {
ret = es8156_start(codec);
es8156_pa_power(codec, ES_PA_ENABLE);
} else {
es8156_pa_power(codec, ES_PA_DISABLE);
ret = es8156_stop(codec);
}
if (ret == ESP_CODEC_DEV_OK) {
codec->enabled = enable;
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
}
return ret;
}
static int es8156_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8156_write_reg(codec, reg, value);
}
static int es8156_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8156_read_reg(codec, reg, value);
}
static void es8156_dump(const audio_codec_if_t *h)
{
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i <= 0x25; i++) {
int value = 0;
int ret = es8156_read_reg(codec, i, &value);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, value);
}
}
const audio_codec_if_t *es8156_codec_new(es8156_codec_cfg_t *codec_cfg)
{
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_es8156_t *codec = (audio_codec_es8156_t *) calloc(1, sizeof(audio_codec_es8156_t));
if (codec == NULL) {
CODEC_MEM_CHECK(codec);
return NULL;
}
codec->ctrl_if = codec_cfg->ctrl_if;
codec->base.open = es8156_open;
codec->base.enable = es8156_enable;
codec->base.set_vol = es8156_set_vol;
codec->base.mute = es8156_set_mute;
codec->base.set_reg = es8156_set_reg;
codec->base.get_reg = es8156_get_reg;
codec->base.dump_reg = es8156_dump;
codec->base.close = es8156_close;
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es8156_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,75 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES8156_H
#define _ES8156_H
#ifdef __cplusplus
extern "C" {
#endif
/* ES8156 register space */
/*
* RESET Control
*/
#define ES8156_RESET_REG00 0x00
/*
* Clock Managerment
*/
#define ES8156_MAINCLOCK_CTL_REG01 0x01
#define ES8156_SCLK_MODE_REG02 0x02
#define ES8156_LRCLK_DIV_H_REG03 0x03
#define ES8156_LRCLK_DIV_L_REG04 0x04
#define ES8156_SCLK_DIV_REG05 0x05
#define ES8156_NFS_CONFIG_REG06 0x06
#define ES8156_MISC_CONTROL1_REG07 0x07
#define ES8156_CLOCK_ON_OFF_REG08 0x08
#define ES8156_MISC_CONTROL2_REG09 0x09
#define ES8156_TIME_CONTROL1_REG0A 0x0a
#define ES8156_TIME_CONTROL2_REG0B 0x0b
/*
* System Control
*/
#define ES8156_CHIP_STATUS_REG0C 0x0c
#define ES8156_P2S_CONTROL_REG0D 0x0d
#define ES8156_DAC_OSR_COUNTER_REG10 0x10
/*
* SDP Control
*/
#define ES8156_DAC_SDP_REG11 0x11
#define ES8156_AUTOMUTE_SET_REG12 0x12
#define ES8156_DAC_MUTE_REG13 0x13
#define ES8156_VOLUME_CONTROL_REG14 0x14
/*
* ALC Control
*/
#define ES8156_ALC_CONFIG1_REG15 0x15
#define ES8156_ALC_CONFIG2_REG16 0x16
#define ES8156_ALC_CONFIG3_REG17 0x17
#define ES8156_MISC_CONTROL3_REG18 0x18
#define ES8156_EQ_CONTROL1_REG19 0x19
#define ES8156_EQ_CONTROL2_REG1A 0x1a
/*
* Analog System Control
*/
#define ES8156_ANALOG_SYS1_REG20 0x20
#define ES8156_ANALOG_SYS2_REG21 0x21
#define ES8156_ANALOG_SYS3_REG22 0x22
#define ES8156_ANALOG_SYS4_REG23 0x23
#define ES8156_ANALOG_LP_REG24 0x24
#define ES8156_ANALOG_SYS5_REG25 0x25
/*
* Chip Information
*/
#define ES8156_I2C_PAGESEL_REGFC 0xFC
#define ES8156_CHIPID1_REGFD 0xFD
#define ES8156_CHIPID0_REGFE 0xFE
#define ES8156_CHIP_VERSION_REGFF 0xFF
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,693 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "es8311_codec.h"
#include "es8311_reg.h"
#include "esp_log.h"
#include "es_common.h"
#define TAG "ES8311"
typedef struct {
audio_codec_if_t base;
es8311_codec_cfg_t cfg;
bool is_open;
bool enabled;
float hw_gain;
} audio_codec_es8311_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 x1, x2, x4 and x8 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[] = {
// mclk rate pre_div mult adc_div dac_div fs_mode lrch lrcl bckdiv osr
/* 8k */
{12288000, 8000, 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{18432000, 8000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x20},
{16384000, 8000, 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{8192000, 8000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{6144000, 8000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{4096000, 8000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{3072000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{2048000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{1536000, 8000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{1024000, 8000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
/* 11.025k */
{11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{5644800, 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{2822400, 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{1411200, 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
/* 12k */
{12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{6144000, 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{3072000, 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{1536000, 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
/* 16k */
{12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x20},
{16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{8192000, 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{6144000, 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{4096000, 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{3072000, 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{2048000, 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{1536000, 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
{1024000, 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20},
/* 22.05k */
{11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800, 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2822400, 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1411200, 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 24k */
{12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000, 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000, 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000, 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 32k */
{12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10},
{16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{8192000, 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000, 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{4096000, 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000, 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2048000, 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000, 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
{1024000, 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 44.1k */
{11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800, 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2822400, 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1411200, 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 48k */
{12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000, 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000, 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000, 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
/* 64k */
{12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
{16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{8192000, 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000, 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
{4096000, 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000, 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10},
{2048000, 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18},
{1024000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
/* 88.2k */
{11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{5644800, 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{2822400, 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1411200, 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
/* 96k */
{24576000, 96000, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{6144000, 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{3072000, 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10},
{1536000, 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10},
};
static const esp_codec_dev_vol_range_t vol_range = {
.min_vol =
{
.vol = 0x0,
.db_value = -95.5,
},
.max_vol =
{
.vol = 0xFF,
.db_value = 32.0,
},
};
static int es8311_write_reg(audio_codec_es8311_t *codec, int reg, int value)
{
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg, 1, &value, 1);
}
static int es8311_read_reg(audio_codec_es8311_t *codec, int reg, int *value)
{
*value = 0;
return codec->cfg.ctrl_if->read_reg(codec->cfg.ctrl_if, reg, 1, value, 1);
}
static int es8311_config_fmt(audio_codec_es8311_t *codec, es_i2s_fmt_t fmt)
{
int ret = ESP_CODEC_DEV_OK;
int adc_iface = 0, dac_iface = 0;
ret = es8311_read_reg(codec, ES8311_SDPIN_REG09, &dac_iface);
ret |= es8311_read_reg(codec, ES8311_SDPOUT_REG0A, &adc_iface);
switch (fmt) {
case ES_I2S_NORMAL:
ESP_LOGD(TAG, "ES8311 in I2S Format");
dac_iface &= 0xFC;
adc_iface &= 0xFC;
break;
case ES_I2S_LEFT:
case ES_I2S_RIGHT:
ESP_LOGD(TAG, "ES8311 in LJ Format");
adc_iface &= 0xFC;
dac_iface &= 0xFC;
adc_iface |= 0x01;
dac_iface |= 0x01;
break;
case ES_I2S_DSP:
ESP_LOGD(TAG, "ES8311 in DSP-A Format");
adc_iface &= 0xDC;
dac_iface &= 0xDC;
adc_iface |= 0x03;
dac_iface |= 0x03;
break;
default:
dac_iface &= 0xFC;
adc_iface &= 0xFC;
break;
}
ret |= es8311_write_reg(codec, ES8311_SDPIN_REG09, dac_iface);
ret |= es8311_write_reg(codec, ES8311_SDPOUT_REG0A, adc_iface);
return ret;
}
static int es8311_set_bits_per_sample(audio_codec_es8311_t *codec, int bits)
{
int ret = ESP_CODEC_DEV_OK;
int adc_iface = 0, dac_iface = 0;
ret |= es8311_read_reg(codec, ES8311_SDPIN_REG09, &dac_iface);
ret |= es8311_read_reg(codec, ES8311_SDPOUT_REG0A, &adc_iface);
switch (bits) {
case 16:
default:
dac_iface |= 0x0c;
adc_iface |= 0x0c;
break;
case 24:
dac_iface &= ~0x1c;
adc_iface &= ~0x1c;
break;
case 32:
dac_iface |= 0x10;
adc_iface |= 0x10;
break;
}
ret |= es8311_write_reg(codec, ES8311_SDPIN_REG09, dac_iface);
ret |= es8311_write_reg(codec, ES8311_SDPOUT_REG0A, adc_iface);
return ret;
}
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 ESP_CODEC_DEV_NOT_FOUND;
}
static int es8311_suspend(audio_codec_es8311_t *codec)
{
int ret = es8311_write_reg(codec, ES8311_DAC_REG32, 0x00);
ret |= es8311_write_reg(codec, ES8311_ADC_REG17, 0x00);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0E, 0xFF);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG12, 0x02);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG14, 0x00);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0D, 0xFA);
ret |= es8311_write_reg(codec, ES8311_ADC_REG15, 0x00);
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG02, 0x10);
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, 0x00);
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, 0x1F);
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, 0x30);
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, 0x00);
ret |= es8311_write_reg(codec, ES8311_GP_REG45, 0x00);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0D, 0xFC);
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG02, 0x00);
return ret;
}
static int es8311_start(audio_codec_es8311_t *codec)
{
int ret = ESP_CODEC_DEV_OK;
int adc_iface = 0, dac_iface = 0;
int regv = 0x80;
if (codec->cfg.master_mode) {
regv |= 0x40;
} else {
regv &= 0xBF;
}
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, regv);
regv = 0x3F;
if (codec->cfg.use_mclk) {
regv &= 0x7F;
} else {
regv |= 0x80;
}
if (codec->cfg.invert_mclk) {
regv |= 0x40;
} else {
regv &= ~(0x40);
}
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, regv);
ret = es8311_read_reg(codec, ES8311_SDPIN_REG09, &dac_iface);
ret |= es8311_read_reg(codec, ES8311_SDPOUT_REG0A, &adc_iface);
if (ret != ESP_CODEC_DEV_OK) {
return ret;
}
dac_iface &= 0xBF;
adc_iface &= 0xBF;
adc_iface |= BITS(6);
dac_iface |= BITS(6);
int codec_mode = codec->cfg.codec_mode;
if (codec_mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
ESP_LOGE(TAG, "Codec not support LINE mode");
return ESP_CODEC_DEV_NOT_SUPPORT;
}
if (codec_mode == ESP_CODEC_DEV_WORK_MODE_ADC || codec_mode == ESP_CODEC_DEV_WORK_MODE_BOTH) {
adc_iface &= ~(BITS(6));
}
if (codec_mode == ESP_CODEC_DEV_WORK_MODE_DAC || codec_mode == ESP_CODEC_DEV_WORK_MODE_BOTH) {
dac_iface &= ~(BITS(6));
}
ret |= es8311_write_reg(codec, ES8311_SDPIN_REG09, dac_iface);
ret |= es8311_write_reg(codec, ES8311_SDPOUT_REG0A, adc_iface);
ret |= es8311_write_reg(codec, ES8311_ADC_REG17, 0xBF);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0E, 0x02);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG12, 0x00);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG14, 0x1A);
// pdm dmic enable or disable
regv = 0;
ret |= es8311_read_reg(codec, ES8311_SYSTEM_REG14, &regv);
if (codec->cfg.digital_mic) {
regv |= 0x40;
} else {
regv &= ~(0x40);
}
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG14, regv);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0D, 0x01);
ret |= es8311_write_reg(codec, ES8311_ADC_REG15, 0x40);
ret |= es8311_write_reg(codec, ES8311_DAC_REG37, 0x08);
ret |= es8311_write_reg(codec, ES8311_GP_REG45, 0x00);
return ret;
}
static int es8311_set_mute(const audio_codec_if_t *h, bool mute)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL || codec->is_open == false) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int regv;
int ret = es8311_read_reg(codec, ES8311_DAC_REG31, &regv);
regv &= 0x9f;
if (mute) {
es8311_write_reg(codec, ES8311_DAC_REG31, regv | 0x60);
} else {
es8311_write_reg(codec, ES8311_DAC_REG31, regv);
}
return ret;
}
static int es8311_set_vol(const audio_codec_if_t *h, float db_value)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
db_value -= codec->hw_gain;
int reg = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
ESP_LOGD(TAG, "Set volume reg:%x db:%d", reg, (int) db_value);
return es8311_write_reg(codec, ES8311_DAC_REG32, (uint8_t) reg);
}
static int es8311_set_mic_gain(const audio_codec_if_t *h, float db)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
es8311_mic_gain_t gain_db = ES8311_MIC_GAIN_0DB;
if (db < 6) {
} else if (db < 12) {
gain_db = ES8311_MIC_GAIN_6DB;
} else if (db < 18) {
gain_db = ES8311_MIC_GAIN_12DB;
} else if (db < 24) {
gain_db = ES8311_MIC_GAIN_18DB;
} else if (db < 30) {
gain_db = ES8311_MIC_GAIN_24DB;
} else if (db < 36) {
gain_db = ES8311_MIC_GAIN_30DB;
} else if (db < 42) {
gain_db = ES8311_MIC_GAIN_36DB;
} else {
gain_db = ES8311_MIC_GAIN_42DB;
}
int ret = es8311_write_reg(codec, ES8311_ADC_REG16, gain_db); // MIC gain scale
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static void es8311_pa_power(audio_codec_es8311_t *codec, es_pa_setting_t pa_setting)
{
int16_t pa_pin = codec->cfg.pa_pin;
if (pa_pin == -1 || codec->cfg.gpio_if == NULL) {
return;
}
if (pa_setting & ES_PA_SETUP) {
codec->cfg.gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
}
if (pa_setting & ES_PA_ENABLE) {
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? false : true);
}
if (pa_setting & ES_PA_DISABLE) {
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? true : false);
}
}
static int es8311_config_sample(audio_codec_es8311_t *codec, int sample_rate)
{
int datmp, regv;
int mclk_fre = sample_rate * codec->cfg.mclk_div;
int coeff = get_coeff(mclk_fre, sample_rate);
if (coeff < 0) {
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_rate, mclk_fre);
return ESP_CODEC_DEV_NOT_SUPPORT;
}
int ret = 0;
ret = es8311_read_reg(codec, ES8311_CLK_MANAGER_REG02, &regv);
regv &= 0x7;
regv |= (coeff_div[coeff].pre_div - 1) << 5;
datmp = 0;
switch (coeff_div[coeff].pre_multi) {
case 1:
datmp = 0;
break;
case 2:
datmp = 1;
break;
case 4:
datmp = 2;
break;
case 8:
datmp = 3;
break;
default:
break;
}
if (codec->cfg.use_mclk == false) {
datmp = 3;
}
regv |= (datmp) << 3;
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG02, regv);
regv = 0x00;
regv |= (coeff_div[coeff].adc_div - 1) << 4;
regv |= (coeff_div[coeff].dac_div - 1) << 0;
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG05, regv);
ret |= es8311_read_reg(codec, ES8311_CLK_MANAGER_REG03, &regv);
regv &= 0x80;
regv |= coeff_div[coeff].fs_mode << 6;
regv |= coeff_div[coeff].adc_osr << 0;
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG03, regv);
ret |= es8311_read_reg(codec, ES8311_CLK_MANAGER_REG04, &regv);
regv &= 0x80;
regv |= coeff_div[coeff].dac_osr << 0;
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG04, regv);
ret |= es8311_read_reg(codec, ES8311_CLK_MANAGER_REG07, &regv);
regv &= 0xC0;
regv |= coeff_div[coeff].lrck_h << 0;
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG07, regv);
regv = 0x00;
regv |= coeff_div[coeff].lrck_l << 0;
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG08, regv);
ret = es8311_read_reg(codec, ES8311_CLK_MANAGER_REG06, &regv);
regv &= 0xE0;
if (coeff_div[coeff].bclk_div < 19) {
regv |= (coeff_div[coeff].bclk_div - 1) << 0;
} else {
regv |= (coeff_div[coeff].bclk_div) << 0;
}
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG06, regv);
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static int es8311_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
es8311_codec_cfg_t *codec_cfg = (es8311_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es8311_codec_cfg_t)) {
return ESP_CODEC_DEV_INVALID_ARG;
}
memcpy(&codec->cfg, cfg, sizeof(es8311_codec_cfg_t));
if (codec->cfg.mclk_div == 0) {
codec->cfg.mclk_div = MCLK_DEFAULT_DIV;
}
int regv;
int ret = ESP_CODEC_DEV_OK;
/* Enhance ES8311 I2C noise immunity */
ret |= es8311_write_reg(codec, ES8311_GPIO_REG44, 0x08);
/* Due to occasional failures during the first I2C write with the ES8311 chip, a second write is performed to ensure reliability */
ret |= es8311_write_reg(codec, ES8311_GPIO_REG44, 0x08);
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, 0x30);
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG02, 0x00);
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG03, 0x10);
ret |= es8311_write_reg(codec, ES8311_ADC_REG16, 0x24);
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG04, 0x10);
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG05, 0x00);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0B, 0x00);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG0C, 0x00);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG10, 0x1F);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG11, 0x7F);
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, 0x80);
ret = es8311_read_reg(codec, ES8311_RESET_REG00, &regv);
if (codec_cfg->master_mode) {
ESP_LOGI(TAG, "Work in Master mode");
regv |= 0x40;
} else {
ESP_LOGI(TAG, "Work in Slave mode");
regv &= 0xBF;
}
ret |= es8311_write_reg(codec, ES8311_RESET_REG00, regv);
// Select clock source for internal mclk
regv = 0x3F;
if (codec_cfg->use_mclk) {
regv &= 0x7F;
} else {
regv |= 0x80;
}
// MCLK inverted or not
if (codec_cfg->invert_mclk) {
regv |= 0x40;
} else {
regv &= ~(0x40);
}
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG01, regv);
// SCLK inverted or not
ret |= es8311_read_reg(codec, ES8311_CLK_MANAGER_REG06, &regv);
if (codec_cfg->invert_sclk) {
regv |= 0x20;
} else {
regv &= ~(0x20);
}
ret |= es8311_write_reg(codec, ES8311_CLK_MANAGER_REG06, regv);
ret |= es8311_write_reg(codec, ES8311_SYSTEM_REG13, 0x10);
ret |= es8311_write_reg(codec, ES8311_ADC_REG1B, 0x0A);
ret |= es8311_write_reg(codec, ES8311_ADC_REG1C, 0x6A);
if (codec_cfg->no_dac_ref == false) {
/* set internal reference signal (ADCL + DACR) */
ret |= es8311_write_reg(codec, ES8311_GPIO_REG44, 0x58);
} else {
ret |= es8311_write_reg(codec, ES8311_GPIO_REG44, 0x08);
}
if (ret != 0) {
return ESP_CODEC_DEV_WRITE_FAIL;
}
es8311_pa_power(codec, ES_PA_SETUP | ES_PA_ENABLE);
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int es8311_close(const audio_codec_if_t *h)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open) {
es8311_suspend(codec);
es8311_pa_power(codec, ES_PA_DISABLE);
codec->is_open = false;
}
return ESP_CODEC_DEV_OK;
}
static int es8311_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL || codec->is_open == false) {
return ESP_CODEC_DEV_INVALID_ARG;
}
es8311_set_bits_per_sample(codec, fs->bits_per_sample);
es8311_config_fmt(codec, ES_I2S_NORMAL);
es8311_config_sample(codec, fs->sample_rate);
return ESP_CODEC_DEV_OK;
}
static int es8311_enable(const audio_codec_if_t *h, bool enable)
{
int ret = ESP_CODEC_DEV_OK;
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (enable == codec->enabled) {
return ESP_CODEC_DEV_OK;
}
if (enable) {
ret = es8311_start(codec);
es8311_pa_power(codec, ES_PA_ENABLE);
} else {
es8311_pa_power(codec, ES_PA_DISABLE);
ret = es8311_suspend(codec);
}
if (ret == ESP_CODEC_DEV_OK) {
codec->enabled = enable;
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
}
return ret;
}
static int es8311_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8311_write_reg(codec, reg, value);
}
static int es8311_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8311_read_reg(codec, reg, value);
}
static void es8311_dump(const audio_codec_if_t *h)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i < ES8311_MAX_REGISTER; i++) {
int value = 0;
int ret = es8311_read_reg(codec, i, &value);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, value);
}
}
const audio_codec_if_t *es8311_codec_new(es8311_codec_cfg_t *codec_cfg)
{
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) calloc(1, sizeof(audio_codec_es8311_t));
if (codec == NULL) {
CODEC_MEM_CHECK(codec);
return NULL;
}
codec->base.open = es8311_open;
codec->base.enable = es8311_enable;
codec->base.set_fs = es8311_set_fs;
codec->base.set_vol = es8311_set_vol;
codec->base.set_mic_gain = es8311_set_mic_gain;
codec->base.mute = es8311_set_mute;
codec->base.set_reg = es8311_set_reg;
codec->base.get_reg = es8311_get_reg;
codec->base.dump_reg = es8311_dump;
codec->base.close = es8311_close;
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es8311_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,99 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES8311_REG_H_
#define _ES8311_REG_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* ES8311_REGISTER NAME_REG_REGISTER ADDRESS
*/
#define ES8311_RESET_REG00 0x00 /*reset digital,csm,clock manager etc.*/
/*
* Clock Scheme Register definition
*/
#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */
#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */
#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */
#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */
#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */
#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */
#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */
#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */
/*
* SDP
*/
#define ES8311_SDPIN_REG09 0x09 /* dac serial digital port */
#define ES8311_SDPOUT_REG0A 0x0A /* adc serial digital port */
/*
* SYSTEM
*/
#define ES8311_SYSTEM_REG0B 0x0B /* system */
#define ES8311_SYSTEM_REG0C 0x0C /* system */
#define ES8311_SYSTEM_REG0D 0x0D /* system, power up/down */
#define ES8311_SYSTEM_REG0E 0x0E /* system, power up/down */
#define ES8311_SYSTEM_REG0F 0x0F /* system, low power */
#define ES8311_SYSTEM_REG10 0x10 /* system */
#define ES8311_SYSTEM_REG11 0x11 /* system */
#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */
#define ES8311_SYSTEM_REG13 0x13 /* system */
#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */
/*
* ADC
*/
#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */
#define ES8311_ADC_REG16 0x16 /* ADC */
#define ES8311_ADC_REG17 0x17 /* ADC, volume */
#define ES8311_ADC_REG18 0x18 /* ADC, alc enable and winsize */
#define ES8311_ADC_REG19 0x19 /* ADC, alc maxlevel */
#define ES8311_ADC_REG1A 0x1A /* ADC, alc automute */
#define ES8311_ADC_REG1B 0x1B /* ADC, alc automute, adc hpf s1 */
#define ES8311_ADC_REG1C 0x1C /* ADC, equalizer, hpf s2 */
/*
* DAC
*/
#define ES8311_DAC_REG31 0x31 /* DAC, mute */
#define ES8311_DAC_REG32 0x32 /* DAC, volume */
#define ES8311_DAC_REG33 0x33 /* DAC, offset */
#define ES8311_DAC_REG34 0x34 /* DAC, drc enable, drc winsize */
#define ES8311_DAC_REG35 0x35 /* DAC, drc maxlevel, minilevel */
#define ES8311_DAC_REG37 0x37 /* DAC, ramprate */
/*
*GPIO
*/
#define ES8311_GPIO_REG44 0x44 /* GPIO, dac2adc for test */
#define ES8311_GP_REG45 0x45 /* GP CONTROL */
/*
* CHIP
*/
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
#define ES8311_CHD2_REGFE 0xFE /* CHIP ID2 */
#define ES8311_CHVER_REGFF 0xFF /* VERSION */
#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */
#define ES8311_MAX_REGISTER 0xFF
typedef enum {
ES8311_MIC_GAIN_MIN = -1,
ES8311_MIC_GAIN_0DB,
ES8311_MIC_GAIN_6DB,
ES8311_MIC_GAIN_12DB,
ES8311_MIC_GAIN_18DB,
ES8311_MIC_GAIN_24DB,
ES8311_MIC_GAIN_30DB,
ES8311_MIC_GAIN_36DB,
ES8311_MIC_GAIN_42DB,
ES8311_MIC_GAIN_MAX
} es8311_mic_gain_t;
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,765 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "es8374_codec.h"
#include "esp_log.h"
#include "esp_codec_dev_vol.h"
#include "es_common.h"
#define TAG "ES8374"
typedef struct {
audio_codec_if_t base;
es8374_codec_cfg_t cfg;
bool is_open;
bool enabled;
} audio_codec_es8374_t;
static const esp_codec_dev_vol_range_t vol_range = {
.min_vol =
{
.vol = 0xC0,
.db_value = -96.0,
},
.max_vol =
{
.vol = 0x0,
.db_value = 0.0,
},
};
static int es8374_write_reg(audio_codec_es8374_t *codec, int reg, int value)
{
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg, 1, &value, 1);
}
static int es8374_read_reg(audio_codec_es8374_t *codec, int reg, int *value)
{
*value = 0;
return codec->cfg.ctrl_if->read_reg(codec->cfg.ctrl_if, reg, 1, value, 1);
}
static int es8374_set_voice_mute(audio_codec_es8374_t *codec, bool enable)
{
int ret = 0;
int reg = 0;
ret |= es8374_read_reg(codec, 0x36, &reg);
if (ret == 0) {
reg = reg & 0xdf;
ret |= es8374_write_reg(codec, 0x36, reg | (((int) enable) << 5));
}
return ret;
}
static es_bits_length_t get_bits(uint8_t bits)
{
switch (bits) {
default:
case 16:
return BIT_LENGTH_16BITS;
case 18:
return BIT_LENGTH_18BITS;
case 20:
return BIT_LENGTH_20BITS;
case 24:
return BIT_LENGTH_24BITS;
case 32:
return BIT_LENGTH_32BITS;
}
}
static int es8374_set_bits_per_sample(audio_codec_es8374_t *codec, uint8_t bits)
{
int ret = ESP_CODEC_DEV_OK;
int reg = 0;
es_bits_length_t bit_per_sample = get_bits(bits);
bits = (int) bit_per_sample & 0x0f;
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
ret |= es8374_read_reg(codec, 0x10, &reg);
if (ret == 0) {
reg = reg & 0xe3;
ret |= es8374_write_reg(codec, 0x10, reg | (bits << 2));
}
}
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
ret |= es8374_read_reg(codec, 0x11, &reg);
if (ret == 0) {
reg = reg & 0xe3;
ret |= es8374_write_reg(codec, 0x11, reg | (bits << 2));
}
}
return ret;
}
static int _set_mic_gain(audio_codec_es8374_t *codec, float gain)
{
int ret = 0;
if (gain >= 0 && gain < 24) {
int gain_n = 0;
gain_n = (int) gain / 3;
ret = es8374_write_reg(codec, 0x22, gain_n | (gain_n << 4)); // MIC PGA
} else {
ret = -1;
}
return ret;
}
static int es8374_i2s_config_clock(audio_codec_es8374_t *codec, es_i2s_clock_t cfg)
{
int ret = 0;
int reg = 0;
ret |= es8374_read_reg(codec, 0x0f, &reg); // power up adc and input
reg &= 0xe0;
int divratio = 0;
switch (cfg.sclk_div) {
case MCLK_DIV_1:
divratio = 1;
break;
case MCLK_DIV_2: // = 2,
divratio = 2;
break;
case MCLK_DIV_3: // = 3,
divratio = 3;
break;
case MCLK_DIV_4: // = 4,
divratio = 4;
break;
case MCLK_DIV_5: // = 20,
divratio = 5;
break;
case MCLK_DIV_6: // = 5,
divratio = 6;
break;
case MCLK_DIV_7: // = 29,
divratio = 7;
break;
case MCLK_DIV_8: // = 6,
divratio = 8;
break;
case MCLK_DIV_9: // = 7,
divratio = 9;
break;
case MCLK_DIV_10: // = 21,
divratio = 10;
break;
case MCLK_DIV_11: // = 8,
divratio = 11;
break;
case MCLK_DIV_12: // = 9,
divratio = 12;
break;
case MCLK_DIV_13: // = 30,
divratio = 13;
break;
case MCLK_DIV_14: // = 31
divratio = 14;
break;
case MCLK_DIV_15: // = 22,
divratio = 15;
break;
case MCLK_DIV_16: // = 10,
divratio = 16;
break;
case MCLK_DIV_17: // = 23,
divratio = 17;
break;
case MCLK_DIV_18: // = 11,
divratio = 18;
break;
case MCLK_DIV_20: // = 24,
divratio = 19;
break;
case MCLK_DIV_22: // = 12,
divratio = 20;
break;
case MCLK_DIV_24: // = 13,
divratio = 21;
break;
case MCLK_DIV_25: // = 25,
divratio = 22;
break;
case MCLK_DIV_30: // = 26,
divratio = 23;
break;
case MCLK_DIV_32: // = 27,
divratio = 24;
break;
case MCLK_DIV_33: // = 14,
divratio = 25;
break;
case MCLK_DIV_34: // = 28,
divratio = 26;
break;
case MCLK_DIV_36: // = 15,
divratio = 27;
break;
case MCLK_DIV_44: // = 16,
divratio = 28;
break;
case MCLK_DIV_48: // = 17,
divratio = 29;
break;
case MCLK_DIV_66: // = 18,
divratio = 30;
break;
case MCLK_DIV_72: // = 19,
divratio = 31;
break;
default:
break;
}
reg |= divratio;
ret |= es8374_write_reg(codec, 0x0f, reg);
int dacratio_l = 0;
int dacratio_h = 0;
switch (cfg.lclk_div) {
case LCLK_DIV_128:
dacratio_l = 128 % 256;
dacratio_h = 128 / 256;
break;
case LCLK_DIV_192:
dacratio_l = 192 % 256;
dacratio_h = 192 / 256;
break;
case LCLK_DIV_256:
dacratio_l = 256 % 256;
dacratio_h = 256 / 256;
break;
case LCLK_DIV_384:
dacratio_l = 384 % 256;
dacratio_h = 384 / 256;
break;
case LCLK_DIV_512:
dacratio_l = 512 % 256;
dacratio_h = 512 / 256;
break;
case LCLK_DIV_576:
dacratio_l = 576 % 256;
dacratio_h = 576 / 256;
break;
case LCLK_DIV_768:
dacratio_l = 768 % 256;
dacratio_h = 768 / 256;
break;
case LCLK_DIV_1024:
dacratio_l = 1024 % 256;
dacratio_h = 1024 / 256;
break;
case LCLK_DIV_1152:
dacratio_l = 1152 % 256;
dacratio_h = 1152 / 256;
break;
case LCLK_DIV_1408:
dacratio_l = 1408 % 256;
dacratio_h = 1408 / 256;
break;
case LCLK_DIV_1536:
dacratio_l = 1536 % 256;
dacratio_h = 1536 / 256;
break;
case LCLK_DIV_2112:
dacratio_l = 2112 % 256;
dacratio_h = 2112 / 256;
break;
case LCLK_DIV_2304:
dacratio_l = 2304 % 256;
dacratio_h = 2304 / 256;
break;
case LCLK_DIV_125:
dacratio_l = 125 % 256;
dacratio_h = 125 / 256;
break;
case LCLK_DIV_136:
dacratio_l = 136 % 256;
dacratio_h = 136 / 256;
break;
case LCLK_DIV_250:
dacratio_l = 250 % 256;
dacratio_h = 250 / 256;
break;
case LCLK_DIV_272:
dacratio_l = 272 % 256;
dacratio_h = 272 / 256;
break;
case LCLK_DIV_375:
dacratio_l = 375 % 256;
dacratio_h = 375 / 256;
break;
case LCLK_DIV_500:
dacratio_l = 500 % 256;
dacratio_h = 500 / 256;
break;
case LCLK_DIV_544:
dacratio_l = 544 % 256;
dacratio_h = 544 / 256;
break;
case LCLK_DIV_750:
dacratio_l = 750 % 256;
dacratio_h = 750 / 256;
break;
case LCLK_DIV_1000:
dacratio_l = 1000 % 256;
dacratio_h = 1000 / 256;
break;
case LCLK_DIV_1088:
dacratio_l = 1088 % 256;
dacratio_h = 1088 / 256;
break;
case LCLK_DIV_1496:
dacratio_l = 1496 % 256;
dacratio_h = 1496 / 256;
break;
case LCLK_DIV_1500:
dacratio_l = 1500 % 256;
dacratio_h = 1500 / 256;
break;
default:
break;
}
ret |= es8374_write_reg(codec, 0x06, dacratio_h); // ADCFsMode,singel SPEED,RATIO=256
ret |= es8374_write_reg(codec, 0x07, dacratio_l); // ADCFsMode,singel SPEED,RATIO=256
return ret;
}
static int es8374_set_d2se_pga(audio_codec_es8374_t *codec, es_d2se_pga_t gain)
{
int ret = 0;
int reg = 0;
if (gain > D2SE_PGA_GAIN_MIN && gain < D2SE_PGA_GAIN_MAX) {
ret = es8374_read_reg(codec, 0x21, &reg);
reg &= 0xfb;
reg |= gain << 2;
ret = es8374_write_reg(codec, 0x21, reg); // MIC PGA
}
return ret;
}
static int es8374_config_fmt(audio_codec_es8374_t *codec, es_i2s_fmt_t fmt)
{
int ret = 0;
int reg = 0;
int fmt_i2s = fmt & 0x0f;
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
ret |= es8374_read_reg(codec, 0x10, &reg);
if (ret == 0) {
reg = reg & 0xfc;
ret |= es8374_write_reg(codec, 0x10, reg | fmt_i2s);
}
}
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
ret |= es8374_read_reg(codec, 0x11, &reg);
if (ret == 0) {
reg = reg & 0xfc;
ret |= es8374_write_reg(codec, 0x11, reg | (fmt_i2s));
}
}
return ret;
}
static int es8374_config_dac_output(audio_codec_es8374_t *codec, es_dac_output_t output)
{
int ret = 0;
int reg = 0;
ret = es8374_write_reg(codec, 0x1d, 0x02);
ret |= es8374_read_reg(codec, 0x1c, &reg); // set spk mixer
reg |= 0x80;
ret |= es8374_write_reg(codec, 0x1c, reg);
ret |= es8374_write_reg(codec, 0x1D, 0x02); // spk set
ret |= es8374_write_reg(codec, 0x1F, 0x00); // spk set
ret |= es8374_write_reg(codec, 0x1E, 0xA0); // spk on
return ret;
}
static int es8374_config_adc_input(audio_codec_es8374_t *codec, es_adc_input_t input)
{
int ret = 0;
int reg = 0;
ret |= es8374_read_reg(codec, 0x21, &reg);
if (ret == 0) {
reg = (reg & 0xcf) | 0x14;
ret |= es8374_write_reg(codec, 0x21, reg);
}
return ret;
}
static int es8374_set_adc_dac_volume(audio_codec_es8374_t *codec, esp_codec_dec_work_mode_t mode, float db_value)
{
int reg = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
int ret = ESP_CODEC_DEV_OK;
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
ret = es8374_write_reg(codec, 0x25, (uint8_t) reg);
}
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
ret = es8374_write_reg(codec, 0x38, (uint8_t) reg);
}
return ret;
}
static int es8374_init_reg(audio_codec_es8374_t *codec, es_i2s_fmt_t fmt, es_i2s_clock_t cfg,
es_dac_output_t out_channel, es_adc_input_t in_channel)
{
int ret = 0;
int reg = 0;
ret |= es8374_write_reg(codec, 0x00, 0x3F); // IC Rst start
ret |= es8374_write_reg(codec, 0x00, 0x03); // IC Rst stop
ret |= es8374_write_reg(codec, 0x01, 0x7F); // IC clk on
ret |= es8374_read_reg(codec, 0x0F, &reg);
reg &= 0x7f;
reg |= (codec->cfg.master_mode << 7);
ret |= es8374_write_reg(codec, 0x0f, reg); // CODEC IN I2S SLAVE MODE
ret |= es8374_write_reg(codec, 0x6F, 0xA0); // pll set:mode enable
ret |= es8374_write_reg(codec, 0x72, 0x41); // pll set:mode set
ret |= es8374_write_reg(codec, 0x09, 0x01); // pll set:reset on ,set start
ret |= es8374_write_reg(codec, 0x0C, 0x22); // pll set:k
ret |= es8374_write_reg(codec, 0x0D, 0x2E); // pll set:k
ret |= es8374_write_reg(codec, 0x0E, 0xC6); // pll set:k
ret |= es8374_write_reg(codec, 0x0A, 0x3A); // pll set:
ret |= es8374_write_reg(codec, 0x0B, 0x07); // pll set:n
ret |= es8374_write_reg(codec, 0x09, 0x41); // pll set:reset off ,set stop
ret |= es8374_i2s_config_clock(codec, cfg);
ret |= es8374_write_reg(codec, 0x24, 0x08); // adc set
ret |= es8374_write_reg(codec, 0x36, 0x00); // dac set
ret |= es8374_write_reg(codec, 0x12, 0x30); // timming set
ret |= es8374_write_reg(codec, 0x13, 0x20); // timming set
ret |= es8374_config_fmt(codec, fmt);
ret |= es8374_write_reg(codec, 0x21, 0x50); // adc set: SEL LIN1 CH+PGAGAIN=0DB
ret |= es8374_write_reg(codec, 0x22, 0xFF); // adc set: PGA GAIN=0DB
ret |= es8374_write_reg(codec, 0x21, 0x14); // adc set: SEL LIN1 CH+PGAGAIN=18DB
ret |= es8374_write_reg(codec, 0x22, 0x55); // pga = +15db
ret |= es8374_write_reg(codec, 0x08, 0x21); // set class d divider = 33, to avoid the high frequency tone on laudspeaker
ret |= es8374_write_reg(codec, 0x00, 0x80); // IC START
ret |= es8374_set_adc_dac_volume(codec, ESP_CODEC_DEV_WORK_MODE_ADC, 0.0); // 0db
ret |= es8374_write_reg(codec, 0x14, 0x8A); // IC START
ret |= es8374_write_reg(codec, 0x15, 0x40); // IC START
ret |= es8374_write_reg(codec, 0x1A, 0xA0); // monoout set
ret |= es8374_write_reg(codec, 0x1B, 0x19); // monoout set
ret |= es8374_write_reg(codec, 0x1C, 0x90); // spk set
ret |= es8374_write_reg(codec, 0x1D, 0x01); // spk set
ret |= es8374_write_reg(codec, 0x1F, 0x00); // spk set
ret |= es8374_write_reg(codec, 0x1E, 0x20); // spk on
ret |= es8374_write_reg(codec, 0x28, 0x00); // alc set
ret |= es8374_write_reg(codec, 0x25, 0x00); // ADCVOLUME on
ret |= es8374_write_reg(codec, 0x38, 0x00); // DACVOLUME on
ret |= es8374_write_reg(codec, 0x37, 0x30); // dac set
ret |= es8374_write_reg(codec, 0x6D, 0x60); // SEL:GPIO1=DMIC CLK OUT+SEL:GPIO2=PLL CLK OUT
ret |= es8374_write_reg(codec, 0x71, 0x05); // for automute setting
ret |= es8374_write_reg(codec, 0x73, 0x70);
ret |= es8374_config_dac_output(codec, out_channel); // 0x3c Enable DAC and Enable Lout/Rout/1/2
ret |= es8374_config_adc_input(codec, in_channel); // 0x00 LINSEL & RINSEL
ret |= es8374_set_adc_dac_volume(codec, ESP_CODEC_DEV_WORK_MODE_DAC, -96.0);
ret |= es8374_write_reg(codec, 0x37, 0x00); // dac set
return ret;
}
static int es8374_stop(audio_codec_es8374_t *codec)
{
int ret = 0;
int reg = 0;
if (codec->cfg.codec_mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
ret |= es8374_read_reg(codec, 0x1a, &reg); // disable lout
reg |= 0x08;
ret |= es8374_write_reg(codec, 0x1a, reg);
reg &= 0x9f;
ret |= es8374_write_reg(codec, 0x1a, reg);
ret |= es8374_write_reg(codec, 0x1D, 0x12); // mute speaker
ret |= es8374_write_reg(codec, 0x1E, 0x20); // disable class d
ret |= es8374_read_reg(codec, 0x1c, &reg); // disable spkmixer
reg &= 0xbf;
ret |= es8374_write_reg(codec, 0x1c, reg);
ret |= es8374_write_reg(codec, 0x1F, 0x00); // spk set
}
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
ret |= es8374_set_voice_mute(codec, true);
ret |= es8374_read_reg(codec, 0x1a, &reg); // disable lout
reg |= 0x08;
ret |= es8374_write_reg(codec, 0x1a, reg);
reg &= 0xdf;
ret |= es8374_write_reg(codec, 0x1a, reg);
ret |= es8374_write_reg(codec, 0x1D, 0x12); // mute speaker
ret |= es8374_write_reg(codec, 0x1E, 0x20); // disable class d
ret |= es8374_read_reg(codec, 0x15, &reg); // power up dac
reg |= 0x20;
ret |= es8374_write_reg(codec, 0x15, reg);
}
if (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
ret |= es8374_read_reg(codec, 0x10, &reg); // power up adc and input
reg |= 0xc0;
ret |= es8374_write_reg(codec, 0x10, reg);
ret |= es8374_read_reg(codec, 0x21, &reg); // power up adc and input
reg |= 0xc0;
ret |= es8374_write_reg(codec, 0x21, reg);
}
return ret;
}
static int es8374_start(audio_codec_es8374_t *codec)
{
int ret = ESP_CODEC_DEV_OK;
int reg = 0;
bool mode_line = (codec->cfg.codec_mode == ESP_CODEC_DEV_WORK_MODE_LINE);
if (mode_line) {
ret |= es8374_read_reg(codec, 0x1a, &reg); // set monomixer
reg |= 0x60;
reg |= 0x20;
reg &= 0xf7;
ret |= es8374_write_reg(codec, 0x1a, reg);
ret |= es8374_read_reg(codec, 0x1c, &reg); // set spk mixer
reg |= 0x40;
ret |= es8374_write_reg(codec, 0x1c, reg);
ret |= es8374_write_reg(codec, 0x1D, 0x02); // spk set
ret |= es8374_write_reg(codec, 0x1F, 0x00); // spk set
ret |= es8374_write_reg(codec, 0x1E, 0xA0); // spk on
}
if (mode_line || (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_ADC)) {
ret |= es8374_read_reg(codec, 0x21, &reg); // power up adc and input
reg &= 0x3f;
ret |= es8374_write_reg(codec, 0x21, reg);
ret |= es8374_read_reg(codec, 0x10, &reg); // power up adc and input
reg &= 0x3f;
ret |= es8374_write_reg(codec, 0x10, reg);
}
if (mode_line || (codec->cfg.codec_mode & ESP_CODEC_DEV_WORK_MODE_DAC)) {
ret |= es8374_read_reg(codec, 0x1a, &reg); // disable lout
reg |= 0x08;
ret |= es8374_write_reg(codec, 0x1a, reg);
reg &= 0xdf;
ret |= es8374_write_reg(codec, 0x1a, reg);
ret |= es8374_write_reg(codec, 0x1D, 0x12); // mute speaker
ret |= es8374_write_reg(codec, 0x1E, 0x20); // disable class d
ret |= es8374_read_reg(codec, 0x15, &reg); // power up dac
reg &= 0xdf;
ret |= es8374_write_reg(codec, 0x15, reg);
ret |= es8374_read_reg(codec, 0x1a, &reg); // disable lout
reg |= 0x20;
ret |= es8374_write_reg(codec, 0x1a, reg);
reg &= 0xf7;
ret |= es8374_write_reg(codec, 0x1a, reg);
ret |= es8374_write_reg(codec, 0x1D, 0x02); // mute speaker
ret |= es8374_write_reg(codec, 0x1E, 0xa0); // disable class d
ret |= es8374_set_voice_mute(codec, false);
}
return ret;
}
static int es8374_set_mute(const audio_codec_if_t *h, bool mute)
{
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
if (codec == NULL || codec->is_open == false) {
return ESP_CODEC_DEV_INVALID_ARG;
}
return es8374_set_voice_mute(codec, mute);
}
static int es8374_set_vol(const audio_codec_if_t *h, float db_value)
{
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8374_set_adc_dac_volume(codec, ESP_CODEC_DEV_WORK_MODE_DAC, db_value);
}
static int es8374_set_mic_gain(const audio_codec_if_t *h, float db)
{
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
int ret = _set_mic_gain(codec, db);
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static void es8374_pa_power(audio_codec_es8374_t *codec, bool enable)
{
int16_t pa_pin = codec->cfg.pa_pin;
if (pa_pin == -1 || codec->cfg.gpio_if == NULL) {
return;
}
codec->cfg.gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? !enable : enable);
}
static int es8374_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
es8374_codec_cfg_t *codec_cfg = (es8374_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es8374_codec_cfg_t)) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = ESP_CODEC_DEV_OK;
memcpy(&codec->cfg, codec_cfg, sizeof(es8374_codec_cfg_t));
es_i2s_clock_t clkdiv;
clkdiv.lclk_div = LCLK_DIV_256;
clkdiv.sclk_div = MCLK_DIV_4;
ret |= es8374_stop(codec);
ret |= es8374_init_reg(codec, (BIT_LENGTH_16BITS << 4) | ES_I2S_NORMAL, clkdiv, DAC_OUTPUT_ALL,
ADC_INPUT_LINPUT1_RINPUT1);
ret |= _set_mic_gain(codec, 15);
ret |= es8374_set_d2se_pga(codec, D2SE_PGA_GAIN_EN);
ret |= es8374_config_fmt(codec, ES_I2S_NORMAL);
if (ret != 0) {
return ESP_CODEC_DEV_WRITE_FAIL;
}
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int es8374_close(const audio_codec_if_t *h)
{
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open) {
es8374_stop(codec);
es8374_write_reg(codec, 0x00, 0x7F); // IC Reset and STOP
es8374_pa_power(codec, false);
codec->is_open = false;
}
return ESP_CODEC_DEV_OK;
}
static int es8374_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
{
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
if (codec == NULL || codec->is_open == false) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int ret = 0;
ret |= es8374_config_fmt(codec, ES_I2S_NORMAL);
ret |= es8374_set_bits_per_sample(codec, fs->bits_per_sample);
return ESP_CODEC_DEV_OK;
}
static int es8374_enable(const audio_codec_if_t *h, bool enable)
{
int ret = ESP_CODEC_DEV_OK;
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (codec->enabled == enable) {
return ESP_CODEC_DEV_OK;
}
if (enable) {
ret = es8374_start(codec);
es8374_pa_power(codec, true);
} else {
es8374_pa_power(codec, false);
ret = es8374_stop(codec);
es8374_write_reg(codec, 0x00, 0x7F); // IC Reset and STOP
}
if (ret == ESP_CODEC_DEV_OK) {
codec->enabled = enable;
ESP_LOGD(TAG, "Codec is %s", enable ? "enable" : "disable");
}
return ret;
}
static int es8374_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8374_write_reg(codec, reg, value);
}
static int es8374_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8374_read_reg(codec, reg, value);
}
static void es8374_dump(const audio_codec_if_t *h)
{
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i < 50; i++) {
int value = 0;
int ret = es8374_read_reg(codec, i, &value);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, value);
}
}
const audio_codec_if_t *es8374_codec_new(es8374_codec_cfg_t *codec_cfg)
{
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_es8374_t *codec = (audio_codec_es8374_t *) calloc(1, sizeof(audio_codec_es8374_t));
if (codec == NULL) {
CODEC_MEM_CHECK(codec);
return NULL;
}
codec->base.open = es8374_open;
codec->base.enable = es8374_enable;
codec->base.set_fs = es8374_set_fs;
codec->base.mute = es8374_set_mute;
codec->base.set_vol = es8374_set_vol;
codec->base.set_mic_gain = es8374_set_mic_gain;
codec->base.set_reg = es8374_set_reg;
codec->base.get_reg = es8374_get_reg;
codec->base.dump_reg = es8374_dump,
codec->base.close = es8374_close;
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es8374_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,430 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"
#include "es8388_reg.h"
#include "es8388_codec.h"
#include "es_common.h"
#include "esp_codec_dev_vol.h"
#define TAG "ES8388"
typedef struct {
audio_codec_if_t base;
const audio_codec_ctrl_if_t *ctrl_if;
const audio_codec_gpio_if_t *gpio_if;
bool is_open;
bool enabled;
esp_codec_dec_work_mode_t codec_mode;
int16_t pa_pin;
bool pa_reverted;
float hw_gain;
} audio_codec_es8388_t;
static const esp_codec_dev_vol_range_t vol_range = {
.min_vol =
{
.vol = 0xC0,
.db_value = -96,
},
.max_vol =
{
.vol = 0,
.db_value = 0.0,
},
};
static int es8388_write_reg(audio_codec_es8388_t *codec, int reg, int value)
{
return codec->ctrl_if->write_reg(codec->ctrl_if, reg, 1, &value, 1);
}
static int es8388_read_reg(audio_codec_es8388_t *codec, int reg, int *value)
{
*value = 0;
return codec->ctrl_if->read_reg(codec->ctrl_if, reg, 1, value, 1);
}
static int es8388_set_adc_dac_volume(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode, int volume, int dot)
{
int res = 0;
if (volume < -96 || volume > 0) {
if (volume < -96)
volume = -96;
else
volume = 0;
}
dot = (dot >= 5 ? 1 : 0);
volume = (-volume << 1) + dot;
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
res |= es8388_write_reg(codec, ES8388_ADCCONTROL8, volume);
res |= es8388_write_reg(codec, ES8388_ADCCONTROL9, volume); // ADC Right Volume=0db
}
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
res |= es8388_write_reg(codec, ES8388_DACCONTROL5, volume);
res |= es8388_write_reg(codec, ES8388_DACCONTROL4, volume);
}
return res;
}
static int es8388_set_voice_mute(audio_codec_es8388_t *codec, bool enable)
{
int res = 0;
int reg = 0;
res = es8388_read_reg(codec, ES8388_DACCONTROL3, &reg);
reg = reg & 0xFB;
res |= es8388_write_reg(codec, ES8388_DACCONTROL3, reg | (((int) enable) << 2));
return res;
}
static int es8388_start(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode)
{
int res = 0;
int prev_data = 0, data = 0;
es8388_read_reg(codec, ES8388_DACCONTROL21, &prev_data);
if (mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
res |= es8388_write_reg(codec, ES8388_DACCONTROL16,
0x09); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 by pass enable
res |= es8388_write_reg(
codec, ES8388_DACCONTROL17,
0x50); // left DAC to left mixer enable and LIN signal to left mixer enable 0db : bupass enable
res |= es8388_write_reg(
codec, ES8388_DACCONTROL20,
0x50); // right DAC to right mixer enable and LIN signal to right mixer enable 0db : bupass enable
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0xC0); // enable adc
} else {
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0x80); // enable dac
}
es8388_read_reg(codec, ES8388_DACCONTROL21, &data);
if (prev_data != data) {
res |= es8388_write_reg(codec, ES8388_CHIPPOWER, 0xF0); // start state machine
res |= es8388_write_reg(codec, ES8388_CHIPPOWER, 0x00); // start state machine
}
if ((mode & ESP_CODEC_DEV_WORK_MODE_ADC) || mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
res |= es8388_write_reg(codec, ES8388_ADCPOWER, 0x00); // power up adc and line in
}
if ((mode & ESP_CODEC_DEV_WORK_MODE_DAC) || mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
res |= es8388_write_reg(codec, ES8388_DACPOWER, 0x3c); // power up dac and line out
res |= es8388_set_voice_mute(codec, false);
ESP_LOGI(TAG, "Start on mode:%d", mode);
}
return res;
}
static int es8388_stop(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode)
{
int res = 0;
if (mode == ESP_CODEC_DEV_WORK_MODE_LINE) {
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0x80); // enable dac
res |= es8388_write_reg(codec, ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
res |= es8388_write_reg(codec, ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db
res |= es8388_write_reg(codec, ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db
return res;
}
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
res |= es8388_write_reg(codec, ES8388_DACPOWER, 0x00);
res |= es8388_set_voice_mute(codec, true);
}
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
res |= es8388_write_reg(codec, ES8388_ADCPOWER, 0xFF); // power down adc and line in
}
if (mode == ESP_CODEC_DEV_WORK_MODE_BOTH) {
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0x9C); // disable mclk
}
return res;
}
static int es8388_config_fmt(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode, es_i2s_fmt_t fmt)
{
int res = 0;
int reg = 0;
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
res = es8388_read_reg(codec, ES8388_ADCCONTROL4, &reg);
reg = reg & 0xfc;
res |= es8388_write_reg(codec, ES8388_ADCCONTROL4, reg | fmt);
}
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
res = es8388_read_reg(codec, ES8388_DACCONTROL1, &reg);
reg = reg & 0xf9;
res |= es8388_write_reg(codec, ES8388_DACCONTROL1, reg | (fmt << 1));
}
return res;
}
static int es8388_set_mic_gain(audio_codec_es8388_t *codec, float db)
{
int gain = db > 0 ? (int) (db / 3) : 0;
gain = (gain << 4) + gain;
return es8388_write_reg(codec, ES8388_ADCCONTROL1, gain); // MIC PGA
}
static es_bits_length_t get_bits_enum(uint8_t bits)
{
switch (bits) {
case 16:
default:
return BIT_LENGTH_16BITS;
case 18:
return BIT_LENGTH_18BITS;
case 20:
return BIT_LENGTH_20BITS;
case 24:
return BIT_LENGTH_24BITS;
case 32:
return BIT_LENGTH_32BITS;
}
}
static int es8388_set_bits_per_sample(audio_codec_es8388_t *codec, esp_codec_dec_work_mode_t mode, uint8_t bits_length)
{
int res = 0;
int reg = 0;
int bits = (int) get_bits_enum(bits_length);
if (mode & ESP_CODEC_DEV_WORK_MODE_ADC) {
res = es8388_read_reg(codec, ES8388_ADCCONTROL4, &reg);
reg = reg & 0xe3;
res |= es8388_write_reg(codec, ES8388_ADCCONTROL4, reg | (bits << 2));
}
if (mode & ESP_CODEC_DEV_WORK_MODE_DAC) {
res = es8388_read_reg(codec, ES8388_DACCONTROL1, &reg);
reg = reg & 0xc7;
res |= es8388_write_reg(codec, ES8388_DACCONTROL1, reg | (bits << 3));
}
return res;
}
static void es8388_pa_power(audio_codec_es8388_t *codec, bool enable)
{
int16_t pa_pin = codec->pa_pin;
if (pa_pin == -1 || codec->gpio_if == NULL) {
return;
}
codec->gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
codec->gpio_if->set(pa_pin, codec->pa_reverted ? !enable : enable);
}
static int es8388_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
es8388_codec_cfg_t *codec_cfg = (es8388_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es8388_codec_cfg_t)) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int res = ESP_CODEC_DEV_OK;
codec->ctrl_if = codec_cfg->ctrl_if;
codec->gpio_if = codec_cfg->gpio_if;
codec->pa_pin = codec_cfg->pa_pin;
codec->pa_reverted = codec_cfg->pa_reverted;
codec->codec_mode = codec_cfg->codec_mode;
// 0x04 mute/0x00 unmute&ramp;
res |= es8388_write_reg(codec, ES8388_DACCONTROL3, 0x04);
/* Chip Control and Power Management */
res |= es8388_write_reg(codec, ES8388_CONTROL2, 0x50);
res |= es8388_write_reg(codec, ES8388_CHIPPOWER, 0x00); // normal all and power up all
// Disable the internal DLL to improve 8K sample rate
res |= es8388_write_reg(codec, 0x35, 0xA0);
res |= es8388_write_reg(codec, 0x37, 0xD0);
res |= es8388_write_reg(codec, 0x39, 0xD0);
res |= es8388_write_reg(codec, ES8388_MASTERMODE, codec_cfg->master_mode); // CODEC IN I2S SLAVE MODE
/* dac */
res |= es8388_write_reg(codec, ES8388_DACPOWER, 0xC0); // disable DAC and disable Lout/Rout/1/2
res |= es8388_write_reg(codec, ES8388_CONTROL1, 0x12); // Enfr=0,Play&Record Mode,(0x17-both of mic&paly)
// res |= es8388_write_reg(codec, ES8388_CONTROL2, 0); //LPVrefBuf=0,Pdn_ana=0
res |= es8388_write_reg(codec, ES8388_DACCONTROL1, 0x18); // 1a 0x18:16bit iis , 0x00:24
res |= es8388_write_reg(codec, ES8388_DACCONTROL2, 0x02); // DACFsMode,SINGLE SPEED; DACFsRatio,256
res |= es8388_write_reg(codec, ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
res |= es8388_write_reg(codec, ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db
res |= es8388_write_reg(codec, ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db
// set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
res |= es8388_write_reg(codec, ES8388_DACCONTROL21, 0x80);
res |= es8388_write_reg(codec, ES8388_DACCONTROL23, 0x00); // vroi=0
res |= es8388_set_adc_dac_volume(codec, ES_MODULE_DAC, 0, 0); // 0db
res |= es8388_write_reg(codec, ES8388_DACCONTROL24, 0x1E); // Set L1 R1 L2 R2 volume. 0x00: -30dB, 0x1E: 0dB, 0x21: 3dB
res |= es8388_write_reg(codec, ES8388_DACCONTROL25, 0x1E);
res |= es8388_write_reg(codec, ES8388_DACCONTROL26, 0);
res |= es8388_write_reg(codec, ES8388_DACCONTROL27, 0);
// TODO default use DAC_ALL
int tmp = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2;
res |= es8388_write_reg(codec, ES8388_DACPOWER, tmp); // 0x3c Enable DAC and Enable Lout/Rout/1/2
/* adc */
res |= es8388_write_reg(codec, ES8388_ADCPOWER, 0xFF);
res |= es8388_write_reg(codec, ES8388_ADCCONTROL1, 0xbb); // MIC Left and Right channel PGA gain
tmp = 0;
// TODO default use ADC LINE1
// 0x00 LINSEL & RINSEL, LIN1/RIN1 as ADC Input; DSSEL,use one DS Reg11; DSR, LINPUT1-RINPUT1
res |= es8388_write_reg(codec, ES8388_ADCCONTROL2, ADC_INPUT_LINPUT1_RINPUT1);
res |= es8388_write_reg(codec, ES8388_ADCCONTROL3, 0x02);
res |= es8388_write_reg(codec, ES8388_ADCCONTROL4, 0x0c); // 16 Bits length and I2S serial audio data format
res |= es8388_write_reg(codec, ES8388_ADCCONTROL5, 0x02); // ADCFsMode,singel SPEED,RATIO=256
// ALC for Microphone
res |= es8388_set_adc_dac_volume(codec, ESP_CODEC_DEV_WORK_MODE_ADC, 0, 0); // 0db
res |= es8388_write_reg(codec, ES8388_ADCPOWER, 0x09); // Power on ADC
if (res != 0) {
ESP_LOGI(TAG, "Fail to write register");
return ESP_CODEC_DEV_WRITE_FAIL;
}
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int es8388_enable(const audio_codec_if_t *h, bool enable)
{
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (codec->enabled == enable) {
return ESP_CODEC_DEV_OK;
}
int res;
if (enable == false) {
es8388_pa_power(codec, false);
res = es8388_stop(codec, codec->codec_mode);
} else {
res = es8388_start(codec, codec->codec_mode);
es8388_pa_power(codec, true);
}
if (res == ESP_CODEC_DEV_OK) {
codec->enabled = enable;
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
}
return res;
}
static int es8388_mute(const audio_codec_if_t *h, bool mute)
{
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8388_set_voice_mute(codec, mute);
}
static int es8388_set_vol(const audio_codec_if_t *h, float db_value)
{
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
db_value -= codec->hw_gain;
int volume = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
int res = es8388_write_reg(codec, ES8388_DACCONTROL5, volume);
res |= es8388_write_reg(codec, ES8388_DACCONTROL4, volume);
ESP_LOGD(TAG, "Set volume reg:%x db:%f", volume, db_value);
return res ? ESP_CODEC_DEV_WRITE_FAIL : ESP_CODEC_DEV_OK;
}
static int es8388_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
{
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
if (codec == NULL || fs == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
int res = 0;
res |= es8388_config_fmt(codec, ESP_CODEC_DEV_WORK_MODE_BOTH, ES_I2S_NORMAL);
res |= es8388_set_bits_per_sample(codec, ESP_CODEC_DEV_WORK_MODE_BOTH, fs->bits_per_sample);
return res;
}
static int es8388_set_gain(const audio_codec_if_t *h, float db)
{
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8388_set_mic_gain(codec, db);
}
static int es8388_close(const audio_codec_if_t *h)
{
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open) {
es8388_pa_power(codec, false);
codec->is_open = false;
}
return ESP_CODEC_DEV_OK;
}
static void es8388_dump(const audio_codec_if_t *h)
{
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i <= ES8388_DACCONTROL30; i++) {
int value = 0;
int ret = es8388_read_reg(codec, i, &value);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, value);
}
}
const audio_codec_if_t *es8388_codec_new(es8388_codec_cfg_t *codec_cfg)
{
// verify param
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_es8388_t *codec = (audio_codec_es8388_t *) calloc(1, sizeof(audio_codec_es8388_t));
if (codec == NULL) {
CODEC_MEM_CHECK(codec);
return NULL;
}
codec->ctrl_if = codec_cfg->ctrl_if;
codec->base.open = es8388_open;
codec->base.enable = es8388_enable;
codec->base.set_fs = es8388_set_fs;
codec->base.set_vol = es8388_set_vol;
codec->base.mute = es8388_mute;
codec->base.set_mic_gain = es8388_set_gain;
codec->base.dump_reg = es8388_dump;
codec->base.close = es8388_close;
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es8388_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Fail to open");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES8388_REG_
#define _ES8388_REG_
#ifdef __cplusplus
extern "C" {
#endif
/* ES8388 register */
#define ES8388_CONTROL1 0x00
#define ES8388_CONTROL2 0x01
#define ES8388_CHIPPOWER 0x02
#define ES8388_ADCPOWER 0x03
#define ES8388_DACPOWER 0x04
#define ES8388_CHIPLOPOW1 0x05
#define ES8388_CHIPLOPOW2 0x06
#define ES8388_ANAVOLMANAG 0x07
#define ES8388_MASTERMODE 0x08
/* ADC */
#define ES8388_ADCCONTROL1 0x09
#define ES8388_ADCCONTROL2 0x0a
#define ES8388_ADCCONTROL3 0x0b
#define ES8388_ADCCONTROL4 0x0c
#define ES8388_ADCCONTROL5 0x0d
#define ES8388_ADCCONTROL6 0x0e
#define ES8388_ADCCONTROL7 0x0f
#define ES8388_ADCCONTROL8 0x10
#define ES8388_ADCCONTROL9 0x11
#define ES8388_ADCCONTROL10 0x12
#define ES8388_ADCCONTROL11 0x13
#define ES8388_ADCCONTROL12 0x14
#define ES8388_ADCCONTROL13 0x15
#define ES8388_ADCCONTROL14 0x16
/* DAC */
#define ES8388_DACCONTROL1 0x17
#define ES8388_DACCONTROL2 0x18
#define ES8388_DACCONTROL3 0x19
#define ES8388_DACCONTROL4 0x1a
#define ES8388_DACCONTROL5 0x1b
#define ES8388_DACCONTROL6 0x1c
#define ES8388_DACCONTROL7 0x1d
#define ES8388_DACCONTROL8 0x1e
#define ES8388_DACCONTROL9 0x1f
#define ES8388_DACCONTROL10 0x20
#define ES8388_DACCONTROL11 0x21
#define ES8388_DACCONTROL12 0x22
#define ES8388_DACCONTROL13 0x23
#define ES8388_DACCONTROL14 0x24
#define ES8388_DACCONTROL15 0x25
#define ES8388_DACCONTROL16 0x26
#define ES8388_DACCONTROL17 0x27
#define ES8388_DACCONTROL18 0x28
#define ES8388_DACCONTROL19 0x29
#define ES8388_DACCONTROL20 0x2a
#define ES8388_DACCONTROL21 0x2b
#define ES8388_DACCONTROL22 0x2c
#define ES8388_DACCONTROL23 0x2d
#define ES8388_DACCONTROL24 0x2e
#define ES8388_DACCONTROL25 0x2f
#define ES8388_DACCONTROL26 0x30
#define ES8388_DACCONTROL27 0x31
#define ES8388_DACCONTROL28 0x32
#define ES8388_DACCONTROL29 0x33
#define ES8388_DACCONTROL30 0x34
#ifdef __cplusplus
}
#endif
#endif //__ES8388_H__

View File

@@ -0,0 +1,716 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <string.h>
#include "es8389_reg.h"
#include "es8389_codec.h"
#include "es_common.h"
#include "esp_log.h"
#define TAG "ES8389"
typedef struct {
audio_codec_if_t base;
es8389_codec_cfg_t cfg;
bool is_open;
bool enabled;
float hw_gain;
bool use_mclk;
} audio_codec_es8389_t;
/*
* Clock coefficient structure
*/
struct _coeff_div {
uint16_t Ratio;
uint32_t MCLK;
uint32_t LRCK;
uint8_t Reg0x04;
uint8_t Reg0x05;
uint8_t Reg0x06;
uint8_t Reg0x07;
uint8_t Reg0x08;
uint8_t Reg0x09;
uint8_t Reg0x0A;
uint8_t Reg0x0F;
uint8_t Reg0x11;
uint8_t Reg0x21;
uint8_t Reg0x22;
uint8_t Reg0x26;
uint8_t Reg0x30;
uint8_t Reg0x41;
uint8_t Reg0x42;
uint8_t Reg0x43;
uint8_t Reg0xF0;
uint8_t Reg0xF1;
uint8_t Reg0x16;
uint8_t Reg0x18;
uint8_t Reg0x19;
};
/* codec hifi mclk clock divider coefficients */
static const struct _coeff_div coeff_div[] = {
// Ratio Freq MCLK FreqLRCK,Reg0x04,Reg0x05,Reg0x06,Reg0x07,Reg0x08,Reg0x09,Reg0x0A,Reg0x0F,Reg0x11,Reg0x21,Reg0x22,Reg0x26,Reg0x30,Reg0x41,Reg0x42,Reg0x43,Reg0xF0,Reg0xF1,Reg0x16,Reg0x18,Reg0x19
{32 ,256000 ,8000 ,0x00 ,0x57 ,0x84 ,0xD0 ,0x03 ,0xC1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{36 ,288000 ,8000 ,0x00 ,0x55 ,0x84 ,0xD0 ,0x01 ,0xC1 ,0x90 ,0x00 ,0x00 ,0x23 ,0x8F ,0xB7 ,0xC0 ,0x1F ,0x8F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{48 ,384000 ,8000 ,0x02 ,0x5F ,0x04 ,0xC0 ,0x03 ,0xC1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{64 ,512000 ,8000 ,0x00 ,0x4D ,0x24 ,0xC0 ,0x03 ,0xD1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{72 ,576000 ,8000 ,0x00 ,0x45 ,0x24 ,0xC0 ,0x01 ,0xD1 ,0x90 ,0x00 ,0x00 ,0x23 ,0x8F ,0xB7 ,0xC0 ,0x1F ,0x8F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{96 ,768000 ,8000 ,0x02 ,0x57 ,0x84 ,0xD0 ,0x03 ,0xC1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{128 ,1024000 ,8000 ,0x00 ,0x45 ,0x04 ,0xD0 ,0x03 ,0xC1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{192 ,1536000 ,8000 ,0x02 ,0x4D ,0x24 ,0xC0 ,0x03 ,0xD1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{256 ,2048000 ,8000 ,0x01 ,0x45 ,0x04 ,0xD0 ,0x03 ,0xC1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{288 ,2304000 ,8000 ,0x01 ,0x51 ,0x00 ,0xC0 ,0x01 ,0xC1 ,0x90 ,0x00 ,0x00 ,0x23 ,0x8F ,0xB7 ,0xC0 ,0x1F ,0x8F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{384 ,3072000 ,8000 ,0x02 ,0x45 ,0x04 ,0xD0 ,0x03 ,0xC1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{512 ,4096000 ,8000 ,0x00 ,0x41 ,0x04 ,0xE0 ,0x00 ,0xD1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{768 ,6144000 ,8000 ,0x05 ,0x45 ,0x04 ,0xD0 ,0x03 ,0xC1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{1024 ,8192000 ,8000 ,0x01 ,0x41 ,0x06 ,0xE0 ,0x00 ,0xD1 ,0xB0 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{1536 ,12288000 ,8000 ,0x02 ,0x41 ,0x04 ,0xE0 ,0x00 ,0xD1 ,0xB0 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{1625 ,13000000 ,8000 ,0x40 ,0x6E ,0x05 ,0xC8 ,0x01 ,0xC2 ,0x90 ,0x40 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{2048 ,16384000 ,8000 ,0x03 ,0x44 ,0x01 ,0xC0 ,0x00 ,0xD2 ,0x80 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{2304 ,18432000 ,8000 ,0x11 ,0x45 ,0x25 ,0xF0 ,0x00 ,0xD1 ,0xB0 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{3072 ,24576000 ,8000 ,0x05 ,0x44 ,0x01 ,0xC0 ,0x00 ,0xD2 ,0x80 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{32 ,512000 ,16000 ,0x00 ,0x55 ,0x84 ,0xD0 ,0x01 ,0xC1 ,0x90 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{36 ,576000 ,16000 ,0x00 ,0x55 ,0x84 ,0xD0 ,0x01 ,0xC1 ,0x90 ,0x00 ,0x00 ,0x23 ,0x8F ,0xB7 ,0xC0 ,0x1F ,0x8F ,0x01 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{48 ,768000 ,16000 ,0x02 ,0x57 ,0x04 ,0xC0 ,0x01 ,0xC1 ,0x90 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{50 ,800000 ,16000 ,0x00 ,0x7E ,0x01 ,0xD9 ,0x00 ,0xC2 ,0x80 ,0x00 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0xC7 ,0x95 ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{64 ,1024000 ,16000 ,0x00 ,0x45 ,0x24 ,0xC0 ,0x01 ,0xD1 ,0x90 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{72 ,1152000 ,16000 ,0x00 ,0x45 ,0x24 ,0xC0 ,0x01 ,0xD1 ,0x90 ,0x00 ,0x00 ,0x23 ,0x8F ,0xB7 ,0xC0 ,0x1F ,0x8F ,0x01 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{96 ,1536000 ,16000 ,0x02 ,0x55 ,0x84 ,0xD0 ,0x01 ,0xC1 ,0x90 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{128 ,2048000 ,16000 ,0x00 ,0x51 ,0x04 ,0xD0 ,0x01 ,0xC1 ,0x90 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{144 ,2304000 ,16000 ,0x00 ,0x51 ,0x00 ,0xC0 ,0x01 ,0xC1 ,0x90 ,0x00 ,0x00 ,0x23 ,0x8F ,0xB7 ,0xC0 ,0x1F ,0x8F ,0x01 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{192 ,3072000 ,16000 ,0x02 ,0x65 ,0x25 ,0xE0 ,0x00 ,0xE1 ,0x90 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{256 ,4096000 ,16000 ,0x00 ,0x41 ,0x04 ,0xC0 ,0x01 ,0xD1 ,0x90 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{300 ,4800000 ,16000 ,0x02 ,0x66 ,0x01 ,0xD9 ,0x00 ,0xC2 ,0x80 ,0x00 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0xC7 ,0x95 ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{384 ,6144000 ,16000 ,0x02 ,0x51 ,0x04 ,0xD0 ,0x01 ,0xC1 ,0x90 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{512 ,8192000 ,16000 ,0x01 ,0x41 ,0x04 ,0xC0 ,0x01 ,0xD1 ,0x90 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{750 ,12000000 ,16000 ,0x0E ,0x7E ,0x01 ,0xC9 ,0x00 ,0xC2 ,0x80 ,0x40 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0xC7 ,0x95 ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{768 ,12288000 ,16000 ,0x02 ,0x41 ,0x04 ,0xC0 ,0x01 ,0xD1 ,0x90 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{1024 ,16384000 ,16000 ,0x03 ,0x41 ,0x04 ,0xC0 ,0x01 ,0xD1 ,0x90 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{1152 ,18432000 ,16000 ,0x08 ,0x51 ,0x04 ,0xD0 ,0x01 ,0xC1 ,0x90 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{1200 ,19200000 ,16000 ,0x0B ,0x66 ,0x01 ,0xD9 ,0x00 ,0xC2 ,0x80 ,0x40 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0xC7 ,0x95 ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{1500 ,24000000 ,16000 ,0x0E ,0x26 ,0x01 ,0xD9 ,0x00 ,0xC2 ,0x80 ,0xC0 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0xC7 ,0x95 ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{1536 ,24576000 ,16000 ,0x05 ,0x41 ,0x04 ,0xC0 ,0x01 ,0xD1 ,0x90 ,0xC0 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0xFF ,0x7F ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{1625 ,26000000 ,16000 ,0x40 ,0x6E ,0x05 ,0xC8 ,0x01 ,0xC2 ,0x90 ,0xC0 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x12 ,0x31 ,0x0E},
{800 ,19200000 ,24000 ,0x07 ,0x66 ,0x01 ,0xD9 ,0x00 ,0xC2 ,0x80 ,0x40 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0xC7 ,0x95 ,0x00 ,0x12 ,0x00 ,0x1A ,0x49 ,0x14},
{600 ,19200000 ,32000 ,0x05 ,0x46 ,0x01 ,0xD8 ,0x10 ,0xD2 ,0x80 ,0x40 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x23 ,0x61 ,0x1B},
{32 ,1411200 ,44100 ,0x00 ,0x45 ,0xA4 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{64 ,2822400 ,44100 ,0x00 ,0x51 ,0x00 ,0xC0 ,0x10 ,0xC1 ,0x80 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{128 ,5644800 ,44100 ,0x00 ,0x41 ,0x04 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{256 ,11289600 ,44100 ,0x01 ,0x41 ,0x04 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{512 ,22579200 ,44100 ,0x03 ,0x41 ,0x04 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0xC0 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{32 ,1536000 ,48000 ,0x00 ,0x45 ,0xA4 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{48 ,2304000 ,48000 ,0x02 ,0x55 ,0x04 ,0xC0 ,0x10 ,0xC1 ,0x80 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{50 ,2400000 ,48000 ,0x00 ,0x76 ,0x01 ,0xC8 ,0x10 ,0xC2 ,0x80 ,0x00 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{64 ,3072000 ,48000 ,0x00 ,0x51 ,0x04 ,0xC0 ,0x10 ,0xC1 ,0x80 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{100 ,4800000 ,48000 ,0x00 ,0x46 ,0x01 ,0xD8 ,0x10 ,0xD2 ,0x80 ,0x00 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{125 ,6000000 ,48000 ,0x04 ,0x6E ,0x05 ,0xC8 ,0x10 ,0xC2 ,0x80 ,0x00 ,0x01 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{128 ,6144000 ,48000 ,0x00 ,0x41 ,0x04 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0x00 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{200 ,9600000 ,48000 ,0x01 ,0x46 ,0x01 ,0xD8 ,0x10 ,0xD2 ,0x80 ,0x00 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{250 ,12000000 ,48000 ,0x04 ,0x76 ,0x01 ,0xC8 ,0x10 ,0xC2 ,0x80 ,0x40 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{256 ,12288000 ,48000 ,0x01 ,0x41 ,0x04 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{384 ,18432000 ,48000 ,0x02 ,0x41 ,0x04 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0x40 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{400 ,19200000 ,48000 ,0x03 ,0x46 ,0x01 ,0xD8 ,0x10 ,0xD2 ,0x80 ,0x40 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{500 ,24000000 ,48000 ,0x04 ,0x46 ,0x01 ,0xD8 ,0x10 ,0xD2 ,0x80 ,0xC0 ,0x00 ,0x18 ,0x95 ,0xD0 ,0xC0 ,0x63 ,0x95 ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{512 ,24576000 ,48000 ,0x03 ,0x41 ,0x04 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0xC0 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{800 ,38400000 ,48000 ,0x18 ,0x45 ,0x04 ,0xC0 ,0x10 ,0xC1 ,0x80 ,0xC0 ,0x00 ,0x1F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x00 ,0x12 ,0x00 ,0x35 ,0x91 ,0x28},
{128 ,11289600 ,88200 ,0x00 ,0x50 ,0x00 ,0xC0 ,0x10 ,0xC1 ,0x80 ,0x40 ,0x00 ,0x9F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x80 ,0x12 ,0xC0 ,0x32 ,0x89 ,0x25},
{64 ,6144000 ,96000 ,0x00 ,0x41 ,0x00 ,0xD0 ,0x10 ,0xD1 ,0x80 ,0x00 ,0x00 ,0x9F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x80 ,0x12 ,0xC0 ,0x35 ,0x91 ,0x28},
{256 ,24576000 ,96000 ,0x00 ,0x40 ,0x00 ,0xC0 ,0x10 ,0xC1 ,0x80 ,0xC0 ,0x00 ,0x9F ,0x7F ,0xBF ,0xC0 ,0x7F ,0x7F ,0x80 ,0x12 ,0xC0 ,0x35 ,0x91 ,0x28},
{128 ,24576000 ,192000 ,0x00 ,0x50 ,0x00 ,0xC0 ,0x18 ,0xC1 ,0x81 ,0xC0 ,0x00 ,0x8F ,0x7F ,0xEF ,0xC0 ,0x3F ,0x7F ,0x80 ,0x12 ,0xC0 ,0x3F ,0xF9 ,0x3F},
{50 ,400000 ,8000 ,0x00 ,0x75 ,0x05 ,0xC8 ,0x01 ,0xC1 ,0x90 ,0x10 ,0x00 ,0x18 ,0xC7 ,0xD0 ,0xC0 ,0x8F ,0xC7 ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{600 ,4800000 ,8000 ,0x05 ,0x65 ,0x25 ,0xF9 ,0x00 ,0xD1 ,0x90 ,0x10 ,0x00 ,0x18 ,0xC7 ,0xD0 ,0xC0 ,0x8F ,0xC7 ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{1500 ,12000000 ,8000 ,0x0E ,0x25 ,0x25 ,0xE8 ,0x00 ,0xD1 ,0x90 ,0x40 ,0x00 ,0x31 ,0xC7 ,0xC5 ,0x00 ,0x8F ,0xC7 ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{2400 ,19200000 ,8000 ,0x0B ,0x01 ,0x00 ,0xD0 ,0x00 ,0xD1 ,0x80 ,0x90 ,0x00 ,0x31 ,0xC7 ,0xC5 ,0x00 ,0xC7 ,0xC7 ,0x00 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{3000 ,24000000 ,8000 ,0x0E ,0x24 ,0x05 ,0xD0 ,0x00 ,0xC2 ,0x80 ,0xC0 ,0x00 ,0x31 ,0xC7 ,0xC5 ,0x00 ,0x8F ,0xC7 ,0x01 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
{3250 ,26000000 ,8000 ,0x40 ,0x05 ,0xA4 ,0xC0 ,0x00 ,0xD1 ,0x80 ,0xD0 ,0x00 ,0x31 ,0xC7 ,0xC5 ,0x00 ,0xC7 ,0xC7 ,0x00 ,0x12 ,0x00 ,0x09 ,0x19 ,0x07},
};
static const esp_codec_dev_vol_range_t vol_range = {
.min_vol =
{
.vol = 0x0,
.db_value = -95.5,
},
.max_vol =
{
.vol = 0xFF,
.db_value = 32.0,
},
};
static int es8389_write_reg(audio_codec_es8389_t *codec, int reg, int value)
{
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg, 1, &value, 1);
}
static int es8389_read_reg(audio_codec_es8389_t *codec, int reg, int *value)
{
*value = 0;
return codec->cfg.ctrl_if->read_reg(codec->cfg.ctrl_if, reg, 1, value, 1);
}
static int es8389_update_bits(audio_codec_es8389_t *codec, uint8_t reg_addr, uint8_t mask, uint8_t val)
{
int regval = 0;
es8389_read_reg(codec, reg_addr, &regval);
regval &= ~mask;
regval |= (val & mask);
return es8389_write_reg(codec, reg_addr, regval);
}
int es8389_set_bias_standby(audio_codec_es8389_t *codec)
{
int ret = es8389_update_bits(codec, ES8389_DAC_CONTROL_REG0x40, 0x03, 0x03);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x10, 0xD4);
vTaskDelay(pdMS_TO_TICKS(70));
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x61, 0x59);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x64, 0x00);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x03, 0x00);
ret |= es8389_write_reg(codec, ES8389_RESET_REG0x00, 0x7E);
ret |= es8389_update_bits(codec, ES8389_DAC_CONTROL_REG0x40, 0x03, 0x00);
return ret;
}
int es8389_set_bias_on(audio_codec_es8389_t *codec)
{
int ret = es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x4D, 0x02);
ret |= es8389_update_bits(codec, ES8389_ANALOG_CONTROL_REG0x69, 0x20, 0x20);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x61, 0xD9);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x64, 0x8F);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x10, 0xE4);
ret |= es8389_write_reg(codec, ES8389_RESET_REG0x00, 0x01);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x03, 0xC3);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x24, 0x6A);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x25, (uint8_t)(0x0A + (0 << 6) + (0 << 5)));
return ret;
}
static int es8389_config_fmt(audio_codec_es8389_t *codec, es_i2s_fmt_t fmt)
{
int ret = ESP_CODEC_DEV_OK;
int state = 0 ;
switch (fmt) {
case ES_I2S_NORMAL:
ESP_LOGD(TAG, "es8389 in I2S Format");
state |= ES8389_DAIFMT_I2S;
ret |= es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x0C, 0xE0, 0x00);
break;
case ES_I2S_LEFT:
case ES_I2S_RIGHT:
ESP_LOGD(TAG, "es8389 in LJ Format");
state |= ES8389_DAIFMT_LEFT_J;
ret |= es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x0C, 0xE0, 0x40);
break;
case ES_I2S_DSP:
ESP_LOGD(TAG, "es8389 in DSP-A Format");
state |= ES8389_DAIFMT_DSP_A;
ret |= es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x0C, 0xE0, 0x80);
break;
default:
ESP_LOGD(TAG, "es8389 in DSP-B Format");
state |= ES8389_DAIFMT_DSP_B;
ret |= es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x0C, 0xE0, 0xA0);
break;
}
ret |= es8389_update_bits(codec, ES8389_ADC_SP_CONTROL_REG0x20, ES8389_MASK_DAIFMT, state);
ret |= es8389_update_bits(codec, ES8389_DAC_CONTROL_REG0x40, ES8389_MASK_DAIFMT, state);
return ret;
}
static int es8389_set_bits_per_sample(audio_codec_es8389_t *codec, int bits)
{
int ret = ESP_CODEC_DEV_OK;
int state = 0 ;
switch (bits) {
case 16:
default:
state |= ES8389_S16_LE;
break;
case 18:
state |= ES8389_S18_LE;
break;
case 20:
state |= ES8389_S20_LE;
break;
case 24:
state |= ES8389_S24_LE;
break;
case 32:
state |= ES8389_S32_LE;
break;
}
ret |= es8389_update_bits(codec, ES8389_ADC_SP_CONTROL_REG0x20, ES8389_MASK_DATALEN, state);
ret |= es8389_update_bits(codec, ES8389_DAC_CONTROL_REG0x40, ES8389_MASK_DATALEN, state);
return ret;
}
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].Ratio == rate && coeff_div[i].MCLK == mclk) {
return i;
}
}
return ESP_CODEC_DEV_NOT_FOUND;
}
static int es8389_suspend(audio_codec_es8389_t *codec)
{
int ret = ESP_CODEC_DEV_OK;
ret |= es8389_update_bits(codec, ES8389_DAC_CONTROL_REG0x40, 0x03, 0x03);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x10, 0xD4);
vTaskDelay(pdMS_TO_TICKS(70));
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x61, 0x59);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x64, 0x00);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x63, 0x00);
ret |= es8389_write_reg(codec, ES8389_RESET_REG0x00, 0x7E);
ret |= es8389_update_bits(codec, ES8389_DAC_CONTROL_REG0x40, 0x03, 0x00);
ret |= es8389_write_reg(codec, ES8389_MISC_CONTROL_REG0x01, 0x28);
ret |= es8389_update_bits(codec, ES8389_ANALOG_CONTROL_REG0x69, 0x20, 0x00);
ret |= es8389_write_reg(codec, ES8389_VMID_CONTROL_REG0x60, 0x00);
ret |= es8389_write_reg(codec, ES8389_RESET_REG0x00, 0x00);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x10, 0xCC);
vTaskDelay(pdMS_TO_TICKS(500));
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x10, 0x00);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x61, 0x08);
ret |= es8389_write_reg(codec, ES8389_ISOLATION_CONTROL_REG0xF3, 0xC1);
ret |= es8389_write_reg(codec, ES8389_PULL_DOWN_CONTROL_REG0xF2, 0x00);
return ret;
}
static int es8389_start(audio_codec_es8389_t *codec)
{
int ret = ESP_CODEC_DEV_OK;
ret |= es8389_set_bias_on(codec);
return ret;
}
static int es8389_set_mute(const audio_codec_if_t *h, bool mute)
{
audio_codec_es8389_t *codec = (audio_codec_es8389_t *) h;
if (codec == NULL || codec->is_open == false) {
return ESP_CODEC_DEV_INVALID_ARG;
}
int regv;
int ret = es8389_read_reg(codec, ES8389_ADC_SP_CONTROL_REG0x20, &regv);
regv &= 0xFC;
if (mute) {
regv |= 0x03;
}
es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x20, regv);
return ret;
}
static int es8389_set_vol(const audio_codec_if_t *h, float db_value)
{
int ret = ESP_CODEC_DEV_OK;
audio_codec_es8389_t *codec = (audio_codec_es8389_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
db_value -= codec->hw_gain;
int reg = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
ESP_LOGD(TAG, "Set volume reg:%x db:%d", reg, (int) db_value);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x46, (uint8_t) reg);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x47, (uint8_t) reg);
return ret;
}
static int es8389_set_mic_gain(const audio_codec_if_t *h, float db)
{
audio_codec_es8389_t *codec = (audio_codec_es8389_t *)h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
es8389_mic_gain_t gain_db = ES8389_MIC_GAIN_0DB;
if (db < 6) {
gain_db = ES8389_MIC_GAIN_3_5DB;
} else if (db < 9) {
gain_db = ES8389_MIC_GAIN_6_5DB;
} else if (db < 12) {
gain_db = ES8389_MIC_GAIN_9_5DB;
} else if (db < 15) {
gain_db = ES8389_MIC_GAIN_12_5DB;
} else if (db < 18) {
gain_db = ES8389_MIC_GAIN_15_5DB;
} else if (db < 21) {
gain_db = ES8389_MIC_GAIN_18_5DB;
} else if (db < 24) {
gain_db = ES8389_MIC_GAIN_21_5DB;
} else if (db < 27) {
gain_db = ES8389_MIC_GAIN_24_5DB;
} else if (db < 30) {
gain_db = ES8389_MIC_GAIN_27_5DB;
} else if (db < 33) {
gain_db = ES8389_MIC_GAIN_30_5DB;
} else if (db < 36) {
gain_db = ES8389_MIC_GAIN_33_5DB;
} else {
gain_db = ES8389_MIC_GAIN_36_5DB;
}
int ret = es8389_write_reg(codec, ES8389_PGA1_GAIN_CONTROL_REG0x72, gain_db | (3 << 4)); // MIC gain scale
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static void es8389_pa_power(audio_codec_es8389_t *codec, es_pa_setting_t pa_setting)
{
int16_t pa_pin = codec->cfg.pa_pin;
if (pa_pin == -1 || codec->cfg.gpio_if == NULL) {
return;
}
if (pa_setting & ES_PA_SETUP) {
codec->cfg.gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
}
if (pa_setting & ES_PA_ENABLE) {
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? false : true);
}
if (pa_setting & ES_PA_DISABLE) {
codec->cfg.gpio_if->set(pa_pin, codec->cfg.pa_reverted ? true : false);
}
}
static int es8389_config_sample(audio_codec_es8389_t *codec, int sample_rate, int bits)
{
int ret = ESP_CODEC_DEV_OK;
int mclk_fre = sample_rate * bits * 2;
int rate = mclk_fre / sample_rate;
int coeff = get_coeff(mclk_fre, rate);
if (coeff < 0) {
ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_rate, mclk_fre);
return ESP_CODEC_DEV_NOT_SUPPORT;
} else {
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x04, coeff_div[coeff].Reg0x04);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x05, coeff_div[coeff].Reg0x05);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x06, coeff_div[coeff].Reg0x06);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x07, coeff_div[coeff].Reg0x07);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x08, coeff_div[coeff].Reg0x08);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x09, coeff_div[coeff].Reg0x09);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x0A, coeff_div[coeff].Reg0x0A);
ret |= es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x0F, 0xC0, coeff_div[coeff].Reg0x0F);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x11, coeff_div[coeff].Reg0x11);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x21, coeff_div[coeff].Reg0x21);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x22, coeff_div[coeff].Reg0x22);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x26, coeff_div[coeff].Reg0x26);
ret |= es8389_update_bits(codec, 0x30, 0xC0, coeff_div[coeff].Reg0x30);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x41, coeff_div[coeff].Reg0x41);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x42, coeff_div[coeff].Reg0x42);
ret |= es8389_update_bits(codec, ES8389_DAC_CONTROL_REG0x43, 0x81, coeff_div[coeff].Reg0x43);
ret |= es8389_update_bits(codec, ES8389_CHIP_MISC_CONTROL_REG0xF0, 0x73, coeff_div[coeff].Reg0xF0);
ret |= es8389_write_reg(codec, ES8389_CSM_STATE_REG0xF1, coeff_div[coeff].Reg0xF1);
ret |= es8389_write_reg(codec, 0x16, coeff_div[coeff].Reg0x16);
ret |= es8389_write_reg(codec, 0x18, coeff_div[coeff].Reg0x18);
ret |= es8389_write_reg(codec, 0x19, coeff_div[coeff].Reg0x19);
}
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static int es8389_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_es8389_t *codec = (audio_codec_es8389_t *)h;
es8389_codec_cfg_t *codec_cfg = (es8389_codec_cfg_t *)cfg;
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(es8389_codec_cfg_t)) {
return ESP_CODEC_DEV_INVALID_ARG;
}
memcpy(&codec->cfg, cfg, sizeof(es8389_codec_cfg_t));
if (codec->cfg.mclk_div == 0) {
codec->cfg.mclk_div = MCLK_DEFAULT_DIV;
}
int ret = ESP_CODEC_DEV_OK;
ret |= es8389_write_reg(codec, ES8389_ISOLATION_CONTROL_REG0xF3, 0x00);
ret |= es8389_write_reg(codec, ES8389_RESET_REG0x00, 0x7E);
ret |= es8389_write_reg(codec, ES8389_ISOLATION_CONTROL_REG0xF3, 0x38);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x24, 0x64);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x25, (int)(0x04 + (0 << 6) + (0 << 5)));
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x45, (int)(0x03 + (0 << 6) + (0 << 5)));
ret |= es8389_write_reg(codec, ES8389_VMID_CONTROL_REG0x60, 0x2A);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x61, 0xC9);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x62, 0x4F);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x63, 0x06);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x6B, 0x00);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x6D, (int)(0x16 + (0 & 0xC0)));
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x6E, 0xAA);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x6F, 0x66);
ret |= es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x70, 0x99);
if (ES8389_Analog_DriveSel == ES8389_DriveSel_LowPower) {
es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x6B, 0x80);
es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x6C, 0x0F);
es8389_write_reg(codec, ES8389_ANALOG_CONTROL_REG0x70, 0x66);
}
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x23, (int)(0x00 + (0 & 0xC0) + (0 << 2) + (0 & 0x03)));
ret |= es8389_write_reg(codec, ES8389_PGA1_GAIN_CONTROL_REG0x72, (int)((1 << 4) + 0));
ret |= es8389_write_reg(codec, ES8389_PGA1_GAIN_CONTROL_REG0x73, (int)((1 << 4) + 0));
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x10, 0xC4);
ret |= es8389_write_reg(codec, ES8389_MISC_CONTROL_REG0x01, (int)(0x08 + (0 << 7) + (0 << 6) + (0 << 5) + (0 << 0)));
ret |= es8389_write_reg(codec, ES8389_CSM_STATE_REG0xF1, 0x00);
ret |= es8389_write_reg(codec, 0x12, 0x01);
ret |= es8389_write_reg(codec, 0x13, 0x01);
ret |= es8389_write_reg(codec, 0x14, 0x01);
ret |= es8389_write_reg(codec, 0x15, 0x01);
ret |= es8389_write_reg(codec, 0x16, 0x35);
ret |= es8389_write_reg(codec, 0x17, 0x09);
ret |= es8389_write_reg(codec, 0x18, 0x91);
ret |= es8389_write_reg(codec, 0x19, 0x28);
ret |= es8389_write_reg(codec, 0x1A, 0x01);
ret |= es8389_write_reg(codec, 0x1B, 0x01);
ret |= es8389_write_reg(codec, 0x1C, 0x11);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x2A, (int)(0x00 + (0 << 4)));
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x20, (int)(0x00 + ES8389_S16_LE + (0 << 4) + ES8389_DAIFMT_I2S + (0 << 1) + (0 << 0)));
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x40, (int)(0x00 + ES8389_S16_LE + (0 << 4) + ES8389_DAIFMT_I2S + (0 << 1) + (0 << 0)));
ret |= es8389_write_reg(codec, ES8389_CHIP_MISC_CONTROL_REG0xF0, (int)(0x1 + (0 << 3) + (0 << 2)));
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x02, (int)(0x00 + (0 << 6) + (0 << 1) + (0 << 0)));
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x04, 0x00);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x05, 0x10);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x06, 0x00);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x07, 0xC0);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x08, 0x00);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x09, 0xC0);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x0A, 0x80);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x0B, 4);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x0C, (int)(256 >> 8));
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x0D, (int)(256 & 0xFF));
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x0F, 0x10);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x21, 0x1F);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x22, 0x7F);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x2F, 0xC0);
ret |= es8389_write_reg(codec, 0x30, 0xF4);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x31, (int)(0x00 + (0 << 7) + (0 << 6)));
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x44, (int)(0x00 + (0 << 3) + (0 << 2) + (0 << 1) + (0 << 0)));
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x41, 0x7F);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x42, 0x7F);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x43, 0x10);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x49, (int)(0x0F + (0 << 4)));
ret |= es8389_write_reg(codec, 0x4C, 0xC0);
ret |= es8389_write_reg(codec, ES8389_RESET_REG0x00, 0x00);
ret |= es8389_write_reg(codec, ES8389_CLK_MANAGER_REG0x03, 0xC1);
ret |= es8389_write_reg(codec, ES8389_RESET_REG0x00, 0x01);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x4D, 0x02);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x26, 191);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x27, 191);
ret |= es8389_write_reg(codec, ES8389_ADC_SP_CONTROL_REG0x28, 191);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x46, 191);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x47, 191);
ret |= es8389_write_reg(codec, ES8389_DAC_CONTROL_REG0x48, (int)(95 << 1));
if (codec_cfg->master_mode) {
ESP_LOGI(TAG, "Work in Master mode");
es8389_update_bits(codec, ES8389_MISC_CONTROL_REG0x01, ES8389_MASK_MSModeSel, 1);
} else {
ESP_LOGI(TAG, "Work in Slave mode");
es8389_update_bits(codec, ES8389_MISC_CONTROL_REG0x01, ES8389_MASK_MSModeSel, 0);
}
// Select clock source for internal mclk
if (codec_cfg->use_mclk) {
es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x02, 0xC0, 0 << 6);
codec->use_mclk = true;
} else {
es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x02, 0xC0, 1 << 6);
codec->use_mclk = false;
}
// MCLK inverted or not
if (codec_cfg->invert_mclk) {
es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x02, 0x02, 1 << 1);
} else {
es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x02, 0x02, 0 << 1);
}
// Set ADC and DAC data format
if (codec_cfg->no_dac_ref == false) {
/* set internal reference signal (ADCL + DACR) */
ret |= es8389_write_reg(codec, ES8389_CHIP_MISC_CONTROL_REG0xF0, (int)(0x12 + (1 << 3) + (0 << 2)));
ESP_LOGI(TAG, "Set internal reference signal");
} else {
ret |= es8389_write_reg(codec, ES8389_CHIP_MISC_CONTROL_REG0xF0, (int)(0x12 + (0 << 3) + (0 << 2)));
}
// SCLK inverted or not
if (codec_cfg->invert_sclk) {
es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x02, 0x01, 1 << 0);
} else {
es8389_update_bits(codec, ES8389_CLK_MANAGER_REG0x02, 0x01, 0 << 0);
}
if (ret != 0) {
return ESP_CODEC_DEV_WRITE_FAIL;
}
es8389_pa_power(codec, ES_PA_SETUP | ES_PA_ENABLE);
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int es8389_close(const audio_codec_if_t *h)
{
audio_codec_es8389_t *codec = (audio_codec_es8389_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open) {
es8389_pa_power(codec, ES_PA_DISABLE);
es8389_suspend(codec);
codec->is_open = false;
}
return ESP_CODEC_DEV_OK;
}
static int es8389_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
{
audio_codec_es8389_t *codec = (audio_codec_es8389_t *) h;
if (codec == NULL || codec->is_open == false) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (!codec->use_mclk) {
es8389_config_sample(codec, fs->sample_rate,fs->bits_per_sample);
}
es8389_set_bits_per_sample(codec, fs->bits_per_sample);
es8389_config_fmt(codec, ES_I2S_NORMAL);
es8389_set_bias_standby(codec);
es8389_set_bias_on(codec);
return ESP_CODEC_DEV_OK;
}
static int es8389_enable(const audio_codec_if_t *h, bool enable)
{
int ret = ESP_CODEC_DEV_OK;
audio_codec_es8389_t *codec = (audio_codec_es8389_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (enable == codec->enabled) {
return ESP_CODEC_DEV_OK;
}
if (enable) {
ret = es8389_start(codec);
es8389_pa_power(codec, ES_PA_ENABLE);
} else {
es8389_pa_power(codec, ES_PA_DISABLE);
ret = es8389_suspend(codec);
}
if (ret == ESP_CODEC_DEV_OK) {
codec->enabled = enable;
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
}
return ret;
}
static int es8389_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_es8389_t *codec = (audio_codec_es8389_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8389_write_reg(codec, reg, value);
}
static int es8389_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_es8389_t *codec = (audio_codec_es8389_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return es8389_read_reg(codec, reg, value);
}
static void es8389_dump(const audio_codec_if_t *h)
{
audio_codec_es8389_t *codec = (audio_codec_es8389_t *) h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i < ES8389_MAX_REGISTER; i++) {
int value = 0;
int ret = es8389_read_reg(codec, i, &value);
if (ret != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, value);
}
}
const audio_codec_if_t *es8389_codec_new(es8389_codec_cfg_t *codec_cfg)
{
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_es8389_t *codec = (audio_codec_es8389_t *) calloc(1, sizeof(audio_codec_es8389_t));
if (codec == NULL) {
CODEC_MEM_CHECK(codec);
return NULL;
}
codec->base.open = es8389_open;
codec->base.enable = es8389_enable;
codec->base.set_fs = es8389_set_fs;
codec->base.set_vol = es8389_set_vol;
codec->base.set_mic_gain = es8389_set_mic_gain;
codec->base.mute = es8389_set_mute;
codec->base.set_reg = es8389_set_reg;
codec->base.get_reg = es8389_get_reg;
codec->base.dump_reg = es8389_dump;
codec->base.close = es8389_close;
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(es8389_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,192 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES8389_REG_H_
#define _ES8389_REG_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* ES8389_REGISTER NAME_REG_REGISTER ADDRESS
*/
#define ES8389_RESET_REG0x00 0x00
/*
* ES8389 MISC CONTROL ADDRESS
*/
#define ES8389_MISC_CONTROL_REG0x01 0x01
/*
* Clock Scheme Register definition
*/
#define ES8389_CLK_MANAGER_REG0x02 0x02
#define ES8389_CLK_MANAGER_REG0x03 0x03
#define ES8389_CLK_MANAGER_REG0x04 0x04
#define ES8389_CLK_MANAGER_REG0x05 0x05
#define ES8389_CLK_MANAGER_REG0x06 0x06
#define ES8389_CLK_MANAGER_REG0x07 0x07
#define ES8389_CLK_MANAGER_REG0x08 0x08
#define ES8389_CLK_MANAGER_REG0x09 0x09
#define ES8389_CLK_MANAGER_REG0x0A 0x0A
#define ES8389_CLK_MANAGER_REG0x0B 0x0B
#define ES8389_CLK_MANAGER_REG0x0C 0x0C
#define ES8389_CLK_MANAGER_REG0x0D 0x0D
#define ES8389_CLK_MANAGER_REG0x0E 0x0E
#define ES8389_CLK_MANAGER_REG0x0F 0x0F
#define ES8389_CLK_MANAGER_REG0x10 0x10
#define ES8389_CLK_MANAGER_REG0x11 0x11
/*
* ADC SP CONTROL
*/
#define ES8389_ADC_SP_CONTROL_REG0x20 0x20
#define ES8389_ADC_SP_CONTROL_REG0x21 0x21
#define ES8389_ADC_SP_CONTROL_REG0x22 0x22
#define ES8389_ADC_SP_CONTROL_REG0x23 0x23
#define ES8389_ADC_SP_CONTROL_REG0x24 0x24
#define ES8389_ADC_SP_CONTROL_REG0x25 0x25
#define ES8389_ADC_SP_CONTROL_REG0x26 0x26
#define ES8389_ADC_SP_CONTROL_REG0x27 0x27
#define ES8389_ADC_SP_CONTROL_REG0x28 0x28
#define ES8389_ADC_SP_CONTROL_REG0x2A 0x2A
#define ES8389_ADC_SP_CONTROL_REG0x2F 0x2F
#define ES8389_ADC_SP_CONTROL_REG0x31 0x31
/*
* ALC CONTROL
*/
#define ES8389_ALC_CONTROL_REG0x29 0x29
#define ES8389_ALC_CONTROL_REG0x2B 0x2B
#define ES8389_ALC_CONTROL_REG0x2C 0x2C
#define ES8389_ALC_CONTROL_REG0x2D 0x2D
/*
* DAC SERIAL PORT CONTROL
*/
#define ES8389_DAC_CONTROL_REG0x40 0x40
#define ES8389_DAC_CONTROL_REG0x41 0x41
#define ES8389_DAC_CONTROL_REG0x42 0x42
#define ES8389_DAC_CONTROL_REG0x43 0x43
#define ES8389_DAC_CONTROL_REG0x44 0x44
#define ES8389_DAC_CONTROL_REG0x45 0x45
#define ES8389_DAC_CONTROL_REG0x46 0x46
#define ES8389_DAC_CONTROL_REG0x47 0x47
#define ES8389_DAC_CONTROL_REG0x48 0x48
#define ES8389_DAC_CONTROL_REG0x49 0x49
#define ES8389_DAC_CONTROL_REG0x4D 0x4D
/*
* VMID CONTROL
*/
#define ES8389_VMID_CONTROL_REG0x60 0x60
/*
* ANALOG ENABLE CONTROL
*/
#define ES8389_ANALOG_CONTROL_REG0x61 0x61
#define ES8389_ANALOG_CONTROL_REG0x62 0x62
#define ES8389_ANALOG_CONTROL_REG0x63 0x63
#define ES8389_ANALOG_CONTROL_REG0x64 0x64
#define ES8389_ANALOG_CONTROL_REG0x69 0x69
#define ES8389_ANALOG_CONTROL_REG0x6B 0x6B
#define ES8389_ANALOG_CONTROL_REG0x6C 0x6C
#define ES8389_ANALOG_CONTROL_REG0x6D 0x6D
#define ES8389_ANALOG_CONTROL_REG0x6E 0x6E
#define ES8389_ANALOG_CONTROL_REG0x6F 0x6F
#define ES8389_ANALOG_CONTROL_REG0x70 0x70
#define ES8389_ANALOG_CONTROL_REG0x71 0x71
/*
* PGA1 GAIN CONTROL
*/
#define ES8389_PGA1_GAIN_CONTROL_REG0x72 0x72
#define ES8389_PGA1_GAIN_CONTROL_REG0x73 0x73
/*
* CHIP MISC CONTROL
*/
#define ES8389_CHIP_MISC_CONTROL_REG0xF0 0xF0
/*
* CSM STATE REPORT
*/
#define ES8389_CSM_STATE_REG0xF1 0xF1
/*
* PULL DOWN CONTROL
*/
#define ES8389_PULL_DOWN_CONTROL_REG0xF2 0xF2
/*
* ISOLATION CONTROL
*/
#define ES8389_ISOLATION_CONTROL_REG0xF3 0xF3
/*
* CSM STATE CONTROL
*/
#define ES8389_CSM_STATE_CONTROL_REG0xF4 0xF4
/*
* CHIP ID1 ID2
*/
#define ES8389_CHIP_ID1_CONTROL_REG0xFD 0xFD
#define ES8389_CHIP_ID2_CONTROL_REG0xFE 0xFE
#define ES8389_MAX_REGISTER 0xFF
/* ES8389 Data Format Definition */
#define ES8389_DAIFMT_I2S (0 << 2)
#define ES8389_DAIFMT_LEFT_J (1 << 2)
#define ES8389_DAIFMT_DSP_A (1 << 3)
#define ES8389_DAIFMT_DSP_B (3 << 3)
/* ES8389 Data length Definition */
#define ES8389_S24_LE (0 << 5)
#define ES8389_S20_LE (1 << 5)
#define ES8389_S18_LE (2 << 5)
#define ES8389_S16_LE (3 << 5)
#define ES8389_S32_LE (4 << 5)
/* ALSA call function used */
#define ES8389_MASK_MSModeSel (1 << 0)
#define ES8389_MASK_LRCKINV (1 << 4)
#define ES8389_MASK_SCLKINV (1 << 0)
#define ES8389_MASK_DATALEN (7 << 5)
#define ES8389_MASK_DAIFMT (7 << 2)
#define ES8389_DriveSel_Normal 0
#define ES8389_DriveSel_LowPower 1
#define ES8389_DriveSel_HeadPhone 2
#define ES8389_Analog_DriveSel ES8389_DriveSel_Normal /* Normal;LowPower;HeadPhone */
typedef enum {
ES8389_MIC_GAIN_MIN = -1,
ES8389_MIC_GAIN_0DB,
ES8389_MIC_GAIN_3_5DB,
ES8389_MIC_GAIN_6_5DB,
ES8389_MIC_GAIN_9_5DB,
ES8389_MIC_GAIN_12_5DB,
ES8389_MIC_GAIN_15_5DB,
ES8389_MIC_GAIN_18_5DB,
ES8389_MIC_GAIN_21_5DB,
ES8389_MIC_GAIN_24_5DB,
ES8389_MIC_GAIN_27_5DB,
ES8389_MIC_GAIN_30_5DB,
ES8389_MIC_GAIN_33_5DB,
ES8389_MIC_GAIN_36_5DB,
ES8389_MIC_GAIN_MAX
} es8389_mic_gain_t;
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _AW88298_DAC_H_
#define _AW88298_DAC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#include "esp_codec_dev_vol.h"
#ifdef __cplusplus
extern "C" {
#endif
#define AW88298_CODEC_DEFAULT_ADDR (0x36 << 1)
/**
* @brief AW88298 codec configuration
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
int16_t reset_pin; /*!< Reset pin */
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
} aw88298_codec_cfg_t;
/**
* @brief New AW88298 codec interface
* @attention Need set mclk_multiple to I2S_MCLK_MULTIPLE_384 in esp_codec_dev_sample_info_t to support 44100
* @param codec_cfg: AW88298 codec configuration
* @return NULL: Fail to new AW88298 codec interface
* -Others: AW88298 codec interface
*/
const audio_codec_if_t *aw88298_codec_new(aw88298_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _CJC8910_CODEC_H_
#define _CJC8910_CODEC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#include "esp_codec_dev_vol.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define CJC8910_CODEC_DEFAULT_ADDR (0x30)
/**
* @brief CJC8910 codec configuration
*
* @note This driver only supports codec work in slave mode
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
int16_t pa_pin; /*!< PA chip power pin */
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
bool invert_lr; /*!< Left/Right channel inverted or not */
bool invert_sclk; /*!< SCLK clock signal inverted or not */
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
} cjc8910_codec_cfg_t;
/**
* @brief New CJC8910 codec interface
*
* @param codec_cfg CJC8910 codec configuration
*
* @return
* - NULL Not enough memory or codec failed to open
* - Others CJC8910 codec interface
*/
const audio_codec_if_t *cjc8910_codec_new(cjc8910_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _CJC8910_CODEC_H_ */

View File

@@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES7210_ADC_H_
#define _ES7210_ADC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ES7210_CODEC_DEFAULT_ADDR (0x80)
#define ES7210_SEL_MIC1 (uint8_t)(1 << 0)
#define ES7210_SEL_MIC2 (uint8_t)(1 << 1)
#define ES7210_SEL_MIC3 (uint8_t)(1 << 2)
#define ES7210_SEL_MIC4 (uint8_t)(1 << 3)
/**
* @brief ES7210 MCLK clock source when work in master mode
*/
typedef enum {
ES7210_MCLK_FROM_PAD,
ES7210_MCLK_FROM_CLOCK_DOUBLER,
} es7210_mclk_src_t;
/**
* @brief ES7210 codec configuration, only support ADC feature
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
bool master_mode; /*!< Whether codec works as I2S master or not */
uint8_t mic_selected; /*!< Selected microphone */
es7210_mclk_src_t mclk_src; /*!< MCLK clock source in master mode */
uint16_t mclk_div; /*!< MCLK/LRCK default is 256 if not provided */
} es7210_codec_cfg_t;
/**
* @brief New ES7210 codec interface
* @param codec_cfg: ES7210 codec configuration
* @return NULL: Fail to new ES7210 codec interface
* -Others: ES7210 codec interface
*/
const audio_codec_if_t *es7210_codec_new(es7210_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES7243_ADC_H_
#define _ES7243_ADC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ES7243_CODEC_DEFAULT_ADDR (0x26)
/**
* @brief ES7243 codec configuration, only support ADC feature
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
} es7243_codec_cfg_t;
/**
* @brief New ES7243 codec interface
* Notes: this API should called after I2S clock ready
* Or else write register may fail
* @param codec_cfg: ES7243 codec configuration
* @return NULL: Fail to new ES7243 codec interface
* -Others: ES7243 codec interface
*/
const audio_codec_if_t *es7243_codec_new(es7243_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES7243E_ADC_H_
#define _ES7243E_ADC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ES7243E_CODEC_DEFAULT_ADDR (0x20)
/**
* @brief ES7243E codec configuration
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
} es7243e_codec_cfg_t;
/**
* @brief New ES7243E codec interface
* @param codec_cfg: ES7243E codec configuration
* @return NULL: Fail to new ES7243E codec interface
* -Others: ES7243E codec interface
*/
const audio_codec_if_t *es7243e_codec_new(es7243e_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES8156_DAC_H_
#define _ES8156_DAC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#include "esp_codec_dev_vol.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ES8156_CODEC_DEFAULT_ADDR (0x10)
/**
* @brief ES8156 codec configuration
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
int16_t pa_pin; /*!< PA chip power pin */
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
} es8156_codec_cfg_t;
/**
* @brief New ES8156 codec interface
* @param codec_cfg: ES8156 codec configuration
* @return NULL: Fail to new ES8156 codec interface
* -Others: ES8156 codec interface
*/
const audio_codec_if_t *es8156_codec_new(es8156_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES8311_CODEC_H_
#define _ES8311_CODEC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#include "esp_codec_dev_vol.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ES8311_CODEC_DEFAULT_ADDR (0x30)
/**
* @brief ES8311 codec configuration
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
int16_t pa_pin; /*!< PA chip power pin */
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
bool master_mode; /*!< Whether codec works as I2S master or not */
bool use_mclk; /*!< Whether use external MCLK clock */
bool digital_mic; /*!< Whether use digital microphone */
bool invert_mclk; /*!< MCLK clock signal inverted or not */
bool invert_sclk; /*!< SCLK clock signal inverted or not */
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
bool no_dac_ref; /*!< When record 2 channel data
false: right channel filled with dac output
true: right channel leave empty
*/
uint16_t mclk_div; /*!< MCLK/LRCK default is 256 if not provided */
} es8311_codec_cfg_t;
/**
* @brief New ES8311 codec interface
* @param codec_cfg: ES8311 codec configuration
* @return NULL: Fail to new ES8311 codec interface
* -Others: ES8311 codec interface
*/
const audio_codec_if_t *es8311_codec_new(es8311_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES8374_CODEC_H_
#define _ES8374_CODEC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ES8374_CODEC_DEFAULT_ADDR (0x20)
#define ES8374_CODEC_DEFAULT_ADDR_1 (0x21)
/**
* @brief ES8374 codec configuration
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
bool master_mode; /*!< Whether codec works as I2S master or not */
int16_t pa_pin; /*!< PA chip power pin */
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
} es8374_codec_cfg_t;
/**
* @brief New ES8374 codec interface
* @param codec_cfg: ES8374 codec configuration
* @return NULL: Fail to new ES8374 codec interface
* -Others: ES8374 codec interface
*/
const audio_codec_if_t *es8374_codec_new(es8374_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif //__ES8374_H__

View File

@@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES8388_CODEC_H_
#define _ES8388_CODEC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#include "esp_codec_dev_vol.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ES8388 default I2C address
*/
#define ES8388_CODEC_DEFAULT_ADDR (0x20)
#define ES8388_CODEC_DEFAULT_ADDR_1 (0x22)
/**
* @brief ES8388 codec configuration
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode on ADC or DAC */
bool master_mode; /*!< Whether codec works as I2S master or not */
int16_t pa_pin; /*!< PA chip power pin */
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
} es8388_codec_cfg_t;
/**
* @brief New ES8388 codec interface
* @param codec_cfg: ES8388 codec configuration
* @return NULL: Fail to new ES8388 codec interface
* -Others: ES8388 codec interface
*/
const audio_codec_if_t *es8388_codec_new(es8388_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES8389_CODEC_H_
#define _ES8389_CODEC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#include "esp_codec_dev_vol.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ES8389_CODEC_DEFAULT_ADDR (0x20)
/**
* @brief ES8389 codec configuration
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
int16_t pa_pin; /*!< PA chip power pin */
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
bool master_mode; /*!< Whether codec works as I2S master or not */
bool use_mclk; /*!< Whether use external MCLK clock */
bool digital_mic; /*!< Whether use digital microphone */
bool invert_mclk; /*!< MCLK clock signal inverted or not */
bool invert_sclk; /*!< SCLK clock signal inverted or not */
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
bool no_dac_ref; /*!< When record 2 channel data
false: right channel filled with dac output
true: right channel leave empty
*/
uint16_t mclk_div; /*!< MCLK/LRCK default is 256 if not provided */
} es8389_codec_cfg_t;
/**
* @brief New ES8389 codec interface
* @param codec_cfg: ES8389 codec configuration
* @return NULL: Fail to new ES8389 codec interface
* -Others: ES8389 codec interface
*/
const audio_codec_if_t *es8389_codec_new(es8389_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _TAS5805M_DAC_H_
#define _TAS5805M_DAC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#include "esp_codec_dev_vol.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TAS5805M_CODEC_DEFAULT_ADDR (0x5c)
/**
* @brief TAS5805M codec configuration
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
bool master_mode; /*!< Whether codec works as I2S master or not */
int16_t reset_pin; /*!< Reset pin */
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
} tas5805m_codec_cfg_t;
/**
* @brief New TAS5805M codec interface
* @param codec_cfg: TAS5805M codec configuration
* @return NULL: Fail to new TAS5805M codec interface
* -Others: TAS5805M codec interface
*/
const audio_codec_if_t *tas5805m_codec_new(tas5805m_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ZL38063_CODEC_H_
#define _ZL38063_CODEC_H_
#include "audio_codec_if.h"
#include "audio_codec_ctrl_if.h"
#include "audio_codec_gpio_if.h"
#include "esp_codec_dev_vol.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ZL38063 codec configuration
* Notes: ZL38063 codec driver provide default configuration of I2S settings in firmware.
* Defaults are 48khz, 16 bits, 2 channels
* To playback other sample rate need do resampling firstly
*/
typedef struct {
const audio_codec_ctrl_if_t *ctrl_if; /*!< Codec Control interface */
const audio_codec_gpio_if_t *gpio_if; /*!< Codec GPIO interface */
esp_codec_dec_work_mode_t codec_mode; /*!< Codec work mode: ADC or DAC */
int16_t pa_pin; /*!< PA chip power pin */
bool pa_reverted; /*!< false: enable PA when pin set to 1, true: enable PA when pin set to 0 */
int16_t reset_pin; /*!< Reset pin */
esp_codec_dev_hw_gain_t hw_gain; /*!< Hardware gain */
} zl38063_codec_cfg_t;
/**
* @brief New ZL38063 codec interface
* @param codec_cfg: ZL38063 codec configuration
* @return NULL: Fail to new ZL38063 codec interface
* -Others: ZL38063 codec interface
*/
const audio_codec_if_t *zl38063_codec_new(zl38063_codec_cfg_t *codec_cfg);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,183 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _ES_COMMON_H_
#define _ES_COMMON_H_
#include "esp_log.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CODEC_MEM_CHECK(ptr) \
if (ptr == NULL) { \
ESP_LOGE(TAG, "Fail to alloc memory at %s:%d", __FUNCTION__, __LINE__);\
}
#define BITS(n) (1 << n)
#define MCLK_DEFAULT_DIV (256)
typedef enum {
BIT_LENGTH_MIN = -1,
BIT_LENGTH_16BITS = 0x03,
BIT_LENGTH_18BITS = 0x02,
BIT_LENGTH_20BITS = 0x01,
BIT_LENGTH_24BITS = 0x00,
BIT_LENGTH_32BITS = 0x04,
BIT_LENGTH_MAX,
} es_bits_length_t;
typedef enum {
MCLK_DIV_MIN = -1,
MCLK_DIV_1 = 1,
MCLK_DIV_2 = 2,
MCLK_DIV_3 = 3,
MCLK_DIV_4 = 4,
MCLK_DIV_6 = 5,
MCLK_DIV_8 = 6,
MCLK_DIV_9 = 7,
MCLK_DIV_11 = 8,
MCLK_DIV_12 = 9,
MCLK_DIV_16 = 10,
MCLK_DIV_18 = 11,
MCLK_DIV_22 = 12,
MCLK_DIV_24 = 13,
MCLK_DIV_33 = 14,
MCLK_DIV_36 = 15,
MCLK_DIV_44 = 16,
MCLK_DIV_48 = 17,
MCLK_DIV_66 = 18,
MCLK_DIV_72 = 19,
MCLK_DIV_5 = 20,
MCLK_DIV_10 = 21,
MCLK_DIV_15 = 22,
MCLK_DIV_17 = 23,
MCLK_DIV_20 = 24,
MCLK_DIV_25 = 25,
MCLK_DIV_30 = 26,
MCLK_DIV_32 = 27,
MCLK_DIV_34 = 28,
MCLK_DIV_7 = 29,
MCLK_DIV_13 = 30,
MCLK_DIV_14 = 31,
MCLK_DIV_MAX,
} es_sclk_div_t;
typedef enum {
LCLK_DIV_MIN = -1,
LCLK_DIV_128 = 0,
LCLK_DIV_192 = 1,
LCLK_DIV_256 = 2,
LCLK_DIV_384 = 3,
LCLK_DIV_512 = 4,
LCLK_DIV_576 = 5,
LCLK_DIV_768 = 6,
LCLK_DIV_1024 = 7,
LCLK_DIV_1152 = 8,
LCLK_DIV_1408 = 9,
LCLK_DIV_1536 = 10,
LCLK_DIV_2112 = 11,
LCLK_DIV_2304 = 12,
LCLK_DIV_125 = 16,
LCLK_DIV_136 = 17,
LCLK_DIV_250 = 18,
LCLK_DIV_272 = 19,
LCLK_DIV_375 = 20,
LCLK_DIV_500 = 21,
LCLK_DIV_544 = 22,
LCLK_DIV_750 = 23,
LCLK_DIV_1000 = 24,
LCLK_DIV_1088 = 25,
LCLK_DIV_1496 = 26,
LCLK_DIV_1500 = 27,
LCLK_DIV_MAX,
} es_lclk_div_t;
typedef enum {
D2SE_PGA_GAIN_MIN = -1,
D2SE_PGA_GAIN_DIS = 0,
D2SE_PGA_GAIN_EN = 1,
D2SE_PGA_GAIN_MAX = 2,
} es_d2se_pga_t;
typedef enum {
ADC_INPUT_MIN = -1,
ADC_INPUT_LINPUT1_RINPUT1 = 0x00,
ADC_INPUT_MIC1 = 0x05,
ADC_INPUT_MIC2 = 0x06,
ADC_INPUT_LINPUT2_RINPUT2 = 0x50,
ADC_INPUT_DIFFERENCE = 0xf0,
ADC_INPUT_MAX,
} es_adc_input_t;
typedef enum {
DAC_OUTPUT_MIN = -1,
DAC_OUTPUT_LOUT1 = 0x04,
DAC_OUTPUT_LOUT2 = 0x08,
DAC_OUTPUT_SPK = 0x09,
DAC_OUTPUT_ROUT1 = 0x10,
DAC_OUTPUT_ROUT2 = 0x20,
DAC_OUTPUT_ALL = 0x3c,
DAC_OUTPUT_MAX,
} es_dac_output_t;
typedef enum {
MIC_GAIN_MIN = -1,
MIC_GAIN_0DB = 0,
MIC_GAIN_3DB = 3,
MIC_GAIN_6DB = 6,
MIC_GAIN_9DB = 9,
MIC_GAIN_12DB = 12,
MIC_GAIN_15DB = 15,
MIC_GAIN_18DB = 18,
MIC_GAIN_21DB = 21,
MIC_GAIN_24DB = 24,
MIC_GAIN_MAX,
} es_mic_gain_t;
typedef enum {
ES_MODULE_MIN = -1,
ES_MODULE_ADC = 0x01,
ES_MODULE_DAC = 0x02,
ES_MODULE_ADC_DAC = 0x03,
ES_MODULE_LINE = 0x04,
ES_MODULE_MAX
} es_module_t;
typedef enum {
ES_MODE_MIN = -1,
ES_MODE_SLAVE = 0x00,
ES_MODE_MASTER = 0x01,
ES_MODE_MAX,
} es_mode_t;
typedef enum {
ES_I2S_MIN = -1,
ES_I2S_NORMAL = 0,
ES_I2S_LEFT = 1,
ES_I2S_RIGHT = 2,
ES_I2S_DSP = 3,
ES_I2S_MAX
} es_i2s_fmt_t;
typedef struct {
es_sclk_div_t sclk_div; /*!< bits clock divide */
es_lclk_div_t lclk_div; /*!< WS clock divide */
} es_i2s_clock_t;
typedef enum {
ES_PA_SETUP = 1,
ES_PA_ENABLE = (1 << 1),
ES_PA_DISABLE = (1 << 2),
} es_pa_setting_t;
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,283 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"
#include "tas5805m_dac.h"
#include "tas5805m_reg.h"
#include "tas5805m_reg_cfg.h"
#include "esp_codec_dev_os.h"
#include "esp_codec_dev_vol.h"
#define TAG "TAS5805M"
typedef struct {
audio_codec_if_t base;
tas5805m_codec_cfg_t cfg;
bool is_open;
float hw_gain;
} audio_codec_tas5805m_t;
static const esp_codec_dev_vol_range_t vol_range = {
.min_vol =
{
.vol = 0xFE,
.db_value = -103.0,
},
.max_vol =
{
.vol = 0,
.db_value = 24.0,
},
};
static int tas5805m_write_reg(audio_codec_tas5805m_t *codec, int reg, int value)
{
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg, 1, &value, 1);
}
static int tas5805m_read_reg(audio_codec_tas5805m_t *codec, int reg, int *value)
{
*value = 0;
return codec->cfg.ctrl_if->read_reg(codec->cfg.ctrl_if, reg, 1, value, 1);
}
static int tas5805m_write_data(audio_codec_tas5805m_t *codec, uint8_t reg_addr, uint8_t *data, int size)
{
return codec->cfg.ctrl_if->write_reg(codec->cfg.ctrl_if, reg_addr, 1, data, size);
}
static int tas5805m_transmit_registers(audio_codec_tas5805m_t *codec, const tas5805m_cfg_reg_t *conf_buf, int size)
{
int i = 0;
int ret = 0;
while (i < size) {
switch (conf_buf[i].offset) {
case CFG_META_SWITCH:
// Used in legacy applications. Ignored here.
break;
case CFG_META_DELAY:
esp_codec_dev_sleep(conf_buf[i].value);
break;
case CFG_META_BURST:
ret = tas5805m_write_data(codec, conf_buf[i + 1].offset, (uint8_t *) (&conf_buf[i + 1].value),
conf_buf[i].value);
i += (conf_buf[i].value / 2) + 1;
break;
default:
ret = tas5805m_write_reg(codec, conf_buf[i].offset, conf_buf[i].value);
break;
}
i++;
}
if (ret != ESP_CODEC_DEV_OK) {
ESP_LOGE(TAG, "Fail to load configuration to tas5805m");
return ret;
}
return ret;
}
static int tas5805m_set_mute_fade(audio_codec_tas5805m_t *codec, int value)
{
int ret = 0;
uint8_t fade_reg = 0;
/* Time for register value
* 000: 11.5 ms
* 001: 53 ms
* 010: 106.5 ms
* 011: 266.5 ms
* 100: 0.535 sec
* 101: 1.065 sec
* 110: 2.665 sec
* 111: 5.33 sec
*/
if (value <= 12) {
fade_reg = 0;
} else if (value <= 53) {
fade_reg = 1;
} else if (value <= 107) {
fade_reg = 2;
} else if (value <= 267) {
fade_reg = 3;
} else if (value <= 535) {
fade_reg = 4;
} else if (value <= 1065) {
fade_reg = 5;
} else if (value <= 2665) {
fade_reg = 6;
} else {
fade_reg = 7;
}
fade_reg |= (fade_reg << 4);
ret |= tas5805m_write_reg(codec, MUTE_TIME_REG_ADDR, fade_reg);
ESP_LOGD(TAG, "Set mute fade, value:%d, reg:0x%x", value, fade_reg);
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static void tas5805m_reset(audio_codec_tas5805m_t *codec, int16_t reset_pin)
{
if (reset_pin <= 0 || codec->cfg.gpio_if == NULL) {
return;
}
codec->cfg.gpio_if->setup(reset_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
codec->cfg.gpio_if->set(reset_pin, 0);
esp_codec_dev_sleep(20);
codec->cfg.gpio_if->set(reset_pin, 1);
esp_codec_dev_sleep(200);
}
static int tas5805m_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
tas5805m_codec_cfg_t *codec_cfg = (tas5805m_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(tas5805m_codec_cfg_t)) {
return ESP_CODEC_DEV_INVALID_ARG;
}
memcpy(&codec->cfg, codec_cfg, sizeof(tas5805m_codec_cfg_t));
tas5805m_reset(codec, codec_cfg->reset_pin);
int ret = tas5805m_transmit_registers(codec, tas5805m_registers,
sizeof(tas5805m_registers) / sizeof(tas5805m_registers[0]));
if (ret != ESP_CODEC_DEV_OK) {
ESP_LOGE(TAG, "Fail write register group");
} else {
codec->is_open = true;
tas5805m_set_mute_fade(codec, 50);
}
return ret;
}
static int tas5805m_set_volume(const audio_codec_if_t *h, float db_value)
{
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
db_value -= codec->hw_gain;
int volume = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
ESP_LOGD(TAG, "Set volume reg:%x db:%f", volume, db_value);
return tas5805m_write_reg(codec, MASTER_VOL_REG_ADDR, volume);
}
int tas5805m_get_volume(audio_codec_tas5805m_t *codec, float *value)
{
/// FIXME: Got the digit volume is not right.
int vol_idx = 0;
int ret = tas5805m_read_reg(codec, MASTER_VOL_REG_ADDR, &vol_idx);
if (ret == ESP_CODEC_DEV_OK) {
*value = esp_codec_dev_vol_calc_db(&vol_range, vol_idx);
ESP_LOGD(TAG, "Volume is %fdb", *value);
return 0;
}
return ESP_CODEC_DEV_READ_FAIL;
}
static int tas5805m_set_mute(const audio_codec_if_t *h, bool enable)
{
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
int mute_reg = 0;
tas5805m_read_reg(codec, TAS5805M_REG_03, &mute_reg);
if (enable) {
mute_reg |= 0x8;
} else {
mute_reg &= (~0x08);
}
int ret = tas5805m_write_reg(codec, TAS5805M_REG_03, mute_reg);
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
static int tas5805m_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return tas5805m_write_reg(codec, reg, value);
}
static int tas5805m_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return tas5805m_read_reg(codec, reg, value);
}
static int tas5805m_close(const audio_codec_if_t *h)
{
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
codec->is_open = false;
return 0;
}
static void tas5805m_dump(const audio_codec_if_t *h)
{
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) h;
if (codec == NULL || codec->is_open == false) {
return;
}
for (int i = 0; i <= TAS5805M_REG_7F; i++) {
int value = 0;
if (tas5805m_read_reg(codec, i, &value) != ESP_CODEC_DEV_OK) {
break;
}
ESP_LOGI(TAG, "%02x: %02x", i, value);
}
}
const audio_codec_if_t *tas5805m_codec_new(tas5805m_codec_cfg_t *codec_cfg)
{
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_tas5805m_t *codec = (audio_codec_tas5805m_t *) calloc(1, sizeof(audio_codec_tas5805m_t));
if (codec == NULL) {
ESP_LOGE(TAG, "No memory for instance");
return NULL;
}
codec->base.open = tas5805m_open;
codec->base.set_vol = tas5805m_set_volume;
codec->base.mute = tas5805m_set_mute;
codec->base.set_reg = tas5805m_set_reg;
codec->base.get_reg = tas5805m_get_reg;
codec->base.close = tas5805m_close;
codec->base.dump_reg = tas5805m_dump;
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(tas5805m_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}

View File

@@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _TAS5805M_REG_H_
#define _TAS5805M_REG_H_
#ifdef __cplusplus
extern "C" {
#endif
#define TAS5805M_REG_00 0x00
#define TAS5805M_REG_02 0x02
#define TAS5805M_REG_03 0x03
#define TAS5805M_REG_24 0x24
#define TAS5805M_REG_25 0x25
#define TAS5805M_REG_26 0x26
#define TAS5805M_REG_27 0x27
#define TAS5805M_REG_28 0x28
#define TAS5805M_REG_29 0x29
#define TAS5805M_REG_2A 0x2a
#define TAS5805M_REG_2B 0x2b
#define TAS5805M_REG_35 0x35
#define TAS5805M_REG_7E 0x7e
#define TAS5805M_REG_7F 0x7f
#define TAS5805M_PAGE_00 0x00
#define TAS5805M_PAGE_2A 0x2a
#define TAS5805M_BOOK_00 0x00
#define TAS5805M_BOOK_8C 0x8c
#define MASTER_VOL_REG_ADDR 0X4C
#define MUTE_TIME_REG_ADDR 0X51
#define TAS5805M_DAMP_MODE_BTL 0x0
#define TAS5805M_DAMP_MODE_PBTL 0x04
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
MIT License
Copyright 2018 Microsemi Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,36 @@
The Source code for the Timberwolf device driver is partitioned into 4 folders.
\api_lib\
There are 5 files in this folder
The VprocTwolf_access.c/h -
is the user's space OS independent API to read/write specific register of the device to
reset the device into one of the 5 supported reset modes and to boot load a firmware/configuration record into the device.
The API must be used in conjunction with a low level device driver such as microsemi_spis_tw.c/h, VprocGal_HAL.c/h
vproc_common.c/h -
includes functions and variable declarations that are common to all Microsemi Voice processing devices.
These functions include the Vproc_msDelay(), VprocWait(). The Variables declarations include
the device status codes, and device reset modes enums. As well as multiple macros to enable debug mode.
the specific user_space hardware abstraction layer code for the ZL38040/05x/06x/08x Timberwolf devices.
\firmware\
This folder contains the firmware image , configuration record files and header file.
NOTE: The firmware of the development board and its corresponding configuration have been encapsulated into a static library.
\example_apps\
This folder contains example host applications for the zl38040/050/060/080 Microsemi devices.
zl38063.c/h -
a series of operations for zl38063, such as initialize, adjust the volume, and so on.
NOTE: You can get the latest firmware and related technical support by registering SDS account.
http://sds.microsemi.com/software.php.
ZLS38063 GUI software(MiTuner Lite GUI Software), ZLS38063 and ZLS38508LITE
http://sds.microsemi.com/software.php?view_type=listrev&id=103386.
firmware of ZL38063
http://sds.microsemi.com/software.php?view_type=listrev&id=104598.

View File

@@ -0,0 +1,10 @@
#ifndef TW_SPI_ACCESS_H
#define TW_SPI_ACCESS_H
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int tw_upload_dsp_firmware(int mode);
#endif

View File

@@ -0,0 +1,74 @@
/****************************************************************************
*
* vprocTwolf_access.h - Voice Processor devices high level access module function
* prototypes, variables
*
****************************************************************************
* Copyright Microsemi Inc, 2018. All rights reserved.
* Licensed under the MIT License. See LICENSE.txt in the project
* root for license information.
*
***************************************************************************/
#ifndef VPROC_TWOLFACCESS_H
#define VPROC_TWOLFACCESS_H
#include "vproc_common.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TWOLF_MAILBOX_SPINWAIT 1000 /*at least a 1000 to avoid mailbox busy */
/*device HBI command structure*/
typedef struct hbiCmdInfo {
unsigned char page;
unsigned char offset;
unsigned char numwords;
} hbiCmdInfo;
/* external function prototypes */
VprocStatusType VprocTwolfHbiInit(void); /*Use this function to initialize the HBI bus*/
VprocStatusType VprocTwolfHbiRead(unsigned short cmd, /*the 16-bit register to read from*/
unsigned char numwords, /* The number of 16-bit words to read*/
unsigned short *pData); /* Pointer to the read data buffer*/
VprocStatusType VprocTwolfHbiWrite(unsigned short cmd, /*the 16-bit register to write to*/
unsigned char numwords, /* The number of 16-bit words to write*/
unsigned short *pData); /*the words (0-255) to write*/
VprocStatusType TwolfHbiNoOp( /*send no-op command to the device*/
unsigned char numWords); /* The number of no-op (0-255) to write*/
/*An alternative method to loading the firmware into the device
* USe this method if you have used the provided tool to convert the *.s3 into
* c code that can be compiled with the application
*/
VprocStatusType
VprocTwolfHbiBoot_alt(/*use this function to boot load the firmware (*.c) from the host to the device RAM*/
twFirmware *st_firmware); /*Pointer to the firmware image in host RAM*/
VprocStatusType VprocTwolfLoadConfig(dataArr *pCr2Buf, unsigned short numElements);
VprocStatusType VprocTwolfHbiCleanup(void);
VprocStatusType VprocTwolfHbiBootPrepare(void);
VprocStatusType VprocTwolfHbiBootMoreData(char *dataBlock);
VprocStatusType VprocTwolfHbiBootConclude(void);
VprocStatusType VprocTwolfFirmwareStop(void); /*Use this function to halt the currently running firmware*/
VprocStatusType VprocTwolfFirmwareStart(void); /*Use this function to start/restart the firmware currently in RAM*/
VprocStatusType VprocTwolfSaveImgToFlash(void); /*Save current loaded firmware from device RAM to FLASH*/
VprocStatusType VprocTwolfSaveCfgToFlash(void); /*Save current device config from device RAM to FLASH*/
VprocStatusType VprocTwolfReset(VprocResetMode mode);
VprocStatusType VprocTwolfEraseFlash(void);
VprocStatusType VprocTwolfLoadFwrCfgFromFlash(uint16 image_number);
VprocStatusType VprocTwolfSetVolume(uint8 vol);
VprocStatusType VprocTwolfGetVolume(int8_t *vol);
VprocStatusType VprocTwolfGetAppStatus(uint16 *status);
#ifdef __cplusplus
}
#endif
#endif /* VPROCTWOLFACCESS_H */

View File

@@ -0,0 +1,85 @@
/****************************************************************************
* vproc_common.c - Hal functions for the VPROC API
*
*
****************************************************************************
* Copyright Microsemi Inc, 2018. All rights reserved.
* Licensed under the MIT License. See LICENSE.txt in the project
* root for license information.
*
***************************************************************************/
#include "vproc_common.h"
#include "esp_codec_dev_os.h"
#include "audio_codec_ctrl_if.h"
/*Note - These functions are PLATFORM SPECIFIC- They must be modified
* accordingly
**********************************************************************/
static audio_codec_ctrl_if_t *vproc_ctrl_if;
void VprocSetCtrlIf(void *ctrl_if)
{
vproc_ctrl_if = (audio_codec_ctrl_if_t *) ctrl_if;
}
static uint16_t convert_edian(uint16_t v)
{
return (v >> 8) | ((v & 0xFF) << 8);
}
void VprocHALcleanup(void)
{
}
int VprocHALInit(void)
{
if (vproc_ctrl_if) {
return 0;
}
return -1;
}
void Vproc_msDelay(unsigned short time)
{
esp_codec_dev_sleep(time);
}
/* VprocWait(): use this function to
* force a delay of specified time in resolution of 125 micro-Seconds
*
* Input Argument: time in unsigned 32-bit
* Return: none
*/
void VprocWait(unsigned long int time)
{
esp_codec_dev_sleep(time);
}
/* This is the platform dependent low level spi
* function to write 16-bit data to the ZL380xx device
*/
int VprocHALWrite(unsigned short val)
{
int ret = 0;
if (vproc_ctrl_if) {
val = convert_edian(val);
ret = vproc_ctrl_if->write_reg(vproc_ctrl_if, 0, 0, &val, sizeof(val));
}
return ret;
}
/* This is the platform dependent low level spi
* function to read 16-bit data from the ZL380xx device
*/
int VprocHALRead(unsigned short *pVal)
{
unsigned short data = 0;
int ret = 0;
if (vproc_ctrl_if) {
ret = vproc_ctrl_if->read_reg(vproc_ctrl_if, 0, 0, &data, sizeof(data));
*pVal = convert_edian(data);
}
return ret;
}

View File

@@ -0,0 +1,124 @@
/****************************************************************************
* vproc_common.h - Hal functions prototypes, macros and variables for the VPROC API
*
*
****************************************************************************
* Copyright Microsemi Inc, 2018. All rights reserved.
* Licensed under the MIT License. See LICENSE.txt in the project
* root for license information.
*
***************************************************************************/
#ifndef VPROC_COMMON_H
#define VPROC_COMMON_H
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "esp_log.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DEBUG_LOGD ESP_LOGD
#define DEBUG_LOGE ESP_LOGE
#define DEBUG_LOGI ESP_LOGI
/*This header includes some platform dependent data types*/
#include "vproc_data_types.h"
//#define RETRY_COUNT 100
#define VPROC_TIMEOUT 500
#define TAG_SPI "SPI"
/* external defines */
#undef VPROC_DEBUG
/*create a 16-bit word out of two bytes*/
#define MAKE16(a, b) (unsigned short) (((unsigned short) (b) << 8) | (unsigned short) (a))
/*create a 32-bit word out of 4 bytes*/
#define MAKE32(a, b, c, d) \
(unsigned long) (((unsigned long) d << 24) | ((unsigned long) c << 16) | ((unsigned long) b << 8) | \
((unsigned long) a))
/*
* debug - print the function name and line number for the source of the error
* the line number count start at 1 and not 0
*/
/*
*Define this macro to report mode debug info
*/
#undef VPROC_API_DBG_INFO
#ifdef VPROC_API_DBG_INFO
#define VPROG_DBG_INFO(s, args...) printf(""s, ##args);
#else
#define VPROG_DBG_INFO(s, args...)
#endif
#define VPROC_API_DBG_ERROR
#ifdef VPROC_API_DBG_ERROR
#define VPROG_DBG_ERROR(s, args...) printf("---%s %d: "s, __func__, __LINE__, ##args);
#else
#define VPROG_DBG_ERROR(s, args...)
#endif
/*unsigned char deviceType;*/
/*device/access Status codes*/
typedef enum VprocStatusType {
VPROC_STATUS_SUCCESS = 0,
VPROC_STATUS_FAILURE,
VPROC_STATUS_INIT_FAILED,
VPROC_STATUS_WR_FAILED,
VPROC_STATUS_RD_FAILED,
VPROC_STATUS_FW_LOAD_FAILED,
VPROC_STATUS_CFG_LOAD_FAILED,
VPROC_STATUS_CLOSE_FAILED,
VPROC_STATUS_FW_SAVE_FAILED,
VPROC_STATUS_GFG_SAVE_FAILED,
VPROC_STATUS_MAU_NOT_READY,
VPROC_STATUS_CHK_FAILED,
VPROC_STATUS_FUNC_NOT_SUPPORTED,
VPROC_STATUS_INVALID_ARG,
VPROC_STATUS_ERR_VTD_CODE,
VPROC_STATUS_ERR_VERIFY,
VPROC_STATUS_DEVICE_BUSY,
VPROC_STATUS_ERR_HBI,
VPROC_STATUS_ERR_IMAGE,
VPROC_STATUS_MAILBOX_BUSY,
VPROC_STATUS_CMDREG_BUSY,
VPROC_STATUS_IN_CRTCL_SECTN,
VPROC_STATUS_BOOT_LOADING_MORE_DATA,
VPROC_STATUS_BOOT_LOADING_CMP,
VPROC_STATUS_DEV_NOT_INITIALIZED,
} VprocStatusType;
/* Device Reset modes*/
typedef enum VprocResetMode {
VPROC_RST_HARDWARE_ROM = 0, /*hardware reset -reset the device and reload the firmware from flash*/
VPROC_RST_HARDWARE_RAM = 1, /*hardware reset -reset the device and reload the firmware from RAM*/
VPROC_RST_SOFTWARE = 2,
VPROC_RST_AEC = 3, /*software reset -reset and runs the firmware from RAM*/
VPROC_RST_BOOT = 4
} VprocResetMode;
typedef enum vProcDeviceType {
VPROC_DEV_GALILEO = 1, /*Galileo devices: ZL38004, ZL38012, ZL38005*/
VPROC_DEV_TIMBERWOLF = 2 /*Timberwolf: ZL38040*/
} VprocDeviceType;
extern void VprocSetCtrlIf(void *ctrl_if);
extern void VprocHALcleanup(void);
extern int VprocHALInit(void);
extern void Vproc_msDelay(unsigned short time);
extern void VprocWait(unsigned long int time);
extern int VprocHALWrite(unsigned short val);
extern int VprocHALRead(unsigned short *pVal);
#ifdef __cplusplus
}
#endif
#endif /* VPROC_COMMON_H */

View File

@@ -0,0 +1,121 @@
/** \file vproc_data_types.h
* vproc_data_types.h
*
* This file is the header for all standard types used in the API code.
*
****************************************************************************
* Copyright Microsemi Inc, 2018. All rights reserved.
* Licensed under the MIT License. See LICENSE.txt in the project
* root for license information.
*
***************************************************************************/
#ifndef VP_API_TYPES_H
#define VP_API_TYPES_H
/* For maximum that can be stored in an int - if file exists in library */
#include "limits.h"
#include "esp_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef NULL
#define NULL (0)
#endif
#ifdef EXTERN
#undef EXTERN
#error EXTERN was redefined!
#endif /* undef EXTERN */
#ifdef __cplusplus
#define EXTERN extern "C"
#else
#define EXTERN extern
#endif /* __cplusplus */
/********************* DECLARATIONS ***************************/
/* Constants */
#define FALSE (0) /* Boolean constant */
#define TRUE (1) /* Boolean constant */
#ifndef __cplusplus
/* C++ language provides a boolean data type; So no need to define
* one more data type; Make use of it
* NOTE: The 'C' potions of the VP-API assume C++ "bool" to be of the
* same size as that of "char". Please make sure this assumption is correct.
*/
// typedef unsigned char bool;
#endif /* __cplusplus */
/****************** typedefs ***********************************/
/* These are the basic number types used */
/* for uint8, uint16, uint32, int8, int16, int32, bool */
// PLATFORM SPECIFIC DEFINITIONS
typedef unsigned char uchar;
typedef signed char int8;
typedef unsigned char UCharT; // 8 bits unsigned - PLATFORM SPECIFIC
typedef unsigned char UInt8T; // 8 bits unsigned - PLATFORM SPECIFIC
typedef unsigned short UInt16T; // 16 bits unsigned - PLATFORM SPECIFIC
typedef unsigned long UInt32T; // 32 bits unsigned - PLATFORM SPECIFIC
typedef signed long Int32T; // 32 bits signed - PLATFORM SPECIFIC
typedef unsigned char uint8; // 8 bits unsigned - PLATFORM SPECIFIC
typedef unsigned short uint16; // 16 bits unsigned - PLATFORM SPECIFIC
typedef uint8 *uint8p; // pointer to 8 bits unsigned - PLATFORM SPECIFIC
typedef uint16 *uint16p; // pointer to 16 bits unsigned - PLATFORM SPECIFIC
typedef uint32_t uint32; // 32 bits unsigned - PLATFORM SPECIFIC
typedef signed short int16; // 32 bits unsigned - PLATFORM SPECIFIC
typedef uint32 *uint32p;
typedef int8 *int8p;
typedef int16 *int16p;
typedef Int32T *int32p;
/* external types */
/* Some compilers optimize the size of enumeration data types based on
* the maximum data value assigned to the members of that data type.
* 'Standard C' requires enumeration data types to be of the same size
* as that of native 'int' implementation.
* The VP-API from a portability persepective adds a 'dummy' member to
* all enumeration data types that force the compilers to allocate the size
* of enumeration data types to be equal to that of native 'int'
* implementation */
#define FORCE_STANDARD_C_ENUM_SIZE (INT_MAX)
/* Eliminate error messages that occur when comparing an enumeration constant
< 0 */
#define FORCE_SIGNED_ENUM (INT_MIN)
/* Define any API specific basic data type ranges (that are necessary) */
#define VP_INT16_MAX (SHRT_MAX)
#define VP_INT16_MIN (SHRT_MIN)
#define VP_INT32_MAX (LONG_MAX)
#define VP_INT32_MIN (LONG_MIN)
/*firmware data structures*/
typedef struct {
uint16 buf[16]; /*the firmware data block to send to the device*/
uint16 numWords; /*the number of words within the block of data stored in buf[]*/
uint32 targetAddr; /*the target base address to write to register 0x00c of the device*/
uint8 useTargetAddr; /*this value is either 0 or 1. When 1 the tarGetAddr must be written to the device*/
} twFwr;
typedef struct {
twFwr *st_Fwr;
uint32 byteCount; /*The total number of bytes within the firmware - NOT USED*/
uint8 havePrgmBase;
uint32 prgmBase;
uint32 execAddr; /*The execution start address of the firmware in RAM*/
uint16 twFirmwareStreamLen; /*The number of blocks within the firmware*/
} twFirmware;
/*config record structures*/
typedef struct {
uint16 reg; /*the register */
uint16 value; /*the value to write into reg */
} dataArr;
#ifdef __cplusplus
}
#endif
#endif /* VP_API_TYPES_H */

View File

@@ -0,0 +1,299 @@
/****************************************************************************
* tw_hal_verify.c - Read/write registers of the device and verify whether the
* device is accessed properly
*
*
****************************************************************************
* Copyright Microsemi Inc, 2018. All rights reserved.
* Licensed under the MIT License. See LICENSE.txt in the project
* root for license information.
*
***************************************************************************/
#include <stdio.h>
#include <stdlib.h> /* malloc, free, rand */
#include "esp_log.h"
#include "vproc_common.h"
#include "zl38063_config.h"
#include "vprocTwolf_access.h"
#include "zl38063_firmware.h"
/*NOTE: notice that the *.c code are included in the apps-
* This is because the compiler I'm using requires that
* But if your makefile is such that compiler knows where to find these files
* then remove the #include *.c below
*/
#undef SAVE_IMAGE_TO_FLASH /*define this macro to save the firmware from RAM to flash*/
#undef SAVE_CFG_TO_FLASH /*define this macro to save the cfg from RAM to flash*/
/*quick test*/
#define TW_HAL_VERIFY_DEBUG
#define MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST 125
static const char *TAG = "TW_HAL_VERIFY";
/*LoadFwrConfig_Alt - to load a converted *s3, *cr2 to c code into the device.
* Basically instead of loading the *.s3, *cr2 directly,
* use the tw_convert tool to convert the ascii hex fwr mage into code and compile
* with the application
*
* input arg: mode: 0 - load both firmware and confing
* 1 - load firmware only
* 2 - load config only
*/
VprocStatusType LoadFwrConfig_Alt(uint8 mode)
{
VprocStatusType status = VPROC_STATUS_SUCCESS;
if ((mode == 0) || (mode == 1)) {
twFirmware st_Firmware;
st_Firmware.st_Fwr = (twFwr *) st_twFirmware;
st_Firmware.twFirmwareStreamLen = (uint16) firmwareStreamLen;
st_Firmware.execAddr = (uint32) executionAddress;
st_Firmware.havePrgmBase = (uint8) haveProgramBaseAddress;
st_Firmware.prgmBase = (uint32) programBaseAddress;
ESP_LOGD(TAG, "Firmware boot loading started ....");
status = VprocTwolfHbiBoot_alt(&st_Firmware);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiBoot()", status);
return -1;
}
ESP_LOGD(TAG, "Loading the image to RAM....done");
#ifdef SAVE_IMAGE_TO_FLASH
ESP_LOGD(TAG, "Saving firmware to flash....");
status = VprocTwolfSaveImgToFlash();
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfSaveImgToFlash()", status);
return status;
}
ESP_LOGD(TAG, "Saving firmware to flash....done");
#endif
status = VprocTwolfFirmwareStart();
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfFirmwareStart()", status);
return status;
}
}
if ((mode == 0) || (mode == 2)) {
ESP_LOGD(TAG, "Loading the config file into the device RAM....");
status = VprocTwolfLoadConfig((dataArr *) st_twConfig, (uint16) configStreamLen);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfLoadConfig()", status);
return status;
}
#ifdef SAVE_CFG_TO_FLASH
ESP_LOGD(TAG, "Saving config to flash....");
status = VprocTwolfSaveCfgToFlash();
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfSaveCfgToFlash()", status);
return status;
}
ESP_LOGD(TAG, "Saving config to flash....done");
#endif
}
{ /*Verify that the boot loading PASS or Fail*/
uint16 val = 0;
status = VprocTwolfHbiRead(0x0022, 1, &val);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
VprocTwolfHbiCleanup();
return -1;
}
if ((val == 38040) || (val == 38050) || (val == 38060) || (val == 38080) || (val == 38051) || (val == 38041)) {
ESP_LOGD(TAG, "Device boot loading completed successfully...");
} else {
ESP_LOGD(TAG, "Device boot loading failed!!!...");
return VPROC_STATUS_FAILURE;
}
}
/*Firmware reset - in order for the configuration to take effect
* NOTE: The ZL38040 needs a soft reset for the uploaded configuration
* to take effect. This soft-reset is sent below
* if the ZL38040 is an I2S slave, if the I2S master is not stable
* at the time of this reset, then that reset will not take effect.
* In that case the host has to to simply resend the reset
* command once the I2S master
* is up and running and is at a stable state.
*/
status = VprocTwolfReset(VPROC_RST_SOFTWARE);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfReset()", status);
return status;
}
ESP_LOGD(TAG, "Device boot loading completed successfully...");
return status;
}
int test_zl38063(void *arg)
{
int status = 0;
uint16 cmdword = 0;
uint16 val[MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST];
uint8 numwords = 0;
uint16 tempbuf[MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST];
uint16 i = 0;
#ifdef TW_HAL_VERIFY_DEBUG
uint16 j = 0;
#endif
status = VprocTwolfHbiInit();
if (status < 0) {
perror("tw_spi_access open");
return -1;
}
if ((MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST > 125) || (MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST < 2)) {
ESP_LOGD(TAG, "MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST must between 2 and 126");
}
memset(val, 0, sizeof(val));
memset(tempbuf, 0, sizeof(tempbuf));
ESP_LOGD(TAG, "Test 1 - Verifying that the device is present and working ....");
cmdword = 0x00C;
numwords = 2;
val[0] = 0x1234;
val[1] = 0x5678;
status = VprocTwolfHbiWrite(cmdword, numwords, val);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiWrite()\n", status);
VprocHALcleanup();
return -1;
}
#ifdef TW_HAL_VERIFY_DEBUG
j = 0;
for (i = 0; i < numwords; i++) {
ESP_LOGD(TAG, "wr: addr 0x%04x = 0x%04x", (cmdword + j), val[i]);
j = j + 2;
}
#endif
status = VprocTwolfHbiRead(cmdword, numwords, tempbuf);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
VprocTwolfHbiCleanup();
return -1;
}
#ifdef TW_HAL_VERIFY_DEBUG
j = 0;
for (i = 0; i < numwords; i++) {
ESP_LOGD(TAG, "RD: addr 0x%04x = 0x%04x", (cmdword + j), tempbuf[i]);
j = j + 2;
}
#endif
if ((tempbuf[0] != 0x1234) && (tempbuf[1] != 0x5600)) {
ESP_LOGD(TAG, "Test 1 - completed - FAIL!!!");
return -1;
}
ESP_LOGD(TAG, "Test 1 - completed - PASS\n\n");
status = VprocTwolfReset(0);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
VprocTwolfHbiCleanup();
return -1;
}
ESP_LOGD(TAG, "Device reset completed successfully...");
ESP_LOGD(TAG, "Test 2 - Verifying single word write/read access ....");
cmdword = 0x0300;
val[0] = 0x4008;
numwords = 1;
status = VprocTwolfHbiWrite(cmdword, numwords, val);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiWrite()", status);
VprocTwolfHbiCleanup();
return -1;
}
#ifdef TW_HAL_VERIFY_DEBUG
j = 0;
for (i = 0; i < numwords; i++) {
ESP_LOGD(TAG, "wr: addr 0x%04x = 0x%04x", (cmdword + j), val[i]);
j = j + 2;
}
#endif
status = VprocTwolfHbiRead(cmdword, numwords, val);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
VprocTwolfHbiCleanup();
return -1;
}
#ifdef TW_HAL_VERIFY_DEBUG
j = 0;
for (i = 0; i < numwords; i++) {
ESP_LOGD(TAG, "RD: addr 0x%04x = 0x%04x\n", (cmdword + j), val[i]);
j = j + 2;
}
#endif
if ((val[0] != 0x4008)) {
ESP_LOGD(TAG, "Test 2 - completed - FAIL!!!");
return -1;
}
ESP_LOGD(TAG, "Test 2 - completed - PASS");
ESP_LOGD(TAG, "Test 3 - Verifying multiple words write/read access ....");
/* Fill the data buffer with unique data values. */
for (i = 0; i < MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST; i++) {
val[i] = i | ((0xFF - i) << 8);
}
cmdword = 0x0300;
numwords = MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST;
status = VprocTwolfHbiWrite(cmdword, numwords, val);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiWrite()", status);
VprocTwolfHbiCleanup();
return -1;
}
#ifdef TW_HAL_VERIFY_DEBUG
j = 0;
for (i = 0; i < numwords; i++) {
ESP_LOGD(TAG, "twr: addr 0x%04x = 0x%04x", (cmdword + j), val[i]);
j = j + 2;
}
#endif
status = VprocTwolfHbiRead(cmdword, numwords, tempbuf);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiRead()", status);
VprocTwolfHbiCleanup();
return -1;
}
#ifdef TW_HAL_VERIFY_DEBUG
j = 0;
for (i = 0; i < numwords; i++) {
ESP_LOGD(TAG, "RD: addr 0x%04x = 0x%04x =? 0x%04x", (cmdword + j), tempbuf[i], val[i]);
j = j + 2;
}
#endif
j = 0;
for (i = 0; i < MAX_WORDS_FOR_MULTIWORD_ACCESS_TEST; i++) {
if (tempbuf[i] != val[i]) {
ESP_LOGD(TAG, "RD: addr 0x%04x = 0x%04x =? 0x%04x", (cmdword + j), tempbuf[i], val[i]);
ESP_LOGD(TAG, "Test 3 - completed - FAIL!!!");
return -1;
}
j = j + 2;
}
ESP_LOGD(TAG, "Test 3 - completed - PASS");
ESP_LOGD(TAG, "Test 4 - Verifying the firmware/config boot loading ....");
if (LoadFwrConfig_Alt(0) != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Device boot loading failed.....");
ESP_LOGD(TAG, "Test 4 - completed - FAIL!!!");
} else
ESP_LOGD(TAG, "Test 4 - completed - PASS");
VprocTwolfHbiCleanup();
return 0;
}

View File

@@ -0,0 +1,161 @@
/****************************************************************************
* tw_ldfwcfg.c - To load a *.s3 firmware and/or a *.cr2 into the device
* and optionally save the loaded image to flash
*
*
****************************************************************************
* Copyright Microsemi Inc, 2018. All rights reserved.
* Licensed under the MIT License. See LICENSE.txt in the project
* root for license information.
*
***************************************************************************/
#include <stdio.h>
#include <stdlib.h> /* malloc, free, rand */
#include "esp_log.h"
#include "vproc_common.h"
#include "vprocTwolf_access.h"
/*NOTE: notice that the *.c code are included in the apps-
* This is because the compiler I'm using requires that
* But if your makefile is such that compiler knows where to find these files
* then remove the #include *.c below
*/
#undef SAVE_CFG_TO_FLASH /*define this macro to save the cfg from RAM to flash*/
static const char *TAG = "TW_LDCFG";
uint16 numElements;
dataArr *pCr2Buf;
/* fseekNunlines() -- The firmware file is an ascii text file.
* the information from fseek will not be useful.
* this is our own fseek equivalent
*/
static unsigned long fseekNunlines(FILE *BOOT_FD)
{
uint32 line_count = 0;
int c;
while ((c = fgetc(BOOT_FD)) != EOF) {
if (c == '\n')
line_count++;
}
return line_count;
}
/* readCfgFile() use this function to
* Read the Voice processing cr2 config file into RAM
* filepath -- pointer to the location where to find the file
* pCr2Buf -- the actual firmware data array will be pointed to this buffer
*/
static int readCfgFile(char *filepath)
{
unsigned int reg[2], val[2], len;
uint8 done = 0;
uint16 index = 0;
FILE *BOOT_FD;
char *s;
char line[512] = "";
BOOT_FD = fopen(filepath, "rb");
if (BOOT_FD != NULL) {
len = fseekNunlines(BOOT_FD);
if (len <= 0) {
ESP_LOGD(TAG, "Error: file is not of the correct format...");
return -1;
}
ESP_LOGD(TAG, "fileLength = %u", len);
/*start at the beginning of the file*/
// fseek(BOOT_FD, 0, SEEK_SET);
/* allocate memory to contain the reg and val:*/
pCr2Buf = (dataArr *) malloc(len * sizeof(dataArr));
if (pCr2Buf == NULL) {
ESP_LOGD(TAG, "not enough memory to allocate %u bytes.. ", len * sizeof(dataArr));
return -1;
}
rewind(BOOT_FD);
/*read and format the data accordingly*/
numElements = 0;
do {
s = fgets(line, 512, BOOT_FD);
if (line[0] == ';') {
continue;
} else if (s != NULL) {
numElements++;
sscanf(line, "%x %c %x", reg, s, val);
pCr2Buf[index].reg = reg[0];
pCr2Buf[index].value = val[0];
// ESP_LOGD(TAG,"pCr2Buf[%d].reg pCr2Buf[%d].value = 0x%04x\t0x%04x\n", index, index,
// pCr2Buf[index].reg, pCr2Buf[index].value);
index++;
} else {
done = 1;
}
} while (done == 0);
fclose(BOOT_FD);
ESP_LOGD(TAG, "size of pCr2Buf = %u bytes.. ", sizeof(pCr2Buf));
} else {
ESP_LOGD(TAG, "Error: can't open file");
}
return 0;
}
/*This example host app load the *.s3 firmware to the device RAM. Optionally save it to flash
* Then start the firmware from the execution address in RAM
* It then stops the firmware - Load the cr2 file into RAM. Optionally save it to flash
* Then restarts the firmware
*/
int main(int argc, char **argv)
{
VprocStatusType status = VPROC_STATUS_SUCCESS;
if (argc != 2) {
ESP_LOGD(TAG, "Error: argc = %d - missing %d arg(s)... ", argc, 3 - (argc - 1));
ESP_LOGD(TAG, "command Usage:%s ConfigPath", argv[0]);
exit(1);
}
ESP_LOGD(TAG, ":%s %s %s", argv[0], argv[1], argv[2]);
/*global file handle*/
status = VprocTwolfHbiInit();
if (status < 0) {
perror("tw_spi_access open");
return -1;
}
if (readCfgFile(argv[1]) < 0) {
ESP_LOGD(TAG, "Error:read %s file", argv[1]);
}
ESP_LOGD(TAG, "a- Reading config file to host RAM - done....");
ESP_LOGD(TAG, "c- Loading the config file into the device RAM");
status = VprocTwolfLoadConfig(pCr2Buf, numElements);
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfLoadConfig()", status);
VprocTwolfHbiCleanup();
return -1;
}
#ifdef SAVE_CONFIG_TO_FLASH
status = VprocTwolfSaveCfgToFlash();
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfSaveCfgToFlash()", status);
VprocTwolfHbiCleanup();
return -1;
}
ESP_LOGD(TAG, "d- Saving config to flash- done....");
#endif
ESP_LOGD(TAG, "e- Loading config record - done....");
free(pCr2Buf);
pCr2Buf = NULL;
VprocTwolfHbiCleanup();
return 0;
}

View File

@@ -0,0 +1,117 @@
/****************************************************************************
* tw_ldfw.c - To load a *.s3 firmware into the device
* and optionally save the loaded image to flash
*
*
****************************************************************************
* Copyright Microsemi Inc, 2018. All rights reserved.
* Licensed under the MIT License. See LICENSE.txt in the project
* root for license information.
*
***************************************************************************/
#include "vproc_common.h"
#include "vprocTwolf_access.h"
#include "esp_log.h"
/*NOTE: notice that the *.c code are included in the apps-
* This is because the compiler I'm using requires that
* But if your makefile is such that compiler knows where to find these files
* then remove the #include *.c below
*/
//#undef SAVE_IMAGE_TO_FLASH /*define this macro to save the firmware from RAM to flash*/
static const char *TAG = "TW_LDFW";
/*quick test*/
/*This example host app load the *.s3 firmware to the device RAM. Optionally save it to flash
* Then start the firmware from the execution address in RAM
*/
int main(int argc, char **argv)
{
VprocStatusType status = VPROC_STATUS_SUCCESS;
FILE *BOOT_FD;
char line[256] = "";
if (argc != 2) {
ESP_LOGD(TAG, "Error: argc = %d - missing %d arg(s)... ", argc, 3 - (argc - 1));
ESP_LOGD(TAG, "command Usage:%s firmwarePath", argv[0]);
exit(1);
}
ESP_LOGD(TAG, ":%s %s %s", argv[0], argv[1], argv[2]);
BOOT_FD = fopen(argv[1], "rb");
if (BOOT_FD == NULL) {
ESP_LOGD(TAG, "Error: can't open file %s", argv[1]);
return -1;
}
/*global file handle*/
status = VprocTwolfHbiInit();
if (status < 0) {
perror("tw_spi_access open");
fclose(BOOT_FD);
return -1;
}
ESP_LOGD(TAG, "1- Opening firmware file - done....");
status = VprocTwolfHbiBootPrepare();
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiBootPrepare()", status);
fclose(BOOT_FD);
VprocHALcleanup();
return -1;
}
ESP_LOGD(TAG, "-- Boot prepare - done....");
while (fgets(line, 256, BOOT_FD) != NULL) {
status = VprocTwolfHbiBootMoreData(line);
if (status == VPROC_STATUS_BOOT_LOADING_MORE_DATA) {
continue;
} else if (status == VPROC_STATUS_BOOT_LOADING_CMP) {
break;
} else if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiBootMoreData()", status);
fclose(BOOT_FD);
VprocHALcleanup();
return -1;
}
}
ESP_LOGD(TAG, "-- Firmware data transfer - done....");
fclose(BOOT_FD);
/*clean up and verify that the boodloading completed correctly*/
status = VprocTwolfHbiBootConclude();
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfHbiBootConclude()", status);
VprocHALcleanup();
return -1;
}
ESP_LOGD(TAG, "2- Loading firmware - done....");
#ifdef SAVE_IMAGE_TO_FLASH
ESP_LOGD(TAG, "-- Saving firmware to flash....");
status = VprocTwolfSaveImgToFlash();
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfSaveImgToFlash()", status);
VprocHALcleanup();
return -1;
}
ESP_LOGD(TAG, "-- Saving firmware to flash....done");
#endif
status = VprocTwolfFirmwareStart();
if (status != VPROC_STATUS_SUCCESS) {
ESP_LOGD(TAG, "Error %d:VprocTwolfFirmwareStart()", status);
VprocHALcleanup();
return -1;
}
ESP_LOGD(TAG, "Device boot loading completed successfully...");
VprocHALcleanup();
return 0;
}

View File

@@ -0,0 +1,223 @@
/****************************************************************************
* tw_ldfwcfg.c - To load a *.s3 firmware and/or a *.cr2 into the device
* and optionally save the loaded image to flash
*
*
****************************************************************************
* Copyright Microsemi Inc, 2018. All rights reserved.
* Licensed under the MIT License. See LICENSE.txt in the project
* root for license information.
*
***************************************************************************/
#include <stdio.h>
#include <stdlib.h> /* malloc, free, rand */
#include "vproc_common.h"
#include "vprocTwolf_access.h"
/*NOTE: notice that the *.c code are included in the apps-
* This is because the compiler I'm using requires that
* But if your makefile is such that compiler knows where to find these files
* then remove the #include *.c below
*/
#undef SAVE_IMAGE_TO_FLASH /*define this macro to save the firmware from RAM to flash*/
#undef SAVE_CFG_TO_FLASH /*define this macro to save the cfg from RAM to flash*/
/*quick test*/
uint16 numElements;
dataArr *pCr2Buf;
/* fseekNunlines() -- The firmware file is an ascii text file.
* the information from fseek will not be useful.
* this is our own fseek equivalent.
*/
static unsigned long fseekNunlines(FILE *BOOT_FD)
{
uint32 line_count = 0;
int c;
while ((c = fgetc(BOOT_FD)) != EOF) {
if (c == '\n')
line_count++;
}
return line_count;
}
/* readCfgFile() use this function to
* Read the Voice processing cr2 config file into RAM
* filepath -- pointer to the location where to find the file
* pCr2Buf -- the actual firmware data array will be pointed to this buffer
*/
static int readCfgFile(char *filepath)
{
unsigned int reg[2], val[2], len;
uint8 done = 0;
uint16 index = 0;
FILE *BOOT_FD;
char *s;
char line[512] = "";
BOOT_FD = fopen(filepath, "rb");
if (BOOT_FD != NULL) {
len = fseekNunlines(BOOT_FD);
if (len <= 0) {
printf("Error: file is not of the correct format...\n");
return -1;
}
// printf("fileLength = %u\n", len);
/*start at the beginning of the file*/
// fseek(BOOT_FD, 0, SEEK_SET);
/* allocate memory to contain the reg and val:*/
pCr2Buf = (dataArr *) malloc(len * sizeof(dataArr));
if (pCr2Buf == NULL) {
printf("not enough memory to allocate %u bytes.. ", len * sizeof(dataArr));
return -1;
}
rewind(BOOT_FD);
/*read and format the data accordingly*/
numElements = 0;
do {
s = fgets(line, 512, BOOT_FD);
if (line[0] == ';') {
continue;
} else if (s != NULL) {
numElements++;
sscanf(line, "%x %c %x", reg, s, val);
pCr2Buf[index].reg = reg[0];
pCr2Buf[index].value = val[0];
// printf("pCr2Buf[%d].reg pCr2Buf[%d].value = 0x%04x\t0x%04x\n", index, index, pCr2Buf[index].reg,
// pCr2Buf[index].value);
index++;
} else {
done = 1;
}
} while (done == 0);
fclose(BOOT_FD);
// printf ("size of pCr2Buf = %u bytes.. \n", len*sizeof(pCr2Buf));
} else {
printf("Error: can't open file\n");
}
return 0;
}
/*This example host app load the *.s3 firmware to the device RAM. Optionally save it to flash
* Then start the firmware from the execution address in RAM
* It then stops the firmware - Load the cr2 file into RAM. Optionally save it to flash
* Then resstarts the firmware
*/
int main(int argc, char **argv)
{
VprocStatusType status = VPROC_STATUS_SUCCESS;
FILE *BOOT_FD;
char line[256] = "";
if (argc < 3) {
printf("Error: argc = %d - missing %d arg(s)... \n", argc, 3 - (argc - 1));
printf("command Usage:%s firmwarePath ConfigPath\n", argv[0]);
exit(1);
}
printf(":%s %s %s\n", argv[0], argv[1], argv[2]);
BOOT_FD = fopen(argv[1], "rb");
if (BOOT_FD == NULL) {
printf("Error: can't open file %s\n", argv[1]);
return -1;
}
/*global file handle*/
status = VprocTwolfHbiInit();
// gTwolf_fd = open(file_name, O_RDWR);
if (status < 0) {
perror("tw_spi_access open");
fclose(BOOT_FD);
return -1;
}
printf("1- Opening firmware file - done....\n");
status = VprocTwolfHbiBootPrepare();
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfHbiBootPrepare()\n", status);
fclose(BOOT_FD);
VprocTwolfHbiCleanup();
return -1;
}
printf("-- Boot prepare - done....\n");
while (fgets(line, 256, BOOT_FD) != NULL) {
status = VprocTwolfHbiBootMoreData(line);
if (status == VPROC_STATUS_BOOT_LOADING_MORE_DATA) {
continue;
} else if (status == VPROC_STATUS_BOOT_LOADING_CMP) {
break;
} else if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfHbiBootMoreData()\n", status);
fclose(BOOT_FD);
VprocTwolfHbiCleanup();
return -1;
}
}
printf("-- Firmware data transfer - done....\n");
fclose(BOOT_FD);
status = VprocTwolfHbiBootConclude();
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfHbiBootConclude()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
#ifdef SAVE_IMAGE_TO_FLASH
printf("-- Saving firmware to flash....\n");
status = VprocTwolfSaveImgToFlash();
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfSaveImgToFlash()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
printf("-- Saving firmware to flash....done\n");
#endif
status = VprocTwolfFirmwareStart();
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfFirmwareStart()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
printf("3- Loading the config file into the device RAM\n");
if (readCfgFile(argv[2]) < 0) {
printf("Error:read %s file\n", argv[2]);
}
printf("a- Reading config file to host RAM - done....\n");
status = VprocTwolfLoadConfig(pCr2Buf, numElements);
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfLoadConfig()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
#ifdef SAVE_CFG_TO_FLASH
printf("-- Saving config to flash....\n");
status = VprocTwolfSaveCfgToFlash();
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfSaveCfgToFlash()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
printf("-- Saving config to flash....done\n");
#endif
printf("Device boot loading completed successfully...\n");
VprocTwolfHbiCleanup();
return 0;
}

View File

@@ -0,0 +1,431 @@
/****************************************************************************
* tw_spi_access.c - Demo apps demonstrating how to access registers of the
* device over spi or I2C. Loading a firmware and or config into the device
*
*
****************************************************************************
* Copyright Microsemi Inc, 2018. All rights reserved.
* Licensed under the MIT License. See LICENSE.txt in the project
* root for license information.
*
***************************************************************************/
#include "vproc_common.h"
#include "vprocTwolf_access.h"
/*NOTE: notice that the *.c code are included in the apps-
* This is because the compiler I'm using requires that
* But if your makefile is such that compiler knows where to find these files
* then remove the #include *.c below
*/
#include "zl38063_config.h"
#include "zl38063_firmware.h"
#include "esp_codec_dev_os.h"
#undef SAVE_IMAGE_TO_FLASH /*define this macro to save the firmware from RAM to flash*/
#undef SAVE_CFG_TO_FLASH /*define this macro to save the cfg from RAM to flash*/
#define SAVE_IMAGE_TO_FLASH
#define SAVE_CFG_TO_FLASH
/*quick test*/
/*LoadFwrConfig_Alt - to load a converted *s3, *cr2 to c code into the device.
* Basically instead of loading the *.s3, *cr2 directly,
* use the tw_convert tool to convert the ascii hex fwr mage into code and compile
* with the application
*
* input arg: mode: 0 - load both firmware and confing
* 1 - load firmware only
* 2 - load config only
* -1 - Force loading
*/
int tw_upload_dsp_firmware(int mode)
{
union {
short a;
char b;
} test_bigendian;
if (mode >= 0) {
uint16 vol = 0;
esp_codec_dev_sleep(1000);
int ret = VprocTwolfGetAppStatus(&vol);
if (vol) {
ESP_LOGW(TAG_SPI, "MCS ret:%d,Status:%d", ret, vol);
return 0;
}
ESP_LOGI(TAG_SPI, "** Loading DSP firmware ret:%d,Status:%d **", ret, vol);
} else {
mode = 0;
}
test_bigendian.a = 1;
ESP_LOGI(TAG_SPI, "b=%d", test_bigendian.b);
int status = VprocTwolfHbiInit();
if (status < 0) {
DEBUG_LOGE(TAG_SPI, "tw_spi_access open");
return -1;
}
if ((mode == 0) || (mode == 1)) {
twFirmware st_Firmware;
st_Firmware.st_Fwr = (twFwr *) st_twFirmware;
st_Firmware.twFirmwareStreamLen = (uint16) firmwareStreamLen;
st_Firmware.execAddr = (uint32) executionAddress;
st_Firmware.havePrgmBase = (uint8) haveProgramBaseAddress;
st_Firmware.prgmBase = (uint32) programBaseAddress;
ESP_LOGI(TAG_SPI, "1- Firmware boot loading started ....");
status = VprocTwolfHbiBoot_alt(&st_Firmware);
if (status != VPROC_STATUS_SUCCESS) {
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfHbiBoot()", status);
// VprocTwolfHbiCleanup();
return -1;
}
ESP_LOGI(TAG_SPI, "2- Loading the image to RAM....done");
#ifdef SAVE_IMAGE_TO_FLASH
ESP_LOGI(TAG_SPI, "-- Saving firmware to flash....");
status = VprocTwolfSaveImgToFlash();
if (status != VPROC_STATUS_SUCCESS) {
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfSaveImgToFlash()", status);
// VprocTwolfHbiCleanup();
return status;
}
ESP_LOGI(TAG_SPI, "-- Saving firmware to flash....done");
#endif
status = VprocTwolfFirmwareStart();
if (status != VPROC_STATUS_SUCCESS) {
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfFirmwareStart()", status);
// VprocTwolfHbiCleanup();
return status;
}
}
#if 1
if ((mode == 0) || (mode == 2)) {
ESP_LOGI(TAG_SPI, "3- Loading the config file into the device RAM....");
status = VprocTwolfLoadConfig((dataArr *) st_twConfig, (uint16) configStreamLen);
if (status != VPROC_STATUS_SUCCESS) {
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfLoadConfig()", status);
// VprocTwolfHbiCleanup();
return status;
}
#ifdef SAVE_CFG_TO_FLASH
ESP_LOGI(TAG_SPI, "-- Saving config to flash....");
status = VprocTwolfSaveCfgToFlash();
if (status != VPROC_STATUS_SUCCESS) {
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfSaveCfgToFlash()", status);
// VprocTwolfHbiCleanup();
return status;
}
ESP_LOGI(TAG_SPI, "-- Saving config to flash....done");
#endif
}
/*Firmware reset - in order for the configuration to take effect*/
status = VprocTwolfReset(VPROC_RST_SOFTWARE);
if (status != VPROC_STATUS_SUCCESS) {
DEBUG_LOGE(TAG_SPI, "Error %d:VprocTwolfReset()", status);
ESP_LOGI(TAG_SPI, "Error");
// VprocTwolfHbiCleanup();
return status;
}
#endif
ESP_LOGI(TAG_SPI, "Device boot loading completed successfully...");
return status;
}
int zl38063_comm(int argc, char **argv)
{
VprocStatusType status = VPROC_STATUS_SUCCESS;
if (argc == 1) {
printf("Usage: for help type:%s -h", argv[0]);
exit(1);
}
if (strcmp(argv[1], "-h") == 0) {
printf("\nUsage:\n\t%s [-cmd mode] [options...] see supported"
" command modes below\n\n",
argv[0]);
printf("\t-wr : to write one or more 16-bit words to the device\n"
"\t-rd : to read one or more 16-bit words from the device\n"
"\t-rst : to reset the device in one of these"
" supported reset modes:\n"
"\t\t - [0: RAM | 1: ROM | 2: SOFT | 3: AEC | 4: BOOT]\n");
printf("\t-lfcff : to load a specific firmware and related"
" config from flash - arg: 1 to 14\n");
printf("\t-lfcfh-a : to load a pre-compiled firmware and related config"
" from host via SPI\n");
printf("\t-lffh-a: to load a pre-compiled firmware from host via SPI\n");
printf("\t-lcfh-a: to load a pre-compiled config from host via SPI\n");
printf("\t-fclr : to erase the content of the ZL380xx slave flash\n");
printf("\t-sto : to reset the device into boot mode\n");
printf("\t-sta : to start execution of firmware found at "
"exec address in RAM\n");
printf("\t-apla : to configure the ZL380xx x-point for "
"audio playback mode\n");
printf("\t-arec : to configure the ZL380xx x-point for audio "
"recording mode\n");
printf("Example:\n");
printf("\tEx to write 0x8004 into register 0x0300:"
"\n\t%s -wr 0x0300 0x8004\n\n",
argv[0]);
printf("\tEx to read 12 words starting from register 0x020:"
"\n\t%s -rd 0x0020 12\n\n",
argv[0]);
printf("\tEx to reset the device in boot mode:"
"\n\t%s -rst n 'where n:[1-4]\n\n",
argv[0]);
printf("\tEx to load to RAM a firmware and config previously"
" saved to flash at index 1:\n\t%s -lfcff 1\n\n",
argv[0]);
printf("\tEx to load to RAM a firmware previously"
" saved to flash at index 1:\n\t%s -lfff 1\n\n",
argv[0]);
printf("\tEx to load to RAM the pre-compiled (in)firmware "
"from teh host "
"HBI (SPI):\n\t%s -lffh-a\n\n",
argv[0]);
printf("\tEx to load to RAM the pre-compiled (in)config "
"from teh host "
"HBI (SPI):\n\t%s -lcfh-a\n\n",
argv[0]);
printf("\tEx to load to RAM the pre-compiled (in)firmware and config "
"from teh host "
"HBI (SPI):\n\t%s -lfcfh-a\n\n",
argv[0]);
printf("\tEx to stop the firmware currently running and clear "
"the RAM:\n\t%s -sto\n\n",
argv[0]);
printf("\tEx to start a firmware previously loaded into "
"RAM:\n\t%s -sta\n\n",
argv[0]);
printf("\tEx to mute SOUT :\n\t%s -mute_s [1 | 0]\n\n", argv[0]);
printf("\tEx to mute ROUT :\n\t%s -mute_r [1 | 0]\n\n", argv[0]);
printf("\tEx to erase the slave flash device controlled by "
"the ZL380xx :\n\t%s -fclr\n\n",
argv[0]);
printf("\tEx to configure the device for recording mode at a "
"desired clock and sample rates"
" with AEC off[0] or on [1]:\n\t%s -arec clkrate "
"fsrate n 'where n:[0 | 1]'\n\n",
argv[0]);
printf("\tEx to configure the device for playback mode at a "
"desired clock and sample rates"
" with AEC off[0] or on [1]:\n\t%s -apla clkrate "
"fsrate n 'where n:[0 | 1]'\n\n",
argv[0]);
return -1;
}
if ((argc < 3) && (strcmp(argv[1], "-wr") == 0)) {
printf("Usage:%s -wr register value0 value1....value124 \n", argv[0]);
return -1;
}
if ((argc < 3) && (strcmp(argv[1], "-rd") == 0)) {
printf("Usage:%s -rd register n 'where n:[1-127]'\n", argv[0]);
return -1;
}
if ((argc < 3) && (strcmp(argv[1], "-rst") == 0)) {
printf("Usage:%s -rst n 'where n:[0-4]'\n", argv[0]);
return -1;
}
if ((argc < 3) && (strcmp(argv[1], "-apla") == 0)) {
printf("Usage:%s -apla <clkrate in KHz> <fsrate in Hz> n"
" 'where n:[0 | 1]'\n",
argv[0]);
return -1;
}
if ((argc < 3) && (strcmp(argv[1], "-arec") == 0)) {
printf("Usage:%s -arec <clkrate in KHz> <fsrate in Hz> n"
" 'where n:[0 | 1]'\n",
argv[0]);
return -1;
}
if ((argc < 3) && (strcmp(argv[1], "-lfcff") == 0)) {
printf("Usage:%s -lfcfh 1\n", argv[0]);
return -1;
}
/*global file handle*/
status = VprocTwolfHbiInit();
if (status < 0) {
perror("tw_spi_access open");
return -1;
}
if ((strcmp(argv[1], "-wr") == 0) || (strcmp(argv[1], "-rd") == 0)) {
int i = 0, j = 0;
unsigned short val[128];
unsigned short cmdword = (unsigned short) strtoul(argv[2], NULL, 0);
unsigned char numwords = 0;
memset(val, 0, sizeof(val));
if (strcmp(argv[1], "-wr") == 0) { /*for WRITING 1 or more ZL380xx registers*/
unsigned short val[128];
numwords = argc - 3; /*calculate the number of words to write*/
;
for (i = 0; i < numwords; i++) {
val[i] = (unsigned short) strtoul(argv[3 + i], NULL, 0);
}
status = VprocTwolfHbiWrite(cmdword, numwords, val);
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfHbiWrite()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
for (i = 0; i < numwords; i++) {
printf("wr: addr 0x%04x = 0x%04x\n", (cmdword + j), val[i]);
j = j + 2;
}
} else { /*for READING 1 or more ZL380xx registers**/
numwords = (unsigned char) strtoul(argv[3], NULL, 0);
if ((numwords == 0) || (numwords > 128)) {
printf("number of words is out of range. Maximum is 128\n");
VprocTwolfHbiCleanup();
return -1;
}
status = VprocTwolfHbiRead(cmdword, numwords, val);
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfHbiRead()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
for (i = 0; i < numwords; i++) {
printf("RD: addr 0x%04x = 0x%04x\n", (cmdword + j), val[i]);
j = j + 2;
}
}
} else if (strcmp(argv[1], "-rst") == 0) { /*for RESETTING ZL380xx*/
unsigned char rstMode = (unsigned char) strtoul(argv[2], NULL, 0);
status = VprocTwolfReset((uint16) rstMode);
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfHbiRead()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
printf("Device reset completed successfully...\n");
} else if (strcmp(argv[1], "-lfcff") == 0) {
/*Load ZL380x0 firmware + related config record from flash*/
unsigned short image_num = (unsigned short) strtoul(argv[2], NULL, 0);
status = VprocTwolfFirmwareStop();
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfFirmwareStop()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
status = VprocTwolfLoadFwrCfgFromFlash(image_num);
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfLoadFwrCfgFromFlash()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
status = VprocTwolfReset(VPROC_RST_HARDWARE_RAM);
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfReset()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
printf("Device boot loading from flash completed successfully...\n");
} else if (strcmp(argv[1], "-lfff") == 0) {
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfLoadFwrFromFlash()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
printf("Device boot loading from flash completed successfully...\n");
} else if (strcmp(argv[1], "-lfcfh-a") == 0) { /*for LOADING FWR/CFG via SPI*/
if (tw_upload_dsp_firmware(0) != VPROC_STATUS_SUCCESS)
printf("Device boot loading failed.....\n");
} else if (strcmp(argv[1], "-lcfh-a") == 0) { /*for LOADING CFG via SPI*/
if (tw_upload_dsp_firmware(2) != VPROC_STATUS_SUCCESS)
printf("Device boot loading failed.....\n");
} else if (strcmp(argv[1], "-lffh-a") == 0) { /*for LOADING FWR via SPI*/
if (tw_upload_dsp_firmware(1) != VPROC_STATUS_SUCCESS)
printf("Device boot loading failed.....\n");
} else if (strcmp(argv[1], "-sto") == 0) { /*for resetting into boot mode*/
if (VprocTwolfFirmwareStop() != 0)
VprocTwolfHbiCleanup();
else
printf("Firmware stopped to boot mode completed"
" successfully...\n");
} else if (strcmp(argv[1], "-sta") == 0) { /*start executing FWR/CFG */
if (VprocTwolfFirmwareStart() != 0)
VprocTwolfHbiCleanup();
else
printf("Firmware is now running successfully...\n");
} else if (strcmp(argv[1], "-mute_r") == 0) { /*start executing FWR/CFG */
uint8 mute = (uint8) strtoul(argv[2], NULL, 0);
// to do need fix
// if(VprocTwolfMute(VPROC_ROUT, mute) != 0)
if (1) {
VprocTwolfHbiCleanup();
} else {
if (mute)
printf("ROUT Port muted sucessfully...\n");
else
printf("ROUT Port unmuted sucessfully...\n");
}
} else if (strcmp(argv[1], "-mute_s") == 0) { /*start executing FWR/CFG */
uint8 mute = (uint8) strtoul(argv[2], NULL, 0);
// to do need fix
// if(VprocTwolfMute(VPROC_SOUT, mute) != 0)
if (1)
VprocTwolfHbiCleanup();
else {
if (mute)
printf("SOUT Port muted sucessfully...\n");
else
printf("SOUT Port unmuted sucessfully...\n");
}
} else if ((strcmp(argv[1], "-arec") == 0) || (strcmp(argv[1], "-apla") == 0))
/* configure the ZL380x0 for either audio recording or playback
* Over an I2S link
*/
{
unsigned short pclkrate = (unsigned short) strtoul(argv[2], NULL, 0);
unsigned short fsrate = (unsigned short) strtoul(argv[3], NULL, 0);
unsigned short aecState = (unsigned char) strtoul(argv[4], NULL, 0);
printf("pclkrate = %u KHz, fsrate = %u Hz, AEC state = %d\n", pclkrate, fsrate, aecState);
// to do need fix
#if 0
if (strcmp(argv[1], "-arec") == 0) {
if (VprocTwolfUpstreamConfigure(pclkrate, fsrate, aecState) != 0)
VprocTwolfHbiCleanup();
else
printf("Device configured for audio recording...\n");
} else if (strcmp(argv[1], "-apla") == 0) {
if (VprocTwolfDownstreamConfigure(pclkrate, fsrate, aecState) != 0)
VprocTwolfHbiCleanup();
else
printf("Device configured for audio playback...\n");
}
#endif
} else if (strcmp(argv[1], "-fclr") == 0) {
/*Erase the full content of the ZL380x0 controlled slave flash*/
status = VprocTwolfEraseFlash();
if (status != VPROC_STATUS_SUCCESS) {
printf("Error %d:VprocTwolfEraseFlash()\n", status);
VprocTwolfHbiCleanup();
return -1;
}
printf("flash erasing completed successfully...\n");
} else {
printf("Usage: for help type:\n%s -h\n", argv[0]);
}
printf("\n");
return 0;
}

View File

@@ -0,0 +1,23 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*Configuration file version: Microsemi_ZLS38063_1_P1_4_0_Config.cr2, modified: Tue Sep 18 20:48:31 2018*/
#ifndef _ZL38063_CONFIG_H_
#define _ZL38063_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
extern const unsigned short configStreamLen;
extern const dataArr st_twConfig[];
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*Firmware Version : Microsemi_ZLS38063_1_P1_4_0_Firmware.s3, modified: Tue Sep 18 20:50:24 2018 */
#ifndef _ZL38063_FIRMWARE_H_
#define _ZL38063_FIRMWARE_H_
#ifdef __cplusplus
extern "C" {
#endif
extern const twFwr st_twFirmware[];
extern const unsigned short firmwareStreamLen;
extern const unsigned long programBaseAddress;
extern const unsigned long executionAddress;
extern const unsigned char haveProgramBaseAddress;
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,312 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"
#include "zl38063_codec.h"
#include "esp_codec_dev_vol.h"
#include "tw_spi_access.h"
#include "vproc_common.h"
#define TAG "zl38063"
#define HBI_PAGED_READ(offset, length) ((uint16_t) (((uint16_t) (offset) << 8) | (length)))
#define HBI_PAGED_WRITE(offset, length) ((uint16_t) (HBI_PAGED_READ(offset, length) | 0x0080))
#define HBI_SELECT_PAGE(page) ((uint16_t) (0xFE00 | (page)))
#define HBI_DIRECT_READ(offset, length) ((uint16_t) (0x8000 | ((uint16_t) (offset) << 8) | (length)))
#define HBI_DIRECT_WRITE(offset, length) ((uint16_t) (HBI_DIRECT_READ(offset, length) | 0x0080))
typedef struct {
audio_codec_if_t base;
const audio_codec_ctrl_if_t *ctrl_if;
const audio_codec_gpio_if_t *gpio_if;
bool is_open;
bool enabled;
bool pa_reverted;
int16_t pa_pin;
int16_t reset_pin;
float hw_gain;
} audio_codec_zl38063_t;
static uint16_t convert_edian(uint16_t v)
{
return (v >> 8) | ((v & 0xFF) << 8);
}
static void get_write_cmd(uint16_t addr, int size, uint16_t *dst, int *n)
{
uint8_t page;
uint8_t offset;
page = addr >> 8;
offset = (addr & 0xFF) / 2;
if (page == 0) {
dst[(*n)++] = convert_edian(HBI_DIRECT_WRITE(offset, size - 1));
}
if (page) {
/*indirect page access*/
if (page != 0xFF) {
page -= 1;
}
dst[(*n)++] = convert_edian(HBI_SELECT_PAGE(page));
dst[(*n)++] = convert_edian(HBI_PAGED_WRITE(offset, size - 1));
}
}
static void get_read_cmd(uint16_t addr, int size, uint16_t *dst, int *n)
{
uint8_t page;
uint8_t offset;
page = addr >> 8;
offset = (addr & 0xFF) / 2;
if (page == 0) {
dst[(*n)++] = convert_edian(HBI_DIRECT_READ(offset, size - 1));
}
if (page) {
// Indirect page access
if (page != 0xFF) {
page -= 1;
}
dst[(*n)++] = convert_edian(HBI_SELECT_PAGE(page));
dst[(*n)++] = convert_edian(HBI_PAGED_READ(offset, size - 1));
}
}
static int read_addr(audio_codec_zl38063_t *codec, uint16_t addr, int words, uint16_t *data)
{
int total_addr = 0;
int n = 0;
get_read_cmd(addr, words, (uint16_t *) &total_addr, &n);
if (codec->ctrl_if->read_reg) {
int ret =
codec->ctrl_if->read_reg(codec->ctrl_if, total_addr, n * sizeof(uint16_t), data, words * sizeof(uint16_t));
for (int i = 0; i < words; i++) {
data[i] = convert_edian(data[i]);
}
return ret;
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
static int write_addr(audio_codec_zl38063_t *codec, uint16_t addr, int words, uint16_t *data)
{
int total_addr = 0;
int n = 0;
get_write_cmd(addr, words, (uint16_t *) &total_addr, &n);
if (codec->ctrl_if->write_reg) {
for (int i = 0; i < words; i++) {
data[i] = convert_edian(data[i]);
}
return codec->ctrl_if->write_reg(codec->ctrl_if, total_addr, n * sizeof(uint16_t), data,
words * sizeof(uint16_t));
}
return ESP_CODEC_DEV_NOT_SUPPORT;
}
static int get_status(audio_codec_zl38063_t *codec, uint16_t *status)
{
return read_addr(codec, 0x030, 1, status);
}
static int zl38063_pa_power(audio_codec_zl38063_t *codec, bool on)
{
int16_t pa_pin = codec->pa_pin;
if (pa_pin != -1 && codec->gpio_if != NULL) {
codec->gpio_if->setup(pa_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
codec->gpio_if->set(pa_pin, codec->pa_reverted ? !on: on);
}
return ESP_CODEC_DEV_OK;
}
static int zl38063_reset(audio_codec_zl38063_t *codec, bool on)
{
int16_t reset_pin = codec->reset_pin;
if (reset_pin != -1 && codec->gpio_if != NULL) {
codec->gpio_if->setup(reset_pin, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT);
codec->gpio_if->set(reset_pin, !on);
}
return ESP_CODEC_DEV_OK;
}
static int _set_vol(audio_codec_zl38063_t *codec, uint8_t vol)
{
uint16_t reg = vol + (vol << 8);
int ret = write_addr(codec, 0x238, 1, &reg);
ret |= write_addr(codec, 0x23A, 1, &reg);
return ret == 0 ? ESP_CODEC_DEV_OK : ESP_CODEC_DEV_WRITE_FAIL;
}
int zl38063_get_vol(audio_codec_zl38063_t *codec, float *vol)
{
uint16_t reg = 0;
int ret = read_addr(codec, 0x238, 1, &reg);
*vol = (int8_t) (reg >> 8);
return ret;
}
static int zl38063_open(const audio_codec_if_t *h, void *cfg, int cfg_size)
{
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
zl38063_codec_cfg_t *codec_cfg = (zl38063_codec_cfg_t *) cfg;
if (codec == NULL || codec_cfg->ctrl_if == NULL || cfg_size != sizeof(zl38063_codec_cfg_t)) {
return ESP_CODEC_DEV_INVALID_ARG;
}
codec->ctrl_if = codec_cfg->ctrl_if;
codec->gpio_if = codec_cfg->gpio_if;
codec->pa_pin = codec_cfg->pa_pin;
codec->reset_pin = codec_cfg->reset_pin;
codec->pa_reverted = codec_cfg->pa_reverted;
uint16_t status = 0;
VprocSetCtrlIf((void *) codec_cfg->ctrl_if);
zl38063_reset(codec, true);
int ret = get_status(codec, &status);
if (ret != 0) {
ESP_LOGE(TAG, "Fail to write register");
return ESP_CODEC_DEV_WRITE_FAIL;
}
if (status == 0) {
ESP_LOGI(TAG, "Start upload firmware");
ret = tw_upload_dsp_firmware(0);
if (ret != 0) {
ESP_LOGE(TAG, "Fail to upload firmware");
return ESP_CODEC_DEV_WRITE_FAIL;
}
}
codec->is_open = true;
return ESP_CODEC_DEV_OK;
}
static int zl38063_enable(const audio_codec_if_t *h, bool enable)
{
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (codec->enabled == enable) {
return ESP_CODEC_DEV_OK;
}
zl38063_pa_power(codec, enable);
codec->enabled = enable;
ESP_LOGD(TAG, "Codec is %s", enable ? "enabled" : "disabled");
return ESP_CODEC_DEV_OK;
}
static int zl38063_set_vol(const audio_codec_if_t *h, float db_vol)
{
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
db_vol -= codec->hw_gain;
if (db_vol < -90.0) {
db_vol = -90.0;
} else if (db_vol > 6.0) {
db_vol = 6.0;
}
int8_t reg = (int8_t) db_vol;
int ret = _set_vol(codec, reg);
ESP_LOGD(TAG, "Set vol reg:%d", reg);
return ret;
}
static int zl38063_close(const audio_codec_if_t *h)
{
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open) {
zl38063_pa_power(codec, false);
codec->is_open = false;
}
zl38063_reset(codec, false);
VprocSetCtrlIf(NULL);
return ESP_CODEC_DEV_OK;
}
static int zl38063_set_reg(const audio_codec_if_t *h, int reg, int value)
{
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
return write_addr(codec, (uint16_t) reg, 1, (uint16_t *) &value);
}
static int zl38063_get_reg(const audio_codec_if_t *h, int reg, int *value)
{
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
*value = 0;
return read_addr(codec, reg, 1, (uint16_t *) value);
}
static int zl38063_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs)
{
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
if (fs->channel != 2 || fs->sample_rate != 48000 || fs->bits_per_sample != 16) {
ESP_LOGE(TAG, "Firmware only support 48k 2channel 16 bits");
return ESP_CODEC_DEV_NOT_SUPPORT;
}
return ESP_CODEC_DEV_OK;
}
const audio_codec_if_t *zl38063_codec_new(zl38063_codec_cfg_t *codec_cfg)
{
// verify param
if (codec_cfg == NULL || codec_cfg->ctrl_if == NULL) {
ESP_LOGE(TAG, "Wrong codec config");
return NULL;
}
if (codec_cfg->ctrl_if->is_open(codec_cfg->ctrl_if) == false) {
ESP_LOGE(TAG, "Control interface not open yet");
return NULL;
}
audio_codec_zl38063_t *codec = (audio_codec_zl38063_t *) calloc(1, sizeof(audio_codec_zl38063_t));
if (codec == NULL) {
ESP_LOGE(TAG, "No memory for instance");
return NULL;
}
codec->ctrl_if = codec_cfg->ctrl_if;
codec->base.open = zl38063_open;
codec->base.enable = zl38063_enable;
codec->base.set_vol = zl38063_set_vol;
codec->base.set_reg = zl38063_set_reg;
codec->base.get_reg = zl38063_get_reg;
codec->base.set_fs = zl38063_set_fs;
codec->base.close = zl38063_close;
codec->hw_gain = esp_codec_dev_col_calc_hw_gain(&codec_cfg->hw_gain);
do {
int ret = codec->base.open(&codec->base, codec_cfg, sizeof(zl38063_codec_cfg_t));
if (ret != 0) {
ESP_LOGE(TAG, "Open fail");
break;
}
return &codec->base;
} while (0);
if (codec) {
free(codec);
}
return NULL;
}