Index: apps/debug_menu.c =================================================================== --- apps/debug_menu.c (revision 19550) +++ apps/debug_menu.c (working copy) @@ -72,6 +72,7 @@ #if CONFIG_TUNER #include "tuner.h" #include "radio.h" +#include "fmradio_i2c.h" #endif #endif @@ -2306,6 +2307,34 @@ (unsigned)nfo.write_regs[2], (unsigned)nfo.write_regs[3], (unsigned)nfo.write_regs[4]); #endif +#if (CONFIG_TUNER & SI4700) + unsigned char buf[32]; + fmradio_i2c_read(0x20, buf, sizeof(buf)); + simplelist_addline(SIMPLELIST_ADD_LINE, + "%02X%02X %02X%02X %02X%02X %02X%02X", + (unsigned)buf[12], (unsigned)buf[13], + (unsigned)buf[14], (unsigned)buf[15], + (unsigned)buf[16], (unsigned)buf[17], + (unsigned)buf[18], (unsigned)buf[19]); + simplelist_addline(SIMPLELIST_ADD_LINE, + "%02X%02X %02X%02X %02X%02X %02X%02X", + (unsigned)buf[20], (unsigned)buf[21], + (unsigned)buf[22], (unsigned)buf[23], + (unsigned)buf[24], (unsigned)buf[25], + (unsigned)buf[26], (unsigned)buf[27]); + simplelist_addline(SIMPLELIST_ADD_LINE, + "%02X%02X %02X%02X %02X%02X %02X%02X", + (unsigned)buf[28], (unsigned)buf[29], + (unsigned)buf[30], (unsigned)buf[31], + (unsigned)buf[0], (unsigned)buf[1], + (unsigned)buf[2], (unsigned)buf[3]); + simplelist_addline(SIMPLELIST_ADD_LINE, + "%02X%02X %02X%02X %02X%02X %02X%02X", + (unsigned)buf[4], (unsigned)buf[5], + (unsigned)buf[6], (unsigned)buf[7], + (unsigned)buf[8], (unsigned)buf[9], + (unsigned)buf[10], (unsigned)buf[11]); +#endif return ACTION_REDRAW; } static bool dbg_fm_radio(void) Index: apps/keymaps/keymap-clip.c =================================================================== --- apps/keymaps/keymap-clip.c (revision 19550) +++ apps/keymaps/keymap-clip.c (working copy) @@ -220,12 +220,19 @@ /** FM Radio Screen **/ static const struct button_mapping button_context_radio[] = { - { ACTION_FM_MENU, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE }, - { ACTION_FM_PRESET, BUTTON_SELECT|BUTTON_REL, BUTTON_NONE }, - { ACTION_FM_STOP, BUTTON_POWER|BUTTON_REL, BUTTON_NONE }, - { ACTION_FM_MODE, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP }, - { ACTION_FM_EXIT, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE }, + /* Copied from keymap-c200.c 20081207. Feel free to suggest a better one.*/ + { ACTION_NONE, BUTTON_UP, BUTTON_NONE }, + { ACTION_FM_MENU, BUTTON_DOWN, BUTTON_NONE }, + { ACTION_FM_PRESET, BUTTON_SELECT, BUTTON_NONE }, + { ACTION_FM_STOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP }, +/* { ACTION_FM_MODE, BUTTON_REC, BUTTON_NONE }, */ + { ACTION_FM_EXIT, BUTTON_POWER|BUTTON_REL, BUTTON_POWER }, { ACTION_FM_PLAY, BUTTON_UP|BUTTON_REL, BUTTON_UP }, + { ACTION_SETTINGS_INC, BUTTON_VOL_UP, BUTTON_NONE }, + { ACTION_SETTINGS_INCREPEAT,BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_SETTINGS_DEC, BUTTON_VOL_DOWN, BUTTON_NONE }, + { ACTION_SETTINGS_DECREPEAT,BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE }, + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS) }; /* button_context_radio */ Index: firmware/export/config-clip.h =================================================================== --- firmware/export/config-clip.h (revision 19550) +++ firmware/export/config-clip.h (working copy) @@ -20,13 +20,12 @@ #define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ #define REC_SAMPR_DEFAULT SAMPR_22 +#endif /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ -#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) +#define INPUT_SRC_CAPS (SRC_CAP_FMRADIO) -#endif - /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP @@ -103,10 +102,8 @@ #define AB_REPEAT_ENABLE 1 /* FM Tuner */ -#if 0 /* disabled since there is no driver (yet) */ #define CONFIG_TUNER SI4700 /* in fact SI4702 but let's hope it's compatible */ //#define HAVE_TUNER_PWR_CTRL -#endif /* Define this for LCD backlight available */ #define HAVE_BACKLIGHT @@ -193,3 +190,4 @@ #define DEFAULT_REC_MIC_GAIN 23 #define DEFAULT_REC_LEFT_GAIN 23 #define DEFAULT_REC_RIGHT_GAIN 23 + Index: firmware/export/config-fuze.h =================================================================== --- firmware/export/config-fuze.h (revision 19550) +++ firmware/export/config-fuze.h (working copy) @@ -19,8 +19,8 @@ /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ +#endif #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) -#endif /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP @@ -99,7 +99,7 @@ #define AB_REPEAT_ENABLE 1 /* FM Tuner - suspected to be the SI4702 */ -//#define CONFIG_TUNER SI4700 +#define CONFIG_TUNER SI4700 /* #define HAVE_TUNER_PWR_CTRL */ /* Define this for LCD backlight available */ Index: firmware/export/config-e200v2.h =================================================================== --- firmware/export/config-e200v2.h (revision 19550) +++ firmware/export/config-e200v2.h (working copy) @@ -17,10 +17,10 @@ #define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ #define REC_SAMPR_DEFAULT SAMPR_22 +#endif /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) -#endif /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP @@ -99,7 +99,7 @@ #define AB_REPEAT_ENABLE 1 /* FM Tuner - suspected to be the SI4702 */ -//#define CONFIG_TUNER SI4700 +#define CONFIG_TUNER SI4700 /* #define HAVE_TUNER_PWR_CTRL */ /* Define this for LCD backlight available */ Index: firmware/export/fmradio_i2c.h =================================================================== --- firmware/export/fmradio_i2c.h (revision 19550) +++ firmware/export/fmradio_i2c.h (working copy) @@ -22,6 +22,8 @@ #ifndef FMRADIO_I2C_H #define FMRADIO_I2C_H +#include + void fmradio_i2c_init(void); void fmradio_i2c_enable(bool enable); int fmradio_i2c_write(unsigned char address, const unsigned char* buf, int count); Index: firmware/target/arm/as3525/system-as3525.c =================================================================== --- firmware/target/arm/as3525/system-as3525.c (revision 19550) +++ firmware/target/arm/as3525/system-as3525.c (working copy) @@ -26,6 +26,7 @@ #include "ascodec-target.h" #include "dma-target.h" #include "clock-target.h" +#include "fmradio_i2c.h" #define default_interrupt(name) \ extern __attribute__((weak,alias("UIRQ"))) void name (void) @@ -263,6 +264,10 @@ * We don't need the power button in the bootloader. */ ascodec_init(); ascodec_write(AS3514_CVDD_DCDC3, ascodec_read(AS3514_CVDD_DCDC3) & (1<<2)); + +#ifdef CONFIG_TUNER + fmradio_i2c_init(); +#endif #endif /* !BOOTLOADER */ #ifdef HAVE_ADJUSTABLE_CPU_FREQ Index: firmware/target/arm/as3525/audio-as3525.c =================================================================== --- firmware/target/arm/as3525/audio-as3525.c (revision 19550) +++ firmware/target/arm/as3525/audio-as3525.c (working copy) @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright © 2008 Rafaël Carré + * Copyright (C) 2007 by Michael Sevakis * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -23,15 +23,70 @@ #include "audio.h" #include "sound.h" -/* TODO */ +int audio_channels = 2; +int audio_output_source = AUDIO_SRC_PLAYBACK; void audio_set_output_source(int source) { - (void)source; -} + if ((unsigned)source >= AUDIO_NUM_SOURCES) + source = AUDIO_SRC_PLAYBACK; + audio_output_source = source; +} /* audio_set_output_source */ + void audio_input_mux(int source, unsigned flags) { - (void)source; - (void)flags; + static int last_source = AUDIO_SRC_PLAYBACK; + static bool last_recording = false; + bool recording = flags & SRCF_RECORDING; + + switch (source) + { + default: /* playback - no recording */ + source = AUDIO_SRC_PLAYBACK; + case AUDIO_SRC_PLAYBACK: + audio_channels = 2; + if (source != last_source) + { + audiohw_set_monitor(false); +#if 0 + audiohw_disable_recording(); +#endif } + break; +#if 0 + case AUDIO_SRC_MIC: /* recording only */ + audio_channels = 1; + if (source != last_source) + { + audiohw_set_monitor(false); + audiohw_enable_recording(true); /* source mic */ + } + break; +#endif + case AUDIO_SRC_FMRADIO: /* recording and playback */ + audio_channels = 2; +#if 0 + if (source == last_source && recording == last_recording) + break; + + last_recording = recording; + if (recording) + { + audiohw_set_monitor(false); + audiohw_enable_recording(false); + } + else + { + audiohw_disable_recording(); +#endif + audiohw_set_monitor(true); /* line 1 analog audio path */ +#if 0 + } +#endif + break; + } /* end switch */ + + last_source = source; +} /* audio_input_mux */ + Index: firmware/drivers/audio/as3514.c =================================================================== --- firmware/drivers/audio/as3514.c (revision 19550) +++ firmware/drivers/audio/as3514.c (working copy) @@ -220,8 +220,8 @@ as3514.vol_r = vol_r; if (source == SOURCE_LINE_IN1_ANALOG) { - mix_reg_r = AS3514_LINE_IN1_R; - mix_reg_l = AS3514_LINE_IN1_L; + mix_reg_r = AS3514_LINE_IN2_R; + mix_reg_l = AS3514_LINE_IN2_L; } else { mix_reg_r = AS3514_DAC_R; mix_reg_l = AS3514_DAC_L; @@ -396,6 +396,8 @@ as3514_write_masked(AS3514_ADC_L, left, AS3514_VOL_MASK); } +#endif /* HAVE_RECORDING */ + /** * Enable line in 1 analog monitoring * @@ -405,9 +407,9 @@ if (enable) { source = SOURCE_LINE_IN1_ANALOG; - as3514_set(AS3514_AUDIOSET1, AUDIOSET1_LIN1_on); - as3514_set(AS3514_LINE_IN1_R, LINE_IN1_R_LI1R_MUTE_off); - as3514_set(AS3514_LINE_IN1_L, LINE_IN1_L_LI1L_MUTE_off); + as3514_set(AS3514_AUDIOSET1, AUDIOSET1_LIN2_on); + as3514_set(AS3514_LINE_IN2_R, LINE_IN2_R_LI2R_MUTE_off); + as3514_set(AS3514_LINE_IN2_L, LINE_IN2_L_LI2L_MUTE_off); } else { as3514_clear(AS3514_AUDIOSET1, AUDIOSET1_LIN1_on); @@ -418,4 +420,4 @@ /* Sync mixer volume */ audiohw_set_master_vol(as3514.vol_l, as3514.vol_r); } -#endif /* HAVE_RECORDING */ + Index: firmware/drivers/tuner/si4700.c =================================================================== --- firmware/drivers/tuner/si4700.c (revision 19550) +++ firmware/drivers/tuner/si4700.c (working copy) @@ -31,24 +31,84 @@ #define I2C_ADR 0x20 -/* I2C writes start at register 02h so the first two bytes are - 02h, next two 03h, etc. */ -static unsigned char write_bytes[8]; /* registers 02 - 05 */ +#define DEVICEID 0x0 +#define CHIPID 0x1 +#define POWERCFG 0x2 +#define CHANNEL 0x3 +#define SYSCONFIG1 0x4 +#define SYSCONFIG2 0x5 +#define SYSCONFIG3 0x6 +#define TEST1 0x7 +#define TEST2 0x8 +#define BOOTCONFIG 0x9 +#define STATUSRSSI 0xA +#define READCHAN 0xB +#define RDSA 0xC +#define RDSB 0xD +#define RDSC 0xE +#define RDSD 0xF + +/* some models use the internal 32 kHz oscillator which needs special attention + during initialisation, power-up and power-down. +*/ +#if defined(SANSA_CLIP) || defined(SANSA_E200V2) || defined(SANSA_FUZE) +#define USE_INTERNAL_OSCILLATOR +#endif + static bool tuner_present = false; +static unsigned short cache[16]; +/* reads registers from radio at offset 0x0A into cache */ +static void si4700_read(int len) +{ + int i; + unsigned char buf[32]; + unsigned char *ptr = buf; + unsigned short data; + + fmradio_i2c_read(I2C_ADR, buf, len * 2); + for (i = 0; i < len; i++) { + data = ptr[0] << 8 | ptr[1]; + cache[(i + STATUSRSSI) & 0xF] = data; + ptr += 2; + } +} + +/* writes registers from cache to radio at offset 0x02 */ +static void si4700_write(int len) +{ + int i; + unsigned char buf[32]; + unsigned char *ptr = buf; + unsigned short data; + + for (i = 0; i < len; i++) { + data = cache[(i + POWERCFG) & 0xF]; + *ptr++ = (data >> 8) & 0xFF; + *ptr++ = data & 0xFF; + } + fmradio_i2c_write(I2C_ADR, buf, len * 2); +} + + void si4700_init(void) { - unsigned char read_bytes[32]; tuner_power(true); - fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes)); - if ((read_bytes[12] << 8 | read_bytes[13]) == 0x1242) + /* read all registers */ + si4700_read(16); + + /* check device id */ + if (cache[DEVICEID] == 0x1242) { tuner_present = true; - /* fill in the initial values in write_bytes */ - memcpy(&write_bytes[0], &read_bytes[16], sizeof(write_bytes)); - /* -6dB volume, keep everything else as default */ - write_bytes[7] = (write_bytes[7] & ~0xf) | 0xc; + +#ifdef USE_INTERNAL_OSCILLATOR + /* enable the internal oscillator */ + cache[TEST1] |= (1 << 15); /* XOSCEN */ + si4700_write(6); + sleep(HZ/2); +#endif } tuner_power(false); @@ -56,20 +116,24 @@ static void si4700_tune(void) { - unsigned char read_bytes[1]; + bool tuned; - write_bytes[2] |= (1 << 7); /* Set TUNE high to start tuning */ - fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); - do { - sleep(HZ/50); - fmradio_i2c_read(I2C_ADR, read_bytes, 1); + cache[CHANNEL] |= (1 << 15); /* Set TUNE high to start tuning */ + si4700_write(2); + + /* tuning should be done within 60 ms according to the datasheet */ + sleep(HZ/14); + si4700_read(2); + + tuned = ((cache[CHANNEL] & 0x3ff) == (cache[READCHAN] & 0x3ff)) && + (cache[STATUSRSSI] & (1 << 14)); + + cache[CHANNEL] &= ~(1 << 15); /* Set TUNE low */ + si4700_write(2); } - while (!(read_bytes[0] & (1 << 6))); /* STC high == Seek/Tune complete */ - - write_bytes[2] &= ~(1 << 7); /* Set TUNE low */ - fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + while (!tuned); } /* tuner abstraction layer: set something to the tuner */ @@ -80,13 +144,25 @@ case RADIO_SLEEP: if (value) { - write_bytes[1] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */ + /* power down */ + cache[POWERCFG] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */ + si4700_write(1); } else { - write_bytes[1] = 1; /* ENABLE high, DISABLE low */ + /* power up */ + cache[POWERCFG] = 1; /* ENABLE high, DISABLE low */ + si4700_write(1); + sleep(110 * HZ / 1000); + + /* update register cache */ + si4700_read(16); + + /* -6dB volume, keep everything else as default */ + cache[SYSCONFIG2] = (cache[SYSCONFIG2] & ~0xF) | 0xC; + si4700_write(5); } - break; + return 1; case RADIO_FREQUENCY: { @@ -95,9 +171,9 @@ 200000, 100000, 50000 }; unsigned int chan; - unsigned int spacing = spacings[(write_bytes[7] >> 4) & 3] ; + unsigned int spacing = spacings[(cache[5] >> 4) & 3] ; - if (write_bytes[7] & (3 << 6)) /* check BAND */ + if (cache[SYSCONFIG2] & (3 << 6)) /* check BAND */ { chan = (value - 76000000) / spacing; } @@ -106,9 +182,7 @@ chan = (value - 87500000) / spacing; } - write_bytes[2] = (write_bytes[2] & ~3) | ((chan & (3 << 8)) >> 8); - write_bytes[3] = (chan & 0xff); - fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + cache[CHANNEL] = (cache[CHANNEL] & ~0x3FF) | chan; si4700_tune(); return 1; } @@ -121,12 +195,12 @@ if (value) { /* mute */ - write_bytes[0] &= ~(1 << 6); + cache[POWERCFG] &= ~(1 << 14); } else { /* unmute */ - write_bytes[0] |= (1 << 6); + cache[POWERCFG] |= (1 << 14); } break; @@ -135,20 +209,20 @@ const struct si4700_region_data *rd = &si4700_region_data[value]; - write_bytes[4] = ((write_bytes[4] & ~(1 << 3)) | (rd->deemphasis << 3)); - write_bytes[7] = ((write_bytes[7] & ~(3 << 6)) | (rd->band << 6)); - write_bytes[7] = ((write_bytes[7] & ~(3 << 4)) | (rd->spacing << 4)); + cache[SYSCONFIG1] = (cache[SYSCONFIG1] & ~(1 << 11)) | (rd->deemphasis << 11); + cache[SYSCONFIG2] = (cache[SYSCONFIG2] & ~(3 << 6)) | (rd->band << 6); + cache[SYSCONFIG2] = (cache[SYSCONFIG2] & ~(3 << 4)) | (rd->spacing << 4); break; } case RADIO_FORCE_MONO: if (value) { - write_bytes[0] |= (1 << 5); + cache[POWERCFG] |= (1 << 13); } else { - write_bytes[0] &= ~(1 << 5); + cache[POWERCFG] &= ~(1 << 13); } break; @@ -156,15 +230,13 @@ return -1; } - fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + si4700_write(5); return 1; } /* tuner abstraction layer: read something from the tuner */ int si4700_get(int setting) { - /* I2C reads start with register 0xA */ - unsigned char read_bytes[1]; int val = -1; /* default for unsupported query */ switch(setting) @@ -178,8 +250,8 @@ break; case RADIO_STEREO: - fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes)); - val = (read_bytes[0] & 1); /* ST high == Stereo */ + si4700_read(1); + val = (cache[STATUSRSSI] & (1 << 8)); /* ST high == Stereo */ break; }