Do some planned radio interface cleanup since adding in the LV24020LP.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13880 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 20dbf76..9c426c2 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -1955,27 +1955,27 @@
                  "HW detected: yes");
 #if (CONFIG_TUNER & LV24020LP)
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, 
-                 "CTRL_STAT: %02X", sanyo_get(RADIO_ALL) );
+                 "CTRL_STAT: %02X", lv24020lp_get(LV24020LP_CTRL_STAT) );
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, 
-                 "RADIO_STAT: %02X", sanyo_get(RADIO_REG_STAT));
+                 "RADIO_STAT: %02X", lv24020lp_get(LV24020LP_REG_STAT));
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, 
-                 "MSS_FM: %d kHz", sanyo_get(RADIO_MSS_FM) );
+                 "MSS_FM: %d kHz", lv24020lp_get(LV24020LP_MSS_FM) );
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN,
-                 "MSS_IF: %d Hz", (sanyo_get(RADIO_MSS_IF) ) );
+                 "MSS_IF: %d Hz", (lv24020lp_get(LV24020LP_MSS_IF) ) );
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, 
-                 "MSS_SD: %d Hz", (sanyo_get(RADIO_MSS_SD) ) );
+                 "MSS_SD: %d Hz", (lv24020lp_get(LV24020LP_MSS_SD) ) );
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, 
-                 "if_set: %d Hz", (sanyo_get(RADIO_IF_SET) ) );
+                 "if_set: %d Hz", (lv24020lp_get(LV24020LP_IF_SET) ) );
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, 
-                 "sd_set: %d Hz", (sanyo_get(RADIO_SD_SET) ) );
+                 "sd_set: %d Hz", (lv24020lp_get(LV24020LP_SD_SET) ) );
 #endif
 #if (CONFIG_TUNER & S1A0903X01)
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, 
-                 "Samsung regs: %08X", samsung_get(RADIO_ALL));
+                 "Samsung regs: %08X", s1a0903x01_get(RADIO_ALL));
 #endif
 #if (CONFIG_TUNER & TEA5767)
-        struct philips_dbg_info info;
-        philips_dbg_info(&info);
+        struct tea5767_dbg_info info;
+        tea5767_dbg_info(&info);
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, "Philips regs:");
         snprintf(debug_list_messages[radio_lines++], DEBUG_MSG_LEN, 
                  "   Read: %02X %02X %02X %02X %02X",
diff --git a/apps/recorder/radio.c b/apps/recorder/radio.c
index 0633289..c15f519 100644
--- a/apps/recorder/radio.c
+++ b/apps/recorder/radio.c
@@ -102,18 +102,6 @@
 #define RADIO_SCAN_MODE 0
 #define RADIO_PRESET_MODE 1
 
-static const struct fm_region_setting fm_region[] = {
-    /* Note: Desriptive strings are just for display atm and are not compiled. */
-    [REGION_EUROPE] =
-        FM_REGION_ENTRY("Europe",    87500000, 108000000,  50000, 0, 0),
-    [REGION_US_CANADA] =
-        FM_REGION_ENTRY("US/Canada", 87900000, 107900000, 200000, 1, 0),
-    [REGION_JAPAN] =
-        FM_REGION_ENTRY("Japan",     76000000,  90000000, 100000, 0, 1),
-    [REGION_KOREA] =
-        FM_REGION_ENTRY("Korea",     87500000, 108000000, 100000, 0, 0),
-    };
-
 static int curr_preset = -1;
 static int curr_freq;
 static int radio_mode = RADIO_SCAN_MODE;
@@ -176,66 +164,57 @@
 #define FMRADIO_START_PAUSED 0x8000
 void radio_start(void)
 {
-    const struct fm_region_setting *fmr;
+    const struct fm_region_data *fmr;
     bool start_paused;
-#if CONFIG_TUNER != LV24020LP
-    int mute_timeout;
-#endif
 
     if(radio_status == FMRADIO_PLAYING)
         return;
 
-    fmr = &fm_region[global_settings.fm_region];
+    fmr = &fm_region_data[global_settings.fm_region];
 
     start_paused = radio_status & FMRADIO_START_PAUSED;
     /* clear flag before any yielding */
     radio_status &= ~FMRADIO_START_PAUSED;
 
     if(radio_status == FMRADIO_OFF)
-        radio_power(true);
+        tuner_power(true);
 
     curr_freq = global_status.last_frequency 
         * fmr->freq_step + fmr->freq_min;
 
-    radio_set(RADIO_SLEEP, 0); /* wake up the tuner */
-#if (CONFIG_TUNER & LV24020LP)
-    radio_set(RADIO_REGION, global_settings.fm_region);
-    radio_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
-#endif
-    radio_set(RADIO_FREQUENCY, curr_freq);
-
-#if CONFIG_TUNER != LV24020LP
+    tuner_set(RADIO_SLEEP, 0); /* wake up the tuner */
 
     if(radio_status == FMRADIO_OFF)
     {
-#if (CONFIG_TUNER & S1A0903X01)
-        radio_set(RADIO_IF_MEASUREMENT, 0);
-        radio_set(RADIO_SENSITIVITY, 0);
+#ifdef HAVE_RADIO_REGION
+        tuner_set(RADIO_REGION, global_settings.fm_region);
 #endif
-        radio_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
-#if (CONFIG_TUNER & TEA5767)
-        radio_set(RADIO_SET_DEEMPHASIS, fmr->deemphasis);
-        radio_set(RADIO_SET_BAND, fmr->band);
-#endif
-        mute_timeout = current_tick + 1*HZ;
-    }
-    else
-    {
-        /* paused */
-        mute_timeout = current_tick + 2*HZ;
+        tuner_set(RADIO_FORCE_MONO, global_settings.fm_force_mono);
     }
 
-    while(!radio_get(RADIO_STEREO) && !radio_get(RADIO_TUNED))
+    tuner_set(RADIO_FREQUENCY, curr_freq);
+
+#ifdef HAVE_RADIO_MUTE_TIMEOUT
     {
-        if(TIME_AFTER(current_tick, mute_timeout))
-             break;
-        yield();
+        unsigned long mute_timeout = current_tick + HZ;
+        if (radio_status != FMRADIO_OFF)
+        {
+            /* paused */
+            mute_timeout += HZ;
+        }
+
+        while(!tuner_get(RADIO_STEREO) && !tuner_get(RADIO_TUNED))
+        {
+            if(TIME_AFTER(current_tick, mute_timeout))
+                 break;
+            yield();
+        }
     }
-#endif /* CONFIG_TUNER != LV24020LP */
+#endif
 
     /* keep radio from sounding initially */
     if(!start_paused)
-        radio_set(RADIO_MUTE, 0);
+        tuner_set(RADIO_MUTE, 0);
 
     radio_status = FMRADIO_PLAYING;
 } /* radio_start */
@@ -251,8 +230,8 @@
         radio_start();
     }
 
-    radio_set(RADIO_MUTE, 1);
-    radio_set(RADIO_SLEEP, 1);
+    tuner_set(RADIO_MUTE, 1);
+    tuner_set(RADIO_SLEEP, 1);
 
     radio_status = FMRADIO_PAUSED;
 } /* radio_pause */
@@ -262,30 +241,22 @@
     if(radio_status == FMRADIO_OFF)
         return;
 
-    radio_set(RADIO_MUTE, 1);
-    radio_set(RADIO_SLEEP, 1); /* low power mode, if available */
+    tuner_set(RADIO_MUTE, 1);
+    tuner_set(RADIO_SLEEP, 1); /* low power mode, if available */
     radio_status = FMRADIO_OFF;
-    radio_power(false); /* status update, power off if avail. */
+    tuner_power(false); /* status update, power off if avail. */
 } /* radio_stop */
 
 bool radio_hardware_present(void)
 {
-#ifdef HAVE_TUNER_PWR_CTRL
-    bool ret;
-    bool fmstatus = radio_power(true); /* power it up */
-    ret = radio_get(RADIO_PRESENT);
-    radio_power(fmstatus); /* restore previous state */
-    return ret;
-#else
-    return radio_get(RADIO_PRESENT);
-#endif
+    return tuner_get(RADIO_PRESENT);
 }
 
 /* Keep freq on the grid for the current region */
 static int snap_freq_to_grid(int freq)
 {
-    const struct fm_region_setting * const fmr =
-        &fm_region[global_settings.fm_region];
+    const struct fm_region_data * const fmr =
+        &fm_region_data[global_settings.fm_region];
 
     /* Range clamp if out of range or just round to nearest */
     if (freq < fmr->freq_min)
@@ -346,9 +317,8 @@
 
 static void remember_frequency(void)
 {
-    const struct fm_region_setting * const fmr =
-        &fm_region[global_settings.fm_region];
-
+    const struct fm_region_data * const fmr =
+        &fm_region_data[global_settings.fm_region];
     global_status.last_frequency = (curr_freq - fmr->freq_min) 
                                    / fmr->freq_step;
     status_save();
@@ -367,15 +337,15 @@
     /* Must stay on the current grid for the region */
     curr_freq = snap_freq_to_grid(presets[curr_preset].frequency);
 
-    radio_set(RADIO_FREQUENCY, curr_freq);
+    tuner_set(RADIO_FREQUENCY, curr_freq);
     remember_frequency();
 }
 
 /* Step to the next or previous frequency */
 static int step_freq(int freq, int direction)
 {
-    const struct fm_region_setting * const fmr =
-        &fm_region[global_settings.fm_region];
+    const struct fm_region_data * const fmr =
+        &fm_region_data[global_settings.fm_region];
 
     freq += direction*fmr->freq_step;
 
@@ -402,12 +372,12 @@
     curr_freq = step_freq(curr_freq, direction);
 
     if (radio_status == FMRADIO_PLAYING)
-        radio_set(RADIO_MUTE, 1);
+        tuner_set(RADIO_MUTE, 1);
 
-    radio_set(RADIO_FREQUENCY, curr_freq);
+    tuner_set(RADIO_FREQUENCY, curr_freq);
 
     if (radio_status == FMRADIO_PLAYING)
-        radio_set(RADIO_MUTE, 0);
+        tuner_set(RADIO_MUTE, 0);
 
     curr_preset = find_preset(curr_freq);
     remember_frequency();
@@ -417,7 +387,7 @@
 static void end_search(void)
 {
     if (search_dir != 0 && radio_status == FMRADIO_PLAYING)
-        radio_set(RADIO_MUTE, 0);
+        tuner_set(RADIO_MUTE, 0);
     search_dir = 0;
 }
 
@@ -540,7 +510,7 @@
             curr_freq = step_freq(curr_freq, search_dir);
             update_screen = true;
 
-            if(radio_set(RADIO_SCAN_FREQUENCY, curr_freq))
+            if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
             {
                 curr_preset = find_preset(curr_freq);
                 remember_frequency();
@@ -671,7 +641,7 @@
                 else if (dir == 0)
                 {
                     /* Starting auto scan */
-                    radio_set(RADIO_MUTE, 1);
+                    tuner_set(RADIO_MUTE, 1);
                     update_screen = true;
                 }
                 break;
@@ -847,7 +817,7 @@
                 /* keep "mono" from always being displayed when paused */
                 if (radio_status != FMRADIO_PAUSED)
                 {
-                    stereo = radio_get(RADIO_STEREO) &&
+                    stereo = tuner_get(RADIO_STEREO) &&
                         !global_settings.fm_force_mono;
 
                     if(stereo != last_stereo)
@@ -1329,21 +1299,12 @@
 
 void toggle_mono_mode(bool mono)
 {
-    radio_set(RADIO_FORCE_MONO, mono);
+    tuner_set(RADIO_FORCE_MONO, mono);
 }
 
 void set_radio_region(int region)
 {
-#if (CONFIG_TUNER & LV24020LP)
-    radio_set(RADIO_REGION, global_settings.fm_region);
-#endif
-#if (CONFIG_TUNER & TEA5767)
-    radio_set(RADIO_SET_DEEMPHASIS, 
-        fm_region[region].deemphasis);
-    radio_set(RADIO_SET_BAND, fm_region[region].band);
-#else
-    (void)region;
-#endif
+    tuner_set(RADIO_REGION, region);
     next_station(0);
     remember_frequency();
 }
@@ -1381,15 +1342,16 @@
         
     if(do_scan)
     {
-        const struct fm_region_setting * const fmr =
-            &fm_region[global_settings.fm_region];
+        const struct fm_region_data * const fmr =
+            &fm_region_data[global_settings.fm_region];
+
         char buf[MAX_FMPRESET_LEN + 1];
         int i;
 
         curr_freq = fmr->freq_min;
         num_presets = 0;
         memset(presets, 0, sizeof(presets));
-        radio_set(RADIO_MUTE, 1);
+        tuner_set(RADIO_MUTE, 1);
 
         while(curr_freq <= fmr->freq_max)
         {
@@ -1404,7 +1366,7 @@
             snprintf(buf, MAX_FMPRESET_LEN, str(LANG_FM_SCANNING), freq, frac);
             gui_syncsplash(0, buf);
 
-            if(radio_set(RADIO_SCAN_FREQUENCY, curr_freq))
+            if(tuner_set(RADIO_SCAN_FREQUENCY, curr_freq))
             {
                 /* add preset */
                 snprintf(buf, MAX_FMPRESET_LEN, 
@@ -1418,7 +1380,7 @@
         }
 
         if (radio_status == FMRADIO_PLAYING)
-            radio_set(RADIO_MUTE, 0);
+            tuner_set(RADIO_MUTE, 0);
 
         presets_changed = true;
         
diff --git a/apps/recorder/radio.h b/apps/recorder/radio.h
index f04c14d..c456d3a 100644
--- a/apps/recorder/radio.h
+++ b/apps/recorder/radio.h
@@ -44,23 +44,6 @@
     char name[MAX_FMPRESET_LEN+1];
 };
 
-struct fm_region_setting
-{
-    int freq_min;
-    int freq_max;
-    int freq_step;
-#if (CONFIG_TUNER & TEA5767)
-    char deemphasis;    /* 0: 50us, 1: 75us */
-    char band;          /* 0: europe, 1: japan (BL in TEA spec)*/
-    /* Note: "region" parameter is just for display atm and is not compiled. */
-    #define FM_REGION_ENTRY(region, fmin, fmax, fstep, deemph, band) \
-        { fmin, fmax, fstep, deemph, band }
-#else
-    #define FM_REGION_ENTRY(region, fmin, fmax, fstep, deemph, band) \
-        { fmin, fmax, fstep }
-#endif
-};
+#endif /* CONFIG_TUNER */
 
-#endif
-
-#endif
+#endif /* RADIO_H */
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 7e26ca0..2db8e9e 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -151,17 +151,18 @@
 /* Tuner */
 #if CONFIG_TUNER
 #ifndef SIMULATOR
+tuner.c
+#if (CONFIG_TUNER & LV24020LP)
+drivers/tuner/lv24020lp.c
+#endif /* (CONFIG_TUNER & LV24020LP) */
 #if (CONFIG_TUNER & S1A0903X01)
 drivers/fmradio.c
-tuner_samsung.c
+drivers/tuner/s1a0903x01.c
 #endif /* (CONFIG_TUNER & S1A0903X01) */
 #if (CONFIG_TUNER & TEA5767)
 drivers/fmradio_i2c.c
-tuner_philips.c
+drivers/tuner/tea5767.c
 #endif /* (CONFIG_TUNER & TEA5767) */
-#if (CONFIG_TUNER & LV24020LP)
-tuner_sanyo.c
-#endif /* (CONFIG_TUNER & LV24020LP) */
 #endif /*SIMULATOR */
 #endif /* CONFIG_TUNER */
 
diff --git a/firmware/drivers/power.c b/firmware/drivers/power.c
index eb69fce..50117dd 100644
--- a/firmware/drivers/power.c
+++ b/firmware/drivers/power.c
@@ -37,12 +37,12 @@
 
 static bool powered = false;
 
-bool radio_powered(void)
+bool tuner_powered(void)
 {
     return powered;
 }
 
-bool radio_power(bool status)
+bool tuner_power(bool status)
 {
     bool old_status = powered;
     powered = status;
diff --git a/firmware/tuner_sanyo.c b/firmware/drivers/tuner/lv24020lp.c
similarity index 75%
rename from firmware/tuner_sanyo.c
rename to firmware/drivers/tuner/lv24020lp.c
index 6e73297..9ec68f9 100644
--- a/firmware/tuner_sanyo.c
+++ b/firmware/drivers/tuner/lv24020lp.c
@@ -25,11 +25,9 @@
 #include "kernel.h"
 #include "tuner.h" /* tuner abstraction interface */
 #include "fmradio.h" /* physical interface driver */
-#include "mpeg.h"
 #include "sound.h"
 #include "pp5024.h"
 #include "system.h"
-#include "as3514.h"
 
 #ifndef BOOTLOADER
 
@@ -234,7 +232,7 @@
 #define TUNER_PRESENCE_CHECKED  (1 << 3)
 static unsigned tuner_status = 0;
 
-static unsigned char sanyo_regs[0x1c];
+static unsigned char lv24020lp_regs[0x1c];
 
 static const int sw_osc_low  = 10;  /* 30; */
 static const int sw_osc_high = 240; /* 200; */
@@ -253,7 +251,7 @@
 }
 
 /* send a byte to the tuner - expects write mode to be current */
-static void tuner_sanyo_send_byte(unsigned int byte)
+static void lv24020lp_send_byte(unsigned int byte)
 {
     int i;
 
@@ -274,7 +272,7 @@
 }
 
 /* end a write cycle on the tuner */
-static void tuner_sanyo_end_write(void)
+static void lv24020lp_end_write(void)
 {
     /* switch back to read mode */
     GPIOH_OUTPUT_EN &= ~(1 << FM_DATA_PIN);
@@ -282,7 +280,7 @@
 }
 
 /* prepare a write cycle on the tuner */
-static unsigned int tuner_sanyo_begin_write(unsigned int address)
+static unsigned int lv24020lp_begin_write(unsigned int address)
 {
     /* Get register's block, translate address */
     unsigned int blk = (address >= BLK2_START) ?
@@ -297,29 +295,29 @@
         udelay(FM_CLK_DELAY);
 
         /* current block == register block? */
-        if (blk == sanyo_regs[BLK_SEL])
+        if (blk == lv24020lp_regs[BLK_SEL])
             return address;
 
         /* switch block */
-        sanyo_regs[BLK_SEL] = blk;
+        lv24020lp_regs[BLK_SEL] = blk;
 
         /* data first */
-        tuner_sanyo_send_byte(blk);
+        lv24020lp_send_byte(blk);
         /* then address */
-        tuner_sanyo_send_byte(BLK_SEL);
+        lv24020lp_send_byte(BLK_SEL);
 
-        tuner_sanyo_end_write();
+        lv24020lp_end_write();
 
         udelay(FM_CLK_DELAY);
     }
 }
 
 /* write a byte to a tuner register */
-static void tuner_sanyo_write(unsigned int address, unsigned int data)
+static void lv24020lp_write(unsigned int address, unsigned int data)
 {
     /* shadow logical values but do logical=>physical remappings on some
        registers' data. */
-    sanyo_regs[address] = data;
+    lv24020lp_regs[address] = data;
 
     switch (address)
     {
@@ -340,39 +338,39 @@
         break;
     }
 
-    address = tuner_sanyo_begin_write(address);
+    address = lv24020lp_begin_write(address);
 
     /* data first */
-    tuner_sanyo_send_byte(data);
+    lv24020lp_send_byte(data);
     /* then address */
-    tuner_sanyo_send_byte(address);
+    lv24020lp_send_byte(address);
 
-    tuner_sanyo_end_write();
+    lv24020lp_end_write();
 }
 
 /* helpers to set/clear register bits */
-static void tuner_sanyo_write_or(unsigned int address, unsigned int bits)
+static void lv24020lp_write_or(unsigned int address, unsigned int bits)
 {
-    tuner_sanyo_write(address, sanyo_regs[address] | bits);
+    lv24020lp_write(address, lv24020lp_regs[address] | bits);
 }
 
-static void tuner_sanyo_write_and(unsigned int address, unsigned int bits)
+static void lv24020lp_write_and(unsigned int address, unsigned int bits)
 {
-    tuner_sanyo_write(address, sanyo_regs[address] & bits);
+    lv24020lp_write(address, lv24020lp_regs[address] & bits);
 }
 
 /* read a byte from a tuner register */
-static unsigned int tuner_sanyo_read(unsigned int address)
+static unsigned int lv24020lp_read(unsigned int address)
 {
     int i;
     unsigned int toread;
 
-    address = tuner_sanyo_begin_write(address);
+    address = lv24020lp_begin_write(address);
 
     /* address */
-    tuner_sanyo_send_byte(address);
+    lv24020lp_send_byte(address);
 
-    tuner_sanyo_end_write();
+    lv24020lp_end_write();
 
     /* data */
     toread = 0;
@@ -392,7 +390,7 @@
 /* enables auto frequency centering */
 static void enable_afc(bool enabled)
 {
-    unsigned int radio_ctrl1 = sanyo_regs[RADIO_CTRL1];
+    unsigned int radio_ctrl1 = lv24020lp_regs[RADIO_CTRL1];
 
     if (enabled)
     {
@@ -405,7 +403,7 @@
         radio_ctrl1 &= ~EN_AFC;
     }
 
-    tuner_sanyo_write(RADIO_CTRL1, radio_ctrl1);
+    lv24020lp_write(RADIO_CTRL1, radio_ctrl1);
 }
 
 static int calculate_coef(unsigned fkhz)
@@ -439,25 +437,25 @@
         return 0;
 
     /* enable measuring */
-    tuner_sanyo_write_or(MSRC_SEL, type);
-    tuner_sanyo_write_and(CNT_CTRL, ~CNT_SEL);
-    tuner_sanyo_write_or(RADIO_CTRL1, EN_MEAS);
+    lv24020lp_write_or(MSRC_SEL, type);
+    lv24020lp_write_and(CNT_CTRL, ~CNT_SEL);
+    lv24020lp_write_or(RADIO_CTRL1, EN_MEAS);
 
     /* reset counter */
-    tuner_sanyo_write_or(CNT_CTRL, CNT1_CLR);
-    tuner_sanyo_write_and(CNT_CTRL, ~CNT1_CLR);
+    lv24020lp_write_or(CNT_CTRL, CNT1_CLR);
+    lv24020lp_write_and(CNT_CTRL, ~CNT1_CLR);
 
     /* start counter, delay for specified time and stop it */
-    tuner_sanyo_write_or(CNT_CTRL, CNT_EN);
+    lv24020lp_write_or(CNT_CTRL, CNT_EN);
     udelay(duration*1000 - 16);
-    tuner_sanyo_write_and(CNT_CTRL, ~CNT_EN);
+    lv24020lp_write_and(CNT_CTRL, ~CNT_EN);
 
     /* read tick count */
-    finval = (tuner_sanyo_read(CNT_H) << 8) | tuner_sanyo_read(CNT_L);
+    finval = (lv24020lp_read(CNT_H) << 8) | lv24020lp_read(CNT_L);
 
     /* restore measure mode */
-    tuner_sanyo_write_and(RADIO_CTRL1, ~EN_MEAS);
-    tuner_sanyo_write_and(MSRC_SEL, ~type);
+    lv24020lp_write_and(RADIO_CTRL1, ~EN_MEAS);
+    lv24020lp_write_and(MSRC_SEL, ~type);
 
     /* convert value */
     if (type == MSS_FM)
@@ -469,7 +467,7 @@
 }
 
 /* set the FM oscillator frequency */
-static void sanyo_set_frequency(int freq)
+static void set_frequency(int freq)
 {
     int coef, cap_value, osc_value;
     int f1, f2, x1, x2;
@@ -494,7 +492,7 @@
                     coef_00, coef_01);
 
     osc_value = sw_osc_low;
-    tuner_sanyo_write(FM_OSC, osc_value); 
+    lv24020lp_write(FM_OSC, osc_value); 
 
     /* Just in case - don't go into infinite loop */
     for (count = 0; count < 30; count++)
@@ -505,7 +503,7 @@
                                 coef_10, coef_11);
         int coef_fcur, cap_new, coef_cor, range;
          
-        tuner_sanyo_write(FM_CAP, cap_value);
+        lv24020lp_write(FM_CAP, cap_value);
 
         range     = y1 - y0;
         f1        = tuner_measure(MSS_FM, 1, 16);
@@ -554,7 +552,7 @@
     {
         int x2_new;
 
-        tuner_sanyo_write(FM_OSC, x2);
+        lv24020lp_write(FM_OSC, x2);
         f2 = tuner_measure(MSS_FM, 1, 16);
 
         if (abs(f2 - freq) <= 16)
@@ -632,9 +630,9 @@
 
 static int if_setcmp(int regval)
 {
-    tuner_sanyo_write(IF_OSC, regval);
-    tuner_sanyo_write(IF_CENTER, regval);
-    tuner_sanyo_write(IF_BW, 65*regval/100);
+    lv24020lp_write(IF_OSC, regval);
+    lv24020lp_write(IF_CENTER, regval);
+    lv24020lp_write(IF_BW, 65*regval/100);
 
     if_set = tuner_measure(MSS_IF, 1000, 32);
 
@@ -649,7 +647,7 @@
 
 static int sd_setcmp(int regval)
 {
-    tuner_sanyo_write(SD_OSC, regval);
+    lv24020lp_write(SD_OSC, regval);
 
     sd_set = tuner_measure(MSS_SD, 1000, 32);
 
@@ -659,7 +657,7 @@
     return sd_set < 38300 ? -1 : 1;
 }
 
-static void sanyo_sleep(bool sleep)
+static void set_sleep(bool sleep)
 {
     if (sleep || tuner_awake())
         return;
@@ -673,41 +671,41 @@
     enable_afc(false);
 
     /* 2. Calibrate the IF frequency at 110 kHz: */
-    tuner_sanyo_write_and(RADIO_CTRL2, ~IF_PM_L);
+    lv24020lp_write_and(RADIO_CTRL2, ~IF_PM_L);
     fine_step_tune(if_setcmp, 0x80, 8);
-    tuner_sanyo_write_or(RADIO_CTRL2, IF_PM_L);
+    lv24020lp_write_or(RADIO_CTRL2, IF_PM_L);
 
     /* 3. Calibrate the stereo decoder clock at 38.3 kHz: */
-    tuner_sanyo_write_or(STEREO_CTRL, SD_PM);
+    lv24020lp_write_or(STEREO_CTRL, SD_PM);
     fine_step_tune(sd_setcmp, 0x80, 8);
-    tuner_sanyo_write_and(STEREO_CTRL, ~SD_PM);
+    lv24020lp_write_and(STEREO_CTRL, ~SD_PM);
 
     /* calculate FM tuning coefficients */
-    tuner_sanyo_write(FM_CAP, sw_cap_low);
-    tuner_sanyo_write(FM_OSC, sw_osc_low);
+    lv24020lp_write(FM_CAP, sw_cap_low);
+    lv24020lp_write(FM_OSC, sw_osc_low);
     coef_00 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
 
-    tuner_sanyo_write(FM_CAP, sw_cap_high);
+    lv24020lp_write(FM_CAP, sw_cap_high);
     coef_01 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
 
-    tuner_sanyo_write(FM_CAP, sw_cap_low);
-    tuner_sanyo_write(FM_OSC, sw_osc_high);
+    lv24020lp_write(FM_CAP, sw_cap_low);
+    lv24020lp_write(FM_OSC, sw_osc_high);
     coef_10 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
 
-    tuner_sanyo_write(FM_CAP, sw_cap_high);
+    lv24020lp_write(FM_CAP, sw_cap_high);
     coef_11 = calculate_coef(tuner_measure(MSS_FM, 1, 64));
 
     /* set various audio level settings */
-    tuner_sanyo_write(AUDIO_CTRL1, TONE_LVL_SET(0) | VOL_LVL_SET(0));
-    tuner_sanyo_write_or(RADIO_CTRL2, AGCSP);
-    tuner_sanyo_write_or(RADIO_CTRL3, VOLSH);
-    tuner_sanyo_write(STEREO_CTRL, FMCS_SET(7) | AUTOSSR);
-    tuner_sanyo_write(PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(1) |
-                      PW_RAD);
+    lv24020lp_write(AUDIO_CTRL1, TONE_LVL_SET(0) | VOL_LVL_SET(0));
+    lv24020lp_write_or(RADIO_CTRL2, AGCSP);
+    lv24020lp_write_or(RADIO_CTRL3, VOLSH);
+    lv24020lp_write(STEREO_CTRL, FMCS_SET(7) | AUTOSSR);
+    lv24020lp_write(PW_SCTRL, SS_CTRL_SET(3) | SM_CTRL_SET(1) |
+                    PW_RAD);
 }
 
 /** Public interfaces **/
-bool radio_power(bool status)
+void lv24020lp_power(bool status)
 {
     static const unsigned char tuner_defaults[][2] =
     {
@@ -733,38 +731,13 @@
     };
 
     unsigned i;
-    bool powered = tuner_status & TUNER_POWERED;
-
-    if (status == powered)
-        return powered;
 
     if (status)
     {
-        /* init mystery amplification device */
-        outl(inl(0x70000084) | 0x1, 0x70000084);
-        udelay(5);
-
-        /* When power up, host should initialize the 3-wire bus in host read
-           mode: */
-
-        /* 1. Set direction of the DATA-line to input-mode. */
-        GPIOH_OUTPUT_EN &= ~(1 << FM_DATA_PIN); 
-        GPIOH_ENABLE |= (1 << FM_DATA_PIN); 
-
-        /* 2. Drive NR_W low */
-        GPIOH_OUTPUT_VAL &= ~(1 << FM_NRW_PIN); 
-        GPIOH_OUTPUT_EN |= (1 << FM_NRW_PIN); 
-        GPIOH_ENABLE |= (1 << FM_NRW_PIN); 
-
-        /* 3. Drive CLOCK high */
-        GPIOH_OUTPUT_VAL |= (1 << FM_CLOCK_PIN); 
-        GPIOH_OUTPUT_EN |= (1 << FM_CLOCK_PIN); 
-        GPIOH_ENABLE |= (1 << FM_CLOCK_PIN);
-
-        tuner_status |= TUNER_POWERED;
+        tuner_status |= TUNER_POWERED | TUNER_PRESENCE_CHECKED;
 
         /* if tuner is present, CHIP ID is 0x09 */
-        if (tuner_sanyo_read(CHIP_ID) == 0x09)
+        if (lv24020lp_read(CHIP_ID) == 0x09)
         {
             tuner_status |= TUNER_PRESENT;
 
@@ -772,9 +745,9 @@
                follows: */
 
             /* 1. Write default values to the registers: */
-            sanyo_regs[BLK_SEL] = 0; /* Force a switch on the first */
+            lv24020lp_regs[BLK_SEL] = 0; /* Force a switch on the first */
             for (i = 0; i < ARRAYLEN(tuner_defaults); i++)
-                tuner_sanyo_write(tuner_defaults[i][0], tuner_defaults[i][1]);
+                lv24020lp_write(tuner_defaults[i][0], tuner_defaults[i][1]);
 
             /* Complete the startup calibration if the tuner is woken */
             udelay(100000);
@@ -782,76 +755,55 @@
     }
     else
     {
-        /* Power off and set all as inputs */
+        /* Power off */
         if (tuner_status & TUNER_PRESENT)
-            tuner_sanyo_write_and(PW_SCTRL, ~PW_RAD);
-
-        GPIOH_OUTPUT_EN &= ~((1 << FM_DATA_PIN) | (1 << FM_NRW_PIN) |
-                             (1 << FM_CLOCK_PIN));
-        GPIOH_ENABLE &= ~((1 << FM_DATA_PIN) | (1 << FM_NRW_PIN) |
-                          (1 << FM_CLOCK_PIN)); 
-
-        outl(inl(0x70000084) & ~0x1, 0x70000084);
+            lv24020lp_write_and(PW_SCTRL, ~PW_RAD);
 
         tuner_status &= ~(TUNER_POWERED | TUNER_AWAKE);
     }
-
-    return powered;
 }
 
-bool radio_powered(void)
-{
-    return (tuner_status & TUNER_POWERED) != 0;
-}
-
-int sanyo_set(int setting, int value)
+int lv24020lp_set(int setting, int value)
 {
     int val = 1;
 
     switch(setting)
     {
     case RADIO_SLEEP:
-        sanyo_sleep(value);
+        set_sleep(value);
         break;
 
     case RADIO_FREQUENCY:
-        sanyo_set_frequency(value);
+        set_frequency(value);
         break;
 
     case RADIO_SCAN_FREQUENCY:
         /* TODO: really implement this */
-        sanyo_set_frequency(value);
-        val = sanyo_get(RADIO_TUNED);
+        set_frequency(value);
+        val = lv24020lp_get(RADIO_TUNED);
         break;
 
     case RADIO_MUTE:
         if (value)
-            tuner_sanyo_write_and(RADIO_CTRL3, ~AMUTE_L);
+            lv24020lp_write_and(RADIO_CTRL3, ~AMUTE_L);
         else
-            tuner_sanyo_write_or(RADIO_CTRL3, AMUTE_L);
+            lv24020lp_write_or(RADIO_CTRL3, AMUTE_L);
         break;
 
     case RADIO_REGION:
-        switch (value)
-        {
-        case REGION_EUROPE:
-        case REGION_JAPAN:
-        case REGION_KOREA:
-            tuner_sanyo_write_and(AUDIO_CTRL2, ~DEEMP);
-            break;
-        case REGION_US_CANADA:
-            tuner_sanyo_write_or(AUDIO_CTRL2, DEEMP);
-            break;
-        default:
-            val = -1;
-        }
+    {
+        if (lv24020lp_region_data[value])
+            lv24020lp_write_or(AUDIO_CTRL2, DEEMP);
+        else
+            lv24020lp_write_and(AUDIO_CTRL2, ~DEEMP);
         break;
+        }
 
     case RADIO_FORCE_MONO:
         if (value)
-            tuner_sanyo_write_or(STEREO_CTRL, ST_M);
+            lv24020lp_write_or(STEREO_CTRL, ST_M);
         else
-            tuner_sanyo_write_and(STEREO_CTRL, ~ST_M);
+            lv24020lp_write_and(STEREO_CTRL, ~ST_M);
         break;
 
     default:
@@ -861,45 +813,55 @@
     return val;
 }
 
-int sanyo_get(int setting)
+int lv24020lp_get(int setting)
 {
     int val = -1;
 
     switch(setting)
     {
-    case RADIO_ALL:
-        return tuner_sanyo_read(CTRL_STAT);
-
     case RADIO_TUNED:
         /* TODO: really implement this */
-        val = RSS_FS(tuner_sanyo_read(RADIO_STAT)) < 0x1f;
+        val = RSS_FS(lv24020lp_read(RADIO_STAT)) < 0x1f;
         break;
 
     case RADIO_STEREO:
-        val = (tuner_sanyo_read(RADIO_STAT) & RSS_MS) != 0;
+        val = (lv24020lp_read(RADIO_STAT) & RSS_MS) != 0;
         break;
 
     case RADIO_PRESENT:
+    {
+        bool fmstatus = true;
+
+        if (!(tuner_status & TUNER_PRESENCE_CHECKED))
+            fmstatus = tuner_power(true);
+
         val = (tuner_status & TUNER_PRESENT) != 0;
+
+        if (!fmstatus)
+            tuner_power(false);
         break;
+        }
 
     /* tuner-specific debug info */
-    case RADIO_REG_STAT:
-        return tuner_sanyo_read(RADIO_STAT);
+    case LV24020LP_CTRL_STAT:
+        return lv24020lp_read(CTRL_STAT);
 
-    case RADIO_MSS_FM:
+    case LV24020LP_REG_STAT:
+        return lv24020lp_read(RADIO_STAT);
+
+    case LV24020LP_MSS_FM:
         return tuner_measure(MSS_FM, 1, 16);
 
-    case RADIO_MSS_IF:
+    case LV24020LP_MSS_IF:
         return tuner_measure(MSS_IF, 1000, 16);
 
-    case RADIO_MSS_SD:
+    case LV24020LP_MSS_SD:
         return tuner_measure(MSS_SD, 1000, 16);
 
-    case RADIO_IF_SET:
+    case LV24020LP_IF_SET:
         return if_set;
 
-    case RADIO_SD_SET:
+    case LV24020LP_SD_SET:
         return sd_set;
     }
 
diff --git a/firmware/tuner_samsung.c b/firmware/drivers/tuner/s1a0903x01.c
similarity index 81%
rename from firmware/tuner_samsung.c
rename to firmware/drivers/tuner/s1a0903x01.c
index 82934d7..cdeba2b 100644
--- a/firmware/tuner_samsung.c
+++ b/firmware/drivers/tuner/s1a0903x01.c
@@ -33,9 +33,10 @@
 
 static int fm_in1;
 static int fm_in2;
+static int fm_present = -1; /* unknown */
 
 /* tuner abstraction layer: set something to the tuner */
-int samsung_set(int setting, int value)
+int s1a0903x01_set(int setting, int value)
 {
     int val = 1;
 
@@ -95,12 +96,13 @@
 
         case RADIO_SCAN_FREQUENCY:
             /* Tune in and delay */
-            samsung_set(RADIO_FREQUENCY, value);
+            s1a0903x01_set(RADIO_FREQUENCY, value);
             sleep(1);
             /* Start IF measurement */
-            samsung_set(RADIO_IF_MEASUREMENT, 1);
+            fm_in1 |= 4;
+            fmradio_set(1, fm_in1);
             sleep(1);
-            val = samsung_get(RADIO_TUNED);
+            val = s1a0903x01_get(RADIO_TUNED);
             break;
 
         case RADIO_MUTE:
@@ -108,20 +110,23 @@
             fmradio_set(1, fm_in1);
             break;
 
-        case RADIO_IF_MEASUREMENT:
-            fm_in1 = (fm_in1 & 0xfffffffb) | (value?4:0);
-            fmradio_set(1, fm_in1);
-            break;
-
-        case RADIO_SENSITIVITY:
-            fm_in2 = (fm_in2 & 0xffff9fff) | ((value & 3) << 13);
-            fmradio_set(2, fm_in2);
-            break;
-
         case RADIO_FORCE_MONO:
             fm_in2 = (fm_in2 & 0xfffffffb) | (value?0:4);
             fmradio_set(2, fm_in2);
             break;
+        /* NOTE: These were only zeroed when starting the tuner from OFF
+                 but the default values already set them to 0. */
+#if 0
+        case S1A0903X01_IF_MEASUREMENT:
+            fm_in1 = (fm_in1 & 0xfffffffb) | (value?4:0);
+            fmradio_set(1, fm_in1);
+            break;
+
+        case S1A0903X01_SENSITIVITY:
+            fm_in2 = (fm_in2 & 0xffff9fff) | ((value & 3) << 13);
+            fmradio_set(2, fm_in2);
+            break;
+#endif
         default:
             val = -1;
     }
@@ -130,14 +135,27 @@
 }
 
 /* tuner abstraction layer: read something from the tuner */
-int samsung_get(int setting)
+int s1a0903x01_get(int setting)
 {
     int val = -1;
     switch(setting)
     {
         case RADIO_PRESENT:
-            fmradio_set(2, 0x140885); /* 5kHz, 7.2MHz crystal, test mode 1 */
-            val = (fmradio_read(0) == 0x140885);
+            if (fm_present == -1)
+            {
+#ifdef HAVE_TUNER_PWR_CTRL
+                bool fmstatus = tuner_power(true);
+#endif
+                /* 5kHz, 7.2MHz crystal, test mode 1 */
+                fmradio_set(2, 0x140885);
+                fm_present = (fmradio_read(0) == 0x140885);
+#ifdef HAVE_TUNER_PWR_CTRL
+                if (!fmstatus)
+                    tuner_power(false);
+#endif
+            }
+
+            val = fm_present;
             break;
 
         case RADIO_TUNED:
@@ -148,6 +166,7 @@
         case RADIO_STEREO:
             val = fmradio_read(3);
             val = ((val & 0x100000) ? true : false);
+            break;
     }
     return val;
 }
diff --git a/firmware/tuner_philips.c b/firmware/drivers/tuner/tea5767.c
similarity index 78%
rename from firmware/tuner_philips.c
rename to firmware/drivers/tuner/tea5767.c
index 8520fdb..da7cdfb 100644
--- a/firmware/tuner_philips.c
+++ b/firmware/drivers/tuner/tea5767.c
@@ -23,13 +23,21 @@
 #include <stdlib.h>
 #include "kernel.h"
 #include "tuner.h" /* tuner abstraction interface */
+#include "fmradio.h"
 #include "fmradio_i2c.h" /* physical interface driver */
 
 #define I2C_ADR 0xC0
 static unsigned char write_bytes[5] = { 0x00, 0x00, 0x00, 0x00, 0x00 };
 
+static void tea5767_set_clear(int byte, unsigned char bits, int set)
+{
+    write_bytes[byte] &= ~bits;
+    if (set)
+        write_bytes[byte] |= bits;
+}
+
 /* tuner abstraction layer: set something to the tuner */
-int philips_set(int setting, int value)
+int tea5767_set(int setting, int value)
 {
     switch(setting)
     {
@@ -44,7 +52,7 @@
             write_bytes[3] |= (1<<3) | (1<<1); 
 #endif
             /* sleep / standby mode */
-            write_bytes[3] &= ~(1<<6) | (value ? (1<<6) : 0);
+            tea5767_set_clear(3, (1<<6), value);
             break;
 
         case RADIO_FREQUENCY:
@@ -56,38 +64,41 @@
                 n = (4 * (value - 225000)) / 50000;
 #endif
                 write_bytes[0] = (write_bytes[0] & 0xC0) | (n >> 8);
-                write_bytes[1] = n & 0xFF;
+                write_bytes[1] = n;
             }
             break;
 
         case RADIO_SCAN_FREQUENCY:
-            philips_set(RADIO_FREQUENCY, value);
+            tea5767_set(RADIO_FREQUENCY, value);
             sleep(HZ/30);
-            return philips_get(RADIO_TUNED);
+            return tea5767_get(RADIO_TUNED);
 
         case RADIO_MUTE:
-            write_bytes[0] = (write_bytes[0] & 0x7F) | (value ? 0x80 : 0);
+            tea5767_set_clear(0, 0x80, value);
             break;
 
+        case RADIO_REGION:
+        {
+            const struct tea5767_region_data *rd =
+                &tea5767_region_data[value];
+
+            tea5767_set_clear(4, (1<<6), rd->deemphasis);
+            tea5767_set_clear(3, (1<<5), rd->band);
+            break;
+            }
         case RADIO_FORCE_MONO:
-            write_bytes[2] = (write_bytes[2] & 0xF7) | (value ? 0x08 : 0);
+            tea5767_set_clear(2, 0x08, value);
             break;
-
-        case RADIO_SET_DEEMPHASIS:
-            write_bytes[4] = (write_bytes[4] & ~(1<<6)) | (value ? (1<<6) : 0);
-            break;
-
-        case RADIO_SET_BAND:
-            write_bytes[3] = (write_bytes[3] & ~(1<<5)) | (value ? (1<<5) : 0);
         default:
             return -1;
     }
+
     fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes));
     return 1;
 }
 
 /* tuner abstraction layer: read something from the tuner */
-int philips_get(int setting)
+int tea5767_get(int setting)
 {
     unsigned char read_bytes[5];
     int val = -1; /* default for unsupported query */
@@ -113,10 +124,11 @@
             val = read_bytes[2] >> 7;
             break;
     }
+
     return val;
 }
 
-void philips_dbg_info(struct philips_dbg_info *info)
+void tea5767_dbg_info(struct tea5767_dbg_info *info)
 {
     fmradio_i2c_read(I2C_ADR, info->read_regs, 5);
     memcpy(info->write_regs, write_bytes, 5);
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 86e27d0..056b985 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -245,6 +245,11 @@
 #define CONFIG_REMOTE_DEFAULT_ICON_WIDTH 6
 #endif
 
+#if (CONFIG_TUNER & (CONFIG_TUNER - 1)) != 0
+/* Multiple possible tuners */
+#define CONFIG_TUNER_MULTI
+#endif
+
 /* Enable the directory cache and tagcache in RAM if we have
  * plenty of RAM. Both features can be enabled independently. */
 #if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8) && \
diff --git a/firmware/export/hwcompat.h b/firmware/export/hwcompat.h
index bffb76e..bebca68 100644
--- a/firmware/export/hwcompat.h
+++ b/firmware/export/hwcompat.h
@@ -46,4 +46,11 @@
 bool is_new_player(void);
 #endif
 
+#ifdef CONFIG_TUNER_MULTI
+static inline int tuner_detect_type(void)
+{
+    return (HW_MASK & TUNER_MODEL) ? TEA5767 : S1A0903X01;
+}
+#endif
+
 #endif /* HWCOMPAT_H */
diff --git a/firmware/export/lv24020lp.h b/firmware/export/lv24020lp.h
new file mode 100644
index 0000000..0fc39b1
--- /dev/null
+++ b/firmware/export/lv24020lp.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ * Tuner header for the Sanyo LV24020LP
+ *
+ * Copyright (C) 2007 Michael Sevakis
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _LV24020LP_H_
+#define _LV24020LP_H_
+
+/* Define additional tuner messages here */
+#define HAVE_RADIO_REGION
+
+#define LV24020LP_CTRL_STAT (RADIO_GET_CHIP_FIRST+0)
+#define LV24020LP_REG_STAT  (RADIO_GET_CHIP_FIRST+1)
+#define LV24020LP_MSS_FM    (RADIO_GET_CHIP_FIRST+2)
+#define LV24020LP_MSS_IF    (RADIO_GET_CHIP_FIRST+3)
+#define LV24020LP_MSS_SD    (RADIO_GET_CHIP_FIRST+4)
+#define LV24020LP_IF_SET    (RADIO_GET_CHIP_FIRST+5)
+#define LV24020LP_SD_SET    (RADIO_GET_CHIP_FIRST+6)
+
+struct lv24020lp_region_data
+{
+    unsigned char deemphasis;
+} __attribute__((packed));
+
+const unsigned char lv24020lp_region_data[TUNER_NUM_REGIONS];
+
+int lv24020lp_set(int setting, int value);
+int lv24020lp_get(int setting);
+void lv24020lp_power(bool status);
+
+#ifndef CONFIG_TUNER_MULTI
+#define tuner_set lv24020lp_set
+#define tuner_get lv24020lp_get
+#endif
+
+#endif /* _LV24020LP_H_ */
diff --git a/firmware/export/power.h b/firmware/export/power.h
index cafd6f8..8ecff6c 100644
--- a/firmware/export/power.h
+++ b/firmware/export/power.h
@@ -48,8 +48,8 @@
 #endif
 
 #if CONFIG_TUNER
-extern bool radio_power(bool status);
-extern bool radio_powered(void);
+extern bool tuner_power(bool status);
+extern bool tuner_powered(void);
 #endif
 
 #endif
diff --git a/firmware/export/s1a0903x01.h b/firmware/export/s1a0903x01.h
new file mode 100644
index 0000000..bf49762
--- /dev/null
+++ b/firmware/export/s1a0903x01.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ * Tuner header for the Samsung S1A0903X01
+ *
+ * Copyright (C) 2007 Michael Sevakis
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _S1A0903X01_H_
+#define _S1A0903X01_H_
+
+/* Define additional tuner messages here */
+#define HAVE_RADIO_MUTE_TIMEOUT
+
+#if 0
+#define S1A0903X01_IF_MEASUREMENT (RADIO_SET_CHIP_FIRST+0)
+#define S1A0903X01_SENSITIVITY    (RADIO_SET_CHIP_FIRST+1)
+#endif
+
+int s1a0903x01_set(int setting, int value);
+int s1a0903x01_get(int setting);
+
+#ifndef CONFIG_TUNER_MULTI
+#define tuner_set s1a0903x01_get
+#define tuner_get s1a0903x01_set
+#endif
+
+#endif /* _S1A0903X01_H_ */
diff --git a/firmware/export/tea5767.h b/firmware/export/tea5767.h
new file mode 100644
index 0000000..dfa6149
--- /dev/null
+++ b/firmware/export/tea5767.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ * Tuner header for the Philips TEA5767
+ *
+ * Copyright (C) 2007 Michael Sevakis
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _TEA5767_H_
+#define _TEA5767_H_
+
+#define HAVE_RADIO_REGION
+#define HAVE_RADIO_MUTE_TIMEOUT
+
+struct tea5767_region_data
+{
+    unsigned char deemphasis; /* 0: 50us, 1: 75us */
+    unsigned char band; /* 0: europe, 1: japan (BL in TEA spec)*/
+} __attribute__((packed));
+
+const struct tea5767_region_data tea5767_region_data[TUNER_NUM_REGIONS];
+
+struct tea5767_dbg_info
+{
+    unsigned char read_regs[5];
+    unsigned char write_regs[5];
+};
+
+int tea5767_set(int setting, int value);
+int tea5767_get(int setting);
+void tea5767_dbg_info(struct tea5767_dbg_info *info);
+
+#ifndef CONFIG_TUNER_MULTI
+#define tuner_set tea5767_set
+#define tuner_get tea5767_get
+#endif
+
+#endif /* _TEA5767_H_ */
diff --git a/firmware/export/tuner.h b/firmware/export/tuner.h
index 9f6d29f..cf18102 100644
--- a/firmware/export/tuner.h
+++ b/firmware/export/tuner.h
@@ -22,101 +22,112 @@
 
 #include "hwcompat.h"
 
-/* settings to the tuner layer */
-#define RADIO_ALL               -1 /* debug */
-#define RADIO_SLEEP             0
-#define RADIO_FREQUENCY         1
-#define RADIO_MUTE              2
-#define RADIO_IF_MEASUREMENT    3
-#define RADIO_SENSITIVITY       4
-#define RADIO_FORCE_MONO        5
-#define RADIO_SCAN_FREQUENCY    6
-#if (CONFIG_TUNER & TEA5767)
-#define RADIO_SET_DEEMPHASIS    7
-#define RADIO_SET_BAND          8
-#endif
-#if (CONFIG_TUNER & LV24020LP)
-#define RADIO_REGION            9 /* to be used for all tuners */
-#define RADIO_REG_STAT          100
-#define RADIO_MSS_FM            101
-#define RADIO_MSS_IF            102
-#define RADIO_MSS_SD            103
-#define RADIO_IF_SET            104
-#define RADIO_SD_SET            105
-#endif
-/* readback from the tuner layer */
-#define RADIO_PRESENT           0
-#define RADIO_TUNED             1
-#define RADIO_STEREO            2
+/** Settings to the tuner layer **/
+enum
+{
+    RADIO_ALL = -1, /* debug */
+    RADIO_SLEEP,
+    RADIO_FREQUENCY,
+    RADIO_MUTE,
+    RADIO_FORCE_MONO,
+    RADIO_SCAN_FREQUENCY,
 
-#define REGION_EUROPE           0
-#define REGION_US_CANADA        1
-#define REGION_JAPAN            2
-#define REGION_KOREA            3
+    /* Put new general-purpose settings above this line */
+    __RADIO_SET_STANDARD_LAST
+};
+
+/** Readback from the tuner layer **/
+enum
+{
+    RADIO_PRESENT = 0,
+    RADIO_TUNED,
+    RADIO_STEREO,
+
+    /* Put new general-purpose readback values above this line */
+    __RADIO_GET_STANDARD_LAST
+};
+
+/** Tuner regions **/
+
+/* Basic region information */
+enum
+{
+    REGION_EUROPE = 0,
+    REGION_US_CANADA,
+    REGION_JAPAN,
+    REGION_KOREA,
+
+    /* Add new regions above this line */
+    TUNER_NUM_REGIONS
+};
+
+struct fm_region_data
+{
+    int freq_min;
+    int freq_max;
+    int freq_step;
+};
+
+extern const struct fm_region_data fm_region_data[TUNER_NUM_REGIONS];
 
 #if CONFIG_TUNER
 
 #ifdef SIMULATOR
-int radio_set(int setting, int value);
-int radio_get(int setting);
+int tuner_set(int setting, int value);
+int tuner_get(int setting);
 #else
-#if CONFIG_TUNER == S1A0903X01 /* FM recorder */
-#define radio_set samsung_set
-#define radio_get samsung_get
-#elif CONFIG_TUNER == LV24020LP  /* Sansa */
-#define radio_set sanyo_set
-#define radio_get sanyo_get
-#elif CONFIG_TUNER == TEA5767  /* iRiver, iAudio */
-#define radio_set philips_set
-#define radio_get philips_get
-#elif CONFIG_TUNER == (S1A0903X01 | TEA5767) /* OndioFM */
-#define radio_set _radio_set
-#define radio_get _radio_get
-int (*_radio_set)(int setting, int value);
-int (*_radio_get)(int setting);
-#endif /* CONFIG_TUNER == */
+
+#ifdef CONFIG_TUNER_MULTI
+extern int (*tuner_set)(int setting, int value);
+extern int (*tuner_get)(int setting);
+#endif /* CONFIG_TUNER_MULTI */
+
+/** Sanyo LV24020LP **/
+#if (CONFIG_TUNER & LV24020LP)
+/* Sansa e200 Series */
+#include "lv24020lp.h"
+#endif
+
+/** Samsung S1A0903X01 **/
+#if (CONFIG_TUNER & S1A0903X01)
+/* Ondio FM, FM Recorder */
+#include "s1a0903x01.h"
+#endif
+
+/** Philips TEA5767 **/
+#if (CONFIG_TUNER & TEA5767)
+/* Ondio FM, FM Recorder, Recorder V2, iRiver h100/h300, iAudio x5 */
+#include "tea5767.h"
+#endif
+
 #endif /* SIMULATOR */
 
-#if (CONFIG_TUNER & S1A0903X01)
-int samsung_set(int setting, int value);
-int samsung_get(int setting);
-#endif /* CONFIG_TUNER & S1A0903X01 */
+/* Additional messages that get enumerated after tuner driver headers */
 
-#if (CONFIG_TUNER & LV24020LP)
-int sanyo_set(int setting, int value);
-int sanyo_get(int setting);
-#endif /* CONFIG_TUNER & LV24020LP */
-
-#if (CONFIG_TUNER & TEA5767)
-struct philips_dbg_info
+/* for tuner_set */
+enum
 {
-    unsigned char read_regs[5];
-    unsigned char write_regs[5];
+    __RADIO_SET_ADDITIONAL_START = __RADIO_SET_STANDARD_LAST-1,
+#ifdef HAVE_RADIO_REGION
+    RADIO_REGION,
+#endif
+
+    RADIO_SET_CHIP_FIRST
 };
-int philips_set(int setting, int value);
-int philips_get(int setting);
-void philips_dbg_info(struct philips_dbg_info *info);
-#endif /* CONFIG_TUNER & TEA5767 */
 
-/* Just inline here since only radio screen needs this atm and
-   there's no tuner.c. */
-static inline void tuner_init(void)
+/* for tuner_get */
+enum
 {
-#ifndef SIMULATOR
-#if CONFIG_TUNER == (S1A0903X01 | TEA5767)
-    if (HW_MASK & TUNER_MODEL)
-    {
-        _radio_set = philips_set;
-        _radio_get = philips_get;
-    }
-    else
-    {
-        _radio_set = samsung_set;
-        _radio_get = samsung_get;
-    }
-#endif
-#endif
-}
+    __RADIO_GET_ADDITIONAL_START = __RADIO_SET_STANDARD_LAST-1,
+
+    RADIO_GET_CHIP_FIRST
+};
+
+/** **/
+
+void tuner_init(void);
+bool tuner_power(bool power);
+bool tuner_powered(void);
 
 #endif /* #if CONFIG_TUNER */
 
diff --git a/firmware/target/arm/archos/av300/power-av300.c b/firmware/target/arm/archos/av300/power-av300.c
index cfdce69..6cc8b4f 100644
--- a/firmware/target/arm/archos/av300/power-av300.c
+++ b/firmware/target/arm/archos/av300/power-av300.c
@@ -84,12 +84,12 @@
 
 static bool powered = false;
 
-bool radio_powered()
+bool tuner_powered()
 {
     return powered;
 }
 
-bool radio_power(bool status)
+bool tuner_power(bool status)
 {
     bool old_status = powered;
     powered = status;
diff --git a/firmware/target/arm/iriver/h10/power-h10.c b/firmware/target/arm/iriver/h10/power-h10.c
index 63eb2dc..d0d3ce5 100644
--- a/firmware/target/arm/iriver/h10/power-h10.c
+++ b/firmware/target/arm/iriver/h10/power-h10.c
@@ -40,12 +40,12 @@
 
 static bool powered = false;
 
-bool radio_powered()
+bool tuner_powered()
 {
     return powered;
 }
 
-bool radio_power(bool status)
+bool tuner_power(bool status)
 {
     bool old_status = powered;
     powered = status;
diff --git a/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c b/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c
index 4e4f585..dfdcb1d 100644
--- a/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c
+++ b/firmware/target/arm/pnx0101/iriver-ifp7xx/power-ifp7xx.c
@@ -30,12 +30,12 @@
 
 static bool powered = false;
 
-bool radio_powered(void)
+bool tuner_powered(void)
 {
     return powered;
 }
 
-bool radio_power(bool status)
+bool tuner_power(bool status)
 {
     bool old_status = powered;
     powered = status;
diff --git a/firmware/target/arm/sandisk/sansa-e200/power-e200.c b/firmware/target/arm/sandisk/sansa-e200/power-e200.c
index dfa4211..002dcb8 100644
--- a/firmware/target/arm/sandisk/sansa-e200/power-e200.c
+++ b/firmware/target/arm/sandisk/sansa-e200/power-e200.c
@@ -21,6 +21,7 @@
 #include "system.h"
 #include "cpu.h"
 #include "i2c-pp.h"
+#include "tuner.h"
 
 void power_init(void)
 {
@@ -61,3 +62,60 @@
 {
     (void)on;
 }
+
+/** Tuner **/
+static bool powered = false;
+
+bool tuner_power(bool status)
+{
+    bool old_status = powered;
+
+    if (status != old_status)
+    {
+        if (status)
+        {
+            /* init mystery amplification device */
+            outl(inl(0x70000084) | 0x1, 0x70000084);
+            udelay(5);
+
+            /* When power up, host should initialize the 3-wire bus
+               in host read mode: */
+
+            /* 1. Set direction of the DATA-line to input-mode. */
+            GPIOH_OUTPUT_EN &= ~(1 << 5); 
+            GPIOH_ENABLE |= (1 << 5); 
+
+            /* 2. Drive NR_W low */
+            GPIOH_OUTPUT_VAL &= ~(1 << 3); 
+            GPIOH_OUTPUT_EN |= (1 << 3); 
+            GPIOH_ENABLE |= (1 << 3); 
+
+            /* 3. Drive CLOCK high */
+            GPIOH_OUTPUT_VAL |= (1 << 4); 
+            GPIOH_OUTPUT_EN |= (1 << 4); 
+            GPIOH_ENABLE |= (1 << 4);
+
+            lv24020lp_power(true);
+        }
+        else
+        {
+            lv24020lp_power(false);
+
+            /* set all as inputs */
+            GPIOH_OUTPUT_EN &= ~((1 << 5) | (1 << 3) | (1 << 4));
+            GPIOH_ENABLE &= ~((1 << 5) | (1 << 3) | (1 << 4)); 
+
+            /* turn off mystery amplification device */
+            outl(inl(0x70000084) & ~0x1, 0x70000084);
+        }
+
+        powered = status;
+    }
+
+    return old_status;
+}
+
+bool tuner_powered(void)
+{
+    return powered;
+}
diff --git a/firmware/target/coldfire/iaudio/x5/power-x5.c b/firmware/target/coldfire/iaudio/x5/power-x5.c
index c646570..5c6c388 100644
--- a/firmware/target/coldfire/iaudio/x5/power-x5.c
+++ b/firmware/target/coldfire/iaudio/x5/power-x5.c
@@ -90,12 +90,12 @@
 
 static bool powered = false;
 
-bool radio_powered()
+bool tuner_powered()
 {
     return powered;
 }
 
-bool radio_power(bool status)
+bool tuner_power(bool status)
 {
     bool old_status = powered;
     powered = status;
diff --git a/firmware/target/coldfire/iriver/h100/power-h100.c b/firmware/target/coldfire/iriver/h100/power-h100.c
index 9431689..c184126 100644
--- a/firmware/target/coldfire/iriver/h100/power-h100.c
+++ b/firmware/target/coldfire/iriver/h100/power-h100.c
@@ -29,12 +29,12 @@
 
 static bool powered = false;
 
-bool radio_powered(void)
+bool tuner_powered(void)
 {
     return powered;
 }
 
-bool radio_power(bool status)
+bool tuner_power(bool status)
 {
     bool old_status = powered;
     powered = status;
diff --git a/firmware/target/coldfire/iriver/h300/power-h300.c b/firmware/target/coldfire/iriver/h300/power-h300.c
index 5e57326..b687b29 100644
--- a/firmware/target/coldfire/iriver/h300/power-h300.c
+++ b/firmware/target/coldfire/iriver/h300/power-h300.c
@@ -29,12 +29,12 @@
 
 static bool powered = false;
 
-bool radio_powered(void)
+bool tuner_powered(void)
 {
     return powered;
 }
 
-bool radio_power(bool status)
+bool tuner_power(bool status)
 {
     bool old_status = powered;
     powered = status;
diff --git a/uisimulator/common/fmradio.c b/uisimulator/common/fmradio.c
index 851c11b..372fd10 100644
--- a/uisimulator/common/fmradio.c
+++ b/uisimulator/common/fmradio.c
@@ -30,7 +30,7 @@
 static bool powered = false;
 #endif
 
-int radio_set(int setting, int value)
+int tuner_set(int setting, int value)
 {
     switch(setting)
     {
@@ -59,7 +59,7 @@
     return 1;
 }
 
-int radio_get(int setting)
+int tuner_get(int setting)
 {
     int val = 0;
     
@@ -86,7 +86,7 @@
 }
 
 #ifdef HAVE_TUNER_PWR_CTRL
-bool radio_power(bool status)
+bool tuner_power(bool status)
 {
     bool oldstatus = powered;
     powered = status;