summaryrefslogtreecommitdiffstats
path: root/drivers/extcon
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/extcon')
-rw-r--r--drivers/extcon/Kconfig8
-rw-r--r--drivers/extcon/Makefile5
-rw-r--r--drivers/extcon/extcon-adc-jack.c198
-rw-r--r--drivers/extcon/extcon-arizona.c78
-rw-r--r--drivers/extcon/extcon-class.c (renamed from drivers/extcon/extcon_class.c)12
-rw-r--r--drivers/extcon/extcon-gpio.c (renamed from drivers/extcon/extcon_gpio.c)0
-rw-r--r--drivers/extcon/extcon-max77693.c8
7 files changed, 289 insertions, 20 deletions
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index e175c8ed4ec..07122a9ef36 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -21,6 +21,12 @@ config EXTCON_GPIO
Say Y here to enable GPIO based extcon support. Note that GPIO
extcon supports single state per extcon instance.
+config EXTCON_ADC_JACK
+ tristate "ADC Jack extcon support"
+ depends on IIO
+ help
+ Say Y here to enable extcon device driver based on ADC values.
+
config EXTCON_MAX77693
tristate "MAX77693 EXTCON Support"
depends on MFD_MAX77693
@@ -41,7 +47,7 @@ config EXTCON_MAX8997
config EXTCON_ARIZONA
tristate "Wolfson Arizona EXTCON support"
- depends on MFD_ARIZONA
+ depends on MFD_ARIZONA && INPUT
help
Say Y here to enable support for external accessory detection
with Wolfson Arizona devices. These are audio CODECs with
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 88961b33234..f98a3c4d46e 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -2,8 +2,9 @@
# Makefile for external connector class (extcon) devices
#
-obj-$(CONFIG_EXTCON) += extcon_class.o
-obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o
+obj-$(CONFIG_EXTCON) += extcon-class.o
+obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
+obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c
new file mode 100644
index 00000000000..60ac3fbb4cd
--- /dev/null
+++ b/drivers/extcon/extcon-adc-jack.c
@@ -0,0 +1,198 @@
+/*
+ * drivers/extcon/extcon-adc-jack.c
+ *
+ * Analog Jack extcon driver with ADC-based detection capability.
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * Modified for calling to IIO to get adc by <anish.singh@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/iio/consumer.h>
+#include <linux/extcon/extcon-adc-jack.h>
+#include <linux/extcon.h>
+
+/**
+ * struct adc_jack_data - internal data for adc_jack device driver
+ * @edev - extcon device.
+ * @cable_names - list of supported cables.
+ * @num_cables - size of cable_names.
+ * @adc_conditions - list of adc value conditions.
+ * @num_conditions - size of adc_conditions.
+ * @irq - irq number of attach/detach event (0 if not exist).
+ * @handling_delay - interrupt handler will schedule extcon event
+ * handling at handling_delay jiffies.
+ * @handler - extcon event handler called by interrupt handler.
+ * @chan - iio channel being queried.
+ */
+struct adc_jack_data {
+ struct extcon_dev edev;
+
+ const char **cable_names;
+ int num_cables;
+ struct adc_jack_cond *adc_conditions;
+ int num_conditions;
+
+ int irq;
+ unsigned long handling_delay; /* in jiffies */
+ struct delayed_work handler;
+
+ struct iio_channel *chan;
+};
+
+static void adc_jack_handler(struct work_struct *work)
+{
+ struct adc_jack_data *data = container_of(to_delayed_work(work),
+ struct adc_jack_data,
+ handler);
+ u32 state = 0;
+ int ret, adc_val;
+ int i;
+
+ ret = iio_read_channel_raw(data->chan, &adc_val);
+ if (ret < 0) {
+ dev_err(data->edev.dev, "read channel() error: %d\n", ret);
+ return;
+ }
+
+ /* Get state from adc value with adc_conditions */
+ for (i = 0; i < data->num_conditions; i++) {
+ struct adc_jack_cond *def = &data->adc_conditions[i];
+ if (!def->state)
+ break;
+ if (def->min_adc <= adc_val && def->max_adc >= adc_val) {
+ state = def->state;
+ break;
+ }
+ }
+ /* if no def has met, it means state = 0 (no cables attached) */
+
+ extcon_set_state(&data->edev, state);
+}
+
+static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
+{
+ struct adc_jack_data *data = _data;
+
+ schedule_delayed_work(&data->handler, data->handling_delay);
+ return IRQ_HANDLED;
+}
+
+static int __devinit adc_jack_probe(struct platform_device *pdev)
+{
+ struct adc_jack_data *data;
+ struct adc_jack_pdata *pdata = pdev->dev.platform_data;
+ int i, err = 0;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->edev.name = pdata->name;
+
+ if (!pdata->cable_names) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "error: cable_names not defined.\n");
+ goto out;
+ }
+
+ data->edev.supported_cable = pdata->cable_names;
+
+ /* Check the length of array and set num_cables */
+ for (i = 0; data->edev.supported_cable[i]; i++)
+ ;
+ if (i == 0 || i > SUPPORTED_CABLE_MAX) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
+ i - 1);
+ goto out;
+ }
+ data->num_cables = i;
+
+ if (!pdata->adc_conditions ||
+ !pdata->adc_conditions[0].state) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
+ goto out;
+ }
+ data->adc_conditions = pdata->adc_conditions;
+
+ /* Check the length of array and set num_conditions */
+ for (i = 0; data->adc_conditions[i].state; i++)
+ ;
+ data->num_conditions = i;
+
+ data->chan = iio_channel_get(dev_name(&pdev->dev),
+ pdata->consumer_channel);
+ if (IS_ERR(data->chan)) {
+ err = PTR_ERR(data->chan);
+ goto out;
+ }
+
+ data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
+
+ INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler);
+
+ platform_set_drvdata(pdev, data);
+
+ err = extcon_dev_register(&data->edev, &pdev->dev);
+ if (err)
+ goto out;
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (!data->irq) {
+ dev_err(&pdev->dev, "platform_get_irq failed\n");
+ err = -ENODEV;
+ goto err_irq;
+ }
+
+ err = request_any_context_irq(data->irq, adc_jack_irq_thread,
+ pdata->irq_flags, pdata->name, data);
+
+ if (err) {
+ dev_err(&pdev->dev, "error: irq %d\n", data->irq);
+ err = -EINVAL;
+ goto err_irq;
+ }
+
+ goto out;
+
+err_irq:
+ extcon_dev_unregister(&data->edev);
+out:
+ return err;
+}
+
+static int __devexit adc_jack_remove(struct platform_device *pdev)
+{
+ struct adc_jack_data *data = platform_get_drvdata(pdev);
+
+ free_irq(data->irq, data);
+ cancel_work_sync(&data->handler.work);
+ extcon_dev_unregister(&data->edev);
+
+ return 0;
+}
+
+static struct platform_driver adc_jack_driver = {
+ .probe = adc_jack_probe,
+ .remove = __devexit_p(adc_jack_remove),
+ .driver = {
+ .name = "adc-jack",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(adc_jack_driver);
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c
index 6c19833ed2d..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);
@@ -441,8 +490,16 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
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:
@@ -451,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);
@@ -473,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;
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon-class.c
index f6419f9db76..946a3188b2b 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon-class.c
@@ -30,6 +30,7 @@
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/slab.h>
+#include <linux/sysfs.h>
/*
* extcon_cable_name suggests the standard cable names for commonly used
@@ -442,7 +443,7 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val,
/**
* extcon_register_interest() - Register a notifier for a state change of a
- * specific cable, not a entier set of cables of a
+ * specific cable, not an entier set of cables of a
* extcon device.
* @obj: an empty extcon_specific_cable_nb object to be returned.
* @extcon_name: the name of extcon device.
@@ -498,7 +499,7 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
}
/**
- * extcon_register_notifier() - Register a notifee to get notified by
+ * extcon_register_notifier() - Register a notifiee to get notified by
* any attach status changes from the extcon.
* @edev: the extcon device.
* @nb: a notifier block to be registered.
@@ -515,7 +516,7 @@ int extcon_register_notifier(struct extcon_dev *edev,
EXPORT_SYMBOL_GPL(extcon_register_notifier);
/**
- * extcon_unregister_notifier() - Unregister a notifee from the extcon device.
+ * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
* @edev: the extcon device.
* @nb: a registered notifier block to be unregistered.
*/
@@ -673,10 +674,12 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
cable->attr_g.name = str;
cable->attr_g.attrs = cable->attrs;
+ sysfs_attr_init(&cable->attr_name.attr);
cable->attr_name.attr.name = "name";
cable->attr_name.attr.mode = 0444;
cable->attr_name.show = cable_name_show;
+ sysfs_attr_init(&cable->attr_state.attr);
cable->attr_state.attr.name = "state";
cable->attr_state.attr.mode = 0644;
cable->attr_state.show = cable_state_show;
@@ -722,6 +725,7 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
goto err_muex;
}
strcpy(name, buf);
+ sysfs_attr_init(&edev->d_attrs_muex[index].attr);
edev->d_attrs_muex[index].attr.name = name;
edev->d_attrs_muex[index].attr.mode = 0000;
edev->attrs_muex[index] = &edev->d_attrs_muex[index]
@@ -802,7 +806,7 @@ EXPORT_SYMBOL_GPL(extcon_dev_register);
/**
* extcon_dev_unregister() - Unregister the extcon device.
- * @edev: the extcon device instance to be unregitered.
+ * @edev: the extcon device instance to be unregistered.
*
* Note that this does not call kfree(edev) because edev was not allocated
* by this class.
diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon-gpio.c
index 3cc152e690b..3cc152e690b 100644
--- a/drivers/extcon/extcon_gpio.c
+++ b/drivers/extcon/extcon-gpio.c
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
index 38f9e52f358..e21387e2da5 100644
--- a/drivers/extcon/extcon-max77693.c
+++ b/drivers/extcon/extcon-max77693.c
@@ -356,7 +356,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info,
extcon_set_cable_state(info->edev, "MHL", attached);
break;
default:
- dev_err(info->dev, "faild to detect %s accessory\n",
+ dev_err(info->dev, "failed to detect %s accessory\n",
attached ? "attached" : "detached");
dev_err(info->dev, "- adc:0x%x, adclow:0x%x, adc1k:0x%x\n",
adc, adclow, adc1k);
@@ -548,7 +548,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
curr_adc = info->status[0] & STATUS1_ADC_MASK;
curr_adc >>= STATUS1_ADC_SHIFT;
- /* Check accossory state which is either detached or attached */
+ /* Check accessory state which is either detached or attached */
if (curr_adc == MAX77693_MUIC_ADC_OPEN)
attached = false;
@@ -564,7 +564,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
curr_chg_type = info->status[1] & STATUS2_CHGTYP_MASK;
curr_chg_type >>= STATUS2_CHGTYP_SHIFT;
- /* Check charger accossory state which
+ /* Check charger accessory state which
is either detached or attached */
if (curr_chg_type == MAX77693_CHARGER_TYPE_NONE)
attached = false;
@@ -699,7 +699,7 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
ret = request_threaded_irq(virq, NULL,
max77693_muic_irq_handler,
- 0, muic_irq->name, info);
+ IRQF_ONESHOT, muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d,"