Browse Source

mini i2S backend to allow other DAC

philippe44 5 years ago
parent
commit
1b5fc137d4

+ 1 - 0
components/services/globdefs.h

@@ -32,4 +32,5 @@ extern bool gpio36_39_used;
 #else 
 #define LED_GREEN_GPIO	CONFIG_LED_GREEN_GPIO
 #define LED_RED_GPIO	CONFIG_LED_RED_GPIO
+#define JACK_GPIO		CONFIG_JACK_GPIO
 #endif

+ 431 - 0
components/squeezelite/a1s/ac101.c

@@ -0,0 +1,431 @@
+/*
+ * ESPRESSIF MIT License
+ *
+ * Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
+ *
+ * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
+ * it is free of charge, to any person obtaining a copy of this software and associated
+ * documentation files (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <string.h>
+#include <esp_log.h>
+#include <esp_types.h>
+#include <esp_system.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+#include <driver/i2c.h>
+#include "adac.h"
+
+//#include "audio_hal.h"
+#include "ac101.h"
+
+const static char TAG[] = "AC101";
+
+#define AC_ASSERT(a, format, b, ...) \
+    if ((a) != 0) { \
+        ESP_LOGE(TAG, format, ##__VA_ARGS__); \
+        return b;\
+    }
+	
+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);
+
+struct adac_s dac_a1s = { init, deinit, power, speaker, headset, volume };
+
+static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val);
+static uint16_t i2c_read_reg(uint8_t reg);
+static esp_err_t ac101_start(ac_module_t mode);
+static esp_err_t ac101_stop(void);
+static esp_err_t ac101_set_earph_volume(uint8_t volume);
+static esp_err_t ac101_set_spk_volume(uint8_t volume);
+	
+//static void pa_power(bool enable);
+
+static int i2c_port;
+
+/****************************************************************************************
+ * init
+ */
+static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config) {	 
+	esp_err_t res;
+	
+	ESP_LOGI(TAG, "Initializing AC101");
+	i2c_port = i2c_port_num;
+
+	// configure i2c
+	i2c_config_t i2c_config = {
+			.mode = I2C_MODE_MASTER,
+			.sda_io_num = 33,
+			.sda_pullup_en = GPIO_PULLUP_ENABLE,
+			.scl_io_num = 32,
+			.scl_pullup_en = GPIO_PULLUP_ENABLE,
+			.master.clk_speed = 100000,
+		};
+		
+	i2c_param_config(i2c_port, &i2c_config);
+	i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
+	ESP_LOGI(TAG, "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);
+	
+	//huh?
+	//vTaskDelay(1000 / portTICK_PERIOD_MS); 
+	if (ESP_OK != res) {
+		ESP_LOGE(TAG, "reset failed!");
+		return false;
+	} 
+	
+	i2c_write_reg(SPKOUT_CTRL, 0xe880);
+
+	// Enable the PLL from 256*44.1KHz MCLK source
+	i2c_write_reg(PLL_CTRL1, 0x014f);
+	//res |= i2c_write_reg(PLL_CTRL2, 0x83c0);
+	i2c_write_reg(PLL_CTRL2, 0x8600);
+
+	//Clocking system
+	i2c_write_reg(SYSCLK_CTRL, 0x8b08);
+	i2c_write_reg(MOD_CLK_ENA, 0x800c);
+	i2c_write_reg(MOD_RST_CTRL, 0x800c);
+	i2c_write_reg(I2S_SR_CTRL, 0x7000);			//sample rate
+	 
+	//AIF config
+	i2c_write_reg(I2S1LCK_CTRL, 0x8850);			//BCLK/LRCK
+	i2c_write_reg(I2S1_SDOUT_CTRL, 0xc000);		//
+	i2c_write_reg(I2S1_SDIN_CTRL, 0xc000);
+	i2c_write_reg(I2S1_MXR_SRC, 0x2200);			//
+
+	i2c_write_reg(ADC_SRCBST_CTRL, 0xccc4);
+	i2c_write_reg(ADC_SRC, 0x2020);
+	i2c_write_reg(ADC_DIG_CTRL, 0x8000);
+	i2c_write_reg(ADC_APC_CTRL, 0xbbc3);
+
+	//Path Configuration
+	i2c_write_reg(DAC_MXR_SRC, 0xcc00);
+	i2c_write_reg(DAC_DIG_CTRL, 0x8000);
+	i2c_write_reg(OMIXER_SR, 0x0081);
+	i2c_write_reg(OMIXER_DACA_CTRL, 0xf080);//}
+
+	//* Enable Speaker output
+	i2c_write_reg(0x58, 0xeabd);
+
+    //ac101_pa_power(true);
+
+	uint16_t regval;
+
+	// configure I2S		
+	regval = i2c_read_reg(I2S1LCK_CTRL);
+	regval &= 0xffc3;
+	regval |= (AC_MODE_SLAVE << 15);
+	regval |= (BIT_LENGTH_16_BITS << 4);
+	regval |= (AC_MODE_SLAVE << 2);
+	res |= i2c_write_reg(I2S1LCK_CTRL, regval);
+	res |= i2c_write_reg(I2S_SR_CTRL, SAMPLE_RATE_44100);
+			
+	// configure I2S pins & install driver	
+	i2s_pin_config_t i2s_pin_config = (i2s_pin_config_t) { 	.bck_io_num = 27, .ws_io_num = 26, 
+															.data_out_num = 35, .data_in_num = 25 //Not used 
+								};
+	i2s_driver_install(i2s_num, i2s_config, 0, NULL);
+	i2s_set_pin(i2s_num, &i2s_pin_config);
+
+	ESP_LOGI(TAG, "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);
+
+	return true;
+}	
+
+/****************************************************************************************
+ * init
+ */
+static void deinit(void)	{	 
+	i2c_driver_delete(i2c_port);
+}
+
+/****************************************************************************************
+ * change volume
+ */
+static void volume(unsigned left, unsigned right) {
+	// nothing at that point, volume is handled by backend
+} 
+
+/****************************************************************************************
+ * power
+ */
+static void power(adac_power_e mode) {
+	switch(mode) {
+	case ADAC_STANDBY:
+	case ADAC_OFF:
+		ac101_stop();
+		break;
+	case ADAC_ON:
+		ac101_start(AC_MODULE_ADC);
+		break;		
+	default:
+		ESP_LOGW(TAG, "unknown power command");
+		break;
+	}
+}
+
+/****************************************************************************************
+ * speaker
+ */
+static void speaker(bool active) {
+	if (active) i2c_write_reg(SPKOUT_CTRL, 0xeabd);
+	else i2c_write_reg(SPKOUT_CTRL, 0xe880);		//disable speaker
+} 
+
+/****************************************************************************************
+ * headset
+ */
+static void headset(bool active) {
+	if (active) {
+		i2c_write_reg(OMIXER_DACA_CTRL, 0xff80);
+    	i2c_write_reg(HPOUT_CTRL, 0xc3c1);
+    	i2c_write_reg(HPOUT_CTRL, 0xcb00);
+		// huh?
+    	vTaskDelay(100 / portTICK_PERIOD_MS);
+		i2c_write_reg(HPOUT_CTRL, 0xfbc0);
+	} else {
+		i2c_write_reg(HPOUT_CTRL, 0x01);			//disable earphone
+	}	
+} 	
+
+/****************************************************************************************
+ * 
+ */
+static esp_err_t i2c_write_reg(uint8_t reg, uint16_t val)
+{
+    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    esp_err_t ret =0;
+	uint8_t send_buff[4];
+	send_buff[0] = (AC101_ADDR << 1);
+	send_buff[1] = reg;
+	send_buff[2] = (val>>8) & 0xff;
+	send_buff[3] = val & 0xff;
+    ret |= i2c_master_start(cmd);
+    ret |= i2c_master_write(cmd, send_buff, 4, ACK_CHECK_EN);
+    ret |= i2c_master_stop(cmd);
+    ret |= i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(cmd);
+    return ret;
+}
+
+/****************************************************************************************
+ * 
+ */
+static uint16_t i2c_read_reg(uint8_t reg) {
+	uint8_t data[2] = { 0 };
+	
+	i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, ( AC101_ADDR << 1 ) | WRITE_BIT, ACK_CHECK_EN);
+    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
+    i2c_master_start(cmd);
+    i2c_master_write_byte(cmd, ( AC101_ADDR << 1 ) | READ_BIT, ACK_CHECK_EN);		//check or not
+    i2c_master_read(cmd, data, 2, ACK_VAL);
+    i2c_master_stop(cmd);
+    i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
+    i2c_cmd_link_delete(cmd);
+
+	return (data[0] << 8) + data[1];;
+}
+
+/****************************************************************************************
+ * 
+ */
+void set_codec_clk(ac_adda_fs_i2s1_t rate) {
+	i2c_write_reg(I2S_SR_CTRL, rate);
+}
+
+/****************************************************************************************
+ * 
+ */
+static int ac101_get_spk_volume(void) {
+    int res;
+    res = i2c_read_reg(SPKOUT_CTRL);
+    res &= 0x1f;
+    return res*2;
+}
+
+/****************************************************************************************
+ * 
+ */
+static esp_err_t ac101_set_spk_volume(uint8_t volume) {
+	uint16_t res;
+	esp_err_t ret;
+	volume = volume/2;
+	res = i2c_read_reg(SPKOUT_CTRL);
+	res &= (~0x1f);
+	volume &= 0x1f;
+	res |= volume;
+	ret = i2c_write_reg(SPKOUT_CTRL,res);
+	return ret;
+}
+
+/****************************************************************************************
+ * 
+ */
+static int ac101_get_earph_volume(void) {
+    int res;
+    res = i2c_read_reg(HPOUT_CTRL);
+    return (res>>4)&0x3f;
+}
+
+/****************************************************************************************
+ * 
+ */
+static esp_err_t ac101_set_earph_volume(uint8_t volume) {
+	uint16_t res,tmp;
+	esp_err_t ret;
+	res = i2c_read_reg(HPOUT_CTRL);
+	tmp = ~(0x3f<<4);
+	res &= tmp;
+	volume &= 0x3f;
+	res |= (volume << 4);
+	ret = i2c_write_reg(HPOUT_CTRL,res);
+	return ret;
+}
+
+/****************************************************************************************
+ * 
+ */
+static esp_err_t ac101_set_output_mixer_gain(ac_output_mixer_gain_t gain,ac_output_mixer_source_t source)
+{
+	uint16_t regval,temp,clrbit;
+	esp_err_t ret;
+	regval = i2c_read_reg(OMIXER_BST1_CTRL);
+	switch(source){
+	case SRC_MIC1:
+		temp = (gain&0x7) << 6;
+		clrbit = ~(0x7<<6);
+		break;
+	case SRC_MIC2:
+		temp = (gain&0x7) << 3;
+		clrbit = ~(0x7<<3);
+		break;
+	case SRC_LINEIN:
+		temp = (gain&0x7);
+		clrbit = ~0x7;
+		break;
+	default:
+		return -1;
+	}
+	regval &= clrbit;
+	regval |= temp;
+	ret = i2c_write_reg(OMIXER_BST1_CTRL,regval);
+	return ret;
+}
+
+/****************************************************************************************
+ * 
+ */
+static esp_err_t ac101_start(ac_module_t mode) {
+	esp_err_t res = 0;
+	
+    if (mode == AC_MODULE_LINE) {
+		res |= i2c_write_reg(0x51, 0x0408);
+		res |= i2c_write_reg(0x40, 0x8000);
+		res |= i2c_write_reg(0x50, 0x3bc0);
+    }
+    if (mode == AC_MODULE_ADC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE) {
+		//I2S1_SDOUT_CTRL
+		//res |= i2c_write_reg(PLL_CTRL2, 0x8120);
+    	res |= i2c_write_reg(0x04, 0x800c);
+    	res |= i2c_write_reg(0x05, 0x800c);
+		//res |= i2c_write_reg(0x06, 0x3000);
+    }
+    if (mode == AC_MODULE_DAC || mode == AC_MODULE_ADC_DAC || mode == AC_MODULE_LINE) {
+    	//* Enable Headphone output   注意使用耳机时,最后开以下寄存器
+		res |= i2c_write_reg(OMIXER_DACA_CTRL, 0xff80);
+    	res |= i2c_write_reg(HPOUT_CTRL, 0xc3c1);
+    	res |= i2c_write_reg(HPOUT_CTRL, 0xcb00);
+		// huh?
+    	vTaskDelay(100 / portTICK_PERIOD_MS);
+		res |= i2c_write_reg(HPOUT_CTRL, 0xfbc0);
+
+    	//* Enable Speaker output
+		res |= i2c_write_reg(SPKOUT_CTRL, 0xeabd);
+		// huh?
+		vTaskDelay(10 / portTICK_PERIOD_MS);
+		ac101_set_earph_volume(255);
+		ac101_set_spk_volume(255);
+    }
+
+    return res;
+}
+
+/****************************************************************************************
+ * 
+ */
+esp_err_t ac101_stop(void) {
+	esp_err_t res = 0;
+	res |= i2c_write_reg(HPOUT_CTRL, 0x01);			//disable earphone
+	res |= i2c_write_reg(SPKOUT_CTRL, 0xe880);		//disable speaker
+	return res;
+}
+
+/****************************************************************************************
+ * 
+ */
+esp_err_t ac101_deinit(void) {
+	return	i2c_write_reg(CHIP_AUDIO_RS, 0x123);		//soft reset
+}
+
+
+/****************************************************************************************
+ * Don't know when this one is supposed to be called
+ */
+esp_err_t AC101_i2s_config_clock(ac_i2s_clock_t *cfg) {
+	esp_err_t res = 0;
+	uint16_t regval=0;
+	regval = i2c_read_reg(I2S1LCK_CTRL);
+	regval &= 0xe03f;
+	regval |= (cfg->bclk_div << 9);
+	regval |= (cfg->lclk_div << 6);
+	res = i2c_write_reg(I2S1LCK_CTRL, regval);
+	return res;
+}
+
+/****************************************************************************************
+ * 
+ */
+esp_err_t ac101_get_voice_volume(int* volume) {
+	*volume = ac101_get_earph_volume();
+	return 0;
+}
+
+/*
+void ac101_pa_power(bool enable) {
+    gpio_config_t  io_conf;
+    memset(&io_conf, 0, sizeof(io_conf));
+    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
+    io_conf.mode = GPIO_MODE_OUTPUT;
+    io_conf.pin_bit_mask = BIT(GPIO_PA_EN);
+    io_conf.pull_down_en = 0;
+    io_conf.pull_up_en = 0;
+    gpio_config(&io_conf);
+    if (enable) {
+        gpio_set_level(GPIO_PA_EN, 1);
+    } else {
+        gpio_set_level(GPIO_PA_EN, 0);
+    }
+}
+*/

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

@@ -0,0 +1,176 @@
+/*
+ * ESPRESSIF MIT License
+ *
+ * Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
+ *
+ * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
+ * it is free of charge, to any person obtaining a copy of this software and associated
+ * documentation files (the "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
+ * to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+ 
+#ifndef __AC101_H__
+#define __AC101_H__
+
+#include "esp_types.h"
+
+#define AC101_ADDR			0x1a				/*!< Device address*/
+
+#define WRITE_BIT  			I2C_MASTER_WRITE 	/*!< I2C master write */
+#define READ_BIT   			I2C_MASTER_READ  	/*!< I2C master read */
+#define ACK_CHECK_EN   		0x1     			/*!< I2C master will check ack from slave*/
+#define ACK_CHECK_DIS  		0x0     			/*!< I2C master will not check ack from slave */
+#define ACK_VAL    			0x0         		/*!< I2C ack value */
+#define NACK_VAL   			0x1         		/*!< I2C nack value */
+
+#define CHIP_AUDIO_RS		0x00
+#define PLL_CTRL1			0x01
+#define PLL_CTRL2			0x02
+#define SYSCLK_CTRL			0x03
+#define MOD_CLK_ENA			0x04
+#define MOD_RST_CTRL		0x05
+#define I2S_SR_CTRL			0x06
+#define I2S1LCK_CTRL		0x10
+#define I2S1_SDOUT_CTRL		0x11
+#define I2S1_SDIN_CTRL		0x12
+#define I2S1_MXR_SRC		0x13
+#define I2S1_VOL_CTRL1		0x14
+#define I2S1_VOL_CTRL2		0x15
+#define I2S1_VOL_CTRL3		0x16
+#define I2S1_VOL_CTRL4		0x17
+#define I2S1_MXR_GAIN		0x18
+#define ADC_DIG_CTRL		0x40
+#define ADC_VOL_CTRL		0x41
+#define HMIC_CTRL1			0x44
+#define HMIC_CTRL2			0x45
+#define HMIC_STATUS			0x46
+#define DAC_DIG_CTRL		0x48
+#define DAC_VOL_CTRL		0x49
+#define DAC_MXR_SRC			0x4c
+#define DAC_MXR_GAIN		0x4d
+#define ADC_APC_CTRL		0x50
+#define ADC_SRC				0x51
+#define ADC_SRCBST_CTRL		0x52
+#define OMIXER_DACA_CTRL	0x53
+#define OMIXER_SR			0x54
+#define OMIXER_BST1_CTRL	0x55
+#define HPOUT_CTRL			0x56
+#define SPKOUT_CTRL			0x58
+#define AC_DAC_DAPCTRL		0xa0
+#define AC_DAC_DAPHHPFC 	0xa1
+#define AC_DAC_DAPLHPFC 	0xa2
+#define AC_DAC_DAPLHAVC 	0xa3
+#define AC_DAC_DAPLLAVC 	0xa4
+#define AC_DAC_DAPRHAVC 	0xa5
+#define AC_DAC_DAPRLAVC 	0xa6
+#define AC_DAC_DAPHGDEC 	0xa7
+#define AC_DAC_DAPLGDEC 	0xa8
+#define AC_DAC_DAPHGATC 	0xa9
+#define AC_DAC_DAPLGATC 	0xaa
+#define AC_DAC_DAPHETHD 	0xab
+#define AC_DAC_DAPLETHD 	0xac
+#define AC_DAC_DAPHGKPA 	0xad
+#define AC_DAC_DAPLGKPA 	0xae
+#define AC_DAC_DAPHGOPA 	0xaf
+#define AC_DAC_DAPLGOPA 	0xb0
+#define AC_DAC_DAPOPT   	0xb1
+#define DAC_DAP_ENA     	0xb5
+
+typedef enum{
+	SAMPLE_RATE_8000	= 0x0000,
+	SAMPLE_RATE_11052	= 0x1000,
+	SAMPLE_RATE_12000	= 0x2000,
+	SAMPLE_RATE_16000	= 0x3000,
+	SAMPLE_RATE_22050	= 0x4000,
+	SAMPLE_RATE_24000	= 0x5000,
+	SAMPLE_RATE_32000	= 0x6000,
+	SAMPLE_RATE_44100	= 0x7000,
+	SAMPLE_RATE_48000	= 0x8000,
+	SAMPLE_RATE_96000	= 0x9000,
+	SAMPLE_RATE_192000	= 0xa000,
+} ac_adda_fs_i2s1_t;
+
+typedef enum{
+	BCLK_DIV_1		= 0x0,
+	BCLK_DIV_2		= 0x1,
+	BCLK_DIV_4		= 0x2,
+	BCLK_DIV_6		= 0x3,
+	BCLK_DIV_8		= 0x4,
+	BCLK_DIV_12		= 0x5,
+	BCLK_DIV_16		= 0x6,
+	BCLK_DIV_24		= 0x7,
+	BCLK_DIV_32		= 0x8,
+	BCLK_DIV_48		= 0x9,
+	BCLK_DIV_64		= 0xa,
+	BCLK_DIV_96		= 0xb,
+	BCLK_DIV_128	= 0xc,
+	BCLK_DIV_192	= 0xd,
+} ac_i2s1_bclk_div_t;
+
+typedef enum{
+	LRCK_DIV_16		=0x0,
+	LRCK_DIV_32		=0x1,
+	LRCK_DIV_64		=0x2,
+	LRCK_DIV_128	=0x3,
+	LRCK_DIV_256	=0x4,
+} ac_i2s1_lrck_div_t;
+
+typedef enum {
+    BIT_LENGTH_8_BITS = 0x00,
+    BIT_LENGTH_16_BITS = 0x01,
+    BIT_LENGTH_20_BITS = 0x02,
+    BIT_LENGTH_24_BITS = 0x03,
+} ac_bits_length_t;
+
+typedef enum {
+    AC_MODE_MIN = -1,
+    AC_MODE_SLAVE = 0x00,
+    AC_MODE_MASTER = 0x01,
+    AC_MODE_MAX,
+} ac_mode_sm_t;
+
+typedef enum {
+    AC_MODULE_MIN = -1,
+    AC_MODULE_ADC = 0x01,
+    AC_MODULE_DAC = 0x02,
+    AC_MODULE_ADC_DAC = 0x03,
+    AC_MODULE_LINE = 0x04,
+    AC_MODULE_MAX
+} ac_module_t;
+
+typedef enum{
+	SRC_MIC1	= 1,
+	SRC_MIC2	= 2,
+	SRC_LINEIN	= 3,
+}ac_output_mixer_source_t;
+
+typedef enum {
+    GAIN_N45DB = 0,
+    GAIN_N30DB = 1,
+    GAIN_N15DB = 2,
+    GAIN_0DB   = 3,
+    GAIN_15DB  = 4,
+    GAIN_30DB  = 5,
+    GAIN_45DB  = 6,
+    GAIN_60DB  = 7,
+} ac_output_mixer_gain_t;
+
+typedef struct {
+	ac_i2s1_bclk_div_t bclk_div;    /*!< bits clock divide */
+	ac_i2s1_lrck_div_t lclk_div;    /*!< WS clock divide */
+} ac_i2s_clock_t;
+
+#endif

+ 38 - 0
components/squeezelite/adac.h

@@ -0,0 +1,38 @@
+/* 
+ *  Squeezelite for esp32
+ *
+ *  (c) Sebastien 2019
+ *      Philippe G. 2019, philippe_44@outlook.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "freertos/FreeRTOS.h"
+#include "driver/i2s.h"
+
+typedef enum { ADAC_ON = 0, ADAC_STANDBY, ADAC_OFF } adac_power_e;
+
+struct adac_s {
+	bool (*init)(int i2c_port_num, int i2s_num, i2s_config_t *config);
+	void (*deinit)(void);
+	void (*power)(adac_power_e mode);
+	void (*speaker)(bool active);
+	void (*headset)(bool active);
+	void (*volume)(unsigned left, unsigned right);
+};
+
+extern struct adac_s dac_tas57xx;
+extern struct adac_s dac_a1s;
+extern struct adac_s dac_null;

+ 2 - 1
components/squeezelite/component.mk

@@ -19,5 +19,6 @@ CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ON
 
 #	-I$(COMPONENT_PATH)/../codecs/inc/faad2
 
-
+COMPONENT_SRCDIRS := . tas57xx a1s null
+COMPONENT_ADD_INCLUDEDIRS := . ./tas57xx ./a1s
 

+ 32 - 0
components/squeezelite/null/dac_null.c

@@ -0,0 +1,32 @@
+/* 
+ *  Squeezelite for esp32
+ *
+ *  (c) Sebastien 2019
+ *      Philippe G. 2019, philippe_44@outlook.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+ 
+#include "adac.h"
+
+static bool init(int i2c_port_num, int i2s_num, i2s_config_t *config) { return true; };
+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) { };
+
+struct adac_s dac_null = { init, deinit, power, speaker, headset, volume };
+

+ 0 - 5
components/squeezelite/output_bt.c

@@ -68,11 +68,6 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g
 DECLARE_ALL_MIN_MAX;	
 	
 void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
-#ifdef CONFIG_SQUEEZEAMP
-	gpio_pad_select_gpio(config_spdif_gpio);
-	gpio_set_direction(config_spdif_gpio, GPIO_MODE_OUTPUT);
-	gpio_set_level(config_spdif_gpio, 0);
-#endif			
 	loglevel = level;
 	running = true;
 	output.write_cb = &_write_frames;

+ 59 - 239
components/squeezelite/output_i2s.c

@@ -47,6 +47,7 @@ sure that using rate_delay would fix that
 #include "driver/gpio.h"
 #include "perf_trace.h"
 #include <signal.h>
+#include "adac.h"
 #include "time.h"
 #include "led.h"
 #include "monitor.h"
@@ -57,36 +58,6 @@ sure that using rate_delay would fix that
 
 #define FRAME_BLOCK MAX_SILENCE_FRAMES
 
-// Prevent compile errors if dac output is
-// included in the build and not actually activated in menuconfig
-#ifndef CONFIG_I2S_BCK_IO
-#define CONFIG_I2S_BCK_IO -1
-#endif
-#ifndef CONFIG_I2S_WS_IO
-#define CONFIG_I2S_WS_IO -1
-#endif
-#ifndef CONFIG_I2S_DO_IO
-#define CONFIG_I2S_DO_IO -1
-#endif
-#ifndef CONFIG_I2S_NUM
-#define CONFIG_I2S_NUM -1
-#endif
-
-#ifndef CONFIG_SPDIF_BCK_IO
-#define CONFIG_SPDIF_BCK_IO -1
-#endif
-#ifndef CONFIG_SPDIF_WS_IO
-#define CONFIG_SPDIF_WS_IO -1
-#endif
-#ifndef CONFIG_SPDIF_DO_IO
-#define CONFIG_SPDIF_DO_IO -1
-#endif
-#ifndef CONFIG_SPDIF_NUM
-#define CONFIG_SPDIF_NUM -1
-#endif
-
-typedef enum { DAC_ACTIVE = 0, DAC_STANDBY, DAC_DOWN, DAC_ANALOGUE_OFF, DAC_ANALOGUE_ON, DAC_VOLUME } dac_cmd_e;
-
 // must have an integer ratio with FRAME_BLOCK (see spdif comment)
 #define DMA_BUF_LEN		512	
 #define DMA_BUF_COUNT	12
@@ -112,6 +83,9 @@ extern struct buffer *streambuf;
 extern struct buffer *outputbuf;
 extern u8_t *silencebuf;
 
+// by default no DAC selected
+struct adac_s *adac = &dac_null;
+
 static log_level loglevel;
 
 static bool jack_mutes_amp;
@@ -130,24 +104,12 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
 								s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
 static void *output_thread_i2s();
 static void *output_thread_i2s_stats();
-static void dac_cmd(dac_cmd_e cmd, ...);
-static int tas57_detect(void);
 static void spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count);
 static void (*jack_handler_chain)(bool inserted);
 
+// force all GPIOs to what we need
 #ifdef CONFIG_SQUEEZEAMP
-
 #define TAS57xx
-
-#undef	CONFIG_I2S_BCK_IO 
-#define CONFIG_I2S_BCK_IO 	33
-#undef 	CONFIG_I2S_WS_IO	
-#define CONFIG_I2S_WS_IO	25
-#undef 	CONFIG_I2S_DO_IO
-#define CONFIG_I2S_DO_IO	32
-#undef 	CONFIG_I2S_NUM
-#define CONFIG_I2S_NUM		0
-
 #undef	CONFIG_SPDIF_BCK_IO 
 #define CONFIG_SPDIF_BCK_IO 33
 #undef 	CONFIG_SPDIF_WS_IO	
@@ -156,51 +118,15 @@ static void (*jack_handler_chain)(bool inserted);
 #define CONFIG_SPDIF_DO_IO	15
 #undef 	CONFIG_SPDIF_NUM
 #define CONFIG_SPDIF_NUM	0
+#undef 	CONFIG_I2S_NUM
+#define CONFIG_I2S_NUM		0
+#elif defined CONFIG_A1S
+#define A1S
+#undef 	CONFIG_I2S_NUM
+#define CONFIG_I2S_NUM		0
+#endif
 
 #define I2C_PORT	0
-#define VOLUME_GPIO	14
-
-#define TAS575x 0x98
-#define TAS578x	0x90
-
-struct tas57xx_cmd_s {
-	u8_t reg;
-	u8_t value;
-};
-
-u8_t config_spdif_gpio = CONFIG_SPDIF_DO_IO;
-	
-static const struct tas57xx_cmd_s tas57xx_init_sequence[] = {
-    { 0x00, 0x00 },		// select page 0
-    { 0x02, 0x10 },		// standby
-    { 0x0d, 0x10 },		// use SCK for PLL
-	{ 0x25, 0x08 },		// ignore SCK halt 
-	{ 0x08, 0x10 },		// Mute control enable (from TAS5780)
-	{ 0x54, 0x02 },		// Mute output control (from TAS5780)
-	{ 0x02, 0x00 },		// restart
-	{ 0xff, 0xff }		// end of table
-};
-
-static const i2c_config_t i2c_config = {
-        .mode = I2C_MODE_MASTER,
-        .sda_io_num = 27,
-        .sda_pullup_en = GPIO_PULLUP_ENABLE,
-        .scl_io_num = 26,
-        .scl_pullup_en = GPIO_PULLUP_ENABLE,
-        .master.clk_speed = 100000,
-};
-
-static const struct tas57xx_cmd_s tas57xx_cmd[] = {
-	{ 0x02, 0x00 },	// DAC_ACTIVE
-	{ 0x02, 0x10 },	// DAC_STANDBY
-	{ 0x02, 0x01 },	// DAC_DOWN
-	{ 0x56, 0x10 },	// DAC_ANALOGUE_OFF
-	{ 0x56, 0x00 },	// DAC_ANALOGUE_ON
-};
-
-static u8_t tas57_addr;
-
-#endif
 
 /****************************************************************************************
  * jack insertion handler
@@ -209,9 +135,15 @@ static void jack_handler(bool inserted) {
 	// jack detection bounces a bit but that seems fine
 	if (jack_mutes_amp) {
 		LOG_INFO("switching amplifier %s", inserted ? "OFF" : "ON");
-		if (inserted) dac_cmd(DAC_ANALOGUE_OFF);
-		else dac_cmd(DAC_ANALOGUE_ON);
+		if (inserted) adac->speaker(false);
+		else adac->speaker(true);
 	}
+	
+	// activate headset
+	if (inserted) adac->headset(true);
+	else adac->headset(false);
+	
+	// and chain if any
 	if (jack_handler_chain) (jack_handler_chain)(inserted);
 }
 
@@ -226,44 +158,6 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 	jack_mutes_amp = (strcmp(p,"1") == 0 ||strcasecmp(p,"y") == 0);
 	free(p);
 	
-#ifdef TAS57xx
-	LOG_INFO("Initializing TAS57xx ");
-				
-	adc1_config_width(ADC_WIDTH_BIT_12);
-    adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_0);
-    			
-	// init volume & mute
-	gpio_pad_select_gpio(VOLUME_GPIO);
-	gpio_set_direction(VOLUME_GPIO, GPIO_MODE_OUTPUT);
-	gpio_set_level(VOLUME_GPIO, 0);
-	
-	// configure i2c
-	i2c_param_config(I2C_PORT, &i2c_config);
-	i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, false, false, false);
-	
-	// find which TAS we are using
-	tas57_addr = tas57_detect();
-	
-	i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
-	
-	for (int i = 0; tas57xx_init_sequence[i].reg != 0xff; i++) {
-		i2c_master_start(i2c_cmd);
-		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].value, I2C_MASTER_NACK);
-
-		LOG_DEBUG("i2c write %x at %u", tas57xx_init_sequence[i].reg, tas57xx_init_sequence[i].value);
-	}
-
-	i2c_master_stop(i2c_cmd);	
-	esp_err_t ret = i2c_master_cmd_begin(I2C_PORT, i2c_cmd, 500 / portTICK_RATE_MS);
-    i2c_cmd_link_delete(i2c_cmd);
-	
-	if (ret != ESP_OK) {
-		LOG_ERROR("could not intialize TAS57xx %d", ret);
-	}
-#endif	
-	
 #ifdef CONFIG_I2S_BITS_PER_CHANNEL
 	switch (CONFIG_I2S_BITS_PER_CHANNEL) {
 		case 24:
@@ -287,8 +181,6 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 	bytes_per_frame = 2*2;
 #endif
 
-	if (strcasestr(device, "spdif")) spdif = true;
-
 	output.write_cb = &_i2s_write_frames;
 	obuf = malloc(FRAME_BLOCK * bytes_per_frame);
 	if (!obuf) {
@@ -296,12 +188,20 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
 		return;
 	}
 		
-	running=true;
+	running = true;
 
-	i2s_pin_config_t pin_config;
-	
-	if (spdif) {
-		pin_config = (i2s_pin_config_t) { .bck_io_num = CONFIG_SPDIF_BCK_IO, .ws_io_num = CONFIG_SPDIF_WS_IO, 
+	// common I2S initialization
+	i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX;
+	i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
+	i2s_config.communication_format = I2S_COMM_FORMAT_I2S| I2S_COMM_FORMAT_I2S_MSB;
+	// in case of overflow, do not replay old buffer
+	i2s_config.tx_desc_auto_clear = true;		
+	i2s_config.use_apll = true;
+	i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //Interrupt level 1
+
+	if (strcasestr(device, "spdif")) {
+		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 //Not used
 									};
 		i2s_config.sample_rate = output.current_sample_rate * 2;
@@ -315,48 +215,44 @@ 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)
 		*/   
 		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);
+		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 {
-		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 = -1 //Not used
-									};
+#ifdef TAS57xx
+		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);
+		adac = &dac_tas57xx;
+#elif defined(A1S)
+		adac = &dac_a1s;
+#endif	
 		i2s_config.sample_rate = output.current_sample_rate;
 		i2s_config.bits_per_sample = bytes_per_frame * 8 / 2;
 		// Counted in frames (but i2s allocates a buffer <= 4092 bytes)
 		i2s_config.dma_buf_len = DMA_BUF_LEN;	
 		i2s_config.dma_buf_count = DMA_BUF_COUNT;
 		dma_buf_frames = DMA_BUF_COUNT * DMA_BUF_LEN;	
-#ifdef TAS57xx	
-		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			
+		
+		// finally let DAC driver initialize I2C and I2S
+		adac->init(I2C_PORT, CONFIG_I2S_NUM, &i2s_config);
 	}
 
-	i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX;
-	i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
-	i2s_config.communication_format = I2S_COMM_FORMAT_I2S| I2S_COMM_FORMAT_I2S_MSB;
-	// in case of overflow, do not replay old buffer
-	i2s_config.tx_desc_auto_clear = true;		
-	i2s_config.use_apll = true;
-	i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //Interrupt level 1
-
 	LOG_INFO("Initializing I2S mode %s with rate: %d, bits per sample: %d, buffer frames: %d, number of buffers: %d ", 
 			spdif ? "S/PDIF" : "normal", 
 			i2s_config.sample_rate, i2s_config.bits_per_sample, i2s_config.dma_buf_len, i2s_config.dma_buf_count);
-
-	i2s_driver_install(CONFIG_I2S_NUM, &i2s_config, 0, NULL);
-	i2s_set_pin(CONFIG_I2S_NUM, &pin_config);
+	
 	i2s_stop(CONFIG_I2S_NUM);
 	i2s_zero_dma_buffer(CONFIG_I2S_NUM);
 	isI2SStarted=false;
 	
-	dac_cmd(DAC_STANDBY);
+	adac->power(ADAC_STANDBY);
 
 	jack_handler_chain = jack_handler_svc;
 	jack_handler_svc = jack_handler;
 	
-	if (jack_mutes_amp && jack_inserted_svc()) dac_cmd(DAC_ANALOGUE_OFF);
-	else dac_cmd(DAC_ANALOGUE_ON);
+	if (jack_mutes_amp && jack_inserted_svc()) adac->speaker(false);
+	else adac->speaker(true);
 	
 	esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
 	
@@ -388,22 +284,15 @@ void output_close_i2s(void) {
 	i2s_driver_uninstall(CONFIG_I2S_NUM);
 	free(obuf);
 	
-#ifdef TAS57xx	
-	i2c_driver_delete(I2C_PORT);
-#endif	
+	adac->deinit();
 }
 
 /****************************************************************************************
  * change volume
  */
 bool output_volume_i2s(unsigned left, unsigned right) {
-#ifdef TAS57xx	
-	if (!spdif) {
-		LOG_INFO("TAS57xx volume (L:%u R:%u)", left, right);
-		gpio_set_level(VOLUME_GPIO, left || right);
-	}
-#endif	
- return false;	
+	adac->volume(left, right);
+	return false;	
 } 
 
 /****************************************************************************************
@@ -482,14 +371,10 @@ static void *output_thread_i2s() {
 			LOG_INFO("Output state is %d", output.state);
 			if (output.state == OUTPUT_OFF) led_blink(LED_GREEN, 100, 2500);
 			else if (output.state == OUTPUT_STOPPED) {
-#ifdef TAS57xx				
-				dac_cmd(DAC_ANALOGUE_OFF);
-#endif				
+				adac->speaker(false);
 				led_blink(LED_GREEN, 200, 1000);
 			} else if (output.state == OUTPUT_RUNNING) {
-#ifdef TAS57xx				
-				if (!jack_mutes_amp || !jack_inserted_svc()) dac_cmd(DAC_ANALOGUE_ON);
-#endif				
+				if (!jack_mutes_amp || !jack_inserted_svc()) adac->speaker(true);
 				led_on(LED_GREEN);
 			}	
 		}
@@ -500,7 +385,7 @@ static void *output_thread_i2s() {
 			if (isI2SStarted) {
 				isI2SStarted = false;
 				i2s_stop(CONFIG_I2S_NUM);
-				if (!spdif) dac_cmd(DAC_STANDBY);
+				adac->power(ADAC_STANDBY);
 				count = 0;
 			}
 			usleep(200000);
@@ -546,7 +431,7 @@ static void *output_thread_i2s() {
 			LOG_INFO("Restarting I2S.");
 			i2s_zero_dma_buffer(CONFIG_I2S_NUM);
 			i2s_start(CONFIG_I2S_NUM);
-			if (!spdif) dac_cmd(DAC_ACTIVE);	
+			adac->power(ADAC_ON);	
 		} 
 		
 		// this does not work well as set_sample_rates resets the fifos (and it's too early)
@@ -592,6 +477,7 @@ static void *output_thread_i2s() {
  * Stats output thread
  */
 static void *output_thread_i2s_stats() {
+	//return;
 	while (running) {
 		LOCK;
 		output_state state = output.state;
@@ -621,72 +507,6 @@ static void *output_thread_i2s_stats() {
 	return NULL;
 }
 
-/****************************************************************************************
- * DAC specific commands
- */
-void dac_cmd(dac_cmd_e cmd, ...) {
-	va_list args;
-	esp_err_t ret = ESP_OK;
-	
-	va_start(args, cmd);
-#ifdef TAS57xx	
-	i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
-
-	switch(cmd) {
-	case DAC_VOLUME:
-		LOG_ERROR("volume not handled yet");
-		break;
-	default:
-		i2c_master_start(i2c_cmd);
-		i2c_master_write_byte(i2c_cmd, tas57_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
-		i2c_master_write_byte(i2c_cmd, tas57xx_cmd[cmd].reg, I2C_MASTER_NACK);
-		i2c_master_write_byte(i2c_cmd, tas57xx_cmd[cmd].value, I2C_MASTER_NACK);
-		i2c_master_stop(i2c_cmd);	
-		ret	= i2c_master_cmd_begin(I2C_PORT, i2c_cmd, 50 / portTICK_RATE_MS);
-	}
-	
-    i2c_cmd_link_delete(i2c_cmd);
-	
-	if (ret != ESP_OK) {
-		LOG_ERROR("could not intialize TAS57xx %d", ret);
-	}
-#endif	
-	va_end(args);
-}
-
-/****************************************************************************************
- * TAS57 detection
- */
-#ifdef TAS57xx
-static int tas57_detect(void) {
-	u8_t data, addr[] = {TAS578x, TAS575x};
-	int ret;
-	
-	for (int i = 0; i < sizeof(addr); i++) {
-		i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
-
-		i2c_master_start(i2c_cmd);
-		i2c_master_write_byte(i2c_cmd, addr[i] | I2C_MASTER_WRITE, I2C_MASTER_NACK);
-		i2c_master_write_byte(i2c_cmd, 00, I2C_MASTER_NACK);
-		
-		i2c_master_start(i2c_cmd);			
-		i2c_master_write_byte(i2c_cmd, addr[i] | I2C_MASTER_READ, I2C_MASTER_NACK);
-		i2c_master_read_byte(i2c_cmd, &data, I2C_MASTER_NACK);
-		
-		i2c_master_stop(i2c_cmd);	
-		ret	= i2c_master_cmd_begin(I2C_PORT, i2c_cmd, 50 / portTICK_RATE_MS);
-		i2c_cmd_link_delete(i2c_cmd);	
-		
-		if (ret == ESP_OK) {
-			LOG_INFO("Detected TAS @0x%x", addr[i]);
-			return addr[i];
-		}	
-	}	
-	
-	return 0;
-}
-#endif
-
 /****************************************************************************************
  * SPDIF support
  */

+ 248 - 0
components/squeezelite/tas57xx/dac_57xx.c

@@ -0,0 +1,248 @@
+/* 
+ *  Squeezelite for esp32
+ *
+ *  (c) Sebastien 2019
+ *      Philippe G. 2019, philippe_44@outlook.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+ 
+#include "squeezelite.h" 
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/i2s.h"
+#include "driver/i2c.h"
+#include "driver/gpio.h"
+#include "adac.h"
+
+#define VOLUME_GPIO	14
+#define TAS575x 0x98
+#define TAS578x	0x90
+
+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);
+
+struct adac_s dac_tas57xx = { init, deinit, power, speaker, headset, volume };
+
+struct tas57xx_cmd_s {
+	uint8_t reg;
+	uint8_t value;
+};
+
+static const struct tas57xx_cmd_s tas57xx_init_sequence[] = {
+    { 0x00, 0x00 },		// select page 0
+    { 0x02, 0x10 },		// standby
+    { 0x0d, 0x10 },		// use SCK for PLL
+	{ 0x25, 0x08 },		// ignore SCK halt 
+	{ 0x08, 0x10 },		// Mute control enable (from TAS5780)
+	{ 0x54, 0x02 },		// Mute output control (from TAS5780)
+	{ 0x02, 0x00 },		// restart
+	{ 0xff, 0xff }		// end of table
+};
+
+// matching orders
+typedef enum { TAS57_ACTIVE = 0, TAS57_STANDBY, TAS57_DOWN, TAS57_ANALOGUE_OFF, TAS57_ANALOGUE_ON, TAS57_VOLUME } dac_cmd_e;
+
+static const struct tas57xx_cmd_s tas57xx_cmd[] = {
+	{ 0x02, 0x00 },	// TAS57_ACTIVE
+	{ 0x02, 0x10 },	// TAS57_STANDBY
+	{ 0x02, 0x01 },	// TAS57_DOWN
+	{ 0x56, 0x10 },	// TAS57_ANALOGUE_OFF
+	{ 0x56, 0x00 },	// TAS57_ANALOGUE_ON
+};
+
+static log_level loglevel = lINFO;
+static u8_t tas57_addr;
+static int i2c_port;
+
+static void dac_cmd(dac_cmd_e cmd, ...);
+static int tas57_detect(void);
+
+/****************************************************************************************
+ * init
+ */
+static bool init(int i2c_port_num, int i2s_num, i2s_config_t *i2s_config)	{	 
+	LOG_INFO("Initializing TAS57xx ");
+	
+	i2c_port = i2c_port_num;
+	
+	// init volume & mute
+	gpio_pad_select_gpio(VOLUME_GPIO);
+	gpio_set_direction(VOLUME_GPIO, GPIO_MODE_OUTPUT);
+	gpio_set_level(VOLUME_GPIO, 0);
+	
+	// configure i2c
+	i2c_config_t i2c_config = {
+			.mode = I2C_MODE_MASTER,
+			.sda_io_num = 27,
+			.sda_pullup_en = GPIO_PULLUP_ENABLE,
+			.scl_io_num = 26,
+			.scl_pullup_en = GPIO_PULLUP_ENABLE,
+			.master.clk_speed = 100000,
+		};
+	i2c_param_config(i2c_port, &i2c_config);
+	i2c_driver_install(i2c_port, I2C_MODE_MASTER, false, false, false);
+	LOG_INFO("DAC using I2C sda:%u, scl:%u", i2c_config.sda_io_num, i2c_config.scl_io_num);
+	
+	// find which TAS we are using
+	tas57_addr = tas57_detect();
+	
+	i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
+	
+	for (int i = 0; tas57xx_init_sequence[i].reg != 0xff; i++) {
+		i2c_master_start(i2c_cmd);
+		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].value, I2C_MASTER_NACK);
+
+		LOG_DEBUG("i2c write %x at %u", tas57xx_init_sequence[i].reg, tas57xx_init_sequence[i].value);
+	}
+
+	i2c_master_stop(i2c_cmd);	
+	esp_err_t ret = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
+    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 = 33, .ws_io_num = 25, 
+														.data_out_num = 32, .data_in_num = -1 //Not used 
+								};
+	i2s_driver_install(i2s_num, i2s_config, 0, NULL);
+	i2s_set_pin(i2s_num, &i2s_pin_config);
+	LOG_INFO("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 (ret != ESP_OK) {
+		LOG_ERROR("could not intialize TAS57xx %d", ret);
+		return false;
+	} else {
+		return true;
+	}	
+}	
+
+/****************************************************************************************
+ * init
+ */
+static void deinit(void)	{	 
+	i2c_driver_delete(i2c_port);
+}
+
+/****************************************************************************************
+ * 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);
+} 
+
+/****************************************************************************************
+ * power
+ */
+static void power(adac_power_e mode) {
+	switch(mode) {
+	case ADAC_STANDBY:
+		dac_cmd(TAS57_STANDBY);
+		break;
+	case ADAC_ON:
+		dac_cmd(TAS57_ACTIVE);
+		break;		
+	case ADAC_OFF:
+		dac_cmd(TAS57_DOWN);
+		break;				
+	default:
+		LOG_WARN("unknown DAC command");
+		break;
+	}
+}
+
+/****************************************************************************************
+ * speaker
+ */
+static void speaker(bool active) {
+	if (active) dac_cmd(TAS57_ANALOGUE_ON);
+	else dac_cmd(TAS57_ANALOGUE_OFF);
+} 
+
+/****************************************************************************************
+ * headset
+ */
+static void headset(bool active) {
+} 
+ 
+/****************************************************************************************
+ * DAC specific commands
+ */
+void dac_cmd(dac_cmd_e cmd, ...) {
+	va_list args;
+	esp_err_t ret = ESP_OK;
+	
+	va_start(args, cmd);
+	i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
+
+	switch(cmd) {
+	case TAS57_VOLUME:
+		LOG_ERROR("DAC volume not handled yet");
+		break;
+	default:
+		i2c_master_start(i2c_cmd);
+		i2c_master_write_byte(i2c_cmd, tas57_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
+		i2c_master_write_byte(i2c_cmd, tas57xx_cmd[cmd].reg, I2C_MASTER_NACK);
+		i2c_master_write_byte(i2c_cmd, tas57xx_cmd[cmd].value, I2C_MASTER_NACK);
+		i2c_master_stop(i2c_cmd);	
+		ret	= i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS);
+	}
+	
+    i2c_cmd_link_delete(i2c_cmd);
+	
+	if (ret != ESP_OK) {
+		LOG_ERROR("could not intialize TAS57xx %d", ret);
+	}
+
+	va_end(args);
+}
+
+/****************************************************************************************
+ * TAS57 detection
+ */
+static int tas57_detect(void) {
+	u8_t data, addr[] = {TAS578x, TAS575x};
+	int ret;
+	
+	for (int i = 0; i < sizeof(addr); i++) {
+		i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
+
+		i2c_master_start(i2c_cmd);
+		i2c_master_write_byte(i2c_cmd, addr[i] | I2C_MASTER_WRITE, I2C_MASTER_NACK);
+		i2c_master_write_byte(i2c_cmd, 00, I2C_MASTER_NACK);
+		
+		i2c_master_start(i2c_cmd);			
+		i2c_master_write_byte(i2c_cmd, addr[i] | I2C_MASTER_READ, I2C_MASTER_NACK);
+		i2c_master_read_byte(i2c_cmd, &data, I2C_MASTER_NACK);
+		
+		i2c_master_stop(i2c_cmd);	
+		ret	= i2c_master_cmd_begin(i2c_port, i2c_cmd, 50 / portTICK_RATE_MS);
+		i2c_cmd_link_delete(i2c_cmd);	
+		
+		if (ret == ESP_OK) {
+			LOG_INFO("Detected TAS @0x%x", addr[i]);
+			return addr[i];
+		}	
+	}	
+	
+	return 0;
+}
+

+ 16 - 25
main/Kconfig.projbuild

@@ -62,6 +62,8 @@ menu "Squeezelite-ESP32"
 	            Type of hardware platform
 	        config SQUEEZEAMP 
 	            bool "SqueezeAMP (TAS575x & Bluetooth)"
+			config A1S
+	            bool "ESP32-A1S module"				
 	        config BASIC_I2C_BT
 	            bool "Generic I2S & Bluetooth"
 	    endchoice
@@ -75,7 +77,7 @@ menu "Squeezelite-ESP32"
 		            I2S dma channel to use.  
 		    config I2S_BCK_IO         
 		        int "I2S Bit clock GPIO number. "
-		        default 26
+		        default 33
 		        help
 		            I2S Bit Clock gpio pin to use.  
 		    config I2S_WS_IO         
@@ -85,29 +87,13 @@ menu "Squeezelite-ESP32"
 		            I2S Word Select gpio pin to use.
 		    config I2S_DO_IO         
 		        int "I2S Data I/O GPIO number. "
-		        default 22
+		        default 32
 		        help
 		            I2S data I/O gpio pin to use.
-		    choice 
-	  			prompt "Bit Depth for I2S output"
-		        default I2S_BITS_PER_CHANNEL_16
-		        config I2S_BITS_PER_CHANNEL_24
-		            bool "24 Bits"
-		        config I2S_BITS_PER_CHANNEL_16
-		            bool "16 Bits"
-		        config I2S_BITS_PER_CHANNEL_8
-		            bool "8 Bits"
-		    endchoice
-			config I2S_BITS_PER_CHANNEL
-			    int
-			    default 16
-			    default 16 if I2S_BITS_PER_CHANNEL_16
-			    default 24 if I2S_BITS_PER_CHANNEL_24
-			    default 8 if I2S_BITS_PER_CHANNEL_8
 		endmenu
 		
 		menu "SPDIF settings" 
-		    depends on BASIC_I2C_BT
+		    depends on BASIC_I2C_BT || A1S
 			config SDIF_NUM         
 		        int "SDPIF/I2S channel (0 or 1)"
 		        default 0
@@ -115,19 +101,19 @@ menu "Squeezelite-ESP32"
 		            I2S dma channel to use.  
 		    config SPDIF_BCK_IO         
 		        int "SDPIF/I2S Bit clock GPIO number"
-		        default 26
+		        default -1
 		        help
-		            Not used but must be configured.  
+		            Must be set even if you don't use SPDIF  
 		    config SPDIF_WS_IO         
 		        int "SPDIF/I2S Word Select GPIO number"
-		        default 25
+		        default -1
 		        help
-		            Not used but must be configured.  
+		            Must be set even if you don't use SPDIF  
 		    config SPDIF_DO_IO         
 		        int "I2S Data I/O GPIO number"
-		        default 15
+		        default -1
 		        help
-		            SPDIF/I2S data I/O gpio pin to use
+		            Must be set even if you don't use SPDIF  
 		endmenu
 				
 		menu "A2DP settings"
@@ -217,6 +203,11 @@ menu "Squeezelite-ESP32"
 			default -1
 			help
 				Set to -1 for no LED
+		config JACK_GPIO				
+			int "Jack insertion GPIO"
+			default -1
+			help
+				GPIO to detect speaker jack insertion (0 = inserted). Set to -1 for no detection
 	endmenu	
 	
 endmenu