diff options
author | Laurent Gautier <creposucre@rockbox.org> | 2009-12-01 17:54:40 +0000 |
---|---|---|
committer | Laurent Gautier <creposucre@rockbox.org> | 2009-12-01 17:54:40 +0000 |
commit | 0260852771aef7a6e9045684f0c3d0d7e01909f7 (patch) | |
tree | 6fa4b42230d1289857f84405defad94cc2de86b8 /firmware/drivers | |
parent | 63d79148fd07aebd2b425c0414b7b9622b312399 (diff) |
Add support for the ipod FM remote to the 4G, Color, 5G, nano 1G with RDS
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23805 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers')
-rw-r--r-- | firmware/drivers/tuner/ipod_remote_tuner.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/firmware/drivers/tuner/ipod_remote_tuner.c b/firmware/drivers/tuner/ipod_remote_tuner.c new file mode 100644 index 0000000000..07a5eeb9a6 --- /dev/null +++ b/firmware/drivers/tuner/ipod_remote_tuner.c @@ -0,0 +1,444 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * tuner for the ipod fm remote and other ipod remote tuners + * + * Copyright (C) 2009 Laurent Gautier + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include <stdlib.h> +#include "kernel.h" +#include "iap.h" +#include "tuner.h" /* tuner abstraction interface */ +#include "adc.h" +#include "settings.h" + +static bool powered = false; + +static unsigned char tuner_param = 0x00, old_tuner_param = 0xFF; +/* temp var for tests to avoid looping execution in submenus settings*/ +int mono_mode = -1, old_region = -1; + +int radio_present = 0; +int tuner_frequency = 0; +int tuner_signal_power = 0; +int radio_tuned = 0; +int rds_event = 0; + +char rds_radioname[9]; +char rds_radioinfo[70]; /* do we need more? */ + +union FRQ { + unsigned long int frequency_radio; + char data_frequency[4]; +}Frequency; + +void rmt_tuner_freq(void) +{ + char tempdata[4]; + tempdata[0] = serbuf[6]; + tempdata[1] = serbuf[5]; + tempdata[2] = serbuf[4]; + tempdata[3] = serbuf[3]; + + memcpy(Frequency.data_frequency,tempdata,4); + tuner_frequency = (Frequency.frequency_radio*1000); + radio_tuned = 1; + rmt_tuner_signal_power(serbuf[7]); +} + +void rmt_tuner_set_freq(int curr_freq) +{ + if (curr_freq != tuner_frequency) + { + radio_tuned = 0; + tuner_signal_power = 0; + /* clear rds name and info */ + memset(rds_radioname,' ',sizeof(rds_radioname)); + memset(rds_radioinfo,' ',sizeof(rds_radioinfo)); + /* ex: 00 01 63 14 = 90.9MHz */ + unsigned char data[] = {0x07, 0x0B, 0x00, 0x01, 0x63, 0x14}; + + if (curr_freq != 0) + { + curr_freq = curr_freq / 1000; + char tempdata[4]; + + Frequency.frequency_radio = curr_freq; + tempdata[0] = Frequency.data_frequency[3]; + tempdata[1] = Frequency.data_frequency[2]; + tempdata[2] = Frequency.data_frequency[1]; + tempdata[3] = Frequency.data_frequency[0]; + + memcpy(data+2,tempdata,4); + iap_send_pkt(data, sizeof(data)); + } + } +} + +void rmt_tuner_signal_power(unsigned char value) +{ + tuner_signal_power = (int)(value); +} + +void rmt_tuner_sleep(int state) +{ + if (state == 0) + { + /* tuner HW on */ + unsigned char data[] = {0x07, 0x05, 0x01}; + iap_send_pkt(data, sizeof(data)); + /* boost gain */ + unsigned char data1[] = {0x07, 0x24, 0x06 }; + iap_send_pkt(data1, sizeof(data1)); + /* set volume */ + unsigned char data2[] = {0x03, 0x09, 0x04, 0x00, 0x77 }; + iap_send_pkt(data2, sizeof(data2)); + /* set rds on */ + unsigned char data3[] = {0x07, 0x20, 0x40, 0x00, 0x00, 0x10 }; + iap_send_pkt(data3, sizeof(data3)); + } + else + { + /* unbooste gain */ + unsigned char data[] = {0x07, 0x24, 0x00}; + iap_send_pkt(data, sizeof(data)); + /* set rds off */ + unsigned char data1[] = {0x07, 0x20, 0x00, 0x00, 0x00, 0x00 }; + iap_send_pkt(data1, sizeof(data1)); + /* stop tuner HW */ + unsigned char data2[] = {0x07, 0x05, 0x00}; + iap_send_pkt(data2, sizeof(data2)); + } +} + +void rmt_tuner_scan(int param) +{ + unsigned char data[] = {0x07, 0x11, 0x08}; /* RSSI level */ + unsigned char updown = 0x00; + radio_tuned = 0; + iap_send_pkt(data, sizeof(data)); + + if (param == 1) + { + updown = 0x07; /* scan up */ + } + else if (param == -1) + { + updown = 0x08; /* scan down */ + } + else if (param == 10) + { + updown = 0x01; /* scan up starting from beginning of the band */ + } + unsigned char data1[] = {0x07, 0x12, updown}; + iap_send_pkt(data1, sizeof(data1)); +} + +void rmt_tuner_mute(int value) +{ + /* mute flag off (play) */ + unsigned char data[] = {0x03, 0x09, 0x03, 0x01}; + if (value) + { + /* mute flag on (pause) */ + data[3] = 0x02; + } + iap_send_pkt(data, sizeof(data)); +} + +void rmt_tuner_region(int region) +{ + if (region != old_region) + { + unsigned char data[] = {0x07, 0x08, 0x00}; + if (region == 2) + { + data[2] = 0x02; /* japan band */ + } + else + { + data[2] = 0x01; /* us eur band */ + } + iap_send_pkt(data, sizeof(data)); + sleep(HZ/100); + old_region = region; + } +} + +/* set stereo/mono, deemphasis, delta freq... */ +void rmt_tuner_set_param(unsigned char tuner_param) +{ + if(tuner_param != old_tuner_param) + { + unsigned char data[] = {0x07, 0x0E, 0x00}; + + data[2] = tuner_param; + iap_send_pkt(data, sizeof(data)); + old_tuner_param = tuner_param; + } +} + +void set_deltafreq(int delta) +{ + tuner_param &= 0xFC; + switch (delta) + { + case 1: + { + /* 100KHz */ + tuner_param |= 0x01; + break; + } + case 2: + { + /* 50KHz */ + tuner_param |= 0x02; + break; + } + + default: + { + /* 200KHz */ + tuner_param |= 0x00; + break; + } + } +} + +void set_deemphasis(int deemphasis) +{ + tuner_param &= 0xBF; + switch (deemphasis) + { + case 1: + { + tuner_param |= 0x40; + break; + } + default: + { + tuner_param |= 0x00; + break; + } + } +} + +void set_mono(int value) +{ + tuner_param &= 0xEF; + + if (value != mono_mode) + { + tuner_param |= 0x10; + rmt_tuner_set_param(tuner_param); + sleep(HZ/100); + mono_mode = value; + } +} + +bool reply_timeout(void) +{ + int timeout = 0; + + sleep(HZ/50); + do + { + iap_handlepkt(); + sleep(HZ/50); + timeout++; + } + while((ipod_rmt_tuner_get(RADIO_TUNED) == 0) && (timeout < TIMEOUT_VALUE)); + + if (timeout >= TIMEOUT_VALUE) + return true; + else return false; +} + +void rmt_tuner_rds_data(void) +{ + if (serbuf[3] == 0x1E) + { + strlcpy(rds_radioname,serbuf+5,8); + } + else if(serbuf[3] == 0x04) + { + strlcpy(rds_radioinfo,serbuf+5,(serbuf[0]-4)); + } + rds_event = 1; +} + +/* tuner abstraction layer: set something to the tuner */ +int ipod_rmt_tuner_set(int setting, int value) +{ + switch(setting) + { + case RADIO_SLEEP: + { + rmt_tuner_sleep(value); + sleep(HZ/2); + break; + } + + case RADIO_FREQUENCY: + { + rmt_tuner_set_freq(value); + if (reply_timeout() == true) + return 0; + break; + } + + case RADIO_SCAN_FREQUENCY: + { + const struct fm_region_data * const fmr = + &fm_region_data[global_settings.fm_region]; + + /* case: scan for presets, back to beginning of the band */ + if ((radio_tuned == 1) && (value == fmr->freq_min)) + { + tuner_set(RADIO_FREQUENCY,value); + } + + /* scan through frequencies */ + if (radio_tuned == 1) + { + /* scan down */ + if(value < tuner_frequency) + rmt_tuner_scan(-1); + /* scan up */ + else + rmt_tuner_scan(1); + + if (reply_timeout() == true) + return 0; + radio_tuned = 0; + } + + if (tuner_frequency == value) + { + radio_tuned = 1; + return 1; + } + else + { + radio_tuned = 0; + return 0; + } + } + + case RADIO_MUTE: + { + /* mute flag sent to accessory */ + /* rmt_tuner_mute(value); */ + break; + } + + case RADIO_REGION: + { + const struct rmt_tuner_region_data *rd = + &rmt_tuner_region_data[value]; + + rmt_tuner_region(rd->band); + set_deltafreq(rd->spacing); + set_deemphasis(rd->deemphasis); + rmt_tuner_set_param(tuner_param); + break; + } + + case RADIO_FORCE_MONO: + { + set_mono(value); + break; + } + + default: + return -1; + } + return 1; +} + +/* tuner abstraction layer: read something from the tuner */ +int ipod_rmt_tuner_get(int setting) +{ + int val = -1; /* default for unsupported query */ + + switch(setting) + { + case RADIO_PRESENT: + val = radio_present; + if (val) + { + /* if accessory disconnected */ + if(adc_read(ADC_ACCESSORY) >= 10) + { + radio_present = 0; + val = 0; + } + } + break; + + /* radio tuned: yes no */ + case RADIO_TUNED: + val = 0; + if (radio_tuned == 1) + val = 1; + break; + + /* radio is always stereo */ + /* we can't know when it's in mono mode, depending of signal quality */ + /* except if it is forced in mono mode */ + case RADIO_STEREO: + val = true; + break; + + case RADIO_EVENT: + if (rds_event) + { + val = 1; + rds_event = 0; + } + break; + } + return val; +} + +char* ipod_get_rds_info(int setting) +{ + char *text = NULL; + + switch(setting) + { + case RADIO_RDS_NAME: + text = rds_radioname; + break; + + case RADIO_RDS_TEXT: + text = rds_radioinfo; + break; + } + return text; +} + +bool tuner_power(bool status) +{ + bool oldstatus = powered; + powered = status; + return oldstatus; +} |