Index: apps/debug_menu.c =================================================================== --- apps/debug_menu.c (revision 19367) +++ 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[0], (unsigned)buf[1], + (unsigned)buf[2], (unsigned)buf[3], + (unsigned)buf[4], (unsigned)buf[5], + (unsigned)buf[6], (unsigned)buf[7]); + simplelist_addline(SIMPLELIST_ADD_LINE, + "%02X%02X %02X%02X %02X%02X %02X%02X", + (unsigned)buf[8], (unsigned)buf[9], + (unsigned)buf[10], (unsigned)buf[11], + (unsigned)buf[12], (unsigned)buf[13], + (unsigned)buf[14], (unsigned)buf[15]); + simplelist_addline(SIMPLELIST_ADD_LINE, + "%02X%02X %02X%02X %02X%02X %02X%02X", + (unsigned)buf[16], (unsigned)buf[17], + (unsigned)buf[18], (unsigned)buf[19], + (unsigned)buf[20], (unsigned)buf[21], + (unsigned)buf[22], (unsigned)buf[23]); + simplelist_addline(SIMPLELIST_ADD_LINE, + "%02X%02X %02X%02X %02X%02X %02X%02X", + (unsigned)buf[24], (unsigned)buf[25], + (unsigned)buf[26], (unsigned)buf[27], + (unsigned)buf[28], (unsigned)buf[29], + (unsigned)buf[30], (unsigned)buf[31]); +#endif return ACTION_REDRAW; } static bool dbg_fm_radio(void) Index: apps/keymaps/keymap-clip.c =================================================================== --- apps/keymaps/keymap-clip.c (revision 19367) +++ 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 19367) +++ 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 19367) +++ firmware/export/si4700.h (working copy) @@ -25,6 +25,18 @@ #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]; + +void si4700_init(void); int si4700_set(int setting, int value); int si4700_get(int setting); Index: firmware/SOURCES =================================================================== --- firmware/SOURCES (revision 19367) +++ 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 19367) +++ 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 19367) +++ 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 19367) +++ 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 19367) +++ firmware/drivers/tuner/si4700.c (working copy) @@ -9,7 +9,7 @@ * * Tuner "middleware" for Silicon Labs SI4700 chip * - * Copyright (C) 2008 ??? + * Copyright (C) 2008 Nils Wallménius * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,19 +29,186 @@ #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]; /* registers 02 - 07 */ +static bool tuned = false; +static bool tuner_present = false; + +void si4700_init(void) +{ + unsigned char read_bytes[32]; + unsigned char powercfg[2]; + + tuner_power(true); + + /* set disable */ + powercfg[0] = 0; + powercfg[1] = 1 | (1 << 6); + fmradio_i2c_write(I2C_ADR, powercfg, 2); + sleep(HZ/5); + + /* set enable */ + powercfg[0] = 0; + powercfg[1] = 1; + fmradio_i2c_write(I2C_ADR, powercfg, 2); + sleep(HZ/5); + + fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes)); + if ((read_bytes[12] << 8 | read_bytes[13]) == 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; + } + + /* enable osc */ + write_bytes[10] |= 0x80; + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + sleep(HZ/5); + + tuner_power(false); +} + +static void si4700_tune(void) +{ + unsigned char read_bytes[2]; + + write_bytes[2] |= (1 << 7); /* Set TUNE high to start tuning */ + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); + + sleep(HZ/15); + fmradio_i2c_read(I2C_ADR, read_bytes, 2); + + tuned = ((read_bytes[0] & (1 << 6)) != 0); /* STC high == Seek/Tune complete */ + + write_bytes[2] &= ~(1 << 7); /* Set TUNE low */ + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); +} + /* tuner abstraction layer: set something to the tuner */ int si4700_set(int setting, int value) { - (void)setting; - (void)value; + unsigned char powercfg[2]; + switch(setting) + { + case RADIO_SLEEP: + powercfg[0] = 0; + if (value) + { + write_bytes[1] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */ + powercfg[1] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */ + } + else + { + write_bytes[1] = 1; /* ENABLE high, DISABLE low */ + powercfg[1] = 1; /* ENABLE high, DISABLE low */ + } + fmradio_i2c_write(I2C_ADR, powercfg, 2); + sleep(HZ/9); + return 1; + + 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] & ~3) | ((chan & (3 << 8)) >> 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[0] &= ~(1 << 6); + } + else + { + /* unmute */ + write_bytes[0] |= (1 << 6); + } + break; + + case RADIO_REGION: + { + 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)); + 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[14]; + 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: + val = tuner_present ? 1 : 0; + 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 19367) +++ 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,12 @@ s1a0903x01_set, s1a0903x01_get) #endif + #if (CONFIG_TUNER & SI4700) + TUNER_TYPE_CASE(SI4700, + si4700_set, + si4700_get, + si4700_init()) + #endif } }