浏览代码

porting master changes

There is a divergence in accessors.c that I've not resolved
Philippe G 4 年之前
父节点
当前提交
0865496d76

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

@@ -2,6 +2,9 @@
 # Automatically generated file. DO NOT EDIT.
 # Automatically generated file. DO NOT EDIT.
 # Espressif IoT Development Framework (ESP-IDF) Project Configuration
 # Espressif IoT Development Framework (ESP-IDF) Project Configuration
 #
 #
+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"
 CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000
 CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000

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

@@ -2,6 +2,8 @@
 # Automatically generated file. DO NOT EDIT.
 # Automatically generated file. DO NOT EDIT.
 # Espressif IoT Development Framework (ESP-IDF) Project Configuration
 # Espressif IoT Development Framework (ESP-IDF) Project Configuration
 #
 #
+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"
 CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000
 CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000

+ 3 - 0
build-scripts/SqueezeAmp4MBFlash-sdkconfig.defaults

@@ -118,6 +118,9 @@ CONFIG_SPKFAULT_GPIO=2
 CONFIG_SPKFAULT_GPIO_LEVEL=0
 CONFIG_SPKFAULT_GPIO_LEVEL=0
 CONFIG_BAT_CHANNEL=7
 CONFIG_BAT_CHANNEL=7
 CONFIG_BAT_SCALE="20.24"
 CONFIG_BAT_SCALE="20.24"
+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:0"
+CONFIG_MUTE_GPIO_LEVEL=-1
 CONFIG_WIFI_MANAGER_TASK_PRIORITY=5
 CONFIG_WIFI_MANAGER_TASK_PRIORITY=5
 CONFIG_WIFI_MANAGER_MAX_RETRY=2
 CONFIG_WIFI_MANAGER_MAX_RETRY=2
 CONFIG_DEFAULT_AP_SSID="squeezelite"
 CONFIG_DEFAULT_AP_SSID="squeezelite"

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

@@ -117,6 +117,9 @@ CONFIG_SPKFAULT_GPIO=2
 CONFIG_SPKFAULT_GPIO_LEVEL=0
 CONFIG_SPKFAULT_GPIO_LEVEL=0
 CONFIG_BAT_CHANNEL=7
 CONFIG_BAT_CHANNEL=7
 CONFIG_BAT_SCALE="20.24"
 CONFIG_BAT_SCALE="20.24"
+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_WIFI_MANAGER_TASK_PRIORITY=5
 CONFIG_WIFI_MANAGER_TASK_PRIORITY=5
 CONFIG_WIFI_MANAGER_MAX_RETRY=2
 CONFIG_WIFI_MANAGER_MAX_RETRY=2
 CONFIG_DEFAULT_AP_SSID="squeezelite"
 CONFIG_DEFAULT_AP_SSID="squeezelite"

+ 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;

+ 7 - 5
components/display/SH1106.c

@@ -73,8 +73,11 @@ static void Update( struct GDS_Device* Device ) {
 #endif	
 #endif	
 }
 }
 
 
-static void SetHFlip( struct GDS_Device* Device, bool On ) { Device->WriteCommand( Device, On ? 0xA1 : 0xA0 ); }
-static void SetVFlip( struct GDS_Device *Device, bool On ) { Device->WriteCommand( Device, On ? 0xC8 : 0xC0 ); }
+static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) {
+	Device->WriteCommand( Device, HFlip ? 0xA1 : 0xA0 );
+	Device->WriteCommand( Device, VFlip ? 0xC8 : 0xC0 );
+}	
+
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 
 
@@ -117,8 +120,7 @@ static bool Init( struct GDS_Device* Device ) {
     Device->WriteCommand( Device, 0x40 + 0x00 );
     Device->WriteCommand( Device, 0x40 + 0x00 );
 	Device->SetContrast( Device, 0x7F );
 	Device->SetContrast( Device, 0x7F );
 	// set flip modes
 	// set flip modes
-	Device->SetVFlip( Device, false );
-	Device->SetHFlip( Device, false );
+	Device->SetLayout( Device, false, false, false );
 	// no Display Inversion
 	// no Display Inversion
     Device->WriteCommand( Device, 0xA6 );
     Device->WriteCommand( Device, 0xA6 );
 	// set Clocks
 	// set Clocks
@@ -135,7 +137,7 @@ static bool Init( struct GDS_Device* Device ) {
 
 
 static const struct GDS_Device SH1106 = {
 static const struct GDS_Device SH1106 = {
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
-	.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
+	.SetLayout = SetLayout,
 	.Update = Update, .Init = Init,
 	.Update = Update, .Init = Init,
 	.Depth = 1,
 	.Depth = 1,
 #if !defined SHADOW_BUFFER && defined USE_IRAM	
 #if !defined SHADOW_BUFFER && defined USE_IRAM	

+ 7 - 5
components/display/SSD1306.c

@@ -85,8 +85,11 @@ static void Update( struct GDS_Device* Device ) {
 #endif	
 #endif	
 }
 }
 
 
-static void SetHFlip( struct GDS_Device* Device, bool On ) { Device->WriteCommand( Device, On ? 0xA1 : 0xA0 ); }
-static void SetVFlip( struct GDS_Device *Device, bool On ) { Device->WriteCommand( Device, On ? 0xC8 : 0xC0 ); }
+static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { 
+	Device->WriteCommand( Device, HFlip ? 0xA1 : 0xA0 );
+	Device->WriteCommand( Device, VFlip ? 0xC8 : 0xC0 );
+}
+	
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 
 
@@ -129,8 +132,7 @@ static bool Init( struct GDS_Device* Device ) {
     Device->WriteCommand( Device, 0x40 + 0x00 );
     Device->WriteCommand( Device, 0x40 + 0x00 );
 	Device->SetContrast( Device, 0x7F );
 	Device->SetContrast( Device, 0x7F );
 	// set flip modes
 	// set flip modes
-	Device->SetVFlip( Device, false );
-	Device->SetHFlip( Device, false );
+	Device->SetLayout( Device, false, false, false);
 	// no Display Inversion
 	// no Display Inversion
     Device->WriteCommand( Device, 0xA6 );
     Device->WriteCommand( Device, 0xA6 );
 	// set Clocks
 	// set Clocks
@@ -150,7 +152,7 @@ static bool Init( struct GDS_Device* Device ) {
 
 
 static const struct GDS_Device SSD1306 = {
 static const struct GDS_Device SSD1306 = {
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
-	.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
+	.SetLayout = SetLayout,
 	.Update = Update, .Init = Init,
 	.Update = Update, .Init = Init,
 	.Mode = GDS_MONO, .Depth = 1,
 	.Mode = GDS_MONO, .Depth = 1,
 #if !defined SHADOW_BUFFER && defined USE_IRAM	
 #if !defined SHADOW_BUFFER && defined USE_IRAM	

+ 5 - 13
components/display/SSD1322.c

@@ -96,22 +96,15 @@ static void Update( struct GDS_Device* Device ) {
 #endif	
 #endif	
 }
 }
 
 
-static void SetHFlip( struct GDS_Device* Device, bool On ) { 
+static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { 
 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
-	Private->ReMap = On ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
+	Private->ReMap = HFlip ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
+	Private->ReMap = VFlip ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
 	Device->WriteCommand( Device, 0xA0 );
 	Device->WriteCommand( Device, 0xA0 );
 	Device->WriteData( Device, &Private->ReMap, 1 );
 	Device->WriteData( Device, &Private->ReMap, 1 );
 	WriteDataByte( Device, 0x11 );		
 	WriteDataByte( Device, 0x11 );		
 }	
 }	
 
 
-static void SetVFlip( struct GDS_Device *Device, bool On ) { 
-	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
-	Private->ReMap = On ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
-	Device->WriteCommand( Device, 0xA0 );
-	Device->WriteData( Device, &Private->ReMap, 1 );
-	WriteDataByte( Device, 0x11 );		
-}	
-	
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 
 
@@ -152,8 +145,7 @@ static bool Init( struct GDS_Device* Device ) {
 	
 	
 	// set flip modes
 	// set flip modes
 	Private->ReMap = 0;
 	Private->ReMap = 0;
-	Device->SetVFlip( Device, false );
-	Device->SetHFlip( Device, false );
+	Device->SetLayout( Device, false, false, false);
 	
 	
 	// set Clocks
 	// set Clocks
     Device->WriteCommand( Device, 0xB3 );
     Device->WriteCommand( Device, 0xB3 );
@@ -187,7 +179,7 @@ static bool Init( struct GDS_Device* Device ) {
 
 
 static const struct GDS_Device SSD1322 = {
 static const struct GDS_Device SSD1322 = {
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
-	.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
+	.SetLayout = SetLayout,
 	.Update = Update, .Init = Init,
 	.Update = Update, .Init = Init,
 	.Mode = GDS_GRAYSCALE, .Depth = 4,
 	.Mode = GDS_GRAYSCALE, .Depth = 4,
 };	
 };	

+ 10 - 14
components/display/SSD132x.c

@@ -222,22 +222,19 @@ static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, i
 	}
 	}
 }
 }
 
 
-static void SetHFlip( struct GDS_Device* Device, bool On ) { 
+static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { 
 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
-	if (Private->Model == SSD1326) Private->ReMap = On ? (Private->ReMap | ((1 << 0) | (1 << 2))) : (Private->ReMap & ~((1 << 0) | (1 << 2)));
-	else Private->ReMap = On ? (Private->ReMap | ((1 << 0) | (1 << 1))) : (Private->ReMap & ~((1 << 0) | (1 << 1)));
+	if (Private->Model == SSD1326) {
+		Private->ReMap = HFlip ? (Private->ReMap | ((1 << 0) | (1 << 2))) : (Private->ReMap & ~((1 << 0) | (1 << 2)));
+		Private->ReMap = HFlip ? (Private->ReMap | (1 << 1)) : (Private->ReMap & ~(1 << 1));		
+	} else {
+		Private->ReMap = VFlip ? (Private->ReMap | ((1 << 0) | (1 << 1))) : (Private->ReMap & ~((1 << 0) | (1 << 1)));
+		Private->ReMap = VFlip ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
+	}	
 	Device->WriteCommand( Device, 0xA0 );
 	Device->WriteCommand( Device, 0xA0 );
 	Device->WriteCommand( Device, Private->ReMap );
 	Device->WriteCommand( Device, Private->ReMap );
 }	
 }	
 
 
-static void SetVFlip( struct GDS_Device *Device, bool On ) { 
-	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
-	if (Private->Model == SSD1326) Private->ReMap = On ? (Private->ReMap | (1 << 1)) : (Private->ReMap & ~(1 << 1));
-	else Private->ReMap = On ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
-	Device->WriteCommand( Device, 0xA0 );
-	Device->WriteCommand( Device, Private->ReMap );
-}	
-	
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 
 
@@ -291,8 +288,7 @@ static bool Init( struct GDS_Device* Device ) {
 	Device->WriteCommand( Device, 0x00 );
 	Device->WriteCommand( Device, 0x00 );
 	Device->SetContrast( Device, 0x7F );
 	Device->SetContrast( Device, 0x7F );
 	// set flip modes
 	// set flip modes
-	Device->SetVFlip( Device, false );
-	Device->SetHFlip( Device, false );
+	Device->SetLayout( Device, false, false, false );
 	// no Display Inversion
 	// no Display Inversion
     Device->WriteCommand( Device, 0xA6 );
     Device->WriteCommand( Device, 0xA6 );
 	// set Clocks
 	// set Clocks
@@ -316,7 +312,7 @@ static bool Init( struct GDS_Device* Device ) {
 
 
 static const struct GDS_Device SSD132x = {
 static const struct GDS_Device SSD132x = {
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
-	.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
+	.SetLayout = SetLayout,
 	.Update = Update4, .Init = Init,
 	.Update = Update4, .Init = Init,
 	.Mode = GDS_GRAYSCALE, .Depth = 4,
 	.Mode = GDS_GRAYSCALE, .Depth = 4,
 };	
 };	

+ 14 - 18
components/display/SSD1351.c

@@ -164,34 +164,31 @@ static void Update24( struct GDS_Device* Device ) {
 #else
 #else
 	// always update by full lines
 	// always update by full lines
 	SetColumnAddress( Device, 0, Device->Width - 1);
 	SetColumnAddress( Device, 0, Device->Width - 1);
-	Device->WriteCommand(Device, ENABLE_WRITE);
 	
 	
-	for (int r = 0; r < Device->Height; r += Private->PageSize) {
-		SetRowAddress( Device, r, r + Private->PageSize - 1 );
+	for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
+		int Height = min(Private->PageSize, Device->Height - r);
+		
+		SetRowAddress( Device, r, r + Height - 1 );
+		Device->WriteCommand(Device, ENABLE_WRITE);
+		
 		if (Private->iRAM) {
 		if (Private->iRAM) {
-			memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
-			Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width * 3 );
+			memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
+			Device->WriteData( Device, Private->iRAM, Height * Device->Width * 3 );
 		} else	{
 		} else	{
-			Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
+			Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
 		}	
 		}	
 	}	
 	}	
 #endif	
 #endif	
 }
 }
 
 
-static void SetHFlip( struct GDS_Device* Device, bool On ) { 
+static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { 
 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
-	Private->ReMap = On ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
+	Private->ReMap = HFlip ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
+	Private->ReMap = VFlip ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
 	Device->WriteCommand( Device, 0xA0 );
 	Device->WriteCommand( Device, 0xA0 );
 	WriteByte( Device, Private->ReMap );
 	WriteByte( Device, Private->ReMap );
 }	
 }	
 
 
-static void SetVFlip( struct GDS_Device *Device, bool On ) { 
-	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
-	Private->ReMap = On ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
-	Device->WriteCommand( Device, 0xA0 );
-	WriteByte( Device, Private->ReMap );
-}	
-	
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
 
 
@@ -242,8 +239,7 @@ static bool Init( struct GDS_Device* Device ) {
 	
 	
 	// set flip modes & contrast
 	// set flip modes & contrast
 	Device->SetContrast( Device, 0x7F );
 	Device->SetContrast( Device, 0x7F );
-	Device->SetVFlip( Device, false );
-	Device->SetHFlip( Device, false );
+	Device->SetLayout( Device, false, false, false );
 	
 	
 	// set Adressing Mode Horizontal
 	// set Adressing Mode Horizontal
 	Private->ReMap |= (0 << 2);
 	Private->ReMap |= (0 << 2);
@@ -265,7 +261,7 @@ static bool Init( struct GDS_Device* Device ) {
 
 
 static const struct GDS_Device SSD1351 = {
 static const struct GDS_Device SSD1351 = {
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
-	.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
+	.SetLayout = SetLayout,
 	.Update = Update16, .Init = Init,
 	.Update = Update16, .Init = Init,
 	.Mode = GDS_RGB565, .Depth = 16,
 	.Mode = GDS_RGB565, .Depth = 16,
 };	
 };	

+ 50 - 33
components/display/ST77xx.c

@@ -29,6 +29,9 @@ enum { ST7735, ST7789 };
 
 
 struct PrivateSpace {
 struct PrivateSpace {
 	uint8_t *iRAM, *Shadowbuffer;
 	uint8_t *iRAM, *Shadowbuffer;
+	struct {
+		uint16_t Height, Width;
+	} Offset;
 	uint8_t MADCtl, PageSize;
 	uint8_t MADCtl, PageSize;
 	uint8_t Model;
 	uint8_t Model;
 };
 };
@@ -75,8 +78,8 @@ static void Update16( struct GDS_Device* Device ) {
 		
 		
 		FirstCol *= 2;
 		FirstCol *= 2;
 		LastCol = LastCol * 2 + 1;
 		LastCol = LastCol * 2 + 1;
-		SetRowAddress( Device, FirstRow, LastRow );
-		SetColumnAddress( Device, FirstCol, LastCol );
+		SetRowAddress( Device, FirstRow + Private->Offset.Height, LastRow + Private->Offset.Height);
+		SetColumnAddress( Device, FirstCol + Private->Offset.Width, LastCol + Private->Offset.Width );
 		Device->WriteCommand( Device, ENABLE_WRITE );
 		Device->WriteCommand( Device, ENABLE_WRITE );
 			
 			
 		int ChunkSize = (LastCol - FirstCol + 1) * 2;
 		int ChunkSize = (LastCol - FirstCol + 1) * 2;
@@ -100,12 +103,12 @@ static void Update16( struct GDS_Device* Device ) {
 	}	
 	}	
 #else
 #else
 	// always update by full lines
 	// always update by full lines
-	SetColumnAddress( Device, 0, Device->Width - 1);
+	SetColumnAddress( Device, Private->Offset.Width, Device->Width - 1);
 	
 	
 	for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
 	for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
 		int Height = min(Private->PageSize, Device->Height - r);
 		int Height = min(Private->PageSize, Device->Height - r);
 		
 		
-		SetRowAddress( Device, r, r + Height - 1 );
+		SetRowAddress( Device, Private->Offset.Height + r, Private->Offset.Height + r + Height - 1 );
 		Device->WriteCommand(Device, ENABLE_WRITE);
 		Device->WriteCommand(Device, ENABLE_WRITE);
 		
 		
 		if (Private->iRAM) {
 		if (Private->iRAM) {
@@ -142,8 +145,8 @@ static void Update24( struct GDS_Device* Device ) {
 		
 		
 		FirstCol = (FirstCol * 2) / 3;
 		FirstCol = (FirstCol * 2) / 3;
 		LastCol = (LastCol * 2 + 1) / 3; 
 		LastCol = (LastCol * 2 + 1) / 3; 
-		SetRowAddress( Device, FirstRow, LastRow );
-		SetColumnAddress( Device, FirstCol, LastCol );
+		SetRowAddress( Device, FirstRow + Private->Offset.Height, LastRow + Private->Offset.Height);
+		SetColumnAddress( Device, FirstCol + Private->Offset.Width, LastCol + Private->Offset.Width );
 		Device->WriteCommand( Device, ENABLE_WRITE );
 		Device->WriteCommand( Device, ENABLE_WRITE );
 			
 			
 		int ChunkSize = (LastCol - FirstCol + 1) * 3;
 		int ChunkSize = (LastCol - FirstCol + 1) * 3;
@@ -167,41 +170,55 @@ static void Update24( struct GDS_Device* Device ) {
 	}	
 	}	
 #else
 #else
 	// always update by full lines
 	// always update by full lines
-	SetColumnAddress( Device, 0, Device->Width - 1);
-	Device->WriteCommand(Device, ENABLE_WRITE);
+	SetColumnAddress( Device, Private->Offset.Width, Device->Width - 1);
 	
 	
-	for (int r = 0; r < Device->Height; r += Private->PageSize) {
-		SetRowAddress( Device, r, r + Private->PageSize - 1 );
+	for (int r = 0; r < Device->Height; r += min(Private->PageSize, Device->Height - r)) {
+		int Height = min(Private->PageSize, Device->Height - r);
+		
+		SetRowAddress( Device, Private->Offset.Height + r, Private->Offset.Height + r + Height - 1 );
+		Device->WriteCommand(Device, ENABLE_WRITE);
+		
 		if (Private->iRAM) {
 		if (Private->iRAM) {
-			memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
-			Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width * 3 );
+			memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
+			Device->WriteData( Device, Private->iRAM, Height * Device->Width * 3 );
 		} else	{
 		} else	{
-			Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Private->PageSize * Device->Width * 3 );
+			Device->WriteData( Device, Device->Framebuffer + r * Device->Width * 3, Height * Device->Width * 3 );
 		}	
 		}	
 	}	
 	}	
 #endif	
 #endif	
 }
 }
 
 
-static void SetHFlip( struct GDS_Device* Device, bool On ) { 
+static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { 
 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
-	Private->MADCtl = On ? (Private->MADCtl & ~(1 << 7)) : (Private->MADCtl | (1 << 7));
+	
+	Private->MADCtl = HFlip ? (Private->MADCtl | (1 << 7)) : (Private->MADCtl & ~(1 << 7));
+	Private->MADCtl = VFlip ? (Private->MADCtl | (1 << 6)) : (Private->MADCtl & ~(1 << 6));
+	Private->MADCtl = Rotate ? (Private->MADCtl | (1 << 5)) : (Private->MADCtl & ~(1 << 5));
+	
 	Device->WriteCommand( Device, 0x36 );
 	Device->WriteCommand( Device, 0x36 );
 	WriteByte( Device, Private->MADCtl );
 	WriteByte( Device, Private->MADCtl );
-}	
+	
+	if (Private->Model == ST7789) {
+		if (Rotate) Private->Offset.Width = HFlip ? 320 - Device->Width : 0;
+		else Private->Offset.Height = HFlip ? 320 - Device->Height : 0;
+	}
 
 
-static void SetVFlip( struct GDS_Device *Device, bool On ) { 
-	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
-	Private->MADCtl = On ? (Private->MADCtl | (1 << 6)) : (Private->MADCtl & ~(1 << 6));
-	Device->WriteCommand( Device, 0x36 );
-	WriteByte( Device, Private->MADCtl );
+#ifdef SHADOW_BUFFER
+	// force a full refresh (almost ...)
+	memset(Private->Shadowbuffer, 0xAA, Device->FramebufferSize);
+#endif	
 }	
 }	
-	
+
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x29 ); }
 static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x29 ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x28 ); }
 static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0x28 ); }
 
 
 static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
 static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
 	Device->WriteCommand( Device, 0x51 );
 	Device->WriteCommand( Device, 0x51 );
 	WriteByte( Device, Contrast );
 	WriteByte( Device, Contrast );
+	
+	Device->SetContrast = NULL;
+	GDS_SetContrast( Device, Contrast );
+	Device->SetContrast = SetContrast;
 }
 }
 
 
 static bool Init( struct GDS_Device* Device ) {
 static bool Init( struct GDS_Device* Device ) {
@@ -209,7 +226,7 @@ static bool Init( struct GDS_Device* Device ) {
 	int Depth = (Device->Depth + 8 - 1) / 8;
 	int Depth = (Device->Depth + 8 - 1) / 8;
 	
 	
 	Private->PageSize = min(8, PAGE_BLOCK / (Device->Width * Depth));
 	Private->PageSize = min(8, PAGE_BLOCK / (Device->Width * Depth));
-	
+
 #ifdef SHADOW_BUFFER	
 #ifdef SHADOW_BUFFER	
 	Private->Shadowbuffer = malloc( Device->FramebufferSize );	
 	Private->Shadowbuffer = malloc( Device->FramebufferSize );	
 	memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
 	memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
@@ -224,21 +241,21 @@ static bool Init( struct GDS_Device* Device ) {
 	Device->WriteCommand( Device, 0x11 );
 	Device->WriteCommand( Device, 0x11 );
 		
 		
 	// need BGR & Address Mode
 	// need BGR & Address Mode
-	Private->MADCtl = (1 << 3) | ((Device->Width > Device->Height ? 1 : 0) << 5);
+	Private->MADCtl = 1 << 3;
 	Device->WriteCommand( Device, 0x36 );
 	Device->WriteCommand( Device, 0x36 );
 	WriteByte( Device, Private->MADCtl );		
 	WriteByte( Device, Private->MADCtl );		
 		
 		
 	// set flip modes & contrast
 	// set flip modes & contrast
-	GDS_SetContrast( Device, 0x7F );
-	Device->SetVFlip( Device, false );
-	Device->SetHFlip( Device, false );
+	GDS_SetContrast( Device, 0x7f );
+	Device->SetLayout( Device, false, false, false );
 	
 	
 	// set screen depth (16/18)
 	// set screen depth (16/18)
 	Device->WriteCommand( Device, 0x3A );
 	Device->WriteCommand( Device, 0x3A );
-	WriteByte( Device, Device->Depth == 24 ? 0x06 : 0x05 );
+	if (Private->Model == ST7789) WriteByte( Device, Device->Depth == 24 ? 0x066 : 0x55 );
+	else WriteByte( Device, Device->Depth == 24 ? 0x06 : 0x05 );
 	
 	
 	// no Display Inversion
 	// no Display Inversion
-    Device->WriteCommand( Device, 0x20 );	
+    Device->WriteCommand( Device, Private->Model == ST7735 ? 0x20 : 0x21 );	
 		
 		
 	// gone with the wind
 	// gone with the wind
 	Device->DisplayOn( Device );
 	Device->DisplayOn( Device );
@@ -249,15 +266,15 @@ static bool Init( struct GDS_Device* Device ) {
 
 
 static const struct GDS_Device ST77xx = {
 static const struct GDS_Device ST77xx = {
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff,
 	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff,
-	.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
+	.SetLayout = SetLayout,
 	.Update = Update16, .Init = Init,
 	.Update = Update16, .Init = Init,
 	.Mode = GDS_RGB565, .Depth = 16,
 	.Mode = GDS_RGB565, .Depth = 16,
-};	
+};		
 
 
 struct GDS_Device* ST77xx_Detect(char *Driver, struct GDS_Device* Device) {
 struct GDS_Device* ST77xx_Detect(char *Driver, struct GDS_Device* Device) {
 	uint8_t Model;
 	uint8_t Model;
 	int Depth;
 	int Depth;
-	
+		
 	if (strcasestr(Driver, "ST7735")) Model = ST7735;
 	if (strcasestr(Driver, "ST7735")) Model = ST7735;
 	else if (strcasestr(Driver, "ST7789")) Model = ST7789;
 	else if (strcasestr(Driver, "ST7789")) Model = ST7789;
 	else return NULL;
 	else return NULL;
@@ -275,6 +292,6 @@ struct GDS_Device* ST77xx_Detect(char *Driver, struct GDS_Device* Device) {
 	} 	
 	} 	
 	
 	
 	if (Model == ST7789) Device->SetContrast = SetContrast;
 	if (Model == ST7789) Device->SetContrast = SetContrast;
-	
+
 	return Device;
 	return Device;
 }
 }

+ 1 - 2
components/display/core/gds.c

@@ -236,8 +236,7 @@ void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
 	}
 	}
 }
 }
 	
 	
-void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { if (Device->SetHFlip) Device->SetHFlip( Device, On ); }
-void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { if (Device->SetVFlip) Device->SetVFlip( Device, On ); }
+void GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { if (Device->SetLayout) Device->SetLayout( Device, HFlip, VFlip, Rotate ); }
 void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
 void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
 int	GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
 int	GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
 int	GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }
 int	GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }

+ 1 - 2
components/display/core/gds.h

@@ -35,8 +35,7 @@ void 	GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast );
 void 	GDS_DisplayOn( struct GDS_Device* Device );
 void 	GDS_DisplayOn( struct GDS_Device* Device );
 void 	GDS_DisplayOff( struct GDS_Device* Device ); 
 void 	GDS_DisplayOff( struct GDS_Device* Device ); 
 void 	GDS_Update( struct GDS_Device* Device );
 void 	GDS_Update( struct GDS_Device* Device );
-void 	GDS_SetHFlip( struct GDS_Device* Device, bool On );
-void 	GDS_SetVFlip( struct GDS_Device* Device, bool On );
+void 	GDS_SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate );
 void 	GDS_SetDirty( struct GDS_Device* Device );
 void 	GDS_SetDirty( struct GDS_Device* Device );
 int 	GDS_GetWidth( struct GDS_Device* Device );
 int 	GDS_GetWidth( struct GDS_Device* Device );
 int 	GDS_GetHeight( struct GDS_Device* Device );
 int 	GDS_GetHeight( struct GDS_Device* Device );

+ 4 - 5
components/display/core/gds_private.h

@@ -101,7 +101,7 @@ struct GDS_Device {
 	
 	
 	uint8_t	Alloc;	
 	uint8_t	Alloc;	
 	uint8_t* Framebuffer;
 	uint8_t* Framebuffer;
-    uint16_t FramebufferSize;
+    uint32_t FramebufferSize;
 	bool Dirty;
 	bool Dirty;
 
 
 	// default fonts when using direct draw	
 	// default fonts when using direct draw	
@@ -117,8 +117,7 @@ struct GDS_Device {
 	void (*SetContrast)( struct GDS_Device* Device, uint8_t Contrast );
 	void (*SetContrast)( struct GDS_Device* Device, uint8_t Contrast );
 	void (*DisplayOn)( struct GDS_Device* Device );
 	void (*DisplayOn)( struct GDS_Device* Device );
 	void (*DisplayOff)( struct GDS_Device* Device );
 	void (*DisplayOff)( struct GDS_Device* Device );
-	void (*SetHFlip)( struct GDS_Device* Device, bool On );
-	void (*SetVFlip)( struct GDS_Device* Device, bool On );
+	void (*SetLayout)( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate );
 	// must provide for depth other than 1 (vertical) and 4 (may provide for optimization)
 	// must provide for depth other than 1 (vertical) and 4 (may provide for optimization)
 	void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color );
 	void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color );
 	void (*DrawBitmapCBR)(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color );
 	void (*DrawBitmapCBR)(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color );
@@ -130,8 +129,8 @@ struct GDS_Device {
     WriteCommandProc WriteCommand;
     WriteCommandProc WriteCommand;
     WriteDataProc WriteData;
     WriteDataProc WriteData;
 
 
-	// 16 bytes for whatever the driver wants (should be aligned as it's 32 bits)	
-	uint32_t Private[4];
+	// 32 bytes for whatever the driver wants (should be aligned as it's 32 bits)	
+	uint32_t Private[8];
 };
 };
 
 
 bool GDS_Reset( struct GDS_Device* Device );
 bool GDS_Reset( struct GDS_Device* Device );

+ 4 - 10
components/display/display.c

@@ -59,20 +59,15 @@ static const char *known_drivers[] = {"SH1106",
 static void displayer_task(void *args);
 static void displayer_task(void *args);
 
 
 struct GDS_Device *display;   
 struct GDS_Device *display;   
-extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect;
-GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, NULL };
+extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect;
+GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, NULL };
 
 
 /****************************************************************************************
 /****************************************************************************************
  * 
  * 
  */
  */
 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");
@@ -130,8 +125,7 @@ void display_init(char *welcome) {
 		static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
 		static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
 		static EXT_RAM_ATTR StackType_t xStack[DISPLAYER_STACK_SIZE] __attribute__ ((aligned (4)));
 		static EXT_RAM_ATTR StackType_t xStack[DISPLAYER_STACK_SIZE] __attribute__ ((aligned (4)));
 		
 		
-		GDS_SetHFlip(display, strcasestr(config, "HFlip") ? true : false);
-		GDS_SetVFlip(display, strcasestr(config, "VFlip") ? true : false);
+		GDS_SetLayout( display, strcasestr(config, "HFlip"), strcasestr(config, "VFlip"), strcasestr(config, "rotate"));
 		GDS_SetFont(display, &Font_droid_sans_fallback_15x17 );
 		GDS_SetFont(display, &Font_droid_sans_fallback_15x17 );
 		GDS_TextPos(display, GDS_FONT_MEDIUM, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, welcome);
 		GDS_TextPos(display, GDS_FONT_MEDIUM, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, welcome);
 
 

+ 1 - 1
components/services/accessors.c

@@ -115,7 +115,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);

+ 2 - 2
components/services/led.c

@@ -168,7 +168,7 @@ int led_allocate(void) {
  */
  */
 bool led_config(int idx, gpio_num_t gpio, int onstate, int pwm) {
 bool led_config(int idx, gpio_num_t gpio, int onstate, int pwm) {
 	if (gpio < 0) {
 	if (gpio < 0) {
-		ESP_LOGW(TAG,"LED GPIO not configured");
+		ESP_LOGW(TAG,"LED GPIO -1 ignored");
 		return false;
 		return false;
 	}
 	}
 	
 	
@@ -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) {

+ 16 - 21
components/squeezelite/a1s/ac101.c → components/squeezelite/ac101/ac101.c

@@ -33,7 +33,7 @@
 #include "adac.h"
 #include "adac.h"
 #include "ac101.h"
 #include "ac101.h"
 
 
-const static char TAG[] = "AC101";
+static const char TAG[] = "AC101";
 
 
 #define SPKOUT_EN ((1 << 9) | (1 << 11) | (1 << 7) | (1 << 5))
 #define SPKOUT_EN ((1 << 9) | (1 << 11) | (1 << 7) | (1 << 5))
 #define EAROUT_EN ((1 << 11) | (1 << 12) | (1 << 13))
 #define EAROUT_EN ((1 << 11) | (1 << 12) | (1 << 13))
@@ -48,14 +48,14 @@ const static char TAG[] = "AC101";
         return b;\
         return b;\
     }
     }
 	
 	
-static bool init(int i2c_port_num, int i2s_num, i2s_config_t *config);
+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);
 
 
-struct adac_s dac_a1s = { init, deinit, power, speaker, headset, volume };
+const struct adac_s dac_ac101 = { "AC101", init, deinit, power, speaker, headset, volume };
 
 
 static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val);
 static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val);
 static uint16_t i2c_read_reg(uint8_t reg);
 static uint16_t i2c_read_reg(uint8_t reg);
@@ -70,21 +70,24 @@ static int i2c_port;
 /****************************************************************************************
 /****************************************************************************************
  * init
  * init
  */
  */
-static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config) {	 
+static bool init(char *config, int i2c_port_num) {	 
 	esp_err_t res = ESP_OK;
 	esp_err_t res = ESP_OK;
+	char *p;
 	
 	
-	i2c_port = i2c_port_num;
-
 	// configure i2c
 	// configure i2c
 	i2c_config_t i2c_config = {
 	i2c_config_t i2c_config = {
 			.mode = I2C_MODE_MASTER,
 			.mode = I2C_MODE_MASTER,
-			.sda_io_num = 33,
+			.sda_io_num = -1,
 			.sda_pullup_en = GPIO_PULLUP_ENABLE,
 			.sda_pullup_en = GPIO_PULLUP_ENABLE,
-			.scl_io_num = 32,
+			.scl_io_num = -1,
 			.scl_pullup_en = GPIO_PULLUP_ENABLE,
 			.scl_pullup_en = GPIO_PULLUP_ENABLE,
 			.master.clk_speed = 250000,
 			.master.clk_speed = 250000,
 		};
 		};
-		
+	
+	if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
+	if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
+	
+	i2c_port = i2c_port_num;
 	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);
 	
 	
@@ -96,8 +99,6 @@ static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config) {
 		return 0;		
 		return 0;		
 	}
 	}
 	
 	
-	ESP_LOGI(TAG, "AC101 DAC using I2C sda:%u, scl:%u", i2c_config.sda_io_num, i2c_config.scl_io_num);
-	
 	res = i2c_write_reg(CHIP_AUDIO_RS, 0x123);
 	res = i2c_write_reg(CHIP_AUDIO_RS, 0x123);
 	// huh?
 	// huh?
 	vTaskDelay(100 / portTICK_PERIOD_MS); 
 	vTaskDelay(100 / portTICK_PERIOD_MS); 
@@ -140,13 +141,6 @@ static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config) {
 	i2c_write_reg(OMIXER_SR, 		BIN(0000,0101,0000,1010));	// source=DAC(R/L) and LINEIN(R/L)
 	i2c_write_reg(OMIXER_SR, 		BIN(0000,0101,0000,1010));	// source=DAC(R/L) and LINEIN(R/L)
 #endif	
 #endif	
 	
 	
-	// configure I2S pins & install driver	
-	i2s_pin_config_t i2s_pin_config = (i2s_pin_config_t) { 	.bck_io_num = CONFIG_I2S_BCK_IO, .ws_io_num = CONFIG_I2S_WS_IO, 
-															.data_out_num = CONFIG_I2S_DO_IO, .data_in_num = CONFIG_I2S_DI_IO
-								};
-	res |= i2s_driver_install(i2s_num, i2s_config, 0, NULL);
-	res |= i2s_set_pin(i2s_num, &i2s_pin_config);
-	
 	// enable earphone & speaker
 	// enable earphone & speaker
 	i2c_write_reg(SPKOUT_CTRL, 0x0220);
 	i2c_write_reg(SPKOUT_CTRL, 0x0220);
 	i2c_write_reg(HPOUT_CTRL, 0xf801);
 	i2c_write_reg(HPOUT_CTRL, 0xf801);
@@ -155,7 +149,7 @@ static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config) {
 	ac101_set_spk_volume(100);
 	ac101_set_spk_volume(100);
 	ac101_set_earph_volume(100);
 	ac101_set_earph_volume(100);
 	
 	
-	ESP_LOGI(TAG, "DAC using I2S bck:%d, ws:%d, do:%d", i2s_pin_config.bck_io_num, i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num);
+	ESP_LOGI(TAG, "AC101 uses I2C sda:%d, scl:%d", i2c_config.sda_io_num, i2c_config.scl_io_num);
 
 
 	return (res == ESP_OK);
 	return (res == ESP_OK);
 }	
 }	
@@ -170,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;
 } 
 } 
 
 
 /****************************************************************************************
 /****************************************************************************************

+ 0 - 0
components/squeezelite/a1s/ac101.h → components/squeezelite/ac101/ac101.h


+ 6 - 5
components/squeezelite/adac.h

@@ -15,14 +15,15 @@
 typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
 typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
 
 
 struct adac_s {
 struct adac_s {
-	bool (*init)(int i2c_port_num, int i2s_num, i2s_config_t *config);
+	char *model;
+	bool (*init)(char *config, int i2c_port_num);
 	void (*deinit)(void);
 	void (*deinit)(void);
 	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 struct adac_s dac_tas57xx;
-extern struct adac_s dac_a1s;
-extern struct adac_s dac_external;
+extern const struct adac_s dac_tas57xx;
+extern const struct adac_s dac_ac101;
+extern const struct adac_s dac_external;

+ 2 - 2
components/squeezelite/component.mk

@@ -20,7 +20,7 @@ CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ON
 
 
 #	-I$(COMPONENT_PATH)/../codecs/inc/faad2
 #	-I$(COMPONENT_PATH)/../codecs/inc/faad2
 
 
-COMPONENT_SRCDIRS := . tas57xx a1s external
-COMPONENT_ADD_INCLUDEDIRS := . ./tas57xx ./a1s
+COMPONENT_SRCDIRS := . tas57xx ac101 external
+COMPONENT_ADD_INCLUDEDIRS := . ./tas57xx ./ac101
 COMPONENT_EMBED_FILES := vu.data
 COMPONENT_EMBED_FILES := vu.data
 
 

+ 0 - 1
components/squeezelite/decode_external.c

@@ -28,7 +28,6 @@ extern struct buffer *outputbuf;
 // this is the only system-wide loglevel variable
 // this is the only system-wide loglevel variable
 extern log_level loglevel;
 extern log_level loglevel;
 
 
-
 static bool enable_bt_sink;
 static bool enable_bt_sink;
 static bool enable_airplay;
 static bool enable_airplay;
 
 

+ 2 - 1
components/squeezelite/display.c

@@ -573,8 +573,9 @@ void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x,
 	
 	
 	// adjust to current display window
 	// adjust to current display window
 	if (width > VU_WIDTH) {
 	if (width > VU_WIDTH) {
+		if (rotate) y += (width - VU_WIDTH) / 2;		
+		else x += (width - VU_WIDTH) / 2;		
 		width = VU_WIDTH;
 		width = VU_WIDTH;
-		x += (width - VU_WIDTH) / 2;
 	} else {
 	} else {
 		data += (VU_WIDTH - width) / 2 * VU_HEIGHT;	
 		data += (VU_WIDTH - width) / 2 * VU_HEIGHT;	
 	}	
 	}	

+ 145 - 28
components/squeezelite/external/dac_external.c

@@ -12,46 +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 "platform_config.h"
 #include "platform_config.h"
 #include "adac.h"
 #include "adac.h"
 
 
-static bool init(int i2c_port_num, int i2s_num, i2s_config_t *config);
-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 const char TAG[] = "DAC external";
 
 
-struct adac_s dac_external = { init, deinit, power, speaker, headset, volume };
+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 char TAG[] = "DAC external";
+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);
 
 
-static bool init(int i2c_port_num, int i2s_num, i2s_config_t *config) { 
-	i2s_pin_config_t i2s_pin_config = (i2s_pin_config_t) { 	.bck_io_num = CONFIG_I2S_BCK_IO, .ws_io_num = CONFIG_I2S_WS_IO, 
-															.data_out_num = CONFIG_I2S_DO_IO, .data_in_num = CONFIG_I2S_DI_IO };
-	char *nvs_item = config_alloc_get(NVS_TYPE_STR, "dac_config");
-	
-	if (nvs_item) {
-		char *p;
-		if ((p = strcasestr(nvs_item, "bck")) != NULL) i2s_pin_config.bck_io_num = atoi(strchr(p, '=') + 1);
-		if ((p = strcasestr(nvs_item, "ws")) != NULL) i2s_pin_config.ws_io_num = atoi(strchr(p, '=') + 1);
-		if ((p = strcasestr(nvs_item, "do")) != NULL) i2s_pin_config.data_out_num = atoi(strchr(p, '=') + 1);
-		free(nvs_item);
-	} 
+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;
 	
 	
-	if (i2s_pin_config.bck_io_num != -1 && i2s_pin_config.ws_io_num != -1 && i2s_pin_config.data_out_num != -1) {
-		i2s_driver_install(i2s_num, config, 0, NULL);
-		i2s_set_pin(i2s_num, &i2s_pin_config);
+	// 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,
+		};
 
 
-		ESP_LOGI(TAG, "External DAC using I2S bck:%u, ws:%u, do:%u", i2s_pin_config.bck_io_num, i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num);
+	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;
 		return true;
-	} else {
-		ESP_LOGI(TAG, "Cannot initialize I2S for DAC bck:%d ws:%d do:%d", i2s_pin_config.bck_io_num, 
-																		   i2s_pin_config.ws_io_num, 
-																		   i2s_pin_config.data_out_num);
+	}	
+	
+	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 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");
+}
+
+/****************************************************************************************
+ * 
+ */
+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;
+}
+
+

+ 83 - 43
components/squeezelite/output_i2s.c

@@ -77,8 +77,8 @@ extern struct buffer *streambuf;
 extern struct buffer *outputbuf;
 extern struct buffer *outputbuf;
 extern u8_t *silencebuf;
 extern u8_t *silencebuf;
 
 
-// by default no DAC selected
-struct adac_s *adac = &dac_external;
+const struct adac_s *dac_set[] = { &dac_tas57xx, &dac_ac101, NULL };
+const struct adac_s *adac = &dac_external;
 
 
 static log_level loglevel;
 static log_level loglevel;
 
 
@@ -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);
 	}	
 	}	
 }	
 }	
 
 
@@ -146,7 +152,8 @@ 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;
 	char *p;
-
+	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);
 	jack_mutes_amp = (strcmp(p,"1") == 0 ||strcasecmp(p,"y") == 0);
 	jack_mutes_amp = (strcmp(p,"1") == 0 ||strcasecmp(p,"y") == 0);
 	free(p);
 	free(p);
@@ -182,7 +189,14 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 	}
 	}
 		
 		
 	running = true;
 	running = true;
+	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
+	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
 	// common I2S initialization
 	i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX;
 	i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX;
 	i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
 	i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
@@ -191,21 +205,13 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 	i2s_config.tx_desc_auto_clear = true;		
 	i2s_config.tx_desc_auto_clear = true;		
 	i2s_config.use_apll = true;
 	i2s_config.use_apll = true;
 	i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //Interrupt level 1
 	i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //Interrupt level 1
-
+	
 	if (strcasestr(device, "spdif")) {
 	if (strcasestr(device, "spdif")) {
 		spdif = true;	
 		spdif = true;	
-		i2s_pin_config_t i2s_pin_config = (i2s_pin_config_t) { .bck_io_num = CONFIG_SPDIF_BCK_IO, .ws_io_num = CONFIG_SPDIF_WS_IO, 
-															  .data_out_num = CONFIG_SPDIF_DO_IO, .data_in_num = -1 };
-#ifndef CONFIG_SPDIF_LOCKED															  
-		char *nvs_item = config_alloc_get(NVS_TYPE_STR, "spdif_config");
-		if (nvs_item) {
-			if ((p = strcasestr(nvs_item, "bck")) != NULL) i2s_pin_config.bck_io_num = atoi(strchr(p, '=') + 1);
-			if ((p = strcasestr(nvs_item, "ws")) != NULL) i2s_pin_config.ws_io_num = atoi(strchr(p, '=') + 1);
-			if ((p = strcasestr(nvs_item, "do")) != NULL) i2s_pin_config.data_out_num = atoi(strchr(p, '=') + 1);
-			free(nvs_item);
-		} 
-#endif		
-		
+
+		if ((p = strcasestr(spdif_config, "bck")) != NULL) i2s_pin_config.bck_io_num = atoi(strchr(p, '=') + 1);
+		if ((p = strcasestr(spdif_config, "ws")) != NULL) i2s_pin_config.ws_io_num = atoi(strchr(p, '=') + 1);
+			
 		if (i2s_pin_config.bck_io_num == -1 || i2s_pin_config.ws_io_num == -1 || i2s_pin_config.data_out_num == -1) {
 		if (i2s_pin_config.bck_io_num == -1 || i2s_pin_config.ws_io_num == -1 || i2s_pin_config.data_out_num == -1) {
 			LOG_WARN("Cannot initialize I2S for SPDIF bck:%d ws:%d do:%d", i2s_pin_config.bck_io_num, 
 			LOG_WARN("Cannot initialize I2S for SPDIF bck:%d ws:%d do:%d", i2s_pin_config.bck_io_num, 
 																		   i2s_pin_config.ws_io_num, 
 																		   i2s_pin_config.ws_io_num, 
@@ -223,30 +229,64 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 		   audio frame. So the real depth is true frames is (LEN * COUNT / 2)
 		   audio frame. So the real depth is true frames is (LEN * COUNT / 2)
 		*/   
 		*/   
 		dma_buf_frames = DMA_BUF_COUNT * DMA_BUF_LEN / 2;	
 		dma_buf_frames = DMA_BUF_COUNT * DMA_BUF_LEN / 2;	
-		i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
-		i2s_set_pin(CONFIG_I2S_NUM, &i2s_pin_config);
+		res = i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
+		res |= i2s_set_pin(CONFIG_I2S_NUM, &i2s_pin_config);
 		LOG_INFO("SPDIF using I2S bck:%u, ws:%u, do:%u", i2s_pin_config.bck_io_num, i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num);
 		LOG_INFO("SPDIF using I2S bck:%u, ws:%u, do:%u", i2s_pin_config.bck_io_num, i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num);
 	} else {
 	} else {
-#if CONFIG_SPDIF_DO_IO != -1
-		gpio_pad_select_gpio(CONFIG_SPDIF_DO_IO);
-		gpio_set_direction(CONFIG_SPDIF_DO_IO, GPIO_MODE_OUTPUT);
-		gpio_set_level(CONFIG_SPDIF_DO_IO, 0);
-#endif
-
+		// turn off SPDIF if configured
+		if (i2s_pin_config.data_out_num >= 0) {
+			gpio_pad_select_gpio(i2s_pin_config.data_out_num);
+			gpio_set_direction(i2s_pin_config.data_out_num, GPIO_MODE_OUTPUT);
+			gpio_set_level(i2s_pin_config.data_out_num, 0);
+		}	
+		
+		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);
+		
+		for (int i = 0; adac == &dac_external && dac_set[i]; i++) if (strcasestr(dac_set[i]->model, model)) adac = dac_set[i];
+		res = adac->init(dac_config, I2C_PORT) ? ESP_OK : ESP_FAIL;
+		
+		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;
 		i2s_config.sample_rate = output.current_sample_rate;
 		i2s_config.bits_per_sample = bytes_per_frame * 8 / 2;
 		i2s_config.bits_per_sample = bytes_per_frame * 8 / 2;
 		// Counted in frames (but i2s allocates a buffer <= 4092 bytes)
 		// Counted in frames (but i2s allocates a buffer <= 4092 bytes)
 		i2s_config.dma_buf_len = DMA_BUF_LEN;	
 		i2s_config.dma_buf_len = DMA_BUF_LEN;	
 		i2s_config.dma_buf_count = DMA_BUF_COUNT;
 		i2s_config.dma_buf_count = DMA_BUF_COUNT;
-		dma_buf_frames = DMA_BUF_COUNT * DMA_BUF_LEN;	
+		dma_buf_frames = DMA_BUF_COUNT * DMA_BUF_LEN;			
 		
 		
-		// finally let DAC driver initialize I2C and I2S
-		if (dac_tas57xx.init(I2C_PORT, CONFIG_I2S_NUM, &i2s_config)) adac = &dac_tas57xx;
-		else if (dac_a1s.init(I2C_PORT, CONFIG_I2S_NUM, &i2s_config)) adac = &dac_a1s;
-		else if (!dac_external.init(I2C_PORT, CONFIG_I2S_NUM, &i2s_config)) {
-			LOG_WARN("DAC not configured and SPDIF not enabled, I2S will not continue");
-			return;
-		}
+		res |= i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
+		res |= i2s_set_pin(CONFIG_I2S_NUM, &i2s_pin_config);
+		
+		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);
+	
+	if (res != ESP_OK) {
+		LOG_WARN("no DAC configured");
+		return;
 	}	
 	}	
 
 
 	LOG_INFO("Initializing I2S mode %s with rate: %d, bits per sample: %d, buffer frames: %d, number of buffers: %d ", 
 	LOG_INFO("Initializing I2S mode %s with rate: %d, bits per sample: %d, buffer frames: %d, number of buffers: %d ", 
@@ -314,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);
 } 
 } 
 
 
 /****************************************************************************************
 /****************************************************************************************
@@ -396,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);
@@ -460,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)

+ 34 - 48
components/squeezelite/tas57xx/dac_57xx.c

@@ -9,28 +9,28 @@
  *
  *
  */
  */
  
  
-#include "squeezelite.h" 
+#include <string.h>
 #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 "driver/i2c.h"
 #include "driver/gpio.h"
 #include "driver/gpio.h"
+#include "esp_log.h"
 #include "adac.h"
 #include "adac.h"
 
 
-// this is the only hard-wired thing
-#define VOLUME_GPIO	14	
-
 #define TAS575x 0x98
 #define TAS575x 0x98
 #define TAS578x	0x90
 #define TAS578x	0x90
 
 
-static bool init(int i2c_port_num, int i2s_num, i2s_config_t *config);
+static const char TAG[] = "TAS575x/8x";
+
+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);
 
 
-struct adac_s dac_tas57xx = { init, deinit, power, speaker, headset, volume };
+const struct adac_s dac_tas57xx = { "TAS57xx", init, deinit, power, speaker, headset, volume };
 
 
 struct tas57xx_cmd_s {
 struct tas57xx_cmd_s {
 	uint8_t reg;
 	uint8_t reg;
@@ -59,8 +59,7 @@ static const struct tas57xx_cmd_s tas57xx_cmd[] = {
 	{ 0x56, 0x00 },	// TAS57_ANALOGUE_ON
 	{ 0x56, 0x00 },	// TAS57_ANALOGUE_ON
 };
 };
 
 
-static log_level loglevel = lINFO;
-static u8_t tas57_addr;
+static uint8_t tas57_addr;
 static int i2c_port;
 static int i2c_port;
 
 
 static void dac_cmd(dac_cmd_e cmd, ...);
 static void dac_cmd(dac_cmd_e cmd, ...);
@@ -69,19 +68,23 @@ static int tas57_detect(void);
 /****************************************************************************************
 /****************************************************************************************
  * init
  * init
  */
  */
-static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)	{	 
+static bool init(char *config, int i2c_port_num)	{	 
 	i2c_port = i2c_port_num;
 	i2c_port = i2c_port_num;
-		
+	char *p;	
+	
 	// configure i2c
 	// configure i2c
 	i2c_config_t i2c_config = {
 	i2c_config_t i2c_config = {
 			.mode = I2C_MODE_MASTER,
 			.mode = I2C_MODE_MASTER,
-			.sda_io_num = 27,
+			.sda_io_num = -1,
 			.sda_pullup_en = GPIO_PULLUP_ENABLE,
 			.sda_pullup_en = GPIO_PULLUP_ENABLE,
-			.scl_io_num = 26,
+			.scl_io_num = -1,
 			.scl_pullup_en = GPIO_PULLUP_ENABLE,
 			.scl_pullup_en = GPIO_PULLUP_ENABLE,
-			.master.clk_speed = 100000,
+			.master.clk_speed = 250000,
 		};
 		};
-		
+
+	if ((p = strcasestr(config, "sda")) != NULL) i2c_config.sda_io_num = atoi(strchr(p, '=') + 1);
+	if ((p = strcasestr(config, "scl")) != NULL) i2c_config.scl_io_num = atoi(strchr(p, '=') + 1);
+	
 	i2c_param_config(i2c_port, &i2c_config);
 	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);
 		
 		
@@ -89,13 +92,11 @@ static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)	{
 	tas57_addr = tas57_detect();
 	tas57_addr = tas57_detect();
 	
 	
 	if (!tas57_addr) {
 	if (!tas57_addr) {
-		LOG_WARN("No TAS57xx detected");
+		ESP_LOGW(TAG, "No TAS57xx detected");
 		i2c_driver_delete(i2c_port);
 		i2c_driver_delete(i2c_port);
-		return 0;
+		return false;
 	}
 	}
-	
-	LOG_INFO("TAS57xx DAC using I2C sda:%u, scl:%u", i2c_config.sda_io_num, i2c_config.scl_io_num);
-	
+
 	i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
 	i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
 	
 	
 	for (int i = 0; tas57xx_init_sequence[i].reg != 0xff; i++) {
 	for (int i = 0; tas57xx_init_sequence[i].reg != 0xff; i++) {
@@ -103,32 +104,21 @@ static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)	{
 		i2c_master_write_byte(i2c_cmd, tas57_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
 		i2c_master_write_byte(i2c_cmd, tas57_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
 		i2c_master_write_byte(i2c_cmd, tas57xx_init_sequence[i].reg, I2C_MASTER_NACK);
 		i2c_master_write_byte(i2c_cmd, tas57xx_init_sequence[i].reg, I2C_MASTER_NACK);
 		i2c_master_write_byte(i2c_cmd, tas57xx_init_sequence[i].value, I2C_MASTER_NACK);
 		i2c_master_write_byte(i2c_cmd, tas57xx_init_sequence[i].value, I2C_MASTER_NACK);
-
-		LOG_DEBUG("i2c write %x at %u", tas57xx_init_sequence[i].reg, tas57xx_init_sequence[i].value);
+		ESP_LOGD(TAG, "i2c write %x at %u", tas57xx_init_sequence[i].reg, tas57xx_init_sequence[i].value);
 	}
 	}
 
 
 	i2c_master_stop(i2c_cmd);	
 	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(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
     i2c_cmd_link_delete(i2c_cmd);
     i2c_cmd_link_delete(i2c_cmd);
 
 
-	// configure I2S pins & install driver	
-	i2s_pin_config_t i2s_pin_config = (i2s_pin_config_t) { 	.bck_io_num = CONFIG_I2S_BCK_IO, .ws_io_num = CONFIG_I2S_WS_IO, 
-														.data_out_num = CONFIG_I2S_DO_IO, .data_in_num = CONFIG_I2S_DI_IO,
-								};
-	res |= i2s_driver_install(i2s_num, i2s_config, 0, NULL);
-	res |= i2s_set_pin(i2s_num, &i2s_pin_config);
-	LOG_INFO("DAC using I2S bck:%d, ws:%d, do:%d", i2s_pin_config.bck_io_num, i2s_pin_config.ws_io_num, i2s_pin_config.data_out_num);
+	ESP_LOGI(TAG, "TAS57xx uses I2C sda:%d, scl:%d", i2c_config.sda_io_num, i2c_config.scl_io_num);
 	
 	
-	if (res == ESP_OK) {
-		// init volume & mute
-		gpio_pad_select_gpio(VOLUME_GPIO);
-		gpio_set_direction(VOLUME_GPIO, GPIO_MODE_OUTPUT);
-		gpio_set_level(VOLUME_GPIO, 0);
-		return true;
-	} else {
-		LOG_ERROR("could not intialize TAS57xx %d", res);
+	if (res != ESP_OK) {
+		ESP_LOGE(TAG, "could not intialize TAS57xx %d", res);
 		return false;
 		return false;
 	}	
 	}	
+	
+	return true;
 }	
 }	
 
 
 /****************************************************************************************
 /****************************************************************************************
@@ -141,10 +131,7 @@ static void deinit(void)	{
 /****************************************************************************************
 /****************************************************************************************
  * change volume
  * change volume
  */
  */
-static void volume(unsigned left, unsigned right) {
-	LOG_INFO("TAS57xx volume (L:%u R:%u)", left, right);
-	gpio_set_level(VOLUME_GPIO, left || right);
-} 
+static bool volume(unsigned left, unsigned right) { return false; }
 
 
 /****************************************************************************************
 /****************************************************************************************
  * power
  * power
@@ -161,7 +148,7 @@ static void power(adac_power_e mode) {
 		dac_cmd(TAS57_DOWN);
 		dac_cmd(TAS57_DOWN);
 		break;				
 		break;				
 	default:
 	default:
-		LOG_WARN("unknown DAC command");
+		ESP_LOGW(TAG, "unknown DAC command");
 		break;
 		break;
 	}
 	}
 }
 }
@@ -177,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
@@ -192,7 +178,7 @@ void dac_cmd(dac_cmd_e cmd, ...) {
 
 
 	switch(cmd) {
 	switch(cmd) {
 	case TAS57_VOLUME:
 	case TAS57_VOLUME:
-		LOG_ERROR("DAC volume not handled yet");
+		ESP_LOGE(TAG, "DAC volume not handled yet");
 		break;
 		break;
 	default:
 	default:
 		i2c_master_start(i2c_cmd);
 		i2c_master_start(i2c_cmd);
@@ -206,7 +192,7 @@ void dac_cmd(dac_cmd_e cmd, ...) {
     i2c_cmd_link_delete(i2c_cmd);
     i2c_cmd_link_delete(i2c_cmd);
 	
 	
 	if (ret != ESP_OK) {
 	if (ret != ESP_OK) {
-		LOG_ERROR("could not intialize TAS57xx %d", ret);
+		ESP_LOGE(TAG, "could not intialize TAS57xx %d", ret);
 	}
 	}
 
 
 	va_end(args);
 	va_end(args);
@@ -216,7 +202,7 @@ void dac_cmd(dac_cmd_e cmd, ...) {
  * TAS57 detection
  * TAS57 detection
  */
  */
 static int tas57_detect(void) {
 static int tas57_detect(void) {
-	u8_t data, addr[] = {TAS578x, TAS575x};
+	uint8_t data, addr[] = {TAS578x, TAS575x};
 	int ret;
 	int ret;
 	
 	
 	for (int i = 0; i < sizeof(addr); i++) {
 	for (int i = 0; i < sizeof(addr); i++) {
@@ -235,7 +221,7 @@ static int tas57_detect(void) {
 		i2c_cmd_link_delete(i2c_cmd);	
 		i2c_cmd_link_delete(i2c_cmd);	
 		
 		
 		if (ret == ESP_OK) {
 		if (ret == ESP_OK) {
-			LOG_INFO("Detected TAS @0x%x", addr[i]);
+			ESP_LOGI(TAG, "Detected TAS @0x%x", addr[i]);
 			return addr[i];
 			return addr[i];
 		}	
 		}	
 	}	
 	}	

+ 123 - 75
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,59 +34,109 @@ menu "Squeezelite-ESP32"
 		bool				
 		bool				
 	config SPKFAULT_LOCKED
 	config SPKFAULT_LOCKED
 		bool				
 		bool				
-    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 SPDIF_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
-	  	
-		menu "DAC I2S settings" 
+	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: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
+		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
 			visible if BASIC_I2C_BT
-			config I2S_NUM         
-		        int "I2S channel (0 or 1). "
-		        default 0
-		        help
-		            I2S dma channel to use.  
-		    config I2S_BCK_IO         
-		        int "I2S Bit clock GPIO number. "
-				default 33 if !A1S
-		        default 27 if A1S
-		        help
-		            I2S Bit Clock gpio pin to use.  
-		    config I2S_WS_IO         
-		        int "I2S Word Select GPIO number. "
-		        default 25 if !A1S
-				default 26 if A1S
-		        help
-		            I2S Word Select gpio pin to use.
-		    config I2S_DO_IO         
-		        int "I2S Data Output GPIO number. "
-		        default 32 if !A1S
-				default 25 if A1S
-		        help
-		            I2S data output gpio pin to use.
-			config I2S_DI_IO         
-		        int "I2S Data Input GPIO number. "
-		        default -1 if !A1S
-				default 35 if A1S
-		        help
-		            I2S data input gpio pin to use (not used mostly, leave it to -1).					
-					
+			menu "I2S settings"
+				config I2S_NUM         
+					int "I2S channel (0 or 1). "
+					default 0
+					help
+						I2S dma channel to use.  
+				config I2S_BCK_IO         
+					int "I2S Bit clock GPIO number. "
+					default 33 
+					help
+						I2S Bit Clock gpio pin to use.  
+				config I2S_WS_IO         
+					int "I2S Word Select GPIO number. "
+					default 25 
+					help
+						I2S Word Select gpio pin to use.
+				config I2S_DO_IO         
+					int "I2S Data Output GPIO number. "
+					default 32
+					help
+						I2S data output gpio pin to use.
+				config I2S_DI_IO         
+					int "I2S Data Input GPIO number. "
+					default -1 if !A1S
+					help
+						I2S data input gpio pin to use (not used mostly, leave it to -1).					
+			endmenu
+			menu "I2C settings"	
+				config I2C_SDA
+					int "I2C SDA GPIO number for DAC control. "
+					default -1
+					help
+						I2C data gpio pin to use with DAC (not used mostly, leave it to -1).										
+				config I2C_SCL
+					int "I2C SCL GPIO number for DAC control. "
+					default -1
+					help
+						I2C clock gpio pin to use with DAC (not used mostly, leave it to -1).															
+			endmenu		
+			config MUTE_GPIO
+				int "GPIO for muting DAC"
+				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
 		endmenu
 		
 		
 		menu "SPDIF settings" 
 		menu "SPDIF settings" 
@@ -107,9 +158,7 @@ menu "Squeezelite-ESP32"
 		            Must be set as SPDIF re-uses I2S but only needs DO (recommendation: set it to I2S Word select value)
 		            Must be set as SPDIF re-uses I2S but only needs DO (recommendation: set it to I2S Word select value)
 		    config SPDIF_DO_IO         
 		    config SPDIF_DO_IO         
 		        int "SPDIF Data I/O GPIO number"
 		        int "SPDIF Data I/O GPIO number"
-		        default 15 if SQUEEZEAMP
-				default I2S_DO_IO if !A1S
-				default -1 if A1S
+		        default -1
 				help
 				help
 		            I2S data output IO use to simulate SPDIF
 		            I2S data output IO use to simulate SPDIF
 		endmenu
 		endmenu
@@ -181,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 ""
@@ -199,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>]				
@@ -208,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
@@ -218,40 +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
@@ -260,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
@@ -273,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	

+ 2 - 1
main/esp_app_main.c

@@ -359,7 +359,8 @@ 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);
 	//todo: add dac_config for known targets
 	//todo: add dac_config for known targets
-
+	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);