Mercurial > hg > beaglert
view core/I2c_Codec.cpp @ 476:838a4a4a8580 prerelease
Removed warnings, now libpd_render can compile with Clang as well
author | Giulio Moro <giuliomoro@yahoo.it> |
---|---|
date | Mon, 20 Jun 2016 21:20:28 +0100 |
parents | 869f5e703844 |
children |
line wrap: on
line source
/* * I2c_Codec.cpp * * Handle writing the registers to the TLV320AIC310x * series audio codecs, used on the BeagleBone Audio Cape. * This code is designed to bypass the ALSA driver and * configure the codec directly in a sensible way. It * is complemented by code running on the PRU which uses * the McASP serial port to transfer audio data. * * Created on: May 25, 2014 * Author: Andrew McPherson */ #include "../include/I2c_Codec.h" I2c_Codec::I2c_Codec() : running(false), dacVolumeHalfDbs(0), adcVolumeHalfDbs(0), hpVolumeHalfDbs(0) {} // This method initialises the audio codec to its default state int I2c_Codec::initCodec() { // Write the reset register of the codec if(writeRegister(0x01, 0x80)) // Software reset register { cout << "Failed to reset codec\n"; return 1; } // Wait for codec to process the reset (for safety) usleep(5000); return 0; } // Tell the codec to start generating audio // See the TLV320AIC3106 datasheet for full details of the registers // The dual_rate flag, when true, runs the codec at 88.2kHz; otherwise // it runs at 44.1kHz int I2c_Codec::startAudio(int dual_rate) { // see datasehet for TLV320AIC3104 from page 44 if(writeRegister(0x02, 0x00)) // Codec sample rate register: fs_ref / 1 return 1; // The sampling frequency is given as f_{S(ref)} = (PLLCLK_IN × K × R)/(2048 × P) // The master clock PLLCLK_IN is 12MHz // K can be varied in intervals of resolution of 0.0001 up to 63.9999 // using P=8 and R=1 gives a resolution of 0.0732421875Hz ( 0.000166% at 44.1kHz) // to obtain Fs=44100 we need to have K=60.2112 if(setPllP(8)) return 1; if(setPllR(1)) return 1; if(setAudioSamplingRate(44100)) //this will automatically find and set K for the given P and R so that Fs=44100 return 1; // if(writeRegister(0x03, 0x91)) // PLL register A: enable // return 1; // if(writeRegister(0x04, 0x1C)) // PLL register B // return 1; // if(writeRegister(0x05, 0x52)) // PLL register C // return 1; // if(writeRegister(0x06, 0x40)) // PLL register D // return 1; // if(writeRegister(0x0B, 0x01)) // Audio codec overflow flag register: PLL R = 1 // return 1; // if(setPllD(5264)) //7.5264 gives 44.1kHz nominal value with a 12MHz master clock // return 1; // if(setPllJ(7)) // return 1; if(dual_rate) { if(writeRegister(0x07, 0xEA)) // Codec datapath register: 44.1kHz; dual rate; standard datapath return 1; } else { if(writeRegister(0x07, 0x8A)) // Codec datapath register: 44.1kHz; std rate; standard datapath return 1; } if(writeRegister(0x08, 0xC0)) // Audio serial control register A: BLCK, WCLK outputs return 1; if(writeRegister(0x09, 0x40)) // Audio serial control register B: DSP mode, word len 16 bits return 1; if(writeRegister(0x0A, 0x00)) // Audio serial control register C: 0 bit offset return 1; if(writeRegister(0x0C, 0x00)) // Digital filter register: disabled return 1; if(writeRegister(0x0D, 0x00)) // Headset / button press register A: disabled return 1; if(writeRegister(0x0E, 0x00)) // Headset / button press register B: disabled return 1; //TODO: why are the next four lines, if uncommented, executed AFTER this method has returned? // if(setPga(16, 0)) // Left ADC PGA gain control: not muted; 16dB // return 1; // if(setPga(16, 1)) // Right ADC PGA gain control: not muted; 16dB // return 1; // if(writeRegister(0x0F, 0b01000000)) // Left ADC PGA gain control: not muted; 0x20 = 16dB // return 1; // if(writeRegister(0x10, 0b0)) // Right ADC PGA gain control: not muted; 0x20 = 16dB // return 1; if(writeRegister(0x25, 0xC0)) // DAC power/driver register: DAC power on (left and right) return 1; if(writeRegister(0x26, 0x04)) // High power output driver register: Enable short circuit protection return 1; if(writeRegister(0x28, 0x02)) // High power output stage register: disable soft stepping return 1; if(writeRegister(0x52, 0x80)) // DAC_L1 to LEFT_LOP volume control: routed, volume 0dB return 1; if(writeRegister(0x5C, 0x80)) // DAC_R1 to RIGHT_LOP volume control: routed, volume 0dB return 1; if(writeHPVolumeRegisters()) // Send DAC to high-power outputs return 1; if(writeRegister(0x66, 0x02)) // Clock generation control register: use MCLK, PLL N = 2 return 1; if(writeRegister(0x33, 0x0D)) // HPLOUT output level control: output level = 0dB, not muted, powered up return 1; if(writeRegister(0x41, 0x0D)) // HPROUT output level control: output level = 0dB, not muted, powered up return 1; if(writeRegister(0x56, 0x09)) // LEFT_LOP output level control: 0dB, not muted, powered up return 1; if(writeRegister(0x5D, 0x09)) // RIGHT_LOP output level control: 0dB, not muted, powered up return 1; if(writeDACVolumeRegisters(false)) // Unmute and set volume return 1; if(writeRegister(0x65, 0x00)) // GPIO control register B: disabled; codec uses PLLDIV_OUT return 1; if(writeADCVolumeRegisters(false)) // Unmute and set ADC volume return 1; running = true; return 0; } //set the numerator multiplier for the PLL int I2c_Codec::setPllK(float k){ short unsigned int j=(int)k; unsigned int d=(int)(0.5+(k-j)*10000); //fractional part, between 0 and 9999 if(setPllJ(j)>0) return 1; if(setPllD(d)>0) return 2; return 0; } //set integer part of the numerator mutliplier of the PLL int I2c_Codec::setPllJ(short unsigned int j){ if(j>=64 || j<1){ return 1; } if(writeRegister(0x04, j<<2)){ // PLL register B: j<<2 printf("I2C error while writing PLL j: %d", j); return 1; } pllJ=j; return 0; } //set fractional part(between 0 and 9999) of the numerator mutliplier of the PLL int I2c_Codec::setPllD(unsigned int d){ if(d >9999) return 1; if(writeRegister(0x05, (d>>6)&255)){ // PLL register C: part 1 : 8 most significant bytes of a 14bit integer printf("I2C error while writing PLL d part 1 : %d", d); return 1; } if(writeRegister(0x06, (d<<2)&255)){ // PLL register D: D=5264, part 2 printf("I2C error while writing PLL d part 2 : %d", d); return 1; } pllD=d; return 0; } //set integer part of the numerator mutliplier of the PLL // int I2c_Codec::setPllP(short unsigned int p){ if(p > 8 || p < 1){ return 1; } short unsigned int bits = 0; bits |= 1 << 7; //this means PLL enabled bits |= 0b0010 << 3; // this is the reset value for Q, which is anyhow unused when PLL is active if (p == 8) // 8 is a special value: PLL P Value 000: P = 8 bits = bits | 0; else bits = bits | p; // other values are written with their binary representation. if(writeRegister(0x03, bits)){ // PLL register B: j<<2 printf("I2C error while writing PLL p: %d", p); return 1; } pllP = p; return 0; } int I2c_Codec::setPllR(unsigned int r){ if(r > 16){ //value out of range return 1; } unsigned int bits = 0; //bits D7-D4 are for ADC and DAC overflow flags and are read only if(r == 16) // 16 is a special value: PLL R Value 0000: R = 16 bits |= 0; else bits |= r; // other values are written with their binary representation. if(writeRegister(0x0B, bits)){ // PLL register B: j<<2 printf("I2C error while writing PLL r: %d", r); return 1; } pllR = r; return 0; } int I2c_Codec::setAudioSamplingRate(float newSamplingRate){ long int PLLCLK_IN=12000000; // f_{S(ref)} = (PLLCLK_IN × K × R)/(2048 × P) float k = ((double)(newSamplingRate * pllP * 2048.0f/(float)pllR)) / PLLCLK_IN ; return (setPllK(k)); } short unsigned int I2c_Codec::getPllJ(){ return pllJ; } unsigned int I2c_Codec::getPllD(){ return pllD; } unsigned int I2c_Codec::getPllR(){ return pllR; } unsigned int I2c_Codec::getPllP(){ return pllP; } float I2c_Codec::getPllK(){ float j=getPllJ(); float d=getPllD(); float k=j+d/10000.0f; return k; } float I2c_Codec::getAudioSamplingRate(){ long int PLLCLK_IN=12000000; // f_{S(ref)} = (PLLCLK_IN × K × R)/(2048 × P) float fs = (PLLCLK_IN/2048.0f) * getPllK()*getPllR()/(float)getPllP(); return fs; } int I2c_Codec::setPga(float newGain, unsigned short int channel){ unsigned short int reg; if(channel == 0) reg = 0x0F; else if(channel == 1) reg = 0x10; else return 1; // error, wrong channel if(newGain > 59.5) return 2; // error, gain out of range unsigned short int value; if(newGain < 0) value = 0b10000000; // PGA is muted else { // gain is adjustable from 0 to 59.5dB in steps of 0.5dB between 0x0 and 0x7f. // Values between 0b01110111 and 0b01111111 are clipped to 59.5dB value = (int)(newGain * 2 + 0.5) & 0x7f; } return writeRegister(reg, value); } // Set the volume of the DAC output int I2c_Codec::setDACVolume(int halfDbSteps) { dacVolumeHalfDbs = halfDbSteps; if(running) return writeDACVolumeRegisters(false); return 0; } // Set the volume of the ADC input int I2c_Codec::setADCVolume(int halfDbSteps) { adcVolumeHalfDbs = halfDbSteps; if(running) return writeADCVolumeRegisters(false); return 0; } // Update the DAC volume control registers int I2c_Codec::writeDACVolumeRegisters(bool mute) { int volumeBits = 0; if(dacVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale volumeBits = -dacVolumeHalfDbs; if(volumeBits > 127) volumeBits = 127; } if(mute) { if(writeRegister(0x2B, volumeBits | 0x80)) // Left DAC volume control: muted return 1; if(writeRegister(0x2C, volumeBits | 0x80)) // Right DAC volume control: muted return 1; } else { if(writeRegister(0x2B, volumeBits)) // Left DAC volume control: not muted return 1; if(writeRegister(0x2C, volumeBits)) // Right DAC volume control: not muted return 1; } return 0; } // Update the ADC volume control registers int I2c_Codec::writeADCVolumeRegisters(bool mute) { int volumeBits = 0; // Volume is specified in half-dBs with 0 as full scale // The codec uses 1.5dB steps so we divide this number by 3 if(adcVolumeHalfDbs < 0) { volumeBits = -adcVolumeHalfDbs / 3; if(volumeBits > 8) volumeBits = 8; } if(mute) { if(writeRegister(0x13, 0x00)) // Line1L to Left ADC control register: power down return 1; if(writeRegister(0x16, 0x00)) // Line1R to Right ADC control register: power down return 1; } else { if(writeRegister(0x13, 0x7C)) // Line1L disabled; left ADC powered up with soft step return 1; if(writeRegister(0x16, 0x7C)) // Line1R disabled; right ADC powered up with soft step return 1; if(writeRegister(0x11, (volumeBits << 4) | 0x0F)) // Line2L connected to left ADC return 1; if(writeRegister(0x12, volumeBits | 0xF0)) // Line2R connected to right ADC return 1; } return 0; } // Set the volume of the headphone output int I2c_Codec::setHPVolume(int halfDbSteps) { hpVolumeHalfDbs = halfDbSteps; if(running) return writeHPVolumeRegisters(); return 0; } // Update the headphone volume control registers int I2c_Codec::writeHPVolumeRegisters() { int volumeBits = 0; if(hpVolumeHalfDbs < 0) { // Volume is specified in half-dBs with 0 as full scale volumeBits = -hpVolumeHalfDbs; if(volumeBits > 127) volumeBits = 127; } if(writeRegister(0x2F, volumeBits | 0x80)) // DAC_L1 to HPLOUT register: route to HPLOUT, volume 0dB return 1; if(writeRegister(0x40, volumeBits | 0x80)) // DAC_R1 to HPROUT register: route to HPROUT, volume 0dB return 1; return 0; } // This tells the codec to stop generating audio and mute the outputs int I2c_Codec::stopAudio() { if(writeDACVolumeRegisters(true)) // Mute the DACs return 1; if(writeADCVolumeRegisters(true)) // Mute the ADCs return 1; usleep(10000); if(writeRegister(0x33, 0x0C)) // HPLOUT output level register: muted return 1; if(writeRegister(0x41, 0x0C)) // HPROUT output level register: muted return 1; if(writeRegister(0x56, 0x08)) // LEFT_LOP output level control: muted return 1; if(writeRegister(0x5D, 0x08)) // RIGHT_LOP output level control: muted return 1; if(writeRegister(0x25, 0x00)) // DAC power/driver register: power off return 1; if(writeRegister(0x03, 0x11)) // PLL register A: disable return 1; if(writeRegister(0x01, 0x80)) // Reset codec to defaults return 1; running = false; return 0; } // Write a specific register on the codec int I2c_Codec::writeRegister(unsigned int reg, unsigned int value) { char buf[2] = { static_cast<char>(reg & 0xFF), static_cast<char>(value & 0xFF) }; if(write(i2C_file, buf, 2) != 2) { cout << "Failed to write register " << reg << " on codec\n"; return 1; } return 0; } int I2c_Codec::readI2C() { // Nothing to do here, we only write the registers return 0; } I2c_Codec::~I2c_Codec() { if(running) stopAudio(); }