aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/mfd/arizona/core.h3
-rw-r--r--sound/soc/codecs/arizona.c78
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
115int arizona_clk32k_enable(struct arizona *arizona); 118int 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
1339static 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
1362static 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
1327const struct snd_soc_dai_ops arizona_dai_ops = { 1392const 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,