[alsa-devel] [PATCH v2 07/10] ASoC: upd9976: add jack detection function
Lu Guanqun
guanqun.lu at intel.com
Fri May 6 07:46:34 CEST 2011
All the interrupt handling is done here in codec driver.
Signed-off-by: Lu Guanqun <guanqun.lu at intel.com>
---
sound/soc/codecs/upd9976.c | 182 ++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/upd9976.h | 4 +
2 files changed, 184 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/upd9976.c b/sound/soc/codecs/upd9976.c
index 4a790c5..c1b08f9 100644
--- a/sound/soc/codecs/upd9976.c
+++ b/sound/soc/codecs/upd9976.c
@@ -27,6 +27,8 @@
*/
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/list.h>
#include <asm/intel_scu_ipc.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -37,6 +39,97 @@
#include <sound/jack.h>
#include "upd9976.h"
+struct upd9976_priv {
+ int irq;
+ struct resource *irq_mem;
+ void __iomem *int_base;
+ struct snd_soc_jack *jack;
+ int jack_init;
+ struct list_head pending_status;
+ spinlock_t lock;
+};
+
+struct upd9976_jack_node {
+ struct list_head list;
+ u8 interrupt_status;
+};
+
+static irqreturn_t upd9976_jack_intr_handler(int irq, void *dev)
+{
+ struct upd9976_priv *upd9976 = dev;
+ u8 interrupt_status;
+ struct upd9976_jack_node *node;
+ unsigned long flags;
+
+ memcpy_fromio(&interrupt_status,
+ upd9976->int_base,
+ sizeof(u8));
+
+ /* check if it's valid */
+ if (!(interrupt_status & 0xf))
+ return IRQ_NONE;
+
+ node = kzalloc(sizeof(*node), GFP_ATOMIC);
+ if (!node)
+ return IRQ_NONE;
+
+ INIT_LIST_HEAD(&node->list);
+ node->interrupt_status = interrupt_status;
+
+ spin_lock_irqsave(&upd9976->lock, flags);
+ list_add_tail(&node->list, &upd9976->pending_status);
+ spin_unlock_irqrestore(&upd9976->lock, flags);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t upd9976_jack_thread_handler(int irq, void *data)
+{
+ struct upd9976_priv *upd9976 = data;
+ struct snd_soc_jack *jack = upd9976->jack;
+ struct snd_soc_codec *codec = jack->codec;
+ struct upd9976_jack_node *node;
+ u8 interrupt_status;
+ unsigned long flags;
+ int status = 0;
+ unsigned int value = 0;
+ int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
+
+ spin_lock_irqsave(&upd9976->lock, flags);
+ if (list_empty(&upd9976->pending_status)) {
+ spin_unlock_irqrestore(&upd9976->lock, flags);
+ return IRQ_HANDLED;
+ }
+ node = list_first_entry(&upd9976->pending_status,
+ struct upd9976_jack_node,
+ list);
+ interrupt_status = node->interrupt_status;
+ list_del(&node->list);
+ kfree(node);
+ spin_unlock_irqrestore(&upd9976->lock, flags);
+
+ if (interrupt_status & 0x3)
+ value = snd_soc_read(codec, UPD9976_SAUXINT);
+
+ if (interrupt_status & 0x1 && value == 0x1)
+ status |= SND_JACK_HEADSET;
+
+ if (interrupt_status & 0x2 && value == 0x2)
+ status |= SND_JACK_HEADPHONE;
+
+ if (interrupt_status & 0x4)
+ status |= SND_JACK_HEADSET | SND_JACK_BTN_0;
+
+ if (interrupt_status & 0x8)
+ status |= SND_JACK_HEADSET | SND_JACK_BTN_1;
+
+ snd_soc_jack_report(jack, status, mask);
+ if (status & (SND_JACK_BTN_0 | SND_JACK_BTN_1))
+ snd_soc_jack_report(jack, SND_JACK_HEADSET, mask);
+
+ return IRQ_HANDLED;
+}
+
static inline unsigned int upd9976_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -62,6 +155,24 @@ static inline int upd9976_write(struct snd_soc_codec *codec,
return ret;
}
+int upd9976_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack,
+ unsigned int debounce)
+{
+ struct upd9976_priv *upd9976 = snd_soc_codec_get_drvdata(codec);
+
+ if (!upd9976->jack_init)
+ return -ENODEV;
+
+ upd9976->jack = jack;
+
+ /* set debounce time */
+ snd_soc_write(codec, UPD9976_AUXDBNC, debounce);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(upd9976_jack_detect);
+
/*
* Mixing Volume: from -25 dB to 6 dB in 1 dB steps.
*/
@@ -452,13 +563,50 @@ static int upd9976_set_bias_level(struct snd_soc_codec *codec,
static int upd9976_codec_probe(struct snd_soc_codec *codec)
{
+ struct upd9976_priv *upd9976 = snd_soc_codec_get_drvdata(codec);
+
upd9976_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ /* disable jack detection first, will be enabled later */
+ snd_soc_update_bits(codec, UPD9976_MICCTRL, BIT(4), 0);
+
+ spin_lock_init(&upd9976->lock);
+ INIT_LIST_HEAD(&upd9976->pending_status);
+
+ upd9976->jack_init = 0;
+
+ if (upd9976->irq < 0 || !upd9976->irq_mem)
+ return 0;
+
+ upd9976->int_base = ioremap_nocache(upd9976->irq_mem->start,
+ resource_size(upd9976->irq_mem));
+ if (!upd9976->int_base)
+ return 0;
+
+ if (request_threaded_irq(upd9976->irq,
+ upd9976_jack_intr_handler,
+ upd9976_jack_thread_handler,
+ IRQF_SHARED,
+ "jack detection",
+ upd9976)) {
+ iounmap(upd9976->int_base);
+ return 0;
+ }
+
+ upd9976->jack_init = 1;
+
return 0;
}
static int upd9976_codec_remove(struct snd_soc_codec *codec)
{
+ struct upd9976_priv *upd9976 = snd_soc_codec_get_drvdata(codec);
+
+ if (upd9976->jack_init) {
+ free_irq(upd9976->irq, upd9976);
+ iounmap(upd9976->int_base);
+ }
+
upd9976_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@@ -481,13 +629,43 @@ static struct snd_soc_codec_driver upd9976_codec = {
static int __devinit upd9976_device_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &upd9976_codec,
- upd9976_dais, ARRAY_SIZE(upd9976_dais));
+ struct upd9976_priv *upd9976;
+ int err;
+
+ upd9976 = kzalloc(sizeof(*upd9976), GFP_KERNEL);
+ if (!upd9976) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ upd9976->irq = platform_get_irq(pdev, 0);
+ upd9976->irq_mem = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM,
+ "IRQ_BASE");
+ platform_set_drvdata(pdev, upd9976);
+
+ err = snd_soc_register_codec(&pdev->dev, &upd9976_codec,
+ upd9976_dais, ARRAY_SIZE(upd9976_dais));
+ if (err)
+ goto fail_register_codec;
+
+ return 0;
+
+fail_register_codec:
+ platform_set_drvdata(pdev, NULL);
+ kfree(upd9976);
+fail:
+ return err;
}
static int __devexit upd9976_device_remove(struct platform_device *pdev)
{
+ struct upd9976_priv *upd9976 = platform_get_drvdata(pdev);
+
snd_soc_unregister_codec(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(upd9976);
+
return 0;
}
diff --git a/sound/soc/codecs/upd9976.h b/sound/soc/codecs/upd9976.h
index ab2ea15..b1bb8d0 100644
--- a/sound/soc/codecs/upd9976.h
+++ b/sound/soc/codecs/upd9976.h
@@ -80,4 +80,8 @@
#define UPD9976_SAUXINT 0x132
+int upd9976_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack,
+ unsigned int debounce);
+
#endif
More information about the Alsa-devel
mailing list