summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/features.txt4
-rw-r--r--apps/lang/english.lang34
-rw-r--r--apps/main.c8
-rw-r--r--apps/menus/settings_menu.c6
-rw-r--r--apps/misc.c26
-rw-r--r--apps/settings.h7
-rw-r--r--apps/settings_list.c11
-rw-r--r--firmware/SOURCES7
-rw-r--r--firmware/export/config/ipod4g.h2
-rw-r--r--firmware/export/config/ipodcolor.h2
-rw-r--r--firmware/export/config/ipodmini1g.h2
-rw-r--r--firmware/export/config/ipodmini2g.h2
-rw-r--r--firmware/export/config/ipodnano1g.h2
-rw-r--r--firmware/export/config/ipodnano2g.h2
-rw-r--r--firmware/export/config/ipodvideo.h2
-rw-r--r--firmware/export/thread.h8
-rw-r--r--firmware/target/arm/ipod/piezo.c209
-rw-r--r--firmware/target/arm/ipod/piezo.h30
-rw-r--r--firmware/target/arm/s5l8700/ipodnano2g/piezo-nano2g.c95
-rw-r--r--firmware/target/arm/s5l8700/ipodnano2g/piezo.h24
20 files changed, 481 insertions, 2 deletions
diff --git a/apps/features.txt b/apps/features.txt
index 44ef3ddf95..35f7bb50e0 100644
--- a/apps/features.txt
+++ b/apps/features.txt
@@ -270,3 +270,7 @@ recording_digital
#if MEMORYSIZE <= 2
lowmem
#endif
+
+#if defined(HAVE_HARDWARE_CLICK)
+hardware_click
+#endif
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index de541af1f5..a228a78f7f 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12917,3 +12917,37 @@
*: "Shortcuts"
</voice>
</phrase>
+<phrase>
+ id: LANG_KEYCLICK_SOFTWARE
+ desc: in keyclick settings menu
+ user: core
+ <source>
+ *: none
+ hardware_click: "Headphone Keyclick"
+ </source>
+ <dest>
+ *: none
+ hardware_click: "Headphone Keyclick"
+ </dest>
+ <voice>
+ *: none
+ hardware_click: "Headphone Keyclick"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEYCLICK_HARDWARE
+ desc: in keyclick settings menu
+ user: core
+ <source>
+ *: none
+ hardware_click: "Speaker Keyclick"
+ </source>
+ <dest>
+ *: none
+ hardware_click: "Speaker Keyclick"
+ </dest>
+ <voice>
+ *: none
+ hardware_click: "Speaker Keyclick"
+ </voice>
+</phrase>
diff --git a/apps/main.c b/apps/main.c
index 38de780c8d..9d83c62965 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -118,6 +118,10 @@
#include "m5636.h"
#endif
+#ifdef HAVE_HARDWARE_CLICK
+#include "piezo.h"
+#endif
+
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
#define MAIN_NORETURN_ATTR NORETURN_ATTR
#else
@@ -507,6 +511,10 @@ static void init(void)
radio_init();
#endif
+#ifdef HAVE_HARDWARE_CLICK
+ piezo_init();
+#endif
+
/* Keep the order of this 3 (viewportmanager handles statusbars)
* Must be done before any code uses the multi-screen API */
CHART(">gui_syncstatusbar_init");
diff --git a/apps/menus/settings_menu.c b/apps/menus/settings_menu.c
index 02f68aa4ab..079e86616d 100644
--- a/apps/menus/settings_menu.c
+++ b/apps/menus/settings_menu.c
@@ -229,9 +229,15 @@ MAKE_MENU(limits_menu, ID2P(LANG_LIMITS_MENU), 0, Icon_NOICON,
#if CONFIG_CODEC == SWCODEC
MENUITEM_SETTING(keyclick, &global_settings.keyclick, NULL);
MENUITEM_SETTING(keyclick_repeats, &global_settings.keyclick_repeats, NULL);
+#ifdef HAVE_HARDWARE_CLICK
+MENUITEM_SETTING(keyclick_hardware, &global_settings.keyclick_hardware, NULL);
+MAKE_MENU(keyclick_menu, ID2P(LANG_KEYCLICK), 0, Icon_NOICON,
+ &keyclick, &keyclick_hardware, &keyclick_repeats);
+#else
MAKE_MENU(keyclick_menu, ID2P(LANG_KEYCLICK), 0, Icon_NOICON,
&keyclick, &keyclick_repeats);
#endif
+#endif
#if CONFIG_CODEC == MAS3507D
diff --git a/apps/misc.c b/apps/misc.c
index 0608595d21..04a6d05c91 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -94,6 +94,10 @@
#endif
#endif
+#ifdef HAVE_HARDWARE_CLICK
+#include "piezo.h"
+#endif
+
/* units used with output_dyn_value */
const unsigned char * const byte_units[] =
{
@@ -873,14 +877,32 @@ void system_sound_play(enum system_sound sound)
void keyclick_click(int button)
{
/* Settings filters */
- if (global_settings.keyclick &&
- (global_settings.keyclick_repeats || !(button & BUTTON_REPEAT)))
+ if (
+#ifdef HAVE_HARDWARE_CLICK
+ (global_settings.keyclick || global_settings.keyclick_hardware)
+#else
+ global_settings.keyclick
+#endif
+ && (global_settings.keyclick_repeats || !(button & BUTTON_REPEAT)))
{
/* Button filters */
if (button != BUTTON_NONE && !(button & BUTTON_REL)
&& !(button & (SYS_EVENT|BUTTON_MULTIMEDIA)) )
{
+#ifdef HAVE_HARDWARE_CLICK
+ if (global_settings.keyclick)
+ {
+ system_sound_play(SOUND_KEYCLICK);
+ }
+ if (global_settings.keyclick_hardware)
+ {
+#if !defined(SIMULATOR)
+ piezo_button_beep(false, false);
+#endif
+ }
+#else
system_sound_play(SOUND_KEYCLICK);
+#endif
}
}
}
diff --git a/apps/settings.h b/apps/settings.h
index 92ffaf9019..06eba76e3c 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -835,6 +835,13 @@ struct user_settings
#endif
} hw_eq_bands[AUDIOHW_EQ_BAND_NUM];
#endif /* AUDIOHW_HAVE_EQ */
+
+#ifdef HAVE_HARDWARE_CLICK
+#if CONFIG_CODEC == SWCODEC
+ bool keyclick_hardware; /* hardware piezo keyclick */
+#endif
+#endif
+
char start_directory[MAX_PATHNAME+1];
};
diff --git a/apps/settings_list.c b/apps/settings_list.c
index e739f406cb..9232811ada 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -1757,12 +1757,23 @@ const struct settings_list settings[] = {
#endif /* HAVE_WHEEL_ACCELERATION */
#if CONFIG_CODEC == SWCODEC
/* keyclick */
+#ifdef HAVE_HARDWARE_CLICK
+ CHOICE_SETTING(0, keyclick, LANG_KEYCLICK_SOFTWARE, 0,
+ "keyclick", "off,weak,moderate,strong", NULL, 4,
+ ID2P(LANG_OFF), ID2P(LANG_WEAK), ID2P(LANG_MODERATE),
+ ID2P(LANG_STRONG)),
+ OFFON_SETTING(0, keyclick_repeats, LANG_KEYCLICK_REPEATS, false,
+ "keyclick repeats", NULL),
+ OFFON_SETTING(0, keyclick_hardware, LANG_KEYCLICK_HARDWARE, false,
+ "hardware keyclick", NULL),
+#else
CHOICE_SETTING(0, keyclick, LANG_KEYCLICK, 0,
"keyclick", "off,weak,moderate,strong", NULL, 4,
ID2P(LANG_OFF), ID2P(LANG_WEAK), ID2P(LANG_MODERATE),
ID2P(LANG_STRONG)),
OFFON_SETTING(0, keyclick_repeats, LANG_KEYCLICK_REPEATS, false,
"keyclick repeats", NULL),
+#endif
#endif /* CONFIG_CODEC == SWCODEC */
TEXT_SETTING(0, playlist_catalog_dir, "playlist catalog directory",
PLAYLIST_CATALOG_DEFAULT_DIR, NULL, NULL),
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 0f6c21d673..6a84b5fc11 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1160,6 +1160,7 @@ target/arm/ata-pp5020.c
target/arm/ipod/adc-ipod-pcf.c
target/arm/ipod/backlight-4g_color.c
target/arm/ipod/button-clickwheel.c
+target/arm/ipod/piezo.c
target/arm/ipod/lcd-as-gray.S
target/arm/ipod/lcd-gray.c
target/arm/ipod/power-ipod.c
@@ -1178,6 +1179,7 @@ target/arm/i2s-pp.c
target/arm/ipod/adc-ipod-pcf.c
target/arm/ipod/backlight-4g_color.c
target/arm/ipod/button-clickwheel.c
+target/arm/ipod/piezo.c
target/arm/ipod/lcd-color_nano.c
target/arm/ipod/lcd-as-color-nano.S
target/arm/ipod/power-ipod.c
@@ -1196,6 +1198,7 @@ target/arm/i2s-pp.c
target/arm/ipod/adc-ipod-pcf.c
target/arm/ipod/backlight-nano_video.c
target/arm/ipod/button-clickwheel.c
+target/arm/ipod/piezo.c
target/arm/ipod/lcd-color_nano.c
target/arm/ipod/lcd-as-color-nano.S
target/arm/ipod/power-ipod.c
@@ -1214,6 +1217,7 @@ target/arm/i2s-pp.c
target/arm/ipod/adc-ipod-pcf.c
target/arm/ipod/backlight-nano_video.c
target/arm/ipod/button-clickwheel.c
+target/arm/ipod/piezo.c
target/arm/ipod/power-ipod.c
target/arm/ipod/powermgmt-ipod-pcf.c
target/arm/ipod/video/lcd-as-video.S
@@ -1268,6 +1272,7 @@ target/arm/i2s-pp.c
target/arm/ipod/adc-ipod-pcf.c
target/arm/ipod/backlight-mini1g_mini2g.c
target/arm/ipod/button-mini1g.c
+target/arm/ipod/piezo.c
target/arm/ipod/lcd-as-gray.S
target/arm/ipod/lcd-gray.c
target/arm/ipod/power-ipod.c
@@ -1286,6 +1291,7 @@ target/arm/i2s-pp.c
target/arm/ipod/adc-ipod-pcf.c
target/arm/ipod/backlight-mini1g_mini2g.c
target/arm/ipod/button-clickwheel.c
+target/arm/ipod/piezo.c
target/arm/ipod/lcd-as-gray.S
target/arm/ipod/lcd-gray.c
target/arm/ipod/power-ipod.c
@@ -1639,6 +1645,7 @@ target/arm/s5l8700/pcm-s5l8700.c
target/arm/s5l8700/wmcodec-s5l8700.c
target/arm/s5l8700/ipodnano2g/audio-nano2g.c
target/arm/s5l8700/ipodnano2g/adc-nano2g.c
+target/arm/s5l8700/ipodnano2g/piezo-nano2g.c
#endif
#endif
#endif
diff --git a/firmware/export/config/ipod4g.h b/firmware/export/config/ipod4g.h
index 165f5967b4..957e3a4da5 100644
--- a/firmware/export/config/ipod4g.h
+++ b/firmware/export/config/ipod4g.h
@@ -212,6 +212,8 @@
/* Define this if you can read an absolute wheel position */
#define HAVE_WHEEL_POSITION
+#define HAVE_HARDWARE_CLICK
+
#define BOOTFILE_EXT "ipod"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/ipodcolor.h b/firmware/export/config/ipodcolor.h
index 645e20041c..cf021019b4 100644
--- a/firmware/export/config/ipodcolor.h
+++ b/firmware/export/config/ipodcolor.h
@@ -199,6 +199,8 @@
/* Define this if you can read an absolute wheel position */
#define HAVE_WHEEL_POSITION
+#define HAVE_HARDWARE_CLICK
+
#define BOOTFILE_EXT "ipod"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/ipodmini1g.h b/firmware/export/config/ipodmini1g.h
index 777dc6f9a2..04fa68e500 100644
--- a/firmware/export/config/ipodmini1g.h
+++ b/firmware/export/config/ipodmini1g.h
@@ -202,6 +202,8 @@
/* Define this if you have adjustable CPU frequency */
#define HAVE_ADJUSTABLE_CPU_FREQ
+#define HAVE_HARDWARE_CLICK
+
#define BOOTFILE_EXT "ipod"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/ipodmini2g.h b/firmware/export/config/ipodmini2g.h
index c401170459..c10284e7a4 100644
--- a/firmware/export/config/ipodmini2g.h
+++ b/firmware/export/config/ipodmini2g.h
@@ -205,6 +205,8 @@
/* Define this if you can read an absolute wheel position */
#define HAVE_WHEEL_POSITION
+#define HAVE_HARDWARE_CLICK
+
#define BOOTFILE_EXT "ipod"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/ipodnano1g.h b/firmware/export/config/ipodnano1g.h
index 388ba34447..50f0511384 100644
--- a/firmware/export/config/ipodnano1g.h
+++ b/firmware/export/config/ipodnano1g.h
@@ -199,6 +199,8 @@
/* Define this if you can read an absolute wheel position */
#define HAVE_WHEEL_POSITION
+#define HAVE_HARDWARE_CLICK
+
#define BOOTFILE_EXT "ipod"
#define BOOTFILE "rockbox." BOOTFILE_EXT
#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/config/ipodnano2g.h b/firmware/export/config/ipodnano2g.h
index 37a21b33ba..96cec799e4 100644
--- a/firmware/export/config/ipodnano2g.h
+++ b/firmware/export/config/ipodnano2g.h
@@ -191,6 +191,8 @@
/* Define this if you can read an absolute wheel position */
#define HAVE_WHEEL_POSITION
+#define HAVE_HARDWARE_CLICK
+
/* Define this if you have adjustable CPU frequency */
#define HAVE_ADJUSTABLE_CPU_FREQ
diff --git a/firmware/export/config/ipodvideo.h b/firmware/export/config/ipodvideo.h
index 1dd7d48e30..c4e2cb2986 100644
--- a/firmware/export/config/ipodvideo.h
+++ b/firmware/export/config/ipodvideo.h
@@ -218,6 +218,8 @@
/* Define this if you can read an absolute wheel position */
#define HAVE_WHEEL_POSITION
+#define HAVE_HARDWARE_CLICK
+
/* define this if the device has larger sectors when accessed via USB */
/* (only relevant in disk.c, fat.c now always supports large virtual sectors) */
#define MAX_LOG_SECTOR_SIZE 2048
diff --git a/firmware/export/thread.h b/firmware/export/thread.h
index d0f61f9d85..da06557f9c 100644
--- a/firmware/export/thread.h
+++ b/firmware/export/thread.h
@@ -65,10 +65,18 @@
#if CONFIG_CODEC == SWCODEC
#ifdef HAVE_RECORDING
+#ifdef HAVE_HARDWARE_CLICK
+#define BASETHREADS 18
+#else
+#define BASETHREADS 17
+#endif
+#else
+#ifdef HAVE_HARDWARE_CLICK
#define BASETHREADS 17
#else
#define BASETHREADS 16
#endif
+#endif
#else
#define BASETHREADS 11
diff --git a/firmware/target/arm/ipod/piezo.c b/firmware/target/arm/ipod/piezo.c
new file mode 100644
index 0000000000..2c3968e79a
--- /dev/null
+++ b/firmware/target/arm/ipod/piezo.c
@@ -0,0 +1,209 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Robert Keevil
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#include "thread.h"
+#include "system.h"
+#include "kernel.h"
+#include "usb.h"
+#include "logf.h"
+#include "piezo.h"
+
+static long piezo_stack[DEFAULT_STACK_SIZE/sizeof(long)];
+static const char piezo_thread_name[] = "piezo";
+static struct event_queue piezo_queue;
+static unsigned int duration;
+static bool beeping;
+
+enum {
+ Q_PIEZO_BEEP = 1,
+ Q_PIEZO_BEEP_FOR_TICK,
+ Q_PIEZO_BEEP_FOR_USEC,
+ Q_PIEZO_STOP
+};
+
+static inline void piezo_hw_init(void)
+{
+#ifndef SIMULATOR
+ /*logf("PIEZO: hw_init");*/
+ outl(inl(0x70000010) & ~0xc, 0x70000010);
+ outl(inl(0x6000600c) | 0x20000, 0x6000600c); /* enable device */
+#endif
+}
+
+static void piezo_hw_tick(unsigned int form_and_period)
+{
+#ifndef SIMULATOR
+ outl(0x80000000 | form_and_period, 0x7000a000); /* set pitch */
+#endif
+}
+
+static inline void piezo_hw_stop(void)
+{
+#ifndef SIMULATOR
+ outl(0x0, 0x7000a000); /* piezo off */
+#endif
+}
+
+static void piezo_thread(void)
+{
+ struct queue_event ev;
+ long piezo_usec_off;
+
+ while(1)
+ {
+ queue_wait(&piezo_queue, &ev);
+ switch(ev.id)
+ {
+ case Q_PIEZO_BEEP:
+ piezo_hw_tick((unsigned int)ev.data);
+ beeping = true;
+ break;
+ case Q_PIEZO_BEEP_FOR_TICK:
+ piezo_hw_tick((unsigned int)ev.data);
+ beeping = true;
+ sleep(duration);
+ if (beeping)
+ piezo_hw_stop();
+ beeping = false;
+ /* remove anything that appeared while sleeping */
+ queue_clear(&piezo_queue);
+ break;
+ case Q_PIEZO_BEEP_FOR_USEC:
+ piezo_usec_off = USEC_TIMER + duration;
+ piezo_hw_tick((unsigned int)ev.data);
+ beeping = true;
+ while (TIME_BEFORE(USEC_TIMER, piezo_usec_off))
+ if (duration >= 5000) yield();
+ if (beeping)
+ piezo_hw_stop();
+ beeping = false;
+ /* remove anything that appeared while sleeping */
+ queue_clear(&piezo_queue);
+ break;
+ case Q_PIEZO_STOP:
+ if (beeping)
+ piezo_hw_stop();
+ beeping = false;
+ break;
+#ifndef SIMULATOR
+ case SYS_USB_CONNECTED:
+ /*logf("USB: Piezo core");*/
+ piezo_hw_stop();
+ queue_clear(&piezo_queue);
+ usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ usb_wait_for_disconnect(&piezo_queue);
+ break ;
+#endif
+ case SYS_TIMEOUT:
+ break;
+ }
+ yield();
+ }
+}
+
+
+void piezo_play(unsigned short inv_freq, unsigned char form)
+{
+ queue_post(&piezo_queue, Q_PIEZO_BEEP,
+ (intptr_t)((unsigned int)form << 16 | inv_freq));
+}
+
+void piezo_play_for_tick(unsigned short inv_freq,
+ unsigned char form, unsigned int dur)
+{
+ duration = dur;
+ queue_post(&piezo_queue, Q_PIEZO_BEEP_FOR_TICK,
+ (intptr_t)((unsigned int)form << 16 | inv_freq));
+}
+
+void piezo_play_for_usec(unsigned short inv_freq,
+ unsigned char form, unsigned int dur)
+{
+ duration = dur;
+ queue_post(&piezo_queue, Q_PIEZO_BEEP_FOR_USEC,
+ (intptr_t)((unsigned int)form << 16 | inv_freq));
+}
+
+void piezo_stop(void)
+{
+ queue_post(&piezo_queue, Q_PIEZO_STOP, 0);
+}
+
+void piezo_clear(void)
+{
+ queue_clear(&piezo_queue);
+ piezo_stop();
+}
+
+bool piezo_busy(void)
+{
+ return !queue_empty(&piezo_queue);
+}
+
+/* conversion factor based on the following data
+
+ period Hz
+ 10 8547
+ 20 4465
+ 30 3024
+ 40 2286
+ 50 1846
+ 60 1537
+ 70 1320
+ 80 1165
+ 90 1030
+ 100 928
+
+ someone with better recording/analysing equipment should be able
+ to get more accurate figures
+*/
+unsigned int piezo_hz(unsigned int hz)
+{
+ if (hz > 0)
+ return 91225/hz;
+ else
+ return 0;
+}
+
+void piezo_init(void)
+{
+ /*logf("PIEZO: init");*/
+ piezo_hw_init();
+ queue_init(&piezo_queue, true);
+ create_thread(piezo_thread, piezo_stack, sizeof(piezo_stack), 0,
+ piezo_thread_name IF_PRIO(, PRIORITY_REALTIME)
+ IF_COP(, CPU));
+}
+
+void piezo_button_beep(bool beep, bool force)
+{
+ /* old on clickwheel action - piezo_play_for_usec(50, 0x80, 400);
+ old on button action - piezo_play_for_usec(50, 0x80, 3000); */
+
+ if (force)
+ piezo_clear();
+
+ if (queue_empty(&piezo_queue))
+ {
+ if (beep)
+ piezo_play_for_tick(40, 0x80, HZ/5);
+ else
+ piezo_play_for_usec(91, 0x80, 4000);
+ }
+}
diff --git a/firmware/target/arm/ipod/piezo.h b/firmware/target/arm/ipod/piezo.h
new file mode 100644
index 0000000000..78b50d0d39
--- /dev/null
+++ b/firmware/target/arm/ipod/piezo.h
@@ -0,0 +1,30 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Robert Keevil
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+void piezo_init(void);
+void piezo_play(unsigned short inv_freq, unsigned char form);
+void piezo_play_for_tick(unsigned short inv_freq,
+ unsigned char form, unsigned int dur);
+void piezo_play_for_usec(unsigned short inv_freq,
+ unsigned char form, unsigned int dur);
+void piezo_stop(void);
+void piezo_clear(void);
+bool piezo_busy(void);
+unsigned int piezo_hz(unsigned int hz);
+void piezo_button_beep(bool beep, bool force);
diff --git a/firmware/target/arm/s5l8700/ipodnano2g/piezo-nano2g.c b/firmware/target/arm/s5l8700/ipodnano2g/piezo-nano2g.c
new file mode 100644
index 0000000000..d555b7232f
--- /dev/null
+++ b/firmware/target/arm/s5l8700/ipodnano2g/piezo-nano2g.c
@@ -0,0 +1,95 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Robert Keevil
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "kernel.h"
+#include "piezo.h"
+
+static unsigned int duration;
+static bool beeping;
+
+void INT_TIMERD(void)
+{
+ /* clear interrupt */
+ TDCON = TDCON;
+ if (!(--duration))
+ {
+ beeping = 0;
+ TDCMD = (1 << 1); /* TD_CLR */
+ }
+}
+
+void piezo_start(unsigned short cycles, unsigned short periods)
+{
+#ifndef SIMULATOR
+ duration = periods;
+ beeping = 1;
+ /* configure timer for 100 kHz */
+ TDCMD = (1 << 1); /* TD_CLR */
+ TDPRE = 30 - 1; /* prescaler */
+ TDCON = (1 << 13) | /* TD_INT1_EN */
+ (0 << 12) | /* TD_INT0_EN */
+ (0 << 11) | /* TD_START */
+ (2 << 8) | /* TD_CS = PCLK / 16 */
+ (1 << 4); /* TD_MODE_SEL = PWM mode */
+ TDDATA0 = cycles; /* set interval period */
+ TDDATA1 = cycles << 1; /* set interval period */
+ TDCMD = (1 << 0); /* TD_EN */
+
+ /* enable timer interrupt */
+ INTMSK |= INTMSK_TIMERD;
+#endif
+}
+
+void piezo_stop(void)
+{
+#ifndef SIMULATOR
+ TDCMD = (1 << 1); /* TD_CLR */
+#endif
+}
+
+void piezo_clear(void)
+{
+ piezo_stop();
+}
+
+bool piezo_busy(void)
+{
+ return beeping;
+}
+
+void piezo_init(void)
+{
+ beeping = 0;
+}
+
+void piezo_button_beep(bool beep, bool force)
+{
+ if (force)
+ while (beeping)
+ yield();
+
+ if (!beeping)
+ {
+ if (beep)
+ piezo_start(22, 457);
+ else
+ piezo_start(40, 4);
+ }
+}
diff --git a/firmware/target/arm/s5l8700/ipodnano2g/piezo.h b/firmware/target/arm/s5l8700/ipodnano2g/piezo.h
new file mode 100644
index 0000000000..b8eae0943d
--- /dev/null
+++ b/firmware/target/arm/s5l8700/ipodnano2g/piezo.h
@@ -0,0 +1,24 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006-2007 Robert Keevil
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+void piezo_init(void);
+void piezo_stop(void);
+void piezo_clear(void);
+bool piezo_busy(void);
+void piezo_button_beep(bool beep, bool force);