Index: apps/keymaps/keymap-clip.c =================================================================== --- apps/keymaps/keymap-clip.c (revision 19360) +++ 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 */ + { 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 19360) +++ 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 19360) +++ 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 19360) +++ 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 19360) +++ 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/sansa-clip/lcd-ssd1303.c =================================================================== --- firmware/target/arm/as3525/sansa-clip/lcd-ssd1303.c (revision 19360) +++ firmware/target/arm/as3525/sansa-clip/lcd-ssd1303.c (working copy) @@ -186,10 +186,12 @@ ams3525_dbop_init(); GPIOA_DIR |= 0x33; /* pins 5:4 and 1:0 out */ + GPIOB_DIR |= 0x40; /* pin 6 out */ GPIOA_PIN(1) = (1<<1); GPIOA_PIN(0) = (1<<0); GPIOA_PIN(4) = 0; + GPIOB_PIN(6) = (1<<6); /* Set display clock (divide ratio = 1) and oscillator frequency (1) */ lcd_write_command(LCD_SET_DISPLAY_CLOCK_AND_OSC_FREQ); Index: firmware/target/arm/as3525/sansa-clip/fmradio-i2c-clip.c =================================================================== --- firmware/target/arm/as3525/sansa-clip/fmradio-i2c-clip.c (revision 0) +++ firmware/target/arm/as3525/sansa-clip/fmradio-i2c-clip.c (revision 0) @@ -0,0 +1,112 @@ +#include "as3525.h" +#include "generic_i2c.h" +#include "fmradio_i2c.h" + +#define I2C_GPIO(x) GPIOB_PIN(x) +#define I2C_GPIO_DIR GPIOB_DIR +#define I2C_SCL_PIN 4 +#define I2C_SDA_PIN 5 + +static void fm_scl_hi(void) +{ + I2C_GPIO(I2C_SCL_PIN) = 1 << I2C_SCL_PIN; +} + +static void fm_scl_lo(void) +{ + I2C_GPIO(I2C_SCL_PIN) = 0; +} + +static void fm_sda_hi(void) +{ + I2C_GPIO(I2C_SDA_PIN) = 1 << I2C_SDA_PIN; +} + +static void fm_sda_lo(void) +{ + I2C_GPIO(I2C_SDA_PIN) = 0; +} + +static void fm_sda_input(void) +{ + I2C_GPIO_DIR &= ~(1 << I2C_SDA_PIN); +} + +static void fm_sda_output(void) +{ + I2C_GPIO_DIR |= 1 << I2C_SDA_PIN; +} + +static void fm_scl_input(void) +{ + I2C_GPIO_DIR &= ~(1 << I2C_SCL_PIN); +} + +static void fm_scl_output(void) +{ + I2C_GPIO_DIR |= 1 << I2C_SCL_PIN; +} + +static int fm_sda(void) +{ + return (I2C_GPIO(I2C_SDA_PIN) != 0); +} + +static int fm_scl(void) +{ + return (I2C_GPIO(I2C_SCL_PIN) != 0); +} + + +/* TODO delay is uncalibrated */ +static void fm_delay(void) +{ + volatile int i, j; + + j = 0; + for (i = 0; i < 20; i++) { + j++; + } +} + + +static struct i2c_interface fm_i2c_interface = { + .address = 0x10 << 1, + + .scl_hi = fm_scl_hi, + .scl_lo = fm_scl_lo, + .sda_hi = fm_sda_hi, + .sda_lo = fm_sda_lo, + .sda_input = fm_sda_input, + .sda_output = fm_sda_output, + .scl_input = fm_scl_input, + .scl_output = fm_scl_output, + .scl = fm_scl, + .sda = fm_sda, + + .delay_hd_sta = fm_delay, + .delay_hd_dat = fm_delay, + .delay_su_dat = fm_delay, + .delay_su_sto = fm_delay, + .delay_su_sta = fm_delay, + .delay_thigh = fm_delay +}; + +/* TODO function is not declared in a public header file */ +void fmradio_i2c_init(void) +{ + i2c_add_node(&fm_i2c_interface); +} + +int fmradio_i2c_write(unsigned char address, const unsigned char* buf, int count) +{ + return i2c_write_data(address, -1, buf, count); +} + +int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count) +{ + return i2c_read_data(address, -1, buf, count); +} + + + Index: firmware/drivers/generic_i2c.c =================================================================== --- firmware/drivers/generic_i2c.c (revision 19360) +++ firmware/drivers/generic_i2c.c (working copy) @@ -27,8 +27,8 @@ #define MAX_I2C_INTERFACES 5 -int i2c_num_ifs = 0; -struct i2c_interface *i2c_if[MAX_I2C_INTERFACES]; +static int i2c_num_ifs = 0; +static struct i2c_interface *i2c_if[MAX_I2C_INTERFACES]; static void i2c_start(struct i2c_interface *iface) { Index: firmware/drivers/audio/as3514.c =================================================================== --- firmware/drivers/audio/as3514.c (revision 19360) +++ 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) Index: firmware/drivers/tuner/si4700.c =================================================================== --- firmware/drivers/tuner/si4700.c (revision 19360) +++ firmware/drivers/tuner/si4700.c (working copy) @@ -29,19 +29,166 @@ #include "fmradio.h" #include "fmradio_i2c.h" /* physical interface driver */ +#define I2C_ADR 0x20 + +#define SI470X_DEVICEID 0x1242 + +/* I2C writes start at register 02h so the first two bytes are + 02h, next two 03h, etc. */ +static unsigned char write_bytes[] = { 0xC0, 0x01, /* set DMUTE, set DSMUTE, clear DISABLE, keep ENABLE set */ + 0x00, 0x00, + /* enable GPIO interrupts */ +/* (1 << 6), (1 << 2), */ + 0x00, 0x00, /* disable GPIOs */ + /* -2dB volume */ + 0x00, 0x0E, + 0x00, 0x00, + 0x80, 0x00 }; /* enable oscillator */ + +static bool tuned = false; + +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++ > 50) + { + /* timeout */ + 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] & (3 << 4)] ; + + 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[14]; + int val = -1; /* default for unsupported query */ + unsigned short deviceid; - return -1; + /* I2C reads start with register 0Ah */ + fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes)); + + switch(setting) + { + case RADIO_PRESENT: + deviceid = read_bytes[12] << 8 | read_bytes[13]; + val = (deviceid == SI470X_DEVICEID); + 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 19360) +++ 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 } }