integrate TAS5713 - release

This commit is contained in:
Philippe G
2020-08-03 13:04:42 -07:00
parent f9b7d10243
commit 80bf63ed2a
6 changed files with 237 additions and 52 deletions

View File

@@ -48,7 +48,7 @@ static const char TAG[] = "AC101";
return b;\
}
static bool init(char *config, int i2c_port_num);
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static void deinit(void);
static void speaker(bool active);
static void headset(bool active);
@@ -70,7 +70,7 @@ static int i2c_port;
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port_num) {
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
esp_err_t res = ESP_OK;
char *p;

View File

@@ -16,7 +16,7 @@ typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
struct adac_s {
char *model;
bool (*init)(char *config, int i2c_port_num);
bool (*init)(char *config, int i2c_port_num, i2s_config_t *i2s_config);
void (*deinit)(void);
void (*power)(adac_power_e mode);
void (*speaker)(bool active);
@@ -25,5 +25,6 @@ struct adac_s {
};
extern const struct adac_s dac_tas57xx;
extern const struct adac_s dac_tas5713;
extern const struct adac_s dac_ac101;
extern const struct adac_s dac_external;

View File

@@ -25,7 +25,7 @@ static void speaker(bool active) { }
static void headset(bool active) { }
static bool volume(unsigned left, unsigned right) { return false; }
static void power(adac_power_e mode);
static bool init(char *config, int i2c_port_num);
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static bool i2c_json_execute(char *set);
static esp_err_t i2c_write_reg(uint8_t reg, uint8_t val);
@@ -38,7 +38,7 @@ static cJSON *i2c_json;
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port_num) {
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
char *p;
i2c_port = i2c_port_num;

View File

@@ -77,7 +77,7 @@ extern struct buffer *streambuf;
extern struct buffer *outputbuf;
extern u8_t *silencebuf;
const struct adac_s *dac_set[] = { &dac_tas57xx, &dac_ac101, NULL };
const struct adac_s *dac_set[] = { &dac_tas57xx, &dac_tas5713, &dac_ac101, NULL };
const struct adac_s *adac = &dac_external;
static log_level loglevel;
@@ -85,7 +85,6 @@ static log_level loglevel;
static bool jack_mutes_amp;
static bool running, isI2SStarted;
static i2s_config_t i2s_config;
static int bytes_per_frame;
static u8_t *obuf;
static frames_t oframes;
static bool spdif;
@@ -158,31 +157,14 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
jack_mutes_amp = (strcmp(p,"1") == 0 ||strcasecmp(p,"y") == 0);
free(p);
#ifdef CONFIG_I2S_BITS_PER_CHANNEL
switch (CONFIG_I2S_BITS_PER_CHANNEL) {
case 24:
output.format = S24_BE;
bytes_per_frame = 2*3;
break;
case 16:
output.format = S16_BE;
bytes_per_frame = 2*2;
break;
case 8:
output.format = S8_BE;
bytes_per_frame = 2*4;
break;
default:
LOG_ERROR("Unsupported bit depth %d",CONFIG_I2S_BITS_PER_CHANNEL);
break;
}
#if BYTES_PER_FRAME == 8
output.format = S32_LE;
#else
output.format = S16_LE;
bytes_per_frame = 2*2;
#endif
output.write_cb = &_i2s_write_frames;
obuf = malloc(FRAME_BLOCK * bytes_per_frame);
obuf = malloc(FRAME_BLOCK * BYTES_PER_FRAME);
if (!obuf) {
LOG_ERROR("Cannot allocate i2s buffer");
return;
@@ -240,6 +222,13 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
gpio_set_level(i2s_pin_config.data_out_num, 0);
}
i2s_config.sample_rate = output.current_sample_rate;
i2s_config.bits_per_sample = BYTES_PER_FRAME * 8 / 2;
// Counted in frames (but i2s allocates a buffer <= 4092 bytes)
i2s_config.dma_buf_len = DMA_BUF_LEN;
i2s_config.dma_buf_count = DMA_BUF_COUNT;
dma_buf_frames = DMA_BUF_COUNT * DMA_BUF_LEN;
char *dac_config = config_alloc_get_str("dac_config", CONFIG_DAC_CONFIG, "model=i2s,bck=" STR(CONFIG_I2S_BCK_IO)
",ws=" STR(CONFIG_I2S_WS_IO) ",do=" STR(CONFIG_I2S_DO_IO)
",sda=" STR(CONFIG_I2C_SDA) ",scl=" STR(CONFIG_I2C_SCL)
@@ -248,7 +237,7 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
if ((p = strcasestr(dac_config, "model")) != NULL) sscanf(p, "%*[^=]=%31[^,]", model);
for (int i = 0; adac == &dac_external && dac_set[i]; i++) if (strcasestr(dac_set[i]->model, model)) adac = dac_set[i];
res = adac->init(dac_config, I2C_PORT) ? ESP_OK : ESP_FAIL;
res = adac->init(dac_config, I2C_PORT, &i2s_config) ? ESP_OK : ESP_FAIL;
if ((p = strcasestr(dac_config, "bck")) != NULL) i2s_pin_config.bck_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(dac_config, "ws")) != NULL) i2s_pin_config.ws_io_num = atoi(strchr(p, '=') + 1);
@@ -262,13 +251,6 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
free(dac_config);
i2s_config.sample_rate = output.current_sample_rate;
i2s_config.bits_per_sample = bytes_per_frame * 8 / 2;
// Counted in frames (but i2s allocates a buffer <= 4092 bytes)
i2s_config.dma_buf_len = DMA_BUF_LEN;
i2s_config.dma_buf_count = DMA_BUF_COUNT;
dma_buf_frames = DMA_BUF_COUNT * DMA_BUF_LEN;
res |= i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
res |= i2s_set_pin(CONFIG_I2S_NUM, &i2s_pin_config);
@@ -377,13 +359,13 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
_apply_gain(outputbuf, out_frames, gainL, gainR);
}
memcpy(obuf + oframes * bytes_per_frame, outputbuf->readp, out_frames * bytes_per_frame);
memcpy(obuf + oframes * BYTES_PER_FRAME, outputbuf->readp, out_frames * BYTES_PER_FRAME);
#else
optr = (s32_t*) outputbuf->readp;
#endif
} else {
#if BYTES_PER_FRAME == 4
memcpy(obuf + oframes * bytes_per_frame, silencebuf, out_frames * bytes_per_frame);
memcpy(obuf + oframes * BYTES_PER_FRAME, silencebuf, out_frames * BYTES_PER_FRAME);
#else
optr = (s32_t*) silencebuf;
#endif
@@ -397,10 +379,10 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
dsd_invert((u32_t *) optr, out_frames);
)
_scale_and_pack_frames(obuf + oframes * bytes_per_frame, optr, out_frames, gainL, gainR, output.format);
_scale_and_pack_frames(obuf + oframes * BYTES_PER_FRAME, optr, out_frames, gainL, gainR, output.format);
#endif
output_visu_export((s16_t*) (obuf + oframes * bytes_per_frame), out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
output_visu_export((s16_t*) (obuf + oframes * BYTES_PER_FRAME), out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
oframes += out_frames;
@@ -509,8 +491,8 @@ static void *output_thread_i2s(void *arg) {
/*
if (synced)
// can sleep for a buffer_queue - 1 and then eat a buffer (discard) if we are synced
usleep(((DMA_BUF_COUNT - 1) * DMA_BUF_LEN * bytes_per_frame * 1000) / 44100 * 1000);
discard = DMA_BUF_COUNT * DMA_BUF_LEN * bytes_per_frame;
usleep(((DMA_BUF_COUNT - 1) * DMA_BUF_LEN * BYTES_PER_FRAME * 1000) / 44100 * 1000);
discard = DMA_BUF_COUNT * DMA_BUF_LEN * BYTES_PER_FRAME;
}
*/
i2s_config.sample_rate = output.current_sample_rate;
@@ -523,20 +505,25 @@ static void *output_thread_i2s(void *arg) {
}
// run equalizer
equalizer_process(obuf, oframes * bytes_per_frame, output.current_sample_rate);
equalizer_process(obuf, oframes * BYTES_PER_FRAME, output.current_sample_rate);
// we assume that here we have been able to entirely fill the DMA buffers
if (spdif) {
spdif_convert((ISAMPLE_T*) obuf, oframes, (u32_t*) sbuf, &count);
i2s_write(CONFIG_I2S_NUM, sbuf, oframes * 16, &bytes, portMAX_DELAY);
bytes /= 4;
#if BYTES_PER_FRAME == 4
} else if (i2s_config.bits_per_sample == 32) {
i2s_write_expand(CONFIG_I2S_NUM, obuf, oframes * BYTES_PER_FRAME, 16, 32, &bytes, portMAX_DELAY);
#endif
} else {
i2s_write(CONFIG_I2S_NUM, obuf, oframes * bytes_per_frame, &bytes, portMAX_DELAY);
}
i2s_write(CONFIG_I2S_NUM, obuf, oframes * BYTES_PER_FRAME, &bytes, portMAX_DELAY);
}
fullness = gettime_ms();
if (bytes != oframes * bytes_per_frame) {
LOG_WARN("I2S DMA Overflow! available bytes: %d, I2S wrote %d bytes", oframes * bytes_per_frame, bytes);
if (bytes != oframes * BYTES_PER_FRAME) {
LOG_WARN("I2S DMA Overflow! available bytes: %d, I2S wrote %d bytes", oframes * BYTES_PER_FRAME, bytes);
}
SET_MIN_MAX( TIME_MEASUREMENT_GET(timer_start),i2s_time);
@@ -557,7 +544,7 @@ static void *output_thread_i2s_stats(void *arg) {
output_state state = output.state;
if(stats && state>OUTPUT_STOPPED){
LOG_INFO( "Output State: %d, current sample rate: %d, bytes per frame: %d",state,output.current_sample_rate, bytes_per_frame);
LOG_INFO( "Output State: %d, current sample rate: %d, bytes per frame: %d",state,output.current_sample_rate, BYTES_PER_FRAME);
LOG_INFO( LINE_MIN_MAX_FORMAT_HEAD1);
LOG_INFO( LINE_MIN_MAX_FORMAT_HEAD2);
LOG_INFO( LINE_MIN_MAX_FORMAT_HEAD3);

View File

@@ -0,0 +1,195 @@
/*
* Squeezelite for esp32
*
* (c) Sebastien 2019
* Philippe G. 2019, philippe_44@outlook.com
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*
* (c) C. Rohs 2020 added support for the tas5713 (eg. HiFiBerry AMP+)
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "adac.h"
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array))
#define TAS5713 0x36 /* i2c address of TAS5713 */
// TAS5713 I2C-bus register addresses
#define TAS5713_CLOCK_CTRL 0x00
#define TAS5713_DEVICE_ID 0x01
#define TAS5713_ERROR_STATUS 0x02
#define TAS5713_SYSTEM_CTRL1 0x03
#define TAS5713_SERIAL_DATA_INTERFACE 0x04
#define TAS5713_SYSTEM_CTRL2 0x05
#define TAS5713_SOFT_MUTE 0x06
#define TAS5713_VOL_MASTER 0x07
#define TAS5713_VOL_CH1 0x08
#define TAS5713_VOL_CH2 0x09
#define TAS5713_VOL_HEADPHONE 0x0A
#define TAS5713_OSC_TRIM 0x1B
static const char TAG[] = "TAS5713";
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static void deinit(void);
static void speaker(bool active) { };
static void headset(bool active) { } ;
static bool volume(unsigned left, unsigned right);
static void power(adac_power_e mode) { };
const struct adac_s dac_tas5713 = {"TAS5713", init, deinit, power, speaker, headset, volume};
struct tas5713_cmd_s {
uint8_t reg;
uint8_t value;
};
// matching orders
typedef enum {
TAS57_ACTIVE = 0,
TAS57_STANDBY,
TAS57_DOWN,
TAS57_ANALOGUE_OFF,
TAS57_ANALOGUE_ON,
TAS57_VOLUME
} dac_cmd_e;
static int i2c_port;
static void tas5713_set(uint8_t reg, uint8_t val);
static uint8_t tas5713_get(uint8_t reg);
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
char *p;
i2c_port = i2c_port_num;
// configure i2c
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = -1,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = -1,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 250000,
};
if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
i2c_param_config(i2c_port, &i2c_config);
esp_err_t res = i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
/* find if there is a tas5713 attached. Reg 0 should read non-zero if so */
if (!tas5713_get(0x00)) {
ESP_LOGW(TAG, "No TAS5713 detected");
i2c_driver_delete(i2c_port);
return 0;
}
ESP_LOGI(TAG, "TAS5713 uses I2C sda:%d, scl:%d", i2c_config.sda_io_num, i2c_config.scl_io_num);
/* do the init sequence */
tas5713_set(TAS5713_OSC_TRIM, 0x00); /* a delay is required after this */
vTaskDelay(50 / portTICK_PERIOD_MS);
tas5713_set(TAS5713_SERIAL_DATA_INTERFACE, 0x03); /* I2S LJ 16 bit */
tas5713_set(TAS5713_SYSTEM_CTRL2, 0x00); /* exit all channel shutdown */
tas5713_set(TAS5713_SOFT_MUTE, 0x00); /* unmute */
tas5713_set(TAS5713_VOL_MASTER, 0x20);
tas5713_set(TAS5713_VOL_CH1, 0x30);
tas5713_set(TAS5713_VOL_CH2, 0x30);
tas5713_set(TAS5713_VOL_HEADPHONE, 0xFF);
/* The tas5713 typically has the mclk connected to the sclk. In this
configuration, mclk must be a multiple of the sclk. The lowest workable
multiple is 64x. To achieve this, 32 bits per channel on must be sent
over I2S. Reconfigure the I2S for that here, and expand the I2S stream
when it is sent */
i2s_config->bits_per_sample = 32;
if (res != ESP_OK) {
ESP_LOGE(TAG, "could not intialize TAS5713 %d", res);
return false;
}
return true;
}
/****************************************************************************************
* init
*/
static void deinit(void) {
i2c_driver_delete(i2c_port);
}
/****************************************************************************************
* change volume
*/
static bool volume(unsigned left, unsigned right) {
return false;
}
/****************************************************************************************
* DAC specific commands
*/
void tas5713_set(uint8_t reg, uint8_t val) {
esp_err_t ret = ESP_OK;
ESP_LOGI(TAG,"TAS5713 send %x %x", reg, val);
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd,
TAS5713 | I2C_MASTER_WRITE,
I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, reg, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, val, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd);
ret = i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS);
i2c_cmd_link_delete(i2c_cmd);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Could not send command to TAS5713 %d", ret);
}
}
/*************************************************************************
* Read from i2c for the tas5713. This doubles as tas5713 detect. This function
* returns zero on error, so read register 0x00 for tas detect, which will be
* non-zero in this application.
*/
static uint8_t tas5713_get(uint8_t reg) {
int ret;
uint8_t data = 0;
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, TAS5713 | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, reg, I2C_MASTER_NACK);
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, TAS5713 | I2C_MASTER_READ, I2C_MASTER_NACK);
i2c_master_read_byte(i2c_cmd, &data, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd);
ret = i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS);
i2c_cmd_link_delete(i2c_cmd);
if (ret == ESP_OK) {
ESP_LOGI(TAG,"TAS5713 reg 0x%x is 0x%x", reg, data);
}
return data;
}

View File

@@ -23,7 +23,7 @@
static const char TAG[] = "TAS575x/8x";
static bool init(char *config, int i2c_port_num);
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static void deinit(void);
static void speaker(bool active);
static void headset(bool active);
@@ -68,10 +68,10 @@ static int tas57_detect(void);
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port_num) {
i2c_port = i2c_port_num;
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config) {
char *p;
i2c_port = i2c_port_num;
// configure i2c
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
@@ -131,7 +131,9 @@ static void deinit(void) {
/****************************************************************************************
* change volume
*/
static bool volume(unsigned left, unsigned right) { return false; }
static bool volume(unsigned left, unsigned right) {
return false;
}
/****************************************************************************************
* power