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, |
