diff options
| author | Marcoen Hirschberg <marcoen@gmail.com> | 2007-04-19 10:46:50 +0000 |
|---|---|---|
| committer | Marcoen Hirschberg <marcoen@gmail.com> | 2007-04-19 10:46:50 +0000 |
| commit | 15dfe87f9606a191707d5c7b3b33b21963510f53 (patch) | |
| tree | 5c31520b84dc60aff6997eac6a7e8b4b3ec1127e /firmware/drivers/audio | |
| parent | f725ef12565a65d46ff2f9bd34eb3e19da7b763e (diff) | |
| download | rockbox-15dfe87f9606a191707d5c7b3b33b21963510f53.zip rockbox-15dfe87f9606a191707d5c7b3b33b21963510f53.tar.gz rockbox-15dfe87f9606a191707d5c7b3b33b21963510f53.tar.bz2 rockbox-15dfe87f9606a191707d5c7b3b33b21963510f53.tar.xz | |
move audio drivers together into a subdir
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13209 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers/audio')
| -rw-r--r-- | firmware/drivers/audio/as3514.c | 211 | ||||
| -rw-r--r-- | firmware/drivers/audio/tlv320.c | 305 | ||||
| -rw-r--r-- | firmware/drivers/audio/uda1380.c | 460 | ||||
| -rw-r--r-- | firmware/drivers/audio/wm8731l.c | 305 | ||||
| -rw-r--r-- | firmware/drivers/audio/wm8758.c | 299 | ||||
| -rw-r--r-- | firmware/drivers/audio/wm8975.c | 318 |
6 files changed, 1898 insertions, 0 deletions
diff --git a/firmware/drivers/audio/as3514.c b/firmware/drivers/audio/as3514.c new file mode 100644 index 0000000..89761fb --- /dev/null +++ b/firmware/drivers/audio/as3514.c @@ -0,0 +1,211 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Driver for AS3514 audio codec + * + * Copyright (c) 2007 Daniel Ankers + * Copyright (c) 2007 Christian Gmeiner + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "cpu.h" +#include "debug.h" +#include "system.h" + +#include "as3514.h" +#include "i2s.h" +#include "i2c-pp.h" + +/* Shadow registers */ +int as3514_regs[0x1D]; + +/* + * little helper method to set register values. + * With the help of as3514_regs, we minimize i2c + * traffic. + */ +static void as3514_write(int reg, int value) +{ + if (pp_i2c_send(AS3514_I2C_ADDR, reg, value) != 2) + { + DEBUGF("as3514 error reg=0x%x", reg); + } + as3514_regs[reg] = value; +} + +/* convert tenth of dB volume to master volume register value */ +int tenthdb2master(int db) +{ + /* +6 to -40.43dB in 1.5dB steps == 32 levels = 5 bits */ + /* 11111 == +6dB (0x1f) = 31) */ + /* 11110 == -4.5dB (0x1e) = 30) */ + /* 00001 == -39dB (0x01) */ + /* 00000 == -40.5dB (0x00) */ + + if (db < VOLUME_MIN) { + return 0x0; + } else if (db >= VOLUME_MAX) { + return 0x1f; + } else { + return((db-VOLUME_MIN)/15); /* VOLUME_MIN is negative */ + } +} + +void audiohw_reset(void); + +/* + * Initialise the PP I2C and I2S. + */ +int audiohw_init(void) +{ + unsigned int i; + + /* reset I2C */ + i2c_init(); + + /* normal outputs for CDI and I2S pin groups */ + DEV_INIT &= ~0x300; + + /*mini2?*/ + outl(inl(0x70000010) & ~0x3000000, 0x70000010); + /*mini2?*/ + + /* device reset */ + DEV_RS |= 0x800; + DEV_RS &=~0x800; + + /* device enable */ + DEV_EN |= 0x807; + + /* enable external dev clock clocks */ + DEV_EN |= 0x2; + + /* external dev clock to 24MHz */ + outl(inl(0x70000018) & ~0xc, 0x70000018); + + i2s_reset(); + + /* Set ADC off, mixer on, DAC on, line out off, line in off, mic off */ + as3514_write(AUDIOSET1, 0x20); /* Turn on DAC */ + as3514_write(AUDIOSET3, 0x5); /* Set HPCM off, ZCU off*/ + as3514_write(HPH_OUT_R, 0xc0 | 0x16); /* set vol and set speaker over-current to 0 */ + as3514_write(HPH_OUT_L, 0x16); /* set default vol for headphone */ + as3514_write(PLLMODE, 0x04); + + /* read all reg values */ + for (i = 0; i < sizeof(as3514_regs); i++) + { + as3514_regs[i] = i2c_readbyte(AS3514_I2C_ADDR, i); + } + + return 0; +} + +void audiohw_postinit(void) +{ +} + +/* Silently enable / disable audio output */ +void audiohw_enable_output(bool enable) +{ + int curr; + curr = as3514_regs[HPH_OUT_L]; + + if (enable) + { + /* reset the I2S controller into known state */ + i2s_reset(); + + as3514_write(HPH_OUT_L, curr | 0x40); /* power on */ + audiohw_mute(0); + } else { + audiohw_mute(1); + as3514_write(HPH_OUT_L, curr & ~(0x40)); /* power off */ + } +} + +int audiohw_set_master_vol(int vol_l, int vol_r) +{ + vol_l &= 0x1f; + vol_r &= 0x1f; + + /* we are controling dac volume instead of headphone volume, + as the volume is bigger. + HDP: 1.07 dB gain + DAC: 6 dB gain + */ + as3514_write(DAC_R, vol_r); + as3514_write(DAC_L, 0x40 | vol_l); + + return 0; +} + +int audiohw_set_lineout_vol(int vol_l, int vol_r) +{ + as3514_write(LINE_OUT_R, vol_r); + as3514_write(LINE_OUT_L, 0x40 | vol_l); + + return 0; +} + +int audiohw_mute(int mute) +{ + int curr; + curr = as3514_regs[HPH_OUT_L]; + + if (mute) + { + as3514_write(HPH_OUT_L, curr | 0x80); + } else { + as3514_write(HPH_OUT_L, curr & ~(0x80)); + } + + return 0; +} + +/* Nice shutdown of WM8758 codec */ +void audiohw_close(void) +{ + /* mute headphones */ + audiohw_mute(1); + + /* turn off everything */ + as3514_write(AUDIOSET1, 0x0); +} + +void audiohw_set_sample_rate(int sampling_control) +{ + (void)sampling_control; +} + +void audiohw_enable_recording(bool source_mic) +{ + (void)source_mic; +} + +void audiohw_disable_recording(void) +{ +} + +void audiohw_set_recvol(int left, int right, int type) +{ + (void)left; + (void)right; + (void)type; +} + +void audiohw_set_monitor(int enable) +{ + (void)enable; +} diff --git a/firmware/drivers/audio/tlv320.c b/firmware/drivers/audio/tlv320.c new file mode 100644 index 0000000..3e27d22 --- /dev/null +++ b/firmware/drivers/audio/tlv320.c @@ -0,0 +1,305 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 by Christian Gmeiner + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "lcd.h" +#include "cpu.h" +#include "kernel.h" +#include "thread.h" +#include "power.h" +#include "logf.h" +#include "system.h" +#include "sprintf.h" +#include "button.h" +#include "string.h" +#include "file.h" +#include "buffer.h" +#include "audio.h" + +#include "i2c-coldfire.h" +#include "tlv320.h" + +/* convert tenth of dB volume (-840..0) to master volume register value */ +int tenthdb2master(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 */ +#define TLV320_ADDR 0x34 + +struct tlv320_info +{ + int vol_l; + int vol_r; +} tlv320; + +/* Shadow registers */ +unsigned tlv320_regs[0xf]; + +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 (i2c_write(I2C_IFACE_0, TLV320_ADDR, data, 2) != 2) + { + logf("tlv320 error reg=0x%x", reg); + return; + } + + tlv320_regs[reg] = value; +} + +/* public functions */ + +/** + * Init our tlv with default values + */ +void audiohw_init(void) +{ + 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); + audiohw_set_recvol(0, 0, AUDIO_GAIN_MIC); + audiohw_set_recvol(0, 0, AUDIO_GAIN_LINEIN); + audiohw_mute(true); + tlv320_write_reg(REG_AAP, AAP_DAC | AAP_MICM); + tlv320_write_reg(REG_DAP, 0x00); /* No deemphasis */ + tlv320_write_reg(REG_DAIF, DAIF_IWL_16 | DAIF_FOR_I2S); + 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); +} + +/** + * Resets tlv320 to default values + */ +void audiohw_reset(void) +{ + tlv320_write_reg(REG_RR, RR_RESET); +} + +/** + * 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(unsigned fsel) +{ + /* All rates available for 11.2896MHz besides 8.021 */ + unsigned char values_src[3] = + { + /* Fs: */ + (0x8 << 2) | SRC_CLKIN, /* 11025, 22050 */ + (0x8 << 2), /* 44100 */ + (0xf << 2), /* 88200 */ + }; + + unsigned value_dap, value_pc; + + if (fsel >= ARRAYLEN(values_src)) + fsel = 1; + + /* 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_headphone_vol(int vol_l, int 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 + * + */ +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)); + } +} + +/** + * Mute (mute=true) or enable sound (mute=false) + * + */ +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); +} + +/* 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 */ +} + +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); +} diff --git a/firmware/drivers/audio/uda1380.c b/firmware/drivers/audio/uda1380.c new file mode 100644 index 0000000..6984427 --- /dev/null +++ b/firmware/drivers/audio/uda1380.c @@ -0,0 +1,460 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2005 by Andy Young + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "lcd.h" +#include "cpu.h" +#include "kernel.h" +#include "thread.h" +#include "power.h" +#include "debug.h" +#include "system.h" +#include "sprintf.h" +#include "button.h" +#include "string.h" +#include "file.h" +#include "buffer.h" +#include "audio.h" +#include "logf.h" + +#include "i2c-coldfire.h" +#include "uda1380.h" +#include "pcf50606.h" + +/* convert tenth of dB volume (-840..0) to master volume register value */ +int tenthdb2master(int db) +{ + if (db < -720) /* 1.5 dB steps */ + return (2940 - db) / 15; + else if (db < -660) /* 0.75 dB steps */ + return (1110 - db) * 2 / 15; + else if (db < -520) /* 0.5 dB steps */ + return (520 - db) / 5; + else /* 0.25 dB steps */ + return -db * 2 / 5; +} + +/* convert tenth of dB volume (-780..0) to mixer volume register value */ +int tenthdb2mixer(int db) +{ + if (db < -660) /* 1.5 dB steps */ + return (2640 - db) / 15; + else if (db < -600) /* 0.75 dB steps */ + return (990 - db) * 2 / 15; + else if (db < -460) /* 0.5 dB steps */ + return (460 - db) / 5; + else /* 0.25 dB steps */ + return -db * 2 / 5; +} + +/* ------------------------------------------------- */ +/* Local functions and variables */ +/* ------------------------------------------------- */ + +int uda1380_write_reg(unsigned char reg, unsigned short value); +unsigned short uda1380_regs[0x30]; +short recgain_mic; +short recgain_line; + +/* Definition of a playback configuration to start with */ + +#define NUM_DEFAULT_REGS 13 +unsigned short uda1380_defaults[2*NUM_DEFAULT_REGS] = +{ + REG_0, EN_DAC | EN_INT | EN_DEC | ADC_CLK | DAC_CLK | + SYSCLK_256FS | WSPLL_25_50, + REG_I2S, I2S_IFMT_IIS, + REG_PWR, PON_PLL | PON_BIAS, + /* PON_HP & PON_DAC is enabled later */ + REG_AMIX, AMIX_RIGHT(0x3f) | AMIX_LEFT(0x3f), + /* 00=max, 3f=mute */ + REG_MASTER_VOL, MASTER_VOL_LEFT(0x20) | MASTER_VOL_RIGHT(0x20), + /* 00=max, ff=mute */ + REG_MIX_VOL, MIX_VOL_CH_1(0) | MIX_VOL_CH_2(0xff), + /* 00=max, ff=mute */ + REG_EQ, EQ_MODE_MAX, + /* Bass and treble = 0 dB */ + REG_MUTE, MUTE_MASTER | MUTE_CH2, + /* Mute everything to start with */ + REG_MIX_CTL, MIX_CTL_MIX, + /* Enable mixer */ + REG_DEC_VOL, 0, + REG_PGA, MUTE_ADC, + REG_ADC, SKIP_DCFIL, + REG_AGC, 0 +}; + + + +/* Returns 0 if register was written or -1 if write failed */ +int uda1380_write_reg(unsigned char reg, unsigned short value) +{ + unsigned char data[3]; + + data[0] = reg; + data[1] = value >> 8; + data[2] = value & 0xff; + + if (i2c_write(I2C_IFACE_0, UDA1380_ADDR, data, 3) != 3) + { + DEBUGF("uda1380 error reg=0x%x", reg); + return -1; + } + + uda1380_regs[reg] = value; + + return 0; +} + +/** + * Sets left and right master volume (0(max) to 252(muted)) + */ +int audiohw_set_master_vol(int vol_l, int vol_r) +{ + return uda1380_write_reg(REG_MASTER_VOL, + MASTER_VOL_LEFT(vol_l) | MASTER_VOL_RIGHT(vol_r)); +} + +/** + * Sets mixer volume for both channels (0(max) to 228(muted)) + */ +int audiohw_set_mixer_vol(int channel1, int channel2) +{ + return uda1380_write_reg(REG_MIX_VOL, + MIX_VOL_CH_1(channel1) | MIX_VOL_CH_2(channel2)); +} + +/** + * Sets the bass value (0-12) + */ +void audiohw_set_bass(int value) +{ + uda1380_write_reg(REG_EQ, (uda1380_regs[REG_EQ] & ~BASS_MASK) + | BASSL(value) | BASSR(value)); +} + +/** + * Sets the treble value (0-3) + */ +void audiohw_set_treble(int value) +{ + uda1380_write_reg(REG_EQ, (uda1380_regs[REG_EQ] & ~TREBLE_MASK) + | TREBLEL(value) | TREBLER(value)); +} + +/** + * Mute (mute=1) or enable sound (mute=0) + * + */ +int audiohw_mute(int mute) +{ + unsigned int value = uda1380_regs[REG_MUTE]; + + if (mute) + value = value | MUTE_MASTER; + else + value = value & ~MUTE_MASTER; + + return uda1380_write_reg(REG_MUTE, value); +} + +/* Returns 0 if successful or -1 if some register failed */ +int audiohw_set_regs(void) +{ + int i; + memset(uda1380_regs, 0, sizeof(uda1380_regs)); + + /* Initialize all registers */ + for (i=0; i<NUM_DEFAULT_REGS; i++) + { + unsigned char reg = uda1380_defaults[i*2+0]; + unsigned short value = uda1380_defaults[i*2+1]; + + if (uda1380_write_reg(reg, value) == -1) + return -1; + } + + return 0; +} + +/* Silently enable / disable audio output */ +void audiohw_enable_output(bool enable) +{ + if (enable) { + uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_DAC | PON_HP); + } else { + uda1380_write_reg(REG_MUTE, MUTE_MASTER); + uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] & ~PON_DAC); + } +} + +void audiohw_reset(void) +{ +#ifdef IRIVER_H300_SERIES + int mask = set_irq_level(HIGHEST_IRQ_LEVEL); + pcf50606_write(0x3b, 0x00); /* GPOOD2 high Z */ + pcf50606_write(0x3b, 0x07); /* GPOOD2 low */ + set_irq_level(mask); +#else + /* RESET signal */ + or_l(1<<29, &GPIO_OUT); + or_l(1<<29, &GPIO_ENABLE); + or_l(1<<29, &GPIO_FUNCTION); + sleep(HZ/100); + and_l(~(1<<29), &GPIO_OUT); +#endif +} + +/** + * Sets frequency settings for DAC and ADC relative to MCLK + * + * Selection for frequency ranges: + * Fs: range: with: + * 11025: 0 = 6.25 to 12.5 MCLK/2 SCLK, LRCK: Audio Clk / 16 + * 22050: 1 = 12.5 to 25 MCLK/2 SCLK, LRCK: Audio Clk / 8 + * 44100: 2 = 25 to 50 MCLK SCLK, LRCK: Audio Clk / 4 (default) + * 88200: 3 = 50 to 100 MCLK SCLK, LRCK: Audio Clk / 2 <= TODO: Needs WSPLL + */ +void audiohw_set_frequency(unsigned fsel) +{ + static const unsigned short values_reg[4][2] = + { + /* Fs: */ + { 0, WSPLL_625_125 | SYSCLK_512FS }, /* 11025 */ + { 0, WSPLL_125_25 | SYSCLK_256FS }, /* 22050 */ + { MIX_CTL_SEL_NS, WSPLL_25_50 | SYSCLK_256FS }, /* 44100 */ + { MIX_CTL_SEL_NS, WSPLL_50_100 | SYSCLK_256FS }, /* 88200 */ + }; + + const unsigned short *ent; + + if (fsel >= ARRAYLEN(values_reg)) + fsel = 2; + + ent = values_reg[fsel]; + + /* Set WSPLL input frequency range or SYSCLK divider */ + uda1380_regs[REG_0] &= ~0xf; + uda1380_write_reg(REG_0, uda1380_regs[REG_0] | ent[1]); + + /* Choose 3rd order or 5th order noise shaper */ + uda1380_regs[REG_MIX_CTL] &= ~MIX_CTL_SEL_NS; + uda1380_write_reg(REG_MIX_CTL, uda1380_regs[REG_MIX_CTL] | ent[0]); +} + +/* Initialize UDA1380 codec with default register values (uda1380_defaults) */ +int audiohw_init(void) +{ + recgain_mic = 0; + recgain_line = 0; + + audiohw_reset(); + + if (audiohw_set_regs() == -1) + return -1; + + return 0; +} + +void audiohw_postinit(void) +{ + /* Sleep a while so the power can stabilize (especially a long + delay is needed for the line out connector). */ + sleep(HZ); + /* Power on FSDAC and HP amp. */ + audiohw_enable_output(true); + + /* UDA1380: Unmute the master channel + (DAC should be at zero point now). */ + audiohw_mute(false); +} + +/* Nice shutdown of UDA1380 codec */ +void audiohw_close(void) +{ + /* First enable mute and sleep a while */ + uda1380_write_reg(REG_MUTE, MUTE_MASTER); + sleep(HZ/8); + + /* Then power off the rest of the chip */ + uda1380_write_reg(REG_PWR, 0); + uda1380_write_reg(REG_0, 0); /* Disable codec */ +} + +/** + * Calling this function enables the UDA1380 to send + * sound samples over the I2S bus, which is connected + * to the processor's IIS1 interface. + * + * source_mic: true=record from microphone, false=record from line-in (or radio) + */ +void audiohw_enable_recording(bool source_mic) +{ + uda1380_regs[REG_0] &= ~(ADC_CLK | DAC_CLK); + uda1380_write_reg(REG_0, uda1380_regs[REG_0] | EN_ADC); + + if (source_mic) + { + /* VGA_GAIN: 0=0 dB, F=30dB */ + /* Output of left ADC is fed into right bitstream */ + uda1380_regs[REG_PWR] &= ~(PON_PGAR | PON_ADCR); + uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_LNA | PON_ADCL); + uda1380_regs[REG_ADC] &= ~SKIP_DCFIL; + uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] & VGA_GAIN_MASK) + | SEL_LNA | SEL_MIC | EN_DCFIL); + uda1380_write_reg(REG_PGA, 0); + } + else + { + /* PGA_GAIN: 0=0 dB, F=24dB */ + uda1380_regs[REG_PWR] &= ~PON_LNA; + uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR] | PON_PGAL | PON_ADCL + | PON_PGAR | PON_ADCR); + uda1380_write_reg(REG_ADC, EN_DCFIL); + uda1380_write_reg(REG_PGA, uda1380_regs[REG_PGA] & PGA_GAIN_MASK); + } + + sleep(HZ/8); + + uda1380_write_reg(REG_I2S, uda1380_regs[REG_I2S] | I2S_MODE_MASTER); + uda1380_write_reg(REG_MIX_CTL, MIX_MODE(1)); +} + +/** + * Stop sending samples on the I2S bus + */ +void audiohw_disable_recording(void) +{ + uda1380_write_reg(REG_PGA, MUTE_ADC); + sleep(HZ/8); + + uda1380_write_reg(REG_I2S, I2S_IFMT_IIS); + + uda1380_regs[REG_PWR] &= ~(PON_LNA | PON_ADCL | PON_ADCR | + PON_PGAL | PON_PGAR); + uda1380_write_reg(REG_PWR, uda1380_regs[REG_PWR]); + + uda1380_regs[REG_0] &= ~EN_ADC; + uda1380_write_reg(REG_0, uda1380_regs[REG_0] | ADC_CLK | DAC_CLK); + + uda1380_write_reg(REG_ADC, SKIP_DCFIL); +} + +/** + * Set recording gain and volume + * + * type: params: ranges: + * AUDIO_GAIN_MIC: left -128 .. 108 -> -64 .. 54 dB gain + * AUDIO_GAIN_LINEIN left & right -128 .. 96 -> -64 .. 48 dB gain + * + * Note: - For all types the value 0 gives 0 dB gain. + * - order of setting both values determines if the small glitch will + be a peak or a dip. The small glitch is caused by the time between + setting the two gains + */ +void audiohw_set_recvol(int left, int right, int type) +{ + int left_ag, right_ag; + + switch (type) + { + case AUDIO_GAIN_MIC: + left_ag = MIN(MAX(0, left / 4), 15); + left -= left_ag * 4; + + if(left < recgain_mic) + { + uda1380_write_reg(REG_DEC_VOL, DEC_VOLL(left) + | DEC_VOLR(left)); + uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] + & ~VGA_GAIN_MASK) + | VGA_GAIN(left_ag)); + } + else + { + uda1380_write_reg(REG_ADC, (uda1380_regs[REG_ADC] + & ~VGA_GAIN_MASK) + | VGA_GAIN(left_ag)); + uda1380_write_reg(REG_DEC_VOL, DEC_VOLL(left) + | DEC_VOLR(left)); + } + recgain_mic = left; + logf("Mic: %dA/%dD", left_ag, left); + break; + + case AUDIO_GAIN_LINEIN: + left_ag = MIN(MAX(0, left / 6), 8); + left -= left_ag * 6; + right_ag = MIN(MAX(0, right / 6), 8); + right -= right_ag * 6; + + if(left < recgain_line) + { + /* for this order we can combine both registers, + making the glitch even smaller */ + unsigned char data[5]; + unsigned short value_dec; + unsigned short value_pga; + value_dec = DEC_VOLL(left) | DEC_VOLR(right); + value_pga = (uda1380_regs[REG_PGA] & ~PGA_GAIN_MASK) + | PGA_GAINL(left_ag) | PGA_GAINR(right_ag); + + data[0] = REG_DEC_VOL; + data[1] = value_dec >> 8; + data[2] = value_dec & 0xff; + data[3] = value_pga >> 8; + data[4] = value_pga & 0xff; + + if (i2c_write(I2C_IFACE_0, UDA1380_ADDR, data, 5) != 5) + { + DEBUGF("uda1380 error reg=combi rec gain"); + } + else + { + uda1380_regs[REG_DEC_VOL] = value_dec; + uda1380_regs[REG_PGA] = value_pga; + } + } + else + { + uda1380_write_reg(REG_PGA, (uda1380_regs[REG_PGA] + & ~PGA_GAIN_MASK) + | PGA_GAINL(left_ag) + | PGA_GAINR(right_ag)); + uda1380_write_reg(REG_DEC_VOL, DEC_VOLL(left) + | DEC_VOLR(right)); + } + + recgain_line = left; + logf("Line L: %dA/%dD", left_ag, left); + logf("Line R: %dA/%dD", right_ag, right); + break; + } +} + + +/** + * Enable or disable recording monitor (so one can listen to the recording) + * + */ +void audiohw_set_monitor(int enable) +{ + if (enable) /* enable channel 2 */ + uda1380_write_reg(REG_MUTE, uda1380_regs[REG_MUTE] & ~MUTE_CH2); + else /* mute channel 2 */ + uda1380_write_reg(REG_MUTE, uda1380_regs[REG_MUTE] | MUTE_CH2); +} diff --git a/firmware/drivers/audio/wm8731l.c b/firmware/drivers/audio/wm8731l.c new file mode 100644 index 0000000..62b1b6a --- /dev/null +++ b/firmware/drivers/audio/wm8731l.c @@ -0,0 +1,305 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Driver for WM8731L audio codec + * + * Based on code from the ipodlinux project - http://ipodlinux.org/ + * Adapted for Rockbox in January 2006 + * + * Original file: linux/arch/armnommu/mach-ipod/audio.c + * + * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org) + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "lcd.h" +#include "cpu.h" +#include "kernel.h" +#include "thread.h" +#include "power.h" +#include "debug.h" +#include "system.h" +#include "sprintf.h" +#include "button.h" +#include "string.h" +#include "file.h" +#include "buffer.h" +#include "audio.h" + +#include "wmcodec.h" +#include "wm8731l.h" +#include "i2s.h" + +#define IPOD_PCM_LEVEL 0x65 /* -6dB */ + +/* use zero crossing to reduce clicks during volume changes */ +#define VOLUME_ZC_WAIT (1<<7) + +/* convert tenth of dB volume (-730..60) to master volume register value */ +int tenthdb2master(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)+0x30+73); + } +} + +/* convert tenth of dB volume (-780..0) to mixer volume register value */ +int tenthdb2mixer(int db) +{ + if (db < -660) /* 1.5 dB steps */ + return (2640 - db) / 15; + else if (db < -600) /* 0.75 dB steps */ + return (990 - db) * 2 / 15; + else if (db < -460) /* 0.5 dB steps */ + return (460 - db) / 5; + else /* 0.25 dB steps */ + return -db * 2 / 5; +} + +int audiohw_mute(int mute) +{ + if (mute) + { + /* Set DACMU = 1 to soft-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x8); + } else { + /* Set DACMU = 0 to soft-un-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x0); + } + + return 0; +} + +/** From ipodLinux **/ +static void codec_set_active(int active) +{ + /* set active to 0x0 or 0x1 */ + if (active) { + wmcodec_write(ACTIVECTRL, 0x01); + } else { + wmcodec_write(ACTIVECTRL, 0x00); + } +} + + +/* Silently enable / disable audio output */ +void audiohw_enable_output(bool enable) +{ + if (enable) + { + /* reset the I2S controller into known state */ + i2s_reset(); + + wmcodec_write(RESET, 0x0); /*Reset*/ + + codec_set_active(0x0); + +#ifdef HAVE_WM8721 + /* DACSEL=1 */ + wmcodec_write(0x4, 0x10); +#elif defined HAVE_WM8731 + /* DACSEL=1, BYPASS=1 */ + wmcodec_write(0x4, 0x18); +#endif + + /* set power register to POWEROFF=0 on OUTPD=0, DACPD=0 */ + wmcodec_write(PWRMGMT, 0x67); + + /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */ + /* IWL=00(16 bit) FORMAT=10(I2S format) */ + wmcodec_write(AINTFCE, 0x42); + + audiohw_set_sample_rate(WM8731L_44100HZ); + + /* set the volume to -6dB */ + wmcodec_write(LOUTVOL, IPOD_PCM_LEVEL); + wmcodec_write(ROUTVOL, 0x100 | IPOD_PCM_LEVEL); + + /* ACTIVE=1 */ + codec_set_active(1); + + /* 5. Set DACMU = 0 to soft-un-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x0); + +#if defined(IRIVER_H10) || defined(IRIVER_H10_5GB) + /* We need to enable bit 4 of GPIOL for output for sound on H10 */ + GPIOL_OUTPUT_VAL |= 0x10; +#endif + audiohw_mute(0); + } else { +#if defined(IRIVER_H10) || defined(IRIVER_H10_5GB) + /* We need to disable bit 4 of GPIOL to disable sound on H10 */ + GPIOL_OUTPUT_VAL &= ~0x10; +#endif + audiohw_mute(1); + } +} + +int audiohw_set_master_vol(int vol_l, int vol_r) +{ + /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */ + /* 1111111 == +6dB */ + /* 1111001 == 0dB */ + /* 0110000 == -73dB */ + /* 0101111 == mute (0x2f) */ + + wmcodec_write(LOUTVOL, VOLUME_ZC_WAIT | vol_l); + wmcodec_write(ROUTVOL, VOLUME_ZC_WAIT | vol_r); + + return 0; +} + +int audiohw_set_mixer_vol(int channel1, int channel2) +{ + (void)channel1; + (void)channel2; + + return 0; +} + +void audiohw_set_bass(int value) +{ + (void)value; +} + +void audiohw_set_treble(int value) +{ + (void)value; +} + +/* Nice shutdown of WM8731 codec */ +void audiohw_close(void) +{ + /* set DACMU=1 DEEMPH=0 */ + wmcodec_write(DACCTRL, 0x8); + + /* ACTIVE=0 */ + codec_set_active(0x0); + + /* line in mute left & right*/ + wmcodec_write(LINVOL, 0x100 | 0x80); + + /* set DACSEL=0, MUTEMIC=1 */ + wmcodec_write(0x4, 0x2); + + /* set POWEROFF=0 OUTPD=0 DACPD=1 */ + wmcodec_write(PWRMGMT, 0x6f); + + /* set POWEROFF=1 OUTPD=1 DACPD=1 */ + wmcodec_write(PWRMGMT, 0xff); +} + +/* Change the order of the noise shaper, 5th order is recommended above 32kHz */ +void audiohw_set_nsorder(int order) +{ + (void)order; +} + +void audiohw_set_sample_rate(int sampling_control) +{ + codec_set_active(0x0); + wmcodec_write(SAMPCTRL, sampling_control); + codec_set_active(0x1); +} + +void audiohw_enable_recording(bool source_mic) +{ + static int line_level = 0x17; + static int mic_boost = true; + codec_set_active(0x0); + + /* set BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 + * LRP=0 IWL=00(16 bit) FORMAT=10(I2S format) */ + wmcodec_write(AINTFCE, 0x42); + + wmcodec_write(LOUTVOL, 0x0); /* headphone mute left */ + wmcodec_write(ROUTVOL, 0x0); /* headphone mute right */ + + + if(source_mic){ + wmcodec_write(LINVOL, 0x80); /* line in mute left */ + wmcodec_write(RINVOL, 0x80); /* line in mute right */ + + + if (mic_boost) { + wmcodec_write(AAPCTRL, 0x5); /* INSEL=mic, MIC_BOOST=enable */ + } else { + wmcodec_write(AAPCTRL, 0x4); /* INSEL=mic */ + } + } else { + if (line_level == 0) { + wmcodec_write(LINVOL, 0x80); + wmcodec_write(RINVOL, 0x80); + } else { + wmcodec_write(LINVOL, line_level); + wmcodec_write(RINVOL, line_level); + } + wmcodec_write(AAPCTRL, 0xa); /* BY PASS, mute mic, INSEL=line in */ + } + + /* disable ADC high pass filter, mute dac */ + wmcodec_write(DACCTRL, 0x9); + + /* power on (PWR_OFF=0) */ + if(source_mic){ + /* CLKOUTPD OSCPD OUTPD DACPD LINEINPD */ + wmcodec_write(PWRMGMT, 0x79); + } else { + wmcodec_write(PWRMGMT, 0x7a); /* MICPD */ + } + + codec_set_active(0x1); +} + +void audiohw_disable_recording(void) +{ + /* set DACMU=1 DEEMPH=0 */ + wmcodec_write(DACCTRL, 0x8); + + /* ACTIVE=0 */ + codec_set_active(0x0); + + /* line in mute left & right*/ + wmcodec_write(LINVOL, 0x80); + wmcodec_write(RINVOL, 0x80); + + /* set DACSEL=0, MUTEMIC=1 */ + wmcodec_write(AAPCTRL, 0x2); + + /* set POWEROFF=0 OUTPD=0 DACPD=1 */ + wmcodec_write(PWRMGMT, 0x6f); + + /* set POWEROFF=1 OUTPD=1 DACPD=1 */ + wmcodec_write(PWRMGMT, 0xff); +} + +void audiohw_set_recvol(int left, int right, int type) +{ + (void)left; + (void)right; + (void)type; +} + +void audiohw_set_monitor(int enable) +{ + (void)enable; +} diff --git a/firmware/drivers/audio/wm8758.c b/firmware/drivers/audio/wm8758.c new file mode 100644 index 0000000..b52bac2 --- /dev/null +++ b/firmware/drivers/audio/wm8758.c @@ -0,0 +1,299 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Driver for WM8758 audio codec - based on datasheet for WM8983 + * + * Based on code from the ipodlinux project - http://ipodlinux.org/ + * Adapted for Rockbox in December 2005 + * + * Original file: linux/arch/armnommu/mach-ipod/audio.c + * + * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org) + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "lcd.h" +#include "cpu.h" +#include "kernel.h" +#include "thread.h" +#include "power.h" +#include "debug.h" +#include "system.h" +#include "sprintf.h" +#include "button.h" +#include "string.h" +#include "file.h" +#include "buffer.h" +#include "audio.h" + +#include "wmcodec.h" +#include "wm8758.h" +#include "i2s.h" + +/* convert tenth of dB volume (-57..6) to master volume register value */ +int tenthdb2master(int db) +{ + /* +6 to -57dB in 1dB steps == 64 levels = 6 bits */ + /* 0111111 == +6dB (0x3f) = 63) */ + /* 0111001 == 0dB (0x39) = 57) */ + /* 0000001 == -56dB (0x01) = */ + /* 0000000 == -57dB (0x00) */ + + /* 1000000 == Mute (0x40) */ + + if (db < VOLUME_MIN) { + return 0x40; + } else { + return((db/10)+57); + } +} + +/* convert tenth of dB volume (-780..0) to mixer volume register value */ +int tenthdb2mixer(int db) +{ + if (db < -660) /* 1.5 dB steps */ + return (2640 - db) / 15; + else if (db < -600) /* 0.75 dB steps */ + return (990 - db) * 2 / 15; + else if (db < -460) /* 0.5 dB steps */ + return (460 - db) / 5; + else /* 0.25 dB steps */ + return -db * 2 / 5; +} + +void audiohw_reset(void); + +#define IPOD_PCM_LEVEL 0x65 /* -6dB */ + +//#define BASSCTRL 0x +//#define TREBCTRL 0x0b + +/* Silently enable / disable audio output */ +void audiohw_enable_output(bool enable) +{ + if (enable) + { + /* reset the I2S controller into known state */ + i2s_reset(); + + /* TODO: Review the power-up sequence to prevent pops */ + + wmcodec_write(RESET, 0x1ff); /*Reset*/ + + wmcodec_write(PWRMGMT1, 0x2b); + wmcodec_write(PWRMGMT2, 0x180); + wmcodec_write(PWRMGMT3, 0x6f); + + wmcodec_write(AINTFCE, 0x10); + wmcodec_write(CLKCTRL, 0x49); + + wmcodec_write(OUTCTRL, 1); + + /* The iPod can handle multiple frequencies, but fix at 44.1KHz + for now */ + audiohw_set_sample_rate(WM8758_44100HZ); + + wmcodec_write(LOUTMIX,0x1); /* Enable mixer */ + wmcodec_write(ROUTMIX,0x1); /* Enable mixer */ + audiohw_mute(0); + } else { + audiohw_mute(1); + } +} + +int audiohw_set_master_vol(int vol_l, int vol_r) +{ + /* OUT1 */ + wmcodec_write(LOUT1VOL, 0x080 | vol_l); + wmcodec_write(ROUT1VOL, 0x180 | vol_r); + + return 0; +} + +int audiohw_set_lineout_vol(int vol_l, int vol_r) +{ + /* OUT2 */ + wmcodec_write(LOUT2VOL, vol_l); + wmcodec_write(ROUT2VOL, 0x100 | vol_r); + + return 0; +} + +int audiohw_set_mixer_vol(int channel1, int channel2) +{ + (void)channel1; + (void)channel2; + + return 0; +} + +/* We are using Linear bass control */ +void audiohw_set_bass(int value) +{ + (void)value; +} + +void audiohw_set_treble(int value) +{ + (void)value; +} + +int audiohw_mute(int mute) +{ + if (mute) + { + /* Set DACMU = 1 to soft-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x40); + } else { + /* Set DACMU = 0 to soft-un-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x0); + } + + return 0; +} + +/* Nice shutdown of WM8758 codec */ +void audiohw_close(void) +{ + audiohw_mute(1); + + wmcodec_write(PWRMGMT3, 0x0); + + wmcodec_write(PWRMGMT1, 0x0); + + wmcodec_write(PWRMGMT2, 0x40); +} + +/* Change the order of the noise shaper, 5th order is recommended above 32kHz */ +void audiohw_set_nsorder(int order) +{ + (void)order; +} + +/* Note: Disable output before calling this function */ +void audiohw_set_sample_rate(int sampling_control) +{ + /**** We force 44.1KHz for now. ****/ + (void)sampling_control; + + /* set clock div */ + wmcodec_write(CLKCTRL, 1 | (0 << 2) | (2 << 5)); + + /* setup PLL for MHZ=11.2896 */ + wmcodec_write(PLLN, (1 << 4) | 0x7); + wmcodec_write(PLLK1, 0x21); + wmcodec_write(PLLK2, 0x161); + wmcodec_write(PLLK3, 0x26); + + /* set clock div */ + wmcodec_write(CLKCTRL, 1 | (1 << 2) | (2 << 5) | (1 << 8)); + + /* set srate */ + wmcodec_write(SRATECTRL, (0 << 1)); +} + +void audiohw_enable_recording(bool source_mic) +{ + (void)source_mic; /* We only have a line-in (I think) */ + + /* reset the I2S controller into known state */ + i2s_reset(); + + wmcodec_write(RESET, 0x1ff); /*Reset*/ + + wmcodec_write(PWRMGMT1, 0x2b); + wmcodec_write(PWRMGMT2, 0x18f); /* Enable ADC - 0x0c enables left/right PGA input, and 0x03 turns on power to the ADCs */ + wmcodec_write(PWRMGMT3, 0x6f); + + wmcodec_write(AINTFCE, 0x10); + wmcodec_write(CLKCTRL, 0x49); + + wmcodec_write(OUTCTRL, 1); + + /* The iPod can handle multiple frequencies, but fix at 44.1KHz + for now */ + audiohw_set_sample_rate(WM8758_44100HZ); + + wmcodec_write(INCTRL,0x44); /* Connect L2 and R2 inputs */ + + /* Set L2/R2_2BOOSTVOL to 0db (bits 4-6) */ + /* 000 = disabled + 001 = -12dB + 010 = -9dB + 011 = -6dB + 100 = -3dB + 101 = 0dB + 110 = 3dB + 111 = 6dB + */ + wmcodec_write(LADCBOOST,0x50); + wmcodec_write(RADCBOOST,0x50); + + /* Set L/R input PGA Volume to 0db */ + // wm8758_write(LINPGAVOL,0x3f); + // wm8758_write(RINPGAVOL,0x13f); + + /* Enable monitoring */ + wmcodec_write(LOUTMIX,0x17); /* Enable output mixer - BYPL2LMIX @ 0db*/ + wmcodec_write(ROUTMIX,0x17); /* Enable output mixer - BYPR2RMIX @ 0db*/ + + audiohw_mute(0); +} + +void audiohw_disable_recording(void) { + audiohw_mute(1); + + wmcodec_write(PWRMGMT3, 0x0); + + wmcodec_write(PWRMGMT1, 0x0); + + wmcodec_write(PWRMGMT2, 0x40); +} + +void audiohw_set_recvol(int left, int right, int type) { + + (void)left; + (void)right; + (void)type; +} + +void audiohw_set_monitor(int enable) { + + (void)enable; +} + +void audiohw_set_equalizer_band(int band, int freq, int bw, int gain) +{ + unsigned int eq = 0; + + /* Band 1..3 are peak filters */ + if (band >= 1 && band <= 3) { + eq |= (bw << 8); + } + + eq |= (freq << 5); + eq |= 12 - gain; + + if (band == 0) { + wmcodec_write(EQ1, eq | 0x100); /* Always apply EQ to the DAC path */ + } else if (band == 1) { + wmcodec_write(EQ2, eq); + } else if (band == 2) { + wmcodec_write(EQ3, eq); + } else if (band == 3) { + wmcodec_write(EQ4, eq); + } else if (band == 4) { + wmcodec_write(EQ5, eq); + } +} diff --git a/firmware/drivers/audio/wm8975.c b/firmware/drivers/audio/wm8975.c new file mode 100644 index 0000000..9052236 --- /dev/null +++ b/firmware/drivers/audio/wm8975.c @@ -0,0 +1,318 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Driver for WM8975 audio codec + * + * Based on code from the ipodlinux project - http://ipodlinux.org/ + * Adapted for Rockbox in December 2005 + * + * Original file: linux/arch/armnommu/mach-ipod/audio.c + * + * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org) + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "lcd.h" +#include "cpu.h" +#include "kernel.h" +#include "thread.h" +#include "power.h" +#include "debug.h" +#include "system.h" +#include "sprintf.h" +#include "button.h" +#include "string.h" +#include "file.h" +#include "buffer.h" +#include "audio.h" + +#include "wmcodec.h" +#include "wm8975.h" +#include "i2s.h" + +/* use zero crossing to reduce clicks during volume changes */ +#define VOLUME_ZC_WAIT (1<<7) + + + +/* convert tenth of dB volume (-730..60) to master volume register value */ +int tenthdb2master(int db) +{ + /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */ + /* 1111111 == +6dB (0x7f) */ + /* 1111001 == 0dB (0x79) */ + /* 0110000 == -73dB (0x30 */ + /* 0101111..0000000 == mute (0x2f) */ + + if (db < VOLUME_MIN) { + return 0x0; + } else { + return((db/10)+73+0x30); + } +} + +/* convert tenth of dB volume (-780..0) to mixer volume register value */ +int tenthdb2mixer(int db) +{ + (void)db; + return 0; +} + + +void audiohw_reset(void); + +#define IPOD_PCM_LEVEL 0x65 /* -6dB */ + + +/* Silently enable / disable audio output */ +void audiohw_enable_output(bool enable) +{ + if (enable) + { + /* reset the I2S controller into known state */ + i2s_reset(); + + /* + * 1. Switch on power supplies. + * By default the WM8750L is in Standby Mode, the DAC is + * digitally muted and the Audio Interface, Line outputs + * and Headphone outputs are all OFF (DACMU = 1 Power + * Management registers 1 and 2 are all zeros). + */ + wmcodec_write(RESET, 0x1ff); /*Reset*/ + wmcodec_write(RESET, 0x0); + + /* 2. Enable Vmid and VREF. */ + wmcodec_write(PWRMGMT1, 0xc0); /*Pwr Mgmt(1)*/ + + /* From app notes: allow Vref to stabilize to reduce clicks */ + sleep(HZ/4); + + /* 3. Enable DACs as required. */ + wmcodec_write(PWRMGMT2, 0x180); /*Pwr Mgmt(2)*/ + + /* 4. Enable line and / or headphone output buffers as required. */ + wmcodec_write(PWRMGMT2, 0x1f8); /*Pwr Mgmt(2)*/ + + /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */ + /* IWL=00(16 bit) FORMAT=10(I2S format) */ + wmcodec_write(AINTFCE, 0x42); + + /* The iPod can handle multiple frequencies, but fix at 44.1KHz for now */ + audiohw_set_sample_rate(WM8975_44100HZ); + + /* set the volume to -6dB */ + wmcodec_write(LOUT1VOL, VOLUME_ZC_WAIT | IPOD_PCM_LEVEL); + wmcodec_write(ROUT1VOL, VOLUME_ZC_WAIT | 0x100 | IPOD_PCM_LEVEL); + + wmcodec_write(LOUTMIX1, 0x150); /* Left out Mix(def) */ + wmcodec_write(LOUTMIX2, 0x50); + + wmcodec_write(ROUTMIX1, 0x50); /* Right out Mix(def) */ + wmcodec_write(ROUTMIX2, 0x150); + + wmcodec_write(MOUTMIX1, 0x0); /* Mono out Mix */ + wmcodec_write(MOUTMIX2, 0x0); + + audiohw_mute(0); + } else { + audiohw_mute(1); + } +} + + + +int audiohw_set_master_vol(int vol_l, int vol_r) +{ + /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */ + /* 1111111 == +6dB */ + /* 1111001 == 0dB */ + /* 0110000 == -73dB */ + /* 0101111 == mute (0x2f) */ + + /* OUT1 */ + wmcodec_write(LOUT1VOL, VOLUME_ZC_WAIT | vol_l); + wmcodec_write(ROUT1VOL, VOLUME_ZC_WAIT | 0x100 | vol_r); + + return 0; +} + +int audiohw_set_lineout_vol(int vol_l, int vol_r) +{ + /* OUT2 */ + wmcodec_write(LOUT2VOL, VOLUME_ZC_WAIT | vol_l); + wmcodec_write(ROUT2VOL, VOLUME_ZC_WAIT | 0x100 | vol_r); + + return 0; +} + +int audiohw_set_mixer_vol(int channel1, int channel2) +{ + (void)channel1; + (void)channel2; + + return 0; +} + +void audiohw_set_bass(int value) +{ + const int regvalues[] = { + 11, 10, 10, 9, 8, 8, 0xf, 6, 6, 5, 4, 4, 3, 2, 1, 0 + }; + + if ((value >= -6) && (value <= 9)) { + /* We use linear bass control with 200 Hz cutoff */ + wmcodec_write(BASSCTRL, regvalues[value + 6] | 0x40); + } +} + +void audiohw_set_treble(int value) +{ + const int regvalues[] = { + 11, 10, 10, 9, 8, 8, 0xf, 6, 6, 5, 4, 4, 3, 2, 1, 0 + }; + + if ((value >= -6) && (value <= 9)) { + /* We use linear treble control with 4 kHz cutoff */ + wmcodec_write(TREBCTRL, regvalues[value + 6] | 0x40); + } +} + +int audiohw_mute(int mute) +{ + if (mute) + { + /* Set DACMU = 1 to soft-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x8); + } else { + /* Set DACMU = 0 to soft-un-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x0); + } + + return 0; +} + +/* Nice shutdown of WM8975 codec */ +void audiohw_close(void) +{ + /* 1. Set DACMU = 1 to soft-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x8); + + /* 2. Disable all output buffers. */ + wmcodec_write(PWRMGMT2, 0x0); /*Pwr Mgmt(2)*/ + + /* 3. Switch off the power supplies. */ + wmcodec_write(PWRMGMT1, 0x0); /*Pwr Mgmt(1)*/ +} + +/* Change the order of the noise shaper, 5th order is recommended above 32kHz */ +void audiohw_set_nsorder(int order) +{ + (void)order; +} + +/* Note: Disable output before calling this function */ +void audiohw_set_sample_rate(int sampling_control) { + + wmcodec_write(0x08, sampling_control); + +} + +void audiohw_enable_recording(bool source_mic) +{ + (void)source_mic; + + /* reset the I2S controller into known state */ + i2s_reset(); + + /* + * 1. Switch on power supplies. + * By default the WM8750L is in Standby Mode, the DAC is + * digitally muted and the Audio Interface, Line outputs + * and Headphone outputs are all OFF (DACMU = 1 Power + * Management registers 1 and 2 are all zeros). + */ + wmcodec_write(0x0f, 0x1ff); + wmcodec_write(0x0f, 0x000); + + /* 2. Enable Vmid and VREF. */ + wmcodec_write(0x19, 0xc0); /*Pwr Mgmt(1)*/ + + /* 3. Enable ADCs as required. */ + wmcodec_write(0x19, 0xcc); /*Pwr Mgmt(1)*/ + wmcodec_write(0x1a, 0x180); /*Pwr Mgmt(2)*/ + + /* 4. Enable line and / or headphone output buffers as required. */ + wmcodec_write(0x19, 0xfc); /*Pwr Mgmt(1)*/ + + /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */ + /* IWL=00(16 bit) FORMAT=10(I2S format) */ + wmcodec_write(0x07, 0x42); + + /* The iPod can handle multiple frequencies, but fix at 44.1KHz for now */ + audiohw_set_sample_rate(WM8975_44100HZ); + + /* unmute inputs */ + wmcodec_write(0x00, 0x17); /* LINVOL (def 0dB) */ + wmcodec_write(0x01, 0x117); /* RINVOL (def 0dB) */ + + wmcodec_write(0x15, 0x1d7); /* LADCVOL max vol x was ff */ + wmcodec_write(0x16, 0x1d7); /* RADCVOL max vol x was ff */ + + if (source_mic) { + /* VSEL=10(def) DATSEL=10 (use right ADC only) */ + wmcodec_write(0x17, 0xc9); /* Additional control(1) */ + + /* VROI=1 (sets output resistance to 40kohms) */ + wmcodec_write(0x1b, 0x40); /* Additional control(3) */ + + /* LINSEL=1 (LINPUT2) LMICBOOST=10 (20dB boost) */ + wmcodec_write(0x20, 0x60); /* ADCL signal path */ + wmcodec_write(0x21, 0x60); /* ADCR signal path */ + } else { + /* VSEL=10(def) DATSEL=00 (left->left, right->right) */ + wmcodec_write(0x17, 0xc1); /* Additional control(1) */ + + /* VROI=1 (sets output resistance to 40kohms) */ + wmcodec_write(0x1b, 0x40); /* Additional control(3) */ + + /* LINSEL=0 (LINPUT1) LMICBOOST=00 (bypass boost) */ + wmcodec_write(0x20, 0x00); /* ADCL signal path */ + /* RINSEL=0 (RINPUT1) RMICBOOST=00 (bypass boost) */ + wmcodec_write(0x21, 0x00); /* ADCR signal path */ + } +} + +void audiohw_disable_recording(void) { + /* 1. Set DACMU = 1 to soft-mute the audio DACs. */ + wmcodec_write(0x05, 0x8); + + /* 2. Disable all output buffers. */ + wmcodec_write(0x1a, 0x0); /*Pwr Mgmt(2)*/ + + /* 3. Switch off the power supplies. */ + wmcodec_write(0x19, 0x0); /*Pwr Mgmt(1)*/ +} + +void audiohw_set_recvol(int left, int right, int type) { + + (void)left; + (void)right; + (void)type; +} + +void audiohw_set_monitor(int enable) { + + (void)enable; +} |