annotate core/I2c_Codec.cpp @ 68:59edd5780fef

Changed d-box code to run cleanly when built on board. Updated Makefile to add ne10 include path on board. Some extra docs in Utilities.h
author andrewm
date Fri, 17 Jul 2015 16:57:08 +0100
parents 8a575ba3ab52
children c44fa102d02b
rev   line source
andrewm@0 1 /*
andrewm@0 2 * I2c_Codec.cpp
andrewm@0 3 *
andrewm@0 4 * Handle writing the registers to the TLV320AIC310x
andrewm@0 5 * series audio codecs, used on the BeagleBone Audio Cape.
andrewm@0 6 * This code is designed to bypass the ALSA driver and
andrewm@0 7 * configure the codec directly in a sensible way. It
andrewm@0 8 * is complemented by code running on the PRU which uses
andrewm@0 9 * the McASP serial port to transfer audio data.
andrewm@0 10 *
andrewm@0 11 * Created on: May 25, 2014
andrewm@0 12 * Author: Andrew McPherson
andrewm@0 13 */
andrewm@0 14
andrewm@0 15 #include "../include/I2c_Codec.h"
andrewm@0 16
andrewm@0 17 I2c_Codec::I2c_Codec()
andrewm@0 18 : running(false), dacVolumeHalfDbs(0), adcVolumeHalfDbs(0), hpVolumeHalfDbs(0)
andrewm@0 19 {}
andrewm@0 20
andrewm@0 21 // This method initialises the audio codec to its default state
andrewm@0 22 int I2c_Codec::initCodec()
andrewm@0 23 {
andrewm@0 24 // Write the reset register of the codec
andrewm@0 25 if(writeRegister(0x01, 0x80)) // Software reset register
andrewm@0 26 {
andrewm@0 27 cout << "Failed to reset codec\n";
andrewm@0 28 return 1;
andrewm@0 29 }
andrewm@0 30
andrewm@0 31 // Wait for codec to process the reset (for safety)
andrewm@0 32 usleep(5000);
andrewm@0 33
andrewm@0 34 return 0;
andrewm@0 35 }
andrewm@0 36
andrewm@0 37 // Tell the codec to start generating audio
andrewm@0 38 // See the TLV320AIC3106 datasheet for full details of the registers
andrewm@0 39 // The dual_rate flag, when true, runs the codec at 88.2kHz; otherwise
andrewm@0 40 // it runs at 44.1kHz
andrewm@0 41 int I2c_Codec::startAudio(int dual_rate)
andrewm@0 42 {
andrewm@0 43 if(writeRegister(0x02, 0x00)) // Codec sample rate register: fs_ref / 1
andrewm@0 44 return 1;
andrewm@0 45 if(writeRegister(0x03, 0x91)) // PLL register A: enable
andrewm@0 46 return 1;
andrewm@0 47 if(writeRegister(0x04, 0x1C)) // PLL register B
andrewm@0 48 return 1;
andrewm@0 49 if(writeRegister(0x05, 0x52)) // PLL register C
andrewm@0 50 return 1;
andrewm@0 51 if(writeRegister(0x06, 0x40)) // PLL register D
andrewm@0 52 return 1;
andrewm@0 53 if(dual_rate) {
andrewm@0 54 if(writeRegister(0x07, 0xEA)) // Codec datapath register: 44.1kHz; dual rate; standard datapath
andrewm@0 55 return 1;
andrewm@0 56 }
andrewm@0 57 else {
andrewm@0 58 if(writeRegister(0x07, 0x8A)) // Codec datapath register: 44.1kHz; std rate; standard datapath
andrewm@0 59 return 1;
andrewm@0 60 }
andrewm@0 61 if(writeRegister(0x08, 0xC0)) // Audio serial control register A: BLCK, WCLK outputs
andrewm@0 62 return 1;
andrewm@0 63 if(writeRegister(0x09, 0x40)) // Audio serial control register B: DSP mode, word len 16 bits
andrewm@0 64 return 1;
andrewm@0 65 if(writeRegister(0x0A, 0x00)) // Audio serial control register C: 0 bit offset
andrewm@0 66 return 1;
andrewm@0 67 if(writeRegister(0x0B, 0x01)) // Audio codec overflow flag register: PLL R = 1
andrewm@0 68 return 1;
andrewm@0 69 if(writeRegister(0x0C, 0x00)) // Digital filter register: disabled
andrewm@0 70 return 1;
andrewm@0 71 if(writeRegister(0x0D, 0x00)) // Headset / button press register A: disabled
andrewm@0 72 return 1;
andrewm@0 73 if(writeRegister(0x0E, 0x00)) // Headset / button press register B: disabled
andrewm@0 74 return 1;
andrewm@0 75 if(writeRegister(0x0F, 0x20)) // Left ADC PGA gain control: not muted; 0x20 = 16dB
andrewm@0 76 return 1;
andrewm@0 77 if(writeRegister(0x10, 0x20)) // Right ADC PGA gain control: not muted; 0x20 = 16dB
andrewm@0 78 return 1;
andrewm@0 79
andrewm@0 80 if(writeRegister(0x25, 0xC0)) // DAC power/driver register: DAC power on (left and right)
andrewm@0 81 return 1;
andrewm@0 82 if(writeRegister(0x26, 0x04)) // High power output driver register: Enable short circuit protection
andrewm@0 83 return 1;
andrewm@0 84 if(writeRegister(0x28, 0x02)) // High power output stage register: disable soft stepping
andrewm@0 85 return 1;
andrewm@0 86
andrewm@0 87 if(writeRegister(0x52, 0x80)) // DAC_L1 to LEFT_LOP volume control: routed, volume 0dB
andrewm@0 88 return 1;
andrewm@0 89 if(writeRegister(0x5C, 0x80)) // DAC_R1 to RIGHT_LOP volume control: routed, volume 0dB
andrewm@0 90 return 1;
andrewm@0 91
andrewm@0 92 if(writeHPVolumeRegisters()) // Send DAC to high-power outputs
andrewm@0 93 return 1;
andrewm@0 94
andrewm@0 95 if(writeRegister(0x66, 0x02)) // Clock generation control register: use MCLK, PLL N = 2
andrewm@0 96 return 1;
andrewm@0 97
andrewm@0 98 if(writeRegister(0x33, 0x0D)) // HPLOUT output level control: output level = 0dB, not muted, powered up
andrewm@0 99 return 1;
andrewm@0 100 if(writeRegister(0x41, 0x0D)) // HPROUT output level control: output level = 0dB, not muted, powered up
andrewm@0 101 return 1;
andrewm@0 102 if(writeRegister(0x56, 0x09)) // LEFT_LOP output level control: 0dB, not muted, powered up
andrewm@0 103 return 1;
andrewm@0 104 if(writeRegister(0x5D, 0x09)) // RIGHT_LOP output level control: 0dB, not muted, powered up
andrewm@0 105 return 1;
andrewm@0 106
andrewm@0 107 if(writeDACVolumeRegisters(false)) // Unmute and set volume
andrewm@0 108 return 1;
andrewm@0 109
andrewm@0 110 if(writeRegister(0x65, 0x00)) // GPIO control register B: disabled; codec uses PLLDIV_OUT
andrewm@0 111 return 1;
andrewm@0 112
andrewm@0 113 if(writeADCVolumeRegisters(false)) // Unmute and set ADC volume
andrewm@0 114 return 1;
andrewm@0 115
andrewm@0 116 running = true;
andrewm@0 117 return 0;
andrewm@0 118 }
andrewm@0 119
andrewm@0 120 // Set the volume of the DAC output
andrewm@0 121 int I2c_Codec::setDACVolume(int halfDbSteps)
andrewm@0 122 {
andrewm@0 123 dacVolumeHalfDbs = halfDbSteps;
andrewm@0 124 if(running)
andrewm@0 125 return writeDACVolumeRegisters(false);
andrewm@0 126
andrewm@0 127 return 0;
andrewm@0 128 }
andrewm@0 129
andrewm@0 130 // Set the volume of the DAC output
andrewm@0 131 int I2c_Codec::setADCVolume(int halfDbSteps)
andrewm@0 132 {
andrewm@0 133 adcVolumeHalfDbs = halfDbSteps;
andrewm@0 134 if(running)
andrewm@0 135 return writeADCVolumeRegisters(false);
andrewm@0 136
andrewm@0 137 return 0;
andrewm@0 138 }
andrewm@0 139
andrewm@0 140 // Update the DAC volume control registers
andrewm@0 141 int I2c_Codec::writeDACVolumeRegisters(bool mute)
andrewm@0 142 {
andrewm@0 143 int volumeBits = 0;
andrewm@0 144
andrewm@0 145 if(dacVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale
andrewm@0 146 volumeBits = -dacVolumeHalfDbs;
andrewm@0 147 if(volumeBits > 127)
andrewm@0 148 volumeBits = 127;
andrewm@0 149 }
andrewm@0 150
andrewm@0 151 if(mute) {
andrewm@0 152 if(writeRegister(0x2B, volumeBits | 0x80)) // Left DAC volume control: muted
andrewm@0 153 return 1;
andrewm@0 154 if(writeRegister(0x2C, volumeBits | 0x80)) // Right DAC volume control: muted
andrewm@0 155 return 1;
andrewm@0 156 }
andrewm@0 157 else {
andrewm@0 158 if(writeRegister(0x2B, volumeBits)) // Left DAC volume control: not muted
andrewm@0 159 return 1;
andrewm@0 160 if(writeRegister(0x2C, volumeBits)) // Right DAC volume control: not muted
andrewm@0 161 return 1;
andrewm@0 162 }
andrewm@0 163
andrewm@0 164 return 0;
andrewm@0 165 }
andrewm@0 166
andrewm@0 167 // Update the ADC volume control registers
andrewm@0 168 int I2c_Codec::writeADCVolumeRegisters(bool mute)
andrewm@0 169 {
andrewm@0 170 int volumeBits = 0;
andrewm@0 171
andrewm@0 172 // Volume is specified in half-dBs with 0 as full scale
andrewm@0 173 // The codec uses 1.5dB steps so we divide this number by 3
andrewm@0 174 if(adcVolumeHalfDbs < 0) {
andrewm@0 175 volumeBits = -adcVolumeHalfDbs / 3;
andrewm@0 176 if(volumeBits > 8)
andrewm@0 177 volumeBits = 8;
andrewm@0 178 }
andrewm@0 179
andrewm@0 180 if(mute) {
andrewm@0 181 if(writeRegister(0x13, 0x00)) // Line1L to Left ADC control register: power down
andrewm@0 182 return 1;
andrewm@0 183 if(writeRegister(0x16, 0x00)) // Line1R to Right ADC control register: power down
andrewm@0 184 return 1;
andrewm@0 185 }
andrewm@0 186 else {
andrewm@0 187 if(writeRegister(0x13, 0x7C)) // Line1L disabled; left ADC powered up with soft step
andrewm@0 188 return 1;
andrewm@0 189 if(writeRegister(0x16, 0x7C)) // Line1R disabled; right ADC powered up with soft step
andrewm@0 190 return 1;
andrewm@0 191 if(writeRegister(0x11, (volumeBits << 4) | 0x0F)) // Line2L connected to left ADC
andrewm@0 192 return 1;
andrewm@0 193 if(writeRegister(0x12, volumeBits | 0xF0)) // Line2R connected to right ADC
andrewm@0 194 return 1;
andrewm@0 195 }
andrewm@0 196
andrewm@0 197 return 0;
andrewm@0 198 }
andrewm@0 199
andrewm@0 200 // Set the volume of the headphone output
andrewm@0 201 int I2c_Codec::setHPVolume(int halfDbSteps)
andrewm@0 202 {
andrewm@0 203 hpVolumeHalfDbs = halfDbSteps;
andrewm@0 204 if(running)
andrewm@0 205 return writeHPVolumeRegisters();
andrewm@0 206
andrewm@0 207 return 0;
andrewm@0 208 }
andrewm@0 209
andrewm@0 210
andrewm@0 211 // Update the headphone volume control registers
andrewm@0 212 int I2c_Codec::writeHPVolumeRegisters()
andrewm@0 213 {
andrewm@0 214 int volumeBits = 0;
andrewm@0 215
andrewm@0 216 if(hpVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale
andrewm@0 217 volumeBits = -hpVolumeHalfDbs;
andrewm@0 218 if(volumeBits > 127)
andrewm@0 219 volumeBits = 127;
andrewm@0 220 }
andrewm@0 221
andrewm@0 222 if(writeRegister(0x2F, volumeBits | 0x80)) // DAC_L1 to HPLOUT register: route to HPLOUT, volume 0dB
andrewm@0 223 return 1;
andrewm@0 224 if(writeRegister(0x40, volumeBits | 0x80)) // DAC_R1 to HPROUT register: route to HPROUT, volume 0dB
andrewm@0 225 return 1;
andrewm@0 226
andrewm@0 227 return 0;
andrewm@0 228 }
andrewm@0 229
andrewm@0 230 // This tells the codec to stop generating audio and mute the outputs
andrewm@0 231 int I2c_Codec::stopAudio()
andrewm@0 232 {
andrewm@0 233 if(writeDACVolumeRegisters(true)) // Mute the DACs
andrewm@0 234 return 1;
andrewm@0 235 if(writeADCVolumeRegisters(true)) // Mute the ADCs
andrewm@0 236 return 1;
andrewm@0 237
andrewm@0 238 usleep(10000);
andrewm@0 239
andrewm@0 240 if(writeRegister(0x33, 0x0C)) // HPLOUT output level register: muted
andrewm@0 241 return 1;
andrewm@0 242 if(writeRegister(0x41, 0x0C)) // HPROUT output level register: muted
andrewm@0 243 return 1;
andrewm@0 244 if(writeRegister(0x56, 0x08)) // LEFT_LOP output level control: muted
andrewm@0 245 return 1;
andrewm@0 246 if(writeRegister(0x5D, 0x08)) // RIGHT_LOP output level control: muted
andrewm@0 247 return 1;
andrewm@0 248 if(writeRegister(0x25, 0x00)) // DAC power/driver register: power off
andrewm@0 249 return 1;
andrewm@0 250 if(writeRegister(0x03, 0x11)) // PLL register A: disable
andrewm@0 251 return 1;
andrewm@0 252 if(writeRegister(0x01, 0x80)) // Reset codec to defaults
andrewm@0 253 return 1;
andrewm@0 254
andrewm@0 255 running = false;
andrewm@0 256 return 0;
andrewm@0 257 }
andrewm@0 258
andrewm@0 259 // Write a specific register on the codec
andrewm@0 260 int I2c_Codec::writeRegister(unsigned int reg, unsigned int value)
andrewm@0 261 {
andrewm@0 262 char buf[2] = { reg & 0xFF, value & 0xFF };
andrewm@0 263
andrewm@0 264 if(write(i2C_file, buf, 2) != 2)
andrewm@0 265 {
andrewm@0 266 cout << "Failed to write register " << reg << " on codec\n";
andrewm@0 267 return 1;
andrewm@0 268 }
andrewm@0 269
andrewm@0 270 return 0;
andrewm@0 271 }
andrewm@0 272
andrewm@0 273
andrewm@0 274 int I2c_Codec::readI2C()
andrewm@0 275 {
andrewm@0 276 // Nothing to do here, we only write the registers
andrewm@0 277 return 0;
andrewm@0 278 }
andrewm@0 279
andrewm@0 280
andrewm@0 281 I2c_Codec::~I2c_Codec()
andrewm@0 282 {
andrewm@0 283 if(running)
andrewm@0 284 stopAudio();
andrewm@0 285 }
andrewm@0 286