rockbox/firmware/drivers/audio/tlv320.c
Michael Sevakis 0c7b787398 Straighten out the mad twisted state of sound.c and related areas.
This is going right in since it's long overdue. If anything is goofed,
drop me a line or just tweak it yourself if you know what's wrong. :-)

Make HW/SW codec interface more uniform when emulating HW functionality
on SWCODEC for functions such as "audiohw_set_pitch". The firmware-to-
DSP plumbing is in firmware/drivers/audiohw-swcodec.c. "sound_XXX"
APIs are all in sound.c with none in DSP code any longer.

Reduce number of settings definitions needed by each codec by providing
defaults for common ones like balance, channels and SW tone controls.

Remove need for separate SIM code and tables and add virtual codec header
for hosted targets.

Change-Id: I3f23702bca054fc9bda40f49824ce681bb7f777b
2013-04-15 12:02:05 -04:00

331 lines
9 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 by Christian Gmeiner
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "logf.h"
#include "system.h"
#include "string.h"
#include "audio.h"
#if CONFIG_I2C == I2C_COLDFIRE
#include "i2c-coldfire.h"
#elif CONFIG_I2C == I2C_DM320
#include "i2c-dm320.h"
#endif
#include "audiohw.h"
const struct sound_settings_info audiohw_settings[] = {
[SOUND_VOLUME] = {"dB", 0, 1, -73, 6, -20},
/* HAVE_SW_TONE_CONTROLS */
[SOUND_BASS] = {"dB", 0, 1, -24, 24, 0},
[SOUND_TREBLE] = {"dB", 0, 1, -24, 24, 0},
[SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0},
[SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0},
[SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100},
#ifdef HAVE_RECORDING
[SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 31, 23},
[SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 31, 23},
[SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 1, 1},
#endif
};
/* convert tenth of dB volume (-73..6) to master volume register value */
static int vol_tenthdb2hw(int db)
{
/* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */
/* 1111111 == +6dB (0x7f) */
/* 1111001 == 0dB (0x79) */
/* 0110000 == -73dB (0x30) */
/* 0101111 == mute (0x2f) */
if (db < VOLUME_MIN) {
return 0x2f;
} else {
return((db/10)+73+0x30);
}
}
/* local functions and definations */
#ifndef CREATIVE_ZVx
#define TLV320_ADDR 0x34
#else
#define TLV320_ADDR 0x1A
#endif
static struct tlv320_info
{
int vol_l;
int vol_r;
} tlv320;
/* Shadow registers */
static unsigned tlv320_regs[0xf];
static void tlv320_write_reg(unsigned reg, unsigned value)
{
unsigned char data[2];
/* The register address is the high 7 bits and the data the low 9 bits */
data[0] = (reg << 1) | ((value >> 8) & 1);
data[1] = value;
#if CONFIG_I2C == I2C_COLDFIRE
if (i2c_write(I2C_IFACE_0, TLV320_ADDR, data, 2) != 2)
#elif CONFIG_I2C == I2C_DM320
if (i2c_write(TLV320_ADDR, data, 2) != 0)
#else
#warning Implement tlv320_write_reg()
#endif
{
logf("tlv320 error reg=0x%x", reg);
return;
}
tlv320_regs[reg] = value;
}
static void audiohw_mute(bool mute)
{
unsigned value_dap = tlv320_regs[REG_DAP];
unsigned value_l, value_r;
if (mute)
{
value_l = LHV_LHV(HEADPHONE_MUTE);
value_r = RHV_RHV(HEADPHONE_MUTE);
value_dap |= DAP_DACM;
}
else
{
value_l = LHV_LHV(tlv320.vol_l);
value_r = RHV_RHV(tlv320.vol_r);
if (value_l > HEADPHONE_MUTE || value_r > HEADPHONE_MUTE)
value_dap &= ~DAP_DACM;
}
tlv320_write_reg(REG_LHV, LHV_LZC | value_l);
tlv320_write_reg(REG_RHV, RHV_RZC | value_r);
tlv320_write_reg(REG_DAP, value_dap);
}
/* public functions */
/**
* Init our tlv with default values
*/
void audiohw_init(void)
{
logf("TLV320 init");
memset(tlv320_regs, 0, sizeof(tlv320_regs));
/* Initialize all registers */
/* All ON except OUT, ADC, MIC and LINE */
tlv320_write_reg(REG_PC, PC_OUT | PC_ADC | PC_MIC | PC_LINE);
#ifdef HAVE_RECORDING
audiohw_set_recvol(0, 0, AUDIO_GAIN_MIC);
audiohw_set_recvol(0, 0, AUDIO_GAIN_LINEIN);
#endif
audiohw_mute(true);
tlv320_write_reg(REG_AAP, AAP_DAC | AAP_MICM);
tlv320_write_reg(REG_DAP, 0x00); /* No deemphasis */
#ifndef CREATIVE_ZVx
tlv320_write_reg(REG_DAIF, DAIF_IWL_16 | DAIF_FOR_I2S);
#else
tlv320_write_reg(REG_DAIF, DAIF_IWL_32 | DAIF_FOR_DSP);
#endif
tlv320_write_reg(REG_DIA, DIA_ACT);
audiohw_set_frequency(-1); /* default */
}
/**
* Switch outputs ON
*/
void audiohw_postinit(void)
{
/* All ON except ADC, MIC and LINE */
sleep(HZ);
tlv320_write_reg(REG_PC, PC_ADC | PC_MIC | PC_LINE);
sleep(HZ/4);
audiohw_mute(false);
}
/**
* Sets internal sample rate for DAC and ADC relative to MCLK
* Selection for frequency:
* Fs: tlv: with:
* 11025: 0 = MCLK/2 MCLK/2 SCLK, LRCK: Audio Clk / 16
* 22050: 0 = MCLK/2 MCLK SCLK, LRCK: Audio Clk / 8
* 44100: 1 = MCLK MCLK SCLK, LRCK: Audio Clk / 4 (default)
* 88200: 2 = MCLK*2 MCLK SCLK, LRCK: Audio Clk / 2
*/
void audiohw_set_frequency(int fsel)
{
/* All rates available for 11.2896MHz besides 8.021 */
static const unsigned char values_src[HW_NUM_FREQ] =
{
HW_HAVE_11_([HW_FREQ_11] = (0x8 << 2) | SRC_CLKIN,)
HW_HAVE_22_([HW_FREQ_22] = (0x8 << 2) | SRC_CLKIN,)
HW_HAVE_44_([HW_FREQ_44] = (0x8 << 2),)
HW_HAVE_88_([HW_FREQ_88] = (0xf << 2),)
};
unsigned value_dap, value_pc;
if ((unsigned)fsel >= HW_NUM_FREQ)
fsel = HW_FREQ_DEFAULT;
/* Temporarily turn off the DAC and ADC before switching sample
rates or they don't choose their filters correctly */
value_dap = tlv320_regs[REG_DAP];
value_pc = tlv320_regs[REG_PC];
tlv320_write_reg(REG_DAP, value_dap | DAP_DACM);
tlv320_write_reg(REG_PC, value_pc | PC_DAC | PC_ADC);
tlv320_write_reg(REG_SRC, values_src[fsel]);
tlv320_write_reg(REG_PC, value_pc | PC_DAC);
tlv320_write_reg(REG_PC, value_pc);
tlv320_write_reg(REG_DAP, value_dap);
}
/**
* Sets left and right headphone volume
*
* Left & Right: 48 .. 121 .. 127 => Volume -73dB (mute) .. +0 dB .. +6 dB
*/
void audiohw_set_volume(int vol_l, int vol_r)
{
vol_l = vol_tenthdb2hw(vol_l);
vol_r = vol_tenthdb2hw(vol_r);
unsigned value_dap = tlv320_regs[REG_DAP];
unsigned value_dap_last = value_dap;
unsigned value_l = LHV_LHV(vol_l);
unsigned value_r = RHV_RHV(vol_r);
/* keep track of current setting */
tlv320.vol_l = vol_l;
tlv320.vol_r = vol_r;
if (value_l > HEADPHONE_MUTE || value_r > HEADPHONE_MUTE)
value_dap &= ~DAP_DACM;
else
value_dap |= DAP_DACM;
/* update */
tlv320_write_reg(REG_LHV, LHV_LZC | value_l);
tlv320_write_reg(REG_RHV, RHV_RZC | value_r);
if (value_dap != value_dap_last)
tlv320_write_reg(REG_DAP, value_dap);
}
/**
* Set recording volume
*
* Line in : 0 .. 31 => Volume -34.5 .. +12 dB
* Mic (left): 0 .. 1 => Volume +0, +20 dB
*
*/
#ifdef HAVE_RECORDING
void audiohw_set_recvol(int left, int right, int type)
{
if (type == AUDIO_GAIN_MIC)
{
unsigned value_aap = tlv320_regs[REG_AAP];
if (left)
value_aap |= AAP_MICB; /* Enable mic boost (20dB) */
else
value_aap &= ~AAP_MICB;
tlv320_write_reg(REG_AAP, value_aap);
}
else if (type == AUDIO_GAIN_LINEIN)
{
tlv320_write_reg(REG_LLIV, LLIV_LIV(left));
tlv320_write_reg(REG_RLIV, RLIV_RIV(right));
}
}
#endif
/* Nice shutdown of TLV320 codec */
void audiohw_close(void)
{
audiohw_mute(true);
sleep(HZ/8);
tlv320_write_reg(REG_PC, PC_OFF | PC_CLK | PC_OSC | PC_OUT |
PC_DAC | PC_ADC | PC_MIC | PC_LINE); /* All OFF */
}
#ifdef HAVE_RECORDING
void audiohw_enable_recording(bool source_mic)
{
unsigned value_aap, value_pc;
if (source_mic)
{
/* select MIC and enable mic boost (20 dB) */
value_aap = AAP_DAC | AAP_INSEL | AAP_MICB;
value_pc = PC_LINE; /* power down LINE */
}
else
{
value_aap = AAP_DAC | AAP_MICM;
value_pc = PC_MIC; /* power down MIC */
}
tlv320_write_reg(REG_PC, value_pc);
tlv320_write_reg(REG_AAP, value_aap);
}
void audiohw_disable_recording(void)
{
unsigned value_pc = tlv320_regs[REG_PC];
unsigned value_aap = tlv320_regs[REG_AAP];
value_aap |= AAP_MICM; /* mute MIC */
tlv320_write_reg(REG_PC, value_aap);
value_pc |= PC_ADC | PC_MIC | PC_LINE; /* ADC, MIC and LINE off */
tlv320_write_reg(REG_PC, value_pc);
}
void audiohw_set_monitor(bool enable)
{
unsigned value_aap, value_pc;
if (enable)
{
/* Keep DAC on to allow mixing of voice with analog audio */
value_aap = AAP_DAC | AAP_BYPASS | AAP_MICM;
value_pc = PC_ADC | PC_MIC; /* ADC and MIC off */
}
else
{
value_aap = AAP_DAC | AAP_MICM;
value_pc = PC_ADC | PC_MIC | PC_LINE; /* ADC, MIC and LINE off */
}
tlv320_write_reg(REG_AAP, value_aap);
tlv320_write_reg(REG_PC, value_pc);
}
#endif /* HAVE_RECORDING */