diff options
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 452 |
1 files changed, 222 insertions, 230 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 4df7c6c61c76..5f1681f6ca76 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -120,9 +120,10 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | |||
120 | 120 | ||
121 | /* codec private data */ | 121 | /* codec private data */ |
122 | struct twl4030_priv { | 122 | struct twl4030_priv { |
123 | unsigned int bypass_state; | 123 | struct snd_soc_codec codec; |
124 | |||
124 | unsigned int codec_powered; | 125 | unsigned int codec_powered; |
125 | unsigned int codec_muted; | 126 | unsigned int apll_enabled; |
126 | 127 | ||
127 | struct snd_pcm_substream *master_substream; | 128 | struct snd_pcm_substream *master_substream; |
128 | struct snd_pcm_substream *slave_substream; | 129 | struct snd_pcm_substream *slave_substream; |
@@ -183,19 +184,20 @@ static int twl4030_write(struct snd_soc_codec *codec, | |||
183 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | 184 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) |
184 | { | 185 | { |
185 | struct twl4030_priv *twl4030 = codec->private_data; | 186 | struct twl4030_priv *twl4030 = codec->private_data; |
186 | u8 mode; | 187 | int mode; |
187 | 188 | ||
188 | if (enable == twl4030->codec_powered) | 189 | if (enable == twl4030->codec_powered) |
189 | return; | 190 | return; |
190 | 191 | ||
191 | mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); | ||
192 | if (enable) | 192 | if (enable) |
193 | mode |= TWL4030_CODECPDZ; | 193 | mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); |
194 | else | 194 | else |
195 | mode &= ~TWL4030_CODECPDZ; | 195 | mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); |
196 | 196 | ||
197 | twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | 197 | if (mode >= 0) { |
198 | twl4030->codec_powered = enable; | 198 | twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); |
199 | twl4030->codec_powered = enable; | ||
200 | } | ||
199 | 201 | ||
200 | /* REVISIT: this delay is present in TI sample drivers */ | 202 | /* REVISIT: this delay is present in TI sample drivers */ |
201 | /* but there seems to be no TRM requirement for it */ | 203 | /* but there seems to be no TRM requirement for it */ |
@@ -212,31 +214,30 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) | |||
212 | 214 | ||
213 | /* set all audio section registers to reasonable defaults */ | 215 | /* set all audio section registers to reasonable defaults */ |
214 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) | 216 | for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) |
215 | twl4030_write(codec, i, cache[i]); | 217 | if (i != TWL4030_REG_APLL_CTL) |
218 | twl4030_write(codec, i, cache[i]); | ||
216 | 219 | ||
217 | } | 220 | } |
218 | 221 | ||
219 | static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) | 222 | static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) |
220 | { | 223 | { |
221 | struct twl4030_priv *twl4030 = codec->private_data; | 224 | struct twl4030_priv *twl4030 = codec->private_data; |
222 | u8 reg_val; | 225 | int status; |
223 | 226 | ||
224 | if (mute == twl4030->codec_muted) | 227 | if (enable == twl4030->apll_enabled) |
225 | return; | 228 | return; |
226 | 229 | ||
227 | if (mute) { | 230 | if (enable) |
228 | /* Disable PLL */ | ||
229 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); | ||
230 | reg_val &= ~TWL4030_APLL_EN; | ||
231 | twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); | ||
232 | } else { | ||
233 | /* Enable PLL */ | 231 | /* Enable PLL */ |
234 | reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); | 232 | status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL); |
235 | reg_val |= TWL4030_APLL_EN; | 233 | else |
236 | twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); | 234 | /* Disable PLL */ |
237 | } | 235 | status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); |
236 | |||
237 | if (status >= 0) | ||
238 | twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); | ||
238 | 239 | ||
239 | twl4030->codec_muted = mute; | 240 | twl4030->apll_enabled = enable; |
240 | } | 241 | } |
241 | 242 | ||
242 | static void twl4030_power_up(struct snd_soc_codec *codec) | 243 | static void twl4030_power_up(struct snd_soc_codec *codec) |
@@ -613,6 +614,27 @@ static int handsfreerpga_event(struct snd_soc_dapm_widget *w, | |||
613 | return 0; | 614 | return 0; |
614 | } | 615 | } |
615 | 616 | ||
617 | static int vibramux_event(struct snd_soc_dapm_widget *w, | ||
618 | struct snd_kcontrol *kcontrol, int event) | ||
619 | { | ||
620 | twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff); | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | static int apll_event(struct snd_soc_dapm_widget *w, | ||
625 | struct snd_kcontrol *kcontrol, int event) | ||
626 | { | ||
627 | switch (event) { | ||
628 | case SND_SOC_DAPM_PRE_PMU: | ||
629 | twl4030_apll_enable(w->codec, 1); | ||
630 | break; | ||
631 | case SND_SOC_DAPM_POST_PMD: | ||
632 | twl4030_apll_enable(w->codec, 0); | ||
633 | break; | ||
634 | } | ||
635 | return 0; | ||
636 | } | ||
637 | |||
616 | static void headset_ramp(struct snd_soc_codec *codec, int ramp) | 638 | static void headset_ramp(struct snd_soc_codec *codec, int ramp) |
617 | { | 639 | { |
618 | struct snd_soc_device *socdev = codec->socdev; | 640 | struct snd_soc_device *socdev = codec->socdev; |
@@ -724,67 +746,6 @@ static int headsetrpga_event(struct snd_soc_dapm_widget *w, | |||
724 | return 0; | 746 | return 0; |
725 | } | 747 | } |
726 | 748 | ||
727 | static int bypass_event(struct snd_soc_dapm_widget *w, | ||
728 | struct snd_kcontrol *kcontrol, int event) | ||
729 | { | ||
730 | struct soc_mixer_control *m = | ||
731 | (struct soc_mixer_control *)w->kcontrols->private_value; | ||
732 | struct twl4030_priv *twl4030 = w->codec->private_data; | ||
733 | unsigned char reg, misc; | ||
734 | |||
735 | reg = twl4030_read_reg_cache(w->codec, m->reg); | ||
736 | |||
737 | /* | ||
738 | * bypass_state[0:3] - analog HiFi bypass | ||
739 | * bypass_state[4] - analog voice bypass | ||
740 | * bypass_state[5] - digital voice bypass | ||
741 | * bypass_state[6:7] - digital HiFi bypass | ||
742 | */ | ||
743 | if (m->reg == TWL4030_REG_VSTPGA) { | ||
744 | /* Voice digital bypass */ | ||
745 | if (reg) | ||
746 | twl4030->bypass_state |= (1 << 5); | ||
747 | else | ||
748 | twl4030->bypass_state &= ~(1 << 5); | ||
749 | } else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) { | ||
750 | /* Analog bypass */ | ||
751 | if (reg & (1 << m->shift)) | ||
752 | twl4030->bypass_state |= | ||
753 | (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); | ||
754 | else | ||
755 | twl4030->bypass_state &= | ||
756 | ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); | ||
757 | } else if (m->reg == TWL4030_REG_VDL_APGA_CTL) { | ||
758 | /* Analog voice bypass */ | ||
759 | if (reg & (1 << m->shift)) | ||
760 | twl4030->bypass_state |= (1 << 4); | ||
761 | else | ||
762 | twl4030->bypass_state &= ~(1 << 4); | ||
763 | } else { | ||
764 | /* Digital bypass */ | ||
765 | if (reg & (0x7 << m->shift)) | ||
766 | twl4030->bypass_state |= (1 << (m->shift ? 7 : 6)); | ||
767 | else | ||
768 | twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6)); | ||
769 | } | ||
770 | |||
771 | /* Enable master analog loopback mode if any analog switch is enabled*/ | ||
772 | misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1); | ||
773 | if (twl4030->bypass_state & 0x1F) | ||
774 | misc |= TWL4030_FMLOOP_EN; | ||
775 | else | ||
776 | misc &= ~TWL4030_FMLOOP_EN; | ||
777 | twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc); | ||
778 | |||
779 | if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) { | ||
780 | if (twl4030->bypass_state) | ||
781 | twl4030_codec_mute(w->codec, 0); | ||
782 | else | ||
783 | twl4030_codec_mute(w->codec, 1); | ||
784 | } | ||
785 | return 0; | ||
786 | } | ||
787 | |||
788 | /* | 749 | /* |
789 | * Some of the gain controls in TWL (mostly those which are associated with | 750 | * Some of the gain controls in TWL (mostly those which are associated with |
790 | * the outputs) are implemented in an interesting way: | 751 | * the outputs) are implemented in an interesting way: |
@@ -1192,32 +1153,28 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1192 | SND_SOC_NOPM, 0, 0), | 1153 | SND_SOC_NOPM, 0, 0), |
1193 | 1154 | ||
1194 | /* Analog bypasses */ | 1155 | /* Analog bypasses */ |
1195 | SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, | 1156 | SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, |
1196 | &twl4030_dapm_abypassr1_control, bypass_event, | 1157 | &twl4030_dapm_abypassr1_control), |
1197 | SND_SOC_DAPM_POST_REG), | 1158 | SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, |
1198 | SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, | 1159 | &twl4030_dapm_abypassl1_control), |
1199 | &twl4030_dapm_abypassl1_control, | 1160 | SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, |
1200 | bypass_event, SND_SOC_DAPM_POST_REG), | 1161 | &twl4030_dapm_abypassr2_control), |
1201 | SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, | 1162 | SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, |
1202 | &twl4030_dapm_abypassr2_control, | 1163 | &twl4030_dapm_abypassl2_control), |
1203 | bypass_event, SND_SOC_DAPM_POST_REG), | 1164 | SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0, |
1204 | SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, | 1165 | &twl4030_dapm_abypassv_control), |
1205 | &twl4030_dapm_abypassl2_control, | 1166 | |
1206 | bypass_event, SND_SOC_DAPM_POST_REG), | 1167 | /* Master analog loopback switch */ |
1207 | SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0, | 1168 | SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0, |
1208 | &twl4030_dapm_abypassv_control, | 1169 | NULL, 0), |
1209 | bypass_event, SND_SOC_DAPM_POST_REG), | ||
1210 | 1170 | ||
1211 | /* Digital bypasses */ | 1171 | /* Digital bypasses */ |
1212 | SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0, | 1172 | SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0, |
1213 | &twl4030_dapm_dbypassl_control, bypass_event, | 1173 | &twl4030_dapm_dbypassl_control), |
1214 | SND_SOC_DAPM_POST_REG), | 1174 | SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0, |
1215 | SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0, | 1175 | &twl4030_dapm_dbypassr_control), |
1216 | &twl4030_dapm_dbypassr_control, bypass_event, | 1176 | SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0, |
1217 | SND_SOC_DAPM_POST_REG), | 1177 | &twl4030_dapm_dbypassv_control), |
1218 | SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0, | ||
1219 | &twl4030_dapm_dbypassv_control, bypass_event, | ||
1220 | SND_SOC_DAPM_POST_REG), | ||
1221 | 1178 | ||
1222 | /* Digital mixers, power control for the physical DACs */ | 1179 | /* Digital mixers, power control for the physical DACs */ |
1223 | SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer", | 1180 | SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer", |
@@ -1243,6 +1200,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1243 | SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer", | 1200 | SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer", |
1244 | TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0), | 1201 | TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0), |
1245 | 1202 | ||
1203 | SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event, | ||
1204 | SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), | ||
1205 | |||
1246 | /* Output MIXER controls */ | 1206 | /* Output MIXER controls */ |
1247 | /* Earpiece */ | 1207 | /* Earpiece */ |
1248 | SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, | 1208 | SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, |
@@ -1308,8 +1268,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1308 | 0, 0, NULL, 0, handsfreerpga_event, | 1268 | 0, 0, NULL, 0, handsfreerpga_event, |
1309 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 1269 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), |
1310 | /* Vibra */ | 1270 | /* Vibra */ |
1311 | SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0, | 1271 | SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0, |
1312 | &twl4030_dapm_vibra_control), | 1272 | &twl4030_dapm_vibra_control, vibramux_event, |
1273 | SND_SOC_DAPM_PRE_PMU), | ||
1313 | SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0, | 1274 | SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0, |
1314 | &twl4030_dapm_vibrapath_control), | 1275 | &twl4030_dapm_vibrapath_control), |
1315 | 1276 | ||
@@ -1369,6 +1330,13 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1369 | {"Digital R2 Playback Mixer", NULL, "DAC Right2"}, | 1330 | {"Digital R2 Playback Mixer", NULL, "DAC Right2"}, |
1370 | {"Digital Voice Playback Mixer", NULL, "DAC Voice"}, | 1331 | {"Digital Voice Playback Mixer", NULL, "DAC Voice"}, |
1371 | 1332 | ||
1333 | /* Supply for the digital part (APLL) */ | ||
1334 | {"Digital R1 Playback Mixer", NULL, "APLL Enable"}, | ||
1335 | {"Digital L1 Playback Mixer", NULL, "APLL Enable"}, | ||
1336 | {"Digital R2 Playback Mixer", NULL, "APLL Enable"}, | ||
1337 | {"Digital L2 Playback Mixer", NULL, "APLL Enable"}, | ||
1338 | {"Digital Voice Playback Mixer", NULL, "APLL Enable"}, | ||
1339 | |||
1372 | {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"}, | 1340 | {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"}, |
1373 | {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"}, | 1341 | {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"}, |
1374 | {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"}, | 1342 | {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"}, |
@@ -1482,6 +1450,11 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1482 | {"ADC Virtual Left2", NULL, "TX2 Capture Route"}, | 1450 | {"ADC Virtual Left2", NULL, "TX2 Capture Route"}, |
1483 | {"ADC Virtual Right2", NULL, "TX2 Capture Route"}, | 1451 | {"ADC Virtual Right2", NULL, "TX2 Capture Route"}, |
1484 | 1452 | ||
1453 | {"ADC Virtual Left1", NULL, "APLL Enable"}, | ||
1454 | {"ADC Virtual Right1", NULL, "APLL Enable"}, | ||
1455 | {"ADC Virtual Left2", NULL, "APLL Enable"}, | ||
1456 | {"ADC Virtual Right2", NULL, "APLL Enable"}, | ||
1457 | |||
1485 | /* Analog bypass routes */ | 1458 | /* Analog bypass routes */ |
1486 | {"Right1 Analog Loopback", "Switch", "Analog Right"}, | 1459 | {"Right1 Analog Loopback", "Switch", "Analog Right"}, |
1487 | {"Left1 Analog Loopback", "Switch", "Analog Left"}, | 1460 | {"Left1 Analog Loopback", "Switch", "Analog Left"}, |
@@ -1489,6 +1462,13 @@ static const struct snd_soc_dapm_route intercon[] = { | |||
1489 | {"Left2 Analog Loopback", "Switch", "Analog Left"}, | 1462 | {"Left2 Analog Loopback", "Switch", "Analog Left"}, |
1490 | {"Voice Analog Loopback", "Switch", "Analog Left"}, | 1463 | {"Voice Analog Loopback", "Switch", "Analog Left"}, |
1491 | 1464 | ||
1465 | /* Supply for the Analog loopbacks */ | ||
1466 | {"Right1 Analog Loopback", NULL, "FM Loop Enable"}, | ||
1467 | {"Left1 Analog Loopback", NULL, "FM Loop Enable"}, | ||
1468 | {"Right2 Analog Loopback", NULL, "FM Loop Enable"}, | ||
1469 | {"Left2 Analog Loopback", NULL, "FM Loop Enable"}, | ||
1470 | {"Voice Analog Loopback", NULL, "FM Loop Enable"}, | ||
1471 | |||
1492 | {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, | 1472 | {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, |
1493 | {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, | 1473 | {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, |
1494 | {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, | 1474 | {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, |
@@ -1513,32 +1493,20 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec) | |||
1513 | 1493 | ||
1514 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | 1494 | snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); |
1515 | 1495 | ||
1516 | snd_soc_dapm_new_widgets(codec); | ||
1517 | return 0; | 1496 | return 0; |
1518 | } | 1497 | } |
1519 | 1498 | ||
1520 | static int twl4030_set_bias_level(struct snd_soc_codec *codec, | 1499 | static int twl4030_set_bias_level(struct snd_soc_codec *codec, |
1521 | enum snd_soc_bias_level level) | 1500 | enum snd_soc_bias_level level) |
1522 | { | 1501 | { |
1523 | struct twl4030_priv *twl4030 = codec->private_data; | ||
1524 | |||
1525 | switch (level) { | 1502 | switch (level) { |
1526 | case SND_SOC_BIAS_ON: | 1503 | case SND_SOC_BIAS_ON: |
1527 | twl4030_codec_mute(codec, 0); | ||
1528 | break; | 1504 | break; |
1529 | case SND_SOC_BIAS_PREPARE: | 1505 | case SND_SOC_BIAS_PREPARE: |
1530 | twl4030_power_up(codec); | ||
1531 | if (twl4030->bypass_state) | ||
1532 | twl4030_codec_mute(codec, 0); | ||
1533 | else | ||
1534 | twl4030_codec_mute(codec, 1); | ||
1535 | break; | 1506 | break; |
1536 | case SND_SOC_BIAS_STANDBY: | 1507 | case SND_SOC_BIAS_STANDBY: |
1537 | twl4030_power_up(codec); | 1508 | if (codec->bias_level == SND_SOC_BIAS_OFF) |
1538 | if (twl4030->bypass_state) | 1509 | twl4030_power_up(codec); |
1539 | twl4030_codec_mute(codec, 0); | ||
1540 | else | ||
1541 | twl4030_codec_mute(codec, 1); | ||
1542 | break; | 1510 | break; |
1543 | case SND_SOC_BIAS_OFF: | 1511 | case SND_SOC_BIAS_OFF: |
1544 | twl4030_power_down(codec); | 1512 | twl4030_power_down(codec); |
@@ -1785,29 +1753,23 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |||
1785 | { | 1753 | { |
1786 | struct snd_soc_codec *codec = codec_dai->codec; | 1754 | struct snd_soc_codec *codec = codec_dai->codec; |
1787 | struct twl4030_priv *twl4030 = codec->private_data; | 1755 | struct twl4030_priv *twl4030 = codec->private_data; |
1788 | u8 infreq; | ||
1789 | 1756 | ||
1790 | switch (freq) { | 1757 | switch (freq) { |
1791 | case 19200000: | 1758 | case 19200000: |
1792 | infreq = TWL4030_APLL_INFREQ_19200KHZ; | ||
1793 | twl4030->sysclk = 19200; | ||
1794 | break; | ||
1795 | case 26000000: | 1759 | case 26000000: |
1796 | infreq = TWL4030_APLL_INFREQ_26000KHZ; | ||
1797 | twl4030->sysclk = 26000; | ||
1798 | break; | ||
1799 | case 38400000: | 1760 | case 38400000: |
1800 | infreq = TWL4030_APLL_INFREQ_38400KHZ; | ||
1801 | twl4030->sysclk = 38400; | ||
1802 | break; | 1761 | break; |
1803 | default: | 1762 | default: |
1804 | printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n", | 1763 | dev_err(codec->dev, "Unsupported APLL mclk: %u\n", freq); |
1805 | freq); | ||
1806 | return -EINVAL; | 1764 | return -EINVAL; |
1807 | } | 1765 | } |
1808 | 1766 | ||
1809 | infreq |= TWL4030_APLL_EN; | 1767 | if ((freq / 1000) != twl4030->sysclk) { |
1810 | twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq); | 1768 | dev_err(codec->dev, |
1769 | "Mismatch in APLL mclk: %u (configured: %u)\n", | ||
1770 | freq, twl4030->sysclk * 1000); | ||
1771 | return -EINVAL; | ||
1772 | } | ||
1811 | 1773 | ||
1812 | return 0; | 1774 | return 0; |
1813 | } | 1775 | } |
@@ -1905,18 +1867,16 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream, | |||
1905 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 1867 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
1906 | struct snd_soc_device *socdev = rtd->socdev; | 1868 | struct snd_soc_device *socdev = rtd->socdev; |
1907 | struct snd_soc_codec *codec = socdev->card->codec; | 1869 | struct snd_soc_codec *codec = socdev->card->codec; |
1908 | u8 infreq; | 1870 | struct twl4030_priv *twl4030 = codec->private_data; |
1909 | u8 mode; | 1871 | u8 mode; |
1910 | 1872 | ||
1911 | /* If the system master clock is not 26MHz, the voice PCM interface is | 1873 | /* If the system master clock is not 26MHz, the voice PCM interface is |
1912 | * not avilable. | 1874 | * not avilable. |
1913 | */ | 1875 | */ |
1914 | infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL) | 1876 | if (twl4030->sysclk != 26000) { |
1915 | & TWL4030_APLL_INFREQ; | 1877 | dev_err(codec->dev, "The board is configured for %u Hz, while" |
1916 | 1878 | "the Voice interface needs 26MHz APLL mclk\n", | |
1917 | if (infreq != TWL4030_APLL_INFREQ_26000KHZ) { | 1879 | twl4030->sysclk * 1000); |
1918 | printk(KERN_ERR "TWL4030 voice startup: " | ||
1919 | "MCLK is not 26MHz, call set_sysclk() on init\n"); | ||
1920 | return -EINVAL; | 1880 | return -EINVAL; |
1921 | } | 1881 | } |
1922 | 1882 | ||
@@ -1989,21 +1949,19 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, | |||
1989 | int clk_id, unsigned int freq, int dir) | 1949 | int clk_id, unsigned int freq, int dir) |
1990 | { | 1950 | { |
1991 | struct snd_soc_codec *codec = codec_dai->codec; | 1951 | struct snd_soc_codec *codec = codec_dai->codec; |
1992 | u8 infreq; | 1952 | struct twl4030_priv *twl4030 = codec->private_data; |
1993 | 1953 | ||
1994 | switch (freq) { | 1954 | if (freq != 26000000) { |
1995 | case 26000000: | 1955 | dev_err(codec->dev, "Unsupported APLL mclk: %u, the Voice" |
1996 | infreq = TWL4030_APLL_INFREQ_26000KHZ; | 1956 | "interface needs 26MHz APLL mclk\n", freq); |
1997 | break; | 1957 | return -EINVAL; |
1998 | default: | 1958 | } |
1999 | printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n", | 1959 | if ((freq / 1000) != twl4030->sysclk) { |
2000 | freq); | 1960 | dev_err(codec->dev, |
1961 | "Mismatch in APLL mclk: %u (configured: %u)\n", | ||
1962 | freq, twl4030->sysclk * 1000); | ||
2001 | return -EINVAL; | 1963 | return -EINVAL; |
2002 | } | 1964 | } |
2003 | |||
2004 | infreq |= TWL4030_APLL_EN; | ||
2005 | twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq); | ||
2006 | |||
2007 | return 0; | 1965 | return 0; |
2008 | } | 1966 | } |
2009 | 1967 | ||
@@ -2121,7 +2079,7 @@ struct snd_soc_dai twl4030_dai[] = { | |||
2121 | }; | 2079 | }; |
2122 | EXPORT_SYMBOL_GPL(twl4030_dai); | 2080 | EXPORT_SYMBOL_GPL(twl4030_dai); |
2123 | 2081 | ||
2124 | static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) | 2082 | static int twl4030_soc_suspend(struct platform_device *pdev, pm_message_t state) |
2125 | { | 2083 | { |
2126 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2084 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
2127 | struct snd_soc_codec *codec = socdev->card->codec; | 2085 | struct snd_soc_codec *codec = socdev->card->codec; |
@@ -2131,7 +2089,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) | |||
2131 | return 0; | 2089 | return 0; |
2132 | } | 2090 | } |
2133 | 2091 | ||
2134 | static int twl4030_resume(struct platform_device *pdev) | 2092 | static int twl4030_soc_resume(struct platform_device *pdev) |
2135 | { | 2093 | { |
2136 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2094 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
2137 | struct snd_soc_codec *codec = socdev->card->codec; | 2095 | struct snd_soc_codec *codec = socdev->card->codec; |
@@ -2141,147 +2099,181 @@ static int twl4030_resume(struct platform_device *pdev) | |||
2141 | return 0; | 2099 | return 0; |
2142 | } | 2100 | } |
2143 | 2101 | ||
2144 | /* | 2102 | static struct snd_soc_codec *twl4030_codec; |
2145 | * initialize the driver | ||
2146 | * register the mixer and dsp interfaces with the kernel | ||
2147 | */ | ||
2148 | 2103 | ||
2149 | static int twl4030_init(struct snd_soc_device *socdev) | 2104 | static int twl4030_soc_probe(struct platform_device *pdev) |
2150 | { | 2105 | { |
2151 | struct snd_soc_codec *codec = socdev->card->codec; | 2106 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); |
2152 | struct twl4030_setup_data *setup = socdev->codec_data; | 2107 | struct twl4030_setup_data *setup = socdev->codec_data; |
2153 | struct twl4030_priv *twl4030 = codec->private_data; | 2108 | struct snd_soc_codec *codec; |
2154 | int ret = 0; | 2109 | struct twl4030_priv *twl4030; |
2110 | int ret; | ||
2155 | 2111 | ||
2156 | printk(KERN_INFO "TWL4030 Audio Codec init \n"); | 2112 | BUG_ON(!twl4030_codec); |
2157 | 2113 | ||
2158 | codec->name = "twl4030"; | 2114 | codec = twl4030_codec; |
2159 | codec->owner = THIS_MODULE; | 2115 | twl4030 = codec->private_data; |
2160 | codec->read = twl4030_read_reg_cache; | 2116 | socdev->card->codec = codec; |
2161 | codec->write = twl4030_write; | ||
2162 | codec->set_bias_level = twl4030_set_bias_level; | ||
2163 | codec->dai = twl4030_dai; | ||
2164 | codec->num_dai = ARRAY_SIZE(twl4030_dai), | ||
2165 | codec->reg_cache_size = sizeof(twl4030_reg); | ||
2166 | codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), | ||
2167 | GFP_KERNEL); | ||
2168 | if (codec->reg_cache == NULL) | ||
2169 | return -ENOMEM; | ||
2170 | 2117 | ||
2171 | /* Configuration for headset ramp delay from setup data */ | 2118 | /* Configuration for headset ramp delay from setup data */ |
2172 | if (setup) { | 2119 | if (setup) { |
2173 | unsigned char hs_pop; | 2120 | unsigned char hs_pop; |
2174 | 2121 | ||
2175 | if (setup->sysclk) | 2122 | if (setup->sysclk != twl4030->sysclk) |
2176 | twl4030->sysclk = setup->sysclk; | 2123 | dev_warn(&pdev->dev, |
2177 | else | 2124 | "Mismatch in APLL mclk: %u (configured: %u)\n", |
2178 | twl4030->sysclk = 26000; | 2125 | setup->sysclk, twl4030->sysclk); |
2179 | 2126 | ||
2180 | hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | 2127 | hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); |
2181 | hs_pop &= ~TWL4030_RAMP_DELAY; | 2128 | hs_pop &= ~TWL4030_RAMP_DELAY; |
2182 | hs_pop |= (setup->ramp_delay_value << 2); | 2129 | hs_pop |= (setup->ramp_delay_value << 2); |
2183 | twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 2130 | twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
2184 | } else { | ||
2185 | twl4030->sysclk = 26000; | ||
2186 | } | 2131 | } |
2187 | 2132 | ||
2188 | /* register pcms */ | 2133 | /* register pcms */ |
2189 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 2134 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); |
2190 | if (ret < 0) { | 2135 | if (ret < 0) { |
2191 | printk(KERN_ERR "twl4030: failed to create pcms\n"); | 2136 | dev_err(&pdev->dev, "failed to create pcms\n"); |
2192 | goto pcm_err; | 2137 | return ret; |
2193 | } | 2138 | } |
2194 | 2139 | ||
2195 | twl4030_init_chip(codec); | ||
2196 | |||
2197 | /* power on device */ | ||
2198 | twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
2199 | |||
2200 | snd_soc_add_controls(codec, twl4030_snd_controls, | 2140 | snd_soc_add_controls(codec, twl4030_snd_controls, |
2201 | ARRAY_SIZE(twl4030_snd_controls)); | 2141 | ARRAY_SIZE(twl4030_snd_controls)); |
2202 | twl4030_add_widgets(codec); | 2142 | twl4030_add_widgets(codec); |
2203 | 2143 | ||
2204 | ret = snd_soc_init_card(socdev); | 2144 | return 0; |
2205 | if (ret < 0) { | 2145 | } |
2206 | printk(KERN_ERR "twl4030: failed to register card\n"); | ||
2207 | goto card_err; | ||
2208 | } | ||
2209 | 2146 | ||
2210 | return ret; | 2147 | static int twl4030_soc_remove(struct platform_device *pdev) |
2148 | { | ||
2149 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
2150 | struct snd_soc_codec *codec = socdev->card->codec; | ||
2211 | 2151 | ||
2212 | card_err: | 2152 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); |
2213 | snd_soc_free_pcms(socdev); | 2153 | snd_soc_free_pcms(socdev); |
2214 | snd_soc_dapm_free(socdev); | 2154 | snd_soc_dapm_free(socdev); |
2215 | pcm_err: | 2155 | kfree(codec->private_data); |
2216 | kfree(codec->reg_cache); | 2156 | kfree(codec); |
2217 | return ret; | ||
2218 | } | ||
2219 | 2157 | ||
2220 | static struct snd_soc_device *twl4030_socdev; | 2158 | return 0; |
2159 | } | ||
2221 | 2160 | ||
2222 | static int twl4030_probe(struct platform_device *pdev) | 2161 | static int __devinit twl4030_codec_probe(struct platform_device *pdev) |
2223 | { | 2162 | { |
2224 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2163 | struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; |
2225 | struct snd_soc_codec *codec; | 2164 | struct snd_soc_codec *codec; |
2226 | struct twl4030_priv *twl4030; | 2165 | struct twl4030_priv *twl4030; |
2166 | int ret; | ||
2227 | 2167 | ||
2228 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | 2168 | if (!pdata) { |
2229 | if (codec == NULL) | 2169 | dev_err(&pdev->dev, "platform_data is missing\n"); |
2230 | return -ENOMEM; | 2170 | return -EINVAL; |
2171 | } | ||
2231 | 2172 | ||
2232 | twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); | 2173 | twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); |
2233 | if (twl4030 == NULL) { | 2174 | if (twl4030 == NULL) { |
2234 | kfree(codec); | 2175 | dev_err(&pdev->dev, "Can not allocate memroy\n"); |
2235 | return -ENOMEM; | 2176 | return -ENOMEM; |
2236 | } | 2177 | } |
2237 | 2178 | ||
2179 | codec = &twl4030->codec; | ||
2238 | codec->private_data = twl4030; | 2180 | codec->private_data = twl4030; |
2239 | socdev->card->codec = codec; | 2181 | codec->dev = &pdev->dev; |
2182 | twl4030_dai[0].dev = &pdev->dev; | ||
2183 | twl4030_dai[1].dev = &pdev->dev; | ||
2184 | |||
2240 | mutex_init(&codec->mutex); | 2185 | mutex_init(&codec->mutex); |
2241 | INIT_LIST_HEAD(&codec->dapm_widgets); | 2186 | INIT_LIST_HEAD(&codec->dapm_widgets); |
2242 | INIT_LIST_HEAD(&codec->dapm_paths); | 2187 | INIT_LIST_HEAD(&codec->dapm_paths); |
2243 | 2188 | ||
2244 | twl4030_socdev = socdev; | 2189 | codec->name = "twl4030"; |
2245 | twl4030_init(socdev); | 2190 | codec->owner = THIS_MODULE; |
2191 | codec->read = twl4030_read_reg_cache; | ||
2192 | codec->write = twl4030_write; | ||
2193 | codec->set_bias_level = twl4030_set_bias_level; | ||
2194 | codec->dai = twl4030_dai; | ||
2195 | codec->num_dai = ARRAY_SIZE(twl4030_dai), | ||
2196 | codec->reg_cache_size = sizeof(twl4030_reg); | ||
2197 | codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), | ||
2198 | GFP_KERNEL); | ||
2199 | if (codec->reg_cache == NULL) { | ||
2200 | ret = -ENOMEM; | ||
2201 | goto error_cache; | ||
2202 | } | ||
2203 | |||
2204 | platform_set_drvdata(pdev, twl4030); | ||
2205 | twl4030_codec = codec; | ||
2206 | |||
2207 | /* Set the defaults, and power up the codec */ | ||
2208 | twl4030->sysclk = twl4030_codec_get_mclk() / 1000; | ||
2209 | twl4030_init_chip(codec); | ||
2210 | codec->bias_level = SND_SOC_BIAS_OFF; | ||
2211 | twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | ||
2212 | |||
2213 | ret = snd_soc_register_codec(codec); | ||
2214 | if (ret != 0) { | ||
2215 | dev_err(codec->dev, "Failed to register codec: %d\n", ret); | ||
2216 | goto error_codec; | ||
2217 | } | ||
2218 | |||
2219 | ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); | ||
2220 | if (ret != 0) { | ||
2221 | dev_err(codec->dev, "Failed to register DAIs: %d\n", ret); | ||
2222 | snd_soc_unregister_codec(codec); | ||
2223 | goto error_codec; | ||
2224 | } | ||
2246 | 2225 | ||
2247 | return 0; | 2226 | return 0; |
2227 | |||
2228 | error_codec: | ||
2229 | twl4030_power_down(codec); | ||
2230 | kfree(codec->reg_cache); | ||
2231 | error_cache: | ||
2232 | kfree(twl4030); | ||
2233 | return ret; | ||
2248 | } | 2234 | } |
2249 | 2235 | ||
2250 | static int twl4030_remove(struct platform_device *pdev) | 2236 | static int __devexit twl4030_codec_remove(struct platform_device *pdev) |
2251 | { | 2237 | { |
2252 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 2238 | struct twl4030_priv *twl4030 = platform_get_drvdata(pdev); |
2253 | struct snd_soc_codec *codec = socdev->card->codec; | ||
2254 | 2239 | ||
2255 | printk(KERN_INFO "TWL4030 Audio Codec remove\n"); | 2240 | kfree(twl4030); |
2256 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | ||
2257 | snd_soc_free_pcms(socdev); | ||
2258 | snd_soc_dapm_free(socdev); | ||
2259 | kfree(codec->private_data); | ||
2260 | kfree(codec); | ||
2261 | 2241 | ||
2242 | twl4030_codec = NULL; | ||
2262 | return 0; | 2243 | return 0; |
2263 | } | 2244 | } |
2264 | 2245 | ||
2265 | struct snd_soc_codec_device soc_codec_dev_twl4030 = { | 2246 | MODULE_ALIAS("platform:twl4030_codec_audio"); |
2266 | .probe = twl4030_probe, | 2247 | |
2267 | .remove = twl4030_remove, | 2248 | static struct platform_driver twl4030_codec_driver = { |
2268 | .suspend = twl4030_suspend, | 2249 | .probe = twl4030_codec_probe, |
2269 | .resume = twl4030_resume, | 2250 | .remove = __devexit_p(twl4030_codec_remove), |
2251 | .driver = { | ||
2252 | .name = "twl4030_codec_audio", | ||
2253 | .owner = THIS_MODULE, | ||
2254 | }, | ||
2270 | }; | 2255 | }; |
2271 | EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); | ||
2272 | 2256 | ||
2273 | static int __init twl4030_modinit(void) | 2257 | static int __init twl4030_modinit(void) |
2274 | { | 2258 | { |
2275 | return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); | 2259 | return platform_driver_register(&twl4030_codec_driver); |
2276 | } | 2260 | } |
2277 | module_init(twl4030_modinit); | 2261 | module_init(twl4030_modinit); |
2278 | 2262 | ||
2279 | static void __exit twl4030_exit(void) | 2263 | static void __exit twl4030_exit(void) |
2280 | { | 2264 | { |
2281 | snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); | 2265 | platform_driver_unregister(&twl4030_codec_driver); |
2282 | } | 2266 | } |
2283 | module_exit(twl4030_exit); | 2267 | module_exit(twl4030_exit); |
2284 | 2268 | ||
2269 | struct snd_soc_codec_device soc_codec_dev_twl4030 = { | ||
2270 | .probe = twl4030_soc_probe, | ||
2271 | .remove = twl4030_soc_remove, | ||
2272 | .suspend = twl4030_soc_suspend, | ||
2273 | .resume = twl4030_soc_resume, | ||
2274 | }; | ||
2275 | EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); | ||
2276 | |||
2285 | MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); | 2277 | MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); |
2286 | MODULE_AUTHOR("Steve Sakoman"); | 2278 | MODULE_AUTHOR("Steve Sakoman"); |
2287 | MODULE_LICENSE("GPL"); | 2279 | MODULE_LICENSE("GPL"); |