diff --git a/firmware/SOURCES b/firmware/SOURCES index fe97fe2..2773c14 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -406,11 +406,11 @@ target/arm/pnx0101/timer-pnx0101.c target/arm/as3525/system-as3525.c target/arm/as3525/kernel-as3525.c target/arm/as3525/timer-as3525.c -#ifdef HAVE_SCROLLWHEEL -target/arm/as3525/scrollwheel-as3525.c -#endif #if CONFIG_CPU == AS3525 target/arm/as3525/sd-as3525.c +#ifdef HAVE_SCROLLWHEEL +target/arm/as3525/scrollwheel-as3525.c +#endif /* HAVE_SCROLLWHEEL */ #else /* AS3535v2 */ target/arm/as3525/sd-as3525v2.c #endif diff --git a/firmware/export/config/sansafuzev2.h b/firmware/export/config/sansafuzev2.h index 88d7650..71913ab 100644 --- a/firmware/export/config/sansafuzev2.h +++ b/firmware/export/config/sansafuzev2.h @@ -68,6 +68,15 @@ /* There is no hardware tone control */ #define HAVE_SW_TONE_CONTROLS +/* define this if the unit uses a scrollwheel for navigation */ +#define HAVE_SCROLLWHEEL +/* define to activate advanced wheel acceleration code */ +#define HAVE_WHEEL_ACCELERATION +/* define from which rotation speed [degree/sec] on the acceleration starts */ +#define WHEEL_ACCEL_START 540 +/* define type of acceleration (1 = ^2, 2 = ^3, 3 = ^4) */ +#define WHEEL_ACCELERATION 1 + #endif /* !BOOTLOADER */ /* put the lcd frame buffer in IRAM */ @@ -126,15 +135,6 @@ /* Which backlight fading type? */ #define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING -/* define this if the unit uses a scrollwheel for navigation */ -#define HAVE_SCROLLWHEEL -/* define to activate advanced wheel acceleration code */ -#define HAVE_WHEEL_ACCELERATION -/* define from which rotation speed [degree/sec] on the acceleration starts */ -#define WHEEL_ACCEL_START 540 -/* define type of acceleration (1 = ^2, 2 = ^3, 3 = ^4) */ -#define WHEEL_ACCELERATION 1 - /* define this if you have a flash memory storage */ #define HAVE_FLASH_STORAGE diff --git a/firmware/target/arm/as3525/kernel-as3525.c b/firmware/target/arm/as3525/kernel-as3525.c index ebaef71..c6cb6fd 100644 --- a/firmware/target/arm/as3525/kernel-as3525.c +++ b/firmware/target/arm/as3525/kernel-as3525.c @@ -24,14 +24,19 @@ #include "panic.h" #include "timer.h" -#ifdef HAVE_SCROLLWHEEL +/* We can use a interrupt-based mechanism on the fuzev2 */ +#define INCREASED_SCROLLWHEEL_POLLING \ + (defined(HAVE_SCROLLWHEEL) && (CONFIG_CPU == AS3525)) + + +#if INCREASED_SCROLLWHEEL_POLLING /* let the timer interrupt twice as often for the scrollwheel polling */ #define KERNEL_TIMER_FREQ (TIMER_FREQ/2) #else #define KERNEL_TIMER_FREQ TIMER_FREQ #endif -#ifdef HAVE_SCROLLWHEEL +#if INCREASED_SCROLLWHEEL_POLLING #include "button-target.h" /* The scrollwheel is polled every 5 ms (the tick tasks only every 10) */ static int poll_scrollwheel = 0; diff --git a/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c b/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c index 4848d9e..2017eb4 100644 --- a/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c +++ b/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c @@ -24,42 +24,194 @@ #include "button.h" #include "backlight.h" -extern void scrollwheel(unsigned wheel_value); - #ifdef HAS_BUTTON_HOLD static bool hold_button = false; #endif + +#ifdef HAVE_SCROLLWHEEL +#define SCROLLWHEEL_BITS (1<<7|1<<6) + /* TIMER units */ +#define TIMER_MAX (TIMER_FREQ/HZ) /* how long a tick lasts */ +#define TIMER_MS (TIMER_MAX/(1000/HZ)) /* how long a ms lasts */ + +#define WHEEL_REPEAT_INTERVAL (300*TIMER_MS) /* 300ms */ +#define WHEEL_FAST_ON_INTERVAL ( 15*TIMER_MS) /* 40ms */ +#define WHEEL_FAST_OFF_INTERVAL ( 45*TIMER_MS) /* 45ms */ +/* phsyical clicks per rotation * wheel value changes per phys click */ +#define WHEEL_CHANGES_PER_CLICK 4 +#define WHEELCLICKS_PER_ROTATION (12*WHEEL_CHANGES_PER_CLICK) + +/* + * heavily copied from button-e200.c, adjusted to the AMS timers and fuzev2's + * scrollwheel + */ +static void scrollwheel(unsigned int wheel_value) +{ + /* wheel values and times from the previous irq */ + static unsigned int old_wheel_value = 0; + static unsigned int wheel_repeat = BUTTON_NONE; + static long last_wheel_post = 0; + + /* We only post every 4th action, as this matches better with the physical + * clicks of the wheel */ + static unsigned int wheel_click_count = 0; + /* number of items to skip in lists, 1 in slow mode */ + static unsigned int wheel_delta = 0; + /* accumulated wheel rotations per second */ + static unsigned long wheel_velocity = 0; + /* fast or slow mode? */ + static int wheel_fast_mode = 0; + + /* Read wheel + * Bits 13 and 14 of DBOP_DIN change as follows (Gray Code): + * Clockwise rotation 00 -> 01 -> 11 -> 10 -> 00 + * Counter-clockwise 00 -> 10 -> 11 -> 01 -> 00 + * + * For easy look-up, actual wheel values act as indicies also, + * which is why the table seems to be not ordered correctly + */ + static const unsigned char wheel_tbl[2][4] = + { + { 2, 0, 3, 1 }, /* Clockwise rotation */ + { 1, 3, 0, 2 }, /* Counter-clockwise */ + }; + + if (hold_button) + return; + + unsigned int btn = BUTTON_NONE; + + if (old_wheel_value == wheel_tbl[0][wheel_value]) + btn = BUTTON_SCROLL_FWD; + else if (old_wheel_value == wheel_tbl[1][wheel_value]) + btn = BUTTON_SCROLL_BACK; + + if (btn == BUTTON_NONE) + { + old_wheel_value = wheel_value; + return; + } + + int repeat = 1; /* assume repeat */ + long time = TIMER1_VALUE + current_tick*TIMER_MAX; /* to timer unit */ + long v = (time - last_wheel_post) & 0x7ffffff; + + /* interpolate velocity in timer_freq/timer_unit == 1/s */ + if (v) v = TIMER_FREQ / v; + + /* some velocity filtering to smooth things out */ + wheel_velocity = (7*wheel_velocity + v) / 8; + + if (btn != wheel_repeat) + { + /* direction reversals nullify all fast mode states */ + wheel_repeat = btn; + repeat = + wheel_velocity = + wheel_click_count = 0; + } + + if (wheel_fast_mode != 0) + { + /* fast OFF happens immediately when velocity drops below + threshold */ + if (TIME_AFTER(time, + last_wheel_post + WHEEL_FAST_OFF_INTERVAL)) + { + /* moving out of fast mode */ + wheel_fast_mode = 0; + /* reset velocity */ + wheel_velocity = 0; + /* wheel_delta is always 1 in slow mode */ + wheel_delta = 1; + } + } + else + { + /* fast ON gets filtered to avoid inadvertent jumps to fast mode */ + if (repeat && wheel_velocity > TIMER_FREQ/WHEEL_FAST_ON_INTERVAL) + { + /* moving into fast mode */ + wheel_fast_mode = 1 << 31; + wheel_click_count = 0; + wheel_velocity = TIMER_FREQ/WHEEL_FAST_OFF_INTERVAL; + } + else if (++wheel_click_count < WHEEL_CHANGES_PER_CLICK) + { /* skip some wheel changes, so that 1 post represents + * 1 item in lists */ + btn = BUTTON_NONE; + } + + /* wheel_delta is always 1 in slow mode */ + wheel_delta = 1; + } + + if (btn != BUTTON_NONE) + { + wheel_click_count = 0; + + /* generate repeats if quick enough */ + if (repeat && TIME_BEFORE(time, + last_wheel_post + WHEEL_REPEAT_INTERVAL)) + btn |= BUTTON_REPEAT; + + if (queue_empty(&button_queue)) + { + queue_post(&button_queue, btn, wheel_fast_mode | + (wheel_delta << 24) | wheel_velocity*360/WHEELCLICKS_PER_ROTATION); + /* message posted - reset delta and poke backlight on*/ + wheel_delta = 1; + backlight_on(); + buttonlight_on(); + } + else + { + /* skipped post - increment delta */ + if (++wheel_delta > 0x7f) + wheel_delta = 0x7f; + } + } + + last_wheel_post = time; + + old_wheel_value = wheel_value; +} +#endif + void button_init_device(void) { /* activate the wheel */ +#if defined(HAVE_SCROLLWHEEL) volatile int i; + /* enable wheel */ GPIOB_DIR |= 1<<4; for(i = 20; i; i--) nop; GPIOB_PIN(4) = 0x10; -} -unsigned read_GPIOA_67(void) -{ - unsigned ret = 0; - volatile int i; - DBOP_CTRL |= 1<<19; - for(i = 20; i; i--) nop; - GPIOA_DIR &= ~0xc0; - for(i = 20; i; i--) nop; - if (GPIOA_PIN(6) != 0) - ret = 1<<0; - for(i = 20; i; i--) nop; - if (GPIOA_PIN(7) != 0) - ret |= 1<<1; - DBOP_CTRL &= ~(1<<19); - for(i = 20; i; i--) nop; - return ret; + /* setup scrollwheel isr */ + /* clear previous irq if any */ + GPIOA_IC = SCROLLWHEEL_BITS; + /* enable edge detecting */ + GPIOA_IS &= ~SCROLLWHEEL_BITS; + /* detect both raising and falling edges */ + GPIOA_IBE |= SCROLLWHEEL_BITS; + /* lastly, enable the interrupt */ + GPIOA_IE |= SCROLLWHEEL_BITS; +#endif } -void get_scrollwheel(void) +void button_gpioa_irq_handler(void) { -#if defined(HAVE_SCROLLWHEEL) && !defined(BOOTLOADER) +#if defined(HAVE_SCROLLWHEEL) + /* ack interrupt */ + GPIOA_IC = SCROLLWHEEL_BITS; /* scroll wheel handling */ - scrollwheel(read_GPIOA_67()); + if (GPIOA_MIS & SCROLLWHEEL_BITS) + { + /* read the 2 bits at the same time */ +#define GPIOA_PIN76_offset ((1<<(6+2)) | (1<<(7+2))) +#define GPIOA_PIN76 (*(volatile unsigned char*)(GPIOA_BASE+GPIOA_PIN76_offset)) + scrollwheel(GPIOA_PIN76 >> 6); + } #endif } @@ -77,7 +229,6 @@ int button_read_device(void) unsigned gpiod = GPIOD_DATA; unsigned gpioa_dir = GPIOA_DIR; unsigned gpiod6; - get_scrollwheel(); for(delay = 500; delay; delay--) nop; CCU_IO &= ~(1<<12); for(delay=8;delay;delay--) nop; diff --git a/firmware/target/arm/as3525/sansa-fuzev2/button-target.h b/firmware/target/arm/as3525/sansa-fuzev2/button-target.h index 14f3db1..6dcd374 100644 --- a/firmware/target/arm/as3525/sansa-fuzev2/button-target.h +++ b/firmware/target/arm/as3525/sansa-fuzev2/button-target.h @@ -31,12 +31,6 @@ void button_init_device(void); bool button_hold(void); int button_read_device(void); unsigned short button_read_dbop(void); -void get_scrollwheel(void); - -#define WHEEL_REPEAT_INTERVAL (HZ/5) -#define WHEEL_COUNTER_DIV 4 -#define ACCEL_INCREMENT 2 -#define ACCEL_SHIFT 2 /* Sandisk Sansa Fuze button codes */ /* Main unit's buttons */ diff --git a/firmware/target/arm/as3525/sd-as3525.c b/firmware/target/arm/as3525/sd-as3525.c index 5ccb659..5b22ef6 100644 --- a/firmware/target/arm/as3525/sd-as3525.c +++ b/firmware/target/arm/as3525/sd-as3525.c @@ -131,6 +131,7 @@ bool sd_enabled = false; #if defined(HAVE_MULTIDRIVE) static bool hs_card = false; +#define EXT_SD_BITS (1<<2) #endif static struct wakeup transfer_completion_signal; @@ -176,11 +177,11 @@ static int sd1_oneshot_callback(struct timeout *tmo) return 0; } -void INT_GPIOA(void) +void sd_gpioa_irq_handler(void) { static struct timeout sd1_oneshot; /* acknowledge interrupt */ - GPIOA_IC = (1<<2); + GPIOA_IC = EXT_SD_BITS; timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0); } #endif /* HAVE_HOTSWAP */ @@ -483,15 +484,12 @@ static void init_pl180_controller(const int drive) #ifdef HAVE_MULTIDRIVE VIC_INT_ENABLE = (drive == INTERNAL_AS3525) ? INTERRUPT_NAND : INTERRUPT_MCI0; - - /* setup isr for microsd monitoring */ - VIC_INT_ENABLE = (INTERRUPT_GPIOA); /* clear previous irq */ - GPIOA_IC = (1<<2); + GPIOA_IC = EXT_SD_BITS; /* enable edge detecting */ - GPIOA_IS &= ~(1<<2); + GPIOA_IS &= ~EXT_SD_BITS; /* detect both raising and falling edges */ - GPIOA_IBE |= (1<<2); + GPIOA_IBE |= EXT_SD_BITS; #else VIC_INT_ENABLE = INTERRUPT_NAND; @@ -914,9 +912,9 @@ tCardInfo *card_get_info_target(int card_no) void card_enable_monitoring_target(bool on) { if (on) /* enable interrupt */ - GPIOA_IE |= (1<<2); + GPIOA_IE |= EXT_SD_BITS; else /* disable interrupt */ - GPIOA_IE &= ~(1<<2); + GPIOA_IE &= ~EXT_SD_BITS; } #endif /* HAVE_HOTSWAP */ diff --git a/firmware/target/arm/as3525/sd-as3525v2.c b/firmware/target/arm/as3525/sd-as3525v2.c index 4ca5fbc..5d838a2 100644 --- a/firmware/target/arm/as3525/sd-as3525v2.c +++ b/firmware/target/arm/as3525/sd-as3525v2.c @@ -342,6 +342,7 @@ static volatile bool retry; #if defined(HAVE_MULTIDRIVE) int active_card = 0; +#define EXT_SD_BITS (1<<2) #endif static inline void mci_delay(void) { int i = 0xffff; while(i--) ; } @@ -686,14 +687,12 @@ int sd_init(void) wakeup_init(&transfer_completion_signal); #ifdef HAVE_MULTIDRIVE - /* setup isr for microsd monitoring */ - VIC_INT_ENABLE = (INTERRUPT_GPIOA); /* clear previous irq */ - GPIOA_IC = (1<<2); + GPIOA_IC = EXT_SD_BITS; /* enable edge detecting */ - GPIOA_IS &= ~(1<<2); + GPIOA_IS &= ~EXT_SD_BITS; /* detect both raising and falling edges */ - GPIOA_IBE |= (1<<2); + GPIOA_IBE |= EXT_SD_BITS; #endif VIC_INT_ENABLE = INTERRUPT_NAND; @@ -959,20 +958,20 @@ static int sd1_oneshot_callback(struct timeout *tmo) return 0; } -void INT_GPIOA(void) +void sd_gpioa_irq_handler(void) { static struct timeout sd1_oneshot; /* acknowledge interrupt */ - GPIOA_IC = (1<<2); + GPIOA_IC = EXT_SD_BITS; timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0); } void card_enable_monitoring_target(bool on) { if (on) /* enable interrupt */ - GPIOA_IE |= (1<<2); + GPIOA_IE |= EXT_SD_BITS; else /* disable interrupt */ - GPIOA_IE &= ~(1<<2); + GPIOA_IE &= ~EXT_SD_BITS; } #endif /* HAVE_HOTSWAP */ diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c index 0d91d9c..b41260f 100644 --- a/firmware/target/arm/as3525/system-as3525.c +++ b/firmware/target/arm/as3525/system-as3525.c @@ -70,7 +70,8 @@ default_interrupt(RESERVED6); /* Interrupt 25 : unused */ default_interrupt(RESERVED7); /* Interrupt 26 : unused */ default_interrupt(RESERVED8); /* Interrupt 27 : unused */ default_interrupt(RESERVED9); /* Interrupt 28 : unused */ -default_interrupt(INT_GPIOA); +/* INT_GPIOA is declared in this file */ +void INT_GPIOA(void); default_interrupt(INT_GPIOB); default_interrupt(INT_GPIOC); @@ -144,6 +145,18 @@ static void setup_vic(void) } } +void INT_GPIOA(void) +{ +#ifdef HAVE_MULTIDRIVE + void sd_gpioa_irq_handler(void); + sd_gpioa_irq_handler(); +#endif +#if (defined(HAVE_SCROLLWHEEL) && CONFIG_CPU != AS3525) + void button_gpioa_irq_handler(void); + button_gpioa_irq_handler(); +#endif +} + void irq_handler(void) { asm volatile( "stmfd sp!, {r0-r5,ip,lr} \n" /* Store context */ @@ -311,6 +324,12 @@ void system_init(void) ascodec_init(); #ifndef BOOTLOADER + /* setup isr for microsd monitoring and for scrollwheel irq */ +#if defined(HAVE_MULTIDRIVE) || (defined(HAVE_SCROLLWHEEL) && CONFIG_CPU != AS3525) + VIC_INT_ENABLE = (INTERRUPT_GPIOA); + /* pin selection for irq happens in the drivers */ +#endif + /* Initialize power management settings */ ascodec_write(AS3514_CVDD_DCDC3, AS314_CP_DCDC3_SETTING); #if CONFIG_TUNER