FS#6096. Recording on PortalPlayer targets (H10, iPod Video, iPod 4g, iPod Color, iPod Nano).

* Fix failed compile of enc_config.c when HAVE_MPEG2_SAMPR is not defined.
* Fix bug in AIFF encoder header creation on little endian targets.
* Add recording screen keymaps for H10 and iPod.
* Move pcm_playback PP specific code to target tree.
* Add recording code to wmcodec drivers.
* Add pcm_record code.

Some problems still remain:
* Playback doesn't work after recording until Rockbox is restarted.
* Gain control not implemented.
* Only 16-bit/44KHz for now. The hardware should be capable of up to 24-bit/96KHz.
* Line-in recording not tested on H10.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11794 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Barry Wardell 2006-12-18 01:52:21 +00:00
parent 440353a9aa
commit df0dc2262e
24 changed files with 961 additions and 369 deletions

View File

@ -59,7 +59,7 @@ struct aiff_header aiff_header =
H_TO_BE32(18), /* comm_size */
0, /* num_channels (*) */
0, /* num_sample_frames (*) */
H_TO_BE32(PCM_DEPTH_BITS), /* sample_size */
H_TO_BE16(PCM_DEPTH_BITS), /* sample_size */
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* sample_rate (*) */
{ 'S', 'S', 'N', 'D' }, /* ssnd_id */
0, /* ssnd_size (*) */

View File

@ -159,9 +159,9 @@ static bool mp3_enc_bitrate(struct encoder_config *cfg)
MPEG1_BITR_CAPS | MPEG2_BITR_CAPS, mp3_enc_bitr,
MPEG1_BITR_CAPS
#ifdef HAVE_MPEG2_SAMPR
| (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8)),
| (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8))
#endif
rate_list);
, rate_list);
int index = round_value_to_list32(cfg->mp3_enc.bitrate, rate_list,
n_rates, false);

View File

@ -298,6 +298,12 @@ static const struct button_mapping button_context_bmark[] = {
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
}; /* button_context_bmark */
const struct button_mapping button_context_recscreen[] = {
{ ACTION_REC_PAUSE, BUTTON_PLAY, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)
}; /* button_context_recscreen */
static const struct button_mapping* get_context_mapping_remote( int context )
{
context ^= CONTEXT_REMOTE;
@ -325,6 +331,8 @@ static const struct button_mapping* get_context_mapping_remote( int context )
return remote_button_context_quickscreen;
case CONTEXT_PITCHSCREEN:
return remote_button_context_pitchscreen;
case CONTEXT_RECSCREEN:
return button_context_recscreen;
default:
return remote_button_context_standard;
@ -374,6 +382,8 @@ const struct button_mapping* get_context_mapping(int context)
return button_context_pitchscreen;
case CONTEXT_KEYBOARD:
return button_context_keyboard;
case CONTEXT_RECSCREEN:
return button_context_recscreen;
default:
return button_context_standard;

View File

@ -166,6 +166,12 @@ static const struct button_mapping button_context_keyboard[] = {
LAST_ITEM_IN_LIST
}; /* button_context_keyboard */
const struct button_mapping button_context_recscreen[] = {
{ ACTION_REC_PAUSE, BUTTON_PLAY, BUTTON_NONE },
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)
}; /* button_context_recscreen */
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
@ -202,6 +208,8 @@ const struct button_mapping* get_context_mapping(int context)
return button_context_pitchscreen;
case CONTEXT_KEYBOARD:
return button_context_keyboard;
case CONTEXT_RECSCREEN:
return button_context_recscreen;
default:
return button_context_standard;
}

View File

@ -78,6 +78,9 @@
#endif
#if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING) && !defined(SIMULATOR)
#include "pcm_record.h"
#endif
#ifdef BUTTON_REC
#define SETTINGS_RESET BUTTON_REC
#endif

View File

@ -100,7 +100,7 @@ const char rec_base_directory[] = REC_BASE_DIR;
#include "eq_menu.h"
#endif
#define CONFIG_BLOCK_VERSION 56
#define CONFIG_BLOCK_VERSION 57
#define CONFIG_BLOCK_SIZE 512
#define RTC_BLOCK_SIZE 44
@ -521,13 +521,26 @@ static const struct bit_entry hd_bits[] =
#if CONFIG_CODEC == SWCODEC
#ifdef HAVE_UDA1380
{8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */
#endif
#ifdef HAVE_TLV320
/* TLV320 only has no mic boost or 20db mic boost */
{1, S_O(rec_mic_gain), 0 /* 0 dB */, "rec mic gain", NULL }, /* 0db or 20db */
#endif
{8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
{8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
#elif defined(HAVE_TLV320)
/* TLV320 only has no mic boost or 20db mic boost */
{1, S_O(rec_mic_gain), 0 /* 0 dB */, "rec mic gain", NULL }, /* 0db or 20db */
{8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
{8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
#elif defined(HAVE_WM8975)
{8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */
{8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
{8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
#elif defined(HAVE_WM8758)
{8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */
{8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
{8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
#elif defined(HAVE_WM8731)
{8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */
{8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
{8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
#endif
{REC_FREQ_CFG_NUM_BITS, S_O(rec_frequency), REC_FREQ_DEFAULT,
"rec frequency", REC_FREQ_CFG_VAL_LIST },
{REC_FORMAT_CFG_NUM_BITS ,S_O(rec_format), REC_FORMAT_DEFAULT,

View File

@ -262,6 +262,8 @@ drivers/i2c-pnx0101.c
/* no i2c driver yet */
#endif
#if defined(CPU_PP)
target/arm/pcm-pp.c
target/arm/audio-pp.c
target/arm/crt0-pp.S
#elif defined(CPU_ARM)
target/arm/crt0.S

View File

@ -220,12 +220,73 @@ void audiohw_set_sample_rate(int sampling_control)
void audiohw_enable_recording(bool source_mic)
{
(void)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)

View File

@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
* Driver for WM8758 audio codec
* 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
@ -142,30 +142,11 @@ int audiohw_set_mixer_vol(int channel1, int channel2)
void audiohw_set_bass(int value)
{
(void)value;
#if 0
/* Not yet implemented - this is the wm8975 code*/
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 130Hz cutoff */
wmcodec_write(BASSCTRL, regvalues[value+6]);
}
#endif
}
void audiohw_set_treble(int value)
{
(void)value;
#if 0
/* Not yet implemented - this is the wm8975 code*/
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 a 8Khz cutoff */
wmcodec_write(TREBCTRL, regvalues[value+6]);
}
#endif
}
int audiohw_mute(int mute)
@ -224,11 +205,60 @@ void audiohw_set_sample_rate(int sampling_control)
void audiohw_enable_recording(bool source_mic)
{
(void)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 */
wmcodec_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*/
wmcodec_mute(0);
}
void audiohw_disable_recording(void) {
wmcodec_mute(1);
wmcodec_write(PWRMGMT3, 0x0);
wmcodec_write(PWRMGMT1, 0x0);
wmcodec_write(PWRMGMT2, 0x40);
}
void audiohw_set_recvol(int left, int right, int type) {

View File

@ -224,13 +224,80 @@ void audiohw_set_sample_rate(int sampling_control) {
}
void audiohw_enable_recording(bool source_mic) {
void audiohw_enable_recording(bool source_mic)
{
(void)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 */
wmcodec_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, 0xc8); /* 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, 0xc0); /* 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) {

View File

@ -8,7 +8,13 @@
#define MODEL_NUMBER 13
/* define this if you have recording possibility */
/*#define HAVE_RECORDING 1*/ /* TODO: add support for this */
#define HAVE_RECORDING 1
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44)
/* define the bitmask of recording sample rates */
#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1

View File

@ -8,7 +8,13 @@
#define MODEL_NUMBER 14
/* define this if you have recording possibility */
/*#define HAVE_RECORDING 1*/ /* TODO: add support for this */
#define HAVE_RECORDING 1
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44)
/* define the bitmask of recording sample rates */
#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1

View File

@ -9,7 +9,13 @@
#define MODEL_NUMBER 8
/* define this if you have recording possibility */
/*#define HAVE_RECORDING 1*/
#define HAVE_RECORDING 1
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44)
/* define the bitmask of recording sample rates */
#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1

View File

@ -9,7 +9,13 @@
#define MODEL_NUMBER 3
/* define this if you have recording possibility */
/*#define HAVE_RECORDING 1*/
#define HAVE_RECORDING 1
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44)
/* define the bitmask of recording sample rates */
#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1

View File

@ -9,7 +9,13 @@
#define MODEL_NUMBER 4
/* define this if you have recording possibility */
/*#define HAVE_RECORDING 1*/
#define HAVE_RECORDING 1
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44)
/* define the bitmask of recording sample rates */
#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1

View File

@ -9,7 +9,13 @@
#define MODEL_NUMBER 5
/* define this if you have recording possibility */
/*#define HAVE_RECORDING 1*/
#define HAVE_RECORDING 1
/* define the bitmask of hardware sample rates */
#define HW_SAMPR_CAPS (SAMPR_CAP_44)
/* define the bitmask of recording sample rates */
#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1

View File

@ -48,7 +48,8 @@ enum {
SOUND_MDB_ENABLE,
SOUND_SUPERBASS,
#endif
#if CONFIG_CODEC == MAS3587F || defined(HAVE_UDA1380) || defined(HAVE_TLV320)
#if CONFIG_CODEC == MAS3587F || defined(HAVE_UDA1380) || defined(HAVE_TLV320)\
|| defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_WM8731)
SOUND_LEFT_GAIN,
SOUND_RIGHT_GAIN,
SOUND_MIC_GAIN,

View File

@ -314,8 +314,11 @@ static inline int set_irq_level(int level)
return (cpsr >> 7) & 1;
}
static inline void enable_fiq(void)
static inline void enable_fiq(void(*fiq_handler)(void))
{
/* Install the FIQ handler */
*((unsigned int*)(15*4)) = (unsigned int)fiq_handler;
/* Clear FIQ disable bit */
asm volatile (
"mrs r0, cpsr \n"\

View File

@ -49,6 +49,7 @@ extern void audiohw_set_monitor(int enable);
#define RINVOL 0x01
#define LOUTVOL 0x02
#define ROUTVOL 0x03
#define AAPCTRL 0x04 /* Analog audio path control */
#define DACCTRL 0x05
#define PWRMGMT 0x06
#define AINTFCE 0x07

View File

@ -55,6 +55,11 @@ extern void audiohw_set_equalizer_band(int band, int freq, int bw, int gain);
#define CLKCTRL 0x06
#define SRATECTRL 0x07
#define DACCTRL 0x0a
#define INCTRL 0x2c
#define LINPGAVOL 0x2d
#define RINPGAVOL 0x2e
#define LADCBOOST 0x2f
#define RADCBOOST 0x30
#define OUTCTRL 0x31
#define LOUTMIX 0x32
#define ROUTMIX 0x33

View File

@ -98,326 +98,6 @@ size_t pcm_get_bytes_waiting(void)
return 0;
}
#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \
|| defined(HAVE_WM8731) || defined(HAVE_WM8721) \
|| defined(HAVE_PP5024_CODEC)
/* We need to unify this code with the uda1380 code as much as possible, but
we will keep it separate during early development.
*/
#if CONFIG_CPU == PP5020
#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f0000) >> 16)
#elif CONFIG_CPU == PP5002
#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23)
#elif CONFIG_CPU == PP5024
#define FIFO_FREE_COUNT 4 /* TODO: make this sensible */
#endif
static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
/* NOTE: The order of these two variables is important if you use the iPod
assembler optimised fiq handler, so don't change it. */
unsigned short* p IBSS_ATTR;
size_t p_size IBSS_ATTR;
void pcm_play_dma_start(const void *addr, size_t size)
{
p=(unsigned short*)addr;
p_size=size;
pcm_playing = true;
#if CONFIG_CPU == PP5020
/* setup I2S interrupt for FIQ */
outl(inl(0x6000402c) | I2S_MASK, 0x6000402c);
outl(I2S_MASK, 0x60004024);
#elif CONFIG_CPU == PP5024
#else
/* setup I2S interrupt for FIQ */
outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c);
outl(DMA_OUT_MASK, 0xcf001024);
#endif
/* Clear the FIQ disable bit in cpsr_c */
enable_fiq();
/* Enable playback FIFO */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x20000000;
#elif CONFIG_CPU == PP5002
IISCONFIG |= 0x4;
#endif
/* Fill the FIFO - we assume there are enough bytes in the pcm buffer to
fill the 32-byte FIFO. */
while (p_size > 0) {
if (FIFO_FREE_COUNT < 2) {
/* Enable interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x2;
#elif CONFIG_CPU == PP5002
IISFIFO_CFG |= (1<<9);
#endif
return;
}
IISFIFO_WR = (*(p++))<<16;
IISFIFO_WR = (*(p++))<<16;
p_size-=4;
}
}
/* Stops the DMA transfer and interrupt */
void pcm_play_dma_stop(void)
{
pcm_playing = false;
#if CONFIG_CPU == PP5020
/* Disable playback FIFO */
IISCONFIG &= ~0x20000000;
/* Disable the interrupt */
IISCONFIG &= ~0x2;
#elif CONFIG_CPU == PP5002
/* Disable playback FIFO */
IISCONFIG &= ~0x4;
/* Disable the interrupt */
IISFIFO_CFG &= ~(1<<9);
#endif
disable_fiq();
}
void pcm_play_pause_pause(void)
{
#if CONFIG_CPU == PP5020
/* Disable the interrupt */
IISCONFIG &= ~0x2;
/* Disable playback FIFO */
IISCONFIG &= ~0x20000000;
#elif CONFIG_CPU == PP5002
/* Disable the interrupt */
IISFIFO_CFG &= ~(1<<9);
/* Disable playback FIFO */
IISCONFIG &= ~0x4;
#endif
disable_fiq();
}
void pcm_play_pause_unpause(void)
{
/* Enable the FIFO and fill it */
enable_fiq();
/* Enable playback FIFO */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x20000000;
#elif CONFIG_CPU == PP5002
IISCONFIG |= 0x4;
#endif
/* Fill the FIFO - we assume there are enough bytes in the
pcm buffer to fill the 32-byte FIFO. */
while (p_size > 0) {
if (FIFO_FREE_COUNT < 2) {
/* Enable interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x2;
#elif CONFIG_CPU == PP5002
IISFIFO_CFG |= (1<<9);
#endif
return;
}
IISFIFO_WR = (*(p++))<<16;
IISFIFO_WR = (*(p++))<<16;
p_size-=4;
}
}
void pcm_set_frequency(unsigned int frequency)
{
(void)frequency;
pcm_freq = HW_SAMPR_DEFAULT;
}
size_t pcm_get_bytes_waiting(void)
{
return p_size;
}
/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
has registers r8-r14 banked, and so does not need to be saved. This routine
uses only these registers, and so will never touch the stack unless it
actually needs to do so when calling pcm_callback_for_more. C version is
still included below for reference.
*/
#if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002
void fiq(void) ICODE_ATTR __attribute__((naked));
void fiq(void)
{
/* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual
* FIQ handler. r11 contains address of p (also set in crt0.S). Most other
* addresses we need are generated by using offsets with these two.
* r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG.
* r8 and r9 contains local copies of p_size and p respectively.
* r10 is a working register.
*/
asm volatile (
#if CONFIG_CPU == PP5002
"ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */
"ldr r10, [r10] \n\t"
"ldr r10, [r12, #0x1c]\n\t"
"bic r10, r10, #0x200 \n\t" /* clear interrupt */
"str r10, [r12, #0x1c]\n\t"
#else
"ldr r10, [r12] \n\t"
"bic r10, r10, #0x2 \n\t" /* clear interrupt */
"str r10, [r12] \n\t"
#endif
"ldr r8, [r11, #4] \n\t" /* r8 = p_size */
"ldr r9, [r11] \n\t" /* r9 = p */
".loop: \n\t"
"cmp r8, #0 \n\t" /* is p_size 0? */
"beq .more_data \n\t" /* if so, ask pcmbuf for more data */
".fifo_loop: \n\t"
#if CONFIG_CPU == PP5002
"ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */
"and r10, r10, #0x7800000\n\t"
"cmp r10, #0x800000 \n\t"
#else
"ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */
"and r10, r10, #0x3f0000\n\t"
"cmp r10, #0x10000 \n\t"
#endif
"bls .fifo_full \n\t" /* FIFO full, exit */
"ldr r10, [r9], #4 \n\t" /* load two samples */
"mov r10, r10, ror #16\n\t" /* put left sample at the top bits */
"str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */
"mov r10, r10, lsl #16\n\t" /* shift lower sample up */
"str r10, [r12, #0x40]\n\t" /* then write it */
"subs r8, r8, #4 \n\t" /* check if we have more samples */
"bne .fifo_loop \n\t" /* yes, continue */
".more_data: \n\t"
"stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */
"mov r0, r11 \n\t" /* r0 = &p */
"add r1, r11, #4 \n\t" /* r1 = &p_size */
"str r9, [r0] \n\t" /* save internal copies of variables back */
"str r8, [r1] \n\t"
"ldr r2, =pcm_callback_for_more\n\t"
"ldr r2, [r2] \n\t" /* get callback address */
"cmp r2, #0 \n\t" /* check for null pointer */
"movne lr, pc \n\t" /* call pcm_callback_for_more */
"bxne r2 \n\t"
"ldmia sp!, { r0-r3, r12, lr}\n\t"
"ldr r8, [r11, #4] \n\t" /* reload p_size and p */
"ldr r9, [r11] \n\t"
"cmp r8, #0 \n\t" /* did we actually get more data? */
"bne .loop \n\t" /* yes, continue to try feeding FIFO */
".dma_stop: \n\t" /* no more data, do dma_stop() and exit */
"ldr r10, =pcm_playing\n\t"
"strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */
"ldr r10, [r12] \n\t"
#if CONFIG_CPU == PP5002
"bic r10, r10, #0x4\n\t" /* disable playback FIFO */
"str r10, [r12] \n\t"
"ldr r10, [r12, #0x1c] \n\t"
"bic r10, r10, #0x200 \n\t" /* clear interrupt */
"str r10, [r12, #0x1c] \n\t"
#else
"bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */
"str r10, [r12] \n\t"
#endif
"mrs r10, cpsr \n\t"
"orr r10, r10, #0x40 \n\t" /* disable FIQ */
"msr cpsr_c, r10 \n\t"
".exit: \n\t"
"str r8, [r11, #4] \n\t"
"str r9, [r11] \n\t"
"subs pc, lr, #4 \n\t" /* FIQ specific return sequence */
".fifo_full: \n\t" /* enable IRQ and exit */
#if CONFIG_CPU == PP5002
"ldr r10, [r12, #0x1c]\n\t"
"orr r10, r10, #0x200 \n\t" /* set interrupt */
"str r10, [r12, #0x1c]\n\t"
#else
"ldr r10, [r12] \n\t"
"orr r10, r10, #0x2 \n\t" /* set interrupt */
"str r10, [r12] \n\t"
#endif
"b .exit \n\t"
);
}
#else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */
void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
void fiq(void)
{
/* Clear interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG &= ~0x2;
#elif CONFIG_CPU == PP5002
inl(0xcf001040);
IISFIFO_CFG &= ~(1<<9);
#endif
do {
while (p_size) {
if (FIFO_FREE_COUNT < 2) {
/* Enable interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x2;
#elif CONFIG_CPU == PP5002
IISFIFO_CFG |= (1<<9);
#endif
return;
}
IISFIFO_WR = (*(p++))<<16;
IISFIFO_WR = (*(p++))<<16;
p_size-=4;
}
/* p is empty, get some more data */
if (pcm_callback_for_more) {
pcm_callback_for_more((unsigned char**)&p,&p_size);
}
} while (p_size);
/* No more data, so disable the FIFO/FIQ */
pcm_play_dma_stop();
}
#endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */
#ifdef HAVE_PP5024_CODEC
void pcm_init(void)
{
}
#else
void pcm_init(void)
{
pcm_playing = false;
pcm_paused = false;
pcm_callback_for_more = NULL;
/* Initialize default register values. */
audiohw_init();
/* Power on */
audiohw_enable_output(true);
/* Unmute the master channel (DAC should be at zero point now). */
audiohw_mute(false);
/* Call pcm_play_dma_stop to initialize everything. */
pcm_play_dma_stop();
}
#endif /* HAVE_PP5024_CODEC */
#elif (CONFIG_CPU == PNX0101)
#define DMA_BUF_SAMPLES 0x100
@ -608,7 +288,7 @@ void pcm_mute(bool mute)
if (mute)
sleep(HZ/16);
}
#if !defined(CPU_PP)
/*
* This function goes directly into the DMA buffer to calculate the left and
* right peak values. To avoid missing peaks it tries to look forward two full
@ -632,9 +312,7 @@ void pcm_calculate_peaks(int *left, int *right)
short *addr;
short *end;
{
#if defined(HAVE_WM8975) || defined(HAVE_WM8758) \
|| defined(HAVE_WM8731) || defined(HAVE_WM8721) \
|| (CONFIG_CPU == PNX0101) || defined(HAVE_PP5024_CODEC)
#if CONFIG_CPU == PNX0101
size_t samples = p_size / 4;
addr = p;
#endif
@ -690,7 +368,7 @@ void pcm_calculate_peaks(int *left, int *right)
}
#endif
}
#endif
#endif /* CPU_COLDFIRE */
/****************************************************************************

View File

@ -112,7 +112,19 @@ static const struct sound_settings_info sound_settings_table[] = {
[SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 31, 23, NULL},
[SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 31, 23, NULL},
[SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 1, 1, NULL},
#endif
#elif defined(HAVE_WM8975)
[SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
[SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
[SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16, NULL},
#elif defined(HAVE_WM8758)
[SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
[SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
[SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16, NULL},
#elif defined(HAVE_WM8731)
[SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
[SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
[SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16, NULL},
#endif
};
const char *sound_unit(int setting)

View File

@ -0,0 +1,84 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 by Michael Sevakis
*
* 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 "system.h"
#include "cpu.h"
#include "audio.h"
#include "sound.h"
void audio_set_output_source(int source)
{
if ((unsigned)source >= AUDIO_NUM_SOURCES)
source = AUDIO_SRC_PLAYBACK;
} /* audio_set_output_source */
void audio_set_source(int source, unsigned flags)
{
/* Prevent pops from unneeded switching */
static int last_source = AUDIO_SRC_PLAYBACK;
bool recording = flags & SRCF_RECORDING;
static bool last_recording = false;
switch (source)
{
default: /* playback - no recording */
source = AUDIO_SRC_PLAYBACK;
case AUDIO_SRC_PLAYBACK:
if (source != last_source)
{
audiohw_disable_recording();
audiohw_set_monitor(false);
}
break;
case AUDIO_SRC_MIC: /* recording only */
if (source != last_source)
{
audiohw_enable_recording(true); /* source mic */
audiohw_set_monitor(false);
}
break;
case AUDIO_SRC_LINEIN: /* recording only */
if (source != last_source)
{
audiohw_enable_recording(false); /* source line */
audiohw_set_monitor(false);
}
break;
#ifdef CONFIG_TUNER
case AUDIO_SRC_FMRADIO: /* recording and playback */
if (!recording)
audiohw_set_recvol(0, 0, AUDIO_GAIN_LINEIN);
if (source == last_source && recording == last_recording)
break;
last_recording = recording;
/* I2S recording and playback */
audiohw_enable_recording(false); /* source line */
audiohw_set_monitor(!recording);
break;
#endif
} /* end switch */
last_source = source;
} /* audio_set_source */

View File

@ -0,0 +1,578 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 by Michael Sevakis
*
* 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 <stdlib.h>
#include "system.h"
#include "kernel.h"
#include "logf.h"
#include "audio.h"
#if defined(HAVE_WM8975)
#include "wm8975.h"
#elif defined(HAVE_WM8758)
#include "wm8758.h"
#elif defined(HAVE_WM8731)
#include "wm8731l.h"
#endif
/* peaks */
static int play_peak_left, play_peak_right;
static unsigned long *rec_peak_addr;
static int rec_peak_left, rec_peak_right;
/** DMA **/
#if CONFIG_CPU == PP5020
#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f000000) >> 24)
#elif CONFIG_CPU == PP5002
#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23)
#elif CONFIG_CPU == PP5024
#define FIFO_FREE_COUNT 4 /* TODO: make this sensible */
#endif
/****************************************************************************
** Playback DMA transfer
**/
static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
/* NOTE: The order of these two variables is important if you use the iPod
assembler optimised fiq handler, so don't change it. */
unsigned short* p IBSS_ATTR;
size_t p_size IBSS_ATTR;
/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
has registers r8-r14 banked, and so does not need to be saved. This routine
uses only these registers, and so will never touch the stack unless it
actually needs to do so when calling pcm_callback_for_more. C version is
still included below for reference.
*/
#if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002
void fiq(void) ICODE_ATTR __attribute__((naked));
void fiq(void)
{
/* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual
* FIQ handler. r11 contains address of p (also set in crt0.S). Most other
* addresses we need are generated by using offsets with these two.
* r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG.
* r8 and r9 contains local copies of p_size and p respectively.
* r10 is a working register.
*/
asm volatile (
#if CONFIG_CPU == PP5002
"ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */
"ldr r10, [r10] \n\t"
"ldr r10, [r12, #0x1c]\n\t"
"bic r10, r10, #0x200 \n\t" /* clear interrupt */
"str r10, [r12, #0x1c]\n\t"
#else
"ldr r10, [r12] \n\t"
"bic r10, r10, #0x2 \n\t" /* clear interrupt */
"str r10, [r12] \n\t"
#endif
"ldr r8, [r11, #4] \n\t" /* r8 = p_size */
"ldr r9, [r11] \n\t" /* r9 = p */
".loop: \n\t"
"cmp r8, #0 \n\t" /* is p_size 0? */
"beq .more_data \n\t" /* if so, ask pcmbuf for more data */
".fifo_loop: \n\t"
#if CONFIG_CPU == PP5002
"ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */
"and r10, r10, #0x7800000\n\t"
"cmp r10, #0x800000 \n\t"
#else
"ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */
"and r10, r10, #0x3f0000\n\t"
"cmp r10, #0x10000 \n\t"
#endif
"bls .fifo_full \n\t" /* FIFO full, exit */
"ldr r10, [r9], #4 \n\t" /* load two samples */
"mov r10, r10, ror #16\n\t" /* put left sample at the top bits */
"str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */
"mov r10, r10, lsl #16\n\t" /* shift lower sample up */
"str r10, [r12, #0x40]\n\t" /* then write it */
"subs r8, r8, #4 \n\t" /* check if we have more samples */
"bne .fifo_loop \n\t" /* yes, continue */
".more_data: \n\t"
"stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */
"mov r0, r11 \n\t" /* r0 = &p */
"add r1, r11, #4 \n\t" /* r1 = &p_size */
"str r9, [r0] \n\t" /* save internal copies of variables back */
"str r8, [r1] \n\t"
"ldr r2, =pcm_callback_for_more\n\t"
"ldr r2, [r2] \n\t" /* get callback address */
"cmp r2, #0 \n\t" /* check for null pointer */
"movne lr, pc \n\t" /* call pcm_callback_for_more */
"bxne r2 \n\t"
"ldmia sp!, { r0-r3, r12, lr}\n\t"
"ldr r8, [r11, #4] \n\t" /* reload p_size and p */
"ldr r9, [r11] \n\t"
"cmp r8, #0 \n\t" /* did we actually get more data? */
"bne .loop \n\t" /* yes, continue to try feeding FIFO */
".dma_stop: \n\t" /* no more data, do dma_stop() and exit */
"ldr r10, =pcm_playing\n\t"
"strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */
"ldr r10, [r12] \n\t"
#if CONFIG_CPU == PP5002
"bic r10, r10, #0x4\n\t" /* disable playback FIFO */
"str r10, [r12] \n\t"
"ldr r10, [r12, #0x1c] \n\t"
"bic r10, r10, #0x200 \n\t" /* clear interrupt */
"str r10, [r12, #0x1c] \n\t"
#else
"bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */
"str r10, [r12] \n\t"
#endif
"mrs r10, cpsr \n\t"
"orr r10, r10, #0x40 \n\t" /* disable FIQ */
"msr cpsr_c, r10 \n\t"
".exit: \n\t"
"str r8, [r11, #4] \n\t"
"str r9, [r11] \n\t"
"subs pc, lr, #4 \n\t" /* FIQ specific return sequence */
".fifo_full: \n\t" /* enable IRQ and exit */
#if CONFIG_CPU == PP5002
"ldr r10, [r12, #0x1c]\n\t"
"orr r10, r10, #0x200 \n\t" /* set interrupt */
"str r10, [r12, #0x1c]\n\t"
#else
"ldr r10, [r12] \n\t"
"orr r10, r10, #0x2 \n\t" /* set interrupt */
"str r10, [r12] \n\t"
#endif
"b .exit \n\t"
);
}
#else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */
void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
void fiq(void)
{
/* Clear interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG &= ~0x2;
#elif CONFIG_CPU == PP5002
inl(0xcf001040);
IISFIFO_CFG &= ~(1<<9);
#endif
do {
while (p_size) {
if (FIFO_FREE_COUNT < 2) {
/* Enable interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x2;
#elif CONFIG_CPU == PP5002
IISFIFO_CFG |= (1<<9);
#endif
return;
}
IISFIFO_WR = (*(p++))<<16;
IISFIFO_WR = (*(p++))<<16;
p_size-=4;
}
/* p is empty, get some more data */
if (pcm_callback_for_more) {
pcm_callback_for_more((unsigned char**)&p,&p_size);
}
} while (p_size);
/* No more data, so disable the FIFO/FIQ */
pcm_play_dma_stop();
}
#endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */
void pcm_play_dma_start(const void *addr, size_t size)
{
p=(unsigned short*)addr;
p_size=size;
pcm_playing = true;
#if CONFIG_CPU == PP5020
/* setup I2S interrupt for FIQ */
outl(inl(0x6000402c) | I2S_MASK, 0x6000402c);
outl(I2S_MASK, 0x60004024);
#elif CONFIG_CPU == PP5024
#else
/* setup I2S interrupt for FIQ */
outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c);
outl(DMA_OUT_MASK, 0xcf001024);
#endif
/* Clear the FIQ disable bit in cpsr_c */
enable_fiq(fiq);
/* Enable playback FIFO */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x20000000;
#elif CONFIG_CPU == PP5002
IISCONFIG |= 0x4;
#endif
/* Fill the FIFO - we assume there are enough bytes in the pcm buffer to
fill the 32-byte FIFO. */
while (p_size > 0) {
if (FIFO_FREE_COUNT < 2) {
/* Enable interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x2;
#elif CONFIG_CPU == PP5002
IISFIFO_CFG |= (1<<9);
#endif
return;
}
IISFIFO_WR = (*(p++))<<16;
IISFIFO_WR = (*(p++))<<16;
p_size-=4;
}
}
/* Stops the DMA transfer and interrupt */
void pcm_play_dma_stop(void)
{
pcm_playing = false;
#if CONFIG_CPU == PP5020
/* Disable playback FIFO */
IISCONFIG &= ~0x20000000;
/* Disable the interrupt */
IISCONFIG &= ~0x2;
#elif CONFIG_CPU == PP5002
/* Disable playback FIFO */
IISCONFIG &= ~0x4;
/* Disable the interrupt */
IISFIFO_CFG &= ~(1<<9);
#endif
disable_fiq();
}
void pcm_play_pause_pause(void)
{
#if CONFIG_CPU == PP5020
/* Disable the interrupt */
IISCONFIG &= ~0x2;
/* Disable playback FIFO */
IISCONFIG &= ~0x20000000;
#elif CONFIG_CPU == PP5002
/* Disable the interrupt */
IISFIFO_CFG &= ~(1<<9);
/* Disable playback FIFO */
IISCONFIG &= ~0x4;
#endif
disable_fiq();
}
void pcm_play_pause_unpause(void)
{
/* Enable the FIFO and fill it */
enable_fiq(fiq);
/* Enable playback FIFO */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x20000000;
#elif CONFIG_CPU == PP5002
IISCONFIG |= 0x4;
#endif
/* Fill the FIFO - we assume there are enough bytes in the
pcm buffer to fill the 32-byte FIFO. */
while (p_size > 0) {
if (FIFO_FREE_COUNT < 2) {
/* Enable interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x2;
#elif CONFIG_CPU == PP5002
IISFIFO_CFG |= (1<<9);
#endif
return;
}
IISFIFO_WR = (*(p++))<<16;
IISFIFO_WR = (*(p++))<<16;
p_size-=4;
}
}
void pcm_set_frequency(unsigned int frequency)
{
(void)frequency;
pcm_freq = HW_SAMPR_DEFAULT;
}
size_t pcm_get_bytes_waiting(void)
{
return p_size;
}
#ifdef HAVE_PP5024_CODEC
void pcm_init(void)
{
}
#else
void pcm_init(void)
{
pcm_playing = false;
pcm_paused = false;
pcm_callback_for_more = NULL;
/* Initialize default register values. */
audiohw_init();
/* Power on */
audiohw_enable_output(true);
/* Unmute the master channel (DAC should be at zero point now). */
audiohw_mute(false);
/* Call pcm_play_dma_stop to initialize everything. */
pcm_play_dma_stop();
}
#endif /* HAVE_PP5024_CODEC */
/****************************************************************************
** Recording DMA transfer
**/
static short peak_l, peak_r IBSS_ATTR;
void fiq_record(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
void fiq_record(void)
{
short value;
pcm_more_callback_type2 more_ready;
int status = 0;
/* Clear interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG &= ~0x01;
#elif CONFIG_CPU == PP5002
/* TODO */
#endif
while (p_size > 0) {
if (FIFO_FREE_COUNT < 2) {
/* enable interrupt */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x01;
#elif CONFIG_CPU == PP5002
/* TODO */
#endif
return;
}
value = (unsigned short)(IISFIFO_RD >> 16);
if (value > peak_l) peak_l = value;
else if (-value > peak_l) peak_l = -value;
*(p++) = value;
value = (unsigned short)(IISFIFO_RD >> 16);
if (value > peak_r) peak_r = value;
else if (-value > peak_r) peak_r = -value;
*(p++) = value;
p_size -= 4;
/* If we have filled the current chunk, start a new one */
if (p_size == 0) {
rec_peak_left = peak_l;
rec_peak_right = peak_r;
peak_l = peak_r = 0;
}
}
more_ready = pcm_callback_more_ready;
if (more_ready != NULL && more_ready(status) >= 0)
return;
/* Finished recording */
pcm_rec_dma_stop();
}
/* Continue transferring data in */
void pcm_record_more(void *start, size_t size)
{
rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
p = start;
p_size = size; /* Bytes to transfer */
#if CONFIG_CPU == PP5020
IISCONFIG |= 0x01;
#elif CONFIG_CPU == PP5002
/* TODO */
#endif
}
void pcm_rec_dma_stop(void)
{
logf("pcm_rec_dma_stop");
/* disable fifo */
IISCONFIG &= ~0x10000000;
disable_fiq();
pcm_recording = false;
}
void pcm_rec_dma_start(void *addr, size_t size)
{
logf("pcm_rec_dma_start");
pcm_recording = true;
peak_l = peak_r = 0;
p_size = size;
p = addr;
/* setup FIQ */
outl(inl(0x6000402c) | I2S_MASK, 0x6000402c);
outl(I2S_MASK, 0x60004024);
/* interrupt on full fifo */
outl(inl(0x70002800) | 0x1, 0x70002800);
/* enable record fifo */
outl(inl(0x70002800) | 0x10000000, 0x70002800);
enable_fiq(fiq_record);
}
void pcm_close_recording(void)
{
logf("pcm_close_recording");
pcm_rec_dma_stop();
#if (CONFIG_CPU == PP5020)
disable_fiq();
/* disable fifo */
IISCONFIG &= ~0x10000000;
/* Clear interrupt */
IISCONFIG &= ~0x01;
#endif
} /* pcm_close_recording */
void pcm_init_recording(void)
{
logf("pcm_init_recording");
pcm_recording = false;
pcm_callback_more_ready = NULL;
#if (CONFIG_CPU == PP5020)
#if defined(IPOD_COLOR) || defined (IPOD_4G)
/* The usual magic from IPL - I'm guessing this configures the headphone
socket to be input or output - in this case, input. */
GPIOI_OUTPUT_VAL &= ~0x40;
GPIOA_OUTPUT_VAL &= ~0x4;
#endif
/* Setup the recording FIQ handler */
*((unsigned int*)(15*4)) = (unsigned int)&fiq_record;
#endif
pcm_rec_dma_stop();
} /* pcm_init */
void pcm_calculate_rec_peaks(int *left, int *right)
{
*left = rec_peak_left;
*right = rec_peak_right;
}
/*
* This function goes directly into the DMA buffer to calculate the left and
* right peak values. To avoid missing peaks it tries to look forward two full
* peek periods (2/HZ sec, 100% overlap), although it's always possible that
* the entire period will not be visible. To reduce CPU load it only looks at
* every third sample, and this can be reduced even further if needed (even
* every tenth sample would still be pretty accurate).
*/
/* Check for a peak every PEAK_STRIDE samples */
#define PEAK_STRIDE 3
/* Up to 1/50th of a second of audio for peak calculation */
/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
#define PEAK_SAMPLES (44100/50)
void pcm_calculate_peaks(int *left, int *right)
{
short *addr;
short *end;
{
size_t samples = p_size / 4;
addr = p;
if (samples > PEAK_SAMPLES)
samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
else
samples -= MIN(PEAK_STRIDE - 1, samples);
end = &addr[samples * 2];
}
if (left && right) {
int left_peak = 0, right_peak = 0;
while (addr < end) {
int value;
if ((value = addr [0]) > left_peak)
left_peak = value;
else if (-value > left_peak)
left_peak = -value;
if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
right_peak = value;
else if (-value > right_peak)
right_peak = -value;
addr = &addr[PEAK_STRIDE * 2];
}
*left = left_peak;
*right = right_peak;
}
else if (left || right) {
int peak_value = 0, value;
if (right)
addr += (PEAK_STRIDE | 1);
while (addr < end) {
if ((value = addr [0]) > peak_value)
peak_value = value;
else if (-value > peak_value)
peak_value = -value;
addr += PEAK_STRIDE * 2;
}
if (left)
*left = peak_value;
else
*right = peak_value;
}
}