| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2002 by Daniel Stenberg |
| * |
| * iPod driver based on code from the ipodlinux project - http://ipodlinux.org |
| * Adapted for Rockbox in December 2005 |
| * Original file: linux/arch/armnommu/mach-ipod/keyboard.c |
| * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org) |
| * |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| |
| /* |
| * Rockbox button functions |
| */ |
| |
| #include <stdlib.h> |
| #include "config.h" |
| #include "cpu.h" |
| #include "system.h" |
| #include "button.h" |
| #include "kernel.h" |
| #include "backlight.h" |
| #include "adc.h" |
| #include "serial.h" |
| #include "power.h" |
| #include "system.h" |
| #include "powermgmt.h" |
| |
| /* Variable to use for setting button status in interrupt handler */ |
| int int_btn = BUTTON_NONE; |
| #ifdef HAVE_WHEEL_POSITION |
| static int wheel_position = -1; |
| static bool send_events = true; |
| #endif |
| |
| static void handle_scroll_wheel(int new_scroll, int was_hold) |
| { |
| int wheel_keycode = BUTTON_NONE; |
| static int prev_scroll = -1; |
| static int direction = 0; |
| static int count = 0; |
| static int scroll_state[4][4] = { |
| {0, 1, -1, 0}, |
| {-1, 0, 0, 1}, |
| {1, 0, 0, -1}, |
| {0, -1, 1, 0} |
| }; |
| |
| if ( prev_scroll == -1 ) { |
| prev_scroll = new_scroll; |
| } |
| else if (direction != scroll_state[prev_scroll][new_scroll]) { |
| direction = scroll_state[prev_scroll][new_scroll]; |
| count = 0; |
| } |
| else if (!was_hold) { |
| backlight_on(); |
| reset_poweroff_timer(); |
| if (++count == 6) { /* reduce sensitivity */ |
| count = 0; |
| /* Mini 1st Gen wheel has inverse direction mapping |
| * compared to 1st..3rd Gen wheel. */ |
| switch (direction) { |
| case 1: |
| wheel_keycode = BUTTON_SCROLL_FWD; |
| break; |
| case -1: |
| wheel_keycode = BUTTON_SCROLL_BACK; |
| break; |
| default: |
| /* only happens if we get out of sync */ |
| break; |
| } |
| } |
| } |
| if (wheel_keycode != BUTTON_NONE && queue_empty(&button_queue)) |
| queue_post(&button_queue, wheel_keycode, 0); |
| prev_scroll = new_scroll; |
| } |
| |
| /* mini 1 only, mini 2G uses iPod 4G code */ |
| static int ipod_mini_button_read(void) |
| { |
| unsigned char source, wheel_source, state, wheel_state; |
| static bool was_hold = false; |
| int btn = BUTTON_NONE; |
| |
| /* The ipodlinux source had a udelay(250) here, but testing has shown that |
| it is not needed - tested on mini 1g. */ |
| /* udelay(250);*/ |
| |
| /* get source(s) of interupt */ |
| source = GPIOA_INT_STAT & 0x3f; |
| wheel_source = GPIOB_INT_STAT & 0x30; |
| |
| if (source == 0 && wheel_source == 0) { |
| return BUTTON_NONE; /* not for us */ |
| } |
| |
| /* get current keypad & wheel status */ |
| state = GPIOA_INPUT_VAL & 0x3f; |
| wheel_state = GPIOB_INPUT_VAL & 0x30; |
| |
| /* toggle interrupt level */ |
| GPIOA_INT_LEV = ~state; |
| GPIOB_INT_LEV = ~wheel_state; |
| |
| /* hold switch causes all outputs to go low */ |
| /* we shouldn't interpret these as key presses */ |
| if ((state & 0x20)) { |
| if (!(state & 0x1)) |
| btn |= BUTTON_SELECT; |
| if (!(state & 0x2)) |
| btn |= BUTTON_MENU; |
| if (!(state & 0x4)) |
| btn |= BUTTON_PLAY; |
| if (!(state & 0x8)) |
| btn |= BUTTON_RIGHT; |
| if (!(state & 0x10)) |
| btn |= BUTTON_LEFT; |
| |
| if (wheel_source & 0x30) { |
| handle_scroll_wheel((wheel_state & 0x30) >> 4, was_hold); |
| } |
| } |
| |
| was_hold = button_hold(); |
| |
| /* ack any active interrupts */ |
| if (source) |
| GPIOA_INT_CLR = source; |
| if (wheel_source) |
| GPIOB_INT_CLR = wheel_source; |
| |
| return btn; |
| } |
| |
| void ipod_mini_button_int(void) |
| { |
| CPU_HI_INT_DIS = GPIO0_MASK; |
| int_btn = ipod_mini_button_read(); |
| //CPU_INT_EN = 0x40000000; |
| CPU_HI_INT_EN = GPIO0_MASK; |
| } |
| |
| void button_init_device(void) |
| { |
| /* iPod Mini G1 */ |
| /* buttons - enable as input */ |
| GPIOA_ENABLE |= 0x3f; |
| GPIOA_OUTPUT_EN &= ~0x3f; |
| /* scroll wheel- enable as input */ |
| GPIOB_ENABLE |= 0x30; /* port b 4,5 */ |
| GPIOB_OUTPUT_EN &= ~0x30; /* port b 4,5 */ |
| /* buttons - set interrupt levels */ |
| GPIOA_INT_LEV = ~(GPIOA_INPUT_VAL & 0x3f); |
| GPIOA_INT_CLR = GPIOA_INT_STAT & 0x3f; |
| /* scroll wheel - set interrupt levels */ |
| GPIOB_INT_LEV = ~(GPIOB_INPUT_VAL & 0x30); |
| GPIOB_INT_CLR = GPIOB_INT_STAT & 0x30; |
| /* enable interrupts */ |
| GPIOA_INT_EN = 0x3f; |
| GPIOB_INT_EN = 0x30; |
| /* unmask interrupt */ |
| CPU_INT_EN = 0x40000000; |
| CPU_HI_INT_EN = GPIO0_MASK; |
| } |
| |
| /* |
| * Get button pressed from hardware |
| */ |
| int button_read_device(void) |
| { |
| static bool hold_button = false; |
| bool hold_button_old; |
| |
| /* normal buttons */ |
| hold_button_old = hold_button; |
| hold_button = button_hold(); |
| |
| if (hold_button != hold_button_old) |
| backlight_hold_changed(hold_button); |
| |
| /* The int_btn variable is set in the button interrupt handler */ |
| return int_btn; |
| } |
| |
| bool button_hold(void) |
| { |
| return (GPIOA_INPUT_VAL & 0x20)?false:true; |
| } |
| |
| bool headphones_inserted(void) |
| { |
| return (GPIOA_INPUT_VAL & 0x80)?true:false; |
| } |