diff options
41 files changed, 1213 insertions, 173 deletions
diff --git a/Documentation/ABI/stable/sysfs-driver-w1_ds28e04 b/Documentation/ABI/stable/sysfs-driver-w1_ds28e04 new file mode 100644 index 00000000000..26579ee868c --- /dev/null +++ b/Documentation/ABI/stable/sysfs-driver-w1_ds28e04 @@ -0,0 +1,15 @@ +What: /sys/bus/w1/devices/.../pio +Date: May 2012 +Contact: Markus Franke <franm@hrz.tu-chemnitz.de> +Description: read/write the contents of the two PIO's of the DS28E04-100 + see Documentation/w1/slaves/w1_ds28e04 for detailed information +Users: any user space application which wants to communicate with DS28E04-100 + + + +What: /sys/bus/w1/devices/.../eeprom +Date: May 2012 +Contact: Markus Franke <franm@hrz.tu-chemnitz.de> +Description: read/write the contents of the EEPROM memory of the DS28E04-100 + see Documentation/w1/slaves/w1_ds28e04 for detailed information +Users: any user space application which wants to communicate with DS28E04-100 diff --git a/Documentation/stable_kernel_rules.txt b/Documentation/stable_kernel_rules.txt index 4a7b54bd37e..b0714d8f678 100644 --- a/Documentation/stable_kernel_rules.txt +++ b/Documentation/stable_kernel_rules.txt @@ -1,4 +1,4 @@ -Everything you ever wanted to know about Linux 2.6 -stable releases. +Everything you ever wanted to know about Linux -stable releases. Rules on what kind of patches are accepted, and which ones are not, into the "-stable" tree: @@ -42,10 +42,10 @@ Procedure for submitting patches to the -stable tree: cherry-picked than this can be specified in the following format in the sign-off area: - Cc: <stable@vger.kernel.org> # .32.x: a1f84a3: sched: Check for idle - Cc: <stable@vger.kernel.org> # .32.x: 1b9508f: sched: Rate-limit newidle - Cc: <stable@vger.kernel.org> # .32.x: fd21073: sched: Fix affinity logic - Cc: <stable@vger.kernel.org> # .32.x + Cc: <stable@vger.kernel.org> # 3.3.x: a1f84a3: sched: Check for idle + Cc: <stable@vger.kernel.org> # 3.3.x: 1b9508f: sched: Rate-limit newidle + Cc: <stable@vger.kernel.org> # 3.3.x: fd21073: sched: Fix affinity logic + Cc: <stable@vger.kernel.org> # 3.3.x Signed-off-by: Ingo Molnar <mingo@elte.hu> The tag sequence has the meaning of: @@ -79,6 +79,15 @@ Review cycle: security kernel team, and not go through the normal review cycle. Contact the kernel security team for more details on this procedure. +Trees: + + - The queues of patches, for both completed versions and in progress + versions can be found at: + http://git.kernel.org/?p=linux/kernel/git/stable/stable-queue.git + - The finalized and tagged releases of all stable kernels can be found + in separate branches per version at: + http://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git + Review committee: diff --git a/Documentation/w1/slaves/w1_ds28e04 b/Documentation/w1/slaves/w1_ds28e04 new file mode 100644 index 00000000000..85bc9a7e02f --- /dev/null +++ b/Documentation/w1/slaves/w1_ds28e04 @@ -0,0 +1,36 @@ +Kernel driver w1_ds28e04 +======================== + +Supported chips: + * Maxim DS28E04-100 4096-Bit Addressable 1-Wire EEPROM with PIO + +supported family codes: + W1_FAMILY_DS28E04 0x1C + +Author: Markus Franke, <franke.m@sebakmt.com> <franm@hrz.tu-chemnitz.de> + +Description +----------- + +Support is provided through the sysfs files "eeprom" and "pio". CRC checking +during memory accesses can optionally be enabled/disabled via the device +attribute "crccheck". The strong pull-up can optionally be enabled/disabled +via the module parameter "w1_strong_pullup". + +Memory Access + + A read operation on the "eeprom" file reads the given amount of bytes + from the EEPROM of the DS28E04. + + A write operation on the "eeprom" file writes the given byte sequence + to the EEPROM of the DS28E04. If CRC checking mode is enabled only + fully alligned blocks of 32 bytes with valid CRC16 values (in bytes 30 + and 31) are allowed to be written. + +PIO Access + + The 2 PIOs of the DS28E04-100 are accessible via the "pio" sysfs file. + + The current status of the PIO's is returned as an 8 bit value. Bit 0/1 + represent the state of PIO_0/PIO_1. Bits 2..7 do not care. The PIO's are + driven low-active, i.e. the driver delivers/expects low-active values. diff --git a/MAINTAINERS b/MAINTAINERS index 1b71f6ceae0..63ccf7edec3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2709,6 +2709,14 @@ M: Mimi Zohar <zohar@us.ibm.com> S: Supported F: security/integrity/evm/ +EXTERNAL CONNECTOR SUBSYSTEM (EXTCON) +M: MyungJoo Ham <myungjoo.ham@samsung.com> +M: Chanwoo Choi <cw00.choi@samsung.com> +L: linux-kernel@vger.kernel.org +S: Maintained +F: drivers/extcon/ +F: Documentation/extcon/ + EXYNOS DP DRIVER M: Jingoo Han <jg1.han@samsung.com> L: linux-fbdev@vger.kernel.org diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 2bcef657a60..181ed2660b3 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -743,7 +743,6 @@ int bus_add_driver(struct device_driver *drv) } } - kobject_uevent(&priv->kobj, KOBJ_ADD); return 0; out_unregister: diff --git a/drivers/base/core.c b/drivers/base/core.c index 346be8b78b2..f338037a4f3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -85,14 +85,13 @@ const char *dev_driver_string(const struct device *dev) } EXPORT_SYMBOL(dev_driver_string); -#define to_dev(obj) container_of(obj, struct device, kobj) #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct device_attribute *dev_attr = to_dev_attr(attr); - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->show) @@ -108,7 +107,7 @@ static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct device_attribute *dev_attr = to_dev_attr(attr); - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); ssize_t ret = -EIO; if (dev_attr->store) @@ -182,7 +181,7 @@ EXPORT_SYMBOL_GPL(device_show_int); */ static void device_release(struct kobject *kobj) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); struct device_private *p = dev->p; if (dev->release) @@ -200,7 +199,7 @@ static void device_release(struct kobject *kobj) static const void *device_namespace(struct kobject *kobj) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); const void *ns = NULL; if (dev->class && dev->class->ns_type) @@ -221,7 +220,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) struct kobj_type *ktype = get_ktype(kobj); if (ktype == &device_ktype) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); if (dev->bus) return 1; if (dev->class) @@ -232,7 +231,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); if (dev->bus) return dev->bus->name; @@ -244,7 +243,7 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj) static int dev_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); int retval = 0; /* add device node properties if present */ @@ -1132,7 +1131,7 @@ int device_register(struct device *dev) */ struct device *get_device(struct device *dev) { - return dev ? to_dev(kobject_get(&dev->kobj)) : NULL; + return dev ? kobj_to_dev(kobject_get(&dev->kobj)) : NULL; } /** @@ -1754,25 +1753,25 @@ int device_move(struct device *dev, struct device *new_parent, set_dev_node(dev, dev_to_node(new_parent)); } - if (!dev->class) - goto out_put; - error = device_move_class_links(dev, old_parent, new_parent); - if (error) { - /* We ignore errors on cleanup since we're hosed anyway... */ - device_move_class_links(dev, new_parent, old_parent); - if (!kobject_move(&dev->kobj, &old_parent->kobj)) { - if (new_parent) - klist_remove(&dev->p->knode_parent); - dev->parent = old_parent; - if (old_parent) { - klist_add_tail(&dev->p->knode_parent, - &old_parent->p->klist_children); - set_dev_node(dev, dev_to_node(old_parent)); + if (dev->class) { + error = device_move_class_links(dev, old_parent, new_parent); + if (error) { + /* We ignore errors on cleanup since we're hosed anyway... */ + device_move_class_links(dev, new_parent, old_parent); + if (!kobject_move(&dev->kobj, &old_parent->kobj)) { + if (new_parent) + klist_remove(&dev->p->knode_parent); + dev->parent = old_parent; + if (old_parent) { + klist_add_tail(&dev->p->knode_parent, + &old_parent->p->klist_children); + set_dev_node(dev, dev_to_node(old_parent)); + } } + cleanup_glue_dir(dev, new_parent_kobj); + put_device(new_parent); + goto out; } - cleanup_glue_dir(dev, new_parent_kobj); - put_device(new_parent); - goto out; } switch (dpm_order) { case DPM_ORDER_NONE: @@ -1787,7 +1786,7 @@ int device_move(struct device *dev, struct device *new_parent, device_pm_move_last(dev); break; } -out_put: + put_device(old_parent); out: device_pm_unlock(); @@ -1812,6 +1811,13 @@ void device_shutdown(void) while (!list_empty(&devices_kset->list)) { dev = list_entry(devices_kset->list.prev, struct device, kobj.entry); + + /* + * hold reference count of device's parent to + * prevent it from being freed because parent's + * lock is to be held + */ + get_device(dev->parent); get_device(dev); /* * Make sure the device is off the kset list, in the @@ -1820,6 +1826,11 @@ void device_shutdown(void) list_del_init(&dev->kobj.entry); spin_unlock(&devices_kset->list_lock); + /* hold lock to avoid race with probe/release */ + if (dev->parent) + device_lock(dev->parent); + device_lock(dev); + /* Don't allow any more runtime suspends */ pm_runtime_get_noresume(dev); pm_runtime_barrier(dev); @@ -1831,7 +1842,13 @@ void device_shutdown(void) dev_dbg(dev, "shutdown\n"); dev->driver->shutdown(dev); } + + device_unlock(dev); + if (dev->parent) + device_unlock(dev->parent); + put_device(dev); + put_device(dev->parent); spin_lock(&devices_kset->list_lock); } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index dcb8a6e4869..9b0aca47958 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -85,8 +85,20 @@ static void deferred_probe_work_func(struct work_struct *work) * manipulate the deferred list */ mutex_unlock(&deferred_probe_mutex); + + /* + * Force the device to the end of the dpm_list since + * the PM code assumes that the order we add things to + * the list is a good order for suspend but deferred + * probe makes that very unsafe. + */ + device_pm_lock(); + device_pm_move_last(dev); + device_pm_unlock(); + dev_dbg(dev, "Retrying from deferred list\n"); bus_probe_device(dev); + mutex_lock(&deferred_probe_mutex); put_device(dev); @@ -283,6 +295,7 @@ probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; + dev_set_drvdata(dev, NULL); if (ret == -EPROBE_DEFER) { /* Driver requested deferred probing */ @@ -487,6 +500,7 @@ static void __device_release_driver(struct device *dev) drv->remove(dev); devres_release_all(dev); dev->driver = NULL; + dev_set_drvdata(dev, NULL); klist_remove(&dev->p->knode_driver); if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index 24e88fe29ec..c30f3e1d0ef 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -493,6 +493,7 @@ EXPORT_SYMBOL_GPL(dma_buf_vmap); /** * dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap. * @dmabuf: [in] buffer to vunmap + * @vaddr: [in] vmap to vunmap */ void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) { diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index 1b85949e3d2..560a7173f81 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -186,6 +186,7 @@ EXPORT_SYMBOL(dma_release_from_coherent); * @vma: vm_area for the userspace memory * @vaddr: cpu address returned by dma_alloc_from_coherent * @size: size of the memory buffer allocated by dma_alloc_from_coherent + * @ret: result from remap_pfn_range() * * This checks whether the memory was allocated from the per-device * coherent memory pool and if so, maps that memory to the provided vma. diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 207c27ddf82..1b500d6fcc2 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -187,6 +187,9 @@ int driver_register(struct device_driver *drv) ret = driver_add_groups(drv, drv->groups); if (ret) bus_remove_driver(drv); + + kobject_uevent(&drv->p->kobj, KOBJ_ADD); + return ret; } EXPORT_SYMBOL_GPL(driver_register); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 5401814c874..803cfc1597a 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -22,8 +22,6 @@ #include <linux/slab.h> #include <linux/sched.h> -#define to_dev(obj) container_of(obj, struct device, kobj) - MODULE_AUTHOR("Manuel Estrada Sainz"); MODULE_DESCRIPTION("Multi purpose firmware loading support"); MODULE_LICENSE("GPL"); @@ -290,7 +288,7 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); struct firmware_priv *fw_priv = to_firmware_priv(dev); struct firmware *fw; ssize_t ret_count; @@ -384,7 +382,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { - struct device *dev = to_dev(kobj); + struct device *dev = kobj_to_dev(kobj); struct firmware_priv *fw_priv = to_firmware_priv(dev); struct firmware *fw; ssize_t retval; diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 29c5cf852ef..bb385ac629a 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -29,4 +29,12 @@ config EXTCON_MAX8997 Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory detector and switch. +config EXTCON_ARIZONA + tristate "Wolfson Arizona EXTCON support" + depends on MFD_ARIZONA + help + Say Y here to enable support for external accessory detection + with Wolfson Arizona devices. These are audio CODECs with + advanced audio accessory detection support. + endif # MULTISTATE_SWITCH diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 86020bdb6da..e932caaa311 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_EXTCON) += extcon_class.o obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o +obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c new file mode 100644 index 00000000000..b068bc9defe --- /dev/null +++ b/drivers/extcon/extcon-arizona.c @@ -0,0 +1,491 @@ +/* + * extcon-arizona.c - Extcon driver Wolfson Arizona devices + * + * Copyright (C) 2012 Wolfson Microelectronics plc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/extcon.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/pdata.h> +#include <linux/mfd/arizona/registers.h> + +struct arizona_extcon_info { + struct device *dev; + struct arizona *arizona; + struct mutex lock; + struct regulator *micvdd; + + int micd_mode; + const struct arizona_micd_config *micd_modes; + int micd_num_modes; + + bool micd_reva; + + bool mic; + bool detecting; + int jack_flips; + + struct extcon_dev edev; +}; + +static const struct arizona_micd_config micd_default_modes[] = { + { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 }, + { 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 }, +}; + +#define ARIZONA_CABLE_MECHANICAL "Mechanical" +#define ARIZONA_CABLE_HEADPHONE "Headphone" +#define ARIZONA_CABLE_HEADSET "Headset" + +static const char *arizona_cable[] = { + ARIZONA_CABLE_MECHANICAL, + ARIZONA_CABLE_HEADSET, + ARIZONA_CABLE_HEADPHONE, + NULL, +}; + +static const u32 arizona_exclusions[] = { + 0x6, /* Headphone and headset */ + 0, +}; + +static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) +{ + struct arizona *arizona = info->arizona; + + gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio, + info->micd_modes[mode].gpio); + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_BIAS_SRC_MASK, + info->micd_modes[mode].bias); + regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_SRC, info->micd_modes[mode].src); + + info->micd_mode = mode; + + dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode); +} + +static void arizona_start_mic(struct arizona_extcon_info *info) +{ + struct arizona *arizona = info->arizona; + bool change; + int ret; + + info->detecting = true; + info->mic = false; + info->jack_flips = 0; + + /* Microphone detection can't use idle mode */ + pm_runtime_get(info->dev); + + ret = regulator_enable(info->micvdd); + if (ret != 0) { + dev_err(arizona->dev, "Failed to enable MICVDD: %d\n", + ret); + } + + if (info->micd_reva) { + regmap_write(arizona->regmap, 0x80, 0x3); + regmap_write(arizona->regmap, 0x294, 0); + regmap_write(arizona->regmap, 0x80, 0x0); + } + + regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, + &change); + if (!change) { + regulator_disable(info->micvdd); + pm_runtime_put_autosuspend(info->dev); + } +} + +static void arizona_stop_mic(struct arizona_extcon_info *info) +{ + struct arizona *arizona = info->arizona; + bool change; + + regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, 0, + &change); + + if (info->micd_reva) { + regmap_write(arizona->regmap, 0x80, 0x3); + regmap_write(arizona->regmap, 0x294, 2); + regmap_write(arizona->regmap, 0x80, 0x0); + } + + if (change) { + regulator_disable(info->micvdd); + pm_runtime_put_autosuspend(info->dev); + } +} + +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; + + mutex_lock(&info->lock); + + ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret); + return IRQ_NONE; + } + + dev_dbg(arizona->dev, "MICDET: %x\n", val); + + if (!(val & ARIZONA_MICD_VALID)) { + dev_warn(arizona->dev, "Microphone detection state invalid\n"); + mutex_unlock(&info->lock); + return IRQ_NONE; + } + + /* Due to jack detect this should never happen */ + if (!(val & ARIZONA_MICD_STS)) { + dev_warn(arizona->dev, "Detected open circuit\n"); + info->detecting = false; + goto handled; + } + + /* If we got a high impedence we should have a headset, report it. */ + if (info->detecting && (val & 0x400)) { + ret = extcon_set_cable_state(&info->edev, + ARIZONA_CABLE_HEADSET, true); + + if (ret != 0) + dev_err(arizona->dev, "Headset report failed: %d\n", + ret); + + info->mic = true; + info->detecting = false; + goto handled; + } + + /* If we detected a lower impedence during initial startup + * then we probably have the wrong polarity, flip it. Don't + * do this for the lowest impedences to speed up detection of + * plain headphones. If both polarities report a low + * impedence then give up and report headphones. + */ + if (info->detecting && (val & 0x3f8)) { + info->jack_flips++; + + if (info->jack_flips >= info->micd_num_modes) { + dev_dbg(arizona->dev, "Detected headphone\n"); + info->detecting = false; + ret = extcon_set_cable_state(&info->edev, + ARIZONA_CABLE_HEADPHONE, + true); + if (ret != 0) + dev_err(arizona->dev, + "Headphone report failed: %d\n", + ret); + } else { + info->micd_mode++; + if (info->micd_mode == info->micd_num_modes) + info->micd_mode = 0; + arizona_extcon_set_mode(info, info->micd_mode); + + info->jack_flips++; + } + + goto handled; + } + + /* + * 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. + */ + if (val & 0x3fc) { + if (info->mic) { + dev_dbg(arizona->dev, "Mic button detected\n"); + + } else if (info->detecting) { + dev_dbg(arizona->dev, "Headphone detected\n"); + info->detecting = false; + arizona_stop_mic(info); + + ret = extcon_set_cable_state(&info->edev, + ARIZONA_CABLE_HEADPHONE, + true); + if (ret != 0) + dev_err(arizona->dev, + "Headphone report failed: %d\n", + ret); + } else { + dev_warn(arizona->dev, "Button with no mic: %x\n", + val); + } + } else { + dev_dbg(arizona->dev, "Mic button released\n"); + } + +handled: + pm_runtime_mark_last_busy(info->dev); + mutex_unlock(&info->lock); + + return IRQ_HANDLED; +} + +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; + + pm_runtime_get_sync(info->dev); + + mutex_lock(&info->lock); + + ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read jackdet status: %d\n", + ret); + mutex_unlock(&info->lock); + pm_runtime_put_autosuspend(info->dev); + return IRQ_NONE; + } + + if (val & ARIZONA_JD1_STS) { + dev_dbg(arizona->dev, "Detected jack\n"); + ret = extcon_set_cable_state(&info->edev, + ARIZONA_CABLE_MECHANICAL, true); + + if (ret != 0) + dev_err(arizona->dev, "Mechanical report failed: %d\n", + ret); + + arizona_start_mic(info); + } else { + dev_dbg(arizona->dev, "Detected jack removal\n"); + + arizona_stop_mic(info); + + ret = extcon_update_state(&info->edev, 0xffffffff, 0); + if (ret != 0) + dev_err(arizona->dev, "Removal report failed: %d\n", + ret); + } + + mutex_unlock(&info->lock); + + pm_runtime_mark_last_busy(info->dev); + pm_runtime_put_autosuspend(info->dev); + + return IRQ_HANDLED; +} + +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; + + 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"); + ret = -ENOMEM; + goto err; + } + + info->micvdd = devm_regulator_get(arizona->dev, "MICVDD"); + if (IS_ERR(info->micvdd)) { |