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 {
|
giuliomoro@142
|
43 // see datasehet for TLV320AIC3104 from page 44
|
andrewm@0
|
44 if(writeRegister(0x02, 0x00)) // Codec sample rate register: fs_ref / 1
|
andrewm@0
|
45 return 1;
|
giuliomoro@145
|
46 // The sampling frequency is given as f_{S(ref)} = (PLLCLK_IN × K × R)/(2048 × P)
|
giuliomoro@145
|
47 // The master clock PLLCLK_IN is 12MHz
|
giuliomoro@145
|
48 // K can be varied in intervals of resolution of 0.0001 up to 63.9999
|
giuliomoro@145
|
49 // using P=8 and R=1 gives a resolution of 0.0732421875Hz ( 0.000166% at 44.1kHz)
|
giuliomoro@145
|
50 // to obtain Fs=44100 we need to have K=60.2112
|
giuliomoro@145
|
51
|
giuliomoro@145
|
52 if(setPllP(8))
|
giuliomoro@145
|
53 return 1;
|
giuliomoro@145
|
54 if(setPllR(1))
|
giuliomoro@145
|
55 return 1;
|
giuliomoro@145
|
56 if(setAudioSamplingRate(44100)) //this will automatically find and set K for the given P and R so that Fs=44100
|
giuliomoro@145
|
57 return 1;
|
giuliomoro@142
|
58 // if(writeRegister(0x03, 0x91)) // PLL register A: enable
|
giuliomoro@142
|
59 // return 1;
|
giuliomoro@134
|
60 // if(writeRegister(0x04, 0x1C)) // PLL register B
|
giuliomoro@134
|
61 // return 1;
|
giuliomoro@134
|
62 // if(writeRegister(0x05, 0x52)) // PLL register C
|
giuliomoro@134
|
63 // return 1;
|
giuliomoro@134
|
64 // if(writeRegister(0x06, 0x40)) // PLL register D
|
giuliomoro@134
|
65 // return 1;
|
giuliomoro@145
|
66 // if(writeRegister(0x0B, 0x01)) // Audio codec overflow flag register: PLL R = 1
|
giuliomoro@145
|
67 // return 1;
|
giuliomoro@145
|
68
|
giuliomoro@145
|
69 // if(setPllD(5264)) //7.5264 gives 44.1kHz nominal value with a 12MHz master clock
|
giuliomoro@145
|
70 // return 1;
|
giuliomoro@145
|
71 // if(setPllJ(7))
|
giuliomoro@145
|
72 // return 1;
|
andrewm@0
|
73 if(dual_rate) {
|
andrewm@0
|
74 if(writeRegister(0x07, 0xEA)) // Codec datapath register: 44.1kHz; dual rate; standard datapath
|
andrewm@0
|
75 return 1;
|
andrewm@0
|
76 }
|
andrewm@0
|
77 else {
|
andrewm@0
|
78 if(writeRegister(0x07, 0x8A)) // Codec datapath register: 44.1kHz; std rate; standard datapath
|
andrewm@0
|
79 return 1;
|
andrewm@0
|
80 }
|
andrewm@0
|
81 if(writeRegister(0x08, 0xC0)) // Audio serial control register A: BLCK, WCLK outputs
|
andrewm@0
|
82 return 1;
|
andrewm@0
|
83 if(writeRegister(0x09, 0x40)) // Audio serial control register B: DSP mode, word len 16 bits
|
andrewm@0
|
84 return 1;
|
andrewm@0
|
85 if(writeRegister(0x0A, 0x00)) // Audio serial control register C: 0 bit offset
|
andrewm@0
|
86 return 1;
|
andrewm@0
|
87 if(writeRegister(0x0C, 0x00)) // Digital filter register: disabled
|
andrewm@0
|
88 return 1;
|
andrewm@0
|
89 if(writeRegister(0x0D, 0x00)) // Headset / button press register A: disabled
|
andrewm@0
|
90 return 1;
|
andrewm@0
|
91 if(writeRegister(0x0E, 0x00)) // Headset / button press register B: disabled
|
andrewm@0
|
92 return 1;
|
giuliomoro@171
|
93 //TODO: why are the next four lines, if uncommented, executed AFTER this method has returned?
|
giuliomoro@171
|
94 // if(setPga(16, 0)) // Left ADC PGA gain control: not muted; 16dB
|
giuliomoro@171
|
95 // return 1;
|
giuliomoro@171
|
96 // if(setPga(16, 1)) // Right ADC PGA gain control: not muted; 16dB
|
giuliomoro@171
|
97 // return 1;
|
giuliomoro@169
|
98 // if(writeRegister(0x0F, 0b01000000)) // Left ADC PGA gain control: not muted; 0x20 = 16dB
|
giuliomoro@169
|
99 // return 1;
|
giuliomoro@169
|
100 // if(writeRegister(0x10, 0b0)) // Right ADC PGA gain control: not muted; 0x20 = 16dB
|
giuliomoro@169
|
101 // return 1;
|
andrewm@0
|
102 if(writeRegister(0x25, 0xC0)) // DAC power/driver register: DAC power on (left and right)
|
andrewm@0
|
103 return 1;
|
andrewm@0
|
104 if(writeRegister(0x26, 0x04)) // High power output driver register: Enable short circuit protection
|
andrewm@0
|
105 return 1;
|
andrewm@0
|
106 if(writeRegister(0x28, 0x02)) // High power output stage register: disable soft stepping
|
andrewm@0
|
107 return 1;
|
andrewm@0
|
108
|
andrewm@0
|
109 if(writeRegister(0x52, 0x80)) // DAC_L1 to LEFT_LOP volume control: routed, volume 0dB
|
andrewm@0
|
110 return 1;
|
andrewm@0
|
111 if(writeRegister(0x5C, 0x80)) // DAC_R1 to RIGHT_LOP volume control: routed, volume 0dB
|
andrewm@0
|
112 return 1;
|
andrewm@0
|
113
|
andrewm@0
|
114 if(writeHPVolumeRegisters()) // Send DAC to high-power outputs
|
andrewm@0
|
115 return 1;
|
andrewm@0
|
116
|
giuliomoro@142
|
117 if(writeRegister(0x66, 0x02)) // Clock generation control register: use MCLK, PLL N = 2
|
andrewm@0
|
118 return 1;
|
andrewm@0
|
119
|
andrewm@0
|
120 if(writeRegister(0x33, 0x0D)) // HPLOUT output level control: output level = 0dB, not muted, powered up
|
andrewm@0
|
121 return 1;
|
andrewm@0
|
122 if(writeRegister(0x41, 0x0D)) // HPROUT output level control: output level = 0dB, not muted, powered up
|
andrewm@0
|
123 return 1;
|
andrewm@0
|
124 if(writeRegister(0x56, 0x09)) // LEFT_LOP output level control: 0dB, not muted, powered up
|
andrewm@0
|
125 return 1;
|
andrewm@0
|
126 if(writeRegister(0x5D, 0x09)) // RIGHT_LOP output level control: 0dB, not muted, powered up
|
andrewm@0
|
127 return 1;
|
andrewm@0
|
128
|
andrewm@0
|
129 if(writeDACVolumeRegisters(false)) // Unmute and set volume
|
andrewm@0
|
130 return 1;
|
andrewm@0
|
131
|
andrewm@0
|
132 if(writeRegister(0x65, 0x00)) // GPIO control register B: disabled; codec uses PLLDIV_OUT
|
andrewm@0
|
133 return 1;
|
andrewm@0
|
134
|
andrewm@0
|
135 if(writeADCVolumeRegisters(false)) // Unmute and set ADC volume
|
andrewm@0
|
136 return 1;
|
andrewm@0
|
137
|
andrewm@0
|
138 running = true;
|
andrewm@0
|
139 return 0;
|
andrewm@0
|
140 }
|
andrewm@0
|
141
|
giuliomoro@97
|
142 //set the numerator multiplier for the PLL
|
giuliomoro@97
|
143 int I2c_Codec::setPllK(float k){
|
giuliomoro@97
|
144 short unsigned int j=(int)k;
|
giuliomoro@134
|
145 unsigned int d=(int)(0.5+(k-j)*10000); //fractional part, between 0 and 9999
|
giuliomoro@97
|
146 if(setPllJ(j)>0)
|
giuliomoro@97
|
147 return 1;
|
giuliomoro@97
|
148 if(setPllD(d)>0)
|
giuliomoro@97
|
149 return 2;
|
giuliomoro@97
|
150 return 0;
|
giuliomoro@97
|
151 }
|
giuliomoro@97
|
152
|
giuliomoro@97
|
153
|
giuliomoro@97
|
154 //set integer part of the numerator mutliplier of the PLL
|
giuliomoro@97
|
155 int I2c_Codec::setPllJ(short unsigned int j){
|
giuliomoro@97
|
156 if(j>=64 || j<1){
|
giuliomoro@97
|
157 return 1;
|
giuliomoro@97
|
158 }
|
giuliomoro@97
|
159 if(writeRegister(0x04, j<<2)){ // PLL register B: j<<2
|
giuliomoro@97
|
160 printf("I2C error while writing PLL j: %d", j);
|
giuliomoro@97
|
161 return 1;
|
giuliomoro@97
|
162 }
|
giuliomoro@134
|
163 pllJ=j;
|
giuliomoro@97
|
164 return 0;
|
giuliomoro@97
|
165 }
|
giuliomoro@97
|
166
|
giuliomoro@97
|
167 //set fractional part(between 0 and 9999) of the numerator mutliplier of the PLL
|
giuliomoro@97
|
168 int I2c_Codec::setPllD(unsigned int d){
|
giuliomoro@97
|
169 if(d<0 || d>9999)
|
giuliomoro@97
|
170 return 1;
|
giuliomoro@97
|
171 if(writeRegister(0x05, (d>>6)&255)){ // PLL register C: part 1 : 8 most significant bytes of a 14bit integer
|
giuliomoro@97
|
172 printf("I2C error while writing PLL d part 1 : %d", d);
|
giuliomoro@97
|
173 return 1;
|
giuliomoro@97
|
174 }
|
giuliomoro@97
|
175 if(writeRegister(0x06, (d<<2)&255)){ // PLL register D: D=5264, part 2
|
giuliomoro@97
|
176 printf("I2C error while writing PLL d part 2 : %d", d);
|
giuliomoro@97
|
177 return 1;
|
giuliomoro@97
|
178 }
|
giuliomoro@134
|
179 pllD=d;
|
giuliomoro@97
|
180 return 0;
|
giuliomoro@97
|
181 }
|
giuliomoro@134
|
182
|
giuliomoro@142
|
183 //set integer part of the numerator mutliplier of the PLL
|
giuliomoro@142
|
184 //
|
giuliomoro@142
|
185 int I2c_Codec::setPllP(short unsigned int p){
|
giuliomoro@142
|
186 if(p > 8 || p < 1){
|
giuliomoro@142
|
187 return 1;
|
giuliomoro@142
|
188 }
|
giuliomoro@142
|
189 short unsigned int bits = 0;
|
giuliomoro@142
|
190 bits |= 1 << 7; //this means PLL enabled
|
giuliomoro@142
|
191 bits |= 0b0010 << 3; // this is the reset value for Q, which is anyhow unused when PLL is active
|
giuliomoro@142
|
192 if (p == 8) // 8 is a special value: PLL P Value 000: P = 8
|
giuliomoro@142
|
193 bits = bits | 0;
|
giuliomoro@142
|
194 else
|
giuliomoro@142
|
195 bits = bits | p; // other values are written with their binary representation.
|
giuliomoro@142
|
196 if(writeRegister(0x03, bits)){ // PLL register B: j<<2
|
giuliomoro@142
|
197 printf("I2C error while writing PLL p: %d", p);
|
giuliomoro@142
|
198 return 1;
|
giuliomoro@142
|
199 }
|
giuliomoro@142
|
200 pllP = p;
|
giuliomoro@142
|
201 return 0;
|
giuliomoro@142
|
202 }
|
giuliomoro@142
|
203 int I2c_Codec::setPllR(unsigned int r){
|
giuliomoro@142
|
204 if(r > 16){ //value out of range
|
giuliomoro@142
|
205 return 1;
|
giuliomoro@142
|
206 }
|
giuliomoro@142
|
207 unsigned int bits = 0;
|
giuliomoro@142
|
208 //bits D7-D4 are for ADC and DAC overflow flags and are read only
|
giuliomoro@142
|
209 if(r == 16) // 16 is a special value: PLL R Value 0000: R = 16
|
giuliomoro@142
|
210 bits |= 0;
|
giuliomoro@142
|
211 else
|
giuliomoro@142
|
212 bits |= r; // other values are written with their binary representation.
|
giuliomoro@142
|
213 if(writeRegister(0x0B, bits)){ // PLL register B: j<<2
|
giuliomoro@142
|
214 printf("I2C error while writing PLL r: %d", r);
|
giuliomoro@142
|
215 return 1;
|
giuliomoro@142
|
216 }
|
giuliomoro@142
|
217 pllR = r;
|
giuliomoro@142
|
218 return 0;
|
giuliomoro@142
|
219 }
|
giuliomoro@134
|
220 int I2c_Codec::setAudioSamplingRate(float newSamplingRate){
|
giuliomoro@134
|
221 long int PLLCLK_IN=12000000;
|
giuliomoro@134
|
222 // f_{S(ref)} = (PLLCLK_IN × K × R)/(2048 × P)
|
giuliomoro@134
|
223 float k = ((double)(newSamplingRate * pllP * 2048.0f/(float)pllR)) / PLLCLK_IN ;
|
giuliomoro@134
|
224 return (setPllK(k));
|
giuliomoro@134
|
225 }
|
giuliomoro@134
|
226
|
giuliomoro@169
|
227
|
giuliomoro@134
|
228 short unsigned int I2c_Codec::getPllJ(){
|
giuliomoro@134
|
229 return pllJ;
|
giuliomoro@134
|
230 }
|
giuliomoro@134
|
231 unsigned int I2c_Codec::getPllD(){
|
giuliomoro@134
|
232 return pllD;
|
giuliomoro@134
|
233 }
|
giuliomoro@142
|
234 unsigned int I2c_Codec::getPllR(){
|
giuliomoro@142
|
235 return pllR;
|
giuliomoro@142
|
236 }
|
giuliomoro@142
|
237 unsigned int I2c_Codec::getPllP(){
|
giuliomoro@142
|
238 return pllP;
|
giuliomoro@142
|
239 }
|
giuliomoro@134
|
240 float I2c_Codec::getPllK(){
|
giuliomoro@134
|
241 float j=getPllJ();
|
giuliomoro@134
|
242 float d=getPllD();
|
giuliomoro@134
|
243 float k=j+d/10000.0f;
|
giuliomoro@134
|
244 return k;
|
giuliomoro@134
|
245 }
|
giuliomoro@134
|
246
|
giuliomoro@134
|
247 float I2c_Codec::getAudioSamplingRate(){
|
giuliomoro@134
|
248 int pllP=1; //TODO: create get/set for pllP and pllR
|
giuliomoro@134
|
249 int pllR=1;
|
giuliomoro@134
|
250 long int PLLCLK_IN=12000000;
|
giuliomoro@134
|
251 // f_{S(ref)} = (PLLCLK_IN × K × R)/(2048 × P)
|
giuliomoro@134
|
252 float fs = (PLLCLK_IN/2048.0f) * getPllK()*pllR/(float)pllP;
|
giuliomoro@134
|
253 return fs;
|
giuliomoro@134
|
254 }
|
giuliomoro@169
|
255
|
giuliomoro@169
|
256 int I2c_Codec::setPga(float newGain, unsigned short int channel){
|
giuliomoro@169
|
257 unsigned short int reg;
|
giuliomoro@169
|
258 if(channel == 0)
|
giuliomoro@169
|
259 reg = 0x0F;
|
giuliomoro@169
|
260 else if(channel == 1)
|
giuliomoro@169
|
261 reg = 0x10;
|
giuliomoro@169
|
262 else
|
giuliomoro@169
|
263 return 1; // error, wrong channel
|
giuliomoro@169
|
264 if(newGain > 59.5)
|
giuliomoro@169
|
265 return 2; // error, gain out of range
|
giuliomoro@169
|
266 unsigned short int value;
|
giuliomoro@169
|
267 if(newGain < 0)
|
giuliomoro@169
|
268 value = 0b10000000; // PGA is muted
|
giuliomoro@169
|
269 else {
|
giuliomoro@169
|
270 // gain is adjustable from 0 to 59.5dB in steps of 0.5dB between 0x0 and 0x7f.
|
giuliomoro@169
|
271 // Values between 0b01110111 and 0b01111111 are clipped to 59.5dB
|
giuliomoro@169
|
272 value = (int)(newGain * 2 + 0.5) & 0x7f;
|
giuliomoro@169
|
273 }
|
giuliomoro@169
|
274 printf("channel: %d, gain: %f, value: 0x%x, reg: 0x%x\n", channel, newGain, value, reg);
|
giuliomoro@169
|
275 int ret = writeRegister(reg, value);
|
giuliomoro@169
|
276 printf("ret: %d\n", ret);
|
giuliomoro@169
|
277 return ret;
|
giuliomoro@169
|
278 }
|
giuliomoro@169
|
279
|
andrewm@0
|
280 // Set the volume of the DAC output
|
andrewm@0
|
281 int I2c_Codec::setDACVolume(int halfDbSteps)
|
andrewm@0
|
282 {
|
andrewm@0
|
283 dacVolumeHalfDbs = halfDbSteps;
|
andrewm@0
|
284 if(running)
|
andrewm@0
|
285 return writeDACVolumeRegisters(false);
|
andrewm@0
|
286
|
andrewm@0
|
287 return 0;
|
andrewm@0
|
288 }
|
andrewm@0
|
289
|
giuliomoro@169
|
290 // Set the volume of the ADC input
|
andrewm@0
|
291 int I2c_Codec::setADCVolume(int halfDbSteps)
|
andrewm@0
|
292 {
|
andrewm@0
|
293 adcVolumeHalfDbs = halfDbSteps;
|
andrewm@0
|
294 if(running)
|
andrewm@0
|
295 return writeADCVolumeRegisters(false);
|
andrewm@0
|
296
|
andrewm@0
|
297 return 0;
|
andrewm@0
|
298 }
|
andrewm@0
|
299
|
andrewm@0
|
300 // Update the DAC volume control registers
|
andrewm@0
|
301 int I2c_Codec::writeDACVolumeRegisters(bool mute)
|
andrewm@0
|
302 {
|
andrewm@0
|
303 int volumeBits = 0;
|
andrewm@0
|
304
|
andrewm@0
|
305 if(dacVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale
|
andrewm@0
|
306 volumeBits = -dacVolumeHalfDbs;
|
andrewm@0
|
307 if(volumeBits > 127)
|
andrewm@0
|
308 volumeBits = 127;
|
andrewm@0
|
309 }
|
andrewm@0
|
310
|
andrewm@0
|
311 if(mute) {
|
andrewm@0
|
312 if(writeRegister(0x2B, volumeBits | 0x80)) // Left DAC volume control: muted
|
andrewm@0
|
313 return 1;
|
andrewm@0
|
314 if(writeRegister(0x2C, volumeBits | 0x80)) // Right DAC volume control: muted
|
andrewm@0
|
315 return 1;
|
andrewm@0
|
316 }
|
andrewm@0
|
317 else {
|
andrewm@0
|
318 if(writeRegister(0x2B, volumeBits)) // Left DAC volume control: not muted
|
andrewm@0
|
319 return 1;
|
andrewm@0
|
320 if(writeRegister(0x2C, volumeBits)) // Right DAC volume control: not muted
|
andrewm@0
|
321 return 1;
|
andrewm@0
|
322 }
|
andrewm@0
|
323
|
andrewm@0
|
324 return 0;
|
andrewm@0
|
325 }
|
andrewm@0
|
326
|
andrewm@0
|
327 // Update the ADC volume control registers
|
andrewm@0
|
328 int I2c_Codec::writeADCVolumeRegisters(bool mute)
|
andrewm@0
|
329 {
|
andrewm@0
|
330 int volumeBits = 0;
|
andrewm@0
|
331
|
andrewm@0
|
332 // Volume is specified in half-dBs with 0 as full scale
|
andrewm@0
|
333 // The codec uses 1.5dB steps so we divide this number by 3
|
andrewm@0
|
334 if(adcVolumeHalfDbs < 0) {
|
andrewm@0
|
335 volumeBits = -adcVolumeHalfDbs / 3;
|
andrewm@0
|
336 if(volumeBits > 8)
|
andrewm@0
|
337 volumeBits = 8;
|
andrewm@0
|
338 }
|
andrewm@0
|
339
|
andrewm@0
|
340 if(mute) {
|
andrewm@0
|
341 if(writeRegister(0x13, 0x00)) // Line1L to Left ADC control register: power down
|
andrewm@0
|
342 return 1;
|
andrewm@0
|
343 if(writeRegister(0x16, 0x00)) // Line1R to Right ADC control register: power down
|
andrewm@0
|
344 return 1;
|
andrewm@0
|
345 }
|
andrewm@0
|
346 else {
|
andrewm@0
|
347 if(writeRegister(0x13, 0x7C)) // Line1L disabled; left ADC powered up with soft step
|
andrewm@0
|
348 return 1;
|
andrewm@0
|
349 if(writeRegister(0x16, 0x7C)) // Line1R disabled; right ADC powered up with soft step
|
andrewm@0
|
350 return 1;
|
andrewm@0
|
351 if(writeRegister(0x11, (volumeBits << 4) | 0x0F)) // Line2L connected to left ADC
|
andrewm@0
|
352 return 1;
|
andrewm@0
|
353 if(writeRegister(0x12, volumeBits | 0xF0)) // Line2R connected to right ADC
|
andrewm@0
|
354 return 1;
|
andrewm@0
|
355 }
|
andrewm@0
|
356
|
andrewm@0
|
357 return 0;
|
andrewm@0
|
358 }
|
andrewm@0
|
359
|
andrewm@0
|
360 // Set the volume of the headphone output
|
andrewm@0
|
361 int I2c_Codec::setHPVolume(int halfDbSteps)
|
andrewm@0
|
362 {
|
andrewm@0
|
363 hpVolumeHalfDbs = halfDbSteps;
|
andrewm@0
|
364 if(running)
|
andrewm@0
|
365 return writeHPVolumeRegisters();
|
andrewm@0
|
366
|
andrewm@0
|
367 return 0;
|
andrewm@0
|
368 }
|
andrewm@0
|
369
|
andrewm@0
|
370
|
andrewm@0
|
371 // Update the headphone volume control registers
|
andrewm@0
|
372 int I2c_Codec::writeHPVolumeRegisters()
|
andrewm@0
|
373 {
|
andrewm@0
|
374 int volumeBits = 0;
|
andrewm@0
|
375
|
andrewm@0
|
376 if(hpVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale
|
andrewm@0
|
377 volumeBits = -hpVolumeHalfDbs;
|
andrewm@0
|
378 if(volumeBits > 127)
|
andrewm@0
|
379 volumeBits = 127;
|
andrewm@0
|
380 }
|
andrewm@0
|
381
|
andrewm@0
|
382 if(writeRegister(0x2F, volumeBits | 0x80)) // DAC_L1 to HPLOUT register: route to HPLOUT, volume 0dB
|
andrewm@0
|
383 return 1;
|
andrewm@0
|
384 if(writeRegister(0x40, volumeBits | 0x80)) // DAC_R1 to HPROUT register: route to HPROUT, volume 0dB
|
andrewm@0
|
385 return 1;
|
andrewm@0
|
386
|
andrewm@0
|
387 return 0;
|
andrewm@0
|
388 }
|
andrewm@0
|
389
|
andrewm@0
|
390 // This tells the codec to stop generating audio and mute the outputs
|
andrewm@0
|
391 int I2c_Codec::stopAudio()
|
andrewm@0
|
392 {
|
andrewm@0
|
393 if(writeDACVolumeRegisters(true)) // Mute the DACs
|
andrewm@0
|
394 return 1;
|
andrewm@0
|
395 if(writeADCVolumeRegisters(true)) // Mute the ADCs
|
andrewm@0
|
396 return 1;
|
andrewm@0
|
397
|
andrewm@0
|
398 usleep(10000);
|
andrewm@0
|
399
|
andrewm@0
|
400 if(writeRegister(0x33, 0x0C)) // HPLOUT output level register: muted
|
andrewm@0
|
401 return 1;
|
andrewm@0
|
402 if(writeRegister(0x41, 0x0C)) // HPROUT output level register: muted
|
andrewm@0
|
403 return 1;
|
andrewm@0
|
404 if(writeRegister(0x56, 0x08)) // LEFT_LOP output level control: muted
|
andrewm@0
|
405 return 1;
|
andrewm@0
|
406 if(writeRegister(0x5D, 0x08)) // RIGHT_LOP output level control: muted
|
andrewm@0
|
407 return 1;
|
andrewm@0
|
408 if(writeRegister(0x25, 0x00)) // DAC power/driver register: power off
|
andrewm@0
|
409 return 1;
|
andrewm@0
|
410 if(writeRegister(0x03, 0x11)) // PLL register A: disable
|
andrewm@0
|
411 return 1;
|
andrewm@0
|
412 if(writeRegister(0x01, 0x80)) // Reset codec to defaults
|
andrewm@0
|
413 return 1;
|
andrewm@0
|
414
|
andrewm@0
|
415 running = false;
|
andrewm@0
|
416 return 0;
|
andrewm@0
|
417 }
|
andrewm@0
|
418
|
andrewm@0
|
419 // Write a specific register on the codec
|
andrewm@0
|
420 int I2c_Codec::writeRegister(unsigned int reg, unsigned int value)
|
andrewm@0
|
421 {
|
andrewm@0
|
422 char buf[2] = { reg & 0xFF, value & 0xFF };
|
andrewm@0
|
423
|
andrewm@0
|
424 if(write(i2C_file, buf, 2) != 2)
|
andrewm@0
|
425 {
|
andrewm@0
|
426 cout << "Failed to write register " << reg << " on codec\n";
|
andrewm@0
|
427 return 1;
|
andrewm@0
|
428 }
|
andrewm@0
|
429
|
andrewm@0
|
430 return 0;
|
andrewm@0
|
431 }
|
andrewm@0
|
432
|
andrewm@0
|
433
|
andrewm@0
|
434 int I2c_Codec::readI2C()
|
andrewm@0
|
435 {
|
andrewm@0
|
436 // Nothing to do here, we only write the registers
|
andrewm@0
|
437 return 0;
|
andrewm@0
|
438 }
|
andrewm@0
|
439
|
andrewm@0
|
440
|
andrewm@0
|
441 I2c_Codec::~I2c_Codec()
|
andrewm@0
|
442 {
|
andrewm@0
|
443 if(running)
|
andrewm@0
|
444 stopAudio();
|
andrewm@0
|
445 }
|
andrewm@0
|
446
|