summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx233
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx233')
-rw-r--r--firmware/target/arm/imx233/button-imx233.c261
-rw-r--r--firmware/target/arm/imx233/button-imx233.h174
2 files changed, 435 insertions, 0 deletions
diff --git a/firmware/target/arm/imx233/button-imx233.c b/firmware/target/arm/imx233/button-imx233.c
new file mode 100644
index 0000000000..4ebba41ad3
--- /dev/null
+++ b/firmware/target/arm/imx233/button-imx233.c
@@ -0,0 +1,261 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2013 by Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdlib.h>
23
24#include "cpu.h"
25#include "kernel.h"
26#include "button-imx233.h"
27#include "lradc-imx233.h"
28#include "pinctrl-imx233.h"
29#include "power-imx233.h"
30#include "backlight.h"
31
32static int delay_chan = -1; /* delay channel used to trigger */
33static int raw_val[LRADC_NUM_CHANNELS]; /* channel values sampled (last two) */
34static int chan_mask; /* trigger channel mask */
35static int irq_chan_mask; /* triggered channel mask */
36static int src_map[LRADC_NUM_SOURCES]; /* physical -> virtual channel map */
37static int src_mask; /* sampled source mask */
38#ifdef HAS_BUTTON_HOLD
39static int hold_idx = -1; /* index of hold button in map */
40#endif
41#ifdef HAVE_HEADPHONE_DETECTION
42static int jack_idx = -1; /* index of jack detect in map */
43#endif
44
45/* shortcut of button map */
46#define MAP imx233_button_map
47
48/* sample rate for LRADC */
49#define RATE HZ
50/* number of samples per irq */
51#define SAMPLES 10
52/* delay's delay */
53#define DELAY (LRADC_DELAY_FREQ / RATE / SAMPLES)
54
55/* correct value for channel with builtin dividers */
56static int correct_lradc(int src, int raw)
57{
58 if(src == LRADC_SRC_VDDIO)
59 return raw * 2;
60 else
61 return raw;
62}
63
64/* return raw value for the button */
65int imx233_button_read_raw(int idx)
66{
67 if(MAP[idx].periph == IMX233_BUTTON_GPIO)
68 return imx233_pinctrl_get_gpio(MAP[idx].u.gpio.bank, MAP[idx].u.gpio.pin);
69 else if(MAP[idx].periph == IMX233_BUTTON_LRADC)
70 return correct_lradc(MAP[idx].u.lradc.src, raw_val[src_map[MAP[idx].u.lradc.src]]);
71 else if(MAP[idx].periph == IMX233_BUTTON_PSWITCH)
72 return imx233_power_read_pswitch();
73 else
74 return -1;
75}
76
77/* return cooked (interpreted) value for the button, ignoring debouncing */
78static bool imx233_button_read_cooked(int idx)
79{
80 int raw = imx233_button_read_raw(idx);
81 bool res;
82 if(MAP[idx].periph == IMX233_BUTTON_GPIO)
83 {
84 res = raw;
85 }
86 else if(MAP[idx].periph == IMX233_BUTTON_LRADC)
87 {
88 /* correct value in relative mode */
89 int rel = MAP[idx].u.lradc.relative;
90 if(rel != -1)
91 raw = (raw * MAP[rel].u.lradc.value) / imx233_button_read_raw(rel);
92 res = abs(raw - MAP[idx].u.lradc.value) <= 30;
93 }
94 else if(MAP[idx].periph == IMX233_BUTTON_PSWITCH)
95 {
96 res = raw == MAP[idx].u.pswitch.level;
97 }
98 else
99 res = false;
100 /* handle inversion */
101 if(MAP[idx].flags & IMX233_BUTTON_INVERTED)
102 res = !res;
103 return res;
104}
105
106/* finish round */
107static void do_round(void)
108{
109 for(int i = 0; MAP[i].btn != IMX233_BUTTON_END; i++)
110 {
111 bool cooked = imx233_button_read_cooked(i);
112 if(MAP[i].last_val == cooked)
113 MAP[i].rounds = MIN(MAP[i].rounds + 1, MAP[i].threshold);
114 else
115 MAP[i].rounds = 1;
116 MAP[i].last_val = cooked;
117 }
118}
119
120/* process IRQ */
121static void button_lradc_irq(int chan)
122{
123 /* read value */
124 raw_val[chan] = imx233_lradc_read_channel(chan) / SAMPLES;
125 imx233_lradc_clear_channel(chan);
126 imx233_lradc_setup_sampling(chan, true, SAMPLES - 1);
127 /* record irq, trigger delay if all IRQs have been fired */
128 irq_chan_mask |= 1 << chan;
129 if(irq_chan_mask == chan_mask)
130 {
131 irq_chan_mask = 0;
132 do_round();
133 imx233_lradc_setup_delay(delay_chan, chan_mask, 0, SAMPLES - 1, DELAY);
134 imx233_lradc_kick_delay(delay_chan);
135 }
136}
137
138bool imx233_button_read_btn(int idx)
139{
140 return MAP[idx].rounds >= MAP[idx].threshold ? MAP[idx].last_val : false;
141}
142
143int imx233_button_read(int others)
144{
145 int res = others;
146#ifdef HAS_BUTTON_HOLD
147 if(imx233_button_read_hold())
148 return 0;
149#endif
150 for(int i = 0; MAP[i].btn != IMX233_BUTTON_END; i++)
151 {
152 if(MAP[i].btn >= 0 && imx233_button_read_btn(i))
153 res |= MAP[i].btn;
154 }
155 return res;
156}
157
158#ifdef HAS_BUTTON_HOLD
159bool imx233_button_read_hold(void)
160{
161 return imx233_button_read_btn(hold_idx);
162}
163
164bool __attribute__((weak)) button_hold(void)
165{
166 bool hold_button = imx233_button_read_hold();
167#ifndef BOOTLOADER
168 static bool hold_button_old = false;
169 /* light handling */
170 if (hold_button != hold_button_old)
171 {
172 hold_button_old = hold_button;
173 backlight_hold_changed(hold_button);
174 }
175#endif /* BOOTLOADER */
176 return hold_button;
177}
178#endif
179
180#ifdef HAVE_HEADPHONE_DETECTION
181bool imx233_button_read_jack(void)
182{
183 return imx233_button_read_btn(jack_idx);
184}
185
186bool __attribute__((weak)) headphones_inserted(void)
187{
188 return imx233_button_read_jack();
189}
190#endif
191
192/* return number of debouncing rounds by type of peripheral */
193static int threshold_by_periph(int periph)
194{
195 if(periph == IMX233_BUTTON_GPIO) return 1; // no debouncing
196 if(periph == IMX233_BUTTON_LRADC) return 2; // 2 times at HZ gives ~10 ms hold
197 if(periph == IMX233_BUTTON_PSWITCH) return 10; // PSWITCH is very slow to ramp
198 return 1; // other ?
199}
200
201void imx233_button_init(void)
202{
203 /* go through the table and init stuff which needs to be */
204 for(int i = 0; MAP[i].btn != IMX233_BUTTON_END; i++)
205 {
206 MAP[i].threshold = threshold_by_periph(MAP[i].periph);
207 if(MAP[i].periph == IMX233_BUTTON_GPIO)
208 {
209 imx233_pinctrl_acquire(MAP[i].u.gpio.bank, MAP[i].u.gpio.pin, MAP[i].name);
210 imx233_pinctrl_set_function(MAP[i].u.gpio.bank, MAP[i].u.gpio.pin, PINCTRL_FUNCTION_GPIO);
211 imx233_pinctrl_enable_gpio(MAP[i].u.gpio.bank, MAP[i].u.gpio.pin, false);
212 bool pullup = !!(MAP[i].flags & IMX233_BUTTON_PULLUP);
213 imx233_pinctrl_enable_pullup(MAP[i].u.gpio.bank, MAP[i].u.gpio.pin, pullup);
214 }
215 else if(MAP[i].periph == IMX233_BUTTON_LRADC)
216 {
217 int src = MAP[i].u.lradc.src;
218 /* if channel was already acquired, there is nothing to do */
219 if(src_mask & (1 << src))
220 continue;
221 src_map[src] = imx233_lradc_acquire_channel(LRADC_SRC(src), TIMEOUT_NOBLOCK);
222 if(src_map[src] < 0)
223 panicf("Cannot get channel for %s", MAP[i].name);
224 imx233_lradc_setup_source(src_map[src], true, src);
225 imx233_lradc_setup_sampling(src_map[src], true, SAMPLES - 1);
226 imx233_lradc_enable_channel_irq(src_map[src], true);
227 imx233_lradc_set_channel_irq_callback(src_map[src], button_lradc_irq);
228 src_mask |= 1 << src;
229 chan_mask |= 1 << src_map[src];
230 }
231#ifdef HAS_BUTTON_HOLD
232 if(MAP[i].btn == IMX233_BUTTON_HOLD)
233 hold_idx = i;
234#endif
235#ifdef HAVE_HEADPHONE_DETECTION
236 if(MAP[i].btn == IMX233_BUTTON_JACK)
237 jack_idx = i;
238#endif
239 }
240#ifdef HAS_BUTTON_HOLD
241 if(hold_idx == -1)
242 panicf("No hold entry found");
243#endif
244#ifdef HAVE_HEADPHONE_DETECTION
245 if(jack_idx == -1)
246 panicf("No jack entry found");
247#endif
248 /* create delay channel if necessary
249 * NOTE other buttons are polled as part of the delay irq processing */
250 if(src_mask != 0)
251 {
252 delay_chan = imx233_lradc_acquire_delay(TIMEOUT_NOBLOCK);
253 if(delay_chan < 0)
254 panicf("Cannot get delay channel");
255 imx233_lradc_setup_delay(delay_chan, chan_mask, 0, SAMPLES - 1, DELAY);
256 imx233_lradc_kick_delay(delay_chan);
257 }
258 /* otherwise we need to regularly poll for other buttons */
259 else
260 tick_add_task(do_round);
261} \ No newline at end of file
diff --git a/firmware/target/arm/imx233/button-imx233.h b/firmware/target/arm/imx233/button-imx233.h
new file mode 100644
index 0000000000..61adff8436
--- /dev/null
+++ b/firmware/target/arm/imx233/button-imx233.h
@@ -0,0 +1,174 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 by Amaury Pouly
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#ifndef __button_imx233__
22#define __button_imx233__
23
24#include "button.h"
25#include "button-target.h"
26#include "lradc-imx233.h"
27#include "pinctrl-imx233.h"
28#include "power-imx233.h"
29
30/* special values for .btn field */
31#define IMX233_BUTTON_END -1 /* end of the list */
32#define IMX233_BUTTON_HOLD -2 /* HOLD button */
33#define IMX233_BUTTON_JACK -3 /* JACK detect */
34#define IMX233_BUTTON_VDDIO -4 /* VDDIO value */
35
36/* values for .periph field */
37#define IMX233_BUTTON_GPIO 0 /* GPIO pin */
38#define IMX233_BUTTON_LRADC 1 /* LRADC channel */
39#define IMX233_BUTTON_PSWITCH 2 /* PSWITCH */
40
41/* values for the .flags field */
42#define IMX233_BUTTON_INVERTED (1 << 0) /* invert button detection */
43#define IMX233_BUTTON_PULLUP (1 << 1) /* pin needs a pullup (GPIO) */
44
45/** target-defined
46 * NOTE for proper operation:
47 * - the table must end which a dummy entry of type IMX233_BUTTON_END
48 * - default is active: high for GPIO, around value for LRADC, exact value for PSWITCH
49 * - inverted flags reverses the above behaviour
50 * - if lradc channel is relative, the .relative field specify a entry in the
51 * button table to which it is relative. This entry must have <0 .btn value
52 * and be of type LRADC. For example, if a channel is VDDIO relative, set
53 * .relative to IMX233_BUTTON_VDDIO and create an entry which .btn set to
54 * IMX233_BUTTON_VDDIO, channel set to VDDIO source and value set to the ideal values
55 * for which no correction is necessary. Note that the IMX233_BUTTON_VDDIO value is
56 * defined for convenience */
57struct imx233_button_map_t
58{
59 int btn; /* target button or magic value (END, HOLD, JACK) */
60 int periph; /* GPIO, LRADC, or PSWITCH, ... */
61 union
62 {
63 struct
64 {
65 int bank;
66 int pin;
67 }gpio;
68 struct
69 {
70 int src; /* source channel */
71 int value; /* expected value */
72 int relative; /* button to which it is relative or -1 if none */
73 }lradc;
74 struct
75 {
76 int level; /* value of the PSWITCH field: low, mid, high */
77 }pswitch;
78 }u;
79 int flags; /* flags */
80 const char *name; /* name of the button */
81 /** private fields */
82 bool last_val; /* last interpreted value */
83 int rounds; /* how many rounds it survived (debouncing) */
84 int threshold; /* round threshold for acceptance */
85};
86
87/* macros for the common cases */
88#define IMX233_BUTTON_PATH_GPIO(bank_, pin_) .periph = IMX233_BUTTON_GPIO, \
89 .u = {.gpio = {.bank = bank_, .pin = pin_}}
90#define IMX233_BUTTON_PATH_LRADC_REL(src_, val_, rel_) .periph = IMX233_BUTTON_LRADC, \
91 .u = {.lradc = {.src = src_, .value = val_, .relative = rel_}}
92#define IMX233_BUTTON_PATH_LRADC(src_, val_) IMX233_BUTTON_PATH_LRADC_REL(src_, val_, -1)
93#define IMX233_BUTTON_PATH_PSWITCH(lvl_) .periph = IMX233_BUTTON_PSWITCH, \
94 .u = {.pswitch = {.level = lvl_}}
95#define IMX233_BUTTON_PATH_END() .periph = -1
96/* special macro for VDDIO, for convenience */
97#define IMX233_BUTTON_PATH_VDDIO(val_) IMX233_BUTTON_PATH_LRADC(LRADC_SRC_VDDIO, val_)
98
99#define IMX233_BUTTON_NAMEFLAGS1(_,name_) .name = name_
100#define IMX233_BUTTON_NAMEFLAGS2(_,name_,f1) .name = name_, .flags = IMX233_BUTTON_##f1
101#define IMX233_BUTTON_NAMEFLAGS3(_,name_,f1,f2) .name = name_, \
102 .flags = IMX233_BUTTON_##f1 | IMX233_BUTTON_##f2
103#define IMX233_BUTTON_NAMEFLAGS4(_,name_,f1,f2,f3) .name = name_, \
104 .flags =IMX233_BUTTON_##f1 | IMX233_BUTTON_##f2 | IMX233_BUTTON_##f3
105
106#define IMX233_BUTTON__(btn_, path_, ...) \
107 {.btn = btn_, IMX233_BUTTON_PATH_##path_, \
108 REG_VARIADIC(IMX233_BUTTON_NAMEFLAGS, dummy, __VA_ARGS__)}
109#define IMX233_BUTTON_(btn_, path_, ...) \
110 IMX233_BUTTON__(IMX233_BUTTON_##btn_, path_, __VA_ARGS__)
111#define IMX233_BUTTON(btn_, path_, ...) \
112 IMX233_BUTTON__(BUTTON_##btn_, path_, __VA_ARGS__)
113
114#define IMX233_BUTTON_END_() \
115 {.btn = IMX233_BUTTON_END}
116
117/**
118 * This header provides macro to ease definition of buttons. There three types of
119 * buttons:
120 * - normal buttons: BUTTON_X -> defined using IMX233_BUTTON(X, ...)
121 * - special buttons IMX233_BUTTON_X -> defined using IMX233_BUTTON_(X, ...)
122 * - others: X -> defined using IMX233_BUTTON__(X, ...)
123 * A definition contains three mandatory parts and a fourth optional:
124 * IMX233_BUTTON(btn, path_spec, name [, flags])
125 * The path_spec can be of the form:
126 * - GPIO(bank, pin)
127 * - LRADC(src, value)
128 * - LRADC_REL(src, value, rel_idx)
129 * - PSWITCH(level)
130 * - END()
131 * The optional flags are specified as a list, without the IMX233_BUTTON_ prefix.
132 * Examples:
133 * - BUTTON_POWER named "power", mapped to PSWITCH mid level, no flags:
134 * IMX233_BUTTON(POWER, PSWICTH(1), "power")
135 * - IMX233_BUTTON_JACK named "jack", mapped to GPIO B2P17 inverted and pullup:
136 * IMX233_BUTTON_(JACK, GPIO(2, 17), "jack", INVERTED, PULLUP)
137 * - BIZARRE named "bizarre", mapped to LRADC channel 0, expected value 42:
138 * IMX233_BUTTON__(BIZARRE, LRADC(0, 42), "bizarre")
139 * - BUTTON_PLAY named "play", mapped to LRADc channel 2, expected value 300 related
140 * to button entry IDX
141 * IMX233_BUTTON(PLAY, LRADC_REL(2, 300, IDX), "play")
142 * - VDDIO entry, expected value 4000 (for relative entries):
143 * IMX233_BUTTON_(VDDIO, VDDIO(4000), "vddio")
144 * - last entry:
145 * IMX233_BUTTON_(END, END(), "")
146 *
147 * The driver also provides default implementations for headphones_inserted()
148 * and button_hold() which can be overriden since they have weak linkage.
149 */
150
151extern struct imx233_button_map_t imx233_button_map[];
152
153/* initialise button driver */
154void imx233_button_init(void);
155/* others gives the bitmask of other buttons: the function will OR the result
156 * with them except if hold is detected in which case 0 is always returned */
157int imx233_button_read(int others);
158#ifdef HAS_BUTTON_HOLD
159bool imx233_button_read_hold(void);
160bool button_hold(void);
161#endif
162#ifdef HAVE_HEADPHONE_DETECTION
163bool imx233_button_read_jack(void);
164bool headphones_inserted(void);
165#endif
166/* return interpreted button value, after processing.
167 * Argument is an index into the imx233_button_map table */
168bool imx233_button_read_btn(int idx);
169/* return raw value for a button, before any processing: GPIO state, LRADC
170 * channel value, PSWITCH value. Return -1 if there is no match.
171 * Argument is an index into the imx233_button_map table */
172int imx233_button_read_raw(int idx);
173
174#endif /* __button_imx233__ */ \ No newline at end of file