From 249aae587a1f35f5df55613c6765ae763db39107 Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Tue, 27 Apr 2010 10:11:52 +0000 Subject: FS#11172 - Fuzev2: Read the scrollwheel scrollwheel via IRQ git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25736 a1c6a512-1295-4272-9138-f99709370657 --- firmware/target/arm/as3525/kernel-as3525.c | 2 +- .../target/arm/as3525/sansa-fuzev2/button-fuzev2.c | 192 +++++++++++++++++++-- .../target/arm/as3525/sansa-fuzev2/button-target.h | 6 - firmware/target/arm/as3525/sd-as3525.c | 21 ++- firmware/target/arm/as3525/sd-as3525v2.c | 20 +-- firmware/target/arm/as3525/system-as3525.c | 21 ++- firmware/target/arm/as3525/system-target.h | 6 +- 7 files changed, 226 insertions(+), 42 deletions(-) (limited to 'firmware/target/arm/as3525') diff --git a/firmware/target/arm/as3525/kernel-as3525.c b/firmware/target/arm/as3525/kernel-as3525.c index 4ae1a03809..ff489a86e8 100644 --- a/firmware/target/arm/as3525/kernel-as3525.c +++ b/firmware/target/arm/as3525/kernel-as3525.c @@ -24,7 +24,7 @@ #include "panic.h" #include "timer.h" -#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 db08414ae5..9421076cce 100644 --- a/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c +++ b/firmware/target/arm/as3525/sansa-fuzev2/button-fuzev2.c @@ -24,37 +24,198 @@ #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_TICK (TIMER_FREQ/HZ) /* how long a tick lasts */ +#define TIMER_MS (TIMER_TICK/(1000/HZ))/* how long a ms lasts */ + +#define WHEEL_REPEAT_INTERVAL (300*TIMER_MS) /* 300ms */ +#define WHEEL_FAST_ON_INTERVAL ( 20*TIMER_MS) /* 20ms */ +#define WHEEL_FAST_OFF_INTERVAL ( 60*TIMER_MS) /* 60ms */ +/* 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) + +/* + * based on button-e200.c, adjusted to the AMS timers and fuzev2's + * scrollwheel and cleaned up a little + */ +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 6 and 7 of GPIOA 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 */ + }; + + 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_TICK; /* to timer unit */ + long v = (time - last_wheel_post); + + /* interpolate velocity in timer_freq/timer_unit == 1/s */ + if (v) v = TIMER_FREQ / v; + + /* accumulate velocities over time with each v */ + 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; + + last_wheel_post = time; + + 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; + } + } + + old_wheel_value = wheel_value; +} +#endif + void button_init_device(void) { +#if defined(HAVE_SCROLLWHEEL) GPIOA_DIR &= ~(1<<6|1<<7); GPIOC_DIR = 0; GPIOB_DIR |= (1<<4)|(1<<0); GPIOB_PIN(4) = 1<<4; /* activate the wheel */ -} -void get_scrollwheel(void) -{ -#if defined(HAVE_SCROLLWHEEL) && !defined(BOOTLOADER) - /* scroll wheel handling */ + /* 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 +} + /* 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); +void button_gpioa_isr(void) +{ +#if defined(HAVE_SCROLLWHEEL) + /* scroll wheel handling */ + if (GPIOA_MIS & SCROLLWHEEL_BITS) + scrollwheel(GPIOA_PIN76 >> 6); + + /* ack interrupt */ + GPIOA_IC = SCROLLWHEEL_BITS; #endif } + /* * Get button pressed from hardware */ - - int button_read_device(void) { int btn = 0; @@ -62,12 +223,12 @@ int button_read_device(void) static long power_counter = 0; unsigned gpiod6; - /* if we don't wait for the fifo to empty, we'll see screen corruption * (the higher the CPU frequency the higher the corruption) */ while ((DBOP_STAT & (1<<10)) == 0); - get_scrollwheel(); + int delay = 30; + while(delay--) nop; CCU_IO &= ~(1<<12); @@ -77,6 +238,7 @@ int button_read_device(void) gpiod6 = GPIOD_PIN(6); GPIOB_PIN(0) = 0; + udelay(1); if (GPIOC_PIN(1) & 1<<1) @@ -114,6 +276,12 @@ int button_read_device(void) { hold_button_old = hold_button; backlight_hold_changed(hold_button); + /* mask scrollwheel irq so we don't need to check for + * the hold button in the isr */ + if (hold_button) + GPIOA_IE &= ~SCROLLWHEEL_BITS; + else + GPIOA_IE |= SCROLLWHEEL_BITS; } #else (void)hold_button_old; diff --git a/firmware/target/arm/as3525/sansa-fuzev2/button-target.h b/firmware/target/arm/as3525/sansa-fuzev2/button-target.h index d7ef9623f9..c64c68f951 100644 --- a/firmware/target/arm/as3525/sansa-fuzev2/button-target.h +++ b/firmware/target/arm/as3525/sansa-fuzev2/button-target.h @@ -30,12 +30,6 @@ void button_init_device(void); bool button_hold(void); int button_read_device(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 1cf7e51cf8..9c3ff0b5fe 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; @@ -172,12 +173,13 @@ static int sd1_oneshot_callback(struct timeout *tmo) return 0; } -void INT_GPIOA(void) +void sd_gpioa_isr(void) { static struct timeout sd1_oneshot; + if (GPIOA_MIS & EXT_SD_BITS) + timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0); /* acknowledge interrupt */ - GPIOA_IC = (1<<2); - timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0); + GPIOA_IC = EXT_SD_BITS; } #endif /* HAVE_HOTSWAP */ @@ -479,15 +481,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; @@ -910,9 +909,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 2f263f7378..65fc13feae 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) { udelay(1000); } @@ -687,14 +688,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; /* Configure XPD for SD-MCI interface */ CCU_IO |= (1<<2); #endif @@ -961,20 +960,21 @@ static int sd1_oneshot_callback(struct timeout *tmo) return 0; } -void INT_GPIOA(void) +void sd_gpioa_isr(void) { static struct timeout sd1_oneshot; + if (GPIOA_MIS & EXT_SD_BITS) + timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0); /* acknowledge interrupt */ - GPIOA_IC = (1<<2); - timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0); + GPIOA_IC = EXT_SD_BITS; } 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 0b1884aa16..e3e41998ce 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_isr(void); + sd_gpioa_isr(); +#endif +#if (defined(HAVE_SCROLLWHEEL) && CONFIG_CPU != AS3525) + void button_gpioa_isr(void); + button_gpioa_isr(); +#endif +} + void irq_handler(void) { asm volatile( "stmfd sp!, {r0-r5,ip,lr} \n" /* Store context */ @@ -348,6 +361,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 diff --git a/firmware/target/arm/as3525/system-target.h b/firmware/target/arm/as3525/system-target.h index 1ccd3282db..d2cf99499d 100644 --- a/firmware/target/arm/as3525/system-target.h +++ b/firmware/target/arm/as3525/system-target.h @@ -27,7 +27,11 @@ #include "clock-target.h" /* CPUFREQ_* are defined here */ -#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 -- cgit v1.2.3