Start of 5.X work

This commit is contained in:
Sebastien L
2025-03-18 17:38:34 -04:00
parent c0ddf0a997
commit 73bd096f37
442 changed files with 227862 additions and 21075 deletions

View File

@@ -3,19 +3,19 @@ if(IDF_TARGET STREQUAL "esp32")
set(target_requires "driver_bt" "platform_config")
endif()
idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978
idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978 cs4265
INCLUDE_DIRS . ac101
PRIV_REQUIRES
codecs
newlib
esp_common
esp-dsp
tools
platform_config
services
spotify
raop
display
tools
audio
led_strip
_override

View File

@@ -32,7 +32,7 @@
#include <driver/i2s.h>
#include "adac.h"
#include "ac101.h"
#include "Configurator.h"
#include "Config.h"
static const char TAG[] = "AC101";
#define SPKOUT_EN ((1 << 9) | (1 << 11) | (1 << 7) | (1 << 5))
@@ -48,13 +48,13 @@ static const char TAG[] = "AC101";
return b;\
}
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config, bool *mck);
static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck);
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_ac101 = { sys_DACModelEnum_AC101, init, adac_deinit, power, speaker, headset, volume };
const struct adac_s dac_ac101 = { sys_dac_models_AC101, init, adac_deinit, power, speaker, headset, volume };
static void ac101_start(ac_module_t mode);
static void ac101_stop(void);
@@ -64,11 +64,11 @@ static void ac101_set_spk_volume(uint8_t volume);
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config, bool *mck) {
adac_init(config, i2c_port);
static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck) {
adac_init(config);
if (adac_read_word(AC101_ADDR, CHIP_AUDIO_RS) == 0xffff) {
ESP_LOGW(TAG, "No AC101 detected");
i2c_driver_delete(i2c_port);
i2c_driver_delete(config->i2c.port-sys_i2c_port_PORT0);
return false;
}

View File

@@ -12,12 +12,12 @@
#include "freertos/FreeRTOS.h"
#include "driver/i2s.h"
#include "driver/i2c.h"
#include "Configurator.h"
#include "Config.h"
typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
struct adac_s {
sys_DACModelEnum model;
bool (*init)(char *config, int i2c_port_num, i2s_config_t *i2s_config, bool *mck);
sys_dac_models model;
bool (*init)(sys_dac_config *config,i2s_config_t *i2s_config, bool *mck);
void (*deinit)(void);
void (*power)(adac_power_e mode);
void (*speaker)(bool active);
@@ -29,9 +29,10 @@ 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_wm8978;
extern const struct adac_s dac_cs4265;
extern const struct adac_s dac_external;
int adac_init(char *config, int i2c_port);
int adac_init(sys_dac_config *config);
void adac_deinit(void);
esp_err_t adac_write(int i2c_addr, uint8_t reg, uint8_t *data, size_t count);
esp_err_t adac_write_byte(int i2c_addr, uint8_t reg, uint8_t val);

View File

@@ -23,16 +23,13 @@
} while (0)
static const char TAG[] = "DAC core";
static int i2c_port = -1;
i2c_port_t i2c_port = -1;
/****************************************************************************************
* init
*/
int adac_init(char *config, int i2c_port_num) {
int adac_init(sys_dac_config *config) {
int i2c_addr = 0;
i2c_port = i2c_port_num;
// configure i2c
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
@@ -42,17 +39,19 @@ int adac_init(char *config, int i2c_port_num) {
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 250000,
};
PARSE_PARAM(config, "i2c", '=', i2c_addr);
PARSE_PARAM(config, "sda", '=', i2c_config.sda_io_num);
PARSE_PARAM(config, "scl", '=', i2c_config.scl_io_num);
i2c_addr = config->addr;
if(config->has_i2c){
i2c_config.sda_io_num = config->i2c.sda;
i2c_config.scl_io_num = config->i2c.scl;
i2c_config.master.clk_speed = config->i2c.speed>0?config->i2c.speed:i2c_config.master.clk_speed;
}
if (i2c_config.sda_io_num == -1 || i2c_config.scl_io_num == -1) {
ESP_LOGW(TAG, "DAC does not use i2c");
return i2c_addr;
}
ESP_LOGI(TAG, "DAC uses I2C port:%d, sda:%d, scl:%d", i2c_port, i2c_config.sda_io_num, i2c_config.scl_io_num);
i2c_port = config->i2c.port-sys_i2c_port_PORT0;
ESP_LOGI(TAG, "DAC uses I2C port:%s, sda:%d, scl:%d", sys_i2c_port_name(config->i2c.port), i2c_config.sda_io_num, i2c_config.scl_io_num);
// we have an I2C configured
i2c_param_config(i2c_port, &i2c_config);

View File

@@ -7,7 +7,7 @@
*/
#include "squeezelite.h"
// #include "Configurator.h"
// #include "Config.h"
#pragma message("fixme: look for TODO below")
#include "audio_controls.h"
@@ -54,7 +54,7 @@ static u16_t server_cport;
static int cli_sock = -1;
static u8_t mac[6];
static void (*chained_notify)(in_addr_t, u16_t, u16_t);
static bool raw_mode;
static bool raw_mode=false;
static void cli_send_cmd(char *cmd);
@@ -244,15 +244,16 @@ static bool ir_handler(u16_t addr, u16_t cmd) {
*/
void sb_controls_init(void) {
// TODO: Add support for the commented code
#pragma message("sb_controls_init needs to be implemented")
// char *p = config_alloc_get_default(NVS_TYPE_STR, "lms_ctrls_raw", "n", 0);
// raw_mode = p && (*p == '1' || *p == 'Y' || *p == 'y');
// free(p);
// LOG_INFO("initializing audio (buttons/rotary/ir) controls (raw:%u)", raw_mode);
LOG_INFO("initializing audio (buttons/rotary/ir) controls (raw:%u)", raw_mode);
// get_mac(mac);
// actrls_set_default(LMS_controls, raw_mode, NULL, ir_handler);
get_mac(mac);
actrls_set_default(LMS_controls, raw_mode, NULL, ir_handler);
// chained_notify = server_notify;
// server_notify = notify;
chained_notify = server_notify;
server_notify = notify;
}

View File

@@ -1,4 +1,4 @@
/*
/*
* Squeezelite for esp32
*
* (c) Sebastien 2019
@@ -8,21 +8,21 @@
* https://opensource.org/licenses/MIT
*
*/
#include <string.h>
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
#include "Config.h"
#include "adac.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "driver/i2s.h"
#include "esp_log.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"
#include "stdio.h"
#include "math.h"
#include "Configurator.h"
#define CS4265_PULL_UP (0x4F )
#define CS4265_PULL_DOWN (0x4E )
#include "stdio.h"
#define CS4265_PULL_UP (0x4F)
#define CS4265_PULL_DOWN (0x4E)
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@@ -30,188 +30,181 @@
static const char TAG[] = "CS4265";
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static bool init(sys_dac_config * config, i2s_config_t* i2s_config, bool* mck);
static void speaker(bool active);
static void headset(bool active);
static bool volume(unsigned left, unsigned right);
static void power(adac_power_e mode);
static esp_err_t cs4265_update_bit(uint8_t reg_no,uint8_t mask,uint8_t val );
static esp_err_t cs4265_update_bit(uint8_t reg_no, uint8_t mask, uint8_t val);
static esp_err_t set_clock();
const struct adac_s dac_cs4265 = { sys_DACModelEnum_CS4265, init, adac_deinit, power, speaker, headset, volume };
const struct adac_s dac_cs4265 = {
sys_dac_models_CS4265, init, adac_deinit, power, speaker, headset, volume};
struct cs4265_cmd_s {
uint8_t reg;
uint8_t value;
uint8_t reg;
uint8_t value;
};
struct cs4265_private {
uint8_t format;
uint32_t sysclk;
i2s_config_t *i2s_config;
int i2c_port;
};
uint8_t format;
uint32_t sysclk;
i2s_config_t* i2s_config;
i2c_port_t port;
};
struct cs4265_private cs4265;
#define CS4265_CHIP_ID 0x1
#define CS4265_CHIP_ID_VAL 0xD0
#define CS4265_CHIP_ID_MASK 0xF0
#define CS4265_REV_ID_MASK 0x0F
#define CS4265_CHIP_ID 0x1
#define CS4265_CHIP_ID_VAL 0xD0
#define CS4265_CHIP_ID_MASK 0xF0
#define CS4265_REV_ID_MASK 0x0F
#define CS4265_PWRCTL 0x02
#define CS4265_PWRCTL_PDN (1 << 0)
#define CS4265_PWRCTL_PDN_DAC (1 << 1)
#define CS4265_PWRCTL_PDN_ADC (1 << 2)
#define CS4265_PWRCTL_PDN_MIC (1 << 3)
#define CS4265_PWRCTL_FREEZE (1 << 7)
#define CS4265_PWRCTL_PDN_ALL CS4265_PWRCTL_PDN | CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_PDN_DAC | CS4265_PWRCTL_PDN_MIC
#define CS4265_PWRCTL 0x02
#define CS4265_PWRCTL_PDN (1 << 0)
#define CS4265_PWRCTL_PDN_DAC (1 << 1)
#define CS4265_PWRCTL_PDN_ADC (1 << 2)
#define CS4265_PWRCTL_PDN_MIC (1 << 3)
#define CS4265_PWRCTL_FREEZE (1 << 7)
#define CS4265_PWRCTL_PDN_ALL \
CS4265_PWRCTL_PDN | CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_PDN_DAC | CS4265_PWRCTL_PDN_MIC
#define CS4265_DAC_CTL 0x3
#define CS4265_DAC_CTL 0x3
// De-Emphasis Control (Bit 1)
// The standard 50/15 i2s digital de-emphasis filter response may be implemented for a sample
// rate of 44.1 kHz when the DeEmph bit is set. NOTE: De-emphasis is available only in Single-Speed Mode.
#define CS4265_DAC_CTL_DEEMPH (1 << 1)
// rate of 44.1 kHz when the DeEmph bit is set. NOTE: De-emphasis is available only in Single-Speed
// Mode.
#define CS4265_DAC_CTL_DEEMPH (1 << 1)
// MUTE DAC
// The DAC outputs will mute and the MUTEC pin will become active when this bit is set. Though this bit is
// active high, it should be noted that the MUTEC pin is active low. The common mode voltage on the outputs
// will be retained when this bit is set. The muting function is effected, similar to attenuation changes, by the
// DACSoft and DACZero bits in the DAC Control 2 register.
#define CS4265_DAC_CTL_MUTE (1 << 2)
// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital Interface
// DAC_DIF1 DAC_DIF0 Description Format Figure
// 0 0 Left Justified, up to 24-bit data (default) 0 5
// 0 1 I²S, up to 24-bit data 1 6
// The DAC outputs will mute and the MUTEC pin will become active when this bit is set. Though this
// bit is active high, it should be noted that the MUTEC pin is active low. The common mode voltage
// on the outputs will be retained when this bit is set. The muting function is effected, similar to
// attenuation changes, by the DACSoft and DACZero bits in the DAC Control 2 register.
#define CS4265_DAC_CTL_MUTE (1 << 2)
// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital
// Interface DAC_DIF1 DAC_DIF0 Description Format Figure 0 0 Left
// Justified, up to 24-bit data (default) 0 5 0 1 I²S, up to 24-bit data 1 6
// 1 0 Right-Justified, 16-bit Data 2 7
// 1 1 Right-Justified, 24-bit Data 3 7
#define CS4265_DAC_CTL_DIF0 (1 << 4)
// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital Interface
// DAC_DIF1 DAC_DIF0 Description Format Figure
// 0 0 Left Justified, up to 24-bit data (default) 0 5
// 0 1 I²S, up to 24-bit data 1 6
#define CS4265_DAC_CTL_DIF0 (1 << 4)
// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital
// Interface DAC_DIF1 DAC_DIF0 Description Format Figure 0 0 Left
// Justified, up to 24-bit data (default) 0 5 0 1 I²S, up to 24-bit data 1 6
// 1 0 Right-Justified, 16-bit Data 2 7
// 1 1 Right-Justified, 24-bit Data 3 7
#define CS4265_DAC_CTL_DIF1 (1 << 5)
#define CS4265_DAC_CTL_DIF1 (1 << 5)
#define CS4265_ADC_CTL 0x4
#define CS4265_ADC_MASTER 1
#define CS4265_ADC_CTL_MUTE (1 << 2)
#define CS4265_ADC_DIF (1 << 4)
#define CS4265_ADC_FM (3 << 6)
#define CS4265_ADC_CTL 0x4
#define CS4265_ADC_MASTER 1
#define CS4265_ADC_CTL_MUTE (1 << 2)
#define CS4265_ADC_DIF (1 << 4)
#define CS4265_ADC_FM (3 << 6)
//Master Clock Dividers (Bits 6:4)
//Sets the frequency of the supplied MCLK signal.
// Master Clock Dividers (Bits 6:4)
// Sets the frequency of the supplied MCLK signal.
//
//MCLK Divider MCLK Freq2 MCLK Freq1 MCLK Freq0
// ÷ 1 0 0 0
// ÷ 1.5 0 0 1
// ÷ 2 0 1 0
// ÷ 3 0 1 1
// ÷ 4 1 0 0
// NA 1 0 1
// NA 1 1 x
#define CS4265_MCLK_FREQ 0x5
#define CS4265_MCLK_FREQ_1_0X (0b000<<4 )
#define CS4265_MCLK_FREQ_1_5X (0b001<<4 )
#define CS4265_MCLK_FREQ_2_0X (0b010<<4 )
#define CS4265_MCLK_FREQ_3_0X (0b011<<4 )
#define CS4265_MCLK_FREQ_4_0X (0b100<<4 )
// MCLK Divider MCLK Freq2 MCLK Freq1 MCLK Freq0
// ÷ 1 0 0 0
// ÷ 1.5 0 0 1
// ÷ 2 0 1 0
// ÷ 3 0 1 1
// ÷ 4 1 0 0
// NA 1 0 1
// NA 1 1 x
#define CS4265_MCLK_FREQ 0x5
#define CS4265_MCLK_FREQ_1_0X (0b000 << 4)
#define CS4265_MCLK_FREQ_1_5X (0b001 << 4)
#define CS4265_MCLK_FREQ_2_0X (0b010 << 4)
#define CS4265_MCLK_FREQ_3_0X (0b011 << 4)
#define CS4265_MCLK_FREQ_4_0X (0b100 << 4)
#define CS4265_MCLK_FREQ_MASK (7 << 4)
#define CS4265_MCLK_FREQ_MASK (7 << 4)
#define CS4265_SIG_SEL 0x6
#define CS4265_SIG_SEL_LOOP (1 << 1)
#define CS4265_SIG_SEL_SDIN2 (1 << 7)
#define CS4265_SIG_SEL_SDIN1 (0 << 7)
#define CS4265_SIG_SEL 0x6
#define CS4265_SIG_SEL_LOOP (1 << 1)
#define CS4265_SIG_SEL_SDIN2 (1 << 7)
#define CS4265_SIG_SEL_SDIN1 (0 << 7)
// Sets the gain or attenuation for the ADC input PGA stage. The gain may be adjusted from -12 dB to
// +12 dB in 0.5 dB steps. The gain bits are in twos complement with the Gain0 bit set for a 0.5 dB step.
// Register settings outside of the ±12 dB range are reserved and must not be used. See Table 13 for example settings
#define CS4265_CHB_PGA_CTL 0x7
// +12 dB in 0.5 dB steps. The gain bits are in twos complement with the Gain0 bit set for a 0.5 dB
// step. Register settings outside of the ±12 dB range are reserved and must not be used. See Table
// 13 for example settings
#define CS4265_CHB_PGA_CTL 0x7
// Sets the gain or attenuation for the ADC input PGA stage. The gain may be adjusted from -12 dB to
// +12 dB in 0.5 dB steps. The gain bits are in twos complement with the Gain0 bit set for a 0.5 dB step.
// Register settings outside of the ±12 dB range are reserved and must not be used. See Table 13 for example settings
#define CS4265_CHA_PGA_CTL 0x8
// +12 dB in 0.5 dB steps. The gain bits are in twos complement with the Gain0 bit set for a 0.5 dB
// step. Register settings outside of the ±12 dB range are reserved and must not be used. See Table
// 13 for example settings
#define CS4265_CHA_PGA_CTL 0x8
// Gain[5:0] Setting
// 101000 -12 dB
// 000000 0 dB
// 011000 +12 dB
#define CS4265_ADC_CTL2 0x9
#define CS4265_ADC_CTL2 0x9
// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to -127 dB.
// The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared. The Vol[7:1] bits
// activate attenuation equal to their decimal equivalent (in dB).
//Binary Code Volume Setting
//00000000 0 dB
//00000001 -0.5 dB
//00101000 -20 dB
//00101001 -20.5 dB
//11111110 -127 dB
//11111111 -127.5 dB
#define CS4265_DAC_CHA_VOL 0xA
// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to -127 dB.
// The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared. The Vol[7:1] bits
// activate attenuation equal to their decimal equivalent (in dB).
//Binary Code Volume Setting
//00000000 0 dB
//00000001 -0.5 dB
//00101000 -20 dB
//00101001 -20.5 dB
//11111110 -127 dB
//11111111 -127.5 dB
#define CS4265_DAC_CHB_VOL 0xB
#define CS4265_DAC_VOL_ATT_000_0 0b00000000
#define CS4265_DAC_VOL_ATT_000_5 0b00000001
#define CS4265_DAC_VOL_ATT_020_0 0b00101000
#define CS4265_DAC_VOL_ATT_020_5 0b00101001
#define CS4265_DAC_VOL_ATT_127_0 0b11111110
#define CS4265_DAC_VOL_ATT_127_5 0b11111111
// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to
// -127 dB. The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared.
// The Vol[7:1] bits activate attenuation equal to their decimal equivalent (in dB).
// Binary Code Volume Setting
// 00000000 0 dB
// 00000001 -0.5 dB
// 00101000 -20 dB
// 00101001 -20.5 dB
// 11111110 -127 dB
// 11111111 -127.5 dB
#define CS4265_DAC_CHA_VOL 0xA
// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to
// -127 dB. The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared.
// The Vol[7:1] bits activate attenuation equal to their decimal equivalent (in dB).
// Binary Code Volume Setting
// 00000000 0 dB
// 00000001 -0.5 dB
// 00101000 -20 dB
// 00101001 -20.5 dB
// 11111110 -127 dB
// 11111111 -127.5 dB
#define CS4265_DAC_CHB_VOL 0xB
#define CS4265_DAC_VOL_ATT_000_0 0b00000000
#define CS4265_DAC_VOL_ATT_000_5 0b00000001
#define CS4265_DAC_VOL_ATT_020_0 0b00101000
#define CS4265_DAC_VOL_ATT_020_5 0b00101001
#define CS4265_DAC_VOL_ATT_127_0 0b11111110
#define CS4265_DAC_VOL_ATT_127_5 0b11111111
// DAC Soft Ramp or Zero Cross Enable (Bits 7:6)
//
// Soft Ramp Enable
// Soft Ramp allows level changes, both muting and attenuation, to be implemented by incrementally ramping, in 1/8 dB steps, from the current level to the new level at a rate of 1 dB per 8 left/right clock periods.
// See Table 17.
// Zero Cross Enable
// Zero Cross Enable dictates that signal-level changes, either by attenuation changes or muting, will occur
// on a signal zero crossing to minimize audible artifacts. The requested level change will occur after a timeout period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz sample rate) if the signal
// does not encounter a zero crossing. The zero cross function is independently monitored and implemented
// for each channel. See Table 17.
// Soft Ramp and Zero Cross Enable
// Soft Ramp and Zero Cross Enable dictate that signal-level changes, either by attenuation changes or muting, will occur in 1/8 dB steps and be implemented on a signal zero crossing. The 1/8 dB level change will
// occur after a time-out period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz sample rate) if the signal does not encounter a zero crossing. The zero cross function is independently monitored and implemented for each channel
// DACSoft DACZeroCross Mode
// 0 0 Changes to affect immediately
// 0 1 Zero Cross enabled
// 1 0 Soft Ramp enabled
// 1 1 Soft Ramp and Zero Cross enabled (default)
#define CS4265_DAC_CTL2 0xC
#define CS4265_DAC_CTL2_ZERO_CROSS_EN (uint8_t)(0b01 <<7)
#define CS4265_DAC_CTL2_SOFT_RAMP_EN (uint8_t)(0b10 <<7)
#define CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN (uint8_t)(0b11 <<7)
// Soft Ramp allows level changes, both muting and attenuation, to be implemented by incrementally
// ramping, in 1/8 dB steps, from the current level to the new level at a rate of 1 dB per 8
// left/right clock periods. See Table 17. Zero Cross Enable Zero Cross Enable dictates that
// signal-level changes, either by attenuation changes or muting, will occur on a signal zero
// crossing to minimize audible artifacts. The requested level change will occur after a timeout
// period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz sample rate) if the
// signal does not encounter a zero crossing. The zero cross function is independently monitored and
// implemented for each channel. See Table 17. Soft Ramp and Zero Cross Enable Soft Ramp and Zero
// Cross Enable dictate that signal-level changes, either by attenuation changes or muting, will
// occur in 1/8 dB steps and be implemented on a signal zero crossing. The 1/8 dB level change will
// occur after a time-out period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz
// sample rate) if the signal does not encounter a zero crossing. The zero cross function is
// independently monitored and implemented for each channel DACSoft DACZeroCross Mode 0 0 Changes to
// affect immediately 0 1 Zero Cross enabled 1 0 Soft Ramp enabled 1 1 Soft Ramp and Zero Cross
// enabled (default)
#define CS4265_DAC_CTL2 0xC
#define CS4265_DAC_CTL2_ZERO_CROSS_EN (uint8_t)(0b01 << 7)
#define CS4265_DAC_CTL2_SOFT_RAMP_EN (uint8_t)(0b10 << 7)
#define CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN (uint8_t)(0b11 << 7)
#define CS4265_INT_STATUS 0xD
#define CS4265_INT_STATUS_ADC_UNDF (1 << 0)
#define CS4265_INT_STATUS_ADC_OVF (1 << 1)
#define CS4265_INT_STATUS_CLKERR (1 << 3)
#define CS4265_INT_STATUS 0xD
#define CS4265_INT_STATUS_ADC_UNDF (1<<0)
#define CS4265_INT_STATUS_ADC_OVF (1<<1)
#define CS4265_INT_STATUS_CLKERR (1<<3)
#define CS4265_INT_MASK 0xE
#define CS4265_STATUS_MODE_MSB 0xF
#define CS4265_STATUS_MODE_LSB 0x10
// Transmitter Control 1 - Address 11h
#define CS4265_SPDIF_CTL1 0x11
#define CS4265_INT_MASK 0xE
#define CS4265_STATUS_MODE_MSB 0xF
#define CS4265_STATUS_MODE_LSB 0x10
//Transmitter Control 1 - Address 11h
#define CS4265_SPDIF_CTL1 0x11
#define CS4265_SPDIF_CTL2 0x12
#define CS4265_SPDIF_CTL2 0x12
// Transmitter Digital Interface Format (Bits 7:6)
// Function:
// The required relationship between LRCK, SCLK and SDIN for the transmitter is defined
@@ -220,343 +213,348 @@ struct cs4265_private cs4265;
// 0 1 I²S, up to 24-bit data 1 6
// 1 0 Right-Justified, 16-bit Data 2 7
// 1 1 Right-Justified, 24-bit Data 3 7
#define CS4265_SPDIF_CTL2_MMTLR (1<<0)
#define CS4265_SPDIF_CTL2_MMTCS (1<<1)
#define CS4265_SPDIF_CTL2_MMT (1<<2)
#define CS4265_SPDIF_CTL2_V (1<<3)
#define CS4265_SPDIF_CTL2_TXMUTE (1<<4)
#define CS4265_SPDIF_CTL2_TXOFF (1<<5)
#define CS4265_SPDIF_CTL2_MUTE (1 << 4)
#define CS4265_SPDIF_CTL2_DIF (3 << 6)
#define CS4265_SPDIF_CTL2_DIF0 (1 << 6)
#define CS4265_SPDIF_CTL2_DIF1 (1 << 7)
#define CS4265_SPDIF_CTL2_MMTLR (1 << 0)
#define CS4265_SPDIF_CTL2_MMTCS (1 << 1)
#define CS4265_SPDIF_CTL2_MMT (1 << 2)
#define CS4265_SPDIF_CTL2_V (1 << 3)
#define CS4265_SPDIF_CTL2_TXMUTE (1 << 4)
#define CS4265_SPDIF_CTL2_TXOFF (1 << 5)
#define CS4265_SPDIF_CTL2_MUTE (1 << 4)
#define CS4265_SPDIF_CTL2_DIF (3 << 6)
#define CS4265_SPDIF_CTL2_DIF0 (1 << 6)
#define CS4265_SPDIF_CTL2_DIF1 (1 << 7)
#define CS4265_C_DATA_BUFF 0x13
#define CS4265_MAX_REGISTER 0x2A
#define CS4265_C_DATA_BUFF 0x13
#define CS4265_MAX_REGISTER 0x2A
struct cs4265_clk_para {
uint32_t mclk;
uint32_t rate;
uint8_t fm_mode; /* values 1, 2, or 4 */
uint8_t mclkdiv;
uint32_t mclk;
uint32_t rate;
uint8_t fm_mode; /* values 1, 2, or 4 */
uint8_t mclkdiv;
};
static const struct cs4265_clk_para clk_map_table[] = {
/*32k*/
{8192000, 32000, 0, 0},
{12288000, 32000, 0, 1},
{16384000, 32000, 0, 2},
{24576000, 32000, 0, 3},
{32768000, 32000, 0, 4},
/*32k*/
{8192000, 32000, 0, 0},
{12288000, 32000, 0, 1},
{16384000, 32000, 0, 2},
{24576000, 32000, 0, 3},
{32768000, 32000, 0, 4},
/*44.1k*/
{11289600, 44100, 0, 0},
{16934400, 44100, 0, 1},
{22579200, 44100, 0, 2},
{33868000, 44100, 0, 3},
{45158400, 44100, 0, 4},
/*44.1k*/
{11289600, 44100, 0, 0},
{16934400, 44100, 0, 1},
{22579200, 44100, 0, 2},
{33868000, 44100, 0, 3},
{45158400, 44100, 0, 4},
/*48k*/
{12288000, 48000, 0, 0},
{18432000, 48000, 0, 1},
{24576000, 48000, 0, 2},
{36864000, 48000, 0, 3},
{49152000, 48000, 0, 4},
/*48k*/
{12288000, 48000, 0, 0},
{18432000, 48000, 0, 1},
{24576000, 48000, 0, 2},
{36864000, 48000, 0, 3},
{49152000, 48000, 0, 4},
/*64k*/
{8192000, 64000, 1, 0},
{12288000, 64000, 1, 1},
{16934400, 64000, 1, 2},
{24576000, 64000, 1, 3},
{32768000, 64000, 1, 4},
/*64k*/
{8192000, 64000, 1, 0},
{12288000, 64000, 1, 1},
{16934400, 64000, 1, 2},
{24576000, 64000, 1, 3},
{32768000, 64000, 1, 4},
/* 88.2k */
{11289600, 88200, 1, 0},
{16934400, 88200, 1, 1},
{22579200, 88200, 1, 2},
{33868000, 88200, 1, 3},
{45158400, 88200, 1, 4},
/* 88.2k */
{11289600, 88200, 1, 0},
{16934400, 88200, 1, 1},
{22579200, 88200, 1, 2},
{33868000, 88200, 1, 3},
{45158400, 88200, 1, 4},
/* 96k */
{12288000, 96000, 1, 0},
{18432000, 96000, 1, 1},
{24576000, 96000, 1, 2},
{36864000, 96000, 1, 3},
{49152000, 96000, 1, 4},
/* 96k */
{12288000, 96000, 1, 0},
{18432000, 96000, 1, 1},
{24576000, 96000, 1, 2},
{36864000, 96000, 1, 3},
{49152000, 96000, 1, 4},
/* 128k */
{8192000, 128000, 2, 0},
{12288000, 128000, 2, 1},
{16934400, 128000, 2, 2},
{24576000, 128000, 2, 3},
{32768000, 128000, 2, 4},
/* 128k */
{8192000, 128000, 2, 0},
{12288000, 128000, 2, 1},
{16934400, 128000, 2, 2},
{24576000, 128000, 2, 3},
{32768000, 128000, 2, 4},
/* 176.4k */
{11289600, 176400, 2, 0},
{16934400, 176400, 2, 1},
{22579200, 176400, 2, 2},
{33868000, 176400, 2, 3},
{49152000, 176400, 2, 4},
/* 176.4k */
{11289600, 176400, 2, 0},
{16934400, 176400, 2, 1},
{22579200, 176400, 2, 2},
{33868000, 176400, 2, 3},
{49152000, 176400, 2, 4},
/* 192k */
{12288000, 192000, 2, 0},
{18432000, 192000, 2, 1},
{24576000, 192000, 2, 2},
{36864000, 192000, 2, 3},
{49152000, 192000, 2, 4},
/* 192k */
{12288000, 192000, 2, 0},
{18432000, 192000, 2, 1},
{24576000, 192000, 2, 2},
{36864000, 192000, 2, 3},
{49152000, 192000, 2, 4},
};
static const struct cs4265_cmd_s cs4265_init_sequence[] = {
{CS4265_PWRCTL, CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_FREEZE | CS4265_PWRCTL_PDN_DAC | CS4265_PWRCTL_PDN_MIC},
{CS4265_DAC_CTL, CS4265_DAC_CTL_DIF0 | CS4265_DAC_CTL_MUTE},
{CS4265_SIG_SEL, CS4265_SIG_SEL_SDIN1},/// SDIN1
{CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_DIF0 },//
{CS4265_ADC_CTL, 0x00 },// // Set the serial audio port in slave mode
{CS4265_MCLK_FREQ, CS4265_MCLK_FREQ_1_0X },// // no divider
{CS4265_CHB_PGA_CTL, 0x00 },// // sets the gain to 0db on channel B
{CS4265_CHA_PGA_CTL, 0x00 },// // sets the gain to 0db on channel A
{CS4265_ADC_CTL2, 0x19 },//
{CS4265_DAC_CHA_VOL,CS4265_DAC_VOL_ATT_000_0 },// Full volume out
{CS4265_DAC_CHB_VOL, CS4265_DAC_VOL_ATT_000_0 },// // Full volume out
{CS4265_DAC_CTL2, CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN },//
{CS4265_SPDIF_CTL1, 0x00 },//
{CS4265_INT_MASK, 0x00 },//
{CS4265_STATUS_MODE_MSB, 0x00 },//
{CS4265_STATUS_MODE_LSB, 0x00 },//
{0xff,0xff}
};
{CS4265_PWRCTL, CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_FREEZE | CS4265_PWRCTL_PDN_DAC |
CS4265_PWRCTL_PDN_MIC},
{CS4265_DAC_CTL, CS4265_DAC_CTL_DIF0 | CS4265_DAC_CTL_MUTE},
{CS4265_SIG_SEL, CS4265_SIG_SEL_SDIN1}, /// SDIN1
{CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_DIF0}, //
{CS4265_ADC_CTL, 0x00}, // // Set the serial audio port in slave mode
{CS4265_MCLK_FREQ, CS4265_MCLK_FREQ_1_0X}, // // no divider
{CS4265_CHB_PGA_CTL, 0x00}, // // sets the gain to 0db on channel B
{CS4265_CHA_PGA_CTL, 0x00}, // // sets the gain to 0db on channel A
{CS4265_ADC_CTL2, 0x19}, //
{CS4265_DAC_CHA_VOL, CS4265_DAC_VOL_ATT_000_0}, // Full volume out
{CS4265_DAC_CHB_VOL, CS4265_DAC_VOL_ATT_000_0}, // // Full volume out
{CS4265_DAC_CTL2, CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN}, //
{CS4265_SPDIF_CTL1, 0x00}, //
{CS4265_INT_MASK, 0x00}, //
{CS4265_STATUS_MODE_MSB, 0x00}, //
{CS4265_STATUS_MODE_LSB, 0x00}, //
{0xff, 0xff}};
// matching orders
typedef enum { cs4265_ACTIVE = 0, cs4265_STANDBY, cs4265_DOWN, cs4265_ANALOGUE_OFF, cs4265_ANALOGUE_ON, cs4265_VOLUME } dac_cmd_e;
typedef enum {
cs4265_ACTIVE = 0,
cs4265_STANDBY,
cs4265_DOWN,
cs4265_ANALOGUE_OFF,
cs4265_ANALOGUE_ON,
cs4265_VOLUME
} dac_cmd_e;
static int cs4265_addr;
static void dac_cmd(dac_cmd_e cmd, ...);
static int cs4265_detect(void);
static uint32_t calc_rnd_mclk_freq(){
float m_scale = (cs4265.i2s_config->sample_rate > 96000 && cs4265.i2s_config->bits_per_sample > 16) ? 4 : 8;
float num_channels = cs4265.i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1;
return (uint32_t) round(cs4265.i2s_config->bits_per_sample*i2s_get_clk(cs4265.i2c_port)* m_scale*num_channels/100)*100;
static uint32_t calc_rnd_mclk_freq() {
float m_scale =
(cs4265.i2s_config->sample_rate > 96000 && cs4265.i2s_config->bits_per_sample > 16) ? 4 : 8;
float num_channels = cs4265.i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1;
return (uint32_t)round(cs4265.i2s_config->bits_per_sample * i2s_get_clk(cs4265.port) *
m_scale * num_channels / 100) *
100;
}
static int cs4265_get_clk_index(int mclk, int rate)
{
for (int i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
if (clk_map_table[i].rate == rate &&
clk_map_table[i].mclk == mclk)
return i;
}
return -1;
static int cs4265_get_clk_index(int mclk, int rate) {
for (int i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
if (clk_map_table[i].rate == rate && clk_map_table[i].mclk == mclk) return i;
}
return -1;
}
static esp_err_t set_clock(){
static esp_err_t set_clock() {
esp_err_t err = ESP_OK;
uint32_t mclk = calc_rnd_mclk_freq();
int index = cs4265_get_clk_index(mclk,cs4265.i2s_config->sample_rate );
if (index >= 0) {
ESP_LOGD(TAG, "Setting clock for mclk %u, rate %u (fm mode:%u, clk div:%u))", mclk,cs4265.i2s_config->sample_rate,clk_map_table[index].fm_mode,clk_map_table[index].mclkdiv);
err=cs4265_update_bit(CS4265_ADC_CTL,CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
err|=cs4265_update_bit( CS4265_MCLK_FREQ,CS4265_MCLK_FREQ_MASK,clk_map_table[index].mclkdiv << 4);
} else {
ESP_LOGE(TAG,"can't get correct mclk for ");
return -1;
}
uint32_t mclk = calc_rnd_mclk_freq();
int index = cs4265_get_clk_index(mclk, cs4265.i2s_config->sample_rate);
if (index >= 0) {
ESP_LOGD(TAG, "Setting clock for mclk %u, rate %u (fm mode:%u, clk div:%u))", mclk,
cs4265.i2s_config->sample_rate, clk_map_table[index].fm_mode,
clk_map_table[index].mclkdiv);
err = cs4265_update_bit(CS4265_ADC_CTL, CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
err |= cs4265_update_bit(
CS4265_MCLK_FREQ, CS4265_MCLK_FREQ_MASK, clk_map_table[index].mclkdiv << 4);
} else {
ESP_LOGE(TAG, "can't get correct mclk for ");
return -1;
}
return err;
}
static void get_status(){
uint8_t sts1= adac_read_byte(cs4265_addr, CS4265_INT_STATUS);
ESP_LOGD(TAG,"Status: %s",sts1&CS4265_INT_STATUS_CLKERR?"CLK Error":"CLK OK");
static void get_status() {
uint8_t sts1 = adac_read_byte(cs4265_addr, CS4265_INT_STATUS);
ESP_LOGD(TAG, "Status: %s", sts1 & CS4265_INT_STATUS_CLKERR ? "CLK Error" : "CLK OK");
}
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {
// find which TAS we are using (if any)
cs4265_addr = adac_init(config, i2c_port);
cs4265.i2s_config = i2s_config;
cs4265.i2c_port=i2c_port;
if (!cs4265_addr) cs4265_addr = cs4265_detect();
if (!cs4265_addr) {
ESP_LOGE(TAG, "No cs4265 detected");
adac_deinit();
return false;
}
#if BYTES_PER_FRAME == 8
ESP_LOGE(TAG,"The CS4265 does not support 32 bits mode. ");
adac_deinit();
return false;
#endif
// configure MLK
ESP_LOGD(TAG, "Configuring MCLK on GPIO0");
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_WRITE(PIN_CTRL, 0xFFFFFFF0);
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
for (int i = 0; cs4265_init_sequence[i].reg != 0xff; i++) {
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (cs4265_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].reg, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].value, I2C_MASTER_NACK);
ESP_LOGD(TAG, "i2c write %x at %u", cs4265_init_sequence[i].reg, cs4265_init_sequence[i].value);
}
static bool init(sys_dac_config * config, i2s_config_t* i2s_config, bool* mck) {
i2c_master_stop(i2c_cmd);
esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
// find which CS4265 we are using (if any)
cs4265_addr = adac_init(config);
cs4265.i2s_config = i2s_config;
if (!cs4265_addr) cs4265_addr = cs4265_detect();
if (!cs4265_addr) {
ESP_LOGE(TAG, "No cs4265 detected");
adac_deinit();
return false;
}
cs4265.port = config->i2c.port-sys_i2c_port_PORT0;
#if BYTES_PER_FRAME == 8
ESP_LOGE(TAG, "The CS4265 does not support 32 bits mode. ");
adac_deinit();
return false;
#endif
// we need mclk for this DAC
*mck = true;
// Initialize the chip
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
for (int i = 0; cs4265_init_sequence[i].reg != 0xff; i++) {
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (cs4265_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].reg, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].value, I2C_MASTER_NACK);
ESP_LOGD(
TAG, "i2c write %x at %u", cs4265_init_sequence[i].reg, cs4265_init_sequence[i].value);
}
i2c_master_stop(i2c_cmd);
esp_err_t res = i2c_master_cmd_begin(cs4265.port, i2c_cmd, 500 / portTICK_RATE_MS);
i2c_cmd_link_delete(i2c_cmd);
if (res != ESP_OK) {
ESP_LOGE(TAG, "could not intialize cs4265 %d", res);
return false;
}
if (res != ESP_OK) {
ESP_LOGE(TAG, "could not intialize cs4265 %d", res);
return false;
}
return true;
return true;
}
}
static esp_err_t cs4265_update_bit(uint8_t reg_no,uint8_t mask,uint8_t val ){
esp_err_t ret=ESP_OK;
uint8_t old= adac_read_byte(cs4265_addr, reg_no);
static esp_err_t cs4265_update_bit(uint8_t reg_no, uint8_t mask, uint8_t val) {
esp_err_t ret = ESP_OK;
uint8_t old = adac_read_byte(cs4265_addr, reg_no);
uint8_t newval = (old & ~mask) | (val & mask);
bool change = old != newval;
if (change){
ret = adac_write_byte(cs4265_addr, reg_no, newval);
if(ret != ESP_OK){
ESP_LOGE(TAG,"Unable to change dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask 0x%02x ",reg_no,old,newval,val,mask);
}
else {
ESP_LOGD(TAG,"Changed dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask 0x%02x ",reg_no,old,newval,val,mask);
}
}
bool change = old != newval;
if (change) {
ret = adac_write_byte(cs4265_addr, reg_no, newval);
if (ret != ESP_OK) {
ESP_LOGE(TAG,
"Unable to change dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask "
"0x%02x ",
reg_no, old, newval, val, mask);
} else {
ESP_LOGD(TAG,
"Changed dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask 0x%02x ",
reg_no, old, newval, val, mask);
}
}
return ret;
}
/****************************************************************************************
* change volume
*/
static bool volume(unsigned left, unsigned right) {
return false;
}
static bool volume(unsigned left, unsigned right) { return false; }
/****************************************************************************************
* power
*/
static void power(adac_power_e mode) {
switch(mode) {
case ADAC_STANDBY:
dac_cmd(cs4265_STANDBY);
break;
case ADAC_ON:
dac_cmd(cs4265_ACTIVE);
break;
case ADAC_OFF:
dac_cmd(cs4265_DOWN);
break;
default:
ESP_LOGW(TAG, "unknown DAC command");
break;
}
switch (mode) {
case ADAC_STANDBY:
dac_cmd(cs4265_STANDBY);
break;
case ADAC_ON:
dac_cmd(cs4265_ACTIVE);
break;
case ADAC_OFF:
dac_cmd(cs4265_DOWN);
break;
default:
ESP_LOGW(TAG, "unknown DAC command");
break;
}
}
/****************************************************************************************
* speaker
*/
static void speaker(bool active) {
if (active) dac_cmd(cs4265_ANALOGUE_ON);
else dac_cmd(cs4265_ANALOGUE_OFF);
}
if (active)
dac_cmd(cs4265_ANALOGUE_ON);
else
dac_cmd(cs4265_ANALOGUE_OFF);
}
/****************************************************************************************
* headset
*/
static void headset(bool active) { }
static void headset(bool active) {}
/****************************************************************************************
* DAC specific commands
*/
void dac_cmd(dac_cmd_e cmd, ...) {
va_list args;
esp_err_t ret = ESP_OK;
va_start(args, cmd);
va_list args;
esp_err_t ret = ESP_OK;
switch(cmd) {
case cs4265_VOLUME:
ESP_LOGE(TAG, "DAC volume not handled yet");
break;
va_start(args, cmd);
switch (cmd) {
case cs4265_VOLUME:
ESP_LOGE(TAG, "DAC volume not handled yet");
break;
case cs4265_ACTIVE:
ESP_LOGD(TAG, "Activating DAC");
adac_write_byte(cs4265_addr, CS4265_PWRCTL,0);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,0);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,0);
cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,0);
break;
ESP_LOGD(TAG, "Activating DAC");
adac_write_byte(cs4265_addr, CS4265_PWRCTL, 0);
cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXOFF, 0);
cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXMUTE, 0);
cs4265_update_bit(CS4265_DAC_CTL, CS4265_DAC_CTL_MUTE, 0);
break;
case cs4265_STANDBY:
ESP_LOGD(TAG, "DAC Stand-by");
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,CS4265_SPDIF_CTL2_TXOFF);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,CS4265_SPDIF_CTL2_TXMUTE);
cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,CS4265_DAC_CTL_MUTE);
break;
ESP_LOGD(TAG, "DAC Stand-by");
cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXOFF, CS4265_SPDIF_CTL2_TXOFF);
cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXMUTE, CS4265_SPDIF_CTL2_TXMUTE);
cs4265_update_bit(CS4265_DAC_CTL, CS4265_DAC_CTL_MUTE, CS4265_DAC_CTL_MUTE);
break;
case cs4265_DOWN:
ESP_LOGD(TAG, "DAC Power Down");
adac_write_byte(cs4265_addr, CS4265_PWRCTL,CS4265_PWRCTL_PDN_ALL);
break;
ESP_LOGD(TAG, "DAC Power Down");
adac_write_byte(cs4265_addr, CS4265_PWRCTL, CS4265_PWRCTL_PDN_ALL);
break;
case cs4265_ANALOGUE_OFF:
ESP_LOGD(TAG, "DAC Analog off");
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,CS4265_SPDIF_CTL2_TXOFF);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,CS4265_SPDIF_CTL2_TXMUTE);
cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,CS4265_DAC_CTL_MUTE);
break;
ESP_LOGD(TAG, "DAC Analog off");
cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXOFF, CS4265_SPDIF_CTL2_TXOFF);
cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXMUTE, CS4265_SPDIF_CTL2_TXMUTE);
cs4265_update_bit(CS4265_DAC_CTL, CS4265_DAC_CTL_MUTE, CS4265_DAC_CTL_MUTE);
break;
case cs4265_ANALOGUE_ON:
ESP_LOGD(TAG, "DAC Analog on");
adac_write_byte(cs4265_addr, CS4265_PWRCTL,0);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,0);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,0);
cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,0);
break;
}
if (ret != ESP_OK) {
ESP_LOGE(TAG, "could not use cs4265 %d", ret);
}
get_status();
// now set the clock
ret=set_clock(cs4265.i2s_config,cs4265.i2c_port);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "could not set the cs4265's clock %d", ret);
}
ESP_LOGD(TAG, "DAC Analog on");
adac_write_byte(cs4265_addr, CS4265_PWRCTL, 0);
cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXOFF, 0);
cs4265_update_bit(CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_TXMUTE, 0);
cs4265_update_bit(CS4265_DAC_CTL, CS4265_DAC_CTL_MUTE, 0);
break;
}
va_end(args);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "could not use cs4265 %d", ret);
}
get_status();
// now set the clock
ret = set_clock();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "could not set the cs4265's clock %d", ret);
}
va_end(args);
}
/****************************************************************************************
* TAS57 detection
*/
static int cs4265_detect(void) {
uint8_t addr[] = {CS4265_PULL_DOWN,CS4265_PULL_UP};
for (int i = 0; i < sizeof(addr); i++) {
ESP_LOGI(TAG,"Looking for CS4265 @0x%x",addr[i]);
uint8_t reg=adac_read_byte(addr[i], CS4265_CHIP_ID);
if(reg==255){
continue;
}
// found a device at that address
uint8_t devid = reg & CS4265_CHIP_ID_MASK;
if (devid != CS4265_CHIP_ID_VAL) {
ESP_LOGE(TAG,"CS4265 Device ID (%X). Expected %X",devid, CS4265_CHIP_ID);
return 0;
}
ESP_LOGI(TAG,"Found DAC @0x%x, Version %x",addr[i], reg & CS4265_REV_ID_MASK);
return addr[i];
}
return 0;
uint8_t addr[] = {CS4265_PULL_DOWN, CS4265_PULL_UP};
ESP_LOGD(TAG, "Detecting chip connection type/address");
for (int i = 0; i < sizeof(addr); i++) {
ESP_LOGI(TAG, "Looking for CS4265 @0x%x", addr[i]);
uint8_t reg = adac_read_byte(addr[i], CS4265_CHIP_ID);
if (reg == 255) {
ESP_LOGD(TAG, "Nothing there");
continue;
}
// found a device at that address
ESP_LOGD(TAG, "Found a device. Check signature");
uint8_t devid = reg & CS4265_CHIP_ID_MASK;
if (devid != CS4265_CHIP_ID_VAL) {
ESP_LOGE(TAG, "CS4265 Device ID (%X). Expected %X", devid, CS4265_CHIP_ID);
return 0;
}
ESP_LOGI(TAG, "Found DAC @0x%x, Version %x", addr[i], reg & CS4265_REV_ID_MASK);
return addr[i];
}
return 0;
}

View File

@@ -1,4 +1,4 @@
/*
/*
* Squeezelite for esp32
*
* (c) Sebastien 2019
@@ -11,14 +11,16 @@
#include <math.h>
#ifdef ESP_PLATFORM
#include "Services.pb.h"
#include "Squeezelite.pb.h"
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
extern sys_squeezelite_config* get_profile(const char* name);
#endif
#include "Configurator.h"
#include "Config.h"
#include "accessors.h"
#include "squeezelite.h"
#if CONFIG_BT_SINK
#include "bt_app_sink.h"
static bool enable_bt_sink;
@@ -34,91 +36,93 @@ static bool enable_cspot;
static bool enable_airplay;
#define RAOP_OUTPUT_SIZE (((RAOP_SAMPLE_RATE * BYTES_PER_FRAME * 2 * 120) / 100) & ~BYTES_PER_FRAME)
#define SYNC_WIN_SLOW 32
#define SYNC_WIN_CHECK 8
#define SYNC_WIN_FAST 2
#define SYNC_WIN_SLOW 32
#define SYNC_WIN_CHECK 8
#define SYNC_WIN_FAST 2
static raop_event_t raop_state;
static sys_Squeezelite * squeezelite;
static raop_event_t raop_state;
static sys_squeezelite_config* squeezelite;
static EXT_RAM_ATTR struct {
bool enabled;
int sum, count, win, errors[SYNC_WIN_SLOW];
s32_t len;
u32_t start_time, playtime;
bool enabled;
int sum, count, win, errors[SYNC_WIN_SLOW];
s32_t len;
u32_t start_time, playtime;
} raop_sync;
#endif
static enum { SINK_RUNNING, SINK_ABORT, SINK_DISCARD } sink_state;
#define LOCK_O mutex_lock(outputbuf->mutex)
#define LOCK_O mutex_lock(outputbuf->mutex)
#define UNLOCK_O mutex_unlock(outputbuf->mutex)
#define LOCK_D mutex_lock(decode.mutex);
#define LOCK_D mutex_lock(decode.mutex);
#define UNLOCK_D mutex_unlock(decode.mutex);
enum { DECODE_BT = 1, DECODE_RAOP, DECODE_CSPOT };
extern struct outputstate output;
extern struct decodestate decode;
extern struct buffer *outputbuf;
extern struct buffer* outputbuf;
// this is the only system-wide loglevel variable
extern log_level loglevel;
/****************************************************************************************
* Common sink data handler
*/
static uint32_t sink_data_handler(const uint8_t *data, uint32_t len, int retries)
{
static uint32_t sink_data_handler(const uint8_t* data, uint32_t len, int retries) {
size_t bytes, space;
uint32_t written = 0;
int wait = retries + 1;
// would be better to lock output, but really, it does not matter
if (!output.external) {
LOG_SDEBUG("Cannot use external sink while LMS is controlling player");
return 0;
}
uint32_t written = 0;
int wait = retries + 1;
LOCK_O;
if (sink_state == SINK_ABORT) sink_state = SINK_RUNNING;
// would be better to lock output, but really, it does not matter
if (!output.external) {
LOG_SDEBUG("Cannot use external sink while LMS is controlling player");
return 0;
}
// there will always be room at some point
while (len && wait && sink_state == SINK_RUNNING) {
bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / (BYTES_PER_FRAME / 4);
bytes = min(len, bytes);
LOCK_O;
if (sink_state == SINK_ABORT) sink_state = SINK_RUNNING;
// there will always be room at some point
while (len && wait && sink_state == SINK_RUNNING) {
bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / (BYTES_PER_FRAME / 4);
bytes = min(len, bytes);
#if BYTES_PER_FRAME == 4
memcpy(outputbuf->writep, data, bytes);
memcpy(outputbuf->writep, data, bytes);
#else
{
s16_t *iptr = (s16_t*) data;
ISAMPLE_T *optr = (ISAMPLE_T *) outputbuf->writep;
size_t n = bytes / 2;
while (n--) *optr++ = *iptr++ << 16;
}
#endif
_buf_inc_writep(outputbuf, bytes * BYTES_PER_FRAME / 4);
space = _buf_space(outputbuf);
{
s16_t* iptr = (s16_t*)data;
ISAMPLE_T* optr = (ISAMPLE_T*)outputbuf->writep;
size_t n = bytes / 2;
while (n--)
*optr++ = *iptr++ << 16;
}
#endif
_buf_inc_writep(outputbuf, bytes * BYTES_PER_FRAME / 4);
space = _buf_space(outputbuf);
len -= bytes;
data += bytes;
len -= bytes;
data += bytes;
written += bytes;
// allow i2s to empty the buffer if needed
if (len && !space) {
if (!retries) break;
wait--;
UNLOCK_O; usleep(50000); LOCK_O;
}
}
if (!wait) {
// allow i2s to empty the buffer if needed
if (len && !space) {
if (!retries) break;
wait--;
UNLOCK_O;
usleep(50000);
LOCK_O;
}
}
if (!wait) {
// re-align the buffer according to what we threw away
_buf_inc_writep(outputbuf, outputbuf->size - (BYTES_PER_FRAME - (len % BYTES_PER_FRAME)));
LOG_WARN("Waited too long, dropping frames %d", len);
}
LOG_WARN("Waited too long, dropping frames %d", len);
}
UNLOCK_O;
return written;
}
@@ -126,77 +130,77 @@ static uint32_t sink_data_handler(const uint8_t *data, uint32_t len, int retries
* BT sink data handler
*/
#if CONFIG_BT_SINK
static void bt_sink_data_handler(const uint8_t *data, uint32_t len) {
static void bt_sink_data_handler(const uint8_t* data, uint32_t len) {
sink_data_handler(data, len, 10);
}
}
/****************************************************************************************
* BT sink command handler
*/
static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
{
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player %d", output.external);
return false;
}
static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args) {
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player %d",
output.external);
return false;
}
LOCK_D;
LOCK_D;
if (cmd != BT_SINK_VOLUME) LOCK_O;
switch(cmd) {
case BT_SINK_AUDIO_STARTED:
_buf_flush(outputbuf);
_buf_limit(outputbuf, 0);
output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
output.external = DECODE_BT;
output.state = OUTPUT_STOPPED;
output.frames_played = 0;
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("BT sink started");
break;
case BT_SINK_AUDIO_STOPPED:
if (output.external == DECODE_BT) {
if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED;
output.external = 0;
output.stop_time = gettime_ms();
LOG_INFO("BT sink stopped");
}
break;
case BT_SINK_PLAY:
output.state = OUTPUT_RUNNING;
LOG_INFO("BT play");
break;
case BT_SINK_STOP:
_buf_flush(outputbuf);
output.state = OUTPUT_STOPPED;
output.stop_time = gettime_ms();
sink_state = SINK_ABORT;
LOG_INFO("BT stop");
break;
case BT_SINK_PAUSE:
output.stop_time = gettime_ms();
LOG_INFO("BT pause, just silence");
break;
case BT_SINK_RATE:
output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
LOG_INFO("Setting BT sample rate %u", output.next_sample_rate);
break;
case BT_SINK_VOLUME: {
u32_t volume = va_arg(args, u32_t);
volume = 65536 * powf(volume / 128.0f, 3);
set_volume(volume, volume);
break;
default:
break;
}
}
if (cmd != BT_SINK_VOLUME) UNLOCK_O;
UNLOCK_D;
if (cmd != BT_SINK_VOLUME) LOCK_O;
return true;
switch (cmd) {
case BT_SINK_AUDIO_STARTED:
_buf_flush(outputbuf);
_buf_limit(outputbuf, 0);
output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
output.external = DECODE_BT;
output.state = OUTPUT_STOPPED;
output.frames_played = 0;
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("BT sink started");
break;
case BT_SINK_AUDIO_STOPPED:
if (output.external == DECODE_BT) {
if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED;
output.external = 0;
output.stop_time = gettime_ms();
LOG_INFO("BT sink stopped");
}
break;
case BT_SINK_PLAY:
output.state = OUTPUT_RUNNING;
LOG_INFO("BT play");
break;
case BT_SINK_STOP:
_buf_flush(outputbuf);
output.state = OUTPUT_STOPPED;
output.stop_time = gettime_ms();
sink_state = SINK_ABORT;
LOG_INFO("BT stop");
break;
case BT_SINK_PAUSE:
output.stop_time = gettime_ms();
LOG_INFO("BT pause, just silence");
break;
case BT_SINK_RATE:
output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
LOG_INFO("Setting BT sample rate %u", output.next_sample_rate);
break;
case BT_SINK_VOLUME: {
u32_t volume = va_arg(args, u32_t);
volume = 65536 * powf(volume / 128.0f, 3);
set_volume(volume, volume);
break;
default:
break;
}
}
if (cmd != BT_SINK_VOLUME) UNLOCK_O;
UNLOCK_D;
return true;
}
#endif
@@ -204,144 +208,154 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
* raop sink data handler
*/
#if CONFIG_AIRPLAY_SINK
static void raop_sink_data_handler(const uint8_t *data, uint32_t len, u32_t playtime) {
raop_sync.playtime = playtime;
raop_sync.len = len;
static void raop_sink_data_handler(const uint8_t* data, uint32_t len, u32_t playtime) {
sink_data_handler(data, len, 10);
}
raop_sync.playtime = playtime;
raop_sync.len = len;
sink_data_handler(data, len, 10);
}
/****************************************************************************************
* AirPlay sink command handler
*/
static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
{
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player %d", output.external);
return false;
}
static bool raop_sink_cmd_handler(raop_event_t event, va_list args) {
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player %d",
output.external);
return false;
}
LOCK_D;
if (event != RAOP_VOLUME) LOCK_O;
// this is async, so player might have been deleted
switch (event) {
case RAOP_TIMING: {
if (!raop_sync.enabled || output.state != OUTPUT_RUNNING || output.frames_played_dmp < output.device_frames) break;
LOCK_D;
u32_t ms, now = gettime_ms();
u32_t level = _buf_used(outputbuf);
int error;
// in how many ms will the most recent block play
ms = (((s32_t)(level - raop_sync.len) / BYTES_PER_FRAME + output.device_frames + output.frames_in_process) * 10) / (RAOP_SAMPLE_RATE / 100) - (s32_t) (now - output.updated);
// when outputbuf is empty, it means we have a network black-out or something
error = level ? (raop_sync.playtime - now) - ms : 0;
if (loglevel == lDEBUG || !level) {
LOG_INFO("head local:%d, remote:%d (delta:%d)", ms, raop_sync.playtime - now, error);
LOG_INFO("obuf:%u, sync_len:%u, devframes:%u, inproc:%u", _buf_used(outputbuf), raop_sync.len, output.device_frames, output.frames_in_process);
}
// calculate sum, error and update sliding window
raop_sync.errors[raop_sync.count++ % raop_sync.win] = error;
raop_sync.sum += error;
error = raop_sync.sum / min(raop_sync.count, raop_sync.win);
if (event != RAOP_VOLUME) LOCK_O;
// wait till we have enough data or there is a strong deviation
if ((raop_sync.count >= raop_sync.win && abs(error) > 10) || (raop_sync.count >= SYNC_WIN_CHECK && abs(error) > 100)) {
if (error < 0) {
output.skip_frames = -(error * RAOP_SAMPLE_RATE) / 1000;
output.state = OUTPUT_SKIP_FRAMES;
LOG_INFO("skipping %u frames (count:%d)", output.skip_frames, raop_sync.count);
} else {
output.pause_frames = (error * RAOP_SAMPLE_RATE) / 1000;
output.state = OUTPUT_PAUSE_FRAMES;
LOG_INFO("pausing for %u frames (count: %d)", output.pause_frames, raop_sync.count);
}
raop_sync.sum = raop_sync.count = 0;
memset(raop_sync.errors, 0, sizeof(raop_sync.errors));
}
// move to normal mode if possible
if (raop_sync.win == 1) {
raop_sync.win = SYNC_WIN_FAST;
LOG_INFO("backend played %u, desired %u, (delta:%d)", ms, raop_sync.playtime - now, error);
} else if (raop_sync.win == SYNC_WIN_FAST && raop_sync.count >= SYNC_WIN_FAST && abs(error) < 10) {
raop_sync.win = SYNC_WIN_SLOW;
LOG_INFO("switching to slow sync mode %u", raop_sync.win);
}
// this is async, so player might have been deleted
switch (event) {
case RAOP_TIMING: {
if (!raop_sync.enabled || output.state != OUTPUT_RUNNING ||
output.frames_played_dmp < output.device_frames)
break;
break;
}
case RAOP_SETUP: {
uint8_t **buffer = va_arg(args, uint8_t**);
size_t *size = va_arg(args, size_t*);
u32_t ms, now = gettime_ms();
u32_t level = _buf_used(outputbuf);
int error;
// steal buffer tail from outputbuf but do not reallocate
*size = _buf_limit(outputbuf, RAOP_OUTPUT_SIZE);
*buffer = outputbuf->writep + RAOP_OUTPUT_SIZE;
// in how many ms will the most recent block play
ms = (((s32_t)(level - raop_sync.len) / BYTES_PER_FRAME + output.device_frames +
output.frames_in_process) *
10) /
(RAOP_SAMPLE_RATE / 100) -
(s32_t)(now - output.updated);
output.frames_played = 0;
output.external = DECODE_RAOP;
output.state = OUTPUT_STOPPED;
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("resizing buffer %u", outputbuf->size);
break;
}
case RAOP_STREAM:
LOG_INFO("Stream", NULL);
raop_state = event;
raop_sync.win = 1;
raop_sync.sum = raop_sync.count = 0;
memset(raop_sync.errors, 0, sizeof(raop_sync.errors));
raop_sync.enabled = !strcasestr(output.device, "BT");
output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
break;
case RAOP_STALLED:
case RAOP_STOP:
output.external = 0;
__attribute__ ((fallthrough));
case RAOP_FLUSH:
LOG_INFO("%s", event == RAOP_FLUSH ? "Flush" : "Stop");
_buf_flush(outputbuf);
raop_state = event;
if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED;
sink_state = SINK_ABORT;
output.frames_played = 0;
output.stop_time = gettime_ms();
break;
case RAOP_PLAY: {
LOG_INFO("Play", NULL);
if (raop_state != RAOP_PLAY) {
output.state = OUTPUT_START_AT;
output.start_at = va_arg(args, u32_t);
raop_sync.start_time = output.start_at;
LOG_INFO("Starting at %u (in %d ms)", output.start_at, output.start_at - gettime_ms());
}
raop_state = event;
break;
}
case RAOP_VOLUME: {
float volume = va_arg(args, double);
LOG_INFO("Volume[0..1] %0.4f", volume);
volume = 65536 * powf(volume, 3);
set_volume(volume, volume);
break;
}
default:
break;
}
if (event != RAOP_VOLUME) UNLOCK_O;
UNLOCK_D;
return true;
// when outputbuf is empty, it means we have a network black-out or something
error = level ? (raop_sync.playtime - now) - ms : 0;
if (loglevel == lDEBUG || !level) {
LOG_INFO("head local:%d, remote:%d (delta:%d)", ms, raop_sync.playtime - now, error);
LOG_INFO("obuf:%u, sync_len:%u, devframes:%u, inproc:%u", _buf_used(outputbuf),
raop_sync.len, output.device_frames, output.frames_in_process);
}
// calculate sum, error and update sliding window
raop_sync.errors[raop_sync.count++ % raop_sync.win] = error;
raop_sync.sum += error;
error = raop_sync.sum / min(raop_sync.count, raop_sync.win);
// wait till we have enough data or there is a strong deviation
if ((raop_sync.count >= raop_sync.win && abs(error) > 10) ||
(raop_sync.count >= SYNC_WIN_CHECK && abs(error) > 100)) {
if (error < 0) {
output.skip_frames = -(error * RAOP_SAMPLE_RATE) / 1000;
output.state = OUTPUT_SKIP_FRAMES;
LOG_INFO("skipping %u frames (count:%d)", output.skip_frames, raop_sync.count);
} else {
output.pause_frames = (error * RAOP_SAMPLE_RATE) / 1000;
output.state = OUTPUT_PAUSE_FRAMES;
LOG_INFO("pausing for %u frames (count: %d)", output.pause_frames, raop_sync.count);
}
raop_sync.sum = raop_sync.count = 0;
memset(raop_sync.errors, 0, sizeof(raop_sync.errors));
}
// move to normal mode if possible
if (raop_sync.win == 1) {
raop_sync.win = SYNC_WIN_FAST;
LOG_INFO(
"backend played %u, desired %u, (delta:%d)", ms, raop_sync.playtime - now, error);
} else if (raop_sync.win == SYNC_WIN_FAST && raop_sync.count >= SYNC_WIN_FAST &&
abs(error) < 10) {
raop_sync.win = SYNC_WIN_SLOW;
LOG_INFO("switching to slow sync mode %u", raop_sync.win);
}
break;
}
case RAOP_SETUP: {
uint8_t** buffer = va_arg(args, uint8_t**);
size_t* size = va_arg(args, size_t*);
// steal buffer tail from outputbuf but do not reallocate
*size = _buf_limit(outputbuf, RAOP_OUTPUT_SIZE);
*buffer = outputbuf->writep + RAOP_OUTPUT_SIZE;
output.frames_played = 0;
output.external = DECODE_RAOP;
output.state = OUTPUT_STOPPED;
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("resizing buffer %u", outputbuf->size);
break;
}
case RAOP_STREAM:
LOG_INFO("Stream", NULL);
raop_state = event;
raop_sync.win = 1;
raop_sync.sum = raop_sync.count = 0;
memset(raop_sync.errors, 0, sizeof(raop_sync.errors));
raop_sync.enabled = !strcasestr(output.device, "BT");
output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
break;
case RAOP_STALLED:
case RAOP_STOP:
output.external = 0;
__attribute__((fallthrough));
case RAOP_FLUSH:
LOG_INFO("%s", event == RAOP_FLUSH ? "Flush" : "Stop");
_buf_flush(outputbuf);
raop_state = event;
if (output.state > OUTPUT_STOPPED) output.state = OUTPUT_STOPPED;
sink_state = SINK_ABORT;
output.frames_played = 0;
output.stop_time = gettime_ms();
break;
case RAOP_PLAY: {
LOG_INFO("Play", NULL);
if (raop_state != RAOP_PLAY) {
output.state = OUTPUT_START_AT;
output.start_at = va_arg(args, u32_t);
raop_sync.start_time = output.start_at;
LOG_INFO("Starting at %u (in %d ms)", output.start_at, output.start_at - gettime_ms());
}
raop_state = event;
break;
}
case RAOP_VOLUME: {
float volume = va_arg(args, double);
LOG_INFO("Volume[0..1] %0.4f", volume);
volume = 65536 * powf(volume, 3);
set_volume(volume, volume);
break;
}
default:
break;
}
if (event != RAOP_VOLUME) UNLOCK_O;
UNLOCK_D;
return true;
}
#endif
@@ -349,98 +363,98 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
* cspot sink data handler
*/
#if CONFIG_CSPOT_SINK
static uint32_t cspot_sink_data_handler(const uint8_t *data, uint32_t len) {
static uint32_t cspot_sink_data_handler(const uint8_t* data, uint32_t len) {
return sink_data_handler(data, len, 0);
}
}
/****************************************************************************************
* cspot sink command handler
*/
static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
{
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_CSPOT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player %d", output.external);
return false;
}
static bool cspot_cmd_handler(cspot_event_t cmd, va_list args) {
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_CSPOT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player %d",
output.external);
return false;
}
LOCK_D;
if (cmd != CSPOT_VOLUME) LOCK_O;
LOCK_D;
switch(cmd) {
case CSPOT_START:
output.current_sample_rate = output.next_sample_rate = va_arg(args, u32_t);
output.external = DECODE_CSPOT;
output.frames_played = 0;
if (cmd != CSPOT_VOLUME) LOCK_O;
switch (cmd) {
case CSPOT_START:
output.current_sample_rate = output.next_sample_rate = va_arg(args, u32_t);
output.external = DECODE_CSPOT;
output.frames_played = 0;
// in 1/10 of seconds
output.threshold = 25;
output.state = OUTPUT_STOPPED;
output.state = OUTPUT_STOPPED;
sink_state = SINK_ABORT;
_buf_flush(outputbuf);
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("CSpot start track");
break;
case CSPOT_DISC:
_buf_flush(outputbuf);
sink_state = SINK_ABORT;
output.external = 0;
output.state = OUTPUT_STOPPED;
output.stop_time = gettime_ms();
LOG_INFO("CSpot disconnected");
break;
case CSPOT_PLAY:
sink_state = SINK_RUNNING;
output.state = OUTPUT_RUNNING;
LOG_INFO("CSpot play");
break;
case CSPOT_SEEK:
_buf_flush(outputbuf);
sink_state = SINK_ABORT;
LOG_INFO("CSpot seek by %d", va_arg(args, uint32_t));
break;
case CSPOT_FLUSH:
_buf_flush(outputbuf);
sink_state = SINK_DISCARD;
output.state = OUTPUT_STOPPED;
LOG_INFO("CSpot flush");
break;
case CSPOT_PAUSE:
output.state = OUTPUT_STOPPED;
output.stop_time = gettime_ms();
LOG_INFO("CSpot pause");
break;
_buf_flush(outputbuf);
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("CSpot start track");
break;
case CSPOT_DISC:
_buf_flush(outputbuf);
sink_state = SINK_ABORT;
output.external = 0;
output.state = OUTPUT_STOPPED;
output.stop_time = gettime_ms();
LOG_INFO("CSpot disconnected");
break;
case CSPOT_PLAY:
sink_state = SINK_RUNNING;
output.state = OUTPUT_RUNNING;
LOG_INFO("CSpot play");
break;
case CSPOT_SEEK:
_buf_flush(outputbuf);
sink_state = SINK_ABORT;
LOG_INFO("CSpot seek by %d", va_arg(args, uint32_t));
break;
case CSPOT_FLUSH:
_buf_flush(outputbuf);
sink_state = SINK_DISCARD;
output.state = OUTPUT_STOPPED;
LOG_INFO("CSpot flush");
break;
case CSPOT_PAUSE:
output.state = OUTPUT_STOPPED;
output.stop_time = gettime_ms();
LOG_INFO("CSpot pause");
break;
case CSPOT_TRACK_MARK:
output.track_start = outputbuf->writep;
break;
case CSPOT_QUERY_REMAINING: {
uint32_t *remaining = va_arg(args, uint32_t*);
uint32_t* remaining = va_arg(args, uint32_t*);
*remaining = (_buf_used(outputbuf) * 1000) / (output.current_sample_rate * BYTES_PER_FRAME);
break;
break;
}
case CSPOT_QUERY_STARTED: {
uint32_t *started = va_arg(args, uint32_t*);
uint32_t* started = va_arg(args, uint32_t*);
*started = output.track_started;
// this is a read_and_clear event
output.track_started = false;
break;
break;
}
case CSPOT_VOLUME: {
u32_t volume = va_arg(args, u32_t);
LOG_INFO("CSpot volume %u", volume);
volume = 65536 * powf(volume / 65536.0f, 4);
set_volume(volume, volume);
break;
default:
break;
}
}
if (cmd != CSPOT_VOLUME) UNLOCK_O;
UNLOCK_D;
return true;
case CSPOT_VOLUME: {
u32_t volume = va_arg(args, u32_t);
LOG_INFO("CSpot volume %u", volume);
volume = 65536 * powf(volume / 65536.0f, 4);
set_volume(volume, volume);
break;
default:
break;
}
}
if (cmd != CSPOT_VOLUME) UNLOCK_O;
UNLOCK_D;
return true;
}
#endif
@@ -448,80 +462,79 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
* We provide the generic codec register option
*/
void register_external(void) {
sys_BluetoothSink * bt_sink;
sys_AirPlay * airplay;
sys_Spotify * spotify;
squeezelite = get_profile(NULL); // get the active profile
#if CONFIG_BT_SINK
enable_bt_sink= (SYS_SERVICES_BTSINK(bt_sink) && bt_sink->enabled);
if ( enable_bt_sink) {
#pragma message ("Is the BT sink logic correct?")
if(SYS_SERVICES_SQUEEZELITE(squeezelite) && squeezelite->output_type == sys_OutputTypeEnum_OUTPUT_Bluetooth ){
LOG_ERROR("BT Sink cannot be enabled with Bluetooth output");
}
else {
bt_sink_init(bt_sink_cmd_handler, bt_sink_data_handler);
}
}
#endif
sys_services_bt_sink* bt_sink;
enable_bt_sink = (sys_services_config_BTSINK(bt_sink) && bt_sink->enabled);
if (enable_bt_sink && squeezelite) {
#pragma message("Is the BT sink logic correct?")
if (squeezelite->output_type == sys_squeezelite_outputs_BT) {
LOG_ERROR("BT Sink cannot be enabled with Bluetooth output");
} else {
bt_sink_init(bt_sink_cmd_handler, bt_sink_data_handler);
}
}
#endif
#if CONFIG_AIRPLAY_SINK
enable_airplay = SYS_SERVICES_AIRPLAY(airplay) && airplay->enabled;
if (enable_airplay){
raop_sink_init(raop_sink_cmd_handler, raop_sink_data_handler);
LOG_INFO("Initializing AirPlay sink");
}
#endif
#if CONFIG_CSPOT_SINK
enable_cspot = SYS_SERVICES_SPOTIFY(spotify) && spotify->enabled;
if (enable_cspot){
cspot_sink_init(cspot_cmd_handler, cspot_sink_data_handler);
LOG_INFO("Initializing CSpot sink");
}
#endif
sys_airplay_config* airplay;
enable_airplay = sys_services_config_AIRPLAY(airplay) && airplay->enabled;
if (enable_airplay) {
raop_sink_init(raop_sink_cmd_handler, raop_sink_data_handler);
LOG_INFO("Initializing AirPlay sink");
}
#endif
#if CONFIG_CSPOT_SINK
sys_spotify_config* spotify;
enable_cspot = sys_services_config_SPOTIFY(spotify) && spotify->enabled;
if (enable_cspot) {
cspot_sink_init(cspot_cmd_handler, cspot_sink_data_handler);
LOG_INFO("Initializing CSpot sink");
}
#endif
}
void deregister_external(void) {
#if CONFIG_BT_SINK
sys_Squeezelite * squeezelite;
if(SYS_SERVICES_SQUEEZELITE(squeezelite) && squeezelite->output_type != sys_OutputTypeEnum_OUTPUT_Bluetooth && enable_bt_sink ){
bt_sink_deinit();
}
squeezelite = get_profile(NULL); // get the active profile
if (squeezelite && squeezelite->output_type == sys_squeezelite_outputs_BT && enable_bt_sink) {
bt_sink_deinit();
}
#endif
#if CONFIG_AIRPLAY_SINK
if (enable_airplay){
LOG_INFO("Stopping AirPlay sink");
raop_sink_deinit();
}
if (enable_airplay) {
LOG_INFO("Stopping AirPlay sink");
raop_sink_deinit();
}
#endif
#if CONFIG_CSPOT_SINK
if (enable_cspot){
LOG_INFO("Stopping CSpot sink");
cspot_sink_deinit();
}
if (enable_cspot) {
LOG_INFO("Stopping CSpot sink");
cspot_sink_deinit();
}
#endif
}
void decode_restore(int external) {
switch (external) {
#if CONFIG_BT_SINK
case DECODE_BT:
bt_disconnect();
break;
switch (external) {
#if CONFIG_BT_SINK
case DECODE_BT:
bt_disconnect();
break;
#endif
#if CONFIG_AIRPLAY_SINK
case DECODE_RAOP:
raop_disconnect();
break;
case DECODE_RAOP:
raop_disconnect();
break;
#endif
#if CONFIG_CSPOT_SINK
case DECODE_CSPOT:
cspot_disconnect();
break;
#endif
}
case DECODE_CSPOT:
cspot_disconnect();
break;
#endif
}
}

View File

@@ -17,13 +17,13 @@
#include "esp_wifi.h"
#include "esp_log.h"
#include "monitor.h"
#include "Configurator.h"
#include "Config.h"
#include "messaging.h"
#include "gpio_exp.h"
#include "accessors.h"
static const char TAG[] = "embedded";
static sys_GPIO * power=NULL;
static sys_gpio_config * power=NULL;
extern void sb_controls_init(void);
extern bool sb_displayer_init(void);
@@ -69,7 +69,9 @@ uint32_t _gettime_ms_(void) {
int embedded_init(void) {
mutex_create(slimp_mutex);
ESP_LOGI(TAG,"Initializing controls");
sb_controls_init();
ESP_LOGI(TAG,"Initializing displayer");
custom_player_id = sb_displayer_init() ? 100 : 101;
@@ -88,10 +90,11 @@ void embedded_exit(int code) {
}
void powering(bool on) {
if (power->pin != -1) {
if (SYS_GPIOS_NAME(power,power) && power->pin != -1) {
ESP_LOGI(TAG, "powering player %s", on ? "ON" : "OFF");
gpio_set_level_x(power->pin, on ? power->level : !power->level);
}
}
u16_t get_RSSI(void) {

View File

@@ -9,7 +9,7 @@
*/
#include "math.h"
#include "Configurator.h"
#include "Config.h"
#include "squeezelite.h"
#include "equalizer.h"
#include "esp_equalizer.h"
@@ -24,7 +24,7 @@ static EXT_RAM_ATTR struct {
float volume;
float loudness_gain[EQ_BANDS];
bool update;
sys_Equalizer *state;
sys_equalizer_config *state;
} equalizer;
@@ -83,19 +83,18 @@ static void calculate_loudness(void) {
* initialize equalizer
*/
void equalizer_init(void) {
sys_Services * services;
sys_Equalizer blank_eq = sys_Equalizer_init_default;
sys_services_config * services;
sys_equalizer_config blank_eq = sys_equalizer_config_init_default;
equalizer.state = &sys_state->equalizer;
if(!sys_state->has_equalizer ){
sys_state->has_equalizer = true;
if(SYS_SERVICES(services) && services->has_equalizer){
memcpy(equalizer.state,&services->equalizer,sizeof(sys_Equalizer));
if(sys_services_config(services) && services->has_equalizer){
memcpy(equalizer.state,&services->equalizer,sizeof(sys_equalizer_config));
}
else {
memcpy(equalizer.state,&blank_eq,sizeof(sys_Equalizer));
memcpy(equalizer.state,&blank_eq,sizeof(sys_equalizer_config));
}
configurator_raise_state_changed();
config_raise_state_changed();
}
}
@@ -160,7 +159,7 @@ void equalizer_set_gain(int8_t *gain) {
// update only if something changed
if (!memcmp(&equalizer.state->gains, gain, EQ_BANDS)) {
equalizer.update = true;
configurator_raise_state_changed();
config_raise_state_changed();
}
LOG_INFO("equalizer gain %s", config);
@@ -178,7 +177,7 @@ void equalizer_set_loudness(uint8_t loudness) {
// update loudness gains as a factor of loudness and volume
if (equalizer.state->loudness != loudness / 10.0) {
equalizer.state->loudness = loudness / 10.0;
configurator_raise_state_changed();
config_raise_state_changed();
calculate_loudness();
equalizer.update = true;
}

View File

@@ -1,34 +1,20 @@
/*
* Squeezelite - lightweight headless squeezebox emulator
* Squeezelite for esp32
*
* (c) Adrian Smith 2012-2015, triode1@btinternet.com
* Ralph Irving 2015-2017, ralph_irving@hotmail.com
* (c) Sebastien 2024
* Philippe G. 2024, philippe_44@outlook.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additions (c) Paul Hermann, 2015-2017 under the same license terms
* -Control of Raspberry pi GPIO for amplifier power
* -Launch script on power status change from LMS
*/
#include "Config.h"
#include "squeezelite.h"
#include <signal.h>
#include "Configurator.h"
extern bool user_rates;
static unsigned int rates[MAX_SUPPORTED_SAMPLERATES] = {0};
sys_Squeezelite* config;
sys_squeezelite_config* config;
log_level loglevel = lDEBUG;
static void sighandler(int signum) {
slimproto_stop();
@@ -38,8 +24,8 @@ static void sighandler(int signum) {
}
unsigned int* get_rates() {
unsigned int ref[] TEST_RATES;
sys_RatesOption* ratescfg = &config->rates;
unsigned int ref[] TEST_RATES;
sys_squeezelite_rates_opt* ratescfg = &config->rates;
if (!config->has_rates || ((ratescfg->list_count == 0 || ratescfg->list[0] == 0) &&
ratescfg->min == 0 && ratescfg->max == 0)) {
user_rates = false;
@@ -71,73 +57,105 @@ unsigned int* get_rates() {
user_rates = true;
return rates;
}
log_level log_level_from_sys_level(sys_DebugLevelEnum level) {
log_level log_level_from_sys_level(sys_squeezelite_debug_levels level) {
switch (level) {
case sys_DebugLevelEnum_DEFAULT:
case sys_squeezelite_debug_levels_DEFAULT:
return lWARN;
break;
case sys_DebugLevelEnum_INFO:
case sys_squeezelite_debug_levels_INFO:
return lINFO;
break;
case sys_DebugLevelEnum_ERROR:
case sys_squeezelite_debug_levels_ERROR:
return lERROR;
break;
case sys_DebugLevelEnum_WARN:
case sys_squeezelite_debug_levels_WARN:
return lWARN;
break;
case sys_DebugLevelEnum_DEBUG:
case sys_squeezelite_debug_levels_DEBUG:
return lDEBUG;
break;
case sys_DebugLevelEnum_SDEBUG:
case sys_squeezelite_debug_levels_SDEBUG:
return lSDEBUG;
break;
default:
return lWARN;
}
}
void build_codec_string(sys_CodexEnum* list, size_t count, char* buffer, size_t buf_size) {
const char* prefix = STR(sys_CodexEnum) "_c_";
void build_codec_string(sys_squeezelite_codecs* list, size_t count, char* buffer, size_t buf_size) {
const char* prefix = STR(sys_squeezelite_codecs) "_c_";
const char* name = NULL;
for (int i = 0; i < count; i++) {
if (i > 0) {
strncat(buffer, ", ", buf_size);
}
name = sys_CodexEnum_name(list[i]) + strlen(prefix);
name = sys_squeezelite_codecs_name(list[i]) + strlen(prefix);
LOG_INFO("Found codec: %s ", name);
strncat(buffer, name, buf_size);
}
LOG_INFO("Codec list: %s ", buffer);
}
sys_squeezelite_config* get_profile(const char* name) {
const char* resolved_name = name && strlen(name) > 0 ? name : platform->services.current_profile;
LOG_DEBUG("get_profile called with name: %s, resolved to: %s",
name ? name : "NULL",
resolved_name ? resolved_name : "NULL or EMPTY");
if (!platform->has_services || platform->services.squeezelite_profiles_count <= 0) {
LOG_ERROR("No squeezelite profiles available");
return NULL;
}
if (!resolved_name || strlen(resolved_name) == 0) {
LOG_WARN("No specific profile requested, using the first available profile");
return &platform->services.squeezelite_profiles[0].profile;
}
for (int i = 0; i < platform->services.squeezelite_profiles_count; i++) {
LOG_DEBUG("Checking profile: %s", platform->services.squeezelite_profiles[i].name);
if (strcmp(platform->services.squeezelite_profiles[i].name, resolved_name) == 0) {
LOG_DEBUG("Profile matched: %s", platform->services.squeezelite_profiles[i].name);
return &platform->services.squeezelite_profiles[i].profile;
}
}
if(platform->services.squeezelite_profiles[0].name && strlen(platform->services.squeezelite_profiles[0].name)>0){
LOG_WARN("Could not find profile %s. Falling back to %s", resolved_name,platform->services.squeezelite_profiles[0].name);
return &platform->services.squeezelite_profiles[0].profile;
}
LOG_ERROR("Profile '%s' not found", resolved_name);
return NULL;
}
int squeezelite_main_start() {
u8_t mac[6];
unsigned output_buf_size = 0;
LOG_INFO("Starting squeezelite");
char include_codecs[101] = {0};
char exclude_codecs[101] = {0};
config = platform->has_services && platform->services.has_squeezelite
? &platform->services.squeezelite
: NULL;
config = get_profile(NULL); // get the active profile
if (!config) {
LOG_ERROR("Squeezelite not configured");
return -1;
return -2;
}
LOG_INFO("Running embedded init");
int err = embedded_init();
if (err) return err;
get_mac(mac);
unsigned int * rates = get_rates();
unsigned int* rates = get_rates();
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
#if defined(SIGQUIT)
signal(SIGQUIT, sighandler);
#endif
#if defined(SIGHUP)
signal(SIGHUP, sighandler);
#endif
output_buf_size = config->buffers.output;
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
#if defined(SIGQUIT)
signal(SIGQUIT, sighandler);
#endif
#if defined(SIGHUP)
signal(SIGHUP, sighandler);
#endif
output_buf_size = config->buffers.output*1024;
// set the output buffer size if not specified on the command line, take account of resampling
if (!output_buf_size) {
// set the output buffer size if not specified in the parameters, take account of resampling
if (output_buf_size == 0) {
output_buf_size = OUTPUTBUF_SIZE;
if (strlen(config->resample) > 0) {
unsigned scale = 8;
@@ -151,32 +169,37 @@ int squeezelite_main_start() {
}
build_codec_string(config->excluded_codex, config->excluded_codex_count, exclude_codecs,
sizeof(exclude_codecs));
build_codec_string(
config->included_codex, config->included_codex, include_codecs, sizeof(include_codecs));
build_codec_string(config->included_codex, config->included_codex_count, include_codecs,
sizeof(include_codecs));
unsigned int stream_buf_size =
config->buffers.stream > 0 ? config->buffers.stream : STREAMBUF_SIZE;
stream_init(
log_level_from_sys_level(platform->services.squeezelite.log.stream), stream_buf_size);
unsigned int stream_buf_size =STREAMBUF_SIZE;
if(config->buffers.stream > 0){
stream_buf_size = config->buffers.stream *1024;
LOG_DEBUG("Found stream buffer configuration: %d (%d)",config->buffers.stream,stream_buf_size);
}
else {
LOG_DEBUG("Using default stream buffer: %d",stream_buf_size);
}
stream_init(log_level_from_sys_level(config->log.stream), stream_buf_size);
output_init_embedded();
decode_init(log_level_from_sys_level(platform->services.squeezelite.log.decode), include_codecs,
exclude_codecs);
decode_init(log_level_from_sys_level(config->log.decode),
strlen(include_codecs) > 0 ? include_codecs : NULL, exclude_codecs);
#if RESAMPLE || RESAMPLE16
if (strlen(config->resample) > 0) {
if (config->resample && strlen(config->resample) > 0) {
process_init(config->resample);
}
#endif
if (!config->enabled) {
LOG_ERROR("LMS is disabled");
LOG_WARN("LMS is disabled");
while (1)
sleep(3600);
}
char* name = strlen(platform->names.squeezelite) > 0 ? platform->names.squeezelite
: platform->names.device;
slimproto(log_level_from_sys_level(platform->services.squeezelite.log.slimproto),
config->server_name_ip, mac, name, NULL, NULL, config->max_rate);
slimproto(log_level_from_sys_level(config->log.slimproto), config->server_name_ip, mac, name,
NULL, NULL, config->max_rate);
decode_close();
stream_close();

View File

@@ -1,4 +1,4 @@
/*
/*
* Squeezelite for esp32
*
* (c) Sebastien 2019
@@ -8,18 +8,19 @@
* https://opensource.org/licenses/MIT
*
*/
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <driver/i2s.h>
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
#include "Config.h"
#include "adac.h"
#include "cJSON.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "esp_check.h"
#include "gpio_exp.h"
#include "cJSON.h"
#include "inttypes.h"
#include "string.h"
// #include "Configurator.h"
#pragma message("fixme: look for TODO below")
#include "adac.h"
#include <driver/i2s.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
static const char TAG[] = "DAC external";
@@ -27,159 +28,167 @@ 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, i2s_config_t *i2s_config, bool *mck);
static bool init(sys_dac_config* config, i2s_config_t* i2s_config, bool* mck);
static bool i2c_json_execute(char *set);
// static bool i2c_json_execute(char *set);
static bool i2c_execute_cmd(sys_dac_control_type cmd_type);
const struct adac_s dac_external = { sys_DACModelEnum_I2S, init, adac_deinit, power, speaker, headset, volume };
static cJSON *i2c_json;
const struct adac_s dac_external = {sys_dac_models_I2S, init, adac_deinit, power, speaker, headset, volume};
static int i2c_addr;
static const struct {
char *model;
bool mclk;
char *controlset;
} codecs[] = {
{ "es8388", true,
"{\"init\":[ \
{\"reg\":8,\"val\":0}, {\"reg\":2,\"val\":243}, {\"reg\":43,\"val\":128}, {\"reg\":0,\"val\":5}, \
{\"reg\":1,\"val\":64}, {\"reg\":4,\"val\":60},"
#if BYTES_PER_FRAME == 8
"{\"reg\":23,\"val\":32},"
#else
"{\"reg\":23,\"val\":24},"
#endif
"{\"reg\":24,\"val\":2}, \
{\"reg\":26,\"val\":0}, {\"reg\":27,\"val\":0}, {\"reg\":25,\"val\":50}, {\"reg\":38,\"val\":0}, \
{\"reg\":39,\"val\":184}, {\"reg\":42,\"val\":184}, {\"reg\":46,\"val\":30}, {\"reg\":47,\"val\":30}, \
{\"reg\":48,\"val\":30}, {\"reg\":49,\"val\":30}, {\"reg\":2,\"val\":170}]}" },
{ NULL, false, NULL }
};
extern sys_dac_default_sets* default_dac_sets;
static EXT_RAM_ATTR sys_dac_control_set* i2c_default_controlset = NULL;
static EXT_RAM_ATTR sys_dac_control_set* i2c_controlset = NULL;
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config, bool *mck) {
char *p=NULL;
void * dummy = &codecs;
// i2c_addr = adac_init(config, i2c_port_num);
// if (!i2c_addr) return true;
// ESP_LOGI(TAG, "DAC on I2C @%d", i2c_addr);
// p = config_alloc_get_str("dac_controlset", CONFIG_DAC_CONTROLSET, NULL);
static bool init(sys_dac_config* config, i2s_config_t* i2s_config, bool* mck) {
// if ((!p || !*p) && (p = strcasestr(config, "model")) != NULL) {
// char model[32] = "";
// int i;
// sscanf(p, "%*[^=]=%31[^,]", model);
// for (i = 0; *model && ((p = codecs[i].controlset) != NULL) && strcasecmp(codecs[i].model, model); i++);
// if (p) *mck = codecs[i].mclk;
// }
#ifdef BYTES_PER_FRAME
uint32_t bytes_per_frame = BYTES_PER_FRAME;
#else
uint32_t bytes_per_frame = 4;
#endif
i2c_addr = adac_init(config);
if (!i2c_addr) return true;
ESP_LOGI(TAG, "DAC on I2C @%d", i2c_addr);
ESP_LOGD(TAG, "Checkinf if there's a default control set for DAC %s in the list of %d presets", sys_dac_models_name(config->model),
default_dac_sets->sets_count);
// i2c_json = cJSON_Parse(p);
// if (!i2c_json) {
// ESP_LOGW(TAG, "no i2c controlset found");
// return true;
// }
// if (!i2c_json_execute("init")) {
// ESP_LOGE(TAG, "could not intialize DAC");
// return false;
// }
// TODO: Add support for the commented code
for (int i = 0; i < default_dac_sets->sets_count; i++) {
ESP_LOGD(TAG, "Checkinf if preset #%d (%s[%d]) matches %s[%d]", i + 1, sys_dac_models_name(default_dac_sets->sets[i].model),
default_dac_sets->sets[i].bytes_per_frame, sys_dac_models_name(config->model), bytes_per_frame);
if (default_dac_sets->sets[i].bytes_per_frame == bytes_per_frame && default_dac_sets->sets[i].model == config->model) {
if (!default_dac_sets->sets[i].valid) {
ESP_LOGE(TAG, "DAC %s is unsupported with %d bytes per frame", sys_dac_models_name(config->model), bytes_per_frame);
return false;
}
i2c_default_controlset = &default_dac_sets->sets[i].set;
}
}
return true;
}
if (config->has_daccontrolset && config->daccontrolset.commands_count > 0) {
i2c_controlset = &config->daccontrolset;
}
if (!i2c_controlset && !i2c_default_controlset) {
ESP_LOGE(TAG, "DAC configuration invalid for %s", sys_dac_models_name(config->model));
return false;
}
if (mck) {
*mck = i2c_controlset != NULL ? i2c_controlset->mclk_needed : i2c_default_controlset->mclk_needed;
ESP_LOGD(TAG, "Master clock is %s", (*mck) ? "NEEDED" : "NOT NEEDED");
}
if (!i2c_execute_cmd(sys_dac_control_type_INIT)) {
ESP_LOGE(TAG, "could not intialize DAC");
return false;
}
return true;
}
/****************************************************************************************
* power
*/
static void power(adac_power_e mode) {
if (mode == ADAC_STANDBY || mode == ADAC_OFF) i2c_json_execute("poweroff");
else i2c_json_execute("poweron");
if (mode == ADAC_STANDBY || mode == ADAC_OFF)
i2c_execute_cmd(sys_dac_control_type_POWER_OFF);
else
i2c_execute_cmd(sys_dac_control_type_POWER_ON);
}
/****************************************************************************************
* speaker
*/
static void speaker(bool active) {
if (active) i2c_json_execute("speakeron");
else i2c_json_execute("speakeroff");
}
if (active)
i2c_execute_cmd(sys_dac_control_type_SPEAKER_ON);
else
i2c_execute_cmd(sys_dac_control_type_SPEAKER_OFF);
}
/****************************************************************************************
* headset
*/
static void headset(bool active) {
if (active) i2c_json_execute("headseton");
else i2c_json_execute("headsetoff");
if (active)
i2c_execute_cmd(sys_dac_control_type_HEADSET_ON);
else
i2c_execute_cmd(sys_dac_control_type_HEADSET_OFF);
}
/****************************************************************************************
*
*
*/
bool i2c_json_execute(char *set) {
cJSON *json_set = cJSON_GetObjectItemCaseSensitive(i2c_json, set);
cJSON *item;
if (!json_set) return true;
cJSON_ArrayForEach(item, json_set) {
cJSON *action;
// is this a delay
if ((action = cJSON_GetObjectItemCaseSensitive(item, "delay")) != NULL) {
vTaskDelay(pdMS_TO_TICKS(action->valueint));
ESP_LOGI(TAG, "DAC waiting %d ms", action->valueint);
continue;
static const sys_dac_control_itm* i2c_get_controlset_cmd(sys_dac_control_set* set, sys_dac_control_type cmd_type, pb_size_t* items_count) {
ESP_RETURN_ON_FALSE(set!=NULL,NULL,TAG,"Invalid set");
ESP_RETURN_ON_FALSE(items_count!=NULL,NULL,TAG,"Invalid items count");
ESP_LOGD(TAG,"Looking for command %s in control from a list of %d commands",sys_dac_control_type_name(cmd_type),set->commands_count);
*items_count = 0;
for (int i = 0; i < set->commands_count; i++) {
if (set->commands[i].type == cmd_type) {
*items_count = set->commands[i].items_count;
return set->commands[i].items;
}
// is this a gpio toggle
if ((action = cJSON_GetObjectItemCaseSensitive(item, "gpio")) != NULL) {
cJSON *level = cJSON_GetObjectItemCaseSensitive(item, "level");
ESP_LOGI(TAG, "set GPIO %d at %d", action->valueint, level->valueint);
gpio_set_direction_x(action->valueint, GPIO_MODE_OUTPUT);
gpio_set_level_x(action->valueint, level->valueint);
continue;
}
ESP_LOGD(TAG,"No control set found for command %s",sys_dac_control_type_name(cmd_type));
return NULL;
}
/****************************************************************************************
*
*/
bool i2c_execute_cmd(sys_dac_control_type cmd_type) {
pb_size_t items_count;
const sys_dac_control_itm* cmd = NULL;
esp_err_t err = ESP_OK;
ESP_LOGD(TAG, "Getting control set for command %s", sys_dac_control_type_name(cmd_type));
if(i2c_controlset){
cmd = i2c_get_controlset_cmd(i2c_controlset, cmd_type, &items_count);
}
if (!cmd || items_count == 0) {
ESP_LOGD(TAG, "Not found. Checking if defaults are known %s", sys_dac_control_type_name(cmd_type));
cmd = i2c_get_controlset_cmd(i2c_default_controlset, cmd_type, &items_count);
}
if (!cmd || items_count == 0) {
ESP_LOGD(TAG, "No commands for %s", sys_dac_control_type_name(cmd_type));
return true;
}
for (pb_size_t i = 0; i < items_count && err==ESP_OK; i++) {
switch (cmd->which_item_type) {
case sys_dac_control_itm_reg_action_tag:
ESP_LOGD(TAG, "Setting reg %d mode %s value %d ", cmd->item_type.reg_action.reg,
sys_dac_control_mode_name(cmd->item_type.reg_action.mode), cmd->item_type.reg_action.val);
if (cmd->item_type.reg_action.mode == sys_dac_control_mode_NOTHING) {
err = adac_write_byte(i2c_addr, cmd->item_type.reg_action.reg, cmd->item_type.reg_action.val);
} else if (cmd->item_type.reg_action.mode == sys_dac_control_mode_OR) {
uint8_t data = adac_read_byte(i2c_addr, cmd->item_type.reg_action.reg);
data |= (uint8_t)cmd->item_type.reg_action.val;
err = adac_write_byte(i2c_addr, cmd->item_type.reg_action.reg, data);
} else if (cmd->item_type.reg_action.mode == sys_dac_control_mode_AND) {
uint8_t data = adac_read_byte(i2c_addr, cmd->item_type.reg_action.reg);
data &= (uint8_t)cmd->item_type.reg_action.val;
err = adac_write_byte(i2c_addr, cmd->item_type.reg_action.reg, data);
}
break;
case sys_dac_control_itm_regs_action_tag:
ESP_LOGD(TAG, "Setting reg %d with %d byte(s)", cmd->item_type.regs_action.reg, cmd->item_type.regs_action.vals_count);
err = adac_write(i2c_addr, cmd->item_type.regs_action.reg, cmd->item_type.regs_action.vals, cmd->item_type.regs_action.vals_count);
break;
case sys_dac_control_itm_gpio_action_tag:
ESP_LOGI(TAG, "set GPIO %d at %s", cmd->item_type.gpio_action.gpio, sys_dac_control_lvl_name(cmd->item_type.gpio_action.level));
err = gpio_set_direction_x(cmd->item_type.gpio_action.gpio, GPIO_MODE_OUTPUT);
if(err == ESP_OK) err = gpio_set_level_x(cmd->item_type.gpio_action.gpio, cmd->item_type.gpio_action.level - sys_dac_control_lvl_LV_0);
break;
case sys_dac_control_itm_delay_action_tag:
vTaskDelay(pdMS_TO_TICKS(cmd->item_type.delay_action.delay));
ESP_LOGI(TAG, "DAC waiting %" PRIu64 " ms", cmd->item_type.delay_action.delay);
break;
default:
break;
}
action= cJSON_GetObjectItemCaseSensitive(item, "reg");
cJSON *val = cJSON_GetObjectItemCaseSensitive(item, "val");
// this is gpio register setting or crap
if (cJSON_IsArray(val)) {
cJSON *value;
uint8_t *data = malloc(cJSON_GetArraySize(val));
int count = 0;
if (!data) continue;
cJSON_ArrayForEach(value, val) {
data[count++] = value->valueint;
}
adac_write(i2c_addr, action->valueint, data, count);
free(data);
} else {
cJSON *mode = cJSON_GetObjectItemCaseSensitive(item, "mode");
}
if (!action || !val) continue;
if (!mode) {
adac_write_byte(i2c_addr, action->valueint, val->valueint);
} else if (!strcasecmp(mode->valuestring, "or")) {
uint8_t data = adac_read_byte(i2c_addr, action->valueint);
data |= (uint8_t) val->valueint;
adac_write_byte(i2c_addr, action->valueint, data);
} else if (!strcasecmp(mode->valuestring, "and")) {
uint8_t data = adac_read_byte(i2c_addr, action->valueint);
data &= (uint8_t) val->valueint;
adac_write_byte(i2c_addr, action->valueint, data);
}
}
}
return true;
}
return (err==ESP_OK);
}

View File

@@ -16,9 +16,10 @@
#include "perf_trace.h"
#include "services.h"
#include "led.h"
#include "Configurator.h"
extern log_level log_level_from_sys_level(sys_DebugLevelEnum level);
static sys_Squeezelite * config = NULL;
#include "Config.h"
extern log_level log_level_from_sys_level(sys_squeezelite_debug_levels level);
extern sys_squeezelite_config* get_profile(const char* name);
static sys_squeezelite_config * config = NULL;
extern struct outputstate output;
extern struct buffer *outputbuf;
extern struct buffer *streambuf;
@@ -76,7 +77,8 @@ static uint32_t bt_idle_callback(void) {
* Init BT sink
*/
void output_init_bt() {
config = &platform->services.squeezelite;
config = get_profile(NULL); // get the active profile
if(!config) return;
loglevel = log_level_from_sys_level(config->log.output);
bt_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
services_sleep_setsleeper(bt_idle_callback);

View File

@@ -10,9 +10,10 @@
*/
#include "squeezelite.h"
#include "equalizer.h"
#include "Configurator.h"
extern log_level log_level_from_sys_level(sys_DebugLevelEnum level);
static sys_Squeezelite* config = NULL;
#include "Config.h"
extern log_level log_level_from_sys_level(sys_squeezelite_debug_levels level);
extern sys_squeezelite_config* get_profile(const char* name);
static sys_squeezelite_config* config = NULL;
extern unsigned int* get_rates() ;
@@ -76,9 +77,9 @@ static bool handler(u8_t* data, int len) {
}
void output_init_embedded() {
config = &platform->services.squeezelite;
config = get_profile(NULL); // get the active profile
loglevel = log_level_from_sys_level(config->log.output);
LOG_INFO("init device: %s", sys_OutputTypeEnum_name(config->output_type));
LOG_INFO("init device: %s", sys_squeezelite_outputs_name(config->output_type));
// chain handlers
slimp_handler_chain = slimp_handler;
@@ -87,15 +88,22 @@ void output_init_embedded() {
// init equalizer before backends
equalizer_init();
memset(&output, 0, sizeof(output));
output_init_common(loglevel, sys_OutputTypeEnum_name(config->output_type),
config->buffers.output, get_rates(), config->amp_gpio_timeout);
unsigned int output_buffer = OUTPUTBUF_SIZE;
if(config->buffers.output > 0){
output_buffer = config->buffers.output *1024;
LOG_DEBUG("Found output buffer configuration: %d (%d)",config->buffers.output,output_buffer);
}
else {
LOG_DEBUG("Using default output buffer: %d",output_buffer);
}
output_init_common(loglevel, sys_squeezelite_outputs_name(config->output_type),
output_buffer, get_rates(), config->amp_gpio_timeout);
output.start_frames = FRAME_BLOCK;
#pragma message("Rate delay logic incomplete")
output.rate_delay = 0;
#if CONFIG_BT_SINK
if (config->output_type == sys_OutputTypeEnum_OUTPUT_Bluetooth) {
if (config->output_type == sys_squeezelite_outputs_BT) {
LOG_INFO("init Bluetooth");
close_cb = &output_close_bt;
output_init_bt(get_rates());
@@ -132,7 +140,7 @@ void set_volume(unsigned left, unsigned right) {
bool test_open(const char* device, unsigned rates[], bool userdef_rates) {
memset(rates, 0, MAX_SUPPORTED_SAMPLERATES * sizeof(unsigned));
if (config->output_type == sys_OutputTypeEnum_OUTPUT_I2S) {
if (config->output_type == sys_squeezelite_outputs_I2S) {
unsigned _rates[] = {
#if BYTES_PER_FRAME == 4
192000,
@@ -152,7 +160,7 @@ bool test_open(const char* device, unsigned rates[], bool userdef_rates) {
0
};
memcpy(rates, _rates, sizeof(_rates));
} else if (config->output_type == sys_OutputTypeEnum_OUTPUT_SPDIF) {
} else if (config->output_type == sys_squeezelite_outputs_SPDIF) {
unsigned _rates[] = {
96000, 88200, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 0};
memcpy(rates, _rates, sizeof(_rates));

View File

@@ -30,27 +30,29 @@ there might be a pop and a de-sync when sampling rate change happens. Not
sure that using rate_delay would fix that
*/
#include "squeezelite.h"
#include "slimproto.h"
#include "esp_pthread.h"
#include "driver/i2s.h"
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "perf_trace.h"
#include <signal.h>
#include "adac.h"
#include "time.h"
#include "led.h"
#include "services.h"
#include "monitor.h"
#include "gpio_exp.h"
#include "Config.h"
#include "accessors.h"
#include "adac.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "driver/i2s.h"
#include "equalizer.h"
#include "esp_pthread.h"
#include "globdefs.h"
#include "Configurator.h"
#include "gpio_exp.h"
#include "hal/gpio_hal.h"
#include "led.h"
#include "monitor.h"
#include "perf_trace.h"
#include "services.h"
#include "slimproto.h"
#include "squeezelite.h"
#include "time.h"
#include <signal.h>
extern log_level log_level_from_sys_level(sys_DebugLevelEnum level);
static sys_Squeezelite* config = NULL;
extern sys_squeezelite_config* get_profile(const char* name);
extern log_level log_level_from_sys_level(sys_squeezelite_debug_levels level);
static sys_squeezelite_config* config = NULL;
#define LOCK mutex_lock(outputbuf->mutex)
#define UNLOCK mutex_unlock(outputbuf->mutex)
@@ -75,18 +77,18 @@ static sys_Squeezelite* config = NULL;
#define DMA_BUF_FRAMES_SPDIF 450
#define DMA_BUF_COUNT_SPDIF 7
#define DECLARE_ALL_MIN_MAX \
DECLARE_MIN_MAX(o); \
DECLARE_MIN_MAX(s); \
DECLARE_MIN_MAX(rec); \
DECLARE_MIN_MAX(i2s_time); \
#define DECLARE_ALL_MIN_MAX \
DECLARE_MIN_MAX(o); \
DECLARE_MIN_MAX(s); \
DECLARE_MIN_MAX(rec); \
DECLARE_MIN_MAX(i2s_time); \
DECLARE_MIN_MAX(buffering);
#define RESET_ALL_MIN_MAX \
RESET_MIN_MAX(o); \
RESET_MIN_MAX(s); \
RESET_MIN_MAX(rec); \
RESET_MIN_MAX(i2s_time); \
#define RESET_ALL_MIN_MAX \
RESET_MIN_MAX(o); \
RESET_MIN_MAX(s); \
RESET_MIN_MAX(rec); \
RESET_MIN_MAX(i2s_time); \
RESET_MIN_MAX(buffering);
#define STATS_PERIOD_MS 5000
@@ -101,7 +103,7 @@ extern struct buffer* streambuf;
extern struct buffer* outputbuf;
extern u8_t* silencebuf;
const struct adac_s* dac_set[] = {&dac_tas57xx, &dac_tas5713, &dac_ac101, &dac_wm8978, NULL};
const struct adac_s* dac_set[] = {&dac_tas57xx, &dac_tas5713, &dac_ac101, &dac_wm8978, &dac_cs4265, NULL};
const struct adac_s* adac = &dac_external;
static log_level loglevel;
@@ -122,21 +124,18 @@ static size_t dma_buf_frames;
static TaskHandle_t output_i2s_task;
static struct {
int gpio, active;
} amp_control = {CONFIG_AMP_GPIO, CONFIG_AMP_GPIO_LEVEL},
mute_control = {CONFIG_MUTE_GPIO, CONFIG_MUTE_GPIO_LEVEL};
} amp_control = {-1, 0}, mute_control = {-1, 0};
DECLARE_ALL_MIN_MAX;
static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T** cross_ptr);
static int _i2s_write_frames(
frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T** cross_ptr);
static void output_thread_i2s(void* arg);
static void i2s_stats(uint32_t now);
static void spdif_convert(ISAMPLE_T* src, size_t frames, u32_t* dst);
static void (*jack_handler_chain)(bool inserted);
#define I2C_PORT 0
/****************************************************************************************
* AUDO packet handler
*/
@@ -151,7 +150,7 @@ static bool handler(u8_t* data, int len) {
if (jack_mutes_amp != (pkt->config == 0)) {
jack_mutes_amp = pkt->config == 0;
platform->dev.dac.jack_mutes_amp = jack_mutes_amp;
configurator_raise_changed();
config_raise_changed(false);
if (jack_mutes_amp && jack_inserted_svc()) {
adac->speaker(false);
@@ -181,8 +180,7 @@ static void jack_handler(bool inserted) {
if (jack_mutes_amp) {
LOG_INFO("switching amplifier %s", inserted ? "OFF" : "ON");
adac->speaker(!inserted);
if (amp_control.gpio != -1)
gpio_set_level_x(amp_control.gpio, inserted ? !amp_control.active : amp_control.active);
if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, inserted ? !amp_control.active : amp_control.active);
}
// activate headset
@@ -195,19 +193,17 @@ static void jack_handler(bool inserted) {
/****************************************************************************************
* Get inactivity callback
*/
static uint32_t i2s_idle_callback(void) {
return output.state <= OUTPUT_STOPPED ? pdTICKS_TO_MS(xTaskGetTickCount()) - i2s_idle_since : 0;
}
static uint32_t i2s_idle_callback(void) { return output.state <= OUTPUT_STOPPED ? pdTICKS_TO_MS(xTaskGetTickCount()) - i2s_idle_since : 0; }
/****************************************************************************************
* Initialize the DAC output
*/
void output_init_i2s() {
int silent_do = -1;
config = &platform->services.squeezelite;
sys_DAC* dac_config = platform->has_dev && platform->dev.has_dac ? &platform->dev.dac : NULL;
sys_SPDIF* spdif_config =
platform->has_dev && platform->dev.has_spdif ? &platform->dev.spdif : NULL;
LOG_DEBUG("Initializing I2S output");
config = get_profile(NULL);
sys_dac_config* dac_config = platform->has_dev && platform->dev.has_dac ? &platform->dev.dac : NULL;
sys_dev_spdif* spdif_config = platform->has_dev && platform->dev.has_spdif ? &platform->dev.spdif : NULL;
loglevel = log_level_from_sys_level(config->log.output);
esp_err_t res;
@@ -216,8 +212,8 @@ void output_init_i2s() {
slimp_handler = handler;
if (dac_config) jack_mutes_amp = dac_config->jack_mutes_amp;
if (platform->has_gpios) {
amp_control.gpio = platform->has_gpios && platform->gpios.has_amp ? platform->gpios.amp.pin:-1;
amp_control.active = platform->has_gpios && platform->gpios.has_amp ? platform->gpios.amp.level:0;
amp_control.gpio = platform->has_gpios && platform->gpios.has_amp ? platform->gpios.amp.pin : -1;
amp_control.active = platform->has_gpios && platform->gpios.has_amp ? platform->gpios.amp.level : 0;
}
#if BYTES_PER_FRAME == 8
@@ -236,16 +232,25 @@ void output_init_i2s() {
running = true;
i2s_pin_config_t i2s_dac_pin = {-1, -1, -1, -1}, i2s_spdif_pin = {-1, -1, -1, -1};
i2s_pin_config_t i2s_dac_pin = {-1, -1, -1, -1, -1}, i2s_spdif_pin = {-1, -1, -1, -1, -1};
if (dac_config) {
i2s_dac_pin.bck_io_num = dac_config->has_bck ? dac_config->bck.pin : -1;
i2s_dac_pin.data_out_num = dac_config->has_dout ? dac_config->dout.pin : -1;
i2s_dac_pin.ws_io_num = dac_config->has_ws ? dac_config->ws.pin : -1;
i2s_dac_pin.bck_io_num = dac_config->bck;
i2s_dac_pin.data_out_num = dac_config->dout;
i2s_dac_pin.ws_io_num = dac_config->ws;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
if (dac_config->mck != sys_dac_mck_NONE && dac_config->mck != sys_dac_mck_INVALID1) {
LOG_INFO("Using I2S Master clock %s", sys_dac_mck_name(dac_config->mck));
i2s_dac_pin.mck_io_num = dac_config->mck - sys_dac_mck_GPIO0;
} else {
LOG_INFO("Configuration does not specify an I2S Master Clock");
i2s_dac_pin.mck_io_num = -1;
}
#endif
}
if (spdif_config) {
i2s_spdif_pin.bck_io_num = spdif_config->has_clk ? spdif_config->clk.pin : -1;
i2s_spdif_pin.data_out_num = spdif_config->has_data ? spdif_config->data.pin : -1;
i2s_spdif_pin.ws_io_num = spdif_config->has_dc ? spdif_config->dc.pin : -1;
i2s_spdif_pin.bck_io_num = spdif_config->clk;
i2s_spdif_pin.data_out_num = spdif_config->data;
i2s_spdif_pin.ws_io_num = spdif_config->ws;
}
if (i2s_dac_pin.data_out_num == -1 && i2s_spdif_pin.data_out_num == -1) {
@@ -266,16 +271,16 @@ void output_init_i2s() {
i2s_config.dma_buf_len = DMA_BUF_FRAMES;
i2s_config.dma_buf_count = DMA_BUF_COUNT;
if (config->output_type == sys_OutputTypeEnum_OUTPUT_SPDIF) {
if (config->output_type == sys_squeezelite_outputs_SPDIF) {
spdif.enabled = true;
LOG_DEBUG("Configuring SPDIF");
if ((spdif.buf = heap_caps_malloc(SPDIF_BLOCK * 16, MALLOC_CAP_INTERNAL)) == NULL) {
LOG_ERROR("Cannot allocate SPDIF buffer");
}
if (i2s_spdif_pin.bck_io_num == -1 || i2s_spdif_pin.ws_io_num == -1 ||
i2s_spdif_pin.data_out_num == -1) {
LOG_WARN("Cannot initialize I2S for SPDIF bck:%d ws:%d do:%d", i2s_spdif_pin.bck_io_num,
i2s_spdif_pin.ws_io_num, i2s_spdif_pin.data_out_num);
if (i2s_spdif_pin.bck_io_num == -1 || i2s_spdif_pin.ws_io_num == -1 || i2s_spdif_pin.data_out_num == -1) {
LOG_WARN(
"Cannot initialize I2S for SPDIF bck:%d ws:%d do:%d", i2s_spdif_pin.bck_io_num, i2s_spdif_pin.ws_io_num, i2s_spdif_pin.data_out_num);
}
i2s_config.sample_rate = output.current_sample_rate * 2;
@@ -291,15 +296,14 @@ void output_init_i2s() {
dma_buf_frames = i2s_config.dma_buf_len * i2s_config.dma_buf_count / 2;
// silence DAC output if sharing the same ws/bck
if (i2s_dac_pin.ws_io_num == i2s_spdif_pin.ws_io_num &&
i2s_dac_pin.bck_io_num == i2s_spdif_pin.bck_io_num)
if (i2s_dac_pin.ws_io_num == i2s_spdif_pin.ws_io_num && i2s_dac_pin.bck_io_num == i2s_spdif_pin.bck_io_num)
silent_do = i2s_dac_pin.data_out_num;
res = i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
res |= i2s_set_pin(CONFIG_I2S_NUM, &i2s_spdif_pin);
LOG_INFO("SPDIF using I2S bck:%d, ws:%d, do:%d", i2s_spdif_pin.bck_io_num,
i2s_spdif_pin.ws_io_num, i2s_spdif_pin.data_out_num);
LOG_INFO("SPDIF using I2S bck:%d, ws:%d, do:%d", i2s_spdif_pin.bck_io_num, i2s_spdif_pin.ws_io_num, i2s_spdif_pin.data_out_num);
} else {
LOG_DEBUG("Configuring DAC");
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)
@@ -312,17 +316,30 @@ void output_init_i2s() {
mute_control.gpio = dac_config && dac_config->has_mute ? dac_config->mute.pin : -1;
mute_control.active = dac_config && dac_config->has_mute ? dac_config->mute.level : 0;
bool mck_required = false;
for (int i = 0; adac == &dac_external && dac_set[i]; i++)
if (dac_set[i]->model == dac_config->model) adac = dac_set[i];
res = adac->init(NULL, I2C_PORT, &i2s_config, &mck_required) ? ESP_OK : ESP_FAIL;
LOG_DEBUG("Looking for %s DAC Driver", sys_dac_models_name(dac_config->model));
for (int i = 0; adac == &dac_external && dac_set[i]; i++) {
if (dac_set[i]->model == dac_config->model) {
LOG_DEBUG("Found Driver");
adac = dac_set[i];
} else {
LOG_DEBUG("Skipping %s", sys_dac_models_name(dac_set[i]->model));
}
}
if (!adac) {
LOG_ERROR("Invalid adac structure");
return;
}
if (!adac->init) {
LOG_ERROR("adac doesn't have a valid init");
return;
}
LOG_DEBUG("Calling DAC init for %s", sys_dac_models_name(adac->model));
res = adac->init(&platform->dev.dac, &i2s_config, &mck_required) ? ESP_OK : ESP_FAIL;
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 0)
int mck_io_num = (dac_config->mck - sys_MCKEnum_MCK0) < 0 && mck_required
? 0
: dac_config->mck - sys_MCKEnum_MCK0;
int mck_io_num = (((int)dac_config->mck) - ((int)sys_MCKEnum_MCK0)) < 0 && mck_required ? 0
: mck_required ? dac_config->mck - sys_MCKEnum_MCK0
: -1;
LOG_INFO("configuring MCLK on GPIO %d", mck_io_num);
if (mck_io_num == GPIO_NUM_0) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
WRITE_PERI_REG(PIN_CTRL, CONFIG_I2S_NUM == I2S_NUM_0 ? 0xFFF0 : 0xFFFF);
@@ -336,22 +353,28 @@ void output_init_i2s() {
LOG_WARN("invalid MCK gpio %d", mck_io_num);
}
#else
if (mck_required && i2s_dac_pin.mck_io_num == -1) i2s_dac_pin.mck_io_num = 0;
LOG_INFO("configuring MCLK on GPIO %d", i2s_dac_pin.mck_io_num);
#endif
res |= i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
res |= i2s_set_pin(CONFIG_I2S_NUM, &i2s_dac_pin);
if (res == ESP_OK && mute_control.gpio >= 0) {
gpio_pad_select_gpio(mute_control.gpio);
gpio_set_direction(mute_control.gpio, GPIO_MODE_OUTPUT);
gpio_set_level(mute_control.gpio, mute_control.active);
if (res == ESP_OK) {
if (mck_required && i2s_dac_pin.mck_io_num == -1) {
LOG_DEBUG("Defaulting MCLK on GPIO %d", i2s_dac_pin.mck_io_num);
i2s_dac_pin.mck_io_num = 0;
} else {
LOG_DEBUG("MCLK configured on GPIO %d", i2s_dac_pin.mck_io_num);
}
}
#endif
if (res == ESP_OK) {
res |= i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
res |= i2s_set_pin(CONFIG_I2S_NUM, &i2s_dac_pin);
LOG_INFO("%s DAC using I2S bck:%d, ws:%d, do:%d, mute:%d:%d (res:%d)",
sys_EthModelEnum_name(dac_config->model), i2s_dac_pin.bck_io_num, i2s_dac_pin.ws_io_num,
i2s_dac_pin.data_out_num, mute_control.gpio, mute_control.active, res);
if (res == ESP_OK && mute_control.gpio >= 0) {
gpio_pad_select_gpio(mute_control.gpio);
gpio_set_direction(mute_control.gpio, GPIO_MODE_OUTPUT);
gpio_set_level(mute_control.gpio, mute_control.active);
}
LOG_INFO("%s DAC using I2S bck:%d, ws:%d, do:%d, mute:%d:%d (res:%d)", sys_dac_models_name(dac_config->model), i2s_dac_pin.bck_io_num,
i2s_dac_pin.ws_io_num, i2s_dac_pin.data_out_num, mute_control.gpio, mute_control.active, res);
}
}
if (res != ESP_OK) {
@@ -368,8 +391,7 @@ void output_init_i2s() {
LOG_INFO("Initializing I2S mode %s with rate: %d, bits per sample: %d, buffer frames: %d, "
"number of buffers: %d ",
spdif.enabled ? "S/PDIF" : "normal", i2s_config.sample_rate, i2s_config.bits_per_sample,
i2s_config.dma_buf_len, i2s_config.dma_buf_count);
spdif.enabled ? "S/PDIF" : "normal", i2s_config.sample_rate, i2s_config.bits_per_sample, i2s_config.dma_buf_len, i2s_config.dma_buf_count);
i2s_stop(CONFIG_I2S_NUM);
i2s_zero_dma_buffer(CONFIG_I2S_NUM);
@@ -408,10 +430,8 @@ void output_init_i2s() {
// create task as a FreeRTOS task but uses stack in internal RAM
{
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__((aligned(4)));
static EXT_RAM_ATTR StackType_t xStack[OUTPUT_THREAD_STACK_SIZE]
__attribute__((aligned(4)));
output_i2s_task = xTaskCreateStaticPinnedToCore((TaskFunction_t)output_thread_i2s,
"output_i2s", OUTPUT_THREAD_STACK_SIZE, NULL,
static EXT_RAM_ATTR StackType_t xStack[OUTPUT_THREAD_STACK_SIZE] __attribute__((aligned(4)));
output_i2s_task = xTaskCreateStaticPinnedToCore((TaskFunction_t)output_thread_i2s, "output_i2s", OUTPUT_THREAD_STACK_SIZE, NULL,
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, xStack, &xTaskBuffer, 0);
}
}
@@ -439,17 +459,15 @@ void output_close_i2s(void) {
* change volume
*/
bool output_volume_i2s(unsigned left, unsigned right) {
if (mute_control.gpio >= 0)
gpio_set_level(
mute_control.gpio, (left | right) ? !mute_control.active : mute_control.active);
if (mute_control.gpio >= 0) gpio_set_level(mute_control.gpio, (left | right) ? !mute_control.active : mute_control.active);
return adac->volume(left, right);
}
/****************************************************************************************
* Write frames to the output buffer
*/
static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T** cross_ptr) {
static int _i2s_write_frames(
frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T** cross_ptr) {
if (!silence) {
if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) {
_apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr);
@@ -463,8 +481,7 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
// don't update visu if we don't have enough data in buffer
if (silence || output.external || _buf_used(outputbuf) > outputbuf->size >> 2) {
output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate,
silence, (gainL + gainR) / 2);
output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
}
oframes += out_frames;
@@ -503,8 +520,7 @@ static void output_thread_i2s(void* arg) {
led_blink(LED_GREEN, 200, 1000);
} else if (output.state == OUTPUT_RUNNING) {
if (!jack_mutes_amp || !jack_inserted_svc()) {
if (amp_control.gpio != -1)
gpio_set_level_x(amp_control.gpio, amp_control.active);
if (amp_control.gpio != -1) gpio_set_level_x(amp_control.gpio, amp_control.active);
adac->speaker(true);
}
led_on(LED_GREEN);
@@ -530,8 +546,7 @@ static void output_thread_i2s(void* arg) {
output.frames_played_dmp = output.frames_played;
// try to estimate how much we have consumed from the DMA buffer (calculation is incorrect
// at the very beginning ...)
output.device_frames =
dma_buf_frames - ((output.updated - fullness) * output.current_sample_rate) / 1000;
output.device_frames = dma_buf_frames - ((output.updated - fullness) * output.current_sample_rate) / 1000;
// we'll try to produce iframes if we have any, but we might return less if outpuf does not
// have enough
_output_frames(iframes);
@@ -572,8 +587,7 @@ static void output_thread_i2s(void* arg) {
// this does not work well as set_sample_rates resets the fifos (and it's too early)
if (i2s_config.sample_rate != output.current_sample_rate) {
LOG_INFO("changing sampling rate %u to %u", i2s_config.sample_rate,
output.current_sample_rate);
LOG_INFO("changing sampling rate %u to %u", i2s_config.sample_rate, output.current_sample_rate);
if (synced) {
/*
// can sleep for a buffer_queue - 1 and then eat a buffer (discard) if we are
@@ -582,8 +596,7 @@ static void output_thread_i2s(void* arg) {
*/
}
i2s_config.sample_rate = output.current_sample_rate;
i2s_set_sample_rates(CONFIG_I2S_NUM,
spdif.enabled ? i2s_config.sample_rate * 2 : i2s_config.sample_rate);
i2s_set_sample_rates(CONFIG_I2S_NUM, spdif.enabled ? i2s_config.sample_rate * 2 : i2s_config.sample_rate);
i2s_zero_dma_buffer(CONFIG_I2S_NUM);
equalizer_set_samplerate(output.current_sample_rate);
@@ -607,8 +620,7 @@ static void output_thread_i2s(void* arg) {
}
#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);
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);
@@ -617,8 +629,7 @@ static void output_thread_i2s(void* arg) {
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);
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);
@@ -643,8 +654,7 @@ static void i2s_stats(uint32_t now) {
if (output.state <= OUTPUT_STOPPED || now < last + STATS_PERIOD_MS) return;
last = now;
LOG_INFO("Output State: %d, current sample rate: %d, bytes per frame: %d", output.state,
output.current_sample_rate, BYTES_PER_FRAME);
LOG_INFO("Output State: %d, current sample rate: %d, bytes per frame: %d", output.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);
@@ -673,27 +683,20 @@ static void i2s_stats(uint32_t now) {
static const u8_t VUCP24[2] = {0xCC, 0x32};
static const u16_t spdif_bmclookup[256] = {0xcccc, 0xb333, 0xd333, 0xaccc, 0xcb33, 0xb4cc, 0xd4cc,
0xab33, 0xcd33, 0xb2cc, 0xd2cc, 0xad33, 0xcacc, 0xb533, 0xd533, 0xaacc, 0xccb3, 0xb34c, 0xd34c,
0xacb3, 0xcb4c, 0xb4b3, 0xd4b3, 0xab4c, 0xcd4c, 0xb2b3, 0xd2b3, 0xad4c, 0xcab3, 0xb54c, 0xd54c,
0xaab3, 0xccd3, 0xb32c, 0xd32c, 0xacd3, 0xcb2c, 0xb4d3, 0xd4d3, 0xab2c, 0xcd2c, 0xb2d3, 0xd2d3,
0xad2c, 0xcad3, 0xb52c, 0xd52c, 0xaad3, 0xccac, 0xb353, 0xd353, 0xacac, 0xcb53, 0xb4ac, 0xd4ac,
0xab53, 0xcd53, 0xb2ac, 0xd2ac, 0xad53, 0xcaac, 0xb553, 0xd553, 0xaaac, 0xcccb, 0xb334, 0xd334,
0xaccb, 0xcb34, 0xb4cb, 0xd4cb, 0xab34, 0xcd34, 0xb2cb, 0xd2cb, 0xad34, 0xcacb, 0xb534, 0xd534,
0xaacb, 0xccb4, 0xb34b, 0xd34b, 0xacb4, 0xcb4b, 0xb4b4, 0xd4b4, 0xab4b, 0xcd4b, 0xb2b4, 0xd2b4,
0xad4b, 0xcab4, 0xb54b, 0xd54b, 0xaab4, 0xccd4, 0xb32b, 0xd32b, 0xacd4, 0xcb2b, 0xb4d4, 0xd4d4,
0xab2b, 0xcd2b, 0xb2d4, 0xd2d4, 0xad2b, 0xcad4, 0xb52b, 0xd52b, 0xaad4, 0xccab, 0xb354, 0xd354,
0xacab, 0xcb54, 0xb4ab, 0xd4ab, 0xab54, 0xcd54, 0xb2ab, 0xd2ab, 0xad54, 0xcaab, 0xb554, 0xd554,
0xaaab, 0xcccd, 0xb332, 0xd332, 0xaccd, 0xcb32, 0xb4cd, 0xd4cd, 0xab32, 0xcd32, 0xb2cd, 0xd2cd,
0xad32, 0xcacd, 0xb532, 0xd532, 0xaacd, 0xccb2, 0xb34d, 0xd34d, 0xacb2, 0xcb4d, 0xb4b2, 0xd4b2,
0xab4d, 0xcd4d, 0xb2b2, 0xd2b2, 0xad4d, 0xcab2, 0xb54d, 0xd54d, 0xaab2, 0xccd2, 0xb32d, 0xd32d,
0xacd2, 0xcb2d, 0xb4d2, 0xd4d2, 0xab2d, 0xcd2d, 0xb2d2, 0xd2d2, 0xad2d, 0xcad2, 0xb52d, 0xd52d,
0xaad2, 0xccad, 0xb352, 0xd352, 0xacad, 0xcb52, 0xb4ad, 0xd4ad, 0xab52, 0xcd52, 0xb2ad, 0xd2ad,
0xad52, 0xcaad, 0xb552, 0xd552, 0xaaad, 0xccca, 0xb335, 0xd335, 0xacca, 0xcb35, 0xb4ca, 0xd4ca,
0xab35, 0xcd35, 0xb2ca, 0xd2ca, 0xad35, 0xcaca, 0xb535, 0xd535, 0xaaca, 0xccb5, 0xb34a, 0xd34a,
0xacb5, 0xcb4a, 0xb4b5, 0xd4b5, 0xab4a, 0xcd4a, 0xb2b5, 0xd2b5, 0xad4a, 0xcab5, 0xb54a, 0xd54a,
0xaab5, 0xccd5, 0xb32a, 0xd32a, 0xacd5, 0xcb2a, 0xb4d5, 0xd4d5, 0xab2a, 0xcd2a, 0xb2d5, 0xd2d5,
0xad2a, 0xcad5, 0xb52a, 0xd52a, 0xaad5, 0xccaa, 0xb355, 0xd355, 0xacaa, 0xcb55, 0xb4aa, 0xd4aa,
static const u16_t spdif_bmclookup[256] = {0xcccc, 0xb333, 0xd333, 0xaccc, 0xcb33, 0xb4cc, 0xd4cc, 0xab33, 0xcd33, 0xb2cc, 0xd2cc, 0xad33, 0xcacc,
0xb533, 0xd533, 0xaacc, 0xccb3, 0xb34c, 0xd34c, 0xacb3, 0xcb4c, 0xb4b3, 0xd4b3, 0xab4c, 0xcd4c, 0xb2b3, 0xd2b3, 0xad4c, 0xcab3, 0xb54c, 0xd54c,
0xaab3, 0xccd3, 0xb32c, 0xd32c, 0xacd3, 0xcb2c, 0xb4d3, 0xd4d3, 0xab2c, 0xcd2c, 0xb2d3, 0xd2d3, 0xad2c, 0xcad3, 0xb52c, 0xd52c, 0xaad3, 0xccac,
0xb353, 0xd353, 0xacac, 0xcb53, 0xb4ac, 0xd4ac, 0xab53, 0xcd53, 0xb2ac, 0xd2ac, 0xad53, 0xcaac, 0xb553, 0xd553, 0xaaac, 0xcccb, 0xb334, 0xd334,
0xaccb, 0xcb34, 0xb4cb, 0xd4cb, 0xab34, 0xcd34, 0xb2cb, 0xd2cb, 0xad34, 0xcacb, 0xb534, 0xd534, 0xaacb, 0xccb4, 0xb34b, 0xd34b, 0xacb4, 0xcb4b,
0xb4b4, 0xd4b4, 0xab4b, 0xcd4b, 0xb2b4, 0xd2b4, 0xad4b, 0xcab4, 0xb54b, 0xd54b, 0xaab4, 0xccd4, 0xb32b, 0xd32b, 0xacd4, 0xcb2b, 0xb4d4, 0xd4d4,
0xab2b, 0xcd2b, 0xb2d4, 0xd2d4, 0xad2b, 0xcad4, 0xb52b, 0xd52b, 0xaad4, 0xccab, 0xb354, 0xd354, 0xacab, 0xcb54, 0xb4ab, 0xd4ab, 0xab54, 0xcd54,
0xb2ab, 0xd2ab, 0xad54, 0xcaab, 0xb554, 0xd554, 0xaaab, 0xcccd, 0xb332, 0xd332, 0xaccd, 0xcb32, 0xb4cd, 0xd4cd, 0xab32, 0xcd32, 0xb2cd, 0xd2cd,
0xad32, 0xcacd, 0xb532, 0xd532, 0xaacd, 0xccb2, 0xb34d, 0xd34d, 0xacb2, 0xcb4d, 0xb4b2, 0xd4b2, 0xab4d, 0xcd4d, 0xb2b2, 0xd2b2, 0xad4d, 0xcab2,
0xb54d, 0xd54d, 0xaab2, 0xccd2, 0xb32d, 0xd32d, 0xacd2, 0xcb2d, 0xb4d2, 0xd4d2, 0xab2d, 0xcd2d, 0xb2d2, 0xd2d2, 0xad2d, 0xcad2, 0xb52d, 0xd52d,
0xaad2, 0xccad, 0xb352, 0xd352, 0xacad, 0xcb52, 0xb4ad, 0xd4ad, 0xab52, 0xcd52, 0xb2ad, 0xd2ad, 0xad52, 0xcaad, 0xb552, 0xd552, 0xaaad, 0xccca,
0xb335, 0xd335, 0xacca, 0xcb35, 0xb4ca, 0xd4ca, 0xab35, 0xcd35, 0xb2ca, 0xd2ca, 0xad35, 0xcaca, 0xb535, 0xd535, 0xaaca, 0xccb5, 0xb34a, 0xd34a,
0xacb5, 0xcb4a, 0xb4b5, 0xd4b5, 0xab4a, 0xcd4a, 0xb2b5, 0xd2b5, 0xad4a, 0xcab5, 0xb54a, 0xd54a, 0xaab5, 0xccd5, 0xb32a, 0xd32a, 0xacd5, 0xcb2a,
0xb4d5, 0xd4d5, 0xab2a, 0xcd2a, 0xb2d5, 0xd2d5, 0xad2a, 0xcad5, 0xb52a, 0xd52a, 0xaad5, 0xccaa, 0xb355, 0xd355, 0xacaa, 0xcb55, 0xb4aa, 0xd4aa,
0xab55, 0xcd55, 0xb2aa, 0xd2aa, 0xad55, 0xcaaa, 0xb555, 0xd555, 0xaaaa};
/*

View File

@@ -39,13 +39,13 @@
static const char TAG[] = "TAS5713";
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config, bool *mck);
static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck);
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 = {sys_DACModelEnum_TAS5713, init, adac_deinit, power, speaker, headset, volume};
const struct adac_s dac_tas5713 = {sys_dac_models_TAS5713, init, adac_deinit, power, speaker, headset, volume};
struct tas5713_cmd_s {
uint8_t reg;
@@ -65,9 +65,9 @@ typedef enum {
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config, bool *mck) {
static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck) {
/* find if there is a tas5713 attached. Reg 0 should read non-zero but not 255 if so */
adac_init(config, i2c_port);
adac_init(config);
if (adac_read_byte(TAS5713, 0x00) == 255) {
ESP_LOGW(TAG, "No TAS5713 detected");
adac_deinit();

View File

@@ -23,13 +23,13 @@
static const char TAG[] = "TAS575x/8x";
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config, bool *mck);
static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck);
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_tas57xx = { sys_DACModelEnum_TAS57xx, init, adac_deinit, power, speaker, headset, volume };
const struct adac_s dac_tas57xx = { sys_dac_models_TAS57xx, init, adac_deinit, power, speaker, headset, volume };
struct tas57xx_cmd_s {
uint8_t reg;
@@ -71,9 +71,9 @@ static int tas57_detect(void);
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config, bool *mck) {
static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck) {
// find which TAS we are using (if any)
tas57_addr = adac_init(config, i2c_port);
tas57_addr = adac_init(config);
if (!tas57_addr) tas57_addr = tas57_detect();
if (!tas57_addr) {
@@ -93,7 +93,7 @@ static bool init(char *config, int i2c_port, i2s_config_t *i2s_config, bool *mck
}
i2c_master_stop(i2c_cmd);
esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
esp_err_t res = i2c_master_cmd_begin(config->i2c.port-sys_i2c_port_PORT0, i2c_cmd, 500 / portTICK_RATE_MS);
i2c_cmd_link_delete(i2c_cmd);
if (res != ESP_OK) {

View File

@@ -23,14 +23,14 @@ 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, i2s_config_t *i2s_config, bool *mck);
static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck);
static esp_err_t i2c_write_shadow(uint8_t reg, uint16_t val);
static uint16_t i2c_read_shadow(uint8_t reg);
static int WM8978;
const struct adac_s dac_wm8978 = { sys_DACModelEnum_WM8978, init, adac_deinit, power, speaker, headset, volume };
const struct adac_s dac_wm8978 = { sys_dac_models_WM8978, init, adac_deinit, power, speaker, headset, volume };
// initiation table for non-readbale 9-bit i2c registers
static uint16_t WM8978_REGVAL_TBL[58] = {
@@ -47,8 +47,8 @@ static uint16_t WM8978_REGVAL_TBL[58] = {
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config, bool *mck) {
WM8978 = adac_init(config, i2c_port);
static bool init(sys_dac_config *config, i2s_config_t *i2s_config, bool *mck) {
WM8978 = adac_init(config);
if (!WM8978) WM8978 = 0x1a;
ESP_LOGI(TAG, "WM8978 detected @%d", WM8978);