From 4e965b4b6c22a4ed206eed418c0e1b9296cc2113 Mon Sep 17 00:00:00 2001 From: Bertrik Sikken Date: Tue, 7 Jul 2009 21:57:16 +0000 Subject: Meizu: implement i2c for the meizu fmradio and update the tea5760 tuner driver git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21703 a1c6a512-1295-4272-9138-f99709370657 --- firmware/drivers/tuner/tea5760uk.c | 111 ++++++++++++------ firmware/export/tea5760.h | 56 +++++++++ firmware/export/tuner.h | 5 + firmware/target/arm/s5l8700/fmradio-i2c-meizu.c | 146 ++++++++++++++++++++++++ firmware/tuner.c | 16 +++ 5 files changed, 296 insertions(+), 38 deletions(-) create mode 100644 firmware/export/tea5760.h create mode 100644 firmware/target/arm/s5l8700/fmradio-i2c-meizu.c diff --git a/firmware/drivers/tuner/tea5760uk.c b/firmware/drivers/tuner/tea5760uk.c index 781e51967c..db07e20cf4 100644 --- a/firmware/drivers/tuner/tea5760uk.c +++ b/firmware/drivers/tuner/tea5760uk.c @@ -28,10 +28,20 @@ #include "fmradio.h" #include "fmradio_i2c.h" /* physical interface driver */ -#define I2C_ADR 0xC0 -static unsigned char write_bytes[7] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - -static void tea5760uk_set_clear(int byte, unsigned char bits, int set) +#define I2C_ADR 0x22 + +static bool tuner_present = false; +static unsigned char write_bytes[7] = { + 0x00, /* INTREG LSB */ + 0x80, /* FRQSET MSB */ + 0x00, /* FRQSET LSB */ + 0x08, /* TNCTRL MSB */ + 0xD2, /* TNCTRL LSB */ + 0x00, /* TESTREG MSB */ + 0x40 /* TESTREG LSB */ +}; + +static void tea5760_set_clear(int byte, unsigned char bits, int set) { write_bytes[byte] &= ~bits; if (set) @@ -39,58 +49,60 @@ static void tea5760uk_set_clear(int byte, unsigned char bits, int set) } /* tuner abstraction layer: set something to the tuner */ -int tea5760uk_set(int setting, int value) +int tea5760_set(int setting, int value) { switch(setting) { case RADIO_SLEEP: - /* init values */ - write_bytes[0] |= (1<<7); /* mute */ -#if CONFIG_TUNER_XTAL == 32768 - /* 32.768kHz, soft mute, stereo noise cancelling */ - write_bytes[3] |= (1<<4) | (1<<3) | (1<<1); -#else - /* soft mute, stereo noise cancelling */ - write_bytes[3] |= (1<<3) | (1<<1); -#endif - /* sleep / standby mode */ - tea5760uk_set_clear(3, (1<<6), value); + if (value) { + /* sleep / standby mode */ + tea5760_set_clear(3, (1<<6), 0); + } + else { + /* active mode */ + tea5760_set_clear(3, (1<<6), 1); + /* disable hard mute */ + tea5760_set_clear(4, (1<<7), 0); + } break; case RADIO_FREQUENCY: { int n; -#if CONFIG_TUNER_XTAL == 32768 + + /* low side injection */ + tea5760_set_clear(4, (1<<4), 0); n = (4 * (value - 225000) + 16384) / 32768; -#else - n = (4 * (value - 225000)) / 50000; -#endif - write_bytes[6] = (write_bytes[6] & 0xC0) | (n >> 8); - write_bytes[7] = n; + + /* set frequency in preset mode */ + write_bytes[1] = (n >> 8) & 0x3F; + write_bytes[2] = n; } break; case RADIO_SCAN_FREQUENCY: - tea5760uk_set(RADIO_FREQUENCY, value); - sleep(HZ/30); - return tea5760uk_get(RADIO_TUNED); + tea5760_set(RADIO_FREQUENCY, value); + sleep(40*HZ/1000); + return tea5760_get(RADIO_TUNED); case RADIO_MUTE: - tea5760uk_set_clear(3, (1<<2), value); + tea5760_set_clear(3, (1<<2), value); break; case RADIO_REGION: - { - const struct tea5760uk_region_data *rd = - &tea5760uk_region_data[value]; + { + const struct tea5760_region_data *rd = + &tea5760_region_data[value]; - tea5760uk_set_clear(4, (1<<1), rd->deemphasis); - tea5760uk_set_clear(3, (1<<5), rd->band); - break; + tea5760_set_clear(4, (1<<1), rd->deemphasis); + tea5760_set_clear(3, (1<<5), rd->band); } + break; + case RADIO_FORCE_MONO: - tea5760uk_set_clear(4, (1<<3), value); + tea5760_set_clear(4, (1<<3), value); break; + default: return -1; } @@ -100,7 +112,7 @@ int tea5760uk_set(int setting, int value) } /* tuner abstraction layer: read something from the tuner */ -int tea5760uk_get(int setting) +int tea5760_get(int setting) { unsigned char read_bytes[16]; int val = -1; /* default for unsupported query */ @@ -110,7 +122,7 @@ int tea5760uk_get(int setting) switch(setting) { case RADIO_PRESENT: - val = 1; /* true */ + val = tuner_present ? 1 : 0; break; case RADIO_TUNED: @@ -130,8 +142,31 @@ int tea5760uk_get(int setting) return val; } -void tea5760uk_dbg_info(struct tea5760uk_dbg_info *info) +void tea5760_init(void) { - fmradio_i2c_read(I2C_ADR, info->read_regs, 5); - memcpy(info->write_regs, write_bytes, 5); + unsigned char buf[16]; + unsigned short manid, chipid; + + /* read all registers */ + fmradio_i2c_read(I2C_ADR, buf, sizeof(buf)); + + /* check device id */ + manid = (buf[12] << 8) | buf[13]; + chipid = (buf[14] << 8) | buf[15]; + if ((manid == 0x202B) && (chipid == 0x5760)) + { + tuner_present = true; + } + + /* write initial values */ + tea5760_set_clear(3, (1<<1), 1); /* soft mute on */ + tea5760_set_clear(3, (1<<0), 1); /* stereo noise cancellation on */ + fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); } + +void tea5760_dbg_info(struct tea5760_dbg_info *info) +{ + fmradio_i2c_read(I2C_ADR, info->read_regs, sizeof(info->read_regs)); + memcpy(info->write_regs, write_bytes, sizeof(info->write_regs)); +} + diff --git a/firmware/export/tea5760.h b/firmware/export/tea5760.h new file mode 100644 index 0000000000..8fa54dfa78 --- /dev/null +++ b/firmware/export/tea5760.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * Tuner header for the Philips TEA5760 + * + * Copyright (C) 2009 Bertrik Sikken + * + * 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. + * + ****************************************************************************/ + +#ifndef _TEA5760_H_ +#define _TEA5760_H_ + +#include "config.h" +#include "tuner.h" + +#define HAVE_RADIO_REGION + +struct tea5760_region_data +{ + unsigned char deemphasis; /* 1: 50us, 0: 75us */ + unsigned char band; /* 0: europe, 1: japan (BL in TEA spec)*/ +} __attribute__((packed)); + +extern const struct tea5760_region_data tea5760_region_data[TUNER_NUM_REGIONS]; + +struct tea5760_dbg_info +{ + unsigned char read_regs[16]; + unsigned char write_regs[7]; +}; + +int tea5760_set(int setting, int value); +int tea5760_get(int setting); +void tea5760_init(void); +void tea5760_dbg_info(struct tea5760_dbg_info *info); + +#ifndef CONFIG_TUNER_MULTI +#define tuner_set tea5760_set +#define tuner_get tea5760_get +#endif + +#endif /* _TEA5760_H_ */ + diff --git a/firmware/export/tuner.h b/firmware/export/tuner.h index 6d6a690d77..f92e94269c 100644 --- a/firmware/export/tuner.h +++ b/firmware/export/tuner.h @@ -96,6 +96,11 @@ extern int (*tuner_get)(int setting); #include "s1a0903x01.h" #endif +/** Philips TEA5760 **/ +#if (CONFIG_TUNER & TEA5760) +#include "tea5760.h" +#endif + /** Philips TEA5767 **/ #if (CONFIG_TUNER & TEA5767) /* Ondio FM, FM Recorder, Recorder V2, iRiver h100/h300, iAudio x5 */ diff --git a/firmware/target/arm/s5l8700/fmradio-i2c-meizu.c b/firmware/target/arm/s5l8700/fmradio-i2c-meizu.c new file mode 100644 index 0000000000..5a4113a6a5 --- /dev/null +++ b/firmware/target/arm/s5l8700/fmradio-i2c-meizu.c @@ -0,0 +1,146 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2008 by Bertrik Sikken + * + * 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. + * + ****************************************************************************/ + +/* + FM radio i2c interface, allows the radio driver to talk to the tuner chip. + + It is implemented using the generic i2c driver, which does "bit-banged" + I2C with a couple of GPIO pins. + */ + +#include "config.h" + +#include "inttypes.h" +#include "s5l8700.h" +#include "generic_i2c.h" +#include "fmradio_i2c.h" + +#define I2C_GPIO PDAT3 +#define I2C_GPIO_DIR PCON3 +#define I2C_SCL_PIN 4 +#define I2C_SDA_PIN 2 + +#define SCL_DIR_MASK (3<<(4*I2C_SCL_PIN)) +#define SCL_DIR_OUT (1<<(4*I2C_SCL_PIN)) +#define SCL_DIR_IN 0 +#define SDA_DIR_MASK (3<<(4*I2C_SDA_PIN)) +#define SDA_DIR_OUT (1<<(4*I2C_SDA_PIN)) +#define SDA_DIR_IN 0 + +static int fm_i2c_bus; + +static void fm_scl_hi(void) +{ + I2C_GPIO |= 1 << I2C_SCL_PIN; +} + +static void fm_scl_lo(void) +{ + I2C_GPIO &= ~(1 << I2C_SCL_PIN); +} + +static void fm_sda_hi(void) +{ + I2C_GPIO |= 1 << I2C_SDA_PIN; +} + +static void fm_sda_lo(void) +{ + I2C_GPIO &= ~(1 << I2C_SDA_PIN); +} + +static void fm_sda_input(void) +{ + I2C_GPIO_DIR = (I2C_GPIO_DIR & ~SDA_DIR_MASK) | SDA_DIR_IN; +} + +static void fm_sda_output(void) +{ + I2C_GPIO_DIR = (I2C_GPIO_DIR & ~SDA_DIR_MASK) | SDA_DIR_OUT; +} + +static void fm_scl_input(void) +{ + I2C_GPIO_DIR = (I2C_GPIO_DIR & ~SCL_DIR_MASK) | SCL_DIR_IN; +} + +static void fm_scl_output(void) +{ + I2C_GPIO_DIR = (I2C_GPIO_DIR & ~SCL_DIR_MASK) | SCL_DIR_OUT; +} + +static int fm_sda(void) +{ + return (I2C_GPIO & (1 << I2C_SDA_PIN)); +} + +static int fm_scl(void) +{ + return (I2C_GPIO & (1 << I2C_SCL_PIN)); +} + +/* simple and crude delay, used for all delays in the generic i2c driver */ +static void fm_delay(void) +{ + volatile int i; + + /* this loop is uncalibrated and could use more sophistication */ + for (i = 0; i < 20; i++) { + } +} + +/* interface towards the generic i2c driver */ +static const struct i2c_interface fm_i2c_interface = { + .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 +}; + +/* initialise i2c for fmradio */ +void fmradio_i2c_init(void) +{ + fm_i2c_bus = i2c_add_node(&fm_i2c_interface); +} + +int fmradio_i2c_write(unsigned char address, const unsigned char* buf, int count) +{ + return i2c_write_data(fm_i2c_bus, address, -1, buf, count); +} + +int fmradio_i2c_read(unsigned char address, unsigned char* buf, int count) +{ + return i2c_read_data(fm_i2c_bus, address, -1, buf, count); +} + diff --git a/firmware/tuner.c b/firmware/tuner.c index c5da27079c..a471c4e970 100644 --- a/firmware/tuner.c +++ b/firmware/tuner.c @@ -49,6 +49,16 @@ const unsigned char lv24020lp_region_data[TUNER_NUM_REGIONS] = }; #endif /* (CONFIG_TUNER & LV24020LP) */ +#if (CONFIG_TUNER & TEA5760) +const struct tea5760_region_data tea5760_region_data[TUNER_NUM_REGIONS] = +{ + [REGION_EUROPE] = { 1, 0 }, /* 50uS, US/Europe band */ + [REGION_US_CANADA] = { 0, 0 }, /* 75uS, US/Europe band */ + [REGION_JAPAN] = { 1, 1 }, /* 50uS, Japanese band */ + [REGION_KOREA] = { 1, 0 }, /* 50uS, US/Europe band */ +}; +#endif /* (CONFIG_TUNER & TEA5760) */ + #if (CONFIG_TUNER & TEA5767) const struct tea5767_region_data tea5767_region_data[TUNER_NUM_REGIONS] = { @@ -95,6 +105,12 @@ void tuner_init(void) lv24020lp_get, lv24020lp_init()) #endif + #if (CONFIG_TUNER & TEA5760) + TUNER_TYPE_CASE(TEA5760, + tea5760_set, + tea5760_get, + tea5760_init()) + #endif #if (CONFIG_TUNER & TEA5767) TUNER_TYPE_CASE(TEA5767, tea5767_set, -- cgit v1.2.3