1 // Copyright 2014 Olivier Gillet. 2 // 3 // Author: Olivier Gillet (ol.gillet@gmail.com) 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 // 23 // See http://creativecommons.org/licenses/MIT/ for more information. 24 // 25 // ----------------------------------------------------------------------------- 26 // 27 // WM8371 Codec support. 28 29 #ifndef ELEMENTS_DRIVERS_CODEC_H_ 30 #define ELEMENTS_DRIVERS_CODEC_H_ 31 32 #include <stm32f4xx_conf.h> 33 34 #include "stmlib/stmlib.h" 35 #include "stmlib/utils/ring_buffer.h" 36 37 namespace elements { 38 39 enum CodecProtocol { 40 CODEC_PROTOCOL_PHILIPS = I2S_Standard_Phillips, 41 CODEC_PROTOCOL_MSB_FIRST = I2S_Standard_MSB, 42 CODEC_PROTOCOL_LSB_FIRST = I2S_Standard_LSB 43 }; 44 45 enum CodecFormat { 46 CODEC_FORMAT_16_BIT = I2S_DataFormat_16b, 47 CODEC_FORMAT_24_BIT = I2S_DataFormat_24b, 48 CODEC_FORMAT_32_BIT = I2S_DataFormat_32b 49 }; 50 51 enum CodecSettings { 52 CODEC_INPUT_0_DB = 0x17, 53 CODEC_HEADPHONES_MUTE = 0x00, 54 CODEC_MIC_BOOST = 0x1, 55 CODEC_MIC_MUTE = 0x2, 56 CODEC_ADC_MIC = 0x4, 57 CODEC_ADC_LINE = 0x0, 58 CODEC_OUTPUT_DAC_ENABLE = 0x10, 59 CODEC_OUTPUT_MONITOR = 0x20, 60 CODEC_DEEMPHASIS_NONE = 0x00, 61 CODEC_DEEMPHASIS_32K = 0x01, 62 CODEC_DEEMPHASIS_44K = 0x02, 63 CODEC_DEEMPHASIS_48K = 0x03, 64 CODEC_SOFT_MUTE = 0x01, 65 CODEC_ADC_HPF = 0x00, 66 67 CODEC_POWER_DOWN_LINE_IN = 0x01, 68 CODEC_POWER_DOWN_MIC = 0x02, 69 CODEC_POWER_DOWN_ADC = 0x04, 70 CODEC_POWER_DOWN_DAC = 0x08, 71 CODEC_POWER_DOWN_LINE_OUT = 0x10, 72 CODEC_POWER_DOWN_OSCILLATOR = 0x20, 73 CODEC_POWER_DOWN_CLOCK_OUTPUT = 0x40, 74 CODEC_POWER_DOWN_EVERYTHING = 0x80, 75 76 CODEC_PROTOCOL_MASK_MSB_FIRST = 0x00, 77 CODEC_PROTOCOL_MASK_LSB_FIRST = 0x01, 78 CODEC_PROTOCOL_MASK_PHILIPS = 0x02, 79 CODEC_PROTOCOL_MASK_DSP = 0x03, 80 81 CODEC_FORMAT_MASK_16_BIT = 0x00 << 2, 82 CODEC_FORMAT_MASK_20_BIT = 0x01 << 2, 83 CODEC_FORMAT_MASK_24_BIT = 0x02 << 2, 84 CODEC_FORMAT_MASK_32_BIT = 0x03 << 2, 85 86 CODEC_FORMAT_LR_SWAP = 0x20, 87 CODEC_FORMAT_MASTER = 0x40, 88 CODEC_FORMAT_SLAVE = 0x00, 89 CODEC_FORMAT_INVERT_CLOCK = 0x80, 90 91 CODEC_RATE_48K_48K = 0x00 << 2, 92 CODEC_RATE_96K_96K = 0x07 << 2, 93 CODEC_RATE_32K_32K = 0x06 << 2, 94 CODEC_RATE_44K_44K = 0x18 << 2, 95 }; 96 97 // Size of an audio chunk in frames (stereo samples). 98 // The DMA buffer is 2 chunks long. 99 const size_t kAudioChunkSize = 16; 100 101 // Size of the additional FIFOs in chunks. This gives an additional layer of 102 // Buffering to make things more robust to jitter (for example if there are 103 // occasional expensive calls in the UI). 104 // Use 0 for using naive double-buffering. 105 const size_t kNumFIFOChunks = 0; 106 107 class Codec { 108 public: Codec()109 Codec() { } ~Codec()110 ~Codec() { } 111 112 typedef struct { short l; short r; } Frame; 113 typedef void (*FillBufferCallback)(Frame* rx, Frame* tx, size_t size); 114 115 bool Init(uint32_t sample_rate, CodecProtocol protocol, CodecFormat format); 116 Start()117 bool Start() { 118 // No callback - the caller is supposed to poll with available() 119 return Start(NULL); 120 } 121 122 bool Start(FillBufferCallback callback); 123 void Stop(); 124 125 void Fill(size_t offset); 126 GetInstance()127 static Codec* GetInstance() { return instance_; } 128 129 // When naive double-buffering is used: 130 // 1. Call available() to know if a half-buffer is available. 131 // 2. Call Grab(&input_ptr, &output_ptr) to retrieve pointer on the 132 // available rx/tx half-buffers, along with their size. available()133 inline bool available() const { 134 return transmitted_ != processed_; 135 } 136 Grab(Frame ** input,Frame ** output)137 inline size_t Grab(Frame** input, Frame** output) { 138 ++processed_; 139 *input = client_rx_; 140 *output = client_tx_; 141 return kAudioChunkSize; 142 } 143 144 // When the extra FIFOs are used: 145 // 1. Call writable() and readable() to know how much data can be read 146 // and written from/to the FIFO. 147 // 2. Call ImmedidateRead() and Overwrite() to read/write from/to the FIFO. writable()148 size_t writable() const { return tx_buffer_.writable(); } readable()149 size_t readable() const { return rx_buffer_.readable(); } ImmediateRead(Frame * destination,size_t size)150 void ImmediateRead(Frame* destination, size_t size) { 151 rx_buffer_.ImmediateRead(destination, size); 152 } 153 Overwrite(Frame * source,size_t size)154 void Overwrite(Frame* source, size_t size) { 155 tx_buffer_.Overwrite(source, size); 156 } 157 158 private: 159 bool InitializeGPIO(); 160 bool InitializeControlInterface(); 161 bool InitializeAudioInterface(uint32_t, CodecProtocol, CodecFormat); 162 163 bool WriteControlRegister(uint8_t address, uint16_t data); 164 bool InitializeCodec(uint32_t, CodecProtocol, CodecFormat); 165 166 bool InitializeDMA(); 167 168 static Codec* instance_; 169 170 bool use_buffering_; 171 size_t transmitted_; 172 size_t processed_; 173 Frame* client_tx_; 174 Frame* client_rx_; 175 176 FillBufferCallback callback_; 177 178 DMA_InitTypeDef dma_init_tx_; 179 DMA_InitTypeDef dma_init_rx_; 180 181 Frame tx_dma_buffer_[kAudioChunkSize * 2]; 182 Frame rx_dma_buffer_[kAudioChunkSize * 2]; 183 stmlib::RingBuffer<Frame, kAudioChunkSize * kNumFIFOChunks> rx_buffer_; 184 stmlib::RingBuffer<Frame, kAudioChunkSize * kNumFIFOChunks> tx_buffer_; 185 186 DISALLOW_COPY_AND_ASSIGN(Codec); 187 }; 188 189 } // namespace elements 190 191 #endif // ELEMENTS_DRIVERS_CODEC_H_ 192