ASoC: twl6040: Add jack support for headset and handset
authorJorge Eduardo Candelaria <jorge.candelaria@ti.com>
Sat, 11 Dec 2010 02:45:17 +0000 (20:45 -0600)
committerLiam Girdwood <lrg@slimlogic.co.uk>
Tue, 14 Dec 2010 20:31:54 +0000 (20:31 +0000)
This patch adds support for reporting twl6040 headset and
handset jack events.

The machine driver retrieves and report the status  through
twl6040_hs_jack_detect.

A workq is used to debounce of the irq.

Signed-off-by: Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com>
Signed-off-by: Margarita Olaya Cabrera <magi.olaya@ti.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
sound/soc/codecs/twl6040.c
sound/soc/codecs/twl6040.h

index b92f2b737e4cfbec222a429e66de9f1eebd8476b..5d7e2f7b194ca000bd8bf568c02537e44bf57a95 100644 (file)
 #define TWL6040_RATES   (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 #define TWL6040_FORMATS         (SNDRV_PCM_FMTBIT_S32_LE)
 
+struct twl6040_jack_data {
+       struct snd_soc_jack *jack;
+       int report;
+};
+
 /* codec private data */
 struct twl6040_data {
        int audpwron;
@@ -52,6 +57,11 @@ struct twl6040_data {
        unsigned int sysclk;
        struct snd_pcm_hw_constraint_list *sysclk_constraints;
        struct completion ready;
+       struct twl6040_jack_data hs_jack;
+       struct snd_soc_codec *codec;
+       struct workqueue_struct *workqueue;
+       struct delayed_work delayed_work;
+       struct mutex mutex;
 };
 
 /*
@@ -381,6 +391,47 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+void twl6040_hs_jack_report(struct snd_soc_codec *codec,
+                               struct snd_soc_jack *jack, int report)
+{
+       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+       int status;
+
+       mutex_lock(&priv->mutex);
+
+       /* Sync status */
+       status = twl6040_read_reg_volatile(codec, TWL6040_REG_STATUS);
+       if (status & TWL6040_PLUGCOMP)
+               snd_soc_jack_report(jack, report, report);
+       else
+               snd_soc_jack_report(jack, 0, report);
+
+       mutex_unlock(&priv->mutex);
+}
+
+void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
+                               struct snd_soc_jack *jack, int report)
+{
+       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+       struct twl6040_jack_data *hs_jack = &priv->hs_jack;
+
+       hs_jack->jack = jack;
+       hs_jack->report = report;
+
+       twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
+}
+EXPORT_SYMBOL_GPL(twl6040_hs_jack_detect);
+
+static void twl6040_accessory_work(struct work_struct *work)
+{
+       struct twl6040_data *priv = container_of(work,
+                                       struct twl6040_data, delayed_work.work);
+       struct snd_soc_codec *codec = priv->codec;
+       struct twl6040_jack_data *hs_jack = &priv->hs_jack;
+
+       twl6040_hs_jack_report(codec, hs_jack->jack, hs_jack->report);
+}
+
 /* audio interrupt handler */
 static irqreturn_t twl6040_naudint_handler(int irq, void *data)
 {
@@ -396,6 +447,9 @@ static irqreturn_t twl6040_naudint_handler(int irq, void *data)
                break;
        case TWL6040_PLUGINT:
        case TWL6040_UNPLUGINT:
+               queue_delayed_work(priv->workqueue, &priv->delayed_work,
+                                                       msecs_to_jiffies(200));
+               break;
        case TWL6040_HOOKINT:
                break;
        case TWL6040_HFINT:
@@ -1023,6 +1077,8 @@ static int twl6040_probe(struct snd_soc_codec *codec)
                return -ENOMEM;
        snd_soc_codec_set_drvdata(codec, priv);
 
+       priv->codec = codec;
+
        if (twl_codec) {
                audpwron = twl_codec->audpwron_gpio;
                naudint = twl_codec->naudint_irq;
@@ -1033,6 +1089,14 @@ static int twl6040_probe(struct snd_soc_codec *codec)
 
        priv->audpwron = audpwron;
        priv->naudint = naudint;
+       priv->workqueue = create_singlethread_workqueue("twl6040-codec");
+
+       if (!priv->workqueue)
+               goto work_err;
+
+       INIT_DELAYED_WORK(&priv->delayed_work, twl6040_accessory_work);
+
+       mutex_init(&priv->mutex);
 
        init_completion(&priv->ready);
 
@@ -1089,6 +1153,8 @@ gpio2_err:
        if (gpio_is_valid(audpwron))
                gpio_free(audpwron);
 gpio1_err:
+       destroy_workqueue(priv->workqueue);
+work_err:
        kfree(priv);
        return ret;
 }
@@ -1107,6 +1173,7 @@ static int twl6040_remove(struct snd_soc_codec *codec)
        if (naudint)
                free_irq(naudint, codec);
 
+       destroy_workqueue(priv->workqueue);
        kfree(priv);
 
        return 0;
index f7c77fa58a3c4b5de3e836dfd7cdc98b4c15b52f..67396f63c1ecf8df41cc2e8e765abe621a3fba2b 100644 (file)
 #define TWL6040_HPPLL_ID               1
 #define TWL6040_LPPLL_ID               2
 
+/* STATUS (0x2E) fields */
+
+#define TWL6040_PLUGCOMP               0x02
+
+void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
+                           struct snd_soc_jack *jack, int report);
+
 #endif /* End of __TWL6040_H__ */