summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThom Johansen <thomj@rockbox.org>2005-12-17 19:08:55 +0000
committerThom Johansen <thomj@rockbox.org>2005-12-17 19:08:55 +0000
commit8bf079ffc148e44cfebd4c8cd1c73e778aae45fa (patch)
treebe752b266296ecb1bb2582c406a8a397cfc7be5e
parenta601fb8d1922ddd8e7fbb39f8ae2c6137b3a12a5 (diff)
downloadrockbox-8bf079ffc148e44cfebd4c8cd1c73e778aae45fa.zip
rockbox-8bf079ffc148e44cfebd4c8cd1c73e778aae45fa.tar.gz
rockbox-8bf079ffc148e44cfebd4c8cd1c73e778aae45fa.tar.bz2
rockbox-8bf079ffc148e44cfebd4c8cd1c73e778aae45fa.tar.xz
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
-rw-r--r--firmware/drivers/button.c152
-rw-r--r--firmware/export/pp5020.h34
-rw-r--r--firmware/system.c3
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)