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
|