summaryrefslogtreecommitdiff
path: root/firmware/drivers/audio
diff options
context:
space:
mode:
authorMarcoen Hirschberg <marcoen@gmail.com>2007-04-19 10:46:50 +0000
committerMarcoen Hirschberg <marcoen@gmail.com>2007-04-19 10:46:50 +0000
commit15dfe87f9606a191707d5c7b3b33b21963510f53 (patch)
tree5c31520b84dc60aff6997eac6a7e8b4b3ec1127e /firmware/drivers/audio
parentf725ef12565a65d46ff2f9bd34eb3e19da7b763e (diff)
downloadrockbox-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.c211
-rw-r--r--firmware/drivers/audio/tlv320.c305
-rw-r--r--firmware/drivers/audio/uda1380.c460
-rw-r--r--firmware/drivers/audio/wm8731l.c305
-rw-r--r--firmware/drivers/audio/wm8758.c299
-rw-r--r--firmware/drivers/audio/wm8975.c318
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;
+}