summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/tlv320aic31xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/tlv320aic31xx.c')
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c61
1 files changed, 60 insertions, 1 deletions
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index c544a1e35f5e..9b37e98da0db 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -25,6 +25,7 @@
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -89,6 +90,7 @@ static bool aic31xx_volatile(struct device *dev, unsigned int reg)
case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */
case AIC31XX_INTRDACFLAG2:
case AIC31XX_INTRADCFLAG2:
+ case AIC31XX_HSDETECT:
return true;
}
return false;
@@ -163,6 +165,7 @@ struct aic31xx_priv {
struct aic31xx_pdata pdata;
struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES];
struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES];
+ struct snd_soc_jack *jack;
unsigned int sysclk;
u8 p_div;
int rate_div_line;
@@ -1261,6 +1264,20 @@ static int aic31xx_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static int aic31xx_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
+
+ aic31xx->jack = jack;
+
+ /* Enable/Disable jack detection */
+ regmap_write(aic31xx->regmap, AIC31XX_HSDETECT,
+ jack ? AIC31XX_HSD_ENABLE : 0);
+
+ return 0;
+}
+
static int aic31xx_codec_probe(struct snd_soc_component *component)
{
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
@@ -1301,6 +1318,7 @@ static int aic31xx_codec_probe(struct snd_soc_component *component)
static const struct snd_soc_component_driver soc_codec_driver_aic31xx = {
.probe = aic31xx_codec_probe,
+ .set_jack = aic31xx_set_jack,
.set_bias_level = aic31xx_set_bias_level,
.controls = common31xx_snd_controls,
.num_controls = ARRAY_SIZE(common31xx_snd_controls),
@@ -1405,8 +1423,47 @@ static irqreturn_t aic31xx_irq(int irq, void *data)
dev_err(dev, "Short circuit on Left output is detected\n");
if (value & AIC31XX_HPRSCDETECT)
dev_err(dev, "Short circuit on Right output is detected\n");
+ if (value & (AIC31XX_HSPLUG | AIC31XX_BUTTONPRESS)) {
+ unsigned int val;
+ int status = 0;
+
+ ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG2,
+ &val);
+ if (ret) {
+ dev_err(dev, "Failed to read interrupt mask: %d\n",
+ ret);
+ goto exit;
+ }
+
+ if (val & AIC31XX_BUTTONPRESS)
+ status |= SND_JACK_BTN_0;
+
+ ret = regmap_read(aic31xx->regmap, AIC31XX_HSDETECT, &val);
+ if (ret) {
+ dev_err(dev, "Failed to read headset type: %d\n", ret);
+ goto exit;
+ }
+
+ switch ((val & AIC31XX_HSD_TYPE_MASK) >>
+ AIC31XX_HSD_TYPE_SHIFT) {
+ case AIC31XX_HSD_HP:
+ status |= SND_JACK_HEADPHONE;
+ break;
+ case AIC31XX_HSD_HS:
+ status |= SND_JACK_HEADSET;
+ break;
+ default:
+ break;
+ }
+
+ if (aic31xx->jack)
+ snd_soc_jack_report(aic31xx->jack, status,
+ AIC31XX_JACK_MASK);
+ }
if (value & ~(AIC31XX_HPLSCDETECT |
- AIC31XX_HPRSCDETECT))
+ AIC31XX_HPRSCDETECT |
+ AIC31XX_HSPLUG |
+ AIC31XX_BUTTONPRESS))
dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value);
read_overflow:
@@ -1518,6 +1575,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
AIC31XX_GPIO1_FUNC_SHIFT);
regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL,
+ AIC31XX_HSPLUGDET |
+ AIC31XX_BUTTONPRESSDET |
AIC31XX_SC |
AIC31XX_ENGINE);