diff core/I2c_Codec.cpp @ 0:8a575ba3ab52

Initial commit.
author andrewm
date Fri, 31 Oct 2014 19:10:17 +0100
parents
children c44fa102d02b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/I2c_Codec.cpp	Fri Oct 31 19:10:17 2014 +0100
@@ -0,0 +1,286 @@
+/*
+ * 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)
+{
+	if(writeRegister(0x02, 0x00))	// Codec sample rate register: fs_ref / 1
+		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(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(0x0B, 0x01))	// Audio codec overflow flag register: PLL R = 1
+		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;
+	if(writeRegister(0x0F, 0x20))	// Left ADC PGA gain control: not muted; 0x20 = 16dB
+		return 1;
+	if(writeRegister(0x10, 0x20))	// 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 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 DAC output
+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] = { reg & 0xFF, 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();
+}
+