284 lines
8.5 KiB
C

/*
* 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;
}