#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#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;
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;
};
/*
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
- twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg);
+ twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &value, reg);
twl6040_write_reg_cache(codec, reg, value);
return value;
return -EIO;
twl6040_write_reg_cache(codec, reg, value);
- return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
+ return twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, value, reg);
}
static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
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)
{
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
u8 intid;
- twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID);
+ twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID);
switch (intid) {
case TWL6040_THINT:
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:
static int twl6040_add_widgets(struct snd_soc_codec *codec)
{
- snd_soc_dapm_new_controls(codec, twl6040_dapm_widgets,
- ARRAY_SIZE(twl6040_dapm_widgets));
-
- snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
- snd_soc_dapm_new_widgets(codec);
+ snd_soc_dapm_new_controls(dapm, twl6040_dapm_widgets,
+ ARRAY_SIZE(twl6040_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+ snd_soc_dapm_new_widgets(dapm);
return 0;
}
msecs_to_jiffies(48));
if (!time_left) {
- twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &intid,
+ twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid,
TWL6040_REG_INTID);
if (!(intid & TWL6040_READYINT)) {
dev_err(codec->dev, "timeout waiting for READYINT\n");
break;
}
- codec->bias_level = level;
+ codec->dapm.bias_level = level;
return 0;
}
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;
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);
if (gpio_is_valid(audpwron))
gpio_free(audpwron);
gpio1_err:
+ destroy_workqueue(priv->workqueue);
+work_err:
kfree(priv);
return ret;
}
if (naudint)
free_irq(naudint, codec);
+ destroy_workqueue(priv->workqueue);
kfree(priv);
return 0;