aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@linaro.org>2013-06-30 07:42:25 -0400
committerMark Brown <broonie@linaro.org>2013-06-30 07:42:25 -0400
commite78e4c49e72db2e040fe986295acbb76811c625b (patch)
tree1c6100d7d61aec6f2e40810bd3a1b87c2495b045 /sound
parent233d6a903e5efd1eb4d4ee245ab79c12d2f8df73 (diff)
parent98c5fb1f875732e49ce223ba920204ec57f51511 (diff)
Merge remote-tracking branch 'asoc/topic/twl6040' into asoc-next
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/twl6040.c109
1 files changed, 107 insertions, 2 deletions
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 9b9a6e587610..44621ddc332d 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -38,6 +38,14 @@
38 38
39#include "twl6040.h" 39#include "twl6040.h"
40 40
41enum twl6040_dai_id {
42 TWL6040_DAI_LEGACY = 0,
43 TWL6040_DAI_UL,
44 TWL6040_DAI_DL1,
45 TWL6040_DAI_DL2,
46 TWL6040_DAI_VIB,
47};
48
41#define TWL6040_RATES SNDRV_PCM_RATE_8000_96000 49#define TWL6040_RATES SNDRV_PCM_RATE_8000_96000
42#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) 50#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
43 51
@@ -67,6 +75,8 @@ struct twl6040_data {
67 int pll_power_mode; 75 int pll_power_mode;
68 int hs_power_mode; 76 int hs_power_mode;
69 int hs_power_mode_locked; 77 int hs_power_mode_locked;
78 bool dl1_unmuted;
79 bool dl2_unmuted;
70 unsigned int clk_in; 80 unsigned int clk_in;
71 unsigned int sysclk; 81 unsigned int sysclk;
72 struct twl6040_jack_data hs_jack; 82 struct twl6040_jack_data hs_jack;
@@ -220,6 +230,25 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
220 return value; 230 return value;
221} 231}
222 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
223/* 252/*
224 * write to the twl6040 register space 253 * write to the twl6040 register space
225 */ 254 */
@@ -232,7 +261,8 @@ static int twl6040_write(struct snd_soc_codec *codec,
232 return -EIO; 261 return -EIO;
233 262
234 twl6040_write_reg_cache(codec, reg, value); 263 twl6040_write_reg_cache(codec, reg, value);
235 if (likely(reg < TWL6040_REG_SW_SHADOW)) 264 if (likely(reg < TWL6040_REG_SW_SHADOW) &&
265 twl6040_is_path_unmuted(codec, reg))
236 return twl6040_reg_write(twl6040, reg, value); 266 return twl6040_reg_write(twl6040, reg, value);
237 else 267 else
238 return 0; 268 return 0;
@@ -1026,16 +1056,84 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
1026 return 0; 1056 return 0;
1027} 1057}
1028 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
1029static const struct snd_soc_dai_ops twl6040_dai_ops = { 1125static const struct snd_soc_dai_ops twl6040_dai_ops = {
1030 .startup = twl6040_startup, 1126 .startup = twl6040_startup,
1031 .hw_params = twl6040_hw_params, 1127 .hw_params = twl6040_hw_params,
1032 .prepare = twl6040_prepare, 1128 .prepare = twl6040_prepare,
1033 .set_sysclk = twl6040_set_dai_sysclk, 1129 .set_sysclk = twl6040_set_dai_sysclk,
1130 .digital_mute = twl6040_digital_mute,
1034}; 1131};
1035 1132
1036static struct snd_soc_dai_driver twl6040_dai[] = { 1133static struct snd_soc_dai_driver twl6040_dai[] = {
1037{ 1134{
1038 .name = "twl6040-legacy", 1135 .name = "twl6040-legacy",
1136 .id = TWL6040_DAI_LEGACY,
1039 .playback = { 1137 .playback = {
1040 .stream_name = "Legacy Playback", 1138 .stream_name = "Legacy Playback",
1041 .channels_min = 1, 1139 .channels_min = 1,
@@ -1054,6 +1152,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
1054}, 1152},
1055{ 1153{
1056 .name = "twl6040-ul", 1154 .name = "twl6040-ul",
1155 .id = TWL6040_DAI_UL,
1057 .capture = { 1156 .capture = {
1058 .stream_name = "Capture", 1157 .stream_name = "Capture",
1059 .channels_min = 1, 1158 .channels_min = 1,
@@ -1065,6 +1164,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
1065}, 1164},
1066{ 1165{
1067 .name = "twl6040-dl1", 1166 .name = "twl6040-dl1",
1167 .id = TWL6040_DAI_DL1,
1068 .playback = { 1168 .playback = {
1069 .stream_name = "Headset Playback", 1169 .stream_name = "Headset Playback",
1070 .channels_min = 1, 1170 .channels_min = 1,
@@ -1076,6 +1176,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
1076}, 1176},
1077{ 1177{
1078 .name = "twl6040-dl2", 1178 .name = "twl6040-dl2",
1179 .id = TWL6040_DAI_DL2,
1079 .playback = { 1180 .playback = {
1080 .stream_name = "Handsfree Playback", 1181 .stream_name = "Handsfree Playback",
1081 .channels_min = 1, 1182 .channels_min = 1,
@@ -1087,6 +1188,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
1087}, 1188},
1088{ 1189{
1089 .name = "twl6040-vib", 1190 .name = "twl6040-vib",
1191 .id = TWL6040_DAI_VIB,
1090 .playback = { 1192 .playback = {
1091 .stream_name = "Vibra Playback", 1193 .stream_name = "Vibra Playback",
1092 .channels_min = 1, 1194 .channels_min = 1,
@@ -1143,7 +1245,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
1143 1245
1144 mutex_init(&priv->mutex); 1246 mutex_init(&priv->mutex);
1145 1247
1146 ret = devm_request_threaded_irq(codec->dev, priv->plug_irq, NULL, 1248 ret = request_threaded_irq(priv->plug_irq, NULL,
1147 twl6040_audio_handler, IRQF_NO_SUSPEND, 1249 twl6040_audio_handler, IRQF_NO_SUSPEND,
1148 "twl6040_irq_plug", codec); 1250 "twl6040_irq_plug", codec);
1149 if (ret) { 1251 if (ret) {
@@ -1159,6 +1261,9 @@ static int twl6040_probe(struct snd_soc_codec *codec)
1159 1261
1160static int twl6040_remove(struct snd_soc_codec *codec) 1262static int twl6040_remove(struct snd_soc_codec *codec)
1161{ 1263{
1264 struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
1265
1266 free_irq(priv->plug_irq, codec);
1162 twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); 1267 twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
1163 1268
1164 return 0; 1269 return 0;