summaryrefslogtreecommitdiff
path: root/src/drivers/include/dac.hpp
blob: 06808a78a4775531a0c08a80e28ff6ce63a98d74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#pragma once

#include <stdint.h>

#include <functional>
#include <memory>
#include <optional>
#include <utility>

#include "driver/i2s_std.h"
#include "driver/i2s_types.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "result.hpp"
#include "span.hpp"

#include "gpio_expander.hpp"
#include "sys/_stdint.h"

namespace drivers {

/**
 * Interface for a PCM5122PWR DAC, configured over I2C.
 */
class AudioDac {
 public:
  enum Error {
    FAILED_TO_BOOT,
    FAILED_TO_CONFIGURE,
    FAILED_TO_INSTALL_I2S,
  };

  static auto create(GpioExpander* expander)
      -> cpp::result<std::unique_ptr<AudioDac>, Error>;

  AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle);
  ~AudioDac();

  /**
   * Sets the volume on a scale from 0 (loudest) to 254 (quietest). A value of
   * 255 engages the soft mute function.
   */
  void WriteVolume(uint8_t volume);

  enum PowerState {
    POWERDOWN = 0b0,
    WAIT_FOR_CP = 0b1,
    CALIBRATION_1 = 0b10,
    CALIBRATION_2 = 0b11,
    RAMP_UP = 0b100,
    RUN = 0b101,
    SHORT = 0b110,
    RAMP_DOWN = 0b111,
    STANDBY = 0b1000,
  };

  /* Returns the current boot-up status and internal state of the DAC */
  std::pair<bool, PowerState> ReadPowerState();

  enum BitsPerSample {
    BPS_16 = I2S_DATA_BIT_WIDTH_16BIT,
    BPS_24 = I2S_DATA_BIT_WIDTH_24BIT,
    BPS_32 = I2S_DATA_BIT_WIDTH_32BIT,
  };
  enum SampleRate {
    SAMPLE_RATE_44_1 = 44100,
    SAMPLE_RATE_48 = 48000,
  };

  // TODO(jacqueline): worth supporting channels here as well?
  auto Reconfigure(BitsPerSample bps, SampleRate rate) -> void;

  auto WriteData(cpp::span<std::byte> data) -> std::size_t;

  auto Stop() -> void;
  auto LogStatus() -> void;

  // Not copyable or movable.
  AudioDac(const AudioDac&) = delete;
  AudioDac& operator=(const AudioDac&) = delete;

 private:
  GpioExpander* gpio_;
  i2s_chan_handle_t i2s_handle_;

  i2s_std_clk_config_t clock_config_;
  i2s_std_slot_config_t slot_config_;

  /*
   * Pools the power state for up to 10ms, waiting for the given predicate to
   * be true.
   */
  bool WaitForPowerState(std::function<bool(bool, PowerState)> predicate);

  enum Register {
    PAGE_SELECT = 0,
    RESET = 1,
    POWER_MODE = 2,
    DE_EMPHASIS = 7,
    DAC_CLOCK_SOURCE = 14,
    CLOCK_ERRORS = 37,
    I2S_FORMAT = 40,
    DIGITAL_VOLUME_L = 61,
    DIGITAL_VOLUME_R = 62,

    SAMPLE_RATE_DETECTION = 91,
    BCK_DETECTION = 93,
    CLOCK_ERROR_STATE = 94,
    CLOCK_STATUS = 95,
    AUTO_MUTE_STATE = 108,
    SOFT_MUTE_STATE = 114,
    SAMPLE_RATE_STATE = 115,
    DSP_BOOT_POWER_STATE = 118,
  };

  void WriteRegister(Register reg, uint8_t val);
  uint8_t ReadRegister(Register reg);
};

}  // namespace drivers