From b15a523870d6aa45b38b92547053beb986b92d9a Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sat, 26 Jun 2010 10:07:17 +0000 Subject: e200v1/c200v1: Implement limited samplerate switching. Rates 24kHz and below are being a bear as far as minor crackling at higher amplitude-- leave them out for the time being since no solution is currently evident. 48, 44, 32 (rec rates 24, 22, 16) seem perfectly fine. I'm betting c200 is ok to include because it uses the same setup as e200. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27139 a1c6a512-1295-4272-9138-f99709370657 --- firmware/drivers/audio/as3514.c | 9 +++ firmware/drivers/audio/sdl.c | 4 ++ firmware/export/as3514.h | 1 + firmware/export/config/sansac200.h | 4 +- firmware/export/config/sansae200.h | 9 ++- firmware/export/pcm.h | 13 ++++- firmware/export/pcm_sampr.h | 20 +++++++ firmware/pcm.c | 20 ++++++- firmware/target/arm/i2s-pp.c | 6 ++ firmware/target/arm/sandisk/audio-c200_e200.c | 83 +++++++++++++++++++++++++++ 10 files changed, 160 insertions(+), 9 deletions(-) (limited to 'firmware') diff --git a/firmware/drivers/audio/as3514.c b/firmware/drivers/audio/as3514.c index 34dc9ad29f..957aba8dbb 100644 --- a/firmware/drivers/audio/as3514.c +++ b/firmware/drivers/audio/as3514.c @@ -327,6 +327,15 @@ void audiohw_close(void) void audiohw_set_frequency(int fsel) { +#if defined(SANSA_E200) || defined(SANSA_C200) + if ((unsigned)fsel >= HW_NUM_FREQ) + fsel = HW_FREQ_DEFAULT; + + as3514_write(AS3514_PLLMODE, hw_freq_sampr[fsel] < 24000 ? + PLLMODE_LRCK_8_23 : PLLMODE_LRCK_24_48); + + audiohw_set_sampr_dividers(fsel); +#endif (void)fsel; } diff --git a/firmware/drivers/audio/sdl.c b/firmware/drivers/audio/sdl.c index c063192873..f4c622d7a6 100644 --- a/firmware/drivers/audio/sdl.c +++ b/firmware/drivers/audio/sdl.c @@ -162,6 +162,10 @@ void audiohw_set_eq_band_width(unsigned int band, int value) void audiohw_set_depth_3d(int value) { (void)value; } #endif +#if defined(HAVE_SAMPR_TYPE_REC) +unsigned int pcm_sampr_type_rec_to_play(int samplerate) + { return samplerate; } +#endif #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) int mas_codec_readreg(int reg) { diff --git a/firmware/export/as3514.h b/firmware/export/as3514.h index ffcf9186e7..de975ce3c6 100644 --- a/firmware/export/as3514.h +++ b/firmware/export/as3514.h @@ -28,6 +28,7 @@ extern int tenthdb2master(int db); extern void audiohw_set_master_vol(int vol_l, int vol_r); extern void audiohw_set_lineout_vol(int vol_l, int vol_r); +extern void audiohw_set_sampr_dividers(int fsel); /* Register Descriptions */ diff --git a/firmware/export/config/sansac200.h b/firmware/export/config/sansac200.h index 5905b6a3f9..6c05ba5a1c 100644 --- a/firmware/export/config/sansac200.h +++ b/firmware/export/config/sansac200.h @@ -7,12 +7,12 @@ #define MODEL_NUMBER 20 #define MODEL_NAME "Sandisk Sansa c200 series" -#define HW_SAMPR_CAPS (SAMPR_CAP_44) +#define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32) /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS (SAMPR_CAP_22) +#define REC_SAMPR_CAPS (SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16) #define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ #define REC_SAMPR_DEFAULT SAMPR_22 diff --git a/firmware/export/config/sansae200.h b/firmware/export/config/sansae200.h index 8f4f3235dc..501e9eac37 100644 --- a/firmware/export/config/sansae200.h +++ b/firmware/export/config/sansae200.h @@ -7,15 +7,20 @@ #define MODEL_NUMBER 16 #define MODEL_NAME "Sandisk Sansa e200 series" -#define HW_SAMPR_CAPS (SAMPR_CAP_44) +#define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32) /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS (SAMPR_CAP_22) +#define REC_SAMPR_CAPS (SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16) #define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ #define REC_SAMPR_DEFAULT SAMPR_22 +/* because the samplerates don't match at each point, we must be able to + * tell PCM which set of rates to use. not needed if recording rates are + * a simple subset of playback rates and are equal values. */ +#define CONFIG_SAMPR_TYPES + /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) diff --git a/firmware/export/pcm.h b/firmware/export/pcm.h index 02fa04cb7e..e388e29f0c 100644 --- a/firmware/export/pcm.h +++ b/firmware/export/pcm.h @@ -54,9 +54,16 @@ typedef void (*pcm_play_callback_type)(unsigned char **start, size_t *size); typedef void (*pcm_rec_callback_type)(int status, void **start, size_t *size); -/* set the pcm frequency - use values in hw_sampr_list - * use -1 for the default frequency - */ +/* set the pcm frequency - use values in hw_sampr_list + * when CONFIG_SAMPR_TYPES is #defined, or-in SAMPR_TYPE_* fields with + * frequency value. SAMPR_TYPE_PLAY is 0 and the default if none is + * specified. */ +#ifdef CONFIG_SAMPR_TYPES +#ifdef SAMPR_TYPE_REC +unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate); +#endif +#endif /* CONFIG_SAMPR_TYPES */ + void pcm_set_frequency(unsigned int samplerate); /* apply settings to hardware immediately */ void pcm_apply_settings(void); diff --git a/firmware/export/pcm_sampr.h b/firmware/export/pcm_sampr.h index 2204b9c059..54db8a1dfb 100644 --- a/firmware/export/pcm_sampr.h +++ b/firmware/export/pcm_sampr.h @@ -312,4 +312,24 @@ enum rec_freq_indexes extern const unsigned long rec_freq_sampr[REC_NUM_FREQ]; #endif /* HAVE_RECORDING */ +#ifdef CONFIG_SAMPR_TYPES + +#define SAMPR_TYPE_MASK (0xff << 24) +#define SAMPR_TYPE_PLAY (0x00 << 24) +#ifdef HAVE_RECORDING +#define SAMPR_TYPE_REC (0x01 << 24) +#endif + +unsigned int sampr_type_rec_to_play(unsigned int samplerate); + +#else /* ndef CONFIG_SAMPR_TYPES */ + +/* Types are ignored and == 0 */ +#define SAMPR_TYPE_PLAY 0 +#ifdef HAVE_RECORDING +#define SAMPR_TYPE_REC 0 +#endif + +#endif /* CONFIG_SAMPR_TYPES */ + #endif /* PCM_SAMPR_H */ diff --git a/firmware/pcm.c b/firmware/pcm.c index 8080823077..72fe23cb16 100644 --- a/firmware/pcm.c +++ b/firmware/pcm.c @@ -361,8 +361,24 @@ void pcm_set_frequency(unsigned int samplerate) { logf("pcm_set_frequency"); - int index = round_value_to_list32(samplerate, hw_freq_sampr, - HW_NUM_FREQ, false); + int index; + +#ifdef CONFIG_SAMPR_TYPES + unsigned int type = samplerate & SAMPR_TYPE_MASK; + samplerate &= ~SAMPR_TYPE_MASK; + +#ifdef SAMPR_TYPE_REC + /* For now, supported targets have direct conversion when configured with + * CONFIG_SAMPR_TYPES. + * Some hypothetical target with independent rates would need slightly + * different handling throughout this source. */ + if (type == SAMPR_TYPE_REC) + samplerate = pcm_sampr_type_rec_to_play(samplerate); +#endif +#endif /* CONFIG_SAMPR_TYPES */ + + index = round_value_to_list32(samplerate, hw_freq_sampr, + HW_NUM_FREQ, false); if (samplerate != hw_freq_sampr[index]) index = HW_FREQ_DEFAULT; /* Invalid = default */ diff --git a/firmware/target/arm/i2s-pp.c b/firmware/target/arm/i2s-pp.c index c9d66d53ae..83f39515c4 100644 --- a/firmware/target/arm/i2s-pp.c +++ b/firmware/target/arm/i2s-pp.c @@ -28,6 +28,10 @@ #include "system.h" #include "cpu.h" #include "i2s.h" +#if defined (SANSA_E200) || defined (SANSA_C200) +#include "audiohw.h" +#include "pcm_sampr.h" +#endif #if CONFIG_CPU == PP5002 void i2s_reset(void) @@ -70,6 +74,8 @@ void i2s_reset(void) IISCLK = (IISCLK & ~0x1ff) | 31; IISDIV = (IISDIV & ~0xc0000000) | (2 << 30); IISDIV = (IISDIV & ~0x3f) | 16; +#elif defined (SANSA_E200) || defined (SANSA_C200) + audiohw_set_sampr_dividers(HW_FREQ_DEFAULT); #else IISCLK = (IISCLK & ~0x1ff) | 33; IISDIV = 7; diff --git a/firmware/target/arm/sandisk/audio-c200_e200.c b/firmware/target/arm/sandisk/audio-c200_e200.c index 1d78e71541..0037bac58b 100644 --- a/firmware/target/arm/sandisk/audio-c200_e200.c +++ b/firmware/target/arm/sandisk/audio-c200_e200.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "audio.h" #include "sound.h" +#include "general.h" int audio_channels = 2; int audio_output_source = AUDIO_SRC_PLAYBACK; @@ -92,3 +93,85 @@ void audio_input_mux(int source, unsigned flags) last_source = source; } /* audio_input_mux */ + + +void audiohw_set_sampr_dividers(int fsel) +{ + /* Seems to predivide 24MHz by 2 for a source clock of 12MHz. Maybe + * there's a way to set that? */ + static const struct + { + unsigned char iisclk; + unsigned char iisdiv; + } regvals[HW_NUM_FREQ] = + { + /* 8kHz - 24kHz work well but there seems to be a minor crackling + * issue for playback at times and all possibilities were checked + * for the divisors without any positive change. + * 32kHz - 48kHz seem fine all around. */ +#if 0 + [HW_FREQ_8] = /* CLK / 1500 (8000Hz) */ + { + .iisclk = 24, + .iisdiv = 59, + }, + [HW_FREQ_11] = /* CLK / 1088 (~11029.41Hz) */ + { + .iisclk = 33, + .iisdiv = 31, + }, + [HW_FREQ_12] = /* CLK / 1000 (120000Hz) */ + { + .iisclk = 49, + .iisdiv = 19, + }, + [HW_FREQ_16] = /* CLK / 750 (16000Hz) */ + { + .iisclk = 24, + .iisdiv = 29, + }, + [HW_FREQ_22] = /* CLK / 544 (~22058.82Hz) */ + { + .iisclk = 33, + .iisdiv = 15, + }, + [HW_FREQ_24] = /* CLK / 500 (24000Hz) */ + { + .iisclk = 49, + .iisdiv = 9, + }, +#endif + [HW_FREQ_32] = /* CLK / 375 (32000Hz) */ + { + .iisclk = 24, + .iisdiv = 14, + }, + [HW_FREQ_44] = /* CLK / 272 (~44117.68Hz) */ + { + .iisclk = 33, + .iisdiv = 7, + }, + [HW_FREQ_48] = /* CLK / 250 (48000Hz) */ + { + .iisclk = 49, + .iisdiv = 4, + }, + /* going a bit higher would be nice to get 64kHz play, 32kHz rec, but a + * close enough division isn't obtainable unless CLK can be changed */ + }; + + IISCLK = (IISCLK & ~0x17ff) | regvals[fsel].iisclk; + IISDIV = (IISDIV & ~0xc000003f) | regvals[fsel].iisdiv; +} + +unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate) +{ + /* Check if the samplerate is in the list of recordable rates. + * Fail to default if not */ + int index = round_value_to_list32(samplerate, rec_freq_sampr, + REC_NUM_FREQ, false); + if (samplerate != rec_freq_sampr[index]) + return HW_SAMPR_DEFAULT; + + return samplerate * 2; /* Recording rates are 1/2 the codec clock */ +} -- cgit