diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-07-20 12:07:29 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-08-16 13:35:52 -0400 |
commit | 34efe4dc47227264a38e60c878b4831d5f0d5a33 (patch) | |
tree | 68db9434049a5119cc5e0a5d689dbfaa7419f6af /drivers/extcon | |
parent | 689ae231afbac8979f96100b372a5a73458baaa9 (diff) |
extcon: arizona: Implement button detection support
As well as identifying accessories the accessory detection hardware on
Arizona class devices can also detect a number of buttons which we should
report via the input API.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Chanwoo Choi <cw00.choi@samsung.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/extcon')
-rw-r--r-- | drivers/extcon/extcon-arizona.c | 72 |
1 files changed, 65 insertions, 7 deletions
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 427a289f32a5..fa2114f1f9ec 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
22 | #include <linux/err.h> | 22 | #include <linux/err.h> |
23 | #include <linux/gpio.h> | 23 | #include <linux/gpio.h> |
24 | #include <linux/input.h> | ||
24 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
25 | #include <linux/pm_runtime.h> | 26 | #include <linux/pm_runtime.h> |
26 | #include <linux/regulator/consumer.h> | 27 | #include <linux/regulator/consumer.h> |
@@ -30,11 +31,14 @@ | |||
30 | #include <linux/mfd/arizona/pdata.h> | 31 | #include <linux/mfd/arizona/pdata.h> |
31 | #include <linux/mfd/arizona/registers.h> | 32 | #include <linux/mfd/arizona/registers.h> |
32 | 33 | ||
34 | #define ARIZONA_NUM_BUTTONS 6 | ||
35 | |||
33 | struct arizona_extcon_info { | 36 | struct arizona_extcon_info { |
34 | struct device *dev; | 37 | struct device *dev; |
35 | struct arizona *arizona; | 38 | struct arizona *arizona; |
36 | struct mutex lock; | 39 | struct mutex lock; |
37 | struct regulator *micvdd; | 40 | struct regulator *micvdd; |
41 | struct input_dev *input; | ||
38 | 42 | ||
39 | int micd_mode; | 43 | int micd_mode; |
40 | const struct arizona_micd_config *micd_modes; | 44 | const struct arizona_micd_config *micd_modes; |
@@ -54,6 +58,18 @@ static const struct arizona_micd_config micd_default_modes[] = { | |||
54 | { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, | 58 | { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, |
55 | }; | 59 | }; |
56 | 60 | ||
61 | static struct { | ||
62 | u16 status; | ||
63 | int report; | ||
64 | } arizona_lvl_to_key[ARIZONA_NUM_BUTTONS] = { | ||
65 | { 0x1, BTN_0 }, | ||
66 | { 0x2, BTN_1 }, | ||
67 | { 0x4, BTN_2 }, | ||
68 | { 0x8, BTN_3 }, | ||
69 | { 0x10, BTN_4 }, | ||
70 | { 0x20, BTN_5 }, | ||
71 | }; | ||
72 | |||
57 | #define ARIZONA_CABLE_MECHANICAL 0 | 73 | #define ARIZONA_CABLE_MECHANICAL 0 |
58 | #define ARIZONA_CABLE_MICROPHONE 1 | 74 | #define ARIZONA_CABLE_MICROPHONE 1 |
59 | #define ARIZONA_CABLE_HEADPHONE 2 | 75 | #define ARIZONA_CABLE_HEADPHONE 2 |
@@ -133,6 +149,7 @@ static void arizona_stop_mic(struct arizona_extcon_info *info) | |||
133 | 149 | ||
134 | if (change) { | 150 | if (change) { |
135 | regulator_disable(info->micvdd); | 151 | regulator_disable(info->micvdd); |
152 | pm_runtime_mark_last_busy(info->dev); | ||
136 | pm_runtime_put_autosuspend(info->dev); | 153 | pm_runtime_put_autosuspend(info->dev); |
137 | } | 154 | } |
138 | } | 155 | } |
@@ -141,8 +158,8 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
141 | { | 158 | { |
142 | struct arizona_extcon_info *info = data; | 159 | struct arizona_extcon_info *info = data; |
143 | struct arizona *arizona = info->arizona; | 160 | struct arizona *arizona = info->arizona; |
144 | unsigned int val; | 161 | unsigned int val, lvl; |
145 | int ret; | 162 | int ret, i; |
146 | 163 | ||
147 | mutex_lock(&info->lock); | 164 | mutex_lock(&info->lock); |
148 | 165 | ||
@@ -219,13 +236,22 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
219 | 236 | ||
220 | /* | 237 | /* |
221 | * If we're still detecting and we detect a short then we've | 238 | * If we're still detecting and we detect a short then we've |
222 | * got a headphone. Otherwise it's a button press, the | 239 | * got a headphone. Otherwise it's a button press. |
223 | * button reporting is stubbed out for now. | ||
224 | */ | 240 | */ |
225 | if (val & 0x3fc) { | 241 | if (val & 0x3fc) { |
226 | if (info->mic) { | 242 | if (info->mic) { |
227 | dev_dbg(arizona->dev, "Mic button detected\n"); | 243 | dev_dbg(arizona->dev, "Mic button detected\n"); |
228 | 244 | ||
245 | lvl = val & ARIZONA_MICD_LVL_MASK; | ||
246 | lvl >>= ARIZONA_MICD_LVL_SHIFT; | ||
247 | |||
248 | for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) | ||
249 | if (lvl & arizona_lvl_to_key[i].status) | ||
250 | input_report_key(info->input, | ||
251 | arizona_lvl_to_key[i].report, | ||
252 | 1); | ||
253 | input_sync(info->input); | ||
254 | |||
229 | } else if (info->detecting) { | 255 | } else if (info->detecting) { |
230 | dev_dbg(arizona->dev, "Headphone detected\n"); | 256 | dev_dbg(arizona->dev, "Headphone detected\n"); |
231 | info->detecting = false; | 257 | info->detecting = false; |
@@ -244,6 +270,10 @@ static irqreturn_t arizona_micdet(int irq, void *data) | |||
244 | } | 270 | } |
245 | } else { | 271 | } else { |
246 | dev_dbg(arizona->dev, "Mic button released\n"); | 272 | dev_dbg(arizona->dev, "Mic button released\n"); |
273 | for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) | ||
274 | input_report_key(info->input, | ||
275 | arizona_lvl_to_key[i].report, 0); | ||
276 | input_sync(info->input); | ||
247 | } | 277 | } |
248 | 278 | ||
249 | handled: | 279 | handled: |
@@ -258,7 +288,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) | |||
258 | struct arizona_extcon_info *info = data; | 288 | struct arizona_extcon_info *info = data; |
259 | struct arizona *arizona = info->arizona; | 289 | struct arizona *arizona = info->arizona; |
260 | unsigned int val; | 290 | unsigned int val; |
261 | int ret; | 291 | int ret, i; |
262 | 292 | ||
263 | pm_runtime_get_sync(info->dev); | 293 | pm_runtime_get_sync(info->dev); |
264 | 294 | ||
@@ -288,6 +318,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data) | |||
288 | 318 | ||
289 | arizona_stop_mic(info); | 319 | arizona_stop_mic(info); |
290 | 320 | ||
321 | for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) | ||
322 | input_report_key(info->input, | ||
323 | arizona_lvl_to_key[i].report, 0); | ||
324 | input_sync(info->input); | ||
325 | |||
291 | ret = extcon_update_state(&info->edev, 0xffffffff, 0); | 326 | ret = extcon_update_state(&info->edev, 0xffffffff, 0); |
292 | if (ret != 0) | 327 | if (ret != 0) |
293 | dev_err(arizona->dev, "Removal report failed: %d\n", | 328 | dev_err(arizona->dev, "Removal report failed: %d\n", |
@@ -307,7 +342,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) | |||
307 | struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); | 342 | struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); |
308 | struct arizona_pdata *pdata; | 343 | struct arizona_pdata *pdata; |
309 | struct arizona_extcon_info *info; | 344 | struct arizona_extcon_info *info; |
310 | int ret, mode; | 345 | int ret, mode, i; |
311 | 346 | ||
312 | pdata = dev_get_platdata(arizona->dev); | 347 | pdata = dev_get_platdata(arizona->dev); |
313 | 348 | ||
@@ -382,6 +417,20 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) | |||
382 | 417 | ||
383 | arizona_extcon_set_mode(info, 0); | 418 | arizona_extcon_set_mode(info, 0); |
384 | 419 | ||
420 | info->input = input_allocate_device(); | ||
421 | if (!info->input) { | ||
422 | dev_err(arizona->dev, "Can't allocate input dev\n"); | ||
423 | ret = -ENOMEM; | ||
424 | goto err_register; | ||
425 | } | ||
426 | |||
427 | for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) | ||
428 | input_set_capability(info->input, EV_KEY, | ||
429 | arizona_lvl_to_key[i].report); | ||
430 | info->input->name = "Headset"; | ||
431 | info->input->phys = "arizona/extcon"; | ||
432 | info->input->dev.parent = &pdev->dev; | ||
433 | |||
385 | pm_runtime_enable(&pdev->dev); | 434 | pm_runtime_enable(&pdev->dev); |
386 | pm_runtime_idle(&pdev->dev); | 435 | pm_runtime_idle(&pdev->dev); |
387 | pm_runtime_get_sync(&pdev->dev); | 436 | pm_runtime_get_sync(&pdev->dev); |
@@ -391,7 +440,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) | |||
391 | if (ret != 0) { | 440 | if (ret != 0) { |
392 | dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n", | 441 | dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n", |
393 | ret); | 442 | ret); |
394 | goto err_register; | 443 | goto err_input; |
395 | } | 444 | } |
396 | 445 | ||
397 | ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1); | 446 | ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1); |
@@ -436,6 +485,12 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) | |||
436 | 485 | ||
437 | pm_runtime_put(&pdev->dev); | 486 | pm_runtime_put(&pdev->dev); |
438 | 487 | ||
488 | ret = input_register_device(info->input); | ||
489 | if (ret) { | ||
490 | dev_err(&pdev->dev, "Can't register input device: %d\n", ret); | ||
491 | goto err_fall_wake; | ||
492 | } | ||
493 | |||
439 | return 0; | 494 | return 0; |
440 | 495 | ||
441 | err_fall_wake: | 496 | err_fall_wake: |
@@ -446,6 +501,8 @@ err_rise_wake: | |||
446 | arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0); | 501 | arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0); |
447 | err_rise: | 502 | err_rise: |
448 | arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info); | 503 | arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info); |
504 | err_input: | ||
505 | input_free_device(info->input); | ||
449 | err_register: | 506 | err_register: |
450 | pm_runtime_disable(&pdev->dev); | 507 | pm_runtime_disable(&pdev->dev); |
451 | extcon_dev_unregister(&info->edev); | 508 | extcon_dev_unregister(&info->edev); |
@@ -468,6 +525,7 @@ static int __devexit arizona_extcon_remove(struct platform_device *pdev) | |||
468 | regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, | 525 | regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, |
469 | ARIZONA_JD1_ENA, 0); | 526 | ARIZONA_JD1_ENA, 0); |
470 | arizona_clk32k_disable(arizona); | 527 | arizona_clk32k_disable(arizona); |
528 | input_unregister_device(info->input); | ||
471 | extcon_dev_unregister(&info->edev); | 529 | extcon_dev_unregister(&info->edev); |
472 | 530 | ||
473 | return 0; | 531 | return 0; |