diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index b92f2b737e4c..5d7e2f7b194c 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -42,6 +42,11 @@ #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; diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h index f7c77fa58a3c..67396f63c1ec 100644 --- a/sound/soc/codecs/twl6040.h +++ b/sound/soc/codecs/twl6040.h @@ -135,4 +135,11 @@ #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__ */