diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2009-01-23 20:55:17 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2009-01-23 20:55:17 +0000 |
commit | 753b7a90e192dbfd1b268a78e200c103dcca80da (patch) | |
tree | f5b6526fe72e1e2203663d69ac9b34368daedffb | |
parent | 34314b1993c0744922754fab1206413441367aea (diff) | |
download | rockbox-753b7a90e192dbfd1b268a78e200c103dcca80da.tar.gz rockbox-753b7a90e192dbfd1b268a78e200c103dcca80da.zip |
Gigabeat S: Add remote control reading and proper headphone insert detection. We need keymaps! A few were copied straight from Gigabeat F/X just to get things worked out.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19830 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/keymaps/keymap-gigabeat-s.c | 64 | ||||
-rw-r--r-- | firmware/SOURCES | 1 | ||||
-rw-r--r-- | firmware/export/config-gigabeat-s.h | 2 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/button-imx31.c | 31 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/button-target.h | 14 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c | 201 |
6 files changed, 290 insertions, 23 deletions
diff --git a/apps/keymaps/keymap-gigabeat-s.c b/apps/keymaps/keymap-gigabeat-s.c index 32c4f22b5b..d32177ea90 100644 --- a/apps/keymaps/keymap-gigabeat-s.c +++ b/apps/keymaps/keymap-gigabeat-s.c | |||
@@ -309,8 +309,72 @@ static const struct button_mapping button_context_radio[] = { | |||
309 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS) | 309 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS) |
310 | }; | 310 | }; |
311 | 311 | ||
312 | /***************************************************************************** | ||
313 | * Remote control mappings | ||
314 | *****************************************************************************/ | ||
315 | static const struct button_mapping remote_button_context_standard[] = { | ||
316 | { ACTION_STD_PREV, BUTTON_RC_VOL_UP, BUTTON_NONE }, | ||
317 | { ACTION_STD_PREVREPEAT, BUTTON_RC_VOL_UP|BUTTON_REPEAT, BUTTON_NONE }, | ||
318 | { ACTION_STD_NEXT, BUTTON_RC_VOL_DOWN, BUTTON_NONE }, | ||
319 | { ACTION_STD_NEXTREPEAT, BUTTON_RC_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE }, | ||
320 | { ACTION_STD_CANCEL, BUTTON_RC_REW, BUTTON_NONE }, | ||
321 | { ACTION_STD_OK, BUTTON_RC_FF|BUTTON_REL, BUTTON_RC_FF }, | ||
322 | { ACTION_STD_CONTEXT, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_RC_FF }, | ||
323 | { ACTION_STD_MENU, BUTTON_RC_DSP, BUTTON_NONE }, | ||
324 | |||
325 | LAST_ITEM_IN_LIST | ||
326 | }; | ||
327 | |||
328 | static const struct button_mapping remote_button_context_wps[] = { | ||
329 | { ACTION_WPS_PLAY, BUTTON_RC_PLAY, BUTTON_NONE }, | ||
330 | |||
331 | { ACTION_WPS_SKIPNEXT, BUTTON_RC_FF|BUTTON_REL, BUTTON_RC_FF }, | ||
332 | { ACTION_WPS_SKIPPREV, BUTTON_RC_REW|BUTTON_REL, BUTTON_RC_REW }, | ||
333 | |||
334 | { ACTION_WPS_SEEKBACK, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE }, | ||
335 | { ACTION_WPS_SEEKFWD, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE }, | ||
336 | { ACTION_WPS_STOPSEEK, BUTTON_RC_REW|BUTTON_REL, BUTTON_RC_REW|BUTTON_REPEAT }, | ||
337 | { ACTION_WPS_STOPSEEK, BUTTON_RC_FF|BUTTON_REL, BUTTON_RC_FF|BUTTON_REPEAT }, | ||
338 | |||
339 | { ACTION_WPS_STOP, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_RC_PLAY }, | ||
340 | { ACTION_WPS_MENU, BUTTON_RC_DSP, BUTTON_NONE }, | ||
341 | |||
342 | { ACTION_WPS_VOLDOWN, BUTTON_RC_VOL_DOWN, BUTTON_NONE }, | ||
343 | { ACTION_WPS_VOLDOWN, BUTTON_RC_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE }, | ||
344 | { ACTION_WPS_VOLUP, BUTTON_RC_VOL_UP|BUTTON_REPEAT, BUTTON_NONE }, | ||
345 | { ACTION_WPS_VOLUP, BUTTON_RC_VOL_UP, BUTTON_NONE }, | ||
346 | |||
347 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) | ||
348 | }; | ||
349 | |||
350 | static const struct button_mapping remote_button_context_tree[] = { | ||
351 | { ACTION_TREE_WPS, BUTTON_RC_PLAY|BUTTON_REL, BUTTON_RC_PLAY }, | ||
352 | { ACTION_TREE_STOP, BUTTON_RC_PLAY|BUTTON_REPEAT, BUTTON_RC_PLAY }, | ||
353 | { ACTION_STD_CANCEL, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE }, | ||
354 | |||
355 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) | ||
356 | }; | ||
357 | |||
358 | static const struct button_mapping* get_context_mapping_remote( int context ) | ||
359 | { | ||
360 | context ^= CONTEXT_REMOTE; | ||
361 | |||
362 | switch (context) | ||
363 | { | ||
364 | case CONTEXT_WPS: | ||
365 | return remote_button_context_wps; | ||
366 | case CONTEXT_MAINMENU: | ||
367 | case CONTEXT_TREE: | ||
368 | return remote_button_context_tree; | ||
369 | } | ||
370 | return remote_button_context_standard; | ||
371 | } | ||
372 | |||
312 | const struct button_mapping* get_context_mapping(int context) | 373 | const struct button_mapping* get_context_mapping(int context) |
313 | { | 374 | { |
375 | if (context&CONTEXT_REMOTE) | ||
376 | return get_context_mapping_remote(context); | ||
377 | |||
314 | switch (context) | 378 | switch (context) |
315 | { | 379 | { |
316 | case CONTEXT_STD: | 380 | case CONTEXT_STD: |
diff --git a/firmware/SOURCES b/firmware/SOURCES index bce384e1c8..c8c38ceee2 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES | |||
@@ -774,6 +774,7 @@ target/arm/imx31/gigabeat-s/wmcodec-imx31.c | |||
774 | #ifndef BOOTLOADER | 774 | #ifndef BOOTLOADER |
775 | target/arm/imx31/gigabeat-s/audio-gigabeat-s.c | 775 | target/arm/imx31/gigabeat-s/audio-gigabeat-s.c |
776 | target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c | 776 | target/arm/imx31/gigabeat-s/fmradio-i2c-gigabeat-s.c |
777 | target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c | ||
777 | target/arm/imx31/gigabeat-s/pcm-imx31.c | 778 | target/arm/imx31/gigabeat-s/pcm-imx31.c |
778 | target/arm/imx31/gigabeat-s/timer-imx31.c | 779 | target/arm/imx31/gigabeat-s/timer-imx31.c |
779 | #endif | 780 | #endif |
diff --git a/firmware/export/config-gigabeat-s.h b/firmware/export/config-gigabeat-s.h index 9e6029f00b..93068ae441 100644 --- a/firmware/export/config-gigabeat-s.h +++ b/firmware/export/config-gigabeat-s.h | |||
@@ -140,7 +140,7 @@ | |||
140 | #define GPIO_EVENT_MASK (USE_GPIO1_EVENTS) | 140 | #define GPIO_EVENT_MASK (USE_GPIO1_EVENTS) |
141 | 141 | ||
142 | /* Define this if target has an additional number of threads specific to it */ | 142 | /* Define this if target has an additional number of threads specific to it */ |
143 | #define TARGET_EXTRA_THREADS 1 | 143 | #define TARGET_EXTRA_THREADS 2 |
144 | 144 | ||
145 | /* Type of mobile power - check this out */ | 145 | /* Type of mobile power - check this out */ |
146 | #define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */ | 146 | #define BATTERY_CAPACITY_DEFAULT 700 /* default battery capacity */ |
diff --git a/firmware/target/arm/imx31/gigabeat-s/button-imx31.c b/firmware/target/arm/imx31/gigabeat-s/button-imx31.c index 587e66e0bc..dec0aa108f 100644 --- a/firmware/target/arm/imx31/gigabeat-s/button-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/button-imx31.c | |||
@@ -32,9 +32,6 @@ | |||
32 | 32 | ||
33 | /* Most code in here is taken from the Linux BSP provided by Freescale | 33 | /* Most code in here is taken from the Linux BSP provided by Freescale |
34 | * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. */ | 34 | * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved. */ |
35 | #ifdef HAVE_HEADPHONE_DETECTION | ||
36 | static bool headphones_detect = false; | ||
37 | #endif | ||
38 | static uint32_t int_btn = BUTTON_NONE; | 35 | static uint32_t int_btn = BUTTON_NONE; |
39 | static bool hold_button = false; | 36 | static bool hold_button = false; |
40 | #ifdef BOOTLOADER | 37 | #ifdef BOOTLOADER |
@@ -123,6 +120,16 @@ bool button_hold(void) | |||
123 | return _button_hold(); | 120 | return _button_hold(); |
124 | } | 121 | } |
125 | 122 | ||
123 | #ifdef HAVE_HEADPHONE_DETECTION | ||
124 | /* Headphone driver pushes the data here */ | ||
125 | void button_headphone_set(int button) | ||
126 | { | ||
127 | int oldstatus = disable_irq_save(); | ||
128 | int_btn = (int_btn & ~BUTTON_REMOTE) | button; | ||
129 | restore_irq(oldstatus); | ||
130 | } | ||
131 | #endif | ||
132 | |||
126 | int button_read_device(void) | 133 | int button_read_device(void) |
127 | { | 134 | { |
128 | /* Simple poll of GPIO status */ | 135 | /* Simple poll of GPIO status */ |
@@ -168,21 +175,6 @@ void button_power_event(void) | |||
168 | restore_irq(oldlevel); | 175 | restore_irq(oldlevel); |
169 | } | 176 | } |
170 | 177 | ||
171 | #ifdef HAVE_HEADPHONE_DETECTION | ||
172 | /* This is called from the mc13783 interrupt thread */ | ||
173 | void headphone_detect_event(void) | ||
174 | { | ||
175 | /* FIXME: Not really the correct method */ | ||
176 | headphones_detect = | ||
177 | (mc13783_read(MC13783_INTERRUPT_SENSE1) & MC13783_ONOFD2S) == 0; | ||
178 | } | ||
179 | |||
180 | bool headphones_inserted(void) | ||
181 | { | ||
182 | return headphones_detect; | ||
183 | } | ||
184 | #endif /* HAVE_HEADPHONE_DETECTION */ | ||
185 | |||
186 | void button_init_device(void) | 178 | void button_init_device(void) |
187 | { | 179 | { |
188 | #ifdef BOOTLOADER | 180 | #ifdef BOOTLOADER |
@@ -223,8 +215,7 @@ void button_init_device(void) | |||
223 | mc13783_enable_event(MC13783_ONOFD1_EVENT); | 215 | mc13783_enable_event(MC13783_ONOFD1_EVENT); |
224 | 216 | ||
225 | #ifdef HAVE_HEADPHONE_DETECTION | 217 | #ifdef HAVE_HEADPHONE_DETECTION |
226 | headphone_detect_event(); | 218 | headphone_init(); |
227 | mc13783_enable_event(MC13783_ONOFD2_EVENT); | ||
228 | #endif | 219 | #endif |
229 | } | 220 | } |
230 | 221 | ||
diff --git a/firmware/target/arm/imx31/gigabeat-s/button-target.h b/firmware/target/arm/imx31/gigabeat-s/button-target.h index 754694eee5..d970e9983c 100644 --- a/firmware/target/arm/imx31/gigabeat-s/button-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/button-target.h | |||
@@ -37,6 +37,8 @@ int button_read_device(void); | |||
37 | void button_power_event(void); | 37 | void button_power_event(void); |
38 | void headphone_detect_event(void); | 38 | void headphone_detect_event(void); |
39 | bool headphones_inserted(void); | 39 | bool headphones_inserted(void); |
40 | void headphone_init(void); | ||
41 | void button_headphone_set(int button); | ||
40 | 42 | ||
41 | /* Toshiba Gigabeat S-specific button codes */ | 43 | /* Toshiba Gigabeat S-specific button codes */ |
42 | 44 | ||
@@ -55,9 +57,17 @@ bool headphones_inserted(void); | |||
55 | #define BUTTON_NEXT (1 << 11) | 57 | #define BUTTON_NEXT (1 << 11) |
56 | #define BUTTON_POWER (1 << 12) /* Read from PMIC */ | 58 | #define BUTTON_POWER (1 << 12) /* Read from PMIC */ |
57 | 59 | ||
58 | #define BUTTON_MAIN (0x1fff) | 60 | #define BUTTON_MAIN (0x00001fff) |
59 | 61 | ||
60 | #define BUTTON_REMOTE 0 | 62 | /* Remote control buttons */ |
63 | #define BUTTON_RC_VOL_UP (1 << 13) | ||
64 | #define BUTTON_RC_VOL_DOWN (1 << 14) | ||
65 | #define BUTTON_RC_FF (1 << 15) | ||
66 | #define BUTTON_RC_REW (1 << 16) | ||
67 | #define BUTTON_RC_PLAY (1 << 17) | ||
68 | #define BUTTON_RC_DSP (1 << 18) | ||
69 | |||
70 | #define BUTTON_REMOTE (0x0007e000) | ||
61 | 71 | ||
62 | #define POWEROFF_BUTTON BUTTON_POWER | 72 | #define POWEROFF_BUTTON BUTTON_POWER |
63 | #define POWEROFF_COUNT 10 | 73 | #define POWEROFF_COUNT 10 |
diff --git a/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c new file mode 100644 index 0000000000..6043d00bf5 --- /dev/null +++ b/firmware/target/arm/imx31/gigabeat-s/headphone-gigabeat-s.c | |||
@@ -0,0 +1,201 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (c) 2009 by Michael Sevakis | ||
11 | * | ||
12 | * Driver to handle headphone jack events | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version 2 | ||
17 | * of the License, or (at your option) any later version. | ||
18 | * | ||
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
20 | * KIND, either express or implied. | ||
21 | * | ||
22 | ****************************************************************************/ | ||
23 | #include "config.h" | ||
24 | #include "system.h" | ||
25 | #include "kernel.h" | ||
26 | #include "thread.h" | ||
27 | #include "mc13783.h" | ||
28 | #include "mc13783-target.h" | ||
29 | #include "adc.h" | ||
30 | #include "button.h" | ||
31 | |||
32 | static struct wakeup headphone_wakeup; | ||
33 | static unsigned int headphone_thread_id; | ||
34 | static int headphone_stack[160/sizeof(int)]; /* Not much stack needed */ | ||
35 | static const char * const headphone_thread_name = "headphone"; | ||
36 | static bool headphones_detect = false; | ||
37 | |||
38 | /* Convert ADC reading into a button value. */ | ||
39 | static int adc_data_to_button(unsigned int data) | ||
40 | { | ||
41 | int btn = BUTTON_NONE; | ||
42 | |||
43 | if (data < 505) | ||
44 | { | ||
45 | if (data < 252) | ||
46 | { | ||
47 | if (data < 149) | ||
48 | { | ||
49 | if (data >= 64) | ||
50 | { | ||
51 | /* Play/Pause */ | ||
52 | btn = BUTTON_RC_PLAY; | ||
53 | } | ||
54 | /* else headphone direct */ | ||
55 | } | ||
56 | else | ||
57 | { | ||
58 | /* DSP */ | ||
59 | btn = BUTTON_RC_DSP; | ||
60 | } | ||
61 | } | ||
62 | else | ||
63 | { | ||
64 | if (data < 370) | ||
65 | { | ||
66 | /* RW */ | ||
67 | btn = BUTTON_RC_REW; | ||
68 | } | ||
69 | else | ||
70 | { | ||
71 | /* FF */ | ||
72 | btn = BUTTON_RC_FF; | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | else | ||
77 | { | ||
78 | if (data < 870) | ||
79 | { | ||
80 | if (data < 675) | ||
81 | { | ||
82 | /* Vol + */ | ||
83 | btn = BUTTON_RC_VOL_UP; | ||
84 | } | ||
85 | else | ||
86 | { | ||
87 | /* Vol - */ | ||
88 | btn = BUTTON_RC_VOL_DOWN; | ||
89 | } | ||
90 | } | ||
91 | #if 0 | ||
92 | else | ||
93 | { | ||
94 | |||
95 | if (data < 951) | ||
96 | { | ||
97 | /* No buttons */ | ||
98 | } | ||
99 | else | ||
100 | { | ||
101 | /* Not inserted */ | ||
102 | |||
103 | } | ||
104 | } | ||
105 | #endif | ||
106 | } | ||
107 | |||
108 | return btn; | ||
109 | } | ||
110 | |||
111 | static void headphone_thread(void) | ||
112 | { | ||
113 | int headphone_sleep_countdown = 0; | ||
114 | int headphone_wait_timeout = TIMEOUT_BLOCK; | ||
115 | |||
116 | while (1) | ||
117 | { | ||
118 | int rc = wakeup_wait(&headphone_wakeup, headphone_wait_timeout); | ||
119 | unsigned int data = adc_read(ADC_HPREMOTE); | ||
120 | |||
121 | if (rc == OBJ_WAIT_TIMEDOUT) | ||
122 | { | ||
123 | if (headphone_sleep_countdown <= 0) | ||
124 | { | ||
125 | /* Polling ADC */ | ||
126 | int btn, btn2; | ||
127 | |||
128 | btn = adc_data_to_button(data); | ||
129 | sleep(HZ/50); | ||
130 | data = adc_read(ADC_HPREMOTE); | ||
131 | btn2 = adc_data_to_button(data); | ||
132 | |||
133 | if (btn != btn2) | ||
134 | { | ||
135 | /* If the buttons dont agree twice in a row, then it's | ||
136 | * none (from meg-fx remote reader). */ | ||
137 | btn = BUTTON_NONE; | ||
138 | } | ||
139 | |||
140 | button_headphone_set(btn); | ||
141 | continue; | ||
142 | } | ||
143 | |||
144 | if (--headphone_sleep_countdown == 0) | ||
145 | { | ||
146 | /* Nothing has changed and remote is not present - | ||
147 | * go to sleep. */ | ||
148 | headphone_wait_timeout = TIMEOUT_BLOCK; | ||
149 | continue; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | headphones_detect = data <= 951; /* Max remote value */ | ||
154 | |||
155 | /* Cancel any buttons if jack readings are unstable. */ | ||
156 | button_headphone_set(BUTTON_NONE); | ||
157 | |||
158 | if (data >= 64 && data <= 951) | ||
159 | { | ||
160 | /* Should be a remote control - accelerate */ | ||
161 | headphone_wait_timeout = HZ/20-HZ/50; | ||
162 | headphone_sleep_countdown = 0; | ||
163 | } | ||
164 | else if (rc == OBJ_WAIT_SUCCEEDED) | ||
165 | { | ||
166 | /* Got signaled - something is being plugged/unplugged. Set | ||
167 | * countdown until we just give up and go to sleep (~10s). */ | ||
168 | headphone_wait_timeout = HZ/2; | ||
169 | headphone_sleep_countdown = 10*2; | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /* This is called from the mc13783 interrupt thread */ | ||
175 | void headphone_detect_event(void) | ||
176 | { | ||
177 | /* Trigger the thread immediately. */ | ||
178 | wakeup_signal(&headphone_wakeup); | ||
179 | } | ||
180 | |||
181 | /* Tell if anything is in the jack. */ | ||
182 | bool headphones_inserted(void) | ||
183 | { | ||
184 | return headphones_detect; | ||
185 | } | ||
186 | |||
187 | void headphone_init(void) | ||
188 | { | ||
189 | /* A thread is required to monitor the remote ADC and jack state. */ | ||
190 | wakeup_init(&headphone_wakeup); | ||
191 | headphone_thread_id = create_thread(headphone_thread, | ||
192 | headphone_stack, | ||
193 | sizeof(headphone_stack), | ||
194 | 0, headphone_thread_name | ||
195 | IF_PRIO(, PRIORITY_REALTIME) | ||
196 | IF_COP(, CPU)); | ||
197 | |||
198 | /* Initially poll and then enable PMIC event */ | ||
199 | headphone_detect_event(); | ||
200 | mc13783_enable_event(MC13783_ONOFD2_EVENT); | ||
201 | } | ||