From f60596d61fc238befd169ea394ba6a458fafd774 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 24 Jun 2013 15:42:03 +0200 Subject: ASoC: twl6040: Drop using devm_request_threaded_irq() We need to free the irq at twl6040_remove() which is called when the machine driver has been removed (the card has been removed). If we fail to do that, next time when the machine driver is loaded the codec's probe will fail since the irq has been already requested. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl6040.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 9b9a6e5..c2f2fdb 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1143,7 +1143,7 @@ static int twl6040_probe(struct snd_soc_codec *codec) mutex_init(&priv->mutex); - ret = devm_request_threaded_irq(codec->dev, priv->plug_irq, NULL, + ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, IRQF_NO_SUSPEND, "twl6040_irq_plug", codec); if (ret) { @@ -1159,6 +1159,9 @@ static int twl6040_probe(struct snd_soc_codec *codec) static int twl6040_remove(struct snd_soc_codec *codec) { + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + free_irq(priv->plug_irq, codec); twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; -- cgit v1.1 From 68897497aa2e1eb9c3a6b55f8212cd1edc22acd5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 24 Jun 2013 15:42:05 +0200 Subject: ASoC: twl6040: Assign id for each DAI Later we can identify the DAIs by this ID number. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl6040.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index c2f2fdb..9ea3dbc 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -38,6 +38,14 @@ #include "twl6040.h" +enum twl6040_dai_id { + TWL6040_DAI_LEGACY = 0, + TWL6040_DAI_UL, + TWL6040_DAI_DL1, + TWL6040_DAI_DL2, + TWL6040_DAI_VIB, +}; + #define TWL6040_RATES SNDRV_PCM_RATE_8000_96000 #define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) @@ -1036,6 +1044,7 @@ static const struct snd_soc_dai_ops twl6040_dai_ops = { static struct snd_soc_dai_driver twl6040_dai[] = { { .name = "twl6040-legacy", + .id = TWL6040_DAI_LEGACY, .playback = { .stream_name = "Legacy Playback", .channels_min = 1, @@ -1054,6 +1063,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = { }, { .name = "twl6040-ul", + .id = TWL6040_DAI_UL, .capture = { .stream_name = "Capture", .channels_min = 1, @@ -1065,6 +1075,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = { }, { .name = "twl6040-dl1", + .id = TWL6040_DAI_DL1, .playback = { .stream_name = "Headset Playback", .channels_min = 1, @@ -1076,6 +1087,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = { }, { .name = "twl6040-dl2", + .id = TWL6040_DAI_DL2, .playback = { .stream_name = "Handsfree Playback", .channels_min = 1, @@ -1087,6 +1099,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = { }, { .name = "twl6040-vib", + .id = TWL6040_DAI_VIB, .playback = { .stream_name = "Vibra Playback", .channels_min = 1, -- cgit v1.1 From 98c5fb1f875732e49ce223ba920204ec57f51511 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 24 Jun 2013 15:42:06 +0200 Subject: ASoC: twl6040: Add digital mute support To reduce pop noise during playback stream start and stop the codec needs to have the digital_mute callback implemented. The codec need to be muted before the CPU dai has been stopped (McPDM). Stopping the McPDM will generate a pop on the codec since no signal on the PDM bus means full negative amplitude. By managing the mute/unmute state of the outputs we can decrease the amount of pop noise when playback starts or stops. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl6040.c | 91 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 9ea3dbc..44621dd 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -75,6 +75,8 @@ struct twl6040_data { int pll_power_mode; int hs_power_mode; int hs_power_mode_locked; + bool dl1_unmuted; + bool dl2_unmuted; unsigned int clk_in; unsigned int sysclk; struct twl6040_jack_data hs_jack; @@ -228,6 +230,25 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, return value; } +static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec, + unsigned int reg) +{ + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + switch (reg) { + case TWL6040_REG_HSLCTL: + case TWL6040_REG_HSRCTL: + case TWL6040_REG_EARCTL: + /* DL1 path */ + return priv->dl1_unmuted; + case TWL6040_REG_HFLCTL: + case TWL6040_REG_HFRCTL: + return priv->dl2_unmuted; + default: + return 1; + }; +} + /* * write to the twl6040 register space */ @@ -240,7 +261,8 @@ static int twl6040_write(struct snd_soc_codec *codec, return -EIO; twl6040_write_reg_cache(codec, reg, value); - if (likely(reg < TWL6040_REG_SW_SHADOW)) + if (likely(reg < TWL6040_REG_SW_SHADOW) && + twl6040_is_path_unmuted(codec, reg)) return twl6040_reg_write(twl6040, reg, value); else return 0; @@ -1034,11 +1056,78 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, return 0; } +static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id, + int mute) +{ + struct twl6040 *twl6040 = codec->control_data; + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int hslctl, hsrctl, earctl; + int hflctl, hfrctl; + + switch (id) { + case TWL6040_DAI_DL1: + hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); + hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL); + earctl = twl6040_read_reg_cache(codec, TWL6040_REG_EARCTL); + + if (mute) { + /* Power down drivers and DACs */ + earctl &= ~0x01; + hslctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA); + hsrctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA); + + } + + twl6040_reg_write(twl6040, TWL6040_REG_EARCTL, earctl); + twl6040_reg_write(twl6040, TWL6040_REG_HSLCTL, hslctl); + twl6040_reg_write(twl6040, TWL6040_REG_HSRCTL, hsrctl); + priv->dl1_unmuted = !mute; + break; + case TWL6040_DAI_DL2: + hflctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFLCTL); + hfrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFRCTL); + + if (mute) { + /* Power down drivers and DACs */ + hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA | + TWL6040_HFDRVENA); + hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA | + TWL6040_HFDRVENA); + } + + twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl); + twl6040_reg_write(twl6040, TWL6040_REG_HFRCTL, hfrctl); + priv->dl2_unmuted = !mute; + break; + default: + break; + }; +} + +static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute) +{ + switch (dai->id) { + case TWL6040_DAI_LEGACY: + twl6040_mute_path(dai->codec, TWL6040_DAI_DL1, mute); + twl6040_mute_path(dai->codec, TWL6040_DAI_DL2, mute); + break; + case TWL6040_DAI_DL1: + case TWL6040_DAI_DL2: + twl6040_mute_path(dai->codec, dai->id, mute); + break; + default: + break; + } + + return 0; +} + static const struct snd_soc_dai_ops twl6040_dai_ops = { .startup = twl6040_startup, .hw_params = twl6040_hw_params, .prepare = twl6040_prepare, .set_sysclk = twl6040_set_dai_sysclk, + .digital_mute = twl6040_digital_mute, }; static struct snd_soc_dai_driver twl6040_dai[] = { -- cgit v1.1