| 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 	long int PLLCLK_IN=12000000; | 
| giuliomoro@134 | 249 	//	f_{S(ref)} = (PLLCLK_IN × K × R)/(2048 × P) | 
| giuliomoro@182 | 250 	float fs = (PLLCLK_IN/2048.0f) * getPllK()*getPllR()/(float)getPllP(); | 
| giuliomoro@134 | 251 	return fs; | 
| giuliomoro@134 | 252 } | 
| giuliomoro@169 | 253 | 
| giuliomoro@169 | 254 int I2c_Codec::setPga(float newGain, unsigned short int channel){ | 
| giuliomoro@169 | 255 	unsigned short int reg; | 
| giuliomoro@169 | 256 	if(channel == 0) | 
| giuliomoro@169 | 257 		reg = 0x0F; | 
| giuliomoro@169 | 258 	else if(channel == 1) | 
| giuliomoro@169 | 259 		reg =  0x10; | 
| giuliomoro@169 | 260 	else | 
| giuliomoro@169 | 261 		return 1; // error, wrong channel | 
| giuliomoro@169 | 262 	if(newGain > 59.5) | 
| giuliomoro@169 | 263 		return 2; // error, gain out of range | 
| giuliomoro@169 | 264 	unsigned short int value; | 
| giuliomoro@169 | 265 	if(newGain < 0) | 
| giuliomoro@169 | 266 		value = 0b10000000; // PGA is muted | 
| giuliomoro@169 | 267 	else { | 
| giuliomoro@169 | 268 		// gain is adjustable from 0 to 59.5dB in steps of 0.5dB between 0x0 and 0x7f. | 
| giuliomoro@169 | 269 		// Values between 0b01110111 and 0b01111111 are clipped to 59.5dB | 
| giuliomoro@169 | 270 		value = (int)(newGain * 2 + 0.5) & 0x7f; | 
| giuliomoro@169 | 271 	} | 
| giuliomoro@172 | 272 	return writeRegister(reg, value); | 
| giuliomoro@169 | 273 } | 
| giuliomoro@169 | 274 | 
| andrewm@0 | 275 // Set the volume of the DAC output | 
| andrewm@0 | 276 int I2c_Codec::setDACVolume(int halfDbSteps) | 
| andrewm@0 | 277 { | 
| andrewm@0 | 278 	dacVolumeHalfDbs = halfDbSteps; | 
| andrewm@0 | 279 	if(running) | 
| andrewm@0 | 280 		return writeDACVolumeRegisters(false); | 
| andrewm@0 | 281 | 
| andrewm@0 | 282 	return 0; | 
| andrewm@0 | 283 } | 
| andrewm@0 | 284 | 
| giuliomoro@169 | 285 // Set the volume of the ADC input | 
| andrewm@0 | 286 int I2c_Codec::setADCVolume(int halfDbSteps) | 
| andrewm@0 | 287 { | 
| andrewm@0 | 288 	adcVolumeHalfDbs = halfDbSteps; | 
| andrewm@0 | 289 	if(running) | 
| andrewm@0 | 290 		return writeADCVolumeRegisters(false); | 
| andrewm@0 | 291 | 
| andrewm@0 | 292 	return 0; | 
| andrewm@0 | 293 } | 
| andrewm@0 | 294 | 
| andrewm@0 | 295 // Update the DAC volume control registers | 
| andrewm@0 | 296 int I2c_Codec::writeDACVolumeRegisters(bool mute) | 
| andrewm@0 | 297 { | 
| andrewm@0 | 298 	int volumeBits = 0; | 
| andrewm@0 | 299 | 
| andrewm@0 | 300 	if(dacVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale | 
| andrewm@0 | 301 		volumeBits = -dacVolumeHalfDbs; | 
| andrewm@0 | 302 		if(volumeBits > 127) | 
| andrewm@0 | 303 			volumeBits = 127; | 
| andrewm@0 | 304 	} | 
| andrewm@0 | 305 | 
| andrewm@0 | 306 	if(mute) { | 
| andrewm@0 | 307 		if(writeRegister(0x2B, volumeBits | 0x80))	// Left DAC volume control: muted | 
| andrewm@0 | 308 			return 1; | 
| andrewm@0 | 309 		if(writeRegister(0x2C, volumeBits | 0x80))	// Right DAC volume control: muted | 
| andrewm@0 | 310 			return 1; | 
| andrewm@0 | 311 	} | 
| andrewm@0 | 312 	else { | 
| andrewm@0 | 313 		if(writeRegister(0x2B, volumeBits))	// Left DAC volume control: not muted | 
| andrewm@0 | 314 			return 1; | 
| andrewm@0 | 315 		if(writeRegister(0x2C, volumeBits))	// Right DAC volume control: not muted | 
| andrewm@0 | 316 			return 1; | 
| andrewm@0 | 317 	} | 
| andrewm@0 | 318 | 
| andrewm@0 | 319 	return 0; | 
| andrewm@0 | 320 } | 
| andrewm@0 | 321 | 
| andrewm@0 | 322 // Update the ADC volume control registers | 
| andrewm@0 | 323 int I2c_Codec::writeADCVolumeRegisters(bool mute) | 
| andrewm@0 | 324 { | 
| andrewm@0 | 325 	int volumeBits = 0; | 
| andrewm@0 | 326 | 
| andrewm@0 | 327 	// Volume is specified in half-dBs with 0 as full scale | 
| andrewm@0 | 328 	// The codec uses 1.5dB steps so we divide this number by 3 | 
| andrewm@0 | 329 	if(adcVolumeHalfDbs < 0) { | 
| andrewm@0 | 330 		volumeBits = -adcVolumeHalfDbs / 3; | 
| andrewm@0 | 331 		if(volumeBits > 8) | 
| andrewm@0 | 332 			volumeBits = 8; | 
| andrewm@0 | 333 	} | 
| andrewm@0 | 334 | 
| andrewm@0 | 335 	if(mute) { | 
| andrewm@0 | 336 		if(writeRegister(0x13, 0x00))		// Line1L to Left ADC control register: power down | 
| andrewm@0 | 337 			return 1; | 
| andrewm@0 | 338 		if(writeRegister(0x16, 0x00))		// Line1R to Right ADC control register: power down | 
| andrewm@0 | 339 			return 1; | 
| andrewm@0 | 340 	} | 
| andrewm@0 | 341 	else { | 
| andrewm@0 | 342 		if(writeRegister(0x13, 0x7C))	// Line1L disabled; left ADC powered up with soft step | 
| andrewm@0 | 343 			return 1; | 
| andrewm@0 | 344 		if(writeRegister(0x16, 0x7C))	// Line1R disabled; right ADC powered up with soft step | 
| andrewm@0 | 345 			return 1; | 
| andrewm@0 | 346 		if(writeRegister(0x11, (volumeBits << 4) | 0x0F))	// Line2L connected to left ADC | 
| andrewm@0 | 347 			return 1; | 
| andrewm@0 | 348 		if(writeRegister(0x12, volumeBits | 0xF0))		    // Line2R connected to right ADC | 
| andrewm@0 | 349 			return 1; | 
| andrewm@0 | 350 	} | 
| andrewm@0 | 351 | 
| andrewm@0 | 352 	return 0; | 
| andrewm@0 | 353 } | 
| andrewm@0 | 354 | 
| andrewm@0 | 355 // Set the volume of the headphone output | 
| andrewm@0 | 356 int I2c_Codec::setHPVolume(int halfDbSteps) | 
| andrewm@0 | 357 { | 
| andrewm@0 | 358 	hpVolumeHalfDbs = halfDbSteps; | 
| andrewm@0 | 359 	if(running) | 
| andrewm@0 | 360 		return writeHPVolumeRegisters(); | 
| andrewm@0 | 361 | 
| andrewm@0 | 362 	return 0; | 
| andrewm@0 | 363 } | 
| andrewm@0 | 364 | 
| andrewm@0 | 365 | 
| andrewm@0 | 366 // Update the headphone volume control registers | 
| andrewm@0 | 367 int I2c_Codec::writeHPVolumeRegisters() | 
| andrewm@0 | 368 { | 
| andrewm@0 | 369 	int volumeBits = 0; | 
| andrewm@0 | 370 | 
| andrewm@0 | 371 	if(hpVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale | 
| andrewm@0 | 372 		volumeBits = -hpVolumeHalfDbs; | 
| andrewm@0 | 373 		if(volumeBits > 127) | 
| andrewm@0 | 374 			volumeBits = 127; | 
| andrewm@0 | 375 	} | 
| andrewm@0 | 376 | 
| andrewm@0 | 377 	if(writeRegister(0x2F, volumeBits | 0x80)) // DAC_L1 to HPLOUT register: route to HPLOUT, volume 0dB | 
| andrewm@0 | 378 		return 1; | 
| andrewm@0 | 379 	if(writeRegister(0x40, volumeBits | 0x80)) // DAC_R1 to HPROUT register: route to HPROUT, volume 0dB | 
| andrewm@0 | 380 		return 1; | 
| andrewm@0 | 381 | 
| andrewm@0 | 382 	return 0; | 
| andrewm@0 | 383 } | 
| andrewm@0 | 384 | 
| andrewm@0 | 385 // This tells the codec to stop generating audio and mute the outputs | 
| andrewm@0 | 386 int I2c_Codec::stopAudio() | 
| andrewm@0 | 387 { | 
| andrewm@0 | 388 	if(writeDACVolumeRegisters(true))	// Mute the DACs | 
| andrewm@0 | 389 		return 1; | 
| andrewm@0 | 390 	if(writeADCVolumeRegisters(true))	// Mute the ADCs | 
| andrewm@0 | 391 		return 1; | 
| andrewm@0 | 392 | 
| andrewm@0 | 393 	usleep(10000); | 
| andrewm@0 | 394 | 
| andrewm@0 | 395 	if(writeRegister(0x33, 0x0C))		// HPLOUT output level register: muted | 
| andrewm@0 | 396 		return 1; | 
| andrewm@0 | 397 	if(writeRegister(0x41, 0x0C))		// HPROUT output level register: muted | 
| andrewm@0 | 398 		return 1; | 
| andrewm@0 | 399 	if(writeRegister(0x56, 0x08))		// LEFT_LOP output level control: muted | 
| andrewm@0 | 400 		return 1; | 
| andrewm@0 | 401 	if(writeRegister(0x5D, 0x08))		// RIGHT_LOP output level control: muted | 
| andrewm@0 | 402 		return 1; | 
| andrewm@0 | 403 	if(writeRegister(0x25, 0x00))		// DAC power/driver register: power off | 
| andrewm@0 | 404 		return 1; | 
| andrewm@0 | 405 	if(writeRegister(0x03, 0x11))		// PLL register A: disable | 
| andrewm@0 | 406 		return 1; | 
| andrewm@0 | 407 	if(writeRegister(0x01, 0x80))		// Reset codec to defaults | 
| andrewm@0 | 408 		return 1; | 
| andrewm@0 | 409 | 
| andrewm@0 | 410 	running = false; | 
| andrewm@0 | 411 	return 0; | 
| andrewm@0 | 412 } | 
| andrewm@0 | 413 | 
| andrewm@0 | 414 // Write a specific register on the codec | 
| andrewm@0 | 415 int I2c_Codec::writeRegister(unsigned int reg, unsigned int value) | 
| andrewm@0 | 416 { | 
| giuliomoro@216 | 417 	char buf[2] = { static_cast<char>(reg & 0xFF), static_cast<char>(value & 0xFF) }; | 
| andrewm@0 | 418 | 
| andrewm@0 | 419 	if(write(i2C_file, buf, 2) != 2) | 
| andrewm@0 | 420 	{ | 
| andrewm@0 | 421 		cout << "Failed to write register " << reg << " on codec\n"; | 
| andrewm@0 | 422 		return 1; | 
| andrewm@0 | 423 	} | 
| andrewm@0 | 424 | 
| andrewm@0 | 425 	return 0; | 
| andrewm@0 | 426 } | 
| andrewm@0 | 427 | 
| andrewm@0 | 428 | 
| andrewm@0 | 429 int I2c_Codec::readI2C() | 
| andrewm@0 | 430 { | 
| andrewm@0 | 431 	// Nothing to do here, we only write the registers | 
| andrewm@0 | 432 	return 0; | 
| andrewm@0 | 433 } | 
| andrewm@0 | 434 | 
| andrewm@0 | 435 | 
| andrewm@0 | 436 I2c_Codec::~I2c_Codec() | 
| andrewm@0 | 437 { | 
| andrewm@0 | 438 	if(running) | 
| andrewm@0 | 439 		stopAudio(); | 
| andrewm@0 | 440 } | 
| andrewm@0 | 441 |