Browse Source

audio refactoring done + T-WATCH2020 support

Philippe G 4 năm trước cách đây
mục cha
commit
340a1bd19e

+ 2 - 0
build-scripts/ESP32-A1S-sdkconfig.defaults

@@ -31,6 +31,8 @@ CONFIG_SPDIF_BCK_IO=27
 CONFIG_SPDIF_WS_IO=26
 CONFIG_SPDIF_WS_IO=26
 CONFIG_SPDIF_DO_IO=-1
 CONFIG_SPDIF_DO_IO=-1
 CONFIG_DAC_CONFIG="model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32"
 CONFIG_DAC_CONFIG="model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32"
+CONFIG_MUTE_GPIO=-1
+CONFIG_MUTE_GPIO_LEVEL=-1
 CONFIG_IDF_TARGET_ESP32=y
 CONFIG_IDF_TARGET_ESP32=y
 CONFIG_IDF_TARGET="esp32"
 CONFIG_IDF_TARGET="esp32"
 
 

+ 2 - 0
build-scripts/I2S-4MFlash-sdkconfig.defaults

@@ -33,6 +33,8 @@ CONFIG_SDIF_NUM=0
 CONFIG_SPDIF_BCK_IO=33
 CONFIG_SPDIF_BCK_IO=33
 CONFIG_SPDIF_WS_IO=25
 CONFIG_SPDIF_WS_IO=25
 CONFIG_SPDIF_DO_IO=-1
 CONFIG_SPDIF_DO_IO=-1
+CONFIG_MUTE_GPIO=-1
+CONFIG_MUTE_GPIO_LEVEL=-1
 
 
 CONFIG_IDF_TARGET_ESP32=y
 CONFIG_IDF_TARGET_ESP32=y
 CONFIG_IDF_TARGET="esp32"
 CONFIG_IDF_TARGET="esp32"

+ 2 - 1
build-scripts/SqueezeAmp4MBFlash-sdkconfig.defaults

@@ -31,9 +31,10 @@ CONFIG_BAT_SCALE="20.24"
 CONFIG_I2S_NUM=0
 CONFIG_I2S_NUM=0
 CONFIG_SDIF_NUM=0
 CONFIG_SDIF_NUM=0
 CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15"
 CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15"
-CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14"
+CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0"
 CONFIG_IDF_TARGET_ESP32=y
 CONFIG_IDF_TARGET_ESP32=y
 CONFIG_IDF_TARGET="esp32"
 CONFIG_IDF_TARGET="esp32"
+CONFIG_MUTE_GPIO_LEVEL=-1
 
 
 #
 #
 # SDK tool configuration
 # SDK tool configuration

+ 1 - 0
build-scripts/SqueezeAmp8MBFlash-sdkconfig.defaults

@@ -32,6 +32,7 @@ CONFIG_I2S_NUM=0
 CONFIG_SDIF_NUM=0
 CONFIG_SDIF_NUM=0
 CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15"
 CONFIG_SPDIF_CONFIG="bck=33,ws=25,do=15"
 CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14"
 CONFIG_DAC_CONFIG="model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14"
+CONFIG_MUTE_GPIO_LEVEL=-1
 
 
 CONFIG_IDF_TARGET_ESP32=y
 CONFIG_IDF_TARGET_ESP32=y
 CONFIG_IDF_TARGET="esp32"
 CONFIG_IDF_TARGET="esp32"

+ 12 - 0
components/config/config.c

@@ -611,9 +611,21 @@ void config_delete_key(const char *key){
 	}
 	}
 	config_unlock();
 	config_unlock();
 }
 }
+
 void * config_alloc_get(nvs_type_t nvs_type, const char *key) {
 void * config_alloc_get(nvs_type_t nvs_type, const char *key) {
 	return config_alloc_get_default(nvs_type, key, NULL, 0);
 	return config_alloc_get_default(nvs_type, key, NULL, 0);
 }
 }
+
+void * config_alloc_get_str(const char *key, char *lead, char *fallback) {
+	if (lead && *lead) return strdup(lead);
+	char *value = config_alloc_get_default(NVS_TYPE_STR, key, NULL, 0);
+	if ((!value || !*value) && fallback) {
+		if (value) free(value);
+		value = strdup(fallback);
+	}
+	return value;
+}
+
 void * config_alloc_get_default(nvs_type_t nvs_type, const char *key, void * default_value, size_t blob_size) {
 void * config_alloc_get_default(nvs_type_t nvs_type, const char *key, void * default_value, size_t blob_size) {
 
 
 	void * value = NULL;
 	void * value = NULL;

+ 1 - 0
components/config/config.h

@@ -34,6 +34,7 @@ void * config_alloc_get_default(nvs_type_t type, const char *key, void * default
 void config_delete_key(const char *key);
 void config_delete_key(const char *key);
 void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
 void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
 void * config_alloc_get(nvs_type_t nvs_type, const char *key) ;
 void * config_alloc_get(nvs_type_t nvs_type, const char *key) ;
+void * config_alloc_get_str(const char *key, char *lead, char *fallback);
 bool wait_for_commit();
 bool wait_for_commit();
 char * config_alloc_get_json(bool bFormatted);
 char * config_alloc_get_json(bool bFormatted);
 esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, void * value);
 esp_err_t config_set_value(nvs_type_t nvs_type, const char *key, void * value);

+ 1 - 6
components/display/display.c

@@ -56,12 +56,7 @@ GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD
  */
  */
 void display_init(char *welcome) {
 void display_init(char *welcome) {
 	bool init = false;
 	bool init = false;
-	char *config = config_alloc_get(NVS_TYPE_STR, "display_config");
-
-	if (!config) {
-		ESP_LOGI(TAG, "no display");
-		return;
-	}	
+	char *config = config_alloc_get_str("display_config", CONFIG_DISPLAY_CONFIG, "N/A");
 	
 	
 	int width = -1, height = -1, backlight_pin = -1;
 	int width = -1, height = -1, backlight_pin = -1;
 	char *p, *drivername = strstr(config, "driver");
 	char *p, *drivername = strstr(config, "driver");

+ 1 - 1
components/services/accessors.c

@@ -75,7 +75,7 @@ const spi_bus_config_t * config_spi_get(spi_host_device_t * spi_host) {
         .quadhd_io_num = -1
         .quadhd_io_num = -1
     };
     };
 
 
-	nvs_item = config_alloc_get(NVS_TYPE_STR, "spi_config");
+	nvs_item = config_alloc_get_str("spi_config", CONFIG_SPI_CONFIG, NULL);
 	if (nvs_item) {
 	if (nvs_item) {
 		if ((p = strcasestr(nvs_item, "data")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(nvs_item, "data")) != NULL) spi.mosi_io_num = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(nvs_item, "clk")) != NULL) spi.sclk_io_num = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(nvs_item, "clk")) != NULL) spi.sclk_io_num = atoi(strchr(p, '=') + 1);

+ 1 - 1
components/services/led.c

@@ -231,7 +231,7 @@ void led_svc_init(void) {
 #ifndef CONFIG_LED_LOCKED
 #ifndef CONFIG_LED_LOCKED
 	parse_set_GPIO(set_led_gpio);
 	parse_set_GPIO(set_led_gpio);
 #endif
 #endif
-	ESP_LOGI(TAG,"Configuring LEDs green:%d (active:%d %d%%), red:%d (active:%d %d%%)", green.gpio, green.active, green.pwm, red.gpio, red.active, red.pwm);
+	ESP_LOGI(TAG,"Configuring LEDs green:%d (active:%d %d%%), red:%d (active:%d %d%%)", green.gpio, green.active, green.pwm, green.gpio, green.active, green.pwm );
 	
 	
 	char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness"), *p; 
 	char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness"), *p; 
 	if (nvs_item) {
 	if (nvs_item) {

+ 3 - 2
components/squeezelite/ac101/ac101.c

@@ -52,7 +52,7 @@ static bool init(char *config, int i2c_port_num);
 static void deinit(void);
 static void deinit(void);
 static void speaker(bool active);
 static void speaker(bool active);
 static void headset(bool active);
 static void headset(bool active);
-static void volume(unsigned left, unsigned right);
+static bool volume(unsigned left, unsigned right);
 static void power(adac_power_e mode);
 static void power(adac_power_e mode);
 
 
 const struct adac_s dac_ac101 = { "AC101", init, deinit, power, speaker, headset, volume };
 const struct adac_s dac_ac101 = { "AC101", init, deinit, power, speaker, headset, volume };
@@ -164,8 +164,9 @@ static void deinit(void)	{
 /****************************************************************************************
 /****************************************************************************************
  * change volume
  * change volume
  */
  */
-static void volume(unsigned left, unsigned right) {
+static bool volume(unsigned left, unsigned right) {
 	// nothing at that point, volume is handled by backend
 	// nothing at that point, volume is handled by backend
+	return false;
 } 
 } 
 
 
 /****************************************************************************************
 /****************************************************************************************

+ 1 - 1
components/squeezelite/adac.h

@@ -21,7 +21,7 @@ struct adac_s {
 	void (*power)(adac_power_e mode);
 	void (*power)(adac_power_e mode);
 	void (*speaker)(bool active);
 	void (*speaker)(bool active);
 	void (*headset)(bool active);
 	void (*headset)(bool active);
-	void (*volume)(unsigned left, unsigned right);
+	bool (*volume)(unsigned left, unsigned right);
 };
 };
 
 
 extern const struct adac_s dac_tas57xx;
 extern const struct adac_s dac_tas57xx;

+ 148 - 7
components/squeezelite/external/dac_external.c

@@ -12,22 +12,163 @@
 #include <freertos/FreeRTOS.h>
 #include <freertos/FreeRTOS.h>
 #include <freertos/task.h>
 #include <freertos/task.h>
 #include <driver/i2s.h>
 #include <driver/i2s.h>
+#include "driver/i2c.h"
 #include "esp_log.h"
 #include "esp_log.h"
+#include "cJSON.h"
 #include "config.h"
 #include "config.h"
 #include "adac.h"
 #include "adac.h"
 
 
 static const char TAG[] = "DAC external";
 static const char TAG[] = "DAC external";
 
 
+static void deinit(void) { }
+static void speaker(bool active) { }
+static void headset(bool active) { } 
+static bool volume(unsigned left, unsigned right) { return false; }
+static void power(adac_power_e mode);
 static bool init(char *config, int i2c_port_num);
 static bool init(char *config, int i2c_port_num);
-static void deinit(void) { };
-static void speaker(bool active) { };
-static void headset(bool active) { } ;
-static void volume(unsigned left, unsigned right) { };
-static void power(adac_power_e mode) { };
+
+static bool i2c_json_execute(char *set);
+static esp_err_t i2c_write_reg(uint8_t reg, uint8_t val);
+static uint8_t i2c_read_reg(uint8_t reg);
 
 
 const struct adac_s dac_external = { "i2s", init, deinit, power, speaker, headset, volume };
 const struct adac_s dac_external = { "i2s", init, deinit, power, speaker, headset, volume };
+static int i2c_port, i2c_addr;
+static cJSON *i2c_json;
+
+/****************************************************************************************
+ * init
+ */
+static bool init(char *config, int i2c_port_num)	{	 
+	char *p;	
+	i2c_port = i2c_port_num;
+	
+	// configure i2c
+	i2c_config_t i2c_config = {
+			.mode = I2C_MODE_MASTER,
+			.sda_io_num = -1,
+			.sda_pullup_en = GPIO_PULLUP_ENABLE,
+			.scl_io_num = -1,
+			.scl_pullup_en = GPIO_PULLUP_ENABLE,
+			.master.clk_speed = 250000,
+		};
+
+	if ((p = strcasestr(config, "i2c")) != NULL) i2c_addr = atoi(strchr(p, '=') + 1);
+	if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
+	if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
+
+	p = config_alloc_get_str("dac_controlset", CONFIG_DAC_CONTROLSET, NULL);
+	i2c_json = cJSON_Parse(p);
+	
+	if (!i2c_addr || !i2c_json || i2c_config.sda_io_num == -1 || i2c_config.scl_io_num == -1) {
+		if (p) free(p);
+		ESP_LOGW(TAG, "No i2c controlset found");
+		return true;
+	}	
+	
+	ESP_LOGI(TAG, "DAC uses I2C @%d with sda:%d, scl:%d", i2c_addr, i2c_config.sda_io_num, i2c_config.scl_io_num);
+	
+	// we have an I2C configured	
+	i2c_param_config(i2c_port, &i2c_config);
+	i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
+		
+	if (!i2c_json_execute("init")) {	
+		ESP_LOGE(TAG, "could not intialize DAC");
+		return false;
+	}	
+	
+	return true;
+}	
 
 
-static bool init(char *config, int i2c_port_num) { 
-	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");
 }
 }
 
 
+/****************************************************************************************
+ * 
+ */
+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 *reg = cJSON_GetObjectItemCaseSensitive(item, "reg");
+		cJSON *val = cJSON_GetObjectItemCaseSensitive(item, "val");
+		cJSON *mode = cJSON_GetObjectItemCaseSensitive(item, "mode");
+
+		if (!reg || !val) continue;
+
+		if (!mode) {
+			i2c_write_reg(reg->valueint, val->valueint);
+		} else if (!strcasecmp(mode->valuestring, "or")) {
+			uint8_t data = i2c_read_reg(reg->valueint);
+			data |= (uint8_t) val->valueint;
+			i2c_write_reg(reg->valueint, data);
+		} else if (!strcasecmp(mode->valuestring, "and")) {
+			uint8_t data = i2c_read_reg(reg->valueint);
+			data &= (uint8_t) val->valueint;
+			i2c_write_reg(reg->valueint, data);
+        }
+	}
+	
+	return true;
+}	
+
+/****************************************************************************************
+ * 
+ */
+static esp_err_t i2c_write_reg(uint8_t reg, uint8_t val) {
+	esp_err_t ret;
+    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+	
+	i2c_master_write_byte(cmd, i2c_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
+	i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
+	i2c_master_write_byte(cmd, val, I2C_MASTER_NACK);
+	
+	i2c_master_stop(cmd);
+    ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(cmd);
+	
+	if (ret != ESP_OK) {
+		ESP_LOGW(TAG, "I2C write failed");
+	}
+	
+    return ret;
+}
+
+/****************************************************************************************
+ * 
+ */
+static uint8_t i2c_read_reg(uint8_t reg) {
+	esp_err_t ret;
+	uint8_t data = 0;
+	
+	i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    
+	i2c_master_write_byte(cmd, i2c_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
+	i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
+
+	i2c_master_start(cmd);			
+	i2c_master_write_byte(cmd, i2c_addr | I2C_MASTER_READ, I2C_MASTER_NACK);
+	i2c_master_read_byte(cmd, &data, I2C_MASTER_NACK);
+	
+    i2c_master_stop(cmd);
+	ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
+	i2c_cmd_link_delete(cmd);
+	
+	if (ret != ESP_OK) {
+		ESP_LOGW(TAG, "I2C read failed");
+	}
+	
+	return data;
+}
+
+

+ 41 - 27
components/squeezelite/output_i2s.c

@@ -93,7 +93,10 @@ static size_t dma_buf_frames;
 static pthread_t thread;
 static pthread_t thread;
 static TaskHandle_t stats_task;
 static TaskHandle_t stats_task;
 static bool stats;
 static bool stats;
-static int amp_gpio = -1;
+static struct {
+	int gpio, active;
+} amp_control = { -1, 1 },
+  mute_control = { CONFIG_MUTE_GPIO, CONFIG_MUTE_GPIO_LEVEL };
 
 
 DECLARE_ALL_MIN_MAX;
 DECLARE_ALL_MIN_MAX;
 
 
@@ -129,14 +132,17 @@ static void jack_handler(bool inserted) {
  * amp GPIO
  * amp GPIO
  */
  */
 static void set_amp_gpio(int gpio, char *value) {
 static void set_amp_gpio(int gpio, char *value) {
+	char *p;
+	
 	if (!strcasecmp(value, "amp")) {
 	if (!strcasecmp(value, "amp")) {
-		amp_gpio = gpio;
+		amp_control.gpio = gpio;
+		if ((p = strchr(value, ':')) != NULL) amp_control.active = atoi(p + 1);
 		
 		
-		gpio_pad_select_gpio(amp_gpio);
-		gpio_set_direction(amp_gpio, GPIO_MODE_OUTPUT);
-		gpio_set_level(amp_gpio, 0);
+		gpio_pad_select_gpio(amp_control.gpio);
+		gpio_set_direction(amp_control.gpio, GPIO_MODE_OUTPUT);
+		gpio_set_level(amp_control.gpio, !amp_control.active);
 		
 		
-		LOG_INFO("setting amplifier GPIO %d", amp_gpio);
+		LOG_INFO("setting amplifier GPIO %d (active:%d)", amp_control.gpio, amp_control.active);
 	}	
 	}	
 }	
 }	
 
 
@@ -145,7 +151,7 @@ static void set_amp_gpio(int gpio, char *value) {
  */
  */
 void output_init_i2s(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
 void output_init_i2s(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
 	loglevel = level;
 	loglevel = level;
-	char *p, *dac_config, *spdif_config;
+	char *p;
 	esp_err_t res;
 	esp_err_t res;
 	
 	
 	p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
 	p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
@@ -186,12 +192,9 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 	i2s_pin_config_t i2s_pin_config = {	.bck_io_num = -1, .ws_io_num = -1, .data_out_num = -1, .data_in_num = -1 }; 				
 	i2s_pin_config_t i2s_pin_config = {	.bck_io_num = -1, .ws_io_num = -1, .data_out_num = -1, .data_in_num = -1 }; 				
 
 
 	// get SPDIF configuration from NVS or compile
 	// get SPDIF configuration from NVS or compile
-#ifdef CONFIG_SPDIF_CONFIG
-	spdif_config = strdup(CONFIG_SPDIF_CONFIG);
-#else
-	spdif_config = config_alloc_get(NVS_TYPE_STR, "spdif_config");
-	if (!spdif_config) spdif_config = strdup("bck=" STR(CONFIG_SPDIF_BCK_IO) ",ws=" STR(CONFIG_SPDIF_WS_IO) ",do=" STR(CONFIG_SPDIF_DO_IO));
-#endif		
+	char *spdif_config = config_alloc_get_str("spdif_config", CONFIG_SPDIF_CONFIG, "bck=" STR(CONFIG_SPDIF_BCK_IO) 
+											  ",ws=" STR(CONFIG_SPDIF_WS_IO) ",do=" STR(CONFIG_SPDIF_DO_IO));
+
 	if ((p = strcasestr(spdif_config, "do")) != NULL) i2s_pin_config.data_out_num = atoi(strchr(p, '=') + 1);
 	if ((p = strcasestr(spdif_config, "do")) != NULL) i2s_pin_config.data_out_num = atoi(strchr(p, '=') + 1);
 	
 	
 	// common I2S initialization
 	// common I2S initialization
@@ -237,12 +240,10 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 			gpio_set_level(i2s_pin_config.data_out_num, 0);
 			gpio_set_level(i2s_pin_config.data_out_num, 0);
 		}	
 		}	
 		
 		
-#ifdef CONFIG_DAC_CONFIG
-		dac_config = strdup(CONFIG_DAC_CONFIG);
-#else
-		dac_config = config_alloc_get(NVS_TYPE_STR, "dac_config");
-		if (!dac_config) dac_config = strdup("model=i2s,bck=" STR(CONFIG_I2S_BCK_IO) ",ws=" STR(CONFIG_I2S_WS_IO) ",do=" STR(CONFIG_I2S_DO_IO) ",sda=" STR(CONFIG_I2C_SDA) ",scl=" STR(CONFIG_I2C_SCL));
-#endif	
+		char *dac_config = config_alloc_get_str("dac_config", CONFIG_DAC_CONFIG, "model=i2s,bck=" STR(CONFIG_I2S_BCK_IO) 
+												",ws=" STR(CONFIG_I2S_WS_IO) ",do=" STR(CONFIG_I2S_DO_IO) 
+												",sda=" STR(CONFIG_I2C_SDA) ",scl=" STR(CONFIG_I2C_SCL)
+												",mute" STR(CONFIG_MUTE_GPIO));
 		char model[32] = "i2s";
 		char model[32] = "i2s";
 		if ((p = strcasestr(dac_config, "model")) != NULL) sscanf(p, "%*[^=]=%31[^,]", model);
 		if ((p = strcasestr(dac_config, "model")) != NULL) sscanf(p, "%*[^=]=%31[^,]", model);
 		
 		
@@ -252,6 +253,13 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 		if ((p = strcasestr(dac_config, "bck")) != NULL) i2s_pin_config.bck_io_num = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(dac_config, "bck")) != NULL) i2s_pin_config.bck_io_num = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(dac_config, "ws")) != NULL) i2s_pin_config.ws_io_num = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(dac_config, "ws")) != NULL) i2s_pin_config.ws_io_num = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(dac_config, "do")) != NULL) i2s_pin_config.data_out_num = atoi(strchr(p, '=') + 1);
 		if ((p = strcasestr(dac_config, "do")) != NULL) i2s_pin_config.data_out_num = atoi(strchr(p, '=') + 1);
+		if ((p = strcasestr(dac_config, "mute")) != NULL) {
+			char mute[8];
+			sscanf(p, "%*[^=]=%7[^,]", mute);
+			mute_control.gpio = atoi(mute);
+			if ((p = strchr(mute, ':')) != NULL) mute_control.active = atoi(p + 1);
+		}	
+		
 		free(dac_config);
 		free(dac_config);
 		
 		
 		i2s_config.sample_rate = output.current_sample_rate;
 		i2s_config.sample_rate = output.current_sample_rate;
@@ -263,9 +271,15 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 		
 		
 		res |= i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
 		res |= i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
 		res |= i2s_set_pin(CONFIG_I2S_NUM, &i2s_pin_config);
 		res |= i2s_set_pin(CONFIG_I2S_NUM, &i2s_pin_config);
-	
-		LOG_INFO("%s DAC using I2S bck:%d, ws:%d, do:%d", model, i2s_pin_config.bck_io_num, 
-							i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num);
+		
+		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)", model, i2s_pin_config.bck_io_num, i2s_pin_config.ws_io_num, 
+																   i2s_pin_config.data_out_num, mute_control.gpio, mute_control.active, res);
 	}	
 	}	
 	
 	
 	free(spdif_config);
 	free(spdif_config);
@@ -340,8 +354,8 @@ void output_close_i2s(void) {
  * change volume
  * change volume
  */
  */
 bool output_volume_i2s(unsigned left, unsigned right) {
 bool output_volume_i2s(unsigned left, unsigned right) {
-	adac->volume(left, right);
-	return false;	
+	if (mute_control.gpio >= 0) gpio_set_level(mute_control.gpio, (left | right) ? !mute_control.active : mute_control.active);
+	return adac->volume(left, right);
 } 
 } 
 
 
 /****************************************************************************************
 /****************************************************************************************
@@ -422,8 +436,8 @@ static void *output_thread_i2s(void *arg) {
 			LOG_INFO("Output state is %d", output.state);
 			LOG_INFO("Output state is %d", output.state);
 			if (output.state == OUTPUT_OFF) {
 			if (output.state == OUTPUT_OFF) {
 				led_blink(LED_GREEN, 100, 2500);
 				led_blink(LED_GREEN, 100, 2500);
-				if (amp_gpio != -1) gpio_set_level(amp_gpio, 0);
-				LOG_INFO("switching off amp GPIO %d", amp_gpio);
+				if (amp_control.gpio != -1) gpio_set_level(amp_control.gpio, !amp_control.active);
+				LOG_INFO("switching off amp GPIO %d", amp_control.gpio);
 			} else if (output.state == OUTPUT_STOPPED) {
 			} else if (output.state == OUTPUT_STOPPED) {
 				adac->speaker(false);
 				adac->speaker(false);
 				led_blink(LED_GREEN, 200, 1000);
 				led_blink(LED_GREEN, 200, 1000);
@@ -486,7 +500,7 @@ static void *output_thread_i2s(void *arg) {
 			i2s_zero_dma_buffer(CONFIG_I2S_NUM);
 			i2s_zero_dma_buffer(CONFIG_I2S_NUM);
 			i2s_start(CONFIG_I2S_NUM);
 			i2s_start(CONFIG_I2S_NUM);
 			adac->power(ADAC_ON);	
 			adac->power(ADAC_ON);	
-			if (amp_gpio != -1) gpio_set_level(amp_gpio, 1);
+			if (amp_control.gpio != -1) gpio_set_level(amp_control.gpio, amp_control.active);
 		} 
 		} 
 		
 		
 		// this does not work well as set_sample_rates resets the fifos (and it's too early)
 		// this does not work well as set_sample_rates resets the fifos (and it's too early)

+ 7 - 19
components/squeezelite/tas57xx/dac_57xx.c

@@ -27,7 +27,7 @@ static bool init(char *config, int i2c_port_num);
 static void deinit(void);
 static void deinit(void);
 static void speaker(bool active);
 static void speaker(bool active);
 static void headset(bool active);
 static void headset(bool active);
-static void volume(unsigned left, unsigned right);
+static bool volume(unsigned left, unsigned right);
 static void power(adac_power_e mode);
 static void power(adac_power_e mode);
 
 
 const struct adac_s dac_tas57xx = { "TAS57xx", init, deinit, power, speaker, headset, volume };
 const struct adac_s dac_tas57xx = { "TAS57xx", init, deinit, power, speaker, headset, volume };
@@ -61,7 +61,6 @@ static const struct tas57xx_cmd_s tas57xx_cmd[] = {
 
 
 static uint8_t tas57_addr;
 static uint8_t tas57_addr;
 static int i2c_port;
 static int i2c_port;
-static int mute_gpio = -1;
 
 
 static void dac_cmd(dac_cmd_e cmd, ...);
 static void dac_cmd(dac_cmd_e cmd, ...);
 static int tas57_detect(void);
 static int tas57_detect(void);
@@ -85,7 +84,6 @@ static bool init(char *config, int i2c_port_num)	{
 
 
 	if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
 	if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
 	if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
 	if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
-	if ((p = strcasestr(config, "mute")) != NULL) mute_gpio = atoi(strchr(p, '=') + 1);
 	
 	
 	i2c_param_config(i2c_port, &i2c_config);
 	i2c_param_config(i2c_port, &i2c_config);
 	i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
 	i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
@@ -113,20 +111,14 @@ static bool init(char *config, int i2c_port_num)	{
 	esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
 	esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
     i2c_cmd_link_delete(i2c_cmd);
     i2c_cmd_link_delete(i2c_cmd);
 
 
-	ESP_LOGI(TAG, "TAS57xx uses I2C sda:%d, scl:%d and mute: %d", i2c_config.sda_io_num, i2c_config.scl_io_num, mute_gpio);
+	ESP_LOGI(TAG, "TAS57xx uses I2C sda:%d, scl:%d", i2c_config.sda_io_num, i2c_config.scl_io_num);
 	
 	
-	if (res == ESP_OK) {
-		if (mute_gpio >= 0) {
-			// init volume & mute
-			gpio_pad_select_gpio(mute_gpio);
-			gpio_set_direction(mute_gpio, GPIO_MODE_OUTPUT);
-			gpio_set_level(mute_gpio, 0);
-		}	
-		return true;
-	} else {
+	if (res != ESP_OK) {
 		ESP_LOGE(TAG, "could not intialize TAS57xx %d", res);
 		ESP_LOGE(TAG, "could not intialize TAS57xx %d", res);
 		return false;
 		return false;
 	}	
 	}	
+	
+	return true;
 }	
 }	
 
 
 /****************************************************************************************
 /****************************************************************************************
@@ -139,10 +131,7 @@ static void deinit(void)	{
 /****************************************************************************************
 /****************************************************************************************
  * change volume
  * change volume
  */
  */
-static void volume(unsigned left, unsigned right) {
-	ESP_LOGI(TAG, "TAS57xx volume (L:%u R:%u)", left, right);
-	if (mute_gpio >= 0) gpio_set_level(mute_gpio, left || right);
-} 
+static bool volume(unsigned left, unsigned right) { return false; }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * power
  * power
@@ -175,8 +164,7 @@ static void speaker(bool active) {
 /****************************************************************************************
 /****************************************************************************************
  * headset
  * headset
  */
  */
-static void headset(bool active) {
-} 
+static void headset(bool active) { } 
  
  
 /****************************************************************************************
 /****************************************************************************************
  * DAC specific commands
  * DAC specific commands

+ 71 - 40
main/Kconfig.projbuild

@@ -21,6 +21,7 @@ menu "Squeezelite-ESP32"
         	help
         	help
         		Set logging level info|debug|sdebug 	
         		Set logging level info|debug|sdebug 	
 	endmenu
 	endmenu
+	
 	config JACK_LOCKED
 	config JACK_LOCKED
 		bool
 		bool
 	config BAT_LOCKED	
 	config BAT_LOCKED	
@@ -33,33 +34,59 @@ menu "Squeezelite-ESP32"
 		bool				
 		bool				
 	config SPKFAULT_LOCKED
 	config SPKFAULT_LOCKED
 		bool				
 		bool				
+	config MUTE_GPIO_LEVEL
+		int 
+		default 0
+		
+# AGGREGATES - begin
+# these parameters are "aggregates"	that take precedence. The must have a default value	
 	config DAC_CONFIG
 	config DAC_CONFIG
 		string 
 		string 
-		default "model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14" if SQUEEZEAMP
+		default "model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0" if SQUEEZEAMP
 		default "model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32" if A1S
 		default "model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32" if A1S
+		default "model=I2S,bck=26,ws=25,do=33,i2c=106,sda=21,scl=22" if TWATCH2020
+		default ""
 	config SPDIF_CONFIG		
 	config SPDIF_CONFIG		
 		string
 		string
 		default "bck=33,ws=25,do=15" if SQUEEZEAMP
 		default "bck=33,ws=25,do=15" if SQUEEZEAMP
-	menu "Audio Output"
-		choice OUTPUT_TYPE
-			prompt "Output Type"
-	        default BASIC_I2C_BT
-	        help
-	            Type of hardware platform
-	        config SQUEEZEAMP 
-				bool "SqueezeAMP"
-				select JACK_LOCKED
-				select BAT_LOCKED
-				select I2C_LOCKED
-				select LED_LOCKED
-				select SPKFAULT_LOCKED
-			config A1S
-	            bool "ESP32-A1S module"				
-				select I2C_LOCKED
-	        config BASIC_I2C_BT
-	            bool "Generic I2S & Bluetooth"
-	    endchoice
-	  	
+		default	""
+	config SPI_CONFIG
+		string
+		default "dc=27,data=19,clk=18" if TWATCH2020		
+		default	""
+	config DISPLAY_CONFIG
+		string
+		default "SPI,driver=ST7789,width=240,height=240,cs=5,back=12,speed=16000000,HFlip,VFlip" if TWATCH2020
+		default ""
+	config DAC_CONTROLSET
+		string
+		default "{ \"init\": [ {\"reg\":41, \"val\":128}, {\"reg\":18, \"val\":255} ], \"poweron\": [ {\"reg\":18, \"val\":64, \"mode\":\"or\"} ], \"poweroff\": [ {\"reg\":18, \"val\":191, \"mode\":\"and\"} ] }" if TWATCH2020
+		default ""		
+# AGGREGATES - end		
+		
+	choice OUTPUT_TYPE
+		prompt "Main system"
+	       default BASIC_I2C_BT
+	       help
+	           Type of hardware platform
+	       config SQUEEZEAMP 
+			bool "SqueezeAMP"
+			select JACK_LOCKED
+			select BAT_LOCKED
+			select I2C_LOCKED
+			select LED_LOCKED
+			select SPKFAULT_LOCKED
+		config A1S
+	           bool "ESP32-A1S module"				
+			select I2C_LOCKED
+		config TWATCH2020	
+			bool "T-WATCH2020 by LilyGo"				
+			select I2C_LOCKED				
+	       config BASIC_I2C_BT
+	           bool "Generic I2S & Bluetooth"
+	endchoice		
+
+	menu "Audio settings"
 		menu "DAC settings" 
 		menu "DAC settings" 
 			visible if BASIC_I2C_BT
 			visible if BASIC_I2C_BT
 			menu "I2S settings"
 			menu "I2S settings"
@@ -106,6 +133,10 @@ menu "Squeezelite-ESP32"
 				default -1 
 				default -1 
 				help
 				help
 					GPIO used to mute DAC (not used mostly, leave it to -1).															
 					GPIO used to mute DAC (not used mostly, leave it to -1).															
+			config MUTE_GPIO_LEVEL
+				int "Mute GPIO active level"
+				depends on MUTE_GPIO != -1
+				default 1 					
 		endmenu
 		endmenu
 		
 		
 		menu "SPDIF settings" 
 		menu "SPDIF settings" 
@@ -199,16 +230,17 @@ menu "Squeezelite-ESP32"
 	endmenu	
 	endmenu	
 
 
 	menu "Display Screen"
 	menu "Display Screen"
+		depends on !TWATCH2020
 		config DISPLAY_CONFIG
 		config DISPLAY_CONFIG
 			string "Screen configuraton"
 			string "Screen configuraton"
-			default ""
 			help
 			help
 				Set parameters for display screen, leave empty for no screen
 				Set parameters for display screen, leave empty for no screen
-				I2C,width=<pixels>,height=<pixels>[address=<i2c_address>][,HFlip][,VFlip]
-				SPI,width=<pixels>,height=<pixels>,cs=<gpio>[,HFlip][,VFlip]
+				I2C,driver=<model>,width=<pixels>,height=<pixels>[address=<i2c_address>][,HFlip][,VFlip][,rotate]
+				SPI,driver=<model>,width=<pixels>,height=<pixels>,cs=<gpio>[,HFlip][,VFlip][,rotate]
 	endmenu	
 	endmenu	
 	
 	
 	menu "Various I/O"
 	menu "Various I/O"
+		visible if !TWATCH2020
 		config I2C_CONFIG
 		config I2C_CONFIG
 			string "I2C system configuration"
 			string "I2C system configuration"
 			default ""
 			default ""
@@ -217,7 +249,6 @@ menu "Squeezelite-ESP32"
 				sda=<gpio>,scl=<gpio>[,speed=<num>][,port=<0|1>]
 				sda=<gpio>,scl=<gpio>[,speed=<num>][,port=<0|1>]
 		config SPI_CONFIG
 		config SPI_CONFIG
 			string "SPI system configuration"
 			string "SPI system configuration"
-			default ""
 			help
 			help
 				Set parameters of shared SPI interface
 				Set parameters of shared SPI interface
 				data=<gpio>,clk=<gpio>[,d/c=<num>][,host=<0|1|2>]				
 				data=<gpio>,clk=<gpio>[,d/c=<num>][,host=<0|1|2>]				
@@ -226,7 +257,7 @@ menu "Squeezelite-ESP32"
 			default ""
 			default ""
 			help
 			help
 				Set parameters of shared GPIO with special values. 
 				Set parameters of shared GPIO with special values. 
-				<gpio_1>=Vcc|GND|amp|jack[:0|1][,<gpio_n>=Vcc|GND|amp|jack[:0|1]]
+				<gpio_1>=Vcc|GND|amp[:0|1]|jack[:0|1][,<gpio_n>=Vcc|GND|amp[:0|1]|jack[:0|1]]
 				'amp'  => GPIO that is set when playback starts 
 				'amp'  => GPIO that is set when playback starts 
 				'jack' => GPIO used for audio jack detection
 				'jack' => GPIO used for audio jack detection
 				'green', 'red' => GPIO for status LED
 				'green', 'red' => GPIO for status LED
@@ -236,39 +267,39 @@ menu "Squeezelite-ESP32"
 			default ""
 			default ""
 			help
 			help
 				Set GPIO for rotary encoder (quadrature phase). See README on SqueezeESP32 project's GitHub for more details
 				Set GPIO for rotary encoder (quadrature phase). See README on SqueezeESP32 project's GitHub for more details
-				A=<gpio>,B=<gpio>[,SW=gpio>[,volume][,longpress]]				
+				A=<gpio>,B=<gpio>[,SW=gpio>[[,knobonly[=<ms>]|[,volume][,longpress]]			
 	endmenu
 	endmenu
 	menu "LED configuration"
 	menu "LED configuration"
-		visible if !SQUEEZEAMP
+		visible if !SQUEEZEAMP && !TWATCH2020
 		config LED_GREEN_GPIO
 		config LED_GREEN_GPIO
 			int "Green led GPIO"
 			int "Green led GPIO"
-			default -1 if !SQUEEZEAMP
-			default 12 if SQUEEZEAMP
+			default 12 if SQUEEZEAMP			
+			default -1 
 			help
 			help
 				Set to -1 for no LED
 				Set to -1 for no LED
 		config LED_GREEN_GPIO_LEVEL
 		config LED_GREEN_GPIO_LEVEL
 			int "Green led ON level"
 			int "Green led ON level"
 			depends on LED_GREEN_GPIO != -1
 			depends on LED_GREEN_GPIO != -1
 			default 0 if SQUEEZEAMP
 			default 0 if SQUEEZEAMP
-			default 1 if !SQUEEZEAMP
+			default 1 
 		config LED_RED_GPIO				
 		config LED_RED_GPIO				
 			int "Red led GPIO"
 			int "Red led GPIO"
-			default -1 if !SQUEEZEAMP
 			default 13 if SQUEEZEAMP
 			default 13 if SQUEEZEAMP
+			default -1
 			help
 			help
 				Set to -1 for no LED
 				Set to -1 for no LED
 		config LED_RED_GPIO_LEVEL
 		config LED_RED_GPIO_LEVEL
 			int "Red led ON level"
 			int "Red led ON level"
 			depends on LED_RED_GPIO != -1
 			depends on LED_RED_GPIO != -1
 			default 0 if SQUEEZEAMP
 			default 0 if SQUEEZEAMP
-			default 1 if !SQUEEZEAMP
+			default 1
 	endmenu
 	endmenu
     menu "Audio JACK"	
     menu "Audio JACK"	
-		visible if !SQUEEZEAMP
+		visible if !SQUEEZEAMP && !TWATCH2020
 		config JACK_GPIO		
 		config JACK_GPIO		
 			int "Jack insertion GPIO"
 			int "Jack insertion GPIO"
-			default -1 if !SQUEEZEAMP
 			default 34 if SQUEEZEAMP
 			default 34 if SQUEEZEAMP
+			default -1
 			help
 			help
 				GPIO to detect speaker jack insertion. Set to -1 for no detection. 
 				GPIO to detect speaker jack insertion. Set to -1 for no detection. 
 		config JACK_GPIO_LEVEL
 		config JACK_GPIO_LEVEL
@@ -277,11 +308,11 @@ menu "Squeezelite-ESP32"
 			default 0
 			default 0
 	endmenu	
 	endmenu	
 	menu "Speaker Fault"	
 	menu "Speaker Fault"	
-		visible if !SQUEEZEAMP
+		visible if !SQUEEZEAMP && !TWATCH2020
 		config SPKFAULT_GPIO		
 		config SPKFAULT_GPIO		
 			int "Speaker fault GPIO"
 			int "Speaker fault GPIO"
-			default -1 if !SQUEEZEAMP
 			default 2 if SQUEEZEAMP
 			default 2 if SQUEEZEAMP
+			default -1
 			help
 			help
 				GPIO to detect speaker fault condition. Set to -1 for no detection. 
 				GPIO to detect speaker fault condition. Set to -1 for no detection. 
 		config SPKFAULT_GPIO_LEVEL
 		config SPKFAULT_GPIO_LEVEL
@@ -290,18 +321,18 @@ menu "Squeezelite-ESP32"
 			default 0
 			default 0
 	endmenu	
 	endmenu	
 	menu "Battery measure"	
 	menu "Battery measure"	
-		visible if !SQUEEZEAMP
+		visible if !SQUEEZEAMP && !TWATCH2020
 		config BAT_CHANNEL	
 		config BAT_CHANNEL	
 			int "Set channel (0..7)"
 			int "Set channel (0..7)"
-			default -1 if !SQUEEZEAMP
 			default 7 if SQUEEZEAMP
 			default 7 if SQUEEZEAMP
+			default -1 
 			help
 			help
 				Read a value every 10s on ADC1 on set Channel
 				Read a value every 10s on ADC1 on set Channel
 		config BAT_SCALE	
 		config BAT_SCALE	
 			string "Set scaling factor"
 			string "Set scaling factor"
 			depends on BAT_CHANNEL != -1
 			depends on BAT_CHANNEL != -1
-			default "" if !SQUEEZEAMP
 			default "20.24" if SQUEEZEAMP
 			default "20.24" if SQUEEZEAMP
+			default "" 
 			help
 			help
 				Set the scaling factor for this 12 bits ADC
 				Set the scaling factor for this 12 bits ADC
 	endmenu	
 	endmenu	

+ 3 - 0
main/esp_app_main.c

@@ -330,6 +330,9 @@ void register_default_nvs(){
 	ESP_LOGD(TAG,"Registering default value for key %s", "dac_config");
 	ESP_LOGD(TAG,"Registering default value for key %s", "dac_config");
 	config_set_default(NVS_TYPE_STR, "dac_config", "", 0);
 	config_set_default(NVS_TYPE_STR, "dac_config", "", 0);
 	
 	
+	ESP_LOGD(TAG,"Registering default value for key %s", "dac_controlset");
+	config_set_default(NVS_TYPE_STR, "dac_controlset", "", 0);
+	
 	ESP_LOGD(TAG,"Registering default value for key %s", "bat_config");
 	ESP_LOGD(TAG,"Registering default value for key %s", "bat_config");
 	config_set_default(NVS_TYPE_STR, "bat_config", "", 0);
 	config_set_default(NVS_TYPE_STR, "bat_config", "", 0);