dac_es8311.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. /**
  2. * @file dac_es8311.c
  3. * @brief ES8311 stereo audio codec DAC driver
  4. *
  5. * Implements the dac_ops_t interface for the ES8311 via I2C control.
  6. * Designed for the Waveshare ESP32-S3-Touch-LCD-1.54 board.
  7. *
  8. * ES8311 datasheet: https://files.waveshare.com/wiki/common/ES8311.DS.pdf
  9. * Register map sourced from espressif/esp-bsp es8311_reg.h
  10. */
  11. #include "dac_es8311.h"
  12. #include "board_utils.h"
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include "driver/i2c_master.h"
  16. #include "esp_log.h"
  17. #include "freertos/FreeRTOS.h"
  18. #include "freertos/semphr.h"
  19. #include "freertos/task.h"
  20. #include "sdkconfig.h"
  21. #define ES8311_ADDR 0x18
  22. #define I2C_LINE_SPEED 400000
  23. // ES8311 register map (from espressif/esp-bsp es8311_reg.h)
  24. #define ES8311_REG00_RESET 0x00
  25. #define ES8311_REG01_CLK1 0x01 // Clock manager: MCLK source, enables
  26. #define ES8311_REG02_CLK2 0x02 // pre_div / pre_multi
  27. #define ES8311_REG03_CLK3 0x03 // fs_mode / adc_osr
  28. #define ES8311_REG04_CLK4 0x04 // dac_osr
  29. #define ES8311_REG05_CLK5 0x05 // adc_div / dac_div
  30. #define ES8311_REG06_CLK6 0x06 // bclk_div
  31. #define ES8311_REG07_CLK7 0x07 // lrck_h
  32. #define ES8311_REG08_CLK8 0x08 // lrck_l
  33. #define ES8311_REG09_SDPIN \
  34. 0x09 // Serial data port in (I2S format + resolution)
  35. #define ES8311_REG0A_SDPOUT 0x0A // Serial data port out
  36. #define ES8311_REG0D_SYS 0x0D // System: analog power
  37. #define ES8311_REG0E_SYS 0x0E // System: PGA + ADC modulator enable
  38. #define ES8311_REG12_SYS 0x12 // System: DAC power-up
  39. #define ES8311_REG13_SYS 0x13 // System: HP output driver enable
  40. #define ES8311_REG31_DAC_CTRL 0x31 // DAC control: bits [6:5] = mute
  41. #define ES8311_REG32_DAC_VOL \
  42. 0x32 // DAC volume: 0x00=-95.5dB, 0xBF=0dB, 0xFF=+32dB
  43. #define ES8311_REG37_DAC_EQ 0x37 // DAC equalizer bypass
  44. #define ES8311_REG1C_ADC_EQ 0x1C // ADC equalizer bypass / DC cancel
  45. // REG01 bits
  46. #define REG01_MCLK_FROM_BCLK (1 << 7) // derive MCLK internally from BCLK
  47. #define REG01_MCLK_INVERT (1 << 6)
  48. #define REG01_ALL_CLOCKS_ON 0x3F // bits [5:0] enable all six clock gates
  49. // REG09/REG0A resolution bits [4:2]
  50. #define SDP_16BIT (3 << 2) // 0x0C
  51. // REG31 mute bits
  52. #define DAC_MUTE_BITS ((1 << 6) | (1 << 5))
  53. static const char TAG[] = "ES8311 DAC";
  54. // Clock coefficient table (from espressif/esp-bsp es8311.c coeff_div[])
  55. // Fields: mclk, rate, pre_div, pre_multi, adc_div, dac_div, fs_mode,
  56. // lrck_h, lrck_l, bclk_div, adc_osr, dac_osr
  57. typedef struct {
  58. uint32_t mclk;
  59. uint32_t rate;
  60. uint8_t pre_div;
  61. uint8_t pre_multi;
  62. uint8_t adc_div;
  63. uint8_t dac_div;
  64. uint8_t fs_mode;
  65. uint8_t lrck_h;
  66. uint8_t lrck_l;
  67. uint8_t bclk_div;
  68. uint8_t adc_osr;
  69. uint8_t dac_osr;
  70. } es8311_coeff_t;
  71. // Subset covering 44.1 kHz and 48 kHz with common MCLK multiples.
  72. // MCLK = sample_rate * 256 when driven from ESP32 I2S MCLK output.
  73. static const es8311_coeff_t s_coeff_div[] = {
  74. // mclk rate pd pm ad dd fm lh ll bd ao do
  75. {11289600, 44100, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x04, 0x10,
  76. 0x10},
  77. {12288000, 48000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x04, 0x10,
  78. 0x10},
  79. // 256× variants derived from BCLK*8 (no external MCLK pin)
  80. {5644800, 44100, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x04, 0x10,
  81. 0x10},
  82. {6144000, 48000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x04, 0x10,
  83. 0x10},
  84. // 384× for higher quality
  85. {16934400, 44100, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x06, 0x10,
  86. 0x10},
  87. {18432000, 48000, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0xFF, 0x06, 0x10,
  88. 0x10},
  89. };
  90. static uint8_t s_es8311_addr;
  91. static i2c_master_dev_handle_t s_es8311_device = NULL;
  92. static SemaphoreHandle_t s_dac_mutex = NULL;
  93. static esp_err_t es8311_reg_write(uint8_t reg, uint8_t val) {
  94. // The ES8311 shares I2C bus 0 with the CST816S touch controller, which is
  95. // polled concurrently by the LVGL task. Bus contention can cause transient
  96. // ESP_ERR_INVALID_STATE failures, so retry a few times before giving up.
  97. esp_err_t err = ESP_OK;
  98. for (int attempt = 0; attempt < 5; attempt++) {
  99. err = board_i2c_write(s_es8311_device, reg, &val, sizeof(uint8_t));
  100. if (err == ESP_OK) {
  101. return ESP_OK;
  102. }
  103. vTaskDelay(pdMS_TO_TICKS(2));
  104. }
  105. ESP_LOGE(TAG, "write reg 0x%02X=0x%02X failed after retries: %s", reg, val,
  106. esp_err_to_name(err));
  107. return err;
  108. }
  109. static const es8311_coeff_t *find_coeff(uint32_t mclk, uint32_t rate) {
  110. for (size_t i = 0; i < sizeof(s_coeff_div) / sizeof(s_coeff_div[0]); i++) {
  111. if (s_coeff_div[i].mclk == mclk && s_coeff_div[i].rate == rate) {
  112. return &s_coeff_div[i];
  113. }
  114. }
  115. return NULL;
  116. }
  117. static esp_err_t es8311_detect(i2c_master_bus_handle_t bus) {
  118. uint8_t addrs[] = {ES8311_ADDR, (ES8311_ADDR | 1)};
  119. for (int i = 0; i < 2; i++) {
  120. if (ESP_OK == i2c_master_probe(bus, addrs[i], 100)) {
  121. ESP_LOGI(TAG, "Detected ES8311 at 0x%02X", addrs[i]);
  122. return ESP_OK;
  123. }
  124. }
  125. ESP_LOGW(TAG, "No ES8311 detected at 0x%02X or 0x%02X", ES8311_ADDR,
  126. ES8311_ADDR | 1);
  127. return ESP_ERR_NOT_FOUND;
  128. }
  129. static esp_err_t es8311_configure_clocks(uint32_t sample_rate) {
  130. // The ESP32-S3 drives MCLK = sample_rate * 256 on I2S_SCK_IO (GPIO8).
  131. // If CONFIG_I2S_SCK_IO < 0, we must derive MCLK internally from BCLK.
  132. // BCLK = sample_rate * 32 (16-bit stereo), so MCLK-from-BCLK ratio = 8.
  133. #if defined(CONFIG_I2S_SCK_IO) && CONFIG_I2S_SCK_IO >= 0
  134. uint32_t mclk = sample_rate * 256;
  135. uint8_t reg01 = REG01_ALL_CLOCKS_ON; // external MCLK on SCK pin
  136. #else
  137. // No MCLK pin — derive from BCLK internally
  138. uint32_t mclk = sample_rate * 256; // virtual MCLK = BCLK * 8
  139. uint8_t reg01 = REG01_ALL_CLOCKS_ON | REG01_MCLK_FROM_BCLK;
  140. #endif
  141. const es8311_coeff_t *c = find_coeff(mclk, sample_rate);
  142. if (!c) {
  143. ESP_LOGE(TAG, "No clock coeff for mclk=%lu rate=%lu", (unsigned long)mclk,
  144. (unsigned long)sample_rate);
  145. return ESP_ERR_NOT_SUPPORTED;
  146. }
  147. esp_err_t err;
  148. err = es8311_reg_write(ES8311_REG01_CLK1, reg01);
  149. if (err != ESP_OK) {
  150. return err;
  151. }
  152. err = es8311_reg_write(ES8311_REG02_CLK2, (uint8_t)((c->pre_div - 1) << 5) |
  153. (c->pre_multi << 3));
  154. if (err != ESP_OK) {
  155. return err;
  156. }
  157. err = es8311_reg_write(ES8311_REG03_CLK3,
  158. (uint8_t)((c->fs_mode << 6) | c->adc_osr));
  159. if (err != ESP_OK) {
  160. return err;
  161. }
  162. err = es8311_reg_write(ES8311_REG04_CLK4, c->dac_osr);
  163. if (err != ESP_OK) {
  164. return err;
  165. }
  166. err = es8311_reg_write(ES8311_REG05_CLK5,
  167. (uint8_t)(((c->adc_div - 1) << 4) | (c->dac_div - 1)));
  168. if (err != ESP_OK) {
  169. return err;
  170. }
  171. // bclk_div < 19: store (bclk_div - 1); otherwise store bclk_div as-is
  172. uint8_t bclk_reg = (c->bclk_div < 19) ? (c->bclk_div - 1) : c->bclk_div;
  173. err = es8311_reg_write(ES8311_REG06_CLK6, bclk_reg);
  174. if (err != ESP_OK) {
  175. return err;
  176. }
  177. err = es8311_reg_write(ES8311_REG07_CLK7, c->lrck_h);
  178. if (err != ESP_OK) {
  179. return err;
  180. }
  181. err = es8311_reg_write(ES8311_REG08_CLK8, c->lrck_l);
  182. if (err != ESP_OK) {
  183. return err;
  184. }
  185. return ESP_OK;
  186. }
  187. static esp_err_t es8311_init(void *i2c_bus) {
  188. i2c_master_bus_handle_t bus = (i2c_master_bus_handle_t)i2c_bus;
  189. esp_err_t err;
  190. if (s_dac_mutex == NULL) {
  191. s_dac_mutex = xSemaphoreCreateMutex();
  192. if (s_dac_mutex == NULL) {
  193. ESP_LOGE(TAG, "Failed to create DAC mutex");
  194. return ESP_ERR_NO_MEM;
  195. }
  196. }
  197. s_es8311_addr = ES8311_ADDR;
  198. err = es8311_detect(bus);
  199. if (err != ESP_OK) {
  200. ESP_LOGW(TAG, "No ES8311 detected");
  201. return ESP_ERR_NOT_FOUND;
  202. }
  203. err = board_i2c_add_device(bus, s_es8311_addr, I2C_LINE_SPEED,
  204. &s_es8311_device);
  205. if (err != ESP_OK) {
  206. ESP_LOGE(TAG, "Failed to add ES8311 to I2C bus: %s", esp_err_to_name(err));
  207. return err;
  208. }
  209. // Reset: 0x1F → 0x00 → 0x80 (soft reset then normal operation)
  210. es8311_reg_write(ES8311_REG00_RESET, 0x1F);
  211. vTaskDelay(pdMS_TO_TICKS(20));
  212. es8311_reg_write(ES8311_REG00_RESET, 0x00);
  213. vTaskDelay(pdMS_TO_TICKS(20));
  214. err = es8311_reg_write(ES8311_REG00_RESET, 0x80);
  215. if (err != ESP_OK) {
  216. ESP_LOGE(TAG, "Reset failed: %s", esp_err_to_name(err));
  217. return err;
  218. }
  219. vTaskDelay(pdMS_TO_TICKS(50));
  220. // Configure clocks for the output sample rate
  221. uint32_t rate = CONFIG_OUTPUT_SAMPLE_RATE_HZ;
  222. err = es8311_configure_clocks(rate);
  223. if (err != ESP_OK) {
  224. ESP_LOGE(TAG, "Clock config failed: %s", esp_err_to_name(err));
  225. return err;
  226. }
  227. // I2S serial port: 16-bit, I2S (Philips) format, slave mode
  228. // REG09 bits [4:2] = 011 → 16-bit; bits [1:0] = 00 → I2S; bit 6 = 0 → slave
  229. err = es8311_reg_write(ES8311_REG09_SDPIN, SDP_16BIT);
  230. if (err != ESP_OK) {
  231. return err;
  232. }
  233. err = es8311_reg_write(ES8311_REG0A_SDPOUT, SDP_16BIT);
  234. if (err != ESP_OK) {
  235. return err;
  236. }
  237. // Power up analog circuitry
  238. err = es8311_reg_write(ES8311_REG0D_SYS, 0x01);
  239. if (err != ESP_OK) {
  240. return err;
  241. }
  242. // Enable analog PGA and ADC modulator
  243. err = es8311_reg_write(ES8311_REG0E_SYS, 0x02);
  244. if (err != ESP_OK) {
  245. return err;
  246. }
  247. // Power up DAC
  248. err = es8311_reg_write(ES8311_REG12_SYS, 0x00);
  249. if (err != ESP_OK) {
  250. return err;
  251. }
  252. // Enable HP output driver
  253. err = es8311_reg_write(ES8311_REG13_SYS, 0x10);
  254. if (err != ESP_OK) {
  255. return err;
  256. }
  257. // Bypass ADC equalizer, cancel DC offset
  258. err = es8311_reg_write(ES8311_REG1C_ADC_EQ, 0x6A);
  259. if (err != ESP_OK) {
  260. return err;
  261. }
  262. // Bypass DAC equalizer
  263. err = es8311_reg_write(ES8311_REG37_DAC_EQ, 0x08);
  264. if (err != ESP_OK) {
  265. return err;
  266. }
  267. // Default volume: 0 dB
  268. err = es8311_reg_write(ES8311_REG32_DAC_VOL, 0xBF);
  269. if (err != ESP_OK) {
  270. return err;
  271. }
  272. // Unmute DAC: REG31 bits [6:5] = 0
  273. err = es8311_reg_write(ES8311_REG31_DAC_CTRL, 0x00);
  274. if (err != ESP_OK) {
  275. return err;
  276. }
  277. ESP_LOGI(TAG, "ES8311 initialized at %lu Hz", (unsigned long)rate);
  278. return ESP_OK;
  279. }
  280. static esp_err_t es8311_deinit(void) {
  281. if (s_es8311_device) {
  282. esp_err_t err = board_i2c_remove_device(s_es8311_device);
  283. if (err != ESP_OK) {
  284. ESP_LOGE(TAG, "failed to remove from i2c bus: %s", esp_err_to_name(err));
  285. }
  286. s_es8311_device = NULL;
  287. }
  288. if (s_dac_mutex != NULL) {
  289. vSemaphoreDelete(s_dac_mutex);
  290. s_dac_mutex = NULL;
  291. }
  292. return ESP_OK;
  293. }
  294. static void es8311_set_power_mode(dac_power_mode_t mode) {
  295. xSemaphoreTake(s_dac_mutex, portMAX_DELAY);
  296. switch (mode) {
  297. case DAC_POWER_ON:
  298. // Re-run clock configuration now that MCLK is present from the I2S master.
  299. // The ES8311's clock state machine only locks when MCLK is running at the
  300. // time REG01-REG08 are written — it cannot lock if they were written before
  301. // I2S was started.
  302. es8311_configure_clocks(CONFIG_OUTPUT_SAMPLE_RATE_HZ);
  303. es8311_reg_write(ES8311_REG12_SYS, 0x00); // power up DAC
  304. es8311_reg_write(ES8311_REG13_SYS, 0x10); // enable HP output
  305. es8311_reg_write(ES8311_REG31_DAC_CTRL, 0x00); // unmute
  306. ESP_LOGI(TAG, "DAC powered on (clocks reconfigured with MCLK active)");
  307. break;
  308. case DAC_POWER_STANDBY:
  309. es8311_reg_write(ES8311_REG31_DAC_CTRL, DAC_MUTE_BITS); // mute only
  310. break;
  311. case DAC_POWER_OFF:
  312. es8311_reg_write(ES8311_REG31_DAC_CTRL, DAC_MUTE_BITS);
  313. es8311_reg_write(ES8311_REG12_SYS, 0x02); // power down DAC
  314. es8311_reg_write(ES8311_REG13_SYS, 0x00); // disable HP output
  315. break;
  316. default:
  317. ESP_LOGW(TAG, "Unhandled power mode: %d", mode);
  318. break;
  319. }
  320. xSemaphoreGive(s_dac_mutex);
  321. }
  322. static void es8311_set_volume(float volume_airplay_db) {
  323. xSemaphoreTake(s_dac_mutex, portMAX_DELAY);
  324. // Clamp AirPlay input to -30..0 dB
  325. if (volume_airplay_db > 0.0f) {
  326. volume_airplay_db = 0.0f;
  327. }
  328. if (volume_airplay_db < -30.0f) {
  329. volume_airplay_db = -30.0f;
  330. }
  331. // Map AirPlay -30..0 dB below the configured codec ceiling. For example,
  332. // max=-60 maps AirPlay 0 dB to -60 dB and AirPlay -30 dB to -120 dB
  333. // before clamping to the ES8311 register range.
  334. float db = (float)CONFIG_ES8311_MAX_VOLUME + (volume_airplay_db * 2.0f);
  335. if (db < -95.5f) {
  336. db = -95.5f;
  337. }
  338. if (db > 32.0f) {
  339. db = 32.0f;
  340. }
  341. // round(db * 2): multiply by 2 again since each step is 0.5 dB
  342. int steps = (int)(db * 2.0f + (db >= 0.0f ? 0.5f : -0.5f));
  343. uint8_t reg_val = (uint8_t)(0xBF + steps);
  344. ESP_LOGD(TAG, "Volume: %.1f dB AirPlay -> %.1f dB DAC -> reg 0x%02X",
  345. volume_airplay_db, db, reg_val);
  346. es8311_reg_write(ES8311_REG32_DAC_VOL, reg_val);
  347. xSemaphoreGive(s_dac_mutex);
  348. }
  349. static void es8311_on_i2s_started(void) {
  350. es8311_set_power_mode(DAC_POWER_ON);
  351. }
  352. static void es8311_enable_speaker(bool enable) {
  353. xSemaphoreTake(s_dac_mutex, portMAX_DELAY);
  354. if (enable) {
  355. es8311_reg_write(ES8311_REG12_SYS, 0x00);
  356. es8311_reg_write(ES8311_REG13_SYS, 0x10);
  357. es8311_reg_write(ES8311_REG31_DAC_CTRL, 0x00);
  358. } else {
  359. es8311_reg_write(ES8311_REG31_DAC_CTRL, DAC_MUTE_BITS);
  360. }
  361. xSemaphoreGive(s_dac_mutex);
  362. }
  363. static void es8311_enable_line_out(bool enable) {
  364. (void)enable;
  365. ESP_LOGW(TAG, "Line out not supported");
  366. }
  367. const dac_ops_t dac_es8311_ops = {
  368. .init = es8311_init,
  369. .deinit = es8311_deinit,
  370. .set_volume = es8311_set_volume,
  371. .set_power_mode = es8311_set_power_mode,
  372. .on_i2s_started = es8311_on_i2s_started,
  373. .enable_speaker = es8311_enable_speaker,
  374. .enable_line_out = es8311_enable_line_out,
  375. };