diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-08-17 13:52:47 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-08-17 13:53:50 -0400 |
commit | d3c9e9a1390f8a34da8b69e09fa1afa90f5067f4 (patch) | |
tree | efd1270eecad034c89aebe0b321dac247ffc3aa9 /sound/soc | |
parent | 0182dcc52c759d005cc3e65deadee9f166cdd7d0 (diff) |
ASoC: Implement TDM configuration for WM8993
Note that the number of slots used internally is specified in terms
of stereo slots while the external API works with mono slots.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/codecs/wm8993.c | 99 |
1 files changed, 81 insertions, 18 deletions
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index f9119a6e616e..13befa338247 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c | |||
@@ -218,6 +218,8 @@ struct wm8993_priv { | |||
218 | struct snd_soc_codec codec; | 218 | struct snd_soc_codec codec; |
219 | int master; | 219 | int master; |
220 | int sysclk_source; | 220 | int sysclk_source; |
221 | int tdm_slots; | ||
222 | int tdm_width; | ||
221 | unsigned int mclk_rate; | 223 | unsigned int mclk_rate; |
222 | unsigned int sysclk_rate; | 224 | unsigned int sysclk_rate; |
223 | unsigned int fs; | 225 | unsigned int fs; |
@@ -1107,24 +1109,30 @@ static int wm8993_hw_params(struct snd_pcm_substream *substream, | |||
1107 | /* What BCLK do we need? */ | 1109 | /* What BCLK do we need? */ |
1108 | wm8993->fs = params_rate(params); | 1110 | wm8993->fs = params_rate(params); |
1109 | wm8993->bclk = 2 * wm8993->fs; | 1111 | wm8993->bclk = 2 * wm8993->fs; |
1110 | switch (params_format(params)) { | 1112 | if (wm8993->tdm_slots) { |
1111 | case SNDRV_PCM_FORMAT_S16_LE: | 1113 | dev_dbg(codec->dev, "Configuring for %d %d bit TDM slots\n", |
1112 | wm8993->bclk *= 16; | 1114 | wm8993->tdm_slots, wm8993->tdm_width); |
1113 | break; | 1115 | wm8993->bclk *= wm8993->tdm_width * wm8993->tdm_slots; |
1114 | case SNDRV_PCM_FORMAT_S20_3LE: | 1116 | } else { |
1115 | wm8993->bclk *= 20; | 1117 | switch (params_format(params)) { |
1116 | aif1 |= 0x8; | 1118 | case SNDRV_PCM_FORMAT_S16_LE: |
1117 | break; | 1119 | wm8993->bclk *= 16; |
1118 | case SNDRV_PCM_FORMAT_S24_LE: | 1120 | break; |
1119 | wm8993->bclk *= 24; | 1121 | case SNDRV_PCM_FORMAT_S20_3LE: |
1120 | aif1 |= 0x10; | 1122 | wm8993->bclk *= 20; |
1121 | break; | 1123 | aif1 |= 0x8; |
1122 | case SNDRV_PCM_FORMAT_S32_LE: | 1124 | break; |
1123 | wm8993->bclk *= 32; | 1125 | case SNDRV_PCM_FORMAT_S24_LE: |
1124 | aif1 |= 0x18; | 1126 | wm8993->bclk *= 24; |
1125 | break; | 1127 | aif1 |= 0x10; |
1126 | default: | 1128 | break; |
1127 | return -EINVAL; | 1129 | case SNDRV_PCM_FORMAT_S32_LE: |
1130 | wm8993->bclk *= 32; | ||
1131 | aif1 |= 0x18; | ||
1132 | break; | ||
1133 | default: | ||
1134 | return -EINVAL; | ||
1135 | } | ||
1128 | } | 1136 | } |
1129 | 1137 | ||
1130 | dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk); | 1138 | dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk); |
@@ -1243,12 +1251,67 @@ static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute) | |||
1243 | return 0; | 1251 | return 0; |
1244 | } | 1252 | } |
1245 | 1253 | ||
1254 | static int wm8993_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, | ||
1255 | unsigned int rx_mask, int slots, int slot_width) | ||
1256 | { | ||
1257 | struct snd_soc_codec *codec = dai->codec; | ||
1258 | struct wm8993_priv *wm8993 = codec->private_data; | ||
1259 | int aif1 = 0; | ||
1260 | int aif2 = 0; | ||
1261 | |||
1262 | /* Don't need to validate anything if we're turning off TDM */ | ||
1263 | if (slots == 0) { | ||
1264 | wm8993->tdm_slots = 0; | ||
1265 | goto out; | ||
1266 | } | ||
1267 | |||
1268 | /* Note that we allow configurations we can't handle ourselves - | ||
1269 | * for example, we can generate clocks for slots 2 and up even if | ||
1270 | * we can't use those slots ourselves. | ||
1271 | */ | ||
1272 | aif1 |= WM8993_AIFADC_TDM; | ||
1273 | aif2 |= WM8993_AIFDAC_TDM; | ||
1274 | |||
1275 | switch (rx_mask) { | ||
1276 | case 3: | ||
1277 | break; | ||
1278 | case 0xc: | ||
1279 | aif1 |= WM8993_AIFADC_TDM_CHAN; | ||
1280 | break; | ||
1281 | default: | ||
1282 | return -EINVAL; | ||
1283 | } | ||
1284 | |||
1285 | |||
1286 | switch (tx_mask) { | ||
1287 | case 3: | ||
1288 | break; | ||
1289 | case 0xc: | ||
1290 | aif2 |= WM8993_AIFDAC_TDM_CHAN; | ||
1291 | break; | ||
1292 | default: | ||
1293 | return -EINVAL; | ||
1294 | } | ||
1295 | |||
1296 | out: | ||
1297 | wm8993->tdm_width = slot_width; | ||
1298 | wm8993->tdm_slots = slots / 2; | ||
1299 | |||
1300 | snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_1, | ||
1301 | WM8993_AIFADC_TDM | WM8993_AIFADC_TDM_CHAN, aif1); | ||
1302 | snd_soc_update_bits(codec, WM8993_AUDIO_INTERFACE_2, | ||
1303 | WM8993_AIFDAC_TDM | WM8993_AIFDAC_TDM_CHAN, aif2); | ||
1304 | |||
1305 | return 0; | ||
1306 | } | ||
1307 | |||
1246 | static struct snd_soc_dai_ops wm8993_ops = { | 1308 | static struct snd_soc_dai_ops wm8993_ops = { |
1247 | .set_sysclk = wm8993_set_sysclk, | 1309 | .set_sysclk = wm8993_set_sysclk, |
1248 | .set_fmt = wm8993_set_dai_fmt, | 1310 | .set_fmt = wm8993_set_dai_fmt, |
1249 | .hw_params = wm8993_hw_params, | 1311 | .hw_params = wm8993_hw_params, |
1250 | .digital_mute = wm8993_digital_mute, | 1312 | .digital_mute = wm8993_digital_mute, |
1251 | .set_pll = wm8993_set_fll, | 1313 | .set_pll = wm8993_set_fll, |
1314 | .set_tdm_slot = wm8993_set_tdm_slot, | ||
1252 | }; | 1315 | }; |
1253 | 1316 | ||
1254 | #define WM8993_RATES SNDRV_PCM_RATE_8000_48000 | 1317 | #define WM8993_RATES SNDRV_PCM_RATE_8000_48000 |