[alsa-devel] [PATCH 0/4] ASoC: twl6040: Fixes and updates
Hi,
Updates for the twl6040 codec: - Fix soc probe when the machine driver is removed and loaded again without removing the codec driver (was failing since the IRQ has been already requested) - Playback pop noise reduction on stream start and stop with implementing the digital_mute callback and related tracking of the mute for different paths.
Regards, Peter --- Peter Ujfalusi (4): ASoC: twl6040: Drop using devm_request_threaded_irq() MFD: twl6040: Update register bit definitions ASoC: twl6040: Assign id for each DAI ASoC: twl6040: Add digital mute support
include/linux/mfd/twl6040.h | 7 +++ sound/soc/codecs/twl6040.c | 109 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 114 insertions(+), 2 deletions(-)
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 peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
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;
On Mon, Jun 24, 2013 at 03:42:03PM +0200, Peter Ujfalusi wrote:
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.
Applied all, thanks.
Add define for: HSDRV, HFDAC, HFPGA and HFDRV enable bits
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Samuel Ortiz sameo@linux.intel.com --- include/linux/mfd/twl6040.h | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 94ac944..7e7fbce 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -125,8 +125,15 @@
#define TWL6040_HSDACENA (1 << 0) #define TWL6040_HSDACMODE (1 << 1) +#define TWL6040_HSDRVENA (1 << 2) #define TWL6040_HSDRVMODE (1 << 3)
+/* HFLCTL/R (0x14/0x16) fields */ + +#define TWL6040_HFDACENA (1 << 0) +#define TWL6040_HFPGAENA (1 << 1) +#define TWL6040_HFDRVENA (1 << 4) + /* VIBCTLL/R (0x18/0x1A) fields */
#define TWL6040_VIBENA (1 << 0)
Hi Peter,
On Mon, Jun 24, 2013 at 03:42:04PM +0200, Peter Ujfalusi wrote:
Add define for: HSDRV, HFDAC, HFPGA and HFDRV enable bits
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com CC: Samuel Ortiz sameo@linux.intel.com
include/linux/mfd/twl6040.h | 7 +++++++ 1 file changed, 7 insertions(+)
I don't see the rest of the patchset, so I can't tell if there's a build time dependeny between this one and the other patches. I applied it for now, please let me know if you'd like the whole patchset to go through another tree at once.
Cheers, Samuel.
On Thu, Jun 27, 2013 at 10:05:47AM +0200, Samuel Ortiz wrote:
I don't see the rest of the patchset, so I can't tell if there's a build time dependeny between this one and the other patches. I applied it for now, please let me know if you'd like the whole patchset to go through another tree at once.
There's build dependencies - the new register bits are used by the following patches. I'd already applied this to ASoC and sent it upstream unfortunately so I can't rebase it out or pull in your commit instead sadly.
On Thu, Jun 27, 2013 at 09:20:03AM +0100, Mark Brown wrote:
On Thu, Jun 27, 2013 at 10:05:47AM +0200, Samuel Ortiz wrote:
I don't see the rest of the patchset, so I can't tell if there's a build time dependeny between this one and the other patches. I applied it for now, please let me know if you'd like the whole patchset to go through another tree at once.
There's build dependencies - the new register bits are used by the following patches. I'd already applied this to ASoC and sent it upstream unfortunately so I can't rebase it out or pull in your commit instead sadly.
No worries, that shouldn't cause any merge issues. I dropped it from mfd-next.
Cheers, Samuel.
Later we can identify the DAIs by this ID number.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
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,
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 peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 91 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-)
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[] = {
participants (3)
-
Mark Brown
-
Peter Ujfalusi
-
Samuel Ortiz