From 8bf079ffc148e44cfebd4c8cd1c73e778aae45fa Mon Sep 17 00:00:00 2001 From: Thom Johansen Date: Sat, 17 Dec 2005 19:08:55 +0000 Subject: Button and click wheel driver for iPod 4g and Nano. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8256 a1c6a512-1295-4272-9138-f99709370657 --- firmware/drivers/button.c | 152 +++++++++++++++++++++++++++++++++++++++++++++- firmware/export/pp5020.h | 34 ++++++++++- firmware/system.c | 3 + 3 files changed, 185 insertions(+), 4 deletions(-) diff --git a/firmware/drivers/button.c b/firmware/drivers/button.c index b9e5043..307b424 100644 --- a/firmware/drivers/button.c +++ b/firmware/drivers/button.c @@ -63,6 +63,143 @@ static int button_read(void); static bool remote_button_hold_only(void); #endif +#if CONFIG_KEYPAD == IPOD_4G_PAD || CONFIG_KEYPAD == IPOD_NANO_PAD +/* Variable to use for setting button status in interrupt handler */ +int int_btn = BUTTON_NONE; + +static void opto_i2c_init(void) +{ + int i, curr_value; + + /* wait for value to settle */ + i = 1000; + curr_value = (inl(0x7000c104) << 16) >> 24; + while (i > 0) + { + int new_value = (inl(0x7000c104) << 16) >> 24; + + if (new_value != curr_value) { + i = 10000; + curr_value = new_value; + } + else { + i--; + } + } + + GPIOB_OUTPUT_VAL |= 0x10; + DEV_EN |= 0x10000; + DEV_RS |= 0x10000; + udelay(5); + DEV_RS &= ~0x10000; /* finish reset */ + + outl(0xffffffff, 0x7000c120); + outl(0xffffffff, 0x7000c124); + outl(0xc00a1f00, 0x7000c100); + outl(0x1000000, 0x7000c104); +} + +static int ipod_4g_button_read(void) +{ + unsigned reg, status; + static int clickwheel_down = 0; + static int old_wheel_value = -1; + int wheel_keycode = BUTTON_NONE; + int wheel_delta, wheel_delta_abs; + int new_wheel_value; + int btn = BUTTON_NONE; + + udelay(250); + reg = 0x7000c104; + if ((inl(0x7000c104) & 0x4000000) != 0) { + reg = reg + 0x3C; /* 0x7000c140 */ + + status = inl(0x7000c140); + outl(0x0, 0x7000c140); /* clear interrupt status? */ + + if ((status & 0x800000ff) == 0x8000001a) { + /* NB: highest wheel = 0x5F, clockwise increases */ + new_wheel_value = ((status << 9) >> 25) & 0xff; + + if (status & 0x100) + btn |= BUTTON_SELECT; + if (status & 0x200) + btn |= BUTTON_RIGHT; + if (status & 0x400) + btn |= BUTTON_LEFT; + if (status & 0x800) + btn |= BUTTON_PLAY; + if (status & 0x1000) + btn |= BUTTON_MENU; + if (status & 0x40000000) { + /* scroll wheel down */ + clickwheel_down = 1; + backlight_on(); + if (old_wheel_value != -1) { + wheel_delta = new_wheel_value - old_wheel_value; + wheel_delta_abs = wheel_delta < 0 ? -wheel_delta : wheel_delta; + + wheel_delta = new_wheel_value - old_wheel_value; + + /* TODO: these thresholds should most definitely be + settings, and we're probably going to want a more + advanced scheme than this anyway. */ + if (wheel_delta > 4) { + wheel_keycode = BUTTON_SCROLL_FWD; + old_wheel_value = new_wheel_value; + } else if (wheel_delta < -4) { + wheel_keycode = BUTTON_SCROLL_BACK; + old_wheel_value = new_wheel_value; + } + + if (wheel_keycode != BUTTON_NONE) + queue_post(&button_queue, wheel_keycode, NULL); + } + else { + old_wheel_value = new_wheel_value; + } + } + else if (clickwheel_down) { + /* scroll wheel up */ + old_wheel_value = -1; + clickwheel_down = 0; + } + } + /* + Don't know why this should be needed, let me know if you do. + else if ((status & 0x800000FF) == 0x8000003A) { + wheel_value = status & 0x800000FF; + } + */ + else if (status == 0xffffffff) { + opto_i2c_init(); + } + } + + if ((inl(reg) & 0x8000000) != 0) { + outl(0xffffffff, 0x7000c120); + outl(0xffffffff, 0x7000c124); + } + return btn; +} + + + +void ipod_4g_button_int(void) +{ + PP5020_CPU_HI_INT_CLR = PP5020_I2C_MASK; + udelay(250); + outl(0x0, 0x7000c140); + int_btn = ipod_4g_button_read(); + outl(inl(0x7000c104) | 0xC000000, 0x7000c104); + outl(0x400a1f00, 0x7000c100); + + GPIOB_OUTPUT_VAL |= 0x10; + PP5020_CPU_INT_EN = 0x40000000; + PP5020_CPU_HI_INT_EN = PP5020_I2C_MASK; +} +#endif + static void button_tick(void) { static int tick = 0; @@ -234,6 +371,18 @@ void button_init(void) /* nothing to initialize here */ #elif CONFIG_KEYPAD == GMINI100_PAD /* nothing to initialize here */ +#elif CONFIG_KEYPAD == IPOD_4G_PAD || CONFIG_KEYPAD == IPOD_NANO_PAD + opto_i2c_init(); + /* hold button - enable as input */ + GPIOA_ENABLE |= 0x20; + GPIOA_OUTPUT_EN &= ~0x20; + /* hold button - set interrupt levels */ + GPIOA_INT_LEV = ~(GPIOA_INPUT_VAL & 0x20); + GPIOA_INT_CLR = GPIOA_INT_STAT & 0x20; + /* enable interrupts */ + GPIOA_INT_EN = 0x20; + PP5020_CPU_INT_EN = 0x40000000; + PP5020_CPU_HI_INT_EN = PP5020_I2C_MASK; #endif /* CONFIG_KEYPAD */ queue_init(&button_queue); @@ -673,8 +822,9 @@ static int button_read(void) btn |= BUTTON_ON; #elif CONFIG_KEYPAD == IPOD_4G_PAD || CONFIG_KEYPAD == IPOD_NANO_PAD - /* TODO: Implement for iPod */ (void)data; + /* The int_btn variable is set in the button interrupt handler */ + btn = int_btn; #endif /* CONFIG_KEYPAD */ diff --git a/firmware/export/pp5020.h b/firmware/export/pp5020.h index 3baa9bd..d2dbffc 100644 --- a/firmware/export/pp5020.h +++ b/firmware/export/pp5020.h @@ -26,12 +26,36 @@ #define GPIOC_ENABLE (*(volatile unsigned long *)(0x6000d008)) #define GPIOD_ENABLE (*(volatile unsigned long *)(0x6000d00c)) #define GPIOA_OUTPUT_EN (*(volatile unsigned long *)(0x6000d010)) +#define GPIOB_OUTPUT_EN (*(volatile unsigned long *)(0x6000d014)) +#define GPIOC_OUTPUT_EN (*(volatile unsigned long *)(0x6000d018)) +#define GPIOD_OUTPUT_EN (*(volatile unsigned long *)(0x6000d01c)) #define GPIOA_OUTPUT_VAL (*(volatile unsigned long *)(0x6000d020)) +#define GPIOB_OUTPUT_VAL (*(volatile unsigned long *)(0x6000d024)) +#define GPIOC_OUTPUT_VAL (*(volatile unsigned long *)(0x6000d028)) +#define GPIOD_OUTPUT_VAL (*(volatile unsigned long *)(0x6000d02c)) #define GPIOA_INPUT_VAL (*(volatile unsigned long *)(0x6000d030)) +#define GPIOB_INPUT_VAL (*(volatile unsigned long *)(0x6000d034)) +#define GPIOC_INPUT_VAL (*(volatile unsigned long *)(0x6000d038)) +#define GPIOD_INPUT_VAL (*(volatile unsigned long *)(0x6000d03c)) #define GPIOA_INT_STAT (*(volatile unsigned long *)(0x6000d040)) +#define GPIOB_INT_STAT (*(volatile unsigned long *)(0x6000d044)) +#define GPIOC_INT_STAT (*(volatile unsigned long *)(0x6000d048)) +#define GPIOD_INT_STAT (*(volatile unsigned long *)(0x6000d04c)) #define GPIOA_INT_EN (*(volatile unsigned long *)(0x6000d050)) +#define GPIOB_INT_EN (*(volatile unsigned long *)(0x6000d054)) +#define GPIOC_INT_EN (*(volatile unsigned long *)(0x6000d058)) +#define GPIOD_INT_EN (*(volatile unsigned long *)(0x6000d05c)) #define GPIOA_INT_LEV (*(volatile unsigned long *)(0x6000d060)) +#define GPIOB_INT_LEV (*(volatile unsigned long *)(0x6000d064)) +#define GPIOC_INT_LEV (*(volatile unsigned long *)(0x6000d068)) +#define GPIOD_INT_LEV (*(volatile unsigned long *)(0x6000d06c)) #define GPIOA_INT_CLR (*(volatile unsigned long *)(0x6000d070)) +#define GPIOB_INT_CLR (*(volatile unsigned long *)(0x6000d074)) +#define GPIOC_INT_CLR (*(volatile unsigned long *)(0x6000d078)) +#define GPIOD_INT_CLR (*(volatile unsigned long *)(0x6000d07c)) + +#define DEV_RS (*(volatile unsigned long *)(0x60006004)) +#define DEV_EN (*(volatile unsigned long *)(0x6000600c)) #define PP5020_TIMER1 (*(volatile unsigned long *)(0x60005000)) #define PP5020_TIMER1_ACK (*(volatile unsigned long *)(0x60005004)) @@ -39,9 +63,13 @@ #define PP5020_TIMER2_ACK (*(volatile unsigned long *)(0x6000500c)) #define PP5020_TIMER_STATUS (*(volatile unsigned long *)(0x60005010)) -#define PP5020_CPU_INT_STAT (*(volatile unsigned long*)(0x64004000)) -#define PP5020_CPU_INT_EN (*(volatile unsigned long*)(0x60004024)) - +#define PP5020_CPU_INT_STAT (*(volatile unsigned long*)(0x64004000)) +#define PP5020_CPU_HI_INT_STAT (*(volatile unsigned long*)(0x64004100)) +#define PP5020_CPU_INT_EN (*(volatile unsigned long*)(0x60004024)) +#define PP5020_CPU_HI_INT_EN (*(volatile unsigned long*)(0x60004124)) +#define PP5020_CPU_INT_CLR (*(volatile unsigned long*)(0x60004028)) +#define PP5020_CPU_HI_INT_CLR (*(volatile unsigned long*)(0x60004128)) + #define PP5020_TIMER1_IRQ 0 #define PP5020_TIMER2_IRQ 1 #define PP5020_I2S_IRQ 10 diff --git a/firmware/system.c b/firmware/system.c index 6ef35e8..2c7c6f3 100644 --- a/firmware/system.c +++ b/firmware/system.c @@ -1107,11 +1107,14 @@ int system_memory_guard(int newmode) #elif CONFIG_CPU==PP5020 extern void TIMER1(void); +extern void ipod_4g_button_int(void); void irq(void) { if (PP5020_CPU_INT_STAT & PP5020_TIMER1_MASK) TIMER1(); + else if (PP5020_CPU_HI_INT_STAT & PP5020_I2C_MASK) + ipod_4g_button_int(); } void system_init(void) -- cgit v1.1