summaryrefslogtreecommitdiff
path: root/firmware/drivers
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-06-05 07:03:30 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-06-05 07:03:30 +0000
commitbcb8a884ee256e31d45a46bdeb83423457ac48d2 (patch)
tree5452ef9fa0b3f968a30835d44360f35951f2a0d5 /firmware/drivers
parentfea88888f0bc271b57a6d6dfab387fddc1cbd20d (diff)
e200: Add recording. Just from MIC right now and FM integration will happen soon. Most every bit of weirdness is nescessary and no problems seem to exist that the retailos doesn't exhibit too (namely noise when LCD is on when recording from MIC).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13557 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers')
-rw-r--r--firmware/drivers/audio/as3514.c309
1 files changed, 240 insertions, 69 deletions
diff --git a/firmware/drivers/audio/as3514.c b/firmware/drivers/audio/as3514.c
index 3c11caa4c7..982bbe16d3 100644
--- a/firmware/drivers/audio/as3514.c
+++ b/firmware/drivers/audio/as3514.c
@@ -22,46 +22,76 @@
#include "cpu.h"
#include "debug.h"
#include "system.h"
+#include "audio.h"
#include "audiohw.h"
#include "i2s.h"
#include "i2c-pp.h"
const struct sound_settings_info audiohw_settings[] = {
- [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25},
+ [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25},
/* HAVE_SW_TONE_CONTROLS */
- [SOUND_BASS] = {"dB", 0, 1, -24, 24, 0},
- [SOUND_TREBLE] = {"dB", 0, 1, -24, 24, 0},
- [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0},
- [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0},
- [SOUND_STEREO_WIDTH] = {"%", 0, 1, 0, 255, 100},
+ [SOUND_BASS] = {"dB", 0, 1, -24, 24, 0},
+ [SOUND_TREBLE] = {"dB", 0, 1, -24, 24, 0},
+ [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0},
+ [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0},
+ [SOUND_STEREO_WIDTH] = {"%", 0, 1, 0, 255, 100},
+ [SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 39, 23},
+ [SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 31, 23},
+ [SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 31, 23},
};
/* Shadow registers */
-int as3514_regs[0x1E]; /* last audio register: PLLMODE 0x1d */
+struct as3514_info
+{
+ int vol_r; /* Cached volume level (R) */
+ int vol_l; /* Cached volume level (L) */
+ unsigned int regs[0x1e]; /* last audio register: PLLMODE 0x1d */
+} as3514;
+
+enum
+{
+ SOURCE_DAC = 0,
+ SOURCE_MIC1,
+ SOURCE_LINE_IN1,
+ SOURCE_LINE_IN1_ANALOG
+};
+
+static unsigned int source = SOURCE_DAC;
/*
* little helper method to set register values.
- * With the help of as3514_regs, we minimize i2c
+ * With the help of as3514.regs, we minimize i2c
* traffic.
*/
-static void as3514_write(int reg, int value)
+static void as3514_write(unsigned int reg, unsigned int value)
{
if (pp_i2c_send(AS3514_I2C_ADDR, reg, value) != 2)
{
- DEBUGF("as3514 error reg=0x%x", reg);
+ DEBUGF("as3514 error reg=0x%02x", reg);
}
- if ((unsigned int)reg < sizeof(as3514_regs) / sizeof(int))
+ if (reg < ARRAYLEN(as3514.regs))
{
- as3514_regs[reg] = value;
+ as3514.regs[reg] = value;
}
else
{
- DEBUGF("as3514 error reg=0x%x", reg);
+ DEBUGF("as3514 error reg=0x%02x", reg);
}
}
+/* Helpers to set/clear bits */
+static void as3514_write_or(unsigned int reg, unsigned int bits)
+{
+ as3514_write(reg, as3514.regs[reg] | bits);
+}
+
+static void as3514_write_and(unsigned int reg, unsigned int bits)
+{
+ as3514_write(reg, as3514.regs[reg] & bits);
+}
+
/* convert tenth of dB volume to master volume register value */
int tenthdb2master(int db)
{
@@ -75,6 +105,26 @@ int tenthdb2master(int db)
}
}
+int sound_val2phys(int setting, int value)
+{
+ int result;
+
+ switch(setting)
+ {
+ case SOUND_LEFT_GAIN:
+ case SOUND_RIGHT_GAIN:
+ case SOUND_MIC_GAIN:
+ result = (value - 23) * 15;
+ break;
+
+ default:
+ result = value;
+ break;
+ }
+
+ return result;
+}
+
void audiohw_reset(void);
/*
@@ -110,20 +160,33 @@ int audiohw_init(void)
i2s_reset();
/* Set ADC off, mixer on, DAC on, line out off, line in off, mic off */
- as3514_write(AUDIOSET1, 0x64); /* Turn on SUM, DAC, LineIn 1 */
- 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 */
-#if 0
- as3514_write(LINE_IN1_R, 0x36); /* unmute lineIn 1 and set gain */
- as3514_write(LINE_IN1_L, 0x36); /* unmute lineIn 1 and set gain */
-#endif
+
+ /* Turn on SUM, DAC */
+ as3514_write(AUDIOSET1, (1 << 6) | (1 << 5));
+
+ /* Set HPCM off, ZCU off*/
+ as3514_write(AUDIOSET3, (1 << 2) | (1 << 0));
+
+ /* set vol and set speaker over-current to 0 */
+ as3514_write(HPH_OUT_R, (0x3 << 6) | 0x16);
+ /* set default vol for headphone */
+ as3514_write(HPH_OUT_L, 0x16);
+
+ /* LRCK 24-48kHz */
as3514_write(PLLMODE, 0x00);
+ /* DAC_Mute_off */
+ as3514_write_or(DAC_L, (1 << 6));
+
+ /* M1_Sup_off */
+ as3514_write_or(MIC1_L, (1 << 7));
+ /* M2_Sup_off */
+ as3514_write_or(MIC2_L, (1 << 7));
+
/* read all reg values */
- for (i = 0; i < sizeof(as3514_regs) / sizeof(int); i++)
+ for (i = 0; i < ARRAYLEN(as3514.regs); i++)
{
- as3514_regs[i] = i2c_readbyte(AS3514_I2C_ADDR, i);
+ as3514.regs[i] = i2c_readbyte(AS3514_I2C_ADDR, i);
}
return 0;
@@ -136,75 +199,80 @@ 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)
- {
+ if (enable) {
/* reset the I2S controller into known state */
i2s_reset();
- as3514_write(HPH_OUT_L, curr | 0x40); /* power on */
+ as3514_write_or(HPH_OUT_L, (1 << 6)); /* power on */
audiohw_mute(0);
} else {
audiohw_mute(1);
- as3514_write(HPH_OUT_L, curr & ~(0x40)); /* power off */
+ as3514_write_and(HPH_OUT_L, ~(1 << 6)); /* power off */
}
}
int audiohw_set_master_vol(int vol_l, int vol_r)
{
- int hph_r = as3514_regs[HPH_OUT_R] & ~0x1f;
- int hph_l = as3514_regs[HPH_OUT_L] & ~0x1f;
-
- /* we are controling dac volume instead of headphone volume,
- as the volume is bigger.
- HDP: 1.07 dB gain
- DAC: 6 dB gain
- */
- if(vol_r <= 0x16)
- {
- as3514_write(DAC_R, vol_r);
- as3514_write(HPH_OUT_R, hph_r); /* set 0 */
- }
- else
- {
- as3514_write(DAC_R, 0x16);
- as3514_write(HPH_OUT_R, hph_r + (vol_r - 0x16));
+ unsigned int hph_r = as3514.regs[HPH_OUT_R] & ~0x1f;
+ unsigned int hph_l = as3514.regs[HPH_OUT_L] & ~0x1f;
+ unsigned int mix_l, mix_r;
+ unsigned int mix_reg_r, mix_reg_l;
+
+ /* keep track of current setting */
+ as3514.vol_l = vol_l;
+ as3514.vol_r = vol_r;
+
+ if (source == SOURCE_LINE_IN1_ANALOG) {
+ mix_reg_r = LINE_IN1_R;
+ mix_reg_l = LINE_IN1_L;
+ } else {
+ mix_reg_r = DAC_R;
+ mix_reg_l = DAC_L;
}
- if(vol_l <= 0x16)
- {
- as3514_write(DAC_L, 0x40 + vol_l);
- as3514_write(HPH_OUT_L, hph_l); /* set 0 */
+ mix_r = as3514.regs[mix_reg_r] & ~0x1f;
+ mix_l = as3514.regs[mix_reg_l] & ~0x1f;
+
+ /* we combine the mixer channel volume range with the headphone volume
+ range */
+ if (vol_r <= 0x16) {
+ mix_r |= vol_r;
+ /* hph_r - set 0 */
+ } else {
+ mix_r |= 0x16;
+ hph_r += vol_r - 0x16;
}
- else
- {
- as3514_write(DAC_L, 0x40 + 0x16);
- as3514_write(HPH_OUT_L, hph_l + (vol_l - 0x16));
+
+ if (vol_l <= 0x16) {
+ mix_l |= vol_l;
+ /* hph_l - set 0 */
+ } else {
+ mix_l |= 0x16;
+ hph_l += vol_l - 0x16;
}
+ as3514_write(mix_reg_r, mix_r);
+ as3514_write(mix_reg_l, mix_l);
+ as3514_write(HPH_OUT_R, hph_r);
+ as3514_write(HPH_OUT_L, hph_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);
+ as3514_write(LINE_OUT_L, (1 << 6) | 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);
+ if (mute) {
+ as3514_write_or(HPH_OUT_L, (1 << 7));
} else {
- as3514_write(HPH_OUT_L, curr & ~(0x80));
+ as3514_write_and(HPH_OUT_L, ~(1 << 7));
}
return 0;
@@ -214,7 +282,7 @@ int audiohw_mute(int mute)
void audiohw_close(void)
{
/* mute headphones */
- audiohw_mute(1);
+ audiohw_mute(true);
/* turn off everything */
as3514_write(AUDIOSET1, 0x0);
@@ -227,21 +295,124 @@ void audiohw_set_sample_rate(int sampling_control)
void audiohw_enable_recording(bool source_mic)
{
- (void)source_mic;
+ if (source_mic) {
+ source = SOURCE_MIC1;
+
+ /* Sync mixer volumes before switching inputs */
+ audiohw_set_master_vol(as3514.vol_l, as3514.vol_r);
+
+ /* ADCmux = Stereo Microphone */
+ as3514_write_and(ADC_R, ~(0x3 << 6));
+ /* MIC1_on, LIN1_off */
+ as3514_write(AUDIOSET1,
+ (as3514.regs[AUDIOSET1] & ~(1 << 2)) | (1 << 0));
+ /* M1_AGC_off */
+ as3514_write_and(MIC1_R, ~(1 << 7));
+ } else {
+ source = SOURCE_LINE_IN1;
+
+ audiohw_set_master_vol(as3514.vol_l, as3514.vol_r);
+
+ /* ADCmux = Line_IN1 */
+ as3514_write(ADC_R,
+ (as3514.regs[ADC_R] & ~(0x3 << 6)) | (0x1 << 6));
+ /* MIC1_off, LIN1_on */
+ as3514_write(AUDIOSET1,
+ (as3514.regs[AUDIOSET1] & ~(1 << 0)) | (1 << 2));
+ }
+
+ /* ADC_Mute_off */
+ as3514_write_or(ADC_L, (1 << 6));
+ /* ADC_on */
+ as3514_write_or(AUDIOSET1, (1 << 7));
}
void audiohw_disable_recording(void)
{
+ source = SOURCE_DAC;
+
+ /* ADC_Mute_on */
+ as3514_write_and(ADC_L, ~(1 << 6));
+
+ /* ADC_off, LIN1_off, MIC_off */
+ as3514_write_and(AUDIOSET1, ~((1 << 7) | (1 << 2) | (1 << 0)));
+
+ audiohw_set_master_vol(as3514.vol_l, as3514.vol_r);
}
+/**
+ * Set recording volume
+ *
+ * Line in : 0 .. 23 .. 31 =>
+ Volume -34.5 .. +00.0 .. +12.0 dB
+ * Mic (left): 0 .. 23 .. 39 =>
+ * Volume -34.5 .. +00.0 .. +24.0 dB
+ *
+ */
void audiohw_set_recvol(int left, int right, int type)
{
- (void)left;
- (void)right;
- (void)type;
+ switch (type)
+ {
+ case AUDIO_GAIN_MIC:
+ {
+ /* Combine MIC gains seamlessly with ADC levels */
+ unsigned int mic1_r = as3514.regs[MIC1_R] & ~(0x3 << 5);
+
+ if (left >= 36) {
+ /* M1_Gain = +40db, ADR_Vol = +7.5dB .. +12.0 dB =>
+ +19.5 dB .. +24.0 dB */
+ left -= 8;
+ mic1_r |= (0x2 << 5);
+ } else if (left >= 32) {
+ /* M1_Gain = +34db, ADR_Vol = +7.5dB .. +12.0 dB =>
+ +13.5 dB .. +18.0 dB */
+ left -= 4;
+ mic1_r |= (0x1 << 5);
+ }
+ /* M1_Gain = +28db, ADR_Vol = -34.5dB .. +12.0 dB =>
+ -34.5 dB .. +12.0 dB */
+
+ right = left;
+
+ as3514_write(MIC1_R, mic1_r);
+ break;
+ }
+ case AUDIO_GAIN_LINEIN:
+ break;
+ default:
+ return;
+ }
+
+ as3514_write(ADC_R, (as3514.regs[ADC_R] & ~0x1f) | right);
+ as3514_write(ADC_L, (as3514.regs[ADC_L] & ~0x1f) | left);
}
+/**
+ * Enable line in 1 analog monitoring
+ *
+ */
void audiohw_set_monitor(int enable)
{
- (void)enable;
+ /* LI1R_Mute_on - default */
+ unsigned int line_in1_r = as3514.regs[LINE_IN1_R] & ~(1 << 5);
+ /* LI1L_Mute_on - default */
+ unsigned int line_in1_l = as3514.regs[LINE_IN1_L] & ~(1 << 5);
+ /* LIN1_off - default */
+ unsigned int audioset1 = as3514.regs[AUDIOSET1] & ~(1 << 2);
+
+ if (enable) {
+ source = SOURCE_LINE_IN1_ANALOG;
+ audiohw_set_master_vol(as3514.vol_l, as3514.vol_r);
+
+ /* LI1R_Mute_off */
+ line_in1_r |= (1 << 5);
+ /* LI1L_Mute_off */
+ line_in1_l |= (1 << 5);
+ /* LIN1_on */
+ audioset1 |= (1 << 2);
+ }
+
+ as3514_write(AUDIOSET1, audioset1);
+ as3514_write(LINE_IN1_R, line_in1_r);
+ as3514_write(LINE_IN1_L, line_in1_l);
}