|
@@ -0,0 +1,98 @@
|
|
|
+/*
|
|
|
+ * Squeezelite for esp32
|
|
|
+ *
|
|
|
+ * (c) Wizmo 2021
|
|
|
+ * Sebastien 2019
|
|
|
+ * Philippe G. 2019, philippe_44@outlook.com
|
|
|
+ *
|
|
|
+ * This software is released under the MIT License.
|
|
|
+ * https://opensource.org/licenses/MIT
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+#include <freertos/FreeRTOS.h>
|
|
|
+#include <freertos/task.h>
|
|
|
+#include <driver/i2s.h>
|
|
|
+#include "driver/i2c.h"
|
|
|
+#include "esp_log.h"
|
|
|
+#include "adac.h"
|
|
|
+
|
|
|
+static const char TAG[] = "WM8978";
|
|
|
+
|
|
|
+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, i2s_config_t *i2s_config);
|
|
|
+
|
|
|
+static esp_err_t i2c_write_shadow(uint8_t reg, uint16_t val);
|
|
|
+static uint16_t i2c_read_shadow(uint8_t reg);
|
|
|
+
|
|
|
+static int WM8978;
|
|
|
+
|
|
|
+const struct adac_s dac_wm8978 = { "WM8978", init, adac_deinit, power, speaker, headset, volume };
|
|
|
+
|
|
|
+// initiation table for non-readbale 9-bit i2c registers
|
|
|
+static uint16_t WM8978_REGVAL_TBL[58] = {
|
|
|
+ 0X0000, 0X0000, 0X0000, 0X0000, 0X0050, 0X0000, 0X0140, 0X0000,
|
|
|
+ 0X0000, 0X0000, 0X0000, 0X00FF, 0X00FF, 0X0000, 0X0100, 0X00FF,
|
|
|
+ 0X00FF, 0X0000, 0X012C, 0X002C, 0X002C, 0X002C, 0X002C, 0X0000,
|
|
|
+ 0X0032, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000, 0X0000,
|
|
|
+ 0X0038, 0X000B, 0X0032, 0X0000, 0X0008, 0X000C, 0X0093, 0X00E9,
|
|
|
+ 0X0000, 0X0000, 0X0000, 0X0000, 0X0003, 0X0010, 0X0010, 0X0100,
|
|
|
+ 0X0100, 0X0002, 0X0001, 0X0001, 0X0039, 0X0039, 0X0039, 0X0039,
|
|
|
+ 0X0001, 0X0001
|
|
|
+};
|
|
|
+
|
|
|
+/****************************************************************************************
|
|
|
+ * init
|
|
|
+ */
|
|
|
+static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {
|
|
|
+ WM8978 = adac_init(config, i2c_port);
|
|
|
+
|
|
|
+ if (!WM8978) WM8978 = 0x1a;
|
|
|
+ ESP_LOGI(TAG, "WM8978 detected @%d", WM8978);
|
|
|
+
|
|
|
+ // init sequence
|
|
|
+ i2c_write_shadow(0, 0);
|
|
|
+ i2c_write_shadow(4, 16);
|
|
|
+ i2c_write_shadow(6, 0);
|
|
|
+ i2c_write_shadow(10, 8);
|
|
|
+ i2c_write_shadow(43, 16);
|
|
|
+ i2c_write_shadow(49, 102);
|
|
|
+
|
|
|
+ // Configure system clk to GPIO0 for DAC MCLK input
|
|
|
+ ESP_LOGI(TAG, "Configuring MCLK on pin:%d", 0);
|
|
|
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
|
|
|
+ REG_WRITE(PIN_CTRL, 0xFFFFFFF0);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/****************************************************************************************
|
|
|
+ * power
|
|
|
+ */
|
|
|
+static void power(adac_power_e mode) {
|
|
|
+ uint16_t *data, off[] = {0, 0, 0}, on[] = {11, 384, 111};
|
|
|
+ data = (mode == ADAC_STANDBY || mode == ADAC_OFF) ? off : on;
|
|
|
+ i2c_write_shadow(1, data[0]);
|
|
|
+ i2c_write_shadow(2, data[1]);
|
|
|
+ i2c_write_shadow(3, data[2]);
|
|
|
+}
|
|
|
+
|
|
|
+/****************************************************************************************
|
|
|
+ * Write with custom reg/value structure
|
|
|
+ */
|
|
|
+ static esp_err_t i2c_write_shadow(uint8_t reg, uint16_t val) {
|
|
|
+ WM8978_REGVAL_TBL[reg] = val;
|
|
|
+ reg = (reg << 1) | ((val >> 8) & 0x01);
|
|
|
+ val &= 0xff;
|
|
|
+ return adac_write_byte(WM8978, reg, val);
|
|
|
+}
|
|
|
+
|
|
|
+/****************************************************************************************
|
|
|
+ * Return local register value
|
|
|
+ */
|
|
|
+static uint16_t i2c_read_shadow(uint8_t reg) {
|
|
|
+ return WM8978_REGVAL_TBL[reg];
|
|
|
+}
|