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>
This commit is contained in:
parent
689ae231af
commit
34efe4dc47
1 changed files with 65 additions and 7 deletions
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/input.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
@ -30,11 +31,14 @@
|
||||||
#include <linux/mfd/arizona/pdata.h>
|
#include <linux/mfd/arizona/pdata.h>
|
||||||
#include <linux/mfd/arizona/registers.h>
|
#include <linux/mfd/arizona/registers.h>
|
||||||
|
|
||||||
|
#define ARIZONA_NUM_BUTTONS 6
|
||||||
|
|
||||||
struct arizona_extcon_info {
|
struct arizona_extcon_info {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct arizona *arizona;
|
struct arizona *arizona;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
struct regulator *micvdd;
|
struct regulator *micvdd;
|
||||||
|
struct input_dev *input;
|
||||||
|
|
||||||
int micd_mode;
|
int micd_mode;
|
||||||
const struct arizona_micd_config *micd_modes;
|
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 },
|
{ 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_MECHANICAL 0
|
||||||
#define ARIZONA_CABLE_MICROPHONE 1
|
#define ARIZONA_CABLE_MICROPHONE 1
|
||||||
#define ARIZONA_CABLE_HEADPHONE 2
|
#define ARIZONA_CABLE_HEADPHONE 2
|
||||||
|
@ -133,6 +149,7 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)
|
||||||
|
|
||||||
if (change) {
|
if (change) {
|
||||||
regulator_disable(info->micvdd);
|
regulator_disable(info->micvdd);
|
||||||
|
pm_runtime_mark_last_busy(info->dev);
|
||||||
pm_runtime_put_autosuspend(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_extcon_info *info = data;
|
||||||
struct arizona *arizona = info->arizona;
|
struct arizona *arizona = info->arizona;
|
||||||
unsigned int val;
|
unsigned int val, lvl;
|
||||||
int ret;
|
int ret, i;
|
||||||
|
|
||||||
mutex_lock(&info->lock);
|
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
|
* If we're still detecting and we detect a short then we've
|
||||||
* got a headphone. Otherwise it's a button press, the
|
* got a headphone. Otherwise it's a button press.
|
||||||
* button reporting is stubbed out for now.
|
|
||||||
*/
|
*/
|
||||||
if (val & 0x3fc) {
|
if (val & 0x3fc) {
|
||||||
if (info->mic) {
|
if (info->mic) {
|
||||||
dev_dbg(arizona->dev, "Mic button detected\n");
|
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) {
|
} else if (info->detecting) {
|
||||||
dev_dbg(arizona->dev, "Headphone detected\n");
|
dev_dbg(arizona->dev, "Headphone detected\n");
|
||||||
info->detecting = false;
|
info->detecting = false;
|
||||||
|
@ -244,6 +270,10 @@ static irqreturn_t arizona_micdet(int irq, void *data)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(arizona->dev, "Mic button released\n");
|
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:
|
handled:
|
||||||
|
@ -258,7 +288,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
||||||
struct arizona_extcon_info *info = data;
|
struct arizona_extcon_info *info = data;
|
||||||
struct arizona *arizona = info->arizona;
|
struct arizona *arizona = info->arizona;
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
int ret;
|
int ret, i;
|
||||||
|
|
||||||
pm_runtime_get_sync(info->dev);
|
pm_runtime_get_sync(info->dev);
|
||||||
|
|
||||||
|
@ -288,6 +318,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
||||||
|
|
||||||
arizona_stop_mic(info);
|
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);
|
ret = extcon_update_state(&info->edev, 0xffffffff, 0);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
dev_err(arizona->dev, "Removal report failed: %d\n",
|
dev_err(arizona->dev, "Removal report failed: %d\n",
|
||||||
|
@ -307,7 +342,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
|
||||||
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
||||||
struct arizona_pdata *pdata;
|
struct arizona_pdata *pdata;
|
||||||
struct arizona_extcon_info *info;
|
struct arizona_extcon_info *info;
|
||||||
int ret, mode;
|
int ret, mode, i;
|
||||||
|
|
||||||
pdata = dev_get_platdata(arizona->dev);
|
pdata = dev_get_platdata(arizona->dev);
|
||||||
|
|
||||||
|
@ -382,6 +417,20 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
arizona_extcon_set_mode(info, 0);
|
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_enable(&pdev->dev);
|
||||||
pm_runtime_idle(&pdev->dev);
|
pm_runtime_idle(&pdev->dev);
|
||||||
pm_runtime_get_sync(&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) {
|
if (ret != 0) {
|
||||||
dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
|
dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
|
||||||
ret);
|
ret);
|
||||||
goto err_register;
|
goto err_input;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1);
|
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)
|
||||||
|
|
||||||
pm_runtime_put(&pdev->dev);
|
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_fall_wake;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_fall_wake:
|
err_fall_wake:
|
||||||
|
@ -446,6 +501,8 @@ err_rise_wake:
|
||||||
arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
|
arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
|
||||||
err_rise:
|
err_rise:
|
||||||
arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
|
arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
|
||||||
|
err_input:
|
||||||
|
input_free_device(info->input);
|
||||||
err_register:
|
err_register:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
extcon_dev_unregister(&info->edev);
|
extcon_dev_unregister(&info->edev);
|
||||||
|
@ -468,6 +525,7 @@ static int __devexit arizona_extcon_remove(struct platform_device *pdev)
|
||||||
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
|
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
|
||||||
ARIZONA_JD1_ENA, 0);
|
ARIZONA_JD1_ENA, 0);
|
||||||
arizona_clk32k_disable(arizona);
|
arizona_clk32k_disable(arizona);
|
||||||
|
input_unregister_device(info->input);
|
||||||
extcon_dev_unregister(&info->edev);
|
extcon_dev_unregister(&info->edev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue