diff options
Diffstat (limited to 'drivers/extcon/extcon-arizona.c')
-rw-r--r-- | drivers/extcon/extcon-arizona.c | 83 |
1 files changed, 74 insertions, 9 deletions
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 427a289f32a..cdab9e59829 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/err.h> #include <linux/gpio.h> +#include <linux/input.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> @@ -30,11 +31,14 @@ #include <linux/mfd/arizona/pdata.h> #include <linux/mfd/arizona/registers.h> +#define ARIZONA_NUM_BUTTONS 6 + struct arizona_extcon_info { struct device *dev; struct arizona *arizona; struct mutex lock; struct regulator *micvdd; + struct input_dev *input; int micd_mode; const struct arizona_micd_config *micd_modes; @@ -54,6 +58,18 @@ static const struct arizona_micd_config micd_default_modes[] = { { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, }; +static struct { + u16 status; + int report; +} arizona_lvl_to_key[ARIZONA_NUM_BUTTONS] = { + { 0x1, BTN_0 }, + { 0x2, BTN_1 }, + { 0x4, BTN_2 }, + { 0x8, BTN_3 }, + { 0x10, BTN_4 }, + { 0x20, BTN_5 }, +}; + #define ARIZONA_CABLE_MECHANICAL 0 #define ARIZONA_CABLE_MICROPHONE 1 #define ARIZONA_CABLE_HEADPHONE 2 @@ -133,6 +149,7 @@ static void arizona_stop_mic(struct arizona_extcon_info *info) if (change) { regulator_disable(info->micvdd); + pm_runtime_mark_last_busy(info->dev); pm_runtime_put_autosuspend(info->dev); } } @@ -141,8 +158,8 @@ static irqreturn_t arizona_micdet(int irq, void *data) { struct arizona_extcon_info *info = data; struct arizona *arizona = info->arizona; - unsigned int val; - int ret; + unsigned int val, lvl; + int ret, i; mutex_lock(&info->lock); @@ -219,13 +236,22 @@ static irqreturn_t arizona_micdet(int irq, void *data) /* * If we're still detecting and we detect a short then we've - * got a headphone. Otherwise it's a button press, the - * button reporting is stubbed out for now. + * got a headphone. Otherwise it's a button press. */ if (val & 0x3fc) { if (info->mic) { dev_dbg(arizona->dev, "Mic button detected\n"); + lvl = val & ARIZONA_MICD_LVL_MASK; + lvl >>= ARIZONA_MICD_LVL_SHIFT; + + for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) + if (lvl & arizona_lvl_to_key[i].status) + input_report_key(info->input, + arizona_lvl_to_key[i].report, + 1); + input_sync(info->input); + } else if (info->detecting) { dev_dbg(arizona->dev, "Headphone detected\n"); info->detecting = false; @@ -244,6 +270,10 @@ static irqreturn_t arizona_micdet(int irq, void *data) } } else { dev_dbg(arizona->dev, "Mic button released\n"); + for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) + input_report_key(info->input, + arizona_lvl_to_key[i].report, 0); + input_sync(info->input); } handled: @@ -258,7 +288,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data) struct arizona_extcon_info *info = data; struct arizona *arizona = info->arizona; unsigned int val; - int ret; + int ret, i; pm_runtime_get_sync(info->dev); @@ -288,6 +318,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data) arizona_stop_mic(info); + for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) + input_report_key(info->input, + arizona_lvl_to_key[i].report, 0); + input_sync(info->input); + ret = extcon_update_state(&info->edev, 0xffffffff, 0); if (ret != 0) dev_err(arizona->dev, "Removal report failed: %d\n", @@ -307,13 +342,13 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); struct arizona_pdata *pdata; struct arizona_extcon_info *info; - int ret, mode; + int ret, mode, i; pdata = dev_get_platdata(arizona->dev); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) { - dev_err(&pdev->dev, "failed to allocate memory\n"); + dev_err(&pdev->dev, "Failed to allocate memory\n"); ret = -ENOMEM; goto err; } @@ -350,7 +385,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) ret = extcon_dev_register(&info->edev, arizona->dev); if (ret < 0) { - dev_err(arizona->dev, "extcon_dev_regster() failed: %d\n", + dev_err(arizona->dev, "extcon_dev_register() failed: %d\n", ret); goto err; } @@ -382,6 +417,20 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) arizona_extcon_set_mode(info, 0); + info->input = input_allocate_device(); + if (!info->input) { + dev_err(arizona->dev, "Can't allocate input dev\n"); + ret = -ENOMEM; + goto err_register; + } + + for (i = 0; i < ARIZONA_NUM_BUTTONS; i++) + input_set_capability(info->input, EV_KEY, + arizona_lvl_to_key[i].report); + info->input->name = "Headset"; + info->input->phys = "arizona/extcon"; + info->input->dev.parent = &pdev->dev; + pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); pm_runtime_get_sync(&pdev->dev); @@ -391,7 +440,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n", ret); - goto err_register; + goto err_input; } ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1); @@ -434,10 +483,23 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev) regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, ARIZONA_JD1_ENA, ARIZONA_JD1_ENA); + ret = regulator_allow_bypass(info->micvdd, true); + if (ret != 0) + dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n", + ret); + pm_runtime_put(&pdev->dev); + ret = input_register_device(info->input); + if (ret) { + dev_err(&pdev->dev, "Can't register input device: %d\n", ret); + goto err_micdet; + } + return 0; +err_micdet: + arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); err_fall_wake: arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0); err_fall: @@ -446,6 +508,8 @@ err_rise_wake: arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0); err_rise: arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info); +err_input: + input_free_device(info->input); err_register: pm_runtime_disable(&pdev->dev); extcon_dev_unregister(&info->edev); @@ -468,6 +532,7 @@ static int __devexit arizona_extcon_remove(struct platform_device *pdev) regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, ARIZONA_JD1_ENA, 0); arizona_clk32k_disable(arizona); + input_unregister_device(info->input); extcon_dev_unregister(&info->edev); return 0; |