eaf6aad690
Also, use rds_reset() now to clear data on station change since the rds driver internal buffers are used. Change-Id: I043b09d661eeec21617381015347f0bcead4f7d4
422 lines
11 KiB
C
422 lines
11 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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"
|
|
#include "power.h"
|
|
#include "rds.h"
|
|
|
|
static unsigned char tuner_param = 0x00, old_tuner_param = 0xFF;
|
|
/* temp var for tests to avoid looping execution in submenus settings*/
|
|
static int mono_mode = -1, old_region = -1;
|
|
|
|
int radio_present = 0;
|
|
|
|
static int tuner_frequency = 0;
|
|
static int tuner_signal_power = 0;
|
|
static bool radio_tuned = false;
|
|
|
|
static void rmt_tuner_signal_power(unsigned char value)
|
|
{
|
|
tuner_signal_power = (int)(value);
|
|
}
|
|
|
|
void rmt_tuner_freq(unsigned int len, const unsigned char *buf)
|
|
{
|
|
/* length currently unused */
|
|
(void)len;
|
|
|
|
unsigned int khz = (buf[2] << 24) | (buf[3] << 16) |
|
|
(buf[4] << 8) | buf[5];
|
|
tuner_frequency = khz *1000 ;
|
|
radio_tuned = true;
|
|
rmt_tuner_signal_power(buf[6]);
|
|
}
|
|
|
|
static void rmt_tuner_set_freq(int curr_freq)
|
|
{
|
|
if (curr_freq != tuner_frequency)
|
|
{
|
|
radio_tuned = false;
|
|
tuner_signal_power = 0;
|
|
/* clear rds data */
|
|
rds_reset();
|
|
/* ex: 00 01 63 14 = 90.9MHz */
|
|
unsigned char data[] = {0x07, 0x0B, 0x00, 0x01, 0x63, 0x14};
|
|
|
|
if (curr_freq != 0)
|
|
{
|
|
unsigned int khz = curr_freq / 1000;
|
|
data[2] = (khz >> 24) & 0xFF;
|
|
data[3] = (khz >> 16) & 0xFF;
|
|
data[4] = (khz >> 8) & 0xFF;
|
|
data[5] = (khz >> 0) & 0xFF;
|
|
iap_send_pkt(data, sizeof(data));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rmt_tuner_sleep(int state)
|
|
{
|
|
if (state == 0)
|
|
{
|
|
rds_init();
|
|
tuner_param = 0x00;
|
|
old_tuner_param = 0xFF;
|
|
mono_mode = -1;
|
|
old_region = -1;
|
|
tuner_frequency = 0;
|
|
radio_tuned = false;
|
|
|
|
/* tuner HW on */
|
|
const unsigned char data[] = {0x07, 0x05, 0x01};
|
|
iap_send_pkt(data, sizeof(data));
|
|
/* boost gain */
|
|
const unsigned char data1[] = {0x07, 0x24, 0x06 };
|
|
iap_send_pkt(data1, sizeof(data1));
|
|
/* set volume */
|
|
unsigned char data2[] = {0x03, 0x09, 0x04, 0x00, 0x00 };
|
|
data2[4] = (char)((global_settings.volume+58) * 4);
|
|
iap_send_pkt(data2, sizeof(data2));
|
|
/* set rds on */
|
|
const unsigned char data3[] = {0x07, 0x20, 0x40, 0x00, 0x00, 0x10 };
|
|
iap_send_pkt(data3, sizeof(data3));
|
|
}
|
|
else
|
|
{
|
|
/* unbooste gain */
|
|
const unsigned char data[] = {0x07, 0x24, 0x00};
|
|
iap_send_pkt(data, sizeof(data));
|
|
/* set rds off */
|
|
const unsigned char data1[] = {0x07, 0x20, 0x00, 0x00, 0x00, 0x00 };
|
|
iap_send_pkt(data1, sizeof(data1));
|
|
/* stop tuner HW */
|
|
const unsigned char data2[] = {0x07, 0x05, 0x00};
|
|
iap_send_pkt(data2, sizeof(data2));
|
|
}
|
|
}
|
|
|
|
static void rmt_tuner_scan(int param)
|
|
{
|
|
const unsigned char data[] = {0x07, 0x11, 0x08}; /* RSSI level */
|
|
unsigned char updown = 0x00;
|
|
radio_tuned = false;
|
|
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));
|
|
}
|
|
|
|
static 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));
|
|
}
|
|
|
|
static void rmt_tuner_region(int region)
|
|
{
|
|
if (region != old_region)
|
|
{
|
|
const struct fm_region_data *rd = &fm_region_data[region];
|
|
unsigned char data[] = {0x07, 0x08, 0x00};
|
|
if (rd->freq_min == 76000000)
|
|
{
|
|
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... */
|
|
static 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;
|
|
}
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void set_deemphasis(int deemphasis)
|
|
{
|
|
tuner_param &= 0xBF;
|
|
switch (deemphasis)
|
|
{
|
|
case 1:
|
|
{
|
|
tuner_param |= 0x40;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
tuner_param |= 0x00;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void set_mono(int value)
|
|
{
|
|
tuner_param &= 0xEF;
|
|
|
|
if (value != mono_mode)
|
|
{
|
|
tuner_param &= 0xEF;
|
|
if (value == 1)
|
|
tuner_param |= 0x10;
|
|
rmt_tuner_set_param(tuner_param);
|
|
sleep(HZ/100);
|
|
mono_mode = value;
|
|
}
|
|
}
|
|
|
|
static 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));
|
|
|
|
return (timeout >= TIMEOUT_VALUE);
|
|
}
|
|
|
|
void rmt_tuner_rds_data(unsigned int len, const unsigned char *buf)
|
|
{
|
|
if (buf[2] == 0x1E)
|
|
{
|
|
rds_push_info(RDS_INFO_PS, (uintptr_t)(buf+4), 8);
|
|
}
|
|
else if(buf[2] == 0x04)
|
|
{
|
|
rds_push_info(RDS_INFO_RT, (uintptr_t)(buf+4), len-4);
|
|
}
|
|
}
|
|
|
|
/* 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/10);
|
|
if(value)
|
|
{
|
|
tuner_frequency = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RADIO_FREQUENCY:
|
|
{
|
|
rmt_tuner_set_freq(value);
|
|
if (reply_timeout())
|
|
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 && (value == fmr->freq_min))
|
|
{
|
|
tuner_set(RADIO_FREQUENCY,value);
|
|
}
|
|
|
|
/* scan through frequencies */
|
|
if (radio_tuned)
|
|
{
|
|
if ((tuner_frequency <= fmr->freq_min)
|
|
&& (tuner_frequency >= fmr->freq_max))
|
|
{
|
|
tuner_set(RADIO_FREQUENCY,value);
|
|
}
|
|
/* scan down */
|
|
if(value < tuner_frequency)
|
|
rmt_tuner_scan(-1);
|
|
/* scan up */
|
|
else
|
|
rmt_tuner_scan(1);
|
|
|
|
sleep(HZ/10);
|
|
if (reply_timeout())
|
|
{
|
|
tuner_set(RADIO_FREQUENCY,value);
|
|
rmt_tuner_scan(1);
|
|
if (reply_timeout() == true)
|
|
return 0;
|
|
}
|
|
radio_tuned = false;
|
|
}
|
|
|
|
if (tuner_frequency == value)
|
|
{
|
|
radio_tuned = true;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
radio_tuned = false;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
case RADIO_MUTE:
|
|
{
|
|
/* mute flag sent to accessory */
|
|
rmt_tuner_mute(value);
|
|
break;
|
|
}
|
|
|
|
case RADIO_REGION:
|
|
{
|
|
const struct fm_region_data *rd = &fm_region_data[value];
|
|
int band = (rd->freq_min == 76000000) ? 2 : 0;
|
|
int spacing = (100000 / rd->freq_step);
|
|
int deemphasis = (rd->deemphasis == 50) ? 1 : 0;
|
|
|
|
rmt_tuner_region(band);
|
|
set_deltafreq(spacing);
|
|
set_deemphasis(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)
|
|
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;
|
|
}
|
|
return val;
|
|
}
|