Index: apps/debug_menu.c =================================================================== --- apps/debug_menu.c (revision 19376) +++ 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 @@ -2234,6 +2235,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 19376) +++ 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 }, - { ACTION_FM_PLAY, BUTTON_UP|BUTTON_REL, BUTTON_UP }, + /* 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 19376) +++ 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/SOURCES =================================================================== --- firmware/SOURCES (revision 19376) +++ firmware/SOURCES (working copy) @@ -1074,6 +1074,8 @@ target/arm/as3525/sansa-clip/button-clip.c target/arm/as3525/sansa-clip/backlight-clip.c #ifndef BOOTLOADER +drivers/generic_i2c.c +target/arm/as3525/fmradio-i2c-as3525.c target/arm/as3525/sansa-clip/powermgmt-clip.c #endif /* !BOOTLOADER */ #endif /* !SIMULATOR */ Index: firmware/target/arm/as3525/system-as3525.c =================================================================== --- firmware/target/arm/as3525/system-as3525.c (revision 19376) +++ firmware/target/arm/as3525/system-as3525.c (working copy) @@ -263,6 +263,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 19376) +++ 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,12 +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) { -} + 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) { -} + 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 19376) +++ firmware/drivers/audio/as3514.c (working copy) @@ -221,8 +221,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; @@ -397,6 +397,8 @@ as3514_write_masked(AS3514_ADC_L, left, AS3514_VOL_MASK); } +#endif /* HAVE_RECORDING */ + /** * Enable line in 1 analog monitoring * @@ -406,9 +408,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); @@ -419,4 +421,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 19376) +++ firmware/drivers/tuner/si4700.c (working copy) @@ -33,43 +33,67 @@ /* 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 */ static bool tuner_present = false; +static unsigned short registers[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]; + registers[(i + 0x0A) % 16] = 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 = registers[(i + 0x02) % 16]; + *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); + if (registers[0] == 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; } - + tuner_power(false); } static void si4700_tune(void) { - unsigned char read_bytes[1]; + registers[3] |= (1 << 15); /* Set TUNE high to start tuning */ + si4700_write(2); - write_bytes[2] |= (1 << 7); /* Set TUNE high to start tuning */ - fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + /* tuning should be done within 60 ms according to the datasheet */ + sleep(HZ/15); + si4700_read(1); - do - { - sleep(HZ/50); - fmradio_i2c_read(I2C_ADR, read_bytes, 1); - } - 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)); + registers[3] &= ~(1 << 15); /* Set TUNE low */ + si4700_write(2); } /* tuner abstraction layer: set something to the tuner */ @@ -80,13 +104,30 @@ case RADIO_SLEEP: if (value) { - write_bytes[1] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */ + /* power down */ + registers[2] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */ + si4700_write(1); } else { - write_bytes[1] = 1; /* ENABLE high, DISABLE low */ + /* power up */ + registers[2] = 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 */ + registers[5] = (registers[5] & ~0xF) | 0xC; + +#if defined(SANSA_CLIP) || defined(SANSA_M200V4) + /* enable oscillator */ + registers[7] |= (1 << 15); /* XOSCEN */ +#endif + si4700_write(6); } - break; + return 1; case RADIO_FREQUENCY: { @@ -95,9 +136,9 @@ 200000, 100000, 50000 }; unsigned int chan; - unsigned int spacing = spacings[(write_bytes[7] >> 4) & 3] ; + unsigned int spacing = spacings[(registers[5] >> 4) & 3] ; - if (write_bytes[7] & (3 << 6)) /* check BAND */ + if (registers[5] & (3 << 6)) /* check BAND */ { chan = (value - 76000000) / spacing; } @@ -106,9 +147,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)); + registers[3] = (registers[3] & 0xFC00) | chan; si4700_tune(); return 1; } @@ -121,12 +160,12 @@ if (value) { /* mute */ - write_bytes[0] &= ~(1 << 6); + registers[2] &= ~(1 << 14); } else { /* unmute */ - write_bytes[0] |= (1 << 6); + registers[2] |= (1 << 14); } break; @@ -135,20 +174,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)); + registers[4] = (registers[4] & ~(1 << 11)) | (rd->deemphasis << 11); + registers[5] = (registers[5] & ~(3 << 6)) | (rd->band << 6); + registers[5] = (registers[5] & ~(3 << 4)) | (rd->spacing << 4); break; } case RADIO_FORCE_MONO: if (value) { - write_bytes[0] |= (1 << 5); + registers[2] |= (1 << 13); } else { - write_bytes[0] &= ~(1 << 5); + registers[2] &= ~(1 << 13); } break; @@ -156,15 +195,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 +215,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 = (registers[0xA] & (1 << 8)); /* ST high == Stereo */ break; }