diff options
-rw-r--r-- | include/linux/mfd/arizona/core.h | 3 | ||||
-rw-r--r-- | sound/soc/codecs/arizona.c | 78 |
2 files changed, 75 insertions, 6 deletions
diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 5cf8b91ce996..11783b511b9a 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h | |||
@@ -110,6 +110,9 @@ struct arizona { | |||
110 | int clk32k_ref; | 110 | int clk32k_ref; |
111 | 111 | ||
112 | struct snd_soc_dapm_context *dapm; | 112 | struct snd_soc_dapm_context *dapm; |
113 | |||
114 | int tdm_width[ARIZONA_MAX_AIF]; | ||
115 | int tdm_slots[ARIZONA_MAX_AIF]; | ||
113 | }; | 116 | }; |
114 | 117 | ||
115 | int arizona_clk32k_enable(struct arizona *arizona); | 118 | int arizona_clk32k_enable(struct arizona *arizona); |
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 29e198f57d4c..e77f61c387f7 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c | |||
@@ -1185,7 +1185,10 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, | |||
1185 | int base = dai->driver->base; | 1185 | int base = dai->driver->base; |
1186 | const int *rates; | 1186 | const int *rates; |
1187 | int i, ret, val; | 1187 | int i, ret, val; |
1188 | int channels = params_channels(params); | ||
1188 | int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; | 1189 | int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; |
1190 | int tdm_width = arizona->tdm_width[dai->id - 1]; | ||
1191 | int tdm_slots = arizona->tdm_slots[dai->id - 1]; | ||
1189 | int bclk, lrclk, wl, frame, bclk_target; | 1192 | int bclk, lrclk, wl, frame, bclk_target; |
1190 | 1193 | ||
1191 | if (params_rate(params) % 8000) | 1194 | if (params_rate(params) % 8000) |
@@ -1193,18 +1196,27 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, | |||
1193 | else | 1196 | else |
1194 | rates = &arizona_48k_bclk_rates[0]; | 1197 | rates = &arizona_48k_bclk_rates[0]; |
1195 | 1198 | ||
1196 | bclk_target = snd_soc_params_to_bclk(params); | 1199 | if (tdm_slots) { |
1197 | if (chan_limit && chan_limit < params_channels(params)) { | 1200 | arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n", |
1201 | tdm_slots, tdm_width); | ||
1202 | bclk_target = tdm_slots * tdm_width * params_rate(params); | ||
1203 | channels = tdm_slots; | ||
1204 | } else { | ||
1205 | bclk_target = snd_soc_params_to_bclk(params); | ||
1206 | } | ||
1207 | |||
1208 | if (chan_limit && chan_limit < channels) { | ||
1198 | arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); | 1209 | arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); |
1199 | bclk_target /= params_channels(params); | 1210 | bclk_target /= channels; |
1200 | bclk_target *= chan_limit; | 1211 | bclk_target *= chan_limit; |
1201 | } | 1212 | } |
1202 | 1213 | ||
1203 | /* Force stereo for I2S mode */ | 1214 | /* Force multiple of 2 channels for I2S mode */ |
1204 | val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT); | 1215 | val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT); |
1205 | if (params_channels(params) == 1 && (val & ARIZONA_AIF1_FMT_MASK)) { | 1216 | if ((channels & 1) && (val & ARIZONA_AIF1_FMT_MASK)) { |
1206 | arizona_aif_dbg(dai, "Forcing stereo mode\n"); | 1217 | arizona_aif_dbg(dai, "Forcing stereo mode\n"); |
1207 | bclk_target *= 2; | 1218 | bclk_target /= channels; |
1219 | bclk_target *= channels + 1; | ||
1208 | } | 1220 | } |
1209 | 1221 | ||
1210 | for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { | 1222 | for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { |
@@ -1324,9 +1336,63 @@ static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) | |||
1324 | ARIZONA_AIF1_TRI, reg); | 1336 | ARIZONA_AIF1_TRI, reg); |
1325 | } | 1337 | } |
1326 | 1338 | ||
1339 | static void arizona_set_channels_to_mask(struct snd_soc_dai *dai, | ||
1340 | unsigned int base, | ||
1341 | int channels, unsigned int mask) | ||
1342 | { | ||
1343 | struct snd_soc_codec *codec = dai->codec; | ||
1344 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
1345 | struct arizona *arizona = priv->arizona; | ||
1346 | int slot, i; | ||
1347 | |||
1348 | for (i = 0; i < channels; ++i) { | ||
1349 | slot = ffs(mask) - 1; | ||
1350 | if (slot < 0) | ||
1351 | return; | ||
1352 | |||
1353 | regmap_write(arizona->regmap, base + i, slot); | ||
1354 | |||
1355 | mask &= ~(1 << slot); | ||
1356 | } | ||
1357 | |||
1358 | if (mask) | ||
1359 | arizona_aif_warn(dai, "Too many channels in TDM mask\n"); | ||
1360 | } | ||
1361 | |||
1362 | static int arizona_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, | ||
1363 | unsigned int rx_mask, int slots, int slot_width) | ||
1364 | { | ||
1365 | struct snd_soc_codec *codec = dai->codec; | ||
1366 | struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
1367 | struct arizona *arizona = priv->arizona; | ||
1368 | int base = dai->driver->base; | ||
1369 | int rx_max_chan = dai->driver->playback.channels_max; | ||
1370 | int tx_max_chan = dai->driver->capture.channels_max; | ||
1371 | |||
1372 | /* Only support TDM for the physical AIFs */ | ||
1373 | if (dai->id > ARIZONA_MAX_AIF) | ||
1374 | return -ENOTSUPP; | ||
1375 | |||
1376 | if (slots == 0) { | ||
1377 | tx_mask = (1 << tx_max_chan) - 1; | ||
1378 | rx_mask = (1 << rx_max_chan) - 1; | ||
1379 | } | ||
1380 | |||
1381 | arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_3, | ||
1382 | tx_max_chan, tx_mask); | ||
1383 | arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_11, | ||
1384 | rx_max_chan, rx_mask); | ||
1385 | |||
1386 | arizona->tdm_width[dai->id - 1] = slot_width; | ||
1387 | arizona->tdm_slots[dai->id - 1] = slots; | ||
1388 | |||
1389 | return 0; | ||
1390 | } | ||
1391 | |||
1327 | const struct snd_soc_dai_ops arizona_dai_ops = { | 1392 | const struct snd_soc_dai_ops arizona_dai_ops = { |
1328 | .startup = arizona_startup, | 1393 | .startup = arizona_startup, |
1329 | .set_fmt = arizona_set_fmt, | 1394 | .set_fmt = arizona_set_fmt, |
1395 | .set_tdm_slot = arizona_set_tdm_slot, | ||
1330 | .hw_params = arizona_hw_params, | 1396 | .hw_params = arizona_hw_params, |
1331 | .set_sysclk = arizona_dai_set_sysclk, | 1397 | .set_sysclk = arizona_dai_set_sysclk, |
1332 | .set_tristate = arizona_set_tristate, | 1398 | .set_tristate = arizona_set_tristate, |