Index: apps/keymaps/keymap-clip.c =================================================================== --- apps/keymaps/keymap-clip.c (revision 19366) +++ 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 19366) +++ 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/si4700.h =================================================================== --- firmware/export/si4700.h (revision 19366) +++ firmware/export/si4700.h (working copy) @@ -25,6 +25,17 @@ #ifndef _SI4700_H_ #define _SI4700_H_ +#define HAVE_RADIO_REGION + +struct si4700_region_data +{ + unsigned char deemphasis; /* 0: 50us, 1: 75us */ + unsigned char band; /* 0: us/europe, 1: japan */ + unsigned char spacing; /* 0: us/australia (200kHz), 1: europe/japan (100kHz), 2: (50kHz) */ +} __attribute__((packed)); + +extern const struct si4700_region_data si4700_region_data[TUNER_NUM_REGIONS]; + int si4700_set(int setting, int value); int si4700_get(int setting); Index: firmware/SOURCES =================================================================== --- firmware/SOURCES (revision 19366) +++ firmware/SOURCES (working copy) @@ -1072,7 +1072,9 @@ 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/sansa-clip/powermgmt-clip.c +target/arm/as3525/sansa-clip/fmradio-i2c-clip.c #endif /* !BOOTLOADER */ #endif /* !SIMULATOR */ #endif /* SANSA_CLIP */ Index: firmware/target/arm/as3525/system-as3525.c =================================================================== --- firmware/target/arm/as3525/system-as3525.c (revision 19366) +++ 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 19366) +++ 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,71 @@ #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 (source == last_source && recording == last_recording) + break; + + last_recording = recording; +#if 0 + 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 19366) +++ firmware/drivers/audio/as3514.c (working copy) @@ -279,12 +279,11 @@ ascodec_suppressor_on(true); #endif - /* turn on common */ - as3514_clear(AS3514_AUDIOSET3, AUDIOSET3_HPCM_off); - /* turn off everything */ as3514_clear(AS3514_HPH_OUT_L, HPH_OUT_L_HP_ON); as3514_write(AS3514_AUDIOSET1, 0x0); + /* turn off common */ + as3514_clear(AS3514_AUDIOSET3, AUDIOSET3_HPCM_off); /* Allow caps to discharge */ sleep(HZ/4); @@ -298,39 +297,37 @@ #if defined(HAVE_RECORDING) void audiohw_enable_recording(bool source_mic) { + /* ADC_on */ + as3514_set(AS3514_AUDIOSET1, AUDIOSET1_ADC_on); + /* ADC_Mute_off */ + as3514_set(AS3514_ADC_L, ADC_L_ADC_MUTE_off); + /* Sync mixer volumes before switching inputs */ + audiohw_set_master_vol(as3514.vol_l, as3514.vol_r); + if (source_mic) { source = SOURCE_MIC1; - /* Sync mixer volumes before switching inputs */ - audiohw_set_master_vol(as3514.vol_l, as3514.vol_r); + /* MIC1_on, LIN1_off */ + as3514_write_masked(AS3514_AUDIOSET1, AUDIOSET1_MIC1_on, + AUDIOSET1_MIC1_on | AUDIOSET1_LIN1_on); /* ADCmux = Stereo Microphone */ as3514_write_masked(AS3514_ADC_R, ADC_R_ADCMUX_ST_MIC, ADC_R_ADCMUX); - /* MIC1_on, LIN1_off */ - as3514_write_masked(AS3514_AUDIOSET1, AUDIOSET1_MIC1_on, - AUDIOSET1_MIC1_on | AUDIOSET1_LIN1_on); /* M1_AGC_off */ as3514_clear(AS3514_MIC1_R, MIC1_R_M1_AGC_off); } else { source = SOURCE_LINE_IN1; - audiohw_set_master_vol(as3514.vol_l, as3514.vol_r); + /* MIC1_off, LIN1_on */ + as3514_write_masked(AS3514_AUDIOSET1, AUDIOSET1_LIN1_on, + AUDIOSET1_MIC1_on | AUDIOSET1_LIN1_on); /* ADCmux = Line_IN1 */ as3514_write_masked(AS3514_ADC_R, ADC_R_ADCMUX_LINE_IN1, ADC_R_ADCMUX); - - /* MIC1_off, LIN1_on */ - as3514_write_masked(AS3514_AUDIOSET1, AUDIOSET1_LIN1_on, - AUDIOSET1_MIC1_on | AUDIOSET1_LIN1_on); } - - /* ADC_Mute_off */ - as3514_set(AS3514_ADC_L, ADC_L_ADC_MUTE_off); - /* ADC_on */ - as3514_set(AS3514_AUDIOSET1, AUDIOSET1_ADC_on); } void audiohw_disable_recording(void) @@ -396,6 +393,7 @@ as3514_write_masked(AS3514_ADC_R, right, AS3514_VOL_MASK); as3514_write_masked(AS3514_ADC_L, left, AS3514_VOL_MASK); } +#endif /* HAVE_RECORDING */ /** * Enable line in 1 analog monitoring @@ -419,4 +417,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 19366) +++ firmware/drivers/tuner/si4700.c (working copy) @@ -29,19 +29,164 @@ #include "fmradio.h" #include "fmradio_i2c.h" /* physical interface driver */ +#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[12]; + +static bool tuned = false; + +void si4700_init(void) +{ + /* fill in the write_bytes array with initial values from the chip to + avoid changing ones that shouldn't be changed */ + fmradio_i2c_read(I2C_ADR, write_bytes, sizeof(write_bytes)); + /* -2dB volume, keep BAND and SPACE defaults */ + write_bytes[7] = 0x0E; + write_bytes[10] = 0x80; /* XOSCEN */ +} + +static void si4700_tune(void) +{ + unsigned char read_byte; + int i; + + write_bytes[2] |= (1 << 7); /* Set TUNE high to start tuning */ + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + + i = 0; + do + { + sleep(HZ/50); + fmradio_i2c_read(I2C_ADR, &read_byte, 1); + if (i++ > 30) { + tuned = false; + return; + } + } + while (!(read_byte & (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)); + tuned = true; +} + /* tuner abstraction layer: set something to the tuner */ int si4700_set(int setting, int value) { - (void)setting; - (void)value; + switch(setting) + { + case RADIO_SLEEP: + if (value) + { + write_bytes[1] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */ + } + else + { + write_bytes[1] = 1; /* ENABLE high, DISABLE low */ + } + break; + case RADIO_FREQUENCY: + { + static const unsigned int spacings[3] = { + 200000, 100000, 50000 + }; + unsigned int chan; + unsigned int spacing = spacings[(write_bytes[7] >> 4) & 3] ; + + if (write_bytes[7] & (3 << 6)) /* check BAND */ + { + chan = (value - 76000000) / spacing; + } + else + { + chan = (value - 87500000) / spacing; + } + write_bytes[2] = (write_bytes[2] & 0xfe) | (chan & (1 << 8)); + write_bytes[3] = (chan & 0xff); + tuned = false; + break; + } + + case RADIO_SCAN_FREQUENCY: + si4700_set(RADIO_FREQUENCY, value); + return si4700_get(RADIO_TUNED); + + case RADIO_MUTE: + if (value) + { + /* mute */ + write_bytes[7] &= ~0xf; + write_bytes[0] &= ~(3 << 6); + } + else + { + /* unmute */ + write_bytes[7] |= 0xe; + write_bytes[0] |= (3 << 6); + } + break; + + case RADIO_REGION: + { + const struct si4700_region_data *rd = + &si4700_region_data[value]; + + write_bytes[4] &= (~(1 << 3) | (rd->deemphasis << 3)); + write_bytes[7] &= (~(3 << 6) | (rd->band << 6)); + write_bytes[7] &= (~(3 << 4) | (rd->spacing << 4)); + break; + } + + case RADIO_FORCE_MONO: + if (value) + { + write_bytes[0] |= (1 << 5); + } + else + { + write_bytes[0] &= ~(1 << 5); + } + break; + + default: + return -1; + } + + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + if (!tuned) + si4700_tune(); return 1; } /* tuner abstraction layer: read something from the tuner */ int si4700_get(int setting) { - (void)setting; + unsigned char read_bytes[8]; + int val = -1; /* default for unsupported query */ - return -1; + /* I2C reads start with register 0Ah */ + fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes)); + + switch(setting) + { + case RADIO_PRESENT: + /* Check if we get back correct manufacturer and chip id */ + if (read_bytes[13] == 0xa && read_bytes[14] == 0x42) + val = 1; /* true */ + break; + + case RADIO_TUNED: + val = tuned; + break; + + case RADIO_STEREO: + val = (read_bytes[0] & 1); /* ST high == Stereo */ + break; + } + + return val; } + Index: firmware/tuner.c =================================================================== --- firmware/tuner.c (revision 19366) +++ firmware/tuner.c (working copy) @@ -59,6 +59,16 @@ }; #endif /* (CONFIG_TUNER & TEA5767) */ +#if (CONFIG_TUNER & SI4700) +const struct si4700_region_data si4700_region_data[TUNER_NUM_REGIONS] = +{ + [REGION_EUROPE] = { 0, 0, 2 }, /* 50uS, US/Europe band */ + [REGION_US_CANADA] = { 1, 0, 0 }, /* 75uS, US/Europe band */ + [REGION_JAPAN] = { 0, 1, 1 }, /* 50uS, Japanese band */ + [REGION_KOREA] = { 0, 0, 1 }, /* 50uS, US/Europe band */ +}; +#endif /* (CONFIG_TUNER & TEA5767) */ + #ifdef CONFIG_TUNER_MULTI int (*tuner_set)(int setting, int value); int (*tuner_get)(int setting); @@ -95,6 +105,11 @@ s1a0903x01_set, s1a0903x01_get) #endif + #if (CONFIG_TUNER & SI4700) + TUNER_TYPE_CASE(SI4700, + si4700_set, + si4700_get) + #endif } }