All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] Extcon: Arizona: Add driver for Wolfson Arizona class devices
@ 2012-06-28  2:08 MyungJoo Ham
  2012-06-28 10:17 ` Mark Brown
  0 siblings, 1 reply; 5+ messages in thread
From: MyungJoo Ham @ 2012-06-28  2:08 UTC (permalink / raw)
  To: Mark Brown, linux-kernel
  Cc: Greg Kroah-Hartman, 박경민, patches, cw00.choi

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=euc-kr, Size: 5316 bytes --]

> Most Wolfson Arizona class audio hub CODECs include a flexible ultra low
> power accessory detection subsystem. This driver exposes initial support
> for this subsystem via the Extcon framework, implementing support for
> ultra low power detection of headphone and headset with the ability to
> detect the polarity of a headset.
> 
> The functionality of the devices is much richer and more flexible than
> the current driver, future patches will extend the features of the
> driver to more fully exploit this.
> 
> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>


The driver looks good.

I only have some performance concerns that may be ignored
if you don't care of it for this device.

> ---
>  drivers/extcon/Kconfig          |    8 +
>  drivers/extcon/Makefile         |    1 +
>  drivers/extcon/extcon-arizona.c |  491 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 500 insertions(+)
>  create mode 100644 drivers/extcon/extcon-arizona.c
> 

[]

> +
> +#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,
> +};

For ARIZONA_CABLE_HEADPHONE and ARIZONA_CABLE_MECHANICAL, you can
use extcon_cable_name[EXTCON_HEADPHONE_OUT] and
extcon_cable_name[EXTCON_MECHANICAL].

It appears that I need to rephrase line 38-41 of extcon_class.c. Anyway,
it is not recommended to import the whole list. However, it is strongly
recommended to reuse the corresponding entries from the list.



Anyway, the HEADSET appears to be a pair of HEADPHONE and MIC.
You may replace HEADSET with MIC in arizona_cable and remove exclusive[]
and regard HEADPHONE | MIC as "HEADSET".

> +
> +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);

You may use extcon_set_cable_state_ for the performance
as you already know the index of HEADSET. Or extcon_update_state();

> +
> +		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);

Same for here.

[]

> +
> +	/*
> +	 * 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);

Here, too.

[]

> +
> +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);

Here.




Cheers!
MyungJoo

ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] Extcon: Arizona: Add driver for Wolfson Arizona class devices
  2012-06-28  2:08 [PATCH] Extcon: Arizona: Add driver for Wolfson Arizona class devices MyungJoo Ham
@ 2012-06-28 10:17 ` Mark Brown
  0 siblings, 0 replies; 5+ messages in thread
From: Mark Brown @ 2012-06-28 10:17 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: linux-kernel, Greg Kroah-Hartman, 박경민,
	patches, cw00.choi

[-- Attachment #1: Type: text/plain, Size: 2793 bytes --]

On Thu, Jun 28, 2012 at 02:08:10AM +0000, MyungJoo Ham wrote:

> I only have some performance concerns that may be ignored
> if you don't care of it for this device.

To be honest I think if we ever care about performance with extcon we've
got a serious problem - cable insertion shouldn't be happening too
quickly and obviously the userspace API has all the same issues.

> > +#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,
> > +};

> For ARIZONA_CABLE_HEADPHONE and ARIZONA_CABLE_MECHANICAL, you can
> use extcon_cable_name[EXTCON_HEADPHONE_OUT] and
> extcon_cable_name[EXTCON_MECHANICAL].

> It appears that I need to rephrase line 38-41 of extcon_class.c. Anyway,
> it is not recommended to import the whole list. However, it is strongly
> recommended to reuse the corresponding entries from the list.

That's what I initially wanted to do but there's real usability problems
fishing the values out of the array, the obvious method does things like
this:

drivers/extcon/extcon-arizona.c:62: error: initializer element is not constant
drivers/extcon/extcon-arizona.c:62: error: (near initialization for 'arizona_cable[0]')

for example and you don't want the driver to end up looking like line
noise.  Perhaps there's some simple way of doing it that didn't occur to
me but there aren't any examples in tree.

> Anyway, the HEADSET appears to be a pair of HEADPHONE and MIC.
> You may replace HEADSET with MIC in arizona_cable and remove exclusive[]
> and regard HEADPHONE | MIC as "HEADSET".

This was done following the example of the Android switch API which
defines these as separate cable types.  Cable type is probably the wrong
name here but it's a bit late now...

> > +	/* 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);

> You may use extcon_set_cable_state_ for the performance
> as you already know the index of HEADSET. Or extcon_update_state();

I didn't use set_cable_state_ as the _ makes it look like
extcon_set_cable_state() is the intended call, obviously almost every
driver will have the indexes known.  If there's much preferenced here
I'd expect the main function to take the numbers as argument and then
have extcon_set_cable_state_by_name() or something.

extcon_update_state() is a bit annoying to use as you need defines for
both indexes and bits or you need shifting so the code looks ugly.  

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] Extcon: Arizona: Add driver for Wolfson Arizona class devices
  2012-06-25  4:48 함명주
@ 2012-06-25  8:51 ` Mark Brown
  0 siblings, 0 replies; 5+ messages in thread
From: Mark Brown @ 2012-06-25  8:51 UTC (permalink / raw)
  To: 함명주
  Cc: Greg Kroah-Hartman, 박경민, patches, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 767 bytes --]

On Mon, Jun 25, 2012 at 04:48:21AM +0000, 함명주 wrote:

> > +	/* Microphone detection can't use idle mode */
> > +	pm_runtime_get(info->dev);

> Is it alright to use asynchronous get here?
> (sync not required?)

Yes, it's OK to do this - we're guaranteed that the device is already
enabled at the time we decide to start the microphone detection (as
we're in the middle of examining the state which needs a runtime
reference).  We're taking an additional reference here so that the
device doesn't get powered off again after the event has been handled.

> > +	ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_RISE,
> > +				  "JACKDET rise", arizona_jackdet, info);

> Is this arizone_request_irq using threaded irq? (it's fine if so)

Yes.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] Extcon: Arizona: Add driver for Wolfson Arizona class devices
@ 2012-06-25  4:48 함명주
  2012-06-25  8:51 ` Mark Brown
  0 siblings, 1 reply; 5+ messages in thread
From: 함명주 @ 2012-06-25  4:48 UTC (permalink / raw)
  To: Mark Brown, Greg Kroah-Hartman, 박경민
  Cc: patches, linux-kernel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=euc-kr, Size: 4163 bytes --]

> Most Wolfson Arizona class audio hub CODECs include a flexible ultra low
> power accessory detection subsystem. This driver exposes initial support
> for this subsystem via the Extcon framework, implementing support for
> ultra low power detection of headphone and headset with the ability to
> detect the polarity of a headset.
> 
> The functionality of the devices is much richer and more flexible than
> the current driver, future patches will extend the features of the
> driver to more fully exploit this.
> 
> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
> ---

Hello,

> +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);

Is it alright to use asynchronous get here?
(sync not required?)

> +
> +	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 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)) {
> +		ret = PTR_ERR(info->micvdd);
> +		dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
> +		goto err;
> +	}
> +
> +	mutex_init(&info->lock);
> +	info->arizona = arizona;
> +	info->dev = &pdev->dev;
> +	info->detecting = true;
> +	platform_set_drvdata(pdev, info);
> +
> +	switch (arizona->type) {
> +	case WM5102:
> +		switch (arizona->rev) {
> +		case 0:
> +			info->micd_reva = true;
> +			break;
> +		default:
> +			break;
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	info->edev.name = "Headset Jack";
> +	info->edev.supported_cable = arizona_cable;
> +	info->edev.mutually_exclusive = arizona_exclusions;
> +
> +	ret = extcon_dev_register(&info->edev, arizona->dev);
> +	if (ret < 0) {
> +		dev_err(arizona->dev, "extcon_dev_regster() failed: %d\n",
> +			ret);
> +		goto err;
> +	}
> +
> +	if (pdata->num_micd_configs) {
> +		info->micd_modes = pdata->micd_configs;
> +		info->micd_num_modes = pdata->num_micd_configs;
> +	} else {
> +		info->micd_modes = micd_default_modes;
> +		info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
> +	}
> +
> +	if (arizona->pdata.micd_pol_gpio > 0) {
> +		if (info->micd_modes[0].gpio)
> +			mode = GPIOF_OUT_INIT_HIGH;
> +		else
> +			mode = GPIOF_OUT_INIT_LOW;
> +
> +		ret = devm_gpio_request_one(&pdev->dev,
> +					    arizona->pdata.micd_pol_gpio,
> +					    mode,
> +					    "MICD polarity");
> +		if (ret != 0) {
> +			dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
> +				arizona->pdata.micd_pol_gpio, ret);
> +			goto err_register;
> +		}
> +	}
> +
> +	arizona_extcon_set_mode(info, 0);
> +
> +	pm_runtime_enable(&pdev->dev);
> +	pm_runtime_idle(&pdev->dev);
> +	pm_runtime_get_sync(&pdev->dev);
> +
> +	ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_RISE,
> +				  "JACKDET rise", arizona_jackdet, info);

Is this arizone_request_irq using threaded irq? (it's fine if so)


Cheers!
MyungJoo.
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH] Extcon: Arizona: Add driver for Wolfson Arizona class devices
@ 2012-06-24 11:09 Mark Brown
  0 siblings, 0 replies; 5+ messages in thread
From: Mark Brown @ 2012-06-24 11:09 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Myungjoo Ham, Kyungmin Park
  Cc: patches, linux-kernel, Mark Brown

Most Wolfson Arizona class audio hub CODECs include a flexible ultra low
power accessory detection subsystem. This driver exposes initial support
for this subsystem via the Extcon framework, implementing support for
ultra low power detection of headphone and headset with the ability to
detect the polarity of a headset.

The functionality of the devices is much richer and more flexible than
the current driver, future patches will extend the features of the
driver to more fully exploit this.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 drivers/extcon/Kconfig          |    8 +
 drivers/extcon/Makefile         |    1 +
 drivers/extcon/extcon-arizona.c |  491 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 500 insertions(+)
 create mode 100644 drivers/extcon/extcon-arizona.c

diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 29c5cf8..bb385ac 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 86020bd..e932caa 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 0000000..b068bc9
--- /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)) {
+		ret = PTR_ERR(info->micvdd);
+		dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
+		goto err;
+	}
+
+	mutex_init(&info->lock);
+	info->arizona = arizona;
+	info->dev = &pdev->dev;
+	info->detecting = true;
+	platform_set_drvdata(pdev, info);
+
+	switch (arizona->type) {
+	case WM5102:
+		switch (arizona->rev) {
+		case 0:
+			info->micd_reva = true;
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	info->edev.name = "Headset Jack";
+	info->edev.supported_cable = arizona_cable;
+	info->edev.mutually_exclusive = arizona_exclusions;
+
+	ret = extcon_dev_register(&info->edev, arizona->dev);
+	if (ret < 0) {
+		dev_err(arizona->dev, "extcon_dev_regster() failed: %d\n",
+			ret);
+		goto err;
+	}
+
+	if (pdata->num_micd_configs) {
+		info->micd_modes = pdata->micd_configs;
+		info->micd_num_modes = pdata->num_micd_configs;
+	} else {
+		info->micd_modes = micd_default_modes;
+		info->micd_num_modes = ARRAY_SIZE(micd_default_modes);
+	}
+
+	if (arizona->pdata.micd_pol_gpio > 0) {
+		if (info->micd_modes[0].gpio)
+			mode = GPIOF_OUT_INIT_HIGH;
+		else
+			mode = GPIOF_OUT_INIT_LOW;
+
+		ret = devm_gpio_request_one(&pdev->dev,
+					    arizona->pdata.micd_pol_gpio,
+					    mode,
+					    "MICD polarity");
+		if (ret != 0) {
+			dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
+				arizona->pdata.micd_pol_gpio, ret);
+			goto err_register;
+		}
+	}
+
+	arizona_extcon_set_mode(info, 0);
+
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_idle(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
+	ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_RISE,
+				  "JACKDET rise", arizona_jackdet, info);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
+			ret);
+		goto err_register;
+	}
+
+	ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to set JD rise IRQ wake: %d\n",
+			ret);
+		goto err_rise;
+	}
+
+	ret = arizona_request_irq(arizona, ARIZONA_IRQ_JD_FALL,
+				  "JACKDET fall", arizona_jackdet, info);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to get JD fall IRQ: %d\n", ret);
+		goto err_rise_wake;
+	}
+
+	ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 1);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to set JD fall IRQ wake: %d\n",
+			ret);
+		goto err_fall;
+	}
+
+	ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET,
+				  "MICDET", arizona_micdet, info);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to get MICDET IRQ: %d\n", ret);
+		goto err_fall_wake;
+	}
+
+	regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+			   ARIZONA_MICD_BIAS_STARTTIME_MASK |
+			   ARIZONA_MICD_RATE_MASK,
+			   7 << ARIZONA_MICD_BIAS_STARTTIME_SHIFT |
+			   8 << ARIZONA_MICD_RATE_SHIFT);
+
+	arizona_clk32k_enable(arizona);
+	regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE,
+			   ARIZONA_JD1_DB, ARIZONA_JD1_DB);
+	regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
+			   ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
+
+	pm_runtime_put(&pdev->dev);
+
+	return 0;
+
+err_fall_wake:
+	arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
+err_fall:
+	arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info);
+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_register:
+	pm_runtime_disable(&pdev->dev);
+	extcon_dev_unregister(&info->edev);
+err:
+	return ret;
+}
+
+static int __devexit arizona_extcon_remove(struct platform_device *pdev)
+{
+	struct arizona_extcon_info *info = platform_get_drvdata(pdev);
+	struct arizona *arizona = info->arizona;
+
+	pm_runtime_disable(&pdev->dev);
+
+	arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
+	arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
+	arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
+	arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
+	arizona_free_irq(arizona, ARIZONA_IRQ_JD_FALL, info);
+	regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
+			   ARIZONA_JD1_ENA, 0);
+	arizona_clk32k_disable(arizona);
+	extcon_dev_unregister(&info->edev);
+
+	return 0;
+}
+
+static struct platform_driver arizona_extcon_driver = {
+	.driver		= {
+		.name	= "arizona-extcon",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= arizona_extcon_probe,
+	.remove		= __devexit_p(arizona_extcon_remove),
+};
+
+module_platform_driver(arizona_extcon_driver);
+
+MODULE_DESCRIPTION("Arizona Extcon driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:extcon-arizona");
-- 
1.7.10


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2012-06-28 10:17 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-06-28  2:08 [PATCH] Extcon: Arizona: Add driver for Wolfson Arizona class devices MyungJoo Ham
2012-06-28 10:17 ` Mark Brown
  -- strict thread matches above, loose matches on Subject: below --
2012-06-25  4:48 함명주
2012-06-25  8:51 ` Mark Brown
2012-06-24 11:09 Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.