Index: apps/pcmbuf.c
===================================================================
--- apps/pcmbuf.c	(revision 12879)
+++ apps/pcmbuf.c	(working copy)
@@ -37,6 +37,15 @@
 #include "dsp.h"
 #include "thread.h"
 
+#if NUM_CORES > 1
+enum { Q_COPHELPER_PCM_STOP=0, Q_COPHELPER_PCM_START, Q_COPHELPER_PCM_PAUSE };
+
+static struct event_queue       cophelper_queue NOCACHEBSS_ATTR;
+static struct queue_sender_list cophelper_queue_sender_list NOCACHEBSS_ATTR;
+static long cophelper_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
+static const char cophelper_thread_name[] = "cophelper";
+#endif
+
 /* Define PCMBUF_MUTING if the codec requires muting to prevent pops
  * Currently assumes anything other than tlv320 and uda1380 require it
  */
@@ -92,7 +101,7 @@
 static size_t crossfade_fade_in_total IDATA_ATTR;
 static size_t crossfade_fade_in_rem IDATA_ATTR;
 
-static size_t pcmbuf_descsize;
+static size_t pcmbuf_descsize NOCACHEBSS_ATTR;
 static struct pcmbufdesc *pcmbuf_read IDATA_ATTR;
 static struct pcmbufdesc *pcmbuf_read_end IDATA_ATTR;
 static struct pcmbufdesc *pcmbuf_write IDATA_ATTR;
@@ -105,11 +114,11 @@
 static struct pcmbufdesc *pcmbuf_mix_chunk IDATA_ATTR;
 static size_t pcmbuf_mix_sample IDATA_ATTR;
 
-static bool low_latency_mode = false;
-static bool pcmbuf_flush;
+static bool low_latency_mode NOCACHEDATA_ATTR = false;
+static bool pcmbuf_flush NOCACHEDATA_ATTR;
 
-#ifdef HAVE_PRIORITY_SCHEDULING
-static int codec_thread_priority = 0;
+#if defined(HAVE_PRIORITY_SCHEDULING) && (NUM_CORES == 1)
+static int codec_thread_priority NOCACHEDATA_ATTR = 0;
 #endif
 
 extern struct thread_entry *codec_thread_p;
@@ -247,7 +256,7 @@
     audiobuffer_fillpos = 0;
 }
 
-#ifdef HAVE_PRIORITY_SCHEDULING
+#if defined(HAVE_PRIORITY_SCHEDULING) && (NUM_CORES == 1)
 static void boost_codec_thread(bool boost)
 {
     if (boost)
@@ -272,7 +281,7 @@
     if (thread_get_current() == codec_thread_p)
 #endif /* SIMULATOR */
     {
-#ifdef HAVE_PRIORITY_SCHEDULING
+#if defined(HAVE_PRIORITY_SCHEDULING) && (NUM_CORES == 1)
         /* If buffer is critically low, override UI priority, else
            set back to the original priority. */
         boost_codec_thread(LOW_DATA(2) && pcm_is_playing());
@@ -363,6 +372,13 @@
 
 void pcmbuf_play_stop(void)
 {
+#if NUM_CORES > 1
+    queue_send(&cophelper_queue, Q_COPHELPER_PCM_STOP, 0);
+}
+
+static void _pcmbuf_play_stop(void)
+{
+#endif
     /** Prevent a very tiny pop from happening by muting audio
      *  until dma has been initialized. */
 #ifdef PCMBUF_MUTING
@@ -386,7 +402,7 @@
     crossfade_active = false;
     pcmbuf_flush = false;
 
-#ifdef HAVE_PRIORITY_SCHEDULING
+#if defined(HAVE_PRIORITY_SCHEDULING) && (NUM_CORES == 1)
     /* Can unboost the codec thread here no matter who's calling */
     boost_codec_thread(false);
 #endif
@@ -491,7 +507,15 @@
 }
 #endif
 
-void pcmbuf_pause(bool pause) {
+void pcmbuf_pause(bool pause)
+{
+#if NUM_CORES > 1
+    queue_send(&cophelper_queue, Q_COPHELPER_PCM_PAUSE, (intptr_t)pause);
+}
+
+static void _pcmbuf_pause(bool pause)
+{
+#endif
 #ifdef PCMBUF_MUTING
     if (pause)
        pcm_mute(true);
@@ -504,9 +528,16 @@
     trigger_cpu_boost();
 }
 
-/* Force playback. */
 void pcmbuf_play_start(void)
 {
+#if NUM_CORES > 1
+    queue_send(&cophelper_queue, Q_COPHELPER_PCM_START, 0);
+}
+
+/* Force playback. */
+static void _pcmbuf_play_start(void)
+{
+#endif
     if (!pcm_is_playing() && pcmbuf_unplayed_bytes)
     {
 #ifdef PCMBUF_MUTING
@@ -1103,3 +1134,45 @@
 
     return crossfade_enabled;
 }
+
+#if NUM_CORES > 1
+void cophelper_thread(void)
+{
+    struct event ev;
+    
+    while (1)
+    {
+        queue_wait(&cophelper_queue, &ev);
+        
+        logf("COPHELPER");
+        switch (ev.id)
+        {
+            case Q_COPHELPER_PCM_STOP:
+                _pcmbuf_play_stop();
+                break;
+            
+            case Q_COPHELPER_PCM_START:
+                _pcmbuf_play_start();
+                break;
+            
+            case Q_COPHELPER_PCM_PAUSE:
+                _pcmbuf_pause((bool)ev.data);
+                break;
+        }
+        
+        queue_reply(&cophelper_queue, 0);
+    }
+    
+}
+
+void pcmbuf_cophelper_init(void)
+{
+    queue_init(&cophelper_queue, false);
+    queue_enable_queue_send(&cophelper_queue, &cophelper_queue_sender_list);
+    
+    create_thread(cophelper_thread, cophelper_stack, sizeof(cophelper_stack),
+                  cophelper_thread_name IF_PRIO(, PRIORITY_PLAYBACK)
+          IF_COP(, CPU, false));
+}
+#endif
+
Index: apps/pcmbuf.h
===================================================================
--- apps/pcmbuf.h	(revision 12879)
+++ apps/pcmbuf.h	(working copy)
@@ -81,4 +81,9 @@
 int pcmbuf_used_descs(void);
 int pcmbuf_descs(void);
 
+#if NUM_CORES > 1
+void pcmbuf_cophelper_init(void);
+#define cophelper_trigger_boost() trigger_cpu_boost()
 #endif
+
+#endif
Index: apps/playback.c
===================================================================
--- apps/playback.c	(revision 12879)
+++ apps/playback.c	(working copy)
@@ -183,7 +183,7 @@
 #define IBSS_ATTR_VOICE_STACK IBSS_ATTR
 #endif
 
-bool audio_is_initialized = false;
+bool audio_is_initialized NOCACHEBSS_ATTR = false;
 
 /* Variables are commented with the threads that use them: *
  * A=audio, C=codec, V=voice. A suffix of - indicates that *
@@ -197,10 +197,10 @@
 static volatile bool filling IDATA_ATTR = false; /* Is file buffer refilling? (A/C-) */
 
 /* Ring buffer where compressed audio and codecs are loaded */
-static unsigned char *filebuf = NULL;       /* Start of buffer (A/C-) */
-static unsigned char *malloc_buf = NULL;    /* Start of malloc buffer (A/C-) */
+static unsigned char *filebuf NOCACHEBSS_ATTR  = NULL;       /* Start of buffer (A/C-) */
+static unsigned char *malloc_buf NOCACHEBSS_ATTR  = NULL;    /* Start of malloc buffer (A/C-) */
 /* FIXME: make filebuflen static */
-size_t filebuflen = 0;                      /* Size of buffer (A/C-) */
+size_t filebuflen NOCACHEBSS_ATTR  = 0;                      /* Size of buffer (A/C-) */
 /* FIXME: make buf_ridx (C/A-) */
 static volatile size_t buf_ridx IDATA_ATTR = 0; /* Buffer read position (A/C)*/
 static volatile size_t buf_widx IDATA_ATTR = 0; /* Buffer write position (A/C-) */
@@ -209,7 +209,7 @@
 #define BUFFER_STATE_TRASHED        -1          /* trashed; must be reset */
 #define BUFFER_STATE_INITIALIZED     0          /* voice+audio OR audio-only */
 #define BUFFER_STATE_VOICED_ONLY     1          /* voice-only */
-static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */
+static int buffer_state NOCACHEDATA_ATTR = BUFFER_STATE_TRASHED; /* Buffer state */
 
 /* Compressed ring buffer helper macros */
 /* Buffer pointer (p) plus value (v), wrapped if necessary */
@@ -224,49 +224,49 @@
 
 /* Track info structure about songs in the file buffer (A/C-) */
 static struct track_info tracks[MAX_TRACK];
-static volatile int track_ridx = 0;  /* Track being decoded (A/C-) */
-static int track_widx = 0;           /* Track being buffered (A) */
+static volatile int track_ridx NOCACHEBSS_ATTR = 0;  /* Track being decoded (A/C-) */
+static int track_widx NOCACHEBSS_ATTR = 0;           /* Track being buffered (A) */
 
-static struct track_info *prev_ti = NULL; /* Previous track info pointer (A/C-) */
+static struct track_info *prev_ti NOCACHEBSS_ATTR = NULL; /* Previous track info pointer (A/C-) */
 #define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */
 
 /* Set by the audio thread when the current track information has updated
  * and the WPS may need to update its cached information */
-static bool track_changed = false;
+static bool track_changed NOCACHEBSS_ATTR = false;
 
 /* Information used only for filling the buffer */
 /* Playlist steps from playing track to next track to be buffered (A) */
-static int last_peek_offset = 0;
+static int last_peek_offset NOCACHEBSS_ATTR = 0;
 /* Partially loaded track file handle to continue buffering (A) */
-static int current_fd = -1;
+static int current_fd NOCACHEDATA_ATTR = -1;
 
 /* Scrobbler support */
-static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/
+static unsigned long prev_track_elapsed NOCACHEBSS_ATTR = 0; /* Previous track elapsed time (C/A-)*/
 
 /* Track change controls */
-static bool automatic_skip = false; /* Who initiated in-progress skip? (C/A-) */
-static bool playlist_end = false;   /* Has the current playlist ended? (A) */
-static bool dir_skip = false;       /* Is a directory skip pending? (A) */
-static bool new_playlist = false;   /* Are we starting a new playlist? (A) */
+static bool automatic_skip NOCACHEBSS_ATTR = false; /* Who initiated in-progress skip? (C/A-) */
+static bool playlist_end NOCACHEBSS_ATTR = false;   /* Has the current playlist ended? (A) */
+static bool dir_skip NOCACHEBSS_ATTR = false;       /* Is a directory skip pending? (A) */
+static bool new_playlist NOCACHEBSS_ATTR = false;   /* Are we starting a new playlist? (A) */
 /* Pending track change offset, to keep WPS responsive (A) */
-static int wps_offset = 0;
+static int wps_offset NOCACHEBSS_ATTR = 0;
 
 /* Callbacks which applications or plugins may set */
 /* When the playing track has changed from the user's perspective */
-void (*track_changed_callback)(struct mp3entry *id3) = NULL;
+void (*track_changed_callback)(struct mp3entry *id3) NOCACHEBSS_ATTR = NULL;
 /* When a track has been buffered */
-void (*track_buffer_callback)(struct mp3entry *id3, bool last_track) = NULL;
+void (*track_buffer_callback)(struct mp3entry *id3, bool last_track) NOCACHEBSS_ATTR = NULL;
 /* When a track's buffer has been overwritten or cleared */
-void (*track_unbuffer_callback)(struct mp3entry *id3, bool last_track) = NULL;
+void (*track_unbuffer_callback)(struct mp3entry *id3, bool last_track) NOCACHEBSS_ATTR = NULL;
 
 /* Configuration */
-static size_t conf_watermark = 0; /* Level to trigger filebuf fill (A/C) FIXME */
-static size_t conf_filechunk = 0; /* Largest chunk the codec accepts (A/C) FIXME */
-static size_t conf_preseek   = 0; /* Codec pre-seek margin (A/C) FIXME */
-static size_t buffer_margin  = 0; /* Buffer margin aka anti-skip buffer (A/C-) */
-static bool v1first = false;      /* ID3 data control, true if V1 then V2 (A) */
+static size_t conf_watermark NOCACHEBSS_ATTR = 0; /* Level to trigger filebuf fill (A/C) FIXME */
+static size_t conf_filechunk NOCACHEBSS_ATTR = 0; /* Largest chunk the codec accepts (A/C) FIXME */
+static size_t conf_preseek NOCACHEBSS_ATTR   = 0; /* Codec pre-seek margin (A/C) FIXME */
+static size_t buffer_margin NOCACHEBSS_ATTR  = 0; /* Buffer margin aka anti-skip buffer (A/C-) */
+static bool v1first NOCACHEBSS_ATTR = false;      /* ID3 data control, true if V1 then V2 (A) */
 #if MEM > 8
-static size_t high_watermark = 0; /* High watermark for rebuffer (A/V/other) */
+static size_t high_watermark NOCACHEBSS_ATTR = 0; /* High watermark for rebuffer (A/V/other) */
 #endif
 
 /* Multiple threads */
@@ -276,8 +276,8 @@
 static void set_filebuf_watermark(int seconds);
 
 /* Audio thread */
-static struct event_queue       audio_queue;
-static struct queue_sender_list audio_queue_sender_list;
+static struct event_queue       audio_queue NOCACHEBSS_ATTR;
+static struct queue_sender_list audio_queue_sender_list NOCACHEBSS_ATTR;
 static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
 static const char audio_thread_name[] = "audio";
 
@@ -319,30 +319,31 @@
 
 /* iram_buf and dram_buf are either both NULL or both non-NULL */
 /* Pointer to IRAM buffer for codec swapping */
-static unsigned char *iram_buf = NULL;
+static unsigned char *iram_buf NOCACHEBSS_ATTR = NULL;
 /* Pointer to DRAM buffer for codec swapping */
-static unsigned char *dram_buf = NULL;
+static unsigned char *dram_buf NOCACHEBSS_ATTR = NULL;
 /* Parity of swap_codec calls - needed because one codec swapping itself in
    automatically swaps in the other and the swap when unlocking should not
    happen if the parity is even.
  */
-static bool          swap_codec_parity = false; /* true=odd, false=even */
+static bool swap_codec_parity NOCACHEBSS_ATTR = false; /* true=odd, false=even */
+
 /* Mutex to control which codec (normal/voice) is running */
 static struct mutex mutex_codecthread NOCACHEBSS_ATTR; 
 
 /* Voice state */
-static volatile bool voice_thread_start = false; /* Triggers voice playback (A/V) */
+static volatile bool voice_thread_start NOCACHEBSS_ATTR = false; /* Triggers voice playback (A/V) */
 static volatile bool voice_is_playing NOCACHEBSS_ATTR = false; /* Is voice currently playing? (V) */
 static volatile bool voice_codec_loaded NOCACHEBSS_ATTR = false; /* Is voice codec loaded (V/A-) */
-static char *voicebuf = NULL;
-static size_t voice_remaining = 0;
+static char *voicebuf NOCACHEBSS_ATTR = NULL;
+static size_t voice_remaining NOCACHEBSS_ATTR = 0;
 
 #ifdef IRAM_STEAL
 /* Voice IRAM has been stolen for other use */
-static bool voice_iram_stolen = false;
+static bool voice_iram_stolen NOCACHEBSS_ATTR = false;
 #endif
 
-static void (*voice_getmore)(unsigned char** start, int* size) = NULL;
+static void (*voice_getmore)(unsigned char** start, int* size) NOCACHEBSS_ATTR = NULL;
 
 struct voice_info {
     void (*callback)(unsigned char **start, int *size);
@@ -394,7 +395,11 @@
 
 bool mp3_is_playing(void)
 {
+#ifdef PLAYBACK_VOICE
     return voice_is_playing;
+#else
+    return false;
+#endif
 }
 
 bool mp3_pause_done(void)
@@ -559,7 +564,7 @@
     queue_post(&codec_queue, Q_ENCODER_LOAD_DISK, (intptr_t)enc_fn);
 
     while (ci.enc_codec_loaded == 0)
-        yield();
+        sleep(1);
 
     logf("codec loaded: %d", ci.enc_codec_loaded);
 
@@ -579,7 +584,7 @@
 
     ci.stop_encoder = true;
     while (ci.enc_codec_loaded > 0)
-        yield();
+        sleep(1);
 #endif
 } /* audio_remove_encoder */
 
@@ -651,6 +656,8 @@
 
 void audio_play(long offset)
 {
+    long start_tick = current_tick;
+    
     logf("audio_play");
 
 #ifdef PLAYBACK_VOICE
@@ -675,8 +682,8 @@
     }
 
     /* Don't return until playback has actually started */
-    while (!playing)
-        yield();
+    while (!playing && current_tick - start_tick < HZ*10)
+        sleep(1);
 }
 
 void audio_stop(void)
@@ -687,7 +694,7 @@
 
     /* Don't return until playback has actually stopped */
     while(playing || !queue_empty(&audio_queue))
-        yield();
+        sleep(1);
 }
 
 void audio_pause(void)
@@ -1990,7 +1997,7 @@
                     logf("Codec slot is empty!");
                     /* Wait for the pcm buffer to go empty */
                     while (pcm_is_playing())
-                        yield();
+                        sleep(1);
                     /* This must be set to prevent an infinite loop */
                     ci.stop_codec = true;
                     LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
@@ -3277,13 +3284,16 @@
 {
     ci.stop_codec = true;
     pcmbuf_pause(true);
+
     while (audio_codec_loaded)
-        yield();
+        sleep(1);
+    
     /* If the audio codec is not loaded any more, and the audio is still
      * playing, it is now and _only_ now safe to call this function from the
      * audio thread */
     if (pcm_is_playing())
         pcmbuf_play_stop();
+
     pcmbuf_pause(paused);
 }
 
@@ -3743,6 +3753,9 @@
     queue_init(&codec_queue, true);
 
     pcm_init();
+#if NUM_CORES > 1
+    pcmbuf_cophelper_init();
+#endif
 
 #ifdef ROCKBOX_HAS_LOGF
     audio_set_track_changed_event(audio_test_track_changed_event);
@@ -3804,7 +3817,7 @@
 
     create_thread(audio_thread, audio_stack, sizeof(audio_stack),
                   audio_thread_name IF_PRIO(, PRIORITY_BUFFERING)
-          IF_COP(, CPU, false));
+          IF_COP(, COP, false));
 
 #ifdef PLAYBACK_VOICE
     /* TODO: Change this around when various speech codecs can be used */
Index: apps/debug_menu.c
===================================================================
--- apps/debug_menu.c	(revision 12879)
+++ apps/debug_menu.c	(working copy)
@@ -145,7 +145,7 @@
                 status = thread_get_status(thread);
 
 # ifdef HAVE_PRIORITY_SCHEDULING
-                snprintf(buf, 32, "(%d) %c%c %d %s: %d%%", core,
+                snprintf(buf, 32, "(%d/%d) %c%c %d %s: %d%%", core, thread->core,
                          (status == STATE_RUNNING) ? '*' : ' ',
                          thread_status_char(status),
                          cores[CURRENT_CORE].threads[i].priority,
@@ -1962,6 +1962,8 @@
         lcd_puts(0, line++, buf);
         snprintf(buf, sizeof(buf), "Commit delayed: %s",
                  stat->commit_delayed ? "Yes" : "No");
+        snprintf(buf, sizeof(buf), "EC enabled: %s",
+                 stat->econ ? "Yes" : "No");
         lcd_puts(0, line++, buf);
 
         lcd_update();
Index: apps/main.c
===================================================================
--- apps/main.c	(revision 12879)
+++ apps/main.c	(working copy)
@@ -18,6 +18,7 @@
  ****************************************************************************/
 #include "config.h"
 
+#include "logf.h"
 #include "ata.h"
 #include "ata_idle_notify.h"
 #include "disk.h"
@@ -111,13 +112,118 @@
 
 static void init(void);
 
+#if 0
+#include "backlight-target.h"
+
+void debug_ports(void)
+{
+    uint32_t address = 0x6000d000;
+    uint32_t data = 0xaaaaaaaa;
+    char buf[40];
+    int mode = 0;
+    unsigned int nibble = 0;
+    unsigned int datanibble = 0;
+    
+    lcd_clear_display();
+    
+    while (1)
+    {
+        int button = button_get_w_tmo(HZ);
+        lcd_puts(0, 6, "                 ");
+        
+        switch (button)
+        {
+            case BUTTON_MENU:
+                mode = !mode;
+                break;
+            case BUTTON_PLAY:
+                data = ~data;
+                break;
+            case BUTTON_SELECT:
+            {
+                uint32_t *p = (uint32_t *)address;
+                lcd_puts(0, 6, "Data set");
+                lcd_update();
+                *p = data;
+                break;
+            }
+            
+            case BUTTON_LEFT:
+            {
+                nibble = (nibble + 1) % 8;
+                if (mode)
+                    datanibble = (data >> (nibble*4)) & 0x0f;
+                else
+                    datanibble = ((unsigned int)address >> (nibble*4)) & 0x0f;
+        
+                break;
+            }
+            case BUTTON_RIGHT:
+            {
+                nibble = (nibble - 1) % 8;
+                if (mode)
+                    datanibble = (data >> (nibble*4)) & 0x0f;
+                else
+                    datanibble = ((unsigned int)address >> (nibble*4)) & 0x0f;
+        
+                break;
+            }
+            
+            case BUTTON_SCROLL_FWD:
+                datanibble = (datanibble + 1) % 16;
+                break;
+            case BUTTON_SCROLL_BACK:
+                datanibble = (datanibble - 1) % 16;
+                break;
+        }
+        
+        if (mode)
+            data = (data & ~(0x0f << (nibble*4))) | (datanibble << (nibble*4));
+        else
+            address = (address & ~(0x0f << (nibble*4))) | (datanibble << (nibble*4));
+        
+        snprintf(buf, sizeof buf, "Address: 0x%08x", (unsigned int)address);
+        lcd_puts(0, 0, buf);
+        
+        snprintf(buf, sizeof buf, "Data   : 0x%08x", (unsigned int)data);
+        lcd_puts(0, 1, buf);
+        
+        snprintf(buf, sizeof buf, "Edit   : %s", mode ? "Value" : "Address");
+        lcd_puts(0, 2, buf);
+        
+        snprintf(buf, sizeof buf, "Nibble : %d", nibble);
+        lcd_puts(0, 3, buf);
+        lcd_update();
+    }
+}
+#endif
+
 #ifdef SIMULATOR
 void app_main(void)
 #else
 static void app_main(void)
 #endif
 {
+#ifdef HWRAND_DEBUG
+#endif
     init();
+    
+    //debug_ports();
+
+#ifdef HWRAND_DEBUG
+    lcd_clear_display();
+    while (1)
+    {
+        snprintf(buf, sizeof(buf), "p = 0x%08x", (unsigned int)p);
+        lcd_puts(0, 0, buf);
+        lcd_update();
+        backlight_on();
+        sleep(HZ);
+        *p = 0xaaaa;
+        p++;
+    }
+    outl(((0x100 | 1) << 7), 0x6000d000);
+#endif
     browse_root();
 }
 
@@ -546,6 +652,7 @@
 #ifdef CPU_PP
 void cop_main(void)
 {
+    logf("COP START");
 /* This is the entry point for the coprocessor
    Anyone not running an upgraded bootloader will never reach this point,
    so it should not be assumed that the coprocessor be usable even on
@@ -556,18 +663,25 @@
 
 #if CONFIG_CPU == PP5002
 /* 3G doesn't have Rolo or dual core support yet */
+    logf("COP 3G");
     while(1) {
         COP_CTL = PROC_SLEEP;
     }
 #else
     extern volatile unsigned char cpu_message;
 
+    logf("COP SYSINIT");
     system_init();
+    logf("COP KERNINIT");
     kernel_init();
 
+    logf("COP RUNNING");
+    
     while(cpu_message != COP_REBOOT) {
-        sleep(HZ);
+        logf("COP MAIN");
+        sleep(HZ*10);
     }
+    logf("COP RESTART");
     rolo_restart_cop();
 #endif /* PP5002 */
 }
Index: firmware/export/kernel.h
===================================================================
--- firmware/export/kernel.h	(revision 12879)
+++ firmware/export/kernel.h	(working copy)
@@ -53,6 +53,7 @@
 struct event
 {
     long     id;
+    long     tick; /* Can be used to determine when the event was sent. */
     intptr_t data;
 };
 
@@ -61,12 +62,14 @@
 {
     struct thread_entry *thread;
     intptr_t             retval;
+    bool active;
 };
 
 struct queue_sender_list
 {
     /* If non-NULL, there is a thread waiting for the corresponding event */
-    struct queue_sender *senders[QUEUE_LENGTH];
+    /* Must be statically allocated to put in non-cached ram. */
+    struct queue_sender senders[QUEUE_LENGTH];
     /* Send info for last message dequeued or NULL if replied or not sent */
     struct queue_sender *curr_sender;
 };
@@ -78,6 +81,9 @@
     struct thread_entry *thread;
     unsigned int read;
     unsigned int write;
+#if NUM_CORES > 1
+    bool irq_safe;
+#endif
 #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
     struct queue_sender_list *send;
 #endif
@@ -110,6 +116,11 @@
 int tick_remove_task(void (*f)(void));
 
 extern void queue_init(struct event_queue *q, bool register_queue);
+#if NUM_CORES > 1
+extern void queue_set_irq_safe(struct event_queue *q, bool state);
+#else
+#define queue_set_irq_safe(q,state)
+#endif
 extern void queue_delete(struct event_queue *q);
 extern void queue_wait(struct event_queue *q, struct event *ev);
 extern void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks);
Index: firmware/export/system.h
===================================================================
--- firmware/export/system.h	(revision 12879)
+++ firmware/export/system.h	(working copy)
@@ -59,7 +59,7 @@
 
 #if defined(HAVE_ADJUSTABLE_CPU_FREQ) \
         && defined(ROCKBOX_HAS_LOGF) 
-#define CPU_BOOST_LOGGING
+//#define CPU_BOOST_LOGGING
 #endif
 
 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
Index: firmware/export/thread.h
===================================================================
--- firmware/export/thread.h	(revision 12879)
+++ firmware/export/thread.h	(working copy)
@@ -20,6 +20,7 @@
 #define THREAD_H
 
 #include "config.h"
+#include "inttypes.h"
 #include <stdbool.h>
 
 /* Priority scheduling (when enabled with HAVE_PRIORITY_SCHEDULING) works
@@ -105,7 +106,8 @@
     unsigned short stack_size;
 #ifdef HAVE_PRIORITY_SCHEDULING
     unsigned short priority;
-    unsigned long priority_x;
+    unsigned short priority_x;
+    unsigned short core; /* To which core threads belongs to. */
     long last_run;
 #endif
     struct thread_entry *next, *prev;
@@ -115,6 +117,13 @@
     struct thread_entry threads[MAXTHREADS];
     struct thread_entry *running;
     struct thread_entry *sleeping;
+#ifdef HAVE_PRIORITY_SCHEDULING
+    long highest_priority;
+#endif
+#if NUM_CORES > 1
+    bool lock_issued;
+#endif
+    long last_tick;
 #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
     int switch_to_irq_level;
     #define STAY_IRQ_LEVEL -1
@@ -127,6 +136,52 @@
 #define IF_PRIO(empty, type)
 #endif
 
+/* PortalPlayer chips have 2 cores, therefore need atomic mutexes 
+ * Just use it for ARM, Coldfire and whatever else well...why not?
+ */
+
+/* Macros generate better code than an inline function is this case */
+#if defined (CPU_PP) || defined (CPU_ARM)
+#define test_and_set(x_, v_) \
+({ \
+    uint32_t old; \
+    asm volatile ( \
+        "swpb %[old], %[v], [%[x]] \r\n" \
+        : [old]"=r"(old) \
+        : [v]"r"((uint32_t)v_), [x]"r"((uint32_t *)x_) \
+    ); \
+    old; \
+    })
+#elif defined (CPU_COLDFIRE)
+#define test_and_set(x_, v_) \
+({ \
+    uint8_t old; \
+    asm volatile ( \
+        "bset.l %[v], (%[x]) \r\n" \
+        "sne.b  %[old]       \r\n" \
+        : [old]"=d,d"(old) \
+        : [v]"i,d"((uint32_t)v_), [x]"a,a"((uint32_t *)x_) \
+    ); \
+    old; \
+    })
+#else
+/* default for no asm version */
+#define test_and_set(x_, v_) \
+({ \
+    uint32_t old = *(uint32_t *)x_; \
+    *(uint32_t *)x_ = v_; \
+    old; \
+    })
+#endif
+
+#if NUM_CORES > 1
+inline void lock_cores(void);
+inline void unlock_cores(void);
+#else
+#define lock_cores(...)
+#define unlock_cores(...)
+#endif
+
 struct thread_entry*
     create_thread(void (*function)(void), void* stack, int stack_size,
                   const char *name IF_PRIO(, int priority)
Index: firmware/kernel.c
===================================================================
--- firmware/kernel.c	(revision 12879)
+++ firmware/kernel.c	(working copy)
@@ -32,8 +32,8 @@
 static void (*tick_funcs[MAX_NUM_TICK_TASKS])(void);
 
 /* This array holds all queues that are initiated. It is used for broadcast. */
-static struct event_queue *all_queues[32];
-static int num_queues;
+static struct event_queue *all_queues[32] NOCACHEBSS_ATTR;
+static int num_queues NOCACHEBSS_ATTR;
 
 void queue_wait(struct event_queue *q, struct event *ev) ICODE_ATTR;
 
@@ -101,25 +101,25 @@
                                unsigned int i)
 {
     int old_level = set_irq_level(HIGHEST_IRQ_LEVEL);
-    struct queue_sender **spp = &send->senders[i];
+    struct queue_sender *sp = &send->senders[i];
 
-    if(*spp)
+    if (sp->active)
     {
-        send->curr_sender = *spp;
-        *spp = NULL;
+        send->curr_sender = sp;
+        sp->active = false;
     }
-
+    
     set_irq_level(old_level);
 }
 
 /* Puts the specified return value in the waiting thread's return value
    and wakes the thread  - a sender should be confirmed to exist first */
-static void queue_release_sender(struct queue_sender **sender,
+static void queue_release_sender(struct queue_sender *sender,
                                  intptr_t retval)
 {
-    (*sender)->retval = retval;
-    wakeup_thread(&(*sender)->thread);
-    *sender = NULL;
+    sender->retval = retval;
+    wakeup_thread(&sender->thread);
+    sender->active = false;
 }
 
 /* Releases any waiting threads that are queued with queue_send -
@@ -131,11 +131,11 @@
         unsigned int i;
         for(i = q->read; i != q->write; i++)
         {
-            struct queue_sender **spp =
+            struct queue_sender *sp =
                 &q->send->senders[i & QUEUE_LENGTH_MASK];
-            if(*spp)
+            if (sp->active)
             {
-                queue_release_sender(spp, 0);
+                queue_release_sender(sp, 0);
             }
         }
     }
@@ -157,6 +157,9 @@
     q->read   = 0;
     q->write  = 0;
     q->thread = NULL;
+#if NUM_CORES > 1
+    q->irq_safe = false;
+#endif
 #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
     q->send   = NULL; /* No message sending by default */
 #endif
@@ -168,11 +171,28 @@
     }
 }
 
+#if NUM_CORES > 1
+/**
+ * If IRQ mode is enabled, some core-wise locking mechanisms are disabled
+ * causing accessing queue to be no longer thread safe from the other core. 
+ * However, that locking mechanism would also kill IRQ handlers.
+ * 
+ * @param q struct of an event_queue
+ * @param state enable/disable IRQ mode
+ * @default state disabled
+ */
+void queue_set_irq_safe(struct event_queue *q, bool state)
+{
+    q->irq_safe = state;
+}
+#endif
+
 void queue_delete(struct event_queue *q)
 {
     int i;
     bool found = false;
 
+    lock_cores();
     wakeup_thread(&q->thread);
     
     /* Find the queue to be deleted */
@@ -201,43 +221,53 @@
         
         num_queues--;
     }
+    
+    unlock_cores();
 }
 
 void queue_wait(struct event_queue *q, struct event *ev)
 {
     unsigned int rd;
 
-    if(q->read == q->write)
+    lock_cores();
+    
+    if (q->read == q->write)
     {
         block_thread(&q->thread);
-    }
+        lock_cores();
+    } 
 
     rd = q->read++ & QUEUE_LENGTH_MASK;
     *ev = q->events[rd];
 
 #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
-    if(q->send && q->send->senders[rd])
+    if(q->send && q->send->senders[rd].active)
     {
         /* Get data for a waiting thread if one */
         queue_fetch_sender(q->send, rd);
     }
 #endif
+    
+    unlock_cores();
 }
 
 void queue_wait_w_tmo(struct event_queue *q, struct event *ev, int ticks)
 {
-    if(q->read == q->write && ticks > 0)
+    lock_cores();
+    
+    if (q->read == q->write && ticks > 0)
     {
         block_thread_w_tmo(&q->thread, ticks);
+        lock_cores();
     }
 
-    if(q->read != q->write)
+    if (q->read != q->write)
     {
         unsigned int rd = q->read++ & QUEUE_LENGTH_MASK;
         *ev = q->events[rd];
 
 #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
-        if(q->send && q->send->senders[rd])
+        if(q->send && q->send->senders[rd].active)
         {
             /* Get data for a waiting thread if one */
             queue_fetch_sender(q->send, rd);
@@ -248,64 +278,84 @@
     {
         ev->id = SYS_TIMEOUT;
     }
+    
+    unlock_cores();
 }
 
 void queue_post(struct event_queue *q, long id, intptr_t data)
 {
     int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
-    unsigned int wr = q->write++ & QUEUE_LENGTH_MASK;
+    unsigned int wr;
+    
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        lock_cores();
+#endif
+    
+    wr = q->write++ & QUEUE_LENGTH_MASK;
 
     q->events[wr].id   = id;
     q->events[wr].data = data;
+    q->events[wr].tick = current_tick;
 
 #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
     if(q->send)
     {
-        struct queue_sender **spp = &q->send->senders[wr];
+        struct queue_sender *sp = &q->send->senders[wr];
 
-        if(*spp)
+        if (sp->active)
         {
             /* overflow protect - unblock any thread waiting at this index */
-            queue_release_sender(spp, 0);
+            queue_release_sender(sp, 0);
         }
     }
 #endif
 
     wakeup_thread(&q->thread);
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        unlock_cores();
+#endif
     set_irq_level(oldlevel);
+    
 }
 
 #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
 intptr_t queue_send(struct event_queue *q, long id, intptr_t data)
 {
     int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
-    unsigned int wr = q->write++ & QUEUE_LENGTH_MASK;
-
+    unsigned int wr;
+    
+    lock_cores();
+    
+    wr = q->write++ & QUEUE_LENGTH_MASK;
     q->events[wr].id   = id;
     q->events[wr].data = data;
-
+    q->events[wr].tick = current_tick;
+    
     if(q->send)
     {
-        struct queue_sender **spp = &q->send->senders[wr];
-        struct queue_sender sender;
+        struct queue_sender *sp = &q->send->senders[wr];
 
-        if(*spp)
+        if (sp->active)
         {
             /* overflow protect - unblock any thread waiting at this index */
-            queue_release_sender(spp, 0);
+            queue_release_sender(sp, 0);
         }
 
-        *spp = &sender;
-        sender.thread = NULL;
+        sp->thread = NULL;
+        sp->active = true;
 
         wakeup_thread(&q->thread);
-        set_irq_level_and_block_thread(&sender.thread, oldlevel);
-        return sender.retval;
+        set_irq_level_and_block_thread(&sp->thread, oldlevel);
+        return sp->retval;
     }
 
     /* Function as queue_post if sending is not enabled */
     wakeup_thread(&q->thread);
+    unlock_cores();
     set_irq_level(oldlevel);
+    
     return 0;
 }
 
@@ -320,22 +370,42 @@
 /* Replies with retval to any dequeued message sent with queue_send */
 void queue_reply(struct event_queue *q, intptr_t retval)
 {
+    lock_cores();
     if(q->send && q->send->curr_sender)
     {
-        queue_release_sender(&q->send->curr_sender, retval);
+        queue_release_sender(q->send->curr_sender, retval);
     }
+    unlock_cores();
 }
 #endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */
 
 bool queue_empty(const struct event_queue* q)
 {
-    return ( q->read == q->write );
+    bool is_empty;
+    
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        lock_cores();
+#endif
+    
+    is_empty = ( q->read == q->write );
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        unlock_cores();
+#endif
+    
+    return is_empty;
 }
 
 void queue_clear(struct event_queue* q)
 {
     int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
 
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        lock_cores();
+#endif
+    
 #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
     /* Release all thread waiting in the queue for a reply -
        dequeued sent message will be handled by owning thread */
@@ -344,6 +414,12 @@
 
     q->read = 0;
     q->write = 0;
+    
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        unlock_cores();
+#endif
+
     set_irq_level(oldlevel);
 }
 
@@ -351,6 +427,11 @@
 {
     int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
     
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        lock_cores();
+#endif
+    
     while(q->read != q->write)
     {
         unsigned int rd = q->read & QUEUE_LENGTH_MASK;
@@ -363,18 +444,23 @@
 #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME
         if(q->send)
         {
-            struct queue_sender **spp = &q->send->senders[rd];
+            struct queue_sender *sp = &q->send->senders[rd];
 
-            if(*spp)
+            if (sp->active)
             {
                 /* Release any thread waiting on this message */
-                queue_release_sender(spp, 0);
+                queue_release_sender(sp, 0);
             }
         }
 #endif
         q->read++;
     }
     
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        unlock_cores();
+#endif
+    
     set_irq_level(oldlevel);
 }
 
@@ -389,11 +475,21 @@
     int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
     int result = 0;
     
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        lock_cores();
+#endif
+    
     if (q->read <= q->write)
         result = q->write - q->read;
     else
         result = QUEUE_LENGTH - (q->read - q->write);
     
+#if NUM_CORES > 1
+    if (!q->irq_safe)
+        unlock_cores();
+#endif
+    
     set_irq_level(oldlevel);
     
     return result;
@@ -401,14 +497,14 @@
 
 int queue_broadcast(long id, intptr_t data)
 {
-   int i;
-
-   for(i = 0;i < num_queues;i++)
-   {
-      queue_post(all_queues[i], id, data);
-   }
+    int i;
+    
+    for(i = 0;i < num_queues;i++)
+    {
+        queue_post(all_queues[i], id, data);
+    }
    
-   return num_queues;
+    return num_queues;
 }
 
 /****************************************************************************
@@ -681,44 +777,6 @@
     m->thread = NULL;
 }
 
-/* PortalPlayer chips have 2 cores, therefore need atomic mutexes 
- * Just use it for ARM, Coldfire and whatever else well...why not?
- */
-
-/* Macros generate better code than an inline function is this case */
-#if defined (CPU_PP) || defined (CPU_ARM)
-#define test_and_set(x_, v_) \
-({ \
-    uint32_t old; \
-    asm volatile ( \
-        "swpb %[old], %[v], [%[x]] \r\n" \
-        : [old]"=r"(old) \
-        : [v]"r"((uint32_t)v_), [x]"r"((uint32_t *)x_) \
-    ); \
-    old; \
-    })
-#elif defined (CPU_COLDFIRE)
-#define test_and_set(x_, v_) \
-({ \
-    uint8_t old; \
-    asm volatile ( \
-        "bset.l %[v], (%[x]) \r\n" \
-        "sne.b  %[old]       \r\n" \
-        : [old]"=d,d"(old) \
-        : [v]"i,d"((uint32_t)v_), [x]"a,a"((uint32_t *)x_) \
-    ); \
-    old; \
-    })
-#else
-/* default for no asm version */
-#define test_and_set(x_, v_) \
-({ \
-    uint32_t old = *(uint32_t *)x_; \
-    *(uint32_t *)x_ = v_; \
-    old; \
-    })
-#endif
-
 void mutex_lock(struct mutex *m)
 {
     if (test_and_set(&m->locked, 1))
@@ -730,10 +788,14 @@
 
 void mutex_unlock(struct mutex *m)
 {
+    lock_cores();
+    
     if (m->thread == NULL)
         m->locked = 0;
     else
         wakeup_thread(&m->thread);
+    
+    unlock_cores();
 }
 
 void spinlock_lock(struct mutex *m)
Index: firmware/system.c
===================================================================
--- firmware/system.c	(revision 12879)
+++ firmware/system.c	(working copy)
@@ -28,13 +28,15 @@
 #include "string.h"
 
 #ifndef SIMULATOR
-long cpu_frequency = CPU_FREQ;
+long cpu_frequency NOCACHEBSS_ATTR = CPU_FREQ;
 #endif
 
 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
-static int boost_counter = 0;
-static bool cpu_idle = false;
+static int boost_counter NOCACHEBSS_ATTR = 0;
+static bool cpu_idle NOCACHEBSS_ATTR = false;
 
+struct mutex boostctrl_mtx NOCACHEBSS_ATTR;
+
 int get_cpu_boost_counter(void)
 {
     return boost_counter;
@@ -722,7 +724,8 @@
 {
     unsigned long postmult;
 
-    if (CURRENT_CORE == CPU)
+    spinlock_lock(&boostctrl_mtx);
+    // if (CURRENT_CORE == CPU)
     {
         if (frequency == CPUFREQ_NORMAL)
             postmult = CPUFREQ_NORMAL_MULT;
@@ -763,6 +766,8 @@
         COP_INT_EN |= TIMER1_MASK;
 #endif
     }
+    
+    spinlock_unlock(&boostctrl_mtx);
 }
 #elif !defined(BOOTLOADER)
 void ipod_set_cpu_frequency(void)
@@ -804,6 +809,8 @@
         outl(-1, 0x60001038);
         outl(-1, 0x60001028);
         outl(-1, 0x6000101c);
+        
+        spinlock_init(&boostctrl_mtx);
 #if (!defined HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES == 1)
         ipod_set_cpu_frequency();
 #endif
Index: firmware/pcm_playback.c
===================================================================
--- firmware/pcm_playback.c	(revision 12879)
+++ firmware/pcm_playback.c	(working copy)
@@ -42,9 +42,9 @@
     be shared semi-privately **/
 
 /* the registered callback function to ask for more mp3 data */
-volatile pcm_more_callback_type pcm_callback_for_more = NULL;
-volatile bool                   pcm_playing           = false;
-volatile bool                   pcm_paused            = false;
+volatile pcm_more_callback_type pcm_callback_for_more NOCACHEDATA_ATTR = NULL;
+volatile bool                   pcm_playing           NOCACHEDATA_ATTR = false;
+volatile bool                   pcm_paused            NOCACHEDATA_ATTR = false;
 
 void pcm_play_dma_start(const void *addr, size_t size);
 void pcm_play_dma_stop(void);
Index: firmware/logf.c
===================================================================
--- firmware/logf.c	(revision 12879)
+++ firmware/logf.c	(working copy)
@@ -37,10 +37,19 @@
 
 #ifndef __PCTOOL__
 unsigned char logfbuffer[MAX_LOGF_LINES][MAX_LOGF_ENTRY];
-int logfindex;
-bool logfwrap;
+int logfindex NOCACHEBSS_ATTR;
+bool logfwrap NOCACHEBSS_ATTR;
 #endif
 
+void flushcache(void)
+{
+    int i;
+    char buf[MAX_LOGF_ENTRY];
+    
+    for (i = 0; i < MAX_LOGF_LINES; i++)
+        memcpy(buf, logfbuffer[i], sizeof buf);
+}
+
 #ifdef HAVE_REMOTE_LCD
 static void displayremote(void)
 {
@@ -89,11 +98,17 @@
     printf("DEBUG: %s\n", buf);
 }
 #else
+#include "system.h"
 void _logf(const char *format, ...)
 {
     int len;
     unsigned char *ptr;
     va_list ap;
+    
+    if (CURRENT_CORE != COP)
+        return;
+    
+    flushcache();
     va_start(ap, format);
 
     if(logfindex >= MAX_LOGF_LINES) {
Index: firmware/backlight.c
===================================================================
--- firmware/backlight.c	(revision 12879)
+++ firmware/backlight.c	(working copy)
@@ -556,6 +556,7 @@
 void backlight_init(void)
 {
     queue_init(&backlight_queue, true);
+    queue_set_irq_safe(&backlight_queue, true);
 
 #ifdef SIMULATOR
     /* do nothing */
Index: firmware/target/arm/ipod/backlight-mini1g_mini2g.c
===================================================================
--- firmware/target/arm/ipod/backlight-mini1g_mini2g.c	(revision 12879)
+++ firmware/target/arm/ipod/backlight-mini1g_mini2g.c	(working copy)
@@ -31,6 +31,11 @@
 #include "timer.h"
 #include "backlight.h"
 
+inline bool __backlight_init(void)
+{
+    return true;
+}
+
 inline void __backlight_on(void)
 {
     /* set port B03 on */
Index: firmware/target/arm/ipod/backlight-target.h
===================================================================
--- firmware/target/arm/ipod/backlight-target.h	(revision 12879)
+++ firmware/target/arm/ipod/backlight-target.h	(working copy)
@@ -19,7 +19,11 @@
 #ifndef BACKLIGHT_TARGET_H
 #define BACKLIGHT_TARGET_H
 
+#define __BACKLIGHT_INIT
+
+bool __backlight_init(void);
 void __backlight_on(void);
 void __backlight_off(void);
+void __backlight_set_brightness(int val);
 
 #endif
Index: firmware/target/arm/ipod/button-clickwheel.c
===================================================================
--- firmware/target/arm/ipod/button-clickwheel.c	(revision 12879)
+++ firmware/target/arm/ipod/button-clickwheel.c	(working copy)
@@ -47,6 +47,19 @@
     static bool send_events = true;
 #endif
 
+#define COUNT_PER_REVOLUTION  96
+#define WHEEL_TRIGGER_SPEED    8
+#define SPEED_MAX_X           16
+#define SPEED_DIV             10
+#define SPEED_MIN              5
+
+#define WHEEL_SPEED_INCREASE   96
+#define WHEEL_SPEED_DECREASE   48
+#define WHEEL_SPEED_DROP       24
+
+static int last_direction = -1;
+static int speed = SPEED_MIN;
+
 static void opto_i2c_init(void)
 {
     int i, curr_value;
@@ -98,6 +111,11 @@
         if ((status & 0x800000ff) == 0x8000001a) {
             static int old_wheel_value IDATA_ATTR = -1;
             static int wheel_repeat = 0;
+            static int last_delta_tick = 0;
+            static int last_tick = 0;
+            static int current_speed = 0;
+            static int wheel_deltasum = 0;
+            static int events_remaining = 0;
 
             if (status & 0x100)
                 btn |= BUTTON_SELECT;
@@ -116,31 +134,124 @@
                 backlight_on();
                 reset_poweroff_timer();
                 /* The queue should have no other events when scrolling */
-                if (queue_empty(&button_queue) && old_wheel_value >= 0) {
+                if (old_wheel_value >= 0) {
 
                     /* This is for later = BUTTON_SCROLL_TOUCH;*/
                     int wheel_delta = new_wheel_value - old_wheel_value;
                     unsigned long data;
                     int wheel_keycode;
 
-                    if (wheel_delta < -48)
-                        wheel_delta += 96; /* Forward wrapping case */
-                    else if (wheel_delta > 48)
-                        wheel_delta -= 96; /* Backward wrapping case */
+                    if (wheel_delta < -COUNT_PER_REVOLUTION/2)
+                    {
+                        wheel_delta += COUNT_PER_REVOLUTION; /* Forward wrapping case */
+                    }
+                    else if (wheel_delta > COUNT_PER_REVOLUTION/2)
+                    {
+                        wheel_delta -= COUNT_PER_REVOLUTION; /* Backward wrapping case */
+                    }
 
-                    if (wheel_delta > 4) {
+                    if (abs(wheel_delta) > 4)
+                    {
+                        int absdelta = abs(wheel_delta);
+                        unsigned int diff = current_tick - last_delta_tick;
+                        
+                        current_speed = HZ*absdelta/diff;
+                        old_wheel_value = new_wheel_value;
+                        last_delta_tick = current_tick;
+                        speed = MAX(SPEED_MIN, current_speed*current_speed/2048);
+                    }
+                    else
+                        goto wheel_end;
+                    
+                    if (current_speed <= WHEEL_TRIGGER_SPEED)
+                    {
+                        events_remaining = 0;
+                        speed = SPEED_MIN;
+                        goto wheel_end;
+                    }
+                    
+                    if (wheel_delta > 0)
+                    {
                         wheel_keycode = BUTTON_SCROLL_FWD;
-                    } else if (wheel_delta < -4) {
+                        if (last_direction != 1)
+                        {
+                            last_tick = 0;
+                            speed = SPEED_MIN;
+                            events_remaining = 0;
+                        }
+                        last_direction = 1;
+                    } 
+                    else 
+                    {
                         wheel_keycode = BUTTON_SCROLL_BACK;
-                    } else goto wheel_end;
-
+                        if (last_direction != 0)
+                        {
+                            last_tick = 0;
+                            speed = SPEED_MIN;
+                            events_remaining = 0;
+                        }
+                        last_direction = 0;
+                    } 
+#if 0
+                    /* Check the scrolling speed. */
+                    wheel_deltasum += abs(wheel_delta);
+                    if (current_tick - last_tick > HZ/4)
+                    {
+                        unsigned int diff = current_tick - last_tick;
+                        int speed_hz = HZ*wheel_deltasum/diff;
+                        int target_speed = MAX(SPEED_MIN, speed_hz*speed_hz/128);
+                        
+                        if (target_speed > speed)
+                            speed++;
+                        else if (target_speed < speed)
+                        {
+                            speed = target_speed;
+                            events_remaining = 0;
+                        }
+                        
+                        // speed += (target_speed - speed) / 10;
+#if 0
+                        if (speed_hz >= WHEEL_SPEED_INCREASE)
+                        {
+                            if (speed < SPEED_MAX_X)
+                                speed++;
+                        }
+                        else if (speed_hz > WHEEL_SPEED_DECREASE)
+                        {
+                            /* Keep the current speed. */
+                        }
+                        else if (speed_hz >= WHEEL_SPEED_DROP)
+                        {
+                            if (speed > SPEED_MIN)
+                                speed--;
+                        }
+                        else
+                        {
+                            speed = SPEED_MIN;
+                        }
+#endif
+                        last_tick = current_tick;
+                        wheel_deltasum = 0;
+                    }
+#endif               
 #ifdef HAVE_WHEEL_POSITION
                     if (send_events)
 #endif
                     {
+                        int i;
+                        int limit;
+                        
+                        events_remaining += speed;
+                        limit = MIN(QUEUE_LENGTH - queue_count(&button_queue) - 1,
+                                        events_remaining/SPEED_DIV);
+                        
                         data = (wheel_delta << 16) | new_wheel_value;
-                        queue_post(&button_queue, wheel_keycode | wheel_repeat,
-                                   data);
+                        for (i = 0; i < limit; i++)
+                        {
+                            queue_post(&button_queue, wheel_keycode | wheel_repeat,
+                                       data);
+                        }
+                        events_remaining -= i*SPEED_DIV;
                     }
 
                     if (!wheel_repeat) wheel_repeat = BUTTON_REPEAT;
Index: firmware/target/arm/ipod/backlight-nano_video.c
===================================================================
--- firmware/target/arm/ipod/backlight-nano_video.c	(revision 12879)
+++ firmware/target/arm/ipod/backlight-nano_video.c	(working copy)
@@ -31,6 +31,14 @@
 #include "timer.h"
 #include "backlight.h"
 
+inline bool __backlight_init(void)
+{
+    /* This seems to lower the backlight brightness a bit. */
+    outl(1 << 7, 0x6000d02c);
+    
+    return true;
+}
+
 inline void __backlight_on(void)
 {
     /* set port B03 on */
Index: firmware/target/arm/pcm-pp.c
===================================================================
--- firmware/target/arm/pcm-pp.c	(revision 12879)
+++ firmware/target/arm/pcm-pp.c	(working copy)
@@ -25,8 +25,8 @@
 
 /* peaks */
 #ifdef HAVE_RECORDING
-static unsigned long *rec_peak_addr;
-static int rec_peak_left, rec_peak_right;
+static unsigned long *rec_peak_addr NOCACHEBSS_ATTR;
+static int rec_peak_left, rec_peak_right NOCACHEBSS_ATTR;
 #endif
 
 /** DMA **/
Index: firmware/thread.c
===================================================================
--- firmware/thread.c	(revision 12879)
+++ firmware/thread.c	(working copy)
@@ -32,9 +32,6 @@
 /* Cast to the the machine int type, whose size could be < 4. */
 
 struct core_entry cores[NUM_CORES] IBSS_ATTR;
-#ifdef HAVE_PRIORITY_SCHEDULING
-static unsigned short highest_priority IBSS_ATTR;
-#endif
 #ifdef HAVE_SCHEDULER_BOOSTCTRL
 static int boosted_threads IBSS_ATTR;
 #endif
@@ -61,6 +58,28 @@
 
 #if (NUM_CORES > 1)
 bool IDATA_ATTR kernel_running_on_cop = false;
+static long cores_locked IDATA_ATTR = 0;
+
+#define LOCK(...) do { } while (test_and_set(&cores_locked, 1))
+#define UNLOCK(...) cores_locked = 0
+
+inline void lock_cores(void)
+{
+    if (!cores[CURRENT_CORE].lock_issued)
+    {
+        LOCK();
+        cores[CURRENT_CORE].lock_issued = true;
+    }
+}
+
+inline void unlock_cores(void)
+{
+    if (cores[CURRENT_CORE].lock_issued)
+    {
+        cores[CURRENT_CORE].lock_issued = false;
+        UNLOCK();
+    }
+}
 #endif
 
 /* Conserve IRAM
@@ -70,7 +89,7 @@
                              struct thread_entry *thread) ICODE_ATTR;
 */
 
-void switch_thread(bool save_context, struct thread_entry **blocked_list)
+void switch_thread(bool save_context, struct thread_entry **blocked_list) 
     ICODE_ATTR;
 
 static inline void store_context(void* addr) __attribute__ ((always_inline));
@@ -291,17 +310,16 @@
 
 static inline void sleep_core(void)
 {
-    static long last_tick = 0;
 #if CONFIG_CPU == S3C2440
     int i;
 #endif
     
     for (;;)
     {
-        if (last_tick != current_tick)
+        if (cores[CURRENT_CORE].last_tick != current_tick)
         {
             check_sleepers();
-            last_tick = current_tick;
+            cores[CURRENT_CORE].last_tick = current_tick;
         }
         
         /* We must sleep until there is at least one process in the list
@@ -316,17 +334,22 @@
         and_b(0x7F, &SBYCR);
         asm volatile ("sleep");
 #elif defined (CPU_PP)
+        unlock_cores();
+        
         /* This should sleep the CPU. It appears to wake by itself on
            interrupts */
         if (CURRENT_CORE == CPU)
             CPU_CTL = PROC_SLEEP;
         else
             COP_CTL = PROC_SLEEP;
+        
+        lock_cores();
 #elif CONFIG_CPU == S3C2440
         CLKCON |= (1 << 2); /* set IDLE bit */
         for(i=0; i<10; i++); /* wait for IDLE */
         CLKCON &= ~(1 << 2); /* reset IDLE bit when wake up */
 #endif
+        
     }
 }
 
@@ -374,8 +397,8 @@
         
 #ifdef HAVE_PRIORITY_SCHEDULING
         /* Reset priorities */
-        if (old->priority == highest_priority)
-            highest_priority = 100;
+        if (old->priority == cores[CURRENT_CORE].highest_priority)
+            cores[CURRENT_CORE].highest_priority = 100;
 #endif
     }
     else
@@ -398,6 +421,8 @@
     /* Do nothing */
 #else
     
+    lock_cores();
+    
     /* Begin task switching by saving our current context so that we can
      * restore the state of the current thread later to the point prior
      * to this call. */
@@ -438,14 +463,16 @@
     {
         int priority = cores[CURRENT_CORE].running->priority;
 
-        if (priority < highest_priority)
-            highest_priority = priority;
+        if (priority < cores[CURRENT_CORE].highest_priority)
+            cores[CURRENT_CORE].highest_priority = priority;
 
-        if (priority == highest_priority ||
+        if (priority == cores[CURRENT_CORE].highest_priority ||
                 (current_tick - cores[CURRENT_CORE].running->last_run >
                  priority * 8) ||
             cores[CURRENT_CORE].running->priority_x != 0)
+        {
             break;
+        }
 
         cores[CURRENT_CORE].running = cores[CURRENT_CORE].running->next;
     }
@@ -455,6 +482,8 @@
 #endif
     
 #endif
+    unlock_cores();
+    
     /* And finally give control to the next thread. */
     load_context(&cores[CURRENT_CORE].running->context);
     
@@ -467,10 +496,13 @@
 {
     struct thread_entry *current;
 
+    lock_cores();
+    
     current = cores[CURRENT_CORE].running;
 
 #ifdef HAVE_SCHEDULER_BOOSTCTRL
-    if (STATE_IS_BOOSTED(current->statearg)) {
+    if (STATE_IS_BOOSTED(current->statearg))
+    {
         boosted_threads--;
         if (!boosted_threads)
         {
@@ -483,12 +515,16 @@
      * so that scheduler removes thread from the list of running processes
      * and puts it in list of sleeping tasks. */
     SET_STATE(current->statearg, STATE_SLEEPING, current_tick + ticks + 1);
+    
     switch_thread(true, NULL);
 }
 
 void block_thread(struct thread_entry **list)
 {
     struct thread_entry *current;
+    
+    lock_cores();
+    
     /* Get the entry for the current running thread. */
     current = cores[CURRENT_CORE].running;
 
@@ -526,11 +562,13 @@
     /* Get the entry for the current running thread. */
     current = cores[CURRENT_CORE].running;
     
+    lock_cores();
 #ifdef HAVE_SCHEDULER_BOOSTCTRL
     /* A block with a timeout is a sleep situation, whatever we are waiting
      * for _may or may not_ happen, regardless of boost state, (user input
      * for instance), so this thread no longer needs to boost */
-    if (STATE_IS_BOOSTED(current->statearg)) {
+    if (STATE_IS_BOOSTED(current->statearg))
+    {
         boosted_threads--;
         if (!boosted_threads)
         {
@@ -583,7 +621,9 @@
     
     /* Check if there is a blocked thread at all. */
     if (*list == NULL)
+    {
         return ;
+    }
     
     /* Wake up the last thread first. */
     thread = *list;
@@ -595,7 +635,7 @@
             /* Remove thread from the list of blocked threads and add it
              * to the scheduler's list of running processes. */
             remove_from_list(list, thread);
-            add_to_list(&cores[CURRENT_CORE].running, thread);
+            add_to_list(&cores[thread->core].running, thread);
         
         case STATE_BLOCKED_W_TMO:
             /* Just remove the timeout to cause scheduler to immediately
@@ -669,6 +709,8 @@
             return NULL;
     }
     
+    lock_cores();
+    
     /* Munge the stack to make it easy to spot stack overflows */
     stacklen = stack_size / sizeof(int);
     stackptr = stack;
@@ -686,9 +728,12 @@
 #ifdef HAVE_PRIORITY_SCHEDULING
     thread->priority_x = 0;
     thread->priority = priority;
-    highest_priority = 100;
+    cores[core].highest_priority = 100;
 #endif
-    add_to_list(&cores[core].running, thread);
+
+#if NUM_CORES > 1
+    thread->core = core;
+#endif
     
     regs = &thread->context;
     /* Align stack to an even 32 bit boundary */
@@ -698,6 +743,9 @@
     /* Do any CPU specific inits after initializing common items
        to have access to valid data */
     THREAD_CPU_INIT(core, thread);
+    
+    add_to_list(&cores[core].running, thread);
+    unlock_cores();
 
     return thread;
 #if NUM_CORES == 1
@@ -708,6 +756,8 @@
 #ifdef HAVE_SCHEDULER_BOOSTCTRL
 void trigger_cpu_boost(void)
 {
+    lock_cores();
+    
     if (!STATE_IS_BOOSTED(cores[CURRENT_CORE].running->statearg))
     {
         SET_BOOST_STATE(cores[CURRENT_CORE].running->statearg);
@@ -717,6 +767,8 @@
         }
         boosted_threads++;
     }
+    
+    unlock_cores();
 }
 #endif
 
@@ -727,26 +779,30 @@
  */
 void remove_thread(struct thread_entry *thread)
 {
+    lock_cores();
+    
     if (thread == NULL)
-        thread = cores[CURRENT_CORE].running;
+        thread = cores[thread->core].running;
     
     /* Free the entry by removing thread name. */
     thread->name = NULL;
 #ifdef HAVE_PRIORITY_SCHEDULING
-    highest_priority = 100;
+    cores[thread->core].highest_priority = 100;
 #endif
     
-    if (thread == cores[CURRENT_CORE].running)
+    if (thread == cores[thread->core].running)
     {
-        remove_from_list(&cores[CURRENT_CORE].running, thread);
+        remove_from_list(&cores[thread->core].running, thread);
         switch_thread(false, NULL);
         return ;
     }
     
-    if (thread == cores[CURRENT_CORE].sleeping)
-        remove_from_list(&cores[CURRENT_CORE].sleeping, thread);
+    if (thread == cores[thread->core].sleeping)
+        remove_from_list(&cores[thread->core].sleeping, thread);
     else
         remove_from_list(NULL, thread);
+    
+    unlock_cores();
 }
 
 #ifdef HAVE_PRIORITY_SCHEDULING
@@ -759,7 +815,7 @@
 
     old_priority = thread->priority;
     thread->priority = priority;
-    highest_priority = 100;
+    cores[thread->core].highest_priority = 100;
     
     return old_priority;
 }
@@ -802,7 +858,8 @@
 #ifdef HAVE_PRIORITY_SCHEDULING
     cores[core].threads[0].priority = PRIORITY_USER_INTERFACE;
     cores[core].threads[0].priority_x = 0;
-    highest_priority = 100;
+    cores[core].threads[0].core = core;
+    cores[core].highest_priority = 100;
 #endif
 #ifdef HAVE_SCHEDULER_BOOSTCTRL
     boosted_threads = 0;
Index: firmware/usb.c
===================================================================
--- firmware/usb.c	(revision 12879)
+++ firmware/usb.c	(working copy)
@@ -427,6 +427,8 @@
 
 #ifndef BOOTLOADER
     queue_init(&usb_queue, true);
+    queue_set_irq_safe(&usb_queue, true);
+    
     create_thread(usb_thread, usb_stack, sizeof(usb_stack), 
                   usb_thread_name IF_PRIO(, PRIORITY_SYSTEM)
 		  IF_COP(, CPU, false));
Index: firmware/drivers/ata.c
===================================================================
--- firmware/drivers/ata.c	(revision 12879)
+++ firmware/drivers/ata.c	(working copy)
@@ -67,30 +67,30 @@
 #define ATA_POWER_OFF_TIMEOUT 2*HZ
 #endif
 
-static struct mutex ata_mtx;
-int ata_device; /* device 0 (master) or 1 (slave) */
+static struct mutex ata_mtx NOCACHEBSS_ATTR;
+int ata_device NOCACHEBSS_ATTR; /* device 0 (master) or 1 (slave) */
 
-int ata_spinup_time = 0;
+int ata_spinup_time  NOCACHEDATA_ATTR = 0;
 #if (CONFIG_LED == LED_REAL)
-static bool ata_led_enabled = true;
-static bool ata_led_on = false;
+static bool ata_led_enabled NOCACHEDATA_ATTR = true;
+static bool ata_led_on NOCACHEDATA_ATTR = false;
 #endif
-static bool spinup = false;
-static bool sleeping = true;
-static bool poweroff = false;
-static long sleep_timeout = 5*HZ;
+static bool spinup NOCACHEDATA_ATTR = false;
+static bool sleeping NOCACHEDATA_ATTR = true;
+static bool poweroff NOCACHEDATA_ATTR = false;
+static long sleep_timeout NOCACHEDATA_ATTR = 5*HZ;
 #ifdef HAVE_LBA48
-static bool lba48 = false; /* set for 48 bit addressing */
+static bool lba48 NOCACHEDATA_ATTR = false; /* set for 48 bit addressing */
 #endif
 static long ata_stack[(DEFAULT_STACK_SIZE*3)/sizeof(long)];
 static const char ata_thread_name[] = "ata";
-static struct event_queue ata_queue;
-static bool initialized = false;
+static struct event_queue ata_queue NOCACHEBSS_ATTR;
+static bool initialized NOCACHEDATA_ATTR = false;
 
-static long last_user_activity = -1;
-long last_disk_activity = -1;
+static long last_user_activity NOCACHEDATA_ATTR = -1;
+long last_disk_activity NOCACHEDATA_ATTR = -1;
 
-static int multisectors; /* number of supported multisectors */
+static int multisectors NOCACHEBSS_ATTR; /* number of supported multisectors */
 static unsigned short identify_info[SECTOR_SIZE];
 
 static int ata_power_on(void);
Index: firmware/drivers/button.c
===================================================================
--- firmware/drivers/button.c	(revision 12879)
+++ firmware/drivers/button.c	(working copy)
@@ -37,6 +37,12 @@
 #include "lcd-remote.h"
 #endif
 
+/* Older than MAX_EVENT_AGE button events are going to be ignored.
+ * Used to prevent for example volume going up uncontrollable when events
+ * are getting queued and UI is lagging too much.
+ */
+#define MAX_EVENT_AGE  HZ
+
 struct event_queue button_queue;
 
 static long lastbtn;   /* Last valid button status */
@@ -290,6 +296,12 @@
     if ( block || pending_count )
     {
         queue_wait(&button_queue, &ev);
+        
+        /* Ignore if the event was too old and for simplicity, just
+         * wait for a new button_get() request. */
+        if (current_tick - ev.tick > MAX_EVENT_AGE)
+            return BUTTON_NONE;
+        
         return ev.id;
     }
     
@@ -318,6 +330,11 @@
     button_init_device();
 
     queue_init(&button_queue, true);
+    
+    /* Enable less protection which would kill IRQ handler. Writing queue is
+     * no longer core-wise thread safe. */
+    queue_set_irq_safe(&button_queue, true);
+    
     button_read();
     lastbtn = button_read();
     tick_add_task(button_tick);
