From 3092b478e40d2f53df999d787b841f3721008876 Mon Sep 17 00:00:00 2001
From: Sonalux <benjavita-prog@yahoo.com>
Date: Fri, 13 Sep 2024 16:19:21 +0100
Subject: [PATCH] Support for PCM3168

---
 control_pcm3168.cpp | 151 ++++++++++++++++++++++++++++++++++
 control_pcm3168.h   | 192 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 343 insertions(+)
 create mode 100644 control_pcm3168.cpp
 create mode 100644 control_pcm3168.h

diff --git a/control_pcm3168.cpp b/control_pcm3168.cpp
new file mode 100644
index 00000000..10d87c75
--- /dev/null
+++ b/control_pcm3168.cpp
@@ -0,0 +1,151 @@
+/* Audio Library for Teensy 3.X
+ * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
+ *
+ * Development of this audio library was funded by PJRC.COM, LLC by sales of
+ * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
+ * open source software by purchasing Teensy or other PJRC products.
+ *
+ * Permission is hereby granted, 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, development funding 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 <Arduino.h>
+#include "control_pcm3168.h"
+
+uint32_t AudioControlPCM3168::reset(int pin)
+{
+	if (pin > 0) // assume >0 is valid!
+	{
+		pinMode(pin,OUTPUT);
+		digitalWriteFast(pin, 0); // start the reset pulse
+		delayMicroseconds(1); 	  // datasheet says 100ns is enough
+		digitalWriteFast(pin, 1); // end the reset pulse
+		resetTime = micros(); 	  // store when we did it
+		
+		if (0 == resetTime) // unlikely. but
+			resetTime = 1;
+	}
+	else
+		resetTime = 0; // say we didn't do a reset
+	
+	return resetTime; // non-zero if we did a reset
+}
+
+bool AudioControlPCM3168::enable(void)
+{
+	wire->begin();
+	
+	// If class function was used to reset, we can check it was done
+	// long enough ago. If not, we just have to assume all is OK.
+	if (0 != resetTime &&					// we have done a reset, but...
+		micros() - resetTime < RESET_WAIT) 	// ...is it too soon to enable?
+		delayMicroseconds(RESET_WAIT - (micros() - resetTime)); // yes, wait
+		
+	return write(DAC_CONTROL_1, DC1_24_BIT_I2S_TDM)
+		&& write(ADC_CONTROL_1, AC1_24_BIT_I2S_TDM)
+		&& write(DAC_SOFT_MUTE_CONTROL, 0x00)
+		&& write(ADC_SOFT_MUTE_CONTROL, 0x00)
+		&& write(DAC_CONTROL_3, 0x80) // BEN UPDATED TO SET All channels control
+		;
+}
+
+
+bool AudioControlPCM3168::disable(void)
+{
+	// not really disabled, but it is muted
+	return write(DAC_SOFT_MUTE_CONTROL, 0x00)
+		&& write(ADC_SOFT_MUTE_CONTROL, 0x00)
+		;
+}
+
+bool AudioControlPCM3168::volumeInteger(uint32_t n)
+{
+	return write(DAC_ATTENUATION_ALL, n);
+}
+
+
+bool AudioControlPCM3168::volumeInteger(int channel, uint32_t n)
+{
+	bool rv = false;
+	
+	channel--; // API is 1-8, we want 0-7
+	if (channel >= 0 && channel < DAC_CHANNELS)
+		rv = write(DAC_ATTENUATION_BASE + channel, n);
+
+	return rv;
+}
+
+
+bool AudioControlPCM3168::inputLevelInteger(int32_t n)
+{
+	uint8_t levels[ADC_CHANNELS];
+	
+	for (int i=0; i < ADC_CHANNELS;i++)
+		levels[i] = n;
+	
+	return write(ADC_ATTENUATION_BASE, levels, ADC_CHANNELS);
+}
+
+
+bool AudioControlPCM3168::inputLevelInteger(int channel, int32_t n)
+{
+	bool rv = false;
+	
+	channel--; // API is 1-6, we want 0-5
+	if (channel >= 0 && channel < ADC_CHANNELS)
+		rv = write(ADC_ATTENUATION_BASE + channel, n);
+
+	return rv;
+}
+
+
+bool AudioControlPCM3168::invertDAC(uint32_t data)
+{
+	return write(DAC_OUTPUT_PHASE, data); // these bits will invert the signal polarity of their respective DAC channels (1-8)
+}
+
+
+bool AudioControlPCM3168::invertADC(uint32_t data)
+{
+	return write(ADC_INPUT_PHASE, data); // these bits will invert the signal polarity of their respective ADC channels (1-6)
+}
+
+
+bool AudioControlPCM3168::write(uint32_t address, uint32_t data)
+{
+	wire->beginTransmission(i2c_addr);
+	wire->write(address);
+	wire->write(data);
+	return wire->endTransmission() == 0;
+}
+
+
+bool AudioControlPCM3168::write(uint32_t address, const void *data, uint32_t len)
+{
+	wire->beginTransmission(i2c_addr);
+	wire->write(address);
+	const uint8_t *p = (const uint8_t *)data;
+	wire->write(p,len);
+	
+	return wire->endTransmission() == 0;
+}
+
+
+void AudioControlPCM3168::setSingleEndedMode() {
+	write(0x53, 0xFF);
+}
\ No newline at end of file
diff --git a/control_pcm3168.h b/control_pcm3168.h
new file mode 100644
index 00000000..1704a206
--- /dev/null
+++ b/control_pcm3168.h
@@ -0,0 +1,192 @@
+/* Audio Library for Teensy 3.X
+ * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
+ *
+ * Development of this audio library was funded by PJRC.COM, LLC by sales of
+ * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
+ * open source software by purchasing Teensy or other PJRC products.
+ *
+ * Permission is hereby granted, 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, development funding 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 control_pcm3168_h_
+#define control_pcm3168_h_
+
+#include "Wire.h"
+#include "AudioControl.h"
+#include <math.h>
+
+
+/*
+From SBAS452A –SEPTEMBER 2008–REVISED JANUARY 2016
+
+	9.5 Register Maps
+	Table 12. Register Map
+	ADDRESS  DATA
+	DAC HEX   B7     B6     B5     B4     B3     B2     B1     B0
+	64  40  MRST   SRST     —      —      —      —    SRDA1  SRDA0
+	65  41  PSMDA  MSDA2  MSDA1  MSDA0  FMTDA3 FMTDA2 FMTDA1 FMTDA0
+	66  42  OPEDA3 OPEDA2 OPEDA1 OPEDA0 FLT3   FLT2   FLT1   FLT0
+	67  43  REVDA8 REVDA7 REVDA6 REVDA5 REVDA4 REVDA3 REVDA2 REVDA1
+	68  44  MUTDA8 MUTDA7 MUTDA6 MUTDA5 MUTDA4 MUTDA3 MUTDA2 MUTDA1
+	69  45  ZERO8  ZERO7  ZERO6  ZERO5  ZERO4  ZERO3  ZERO2  ZERO1
+	70  46  ATMDDA ATSPDA DEMP1  DEMP0  AZRO2  AZRO1  AZRO0  ZREV
+	71  47  ATDA07 ATDA06 ATDA05 ATDA04 ATDA03 ATDA02 ATDA01 ATDA00
+	72  48  ATDA17 ATDA16 ATDA15 ATDA14 ATDA13 ATDA12 ATDA11 ATDA10
+	73  49  ATDA27 ATDA26 ATDA25 ATDA24 ATDA23 ATDA22 ATDA21 ATDA20
+	74  4A  ATDA37 ATDA36 ATDA35 ATDA34 ATDA33 ATDA32 ATDA31 ATDA30
+	75  4B  ATDA47 ATDA46 ATDA45 ATDA44 ATDA43 ATDA42 ATDA41 ATDA40
+	76  4C  ATDA57 ATDA56 ATDA55 ATDA54 ATDA53 ATDA52 ATDA51 ATDA50
+	77  4D  ATDA67 ATDA66 ATDA65 ATDA64 ATDA63 ATDA62 ATDA61 ATDA60
+	78  4E  ATDA77 ATDA76 ATDA75 ATDA74 ATDA73 ATDA72 ATDA71 ATDA70
+	79  4F  ATDA87 ATDA86 ATDA85 ATDA84 ATDA83 ATDA82 ATDA81 ATDA80
+	80  50    —      —      —      —      —      —    SRAD1  SRAD0
+	81  51    —    MSAD2  MSAD1  MSAD0    —    FMTAD2 FMTAD1 FMTAD0
+	82  52    —    PSVAD2 PSVAD1 PSVAD0   —    BYP2   BYP1   BYP0
+	83  53    —      —    SEAD6  SEAD5  SEAD4  SEAD3  SEAD2  SEAD1
+	84  54    —      —    REVAD6 REVAD5 REVAD4 REVAD3 REVAD2 REVAD1
+	85  55    —      —    MUTAD6 MUTAD5 MUTAD4 MUTAD3 MUTAD2 MUTAD1
+	86  56    —      —    OVF6   OVF5   OVF4   OVF3   OVF2   OVF1
+	87  57  ATMDAD ATSPAD    —      —      —      —      —   OVFP
+	88  58  ATAD07 ATAD06 ATAD05 ATAD04 ATAD03 ATAD02 ATAD01 ATAD00
+	89  59  ATAD17 ATAD16 ATAD15 ATAD14 ATAD13 ATAD12 ATAD11 ATAD10
+	90  5A  ATAD27 ATAD26 ATAD25 ATAD24 ATAD23 ATAD22 ATAD21 ATAD20
+	91  5B  ATAD37 ATAD36 ATAD35 ATAD34 ATAD33 ATAD32 ATAD31 ATAD30
+	92  5C  ATAD47 ATAD46 ATAD45 ATAD44 ATAD43 ATAD42 ATAD41 ATAD40
+	93  5D  ATAD57 ATAD56 ATAD55 ATAD54 ATAD53 ATAD52 ATAD51 ATAD50
+	94  5E  ATAD67 ATAD66 ATAD65 ATAD64 ATAD63 ATAD62 ATAD61 ATAD60
+	
+	
+	Table 13. User-Programmable Mode Control Functions
+	FUNCTION                                               RESET DEFAULT      REGISTER  LABEL
+	Mode control register reset for ADC and DAC operation  Normal operation      64     MRST
+	System reset for ADC and DAC operation                 Normal operation      64     SRST
+	DAC sampling mode selection                            Auto                  64     SRDA[1:0]
+	DAC power-save mode selection                          Power save            65     PSMDA
+	DAC master/slave mode selection                        Slave                 65     MSDA[2:0]
+	DAC audio interface format selection                   I2S                   65     FMTDA[3:0]
+	DAC operation control                                  Normal operation      66     OPEDA[3:0]
+	DAC digital filter roll-off control                    Sharp roll-off        66     FLT[3:0]
+	DAC output phase selection                             Normal                67     REVDA[8:1]
+	DAC soft mute control                                  Mute disabled         68     MUTDA[8:1]
+	DAC zero flag                                          Not detected          69     ZERO[8:1]
+	DAC digital attenuation mode                           Channel independent   70     ATMDDA
+	DAC digital attenuation speed                          N × 2048/fS           70     ATSPDA
+	DAC digital de-emphasis function control               Disabled              70     DEMP[1:0]
+	DAC zero flag function selection                       Independent           70     AZRO[2:0]
+	DAC zero flag polarity selection                       High for detection    70     ZREV
+	DAC digital attenuation level shifting                 0 dB, no attenuation 71–79   ATDAx[7:0]
+	ADC sampling mode selection                            Auto                  80     SRAD[1:0]
+	ADC master/slave mode selection                        Slave                 81     MSAD[2:0]
+	ADC audio interface format selection                   I2S                   81     FMTAD[2:0]
+	ADC power-save control                                 Normal operation      82     PSVAD[2:0]
+	ADC HPF bypass control                           Normal output, HPF enabled  82     BYP[2:0]
+	ADC input configuration control                        Differential          83     SEAD[6:1]
+	ADC input phase selection                              Normal                84     REVAD[6:1]
+	ADC soft mute control                                  Mute disabled         85     MUTAD[6:1]
+	ADC overflow flag                                      Not detected          86     OVF[6:1]
+	ADC digital attenuation mode                           Channel independent   87     ATMDAD
+	ADC digital attenuation speed                          N × 2048/fS           87     ATSPAD
+	ADC overflow flag polarity selection                   High for detection    87     OVFP
+	ADC digital attenuation level setting          0 dB, no gain or attenuation  88–94  ATADx[7:0]
+*/
+
+class AudioControlPCM3168 : public AudioControl
+{
+	const uint8_t I2C_BASE = 0x44;
+	const int ADC_CHANNELS = 6;
+	const int DAC_CHANNELS = 8;
+	const uint32_t RESET_WAIT = 200UL;
+	
+	enum PCM3168reg {RESET_CONTROL = 0x40, DAC_CONTROL_1, DAC_CONTROL_2, 
+					 DAC_OUTPUT_PHASE, DAC_SOFT_MUTE_CONTROL, DAC_ZERO_FLAG, 
+					 DAC_CONTROL_3, DAC_ATTENUATION_ALL, DAC_ATTENUATION_BASE /* 8 registers */,
+					 ADC_SAMPLING_MODE = 0x50, ADC_CONTROL_1, ADC_CONTROL_2,
+					 ADC_INPUT_CONFIGURATION, ADC_INPUT_PHASE, ADC_SOFT_MUTE_CONTROL,
+					 ADC_OVERFLOW_FLAG, ADC_CONTROL_3, 
+					 ADC_ATTENUATION_ALL, ADC_ATTENUATION_BASE /* 6 registers */
+	};
+	const int DC1_24_BIT_I2S_TDM = 0x06; // DAC_CONTROL_1: set 24-bit I²S mode, TDM format
+	const int AC1_24_BIT_I2S_TDM = 0x06; // ADC_CONTROL_1: set 24-bit I²S mode, TDM format
+					 
+public:
+	AudioControlPCM3168(void) 
+		: wire(&Wire), i2c_addr(I2C_BASE), muted(true), 
+		  resetTime(0)
+		{ }
+	void setAddress(uint8_t addr) { i2c_addr = I2C_BASE | (addr & 3); }
+	void setWire(TwoWire& w) { wire = &w; }
+	bool enable(void);
+	bool disable(void);
+	bool volume(float level) 
+	{
+		return volumeInteger(volumebyte(level));
+	}
+	
+	bool inputLevel(float level) {
+		return inputLevelInteger(inputlevelbyte(level));
+	}
+	
+	bool inputSelect(int n) {
+		return (n == 0) ? true : false;
+	}
+	
+	bool volume(int channel, float level) {
+		if (channel < 1 || channel > DAC_CHANNELS) return false;
+		return volumeInteger(channel, volumebyte(level)); 
+	}
+	
+	bool inputLevel(int channel, float level) {
+		if (channel < 1 || channel > DAC_CHANNELS) return false;
+		return inputLevelInteger(channel, inputlevelbyte(level));
+	}
+	
+	bool invertDAC(uint32_t data);
+	bool invertADC(uint32_t data);
+	uint32_t reset(int pin);
+	
+	void setSingleEndedMode();
+
+private:
+	bool volumeInteger(uint32_t n);
+	bool volumeInteger(int channel, uint32_t n);
+	bool inputLevelInteger(int32_t n);
+	bool inputLevelInteger(int chnnel, int32_t n);
+	
+	// convert level to volume byte, Table 21, page 45
+	uint32_t volumebyte(float level) {
+		if (level >= 1.0f) return 255;
+		if (level <= 1.0e-5f) return 54; // below -100dB, mute
+		return 255 + roundf(log10f(level) * 40.0f); // 0.5dB steps: 0dB attenuation is 255
+	}
+	
+	// convert level to input gain, Table 20, page 50
+	int32_t inputlevelbyte(float level) {
+		if (level > 10.0)    return 255;
+		if (level < 1.0e-5f) return  15;
+		return 215 + roundf(log10f(level) * 40.0f);
+	}
+	bool write(uint32_t address, uint32_t data);
+	bool write(uint32_t address, const void *data, uint32_t len);
+	TwoWire* wire;
+	uint8_t i2c_addr;
+	bool muted;
+	uint32_t resetTime; // when we were reset
+};
+
+#endif // control_pcm3168_h_