aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/twl6040.c
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@ti.com>2013-06-24 09:42:06 -0400
committerMark Brown <broonie@linaro.org>2013-06-24 11:06:03 -0400
commit98c5fb1f875732e49ce223ba920204ec57f51511 (patch)
tree1fc931d26a50230c9dc2be910e71d3f7bda82bc9 /sound/soc/codecs/twl6040.c
parent68897497aa2e1eb9c3a6b55f8212cd1edc22acd5 (diff)
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 <peter.ujfalusi@ti.com> Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'sound/soc/codecs/twl6040.c')
-rw-r--r--sound/soc/codecs/twl6040.c91
1 files changed, 90 insertions, 1 deletions
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 9ea3dbccc0b3..44621ddc332d 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -75,6 +75,8 @@ struct twl6040_data {
75 int pll_power_mode; 75 int pll_power_mode;
76 int hs_power_mode; 76 int hs_power_mode;
77 int hs_power_mode_locked; 77 int hs_power_mode_locked;
78 bool dl1_unmuted;
79 bool dl2_unmuted;
78 unsigned int clk_in; 80 unsigned int clk_in;
79 unsigned int sysclk; 81 unsigned int sysclk;
80 struct twl6040_jack_data hs_jack; 82 struct twl6040_jack_data hs_jack;
@@ -228,6 +230,25 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
228 return value; 230 return value;
229} 231}
230 232
233static bool twl6040_is_path_unmuted(struct snd_soc_codec *codec,
234 unsigned int reg)
235{
236 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
237
238 switch (reg) {
239 case TWL6040_REG_HSLCTL:
240 case TWL6040_REG_HSRCTL:
241 case TWL6040_REG_EARCTL:
242 /* DL1 path */
243 return priv->dl1_unmuted;
244 case TWL6040_REG_HFLCTL:
245 case TWL6040_REG_HFRCTL:
246 return priv->dl2_unmuted;
247 default:
248 return 1;
249 };
250}
251
231/* 252/*
232 * write to the twl6040 register space 253 * write to the twl6040 register space
233 */ 254 */
@@ -240,7 +261,8 @@ static int twl6040_write(struct snd_soc_codec *codec,
240 return -EIO; 261 return -EIO;
241 262
242 twl6040_write_reg_cache(codec, reg, value); 263 twl6040_write_reg_cache(codec, reg, value);
243 if (likely(reg < TWL6040_REG_SW_SHADOW)) 264 if (likely(reg < TWL6040_REG_SW_SHADOW) &&
265 twl6040_is_path_unmuted(codec, reg))
244 return twl6040_reg_write(twl6040, reg, value); 266 return twl6040_reg_write(twl6040, reg, value);
245 else 267 else
246 return 0; 268 return 0;
@@ -1034,11 +1056,78 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
1034 return 0; 1056 return 0;
1035} 1057}
1036 1058
1059static void twl6040_mute_path(struct snd_soc_codec *codec, enum twl6040_dai_id id,
1060 int mute)
1061{
1062 struct twl6040 *twl6040 = codec->control_data;
1063 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
1064 int hslctl, hsrctl, earctl;
1065 int hflctl, hfrctl;
1066
1067 switch (id) {
1068 case TWL6040_DAI_DL1:
1069 hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
1070 hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
1071 earctl = twl6040_read_reg_cache(codec, TWL6040_REG_EARCTL);
1072
1073 if (mute) {
1074 /* Power down drivers and DACs */
1075 earctl &= ~0x01;
1076 hslctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
1077 hsrctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
1078
1079 }
1080
1081 twl6040_reg_write(twl6040, TWL6040_REG_EARCTL, earctl);
1082 twl6040_reg_write(twl6040, TWL6040_REG_HSLCTL, hslctl);
1083 twl6040_reg_write(twl6040, TWL6040_REG_HSRCTL, hsrctl);
1084 priv->dl1_unmuted = !mute;
1085 break;
1086 case TWL6040_DAI_DL2:
1087 hflctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFLCTL);
1088 hfrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HFRCTL);
1089
1090 if (mute) {
1091 /* Power down drivers and DACs */
1092 hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
1093 TWL6040_HFDRVENA);
1094 hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
1095 TWL6040_HFDRVENA);
1096 }
1097
1098 twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
1099 twl6040_reg_write(twl6040, TWL6040_REG_HFRCTL, hfrctl);
1100 priv->dl2_unmuted = !mute;
1101 break;
1102 default:
1103 break;
1104 };
1105}
1106
1107static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
1108{
1109 switch (dai->id) {
1110 case TWL6040_DAI_LEGACY:
1111 twl6040_mute_path(dai->codec, TWL6040_DAI_DL1, mute);
1112 twl6040_mute_path(dai->codec, TWL6040_DAI_DL2, mute);
1113 break;
1114 case TWL6040_DAI_DL1:
1115 case TWL6040_DAI_DL2:
1116 twl6040_mute_path(dai->codec, dai->id, mute);
1117 break;
1118 default:
1119 break;
1120 }
1121
1122 return 0;
1123}
1124
1037static const struct snd_soc_dai_ops twl6040_dai_ops = { 1125static const struct snd_soc_dai_ops twl6040_dai_ops = {
1038 .startup = twl6040_startup, 1126 .startup = twl6040_startup,
1039 .hw_params = twl6040_hw_params, 1127 .hw_params = twl6040_hw_params,
1040 .prepare = twl6040_prepare, 1128 .prepare = twl6040_prepare,
1041 .set_sysclk = twl6040_set_dai_sysclk, 1129 .set_sysclk = twl6040_set_dai_sysclk,
1130 .digital_mute = twl6040_digital_mute,
1042}; 1131};
1043 1132
1044static struct snd_soc_dai_driver twl6040_dai[] = { 1133static struct snd_soc_dai_driver twl6040_dai[] = {