Browse Source

audio refactoring done + T-WATCH2020 support

Philippe G 4 years ago
parent
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_DO_IO=-1
 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"
 

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

@@ -33,6 +33,8 @@ CONFIG_SDIF_NUM=0
 CONFIG_SPDIF_BCK_IO=33
 CONFIG_SPDIF_WS_IO=25
 CONFIG_SPDIF_DO_IO=-1
+CONFIG_MUTE_GPIO=-1
+CONFIG_MUTE_GPIO_LEVEL=-1
 
 CONFIG_IDF_TARGET_ESP32=y
 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_SDIF_NUM=0
 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"
+CONFIG_MUTE_GPIO_LEVEL=-1
 
 #
 # SDK tool configuration

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

@@ -32,6 +32,7 @@ CONFIG_I2S_NUM=0
 CONFIG_SDIF_NUM=0
 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_MUTE_GPIO_LEVEL=-1
 
 CONFIG_IDF_TARGET_ESP32=y
 CONFIG_IDF_TARGET="esp32"

+ 12 - 0
components/config/config.c

@@ -611,9 +611,21 @@ void config_delete_key(const char *key){
 	}
 	config_unlock();
 }
+
 void * config_alloc_get(nvs_type_t nvs_type, const char *key) {
 	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 * 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_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_str(const char *key, char *lead, char *fallback);
 bool wait_for_commit();
 char * config_alloc_get_json(bool bFormatted);
 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) {
 	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;
 	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
     };
 
-	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 ((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);

+ 1 - 1
components/services/led.c

@@ -231,7 +231,7 @@ void led_svc_init(void) {
 #ifndef CONFIG_LED_LOCKED
 	parse_set_GPIO(set_led_gpio);
 #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; 
 	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 speaker(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);
 
 const struct adac_s dac_ac101 = { "AC101", init, deinit, power, speaker, headset, volume };
@@ -164,8 +164,9 @@ static void deinit(void)	{
 /****************************************************************************************
  * 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
+	return false;
 } 
 
 /****************************************************************************************

+ 1 - 1
components/squeezelite/adac.h

@@ -21,7 +21,7 @@ struct adac_s {
 	void (*power)(adac_power_e mode);
 	void (*speaker)(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;

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

@@ -12,22 +12,163 @@
 #include <freertos/FreeRTOS.h>
 #include <freertos/task.h>
 #include <driver/i2s.h>
+#include "driver/i2c.h"
 #include "esp_log.h"
+#include "cJSON.h"
 #include "config.h"
 #include "adac.h"
 
 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 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 };
+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 TaskHandle_t stats_task;
 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;
 
@@ -129,14 +132,17 @@ static void jack_handler(bool inserted) {
  * amp GPIO
  */
 static void set_amp_gpio(int gpio, char *value) {
+	char *p;
+	
 	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) {
 	loglevel = level;
-	char *p, *dac_config, *spdif_config;
+	char *p;
 	esp_err_t res;
 	
 	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 }; 				
 
 	// 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);
 	
 	// 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);
 		}	
 		
-#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";
 		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, "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, "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);
 		
 		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_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);
@@ -340,8 +354,8 @@ void output_close_i2s(void) {
  * change volume
  */
 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);
 			if (output.state == OUTPUT_OFF) {
 				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) {
 				adac->speaker(false);
 				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_start(CONFIG_I2S_NUM);
 			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)

+ 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 speaker(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);
 
 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 int i2c_port;
-static int mute_gpio = -1;
 
 static void dac_cmd(dac_cmd_e cmd, ...);
 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, "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_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);
     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);
 		return false;
 	}	
+	
+	return true;
 }	
 
 /****************************************************************************************
@@ -139,10 +131,7 @@ static void deinit(void)	{
 /****************************************************************************************
  * 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
@@ -175,8 +164,7 @@ static void speaker(bool active) {
 /****************************************************************************************
  * headset
  */
-static void headset(bool active) {
-} 
+static void headset(bool active) { } 
  
 /****************************************************************************************
  * DAC specific commands

+ 71 - 40
main/Kconfig.projbuild

@@ -21,6 +21,7 @@ menu "Squeezelite-ESP32"
         	help
         		Set logging level info|debug|sdebug 	
 	endmenu
+	
 	config JACK_LOCKED
 		bool
 	config BAT_LOCKED	
@@ -33,33 +34,59 @@ menu "Squeezelite-ESP32"
 		bool				
 	config SPKFAULT_LOCKED
 		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
 		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=I2S,bck=26,ws=25,do=33,i2c=106,sda=21,scl=22" if TWATCH2020
+		default ""
 	config SPDIF_CONFIG		
 		string
 		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" 
 			visible if BASIC_I2C_BT
 			menu "I2S settings"
@@ -106,6 +133,10 @@ menu "Squeezelite-ESP32"
 				default -1 
 				help
 					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
 		
 		menu "SPDIF settings" 
@@ -199,16 +230,17 @@ menu "Squeezelite-ESP32"
 	endmenu	
 
 	menu "Display Screen"
+		depends on !TWATCH2020
 		config DISPLAY_CONFIG
 			string "Screen configuraton"
-			default ""
 			help
 				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	
 	
 	menu "Various I/O"
+		visible if !TWATCH2020
 		config I2C_CONFIG
 			string "I2C system configuration"
 			default ""
@@ -217,7 +249,6 @@ menu "Squeezelite-ESP32"
 				sda=<gpio>,scl=<gpio>[,speed=<num>][,port=<0|1>]
 		config SPI_CONFIG
 			string "SPI system configuration"
-			default ""
 			help
 				Set parameters of shared SPI interface
 				data=<gpio>,clk=<gpio>[,d/c=<num>][,host=<0|1|2>]				
@@ -226,7 +257,7 @@ menu "Squeezelite-ESP32"
 			default ""
 			help
 				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 
 				'jack' => GPIO used for audio jack detection
 				'green', 'red' => GPIO for status LED
@@ -236,39 +267,39 @@ menu "Squeezelite-ESP32"
 			default ""
 			help
 				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
 	menu "LED configuration"
-		visible if !SQUEEZEAMP
+		visible if !SQUEEZEAMP && !TWATCH2020
 		config LED_GREEN_GPIO
 			int "Green led GPIO"
-			default -1 if !SQUEEZEAMP
-			default 12 if SQUEEZEAMP
+			default 12 if SQUEEZEAMP			
+			default -1 
 			help
 				Set to -1 for no LED
 		config LED_GREEN_GPIO_LEVEL
 			int "Green led ON level"
 			depends on LED_GREEN_GPIO != -1
 			default 0 if SQUEEZEAMP
-			default 1 if !SQUEEZEAMP
+			default 1 
 		config LED_RED_GPIO				
 			int "Red led GPIO"
-			default -1 if !SQUEEZEAMP
 			default 13 if SQUEEZEAMP
+			default -1
 			help
 				Set to -1 for no LED
 		config LED_RED_GPIO_LEVEL
 			int "Red led ON level"
 			depends on LED_RED_GPIO != -1
 			default 0 if SQUEEZEAMP
-			default 1 if !SQUEEZEAMP
+			default 1
 	endmenu
     menu "Audio JACK"	
-		visible if !SQUEEZEAMP
+		visible if !SQUEEZEAMP && !TWATCH2020
 		config JACK_GPIO		
 			int "Jack insertion GPIO"
-			default -1 if !SQUEEZEAMP
 			default 34 if SQUEEZEAMP
+			default -1
 			help
 				GPIO to detect speaker jack insertion. Set to -1 for no detection. 
 		config JACK_GPIO_LEVEL
@@ -277,11 +308,11 @@ menu "Squeezelite-ESP32"
 			default 0
 	endmenu	
 	menu "Speaker Fault"	
-		visible if !SQUEEZEAMP
+		visible if !SQUEEZEAMP && !TWATCH2020
 		config SPKFAULT_GPIO		
 			int "Speaker fault GPIO"
-			default -1 if !SQUEEZEAMP
 			default 2 if SQUEEZEAMP
+			default -1
 			help
 				GPIO to detect speaker fault condition. Set to -1 for no detection. 
 		config SPKFAULT_GPIO_LEVEL
@@ -290,18 +321,18 @@ menu "Squeezelite-ESP32"
 			default 0
 	endmenu	
 	menu "Battery measure"	
-		visible if !SQUEEZEAMP
+		visible if !SQUEEZEAMP && !TWATCH2020
 		config BAT_CHANNEL	
 			int "Set channel (0..7)"
-			default -1 if !SQUEEZEAMP
 			default 7 if SQUEEZEAMP
+			default -1 
 			help
 				Read a value every 10s on ADC1 on set Channel
 		config BAT_SCALE	
 			string "Set scaling factor"
 			depends on BAT_CHANNEL != -1
-			default "" if !SQUEEZEAMP
 			default "20.24" if SQUEEZEAMP
+			default "" 
 			help
 				Set the scaling factor for this 12 bits ADC
 	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");
 	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");
 	config_set_default(NVS_TYPE_STR, "bat_config", "", 0);