diff options
| author | Peter Ujfalusi <peter.ujfalusi@nokia.com> | 2009-04-23 07:36:49 -0400 |
|---|---|---|
| committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-04-23 11:11:17 -0400 |
| commit | 8a1f936acdfd53cb0a981f3f80483863dcd84fa9 (patch) | |
| tree | 598b788262d3ad53b219dd0bf457c959dfb9c75b | |
| parent | 31a00c6b7c0c4f01be49f02660de920c8b82b613 (diff) | |
ASoC: TWL4030: Add 4 channel TDM support
Support for 4 channel TDM (SND_SOC_DAIFMT_DSP_A) for twl4030
codec.
The channel allocations are:
Playback:
TDM i2s TWL RX
Channel 1 Left SDRL2
Channel 3 Right SDRR2
Channel 2 -- SDRL1
Channel 4 -- SDRR1
Capture:
TDM i2s TWL TX
Channel 1 Left TXL1
Channel 3 Right TXR1
Channel 2 -- TXL2
Channel 4 -- TXR2
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
| -rw-r--r-- | sound/soc/codecs/twl4030.c | 52 | ||||
| -rw-r--r-- | sound/soc/codecs/twl4030.h | 11 |
2 files changed, 61 insertions, 2 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index fdf88dfbcff9..e23c20c42f19 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
| @@ -1251,6 +1251,28 @@ static void twl4030_constraints(struct twl4030_priv *twl4030, | |||
| 1251 | twl4030->channels); | 1251 | twl4030->channels); |
| 1252 | } | 1252 | } |
| 1253 | 1253 | ||
| 1254 | /* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for | ||
| 1255 | * capture has to be enabled/disabled. */ | ||
| 1256 | static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction, | ||
| 1257 | int enable) | ||
| 1258 | { | ||
| 1259 | u8 reg, mask; | ||
| 1260 | |||
| 1261 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION); | ||
| 1262 | |||
| 1263 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 1264 | mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN; | ||
| 1265 | else | ||
| 1266 | mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN; | ||
| 1267 | |||
| 1268 | if (enable) | ||
| 1269 | reg |= mask; | ||
| 1270 | else | ||
| 1271 | reg &= ~mask; | ||
| 1272 | |||
| 1273 | twl4030_write(codec, TWL4030_REG_OPTION, reg); | ||
| 1274 | } | ||
| 1275 | |||
| 1254 | static int twl4030_startup(struct snd_pcm_substream *substream, | 1276 | static int twl4030_startup(struct snd_pcm_substream *substream, |
| 1255 | struct snd_soc_dai *dai) | 1277 | struct snd_soc_dai *dai) |
| 1256 | { | 1278 | { |
| @@ -1267,6 +1289,15 @@ static int twl4030_startup(struct snd_pcm_substream *substream, | |||
| 1267 | if (twl4030->configured) | 1289 | if (twl4030->configured) |
| 1268 | twl4030_constraints(twl4030, twl4030->master_substream); | 1290 | twl4030_constraints(twl4030, twl4030->master_substream); |
| 1269 | } else { | 1291 | } else { |
| 1292 | if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) & | ||
| 1293 | TWL4030_OPTION_1)) { | ||
| 1294 | /* In option2 4 channel is not supported, set the | ||
| 1295 | * constraint for the first stream for channels, the | ||
| 1296 | * second stream will 'inherit' this cosntraint */ | ||
| 1297 | snd_pcm_hw_constraint_minmax(substream->runtime, | ||
| 1298 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
| 1299 | 2, 2); | ||
| 1300 | } | ||
| 1270 | twl4030->master_substream = substream; | 1301 | twl4030->master_substream = substream; |
| 1271 | } | 1302 | } |
| 1272 | 1303 | ||
| @@ -1292,6 +1323,10 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream, | |||
| 1292 | twl4030->configured = 0; | 1323 | twl4030->configured = 0; |
| 1293 | else if (!twl4030->master_substream->runtime->channels) | 1324 | else if (!twl4030->master_substream->runtime->channels) |
| 1294 | twl4030->configured = 0; | 1325 | twl4030->configured = 0; |
| 1326 | |||
| 1327 | /* If the closing substream had 4 channel, do the necessary cleanup */ | ||
| 1328 | if (substream->runtime->channels == 4) | ||
| 1329 | twl4030_tdm_enable(codec, substream->stream, 0); | ||
| 1295 | } | 1330 | } |
| 1296 | 1331 | ||
| 1297 | static int twl4030_hw_params(struct snd_pcm_substream *substream, | 1332 | static int twl4030_hw_params(struct snd_pcm_substream *substream, |
| @@ -1304,6 +1339,16 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, | |||
| 1304 | struct twl4030_priv *twl4030 = codec->private_data; | 1339 | struct twl4030_priv *twl4030 = codec->private_data; |
| 1305 | u8 mode, old_mode, format, old_format; | 1340 | u8 mode, old_mode, format, old_format; |
| 1306 | 1341 | ||
| 1342 | /* If the substream has 4 channel, do the necessary setup */ | ||
| 1343 | if (params_channels(params) == 4) { | ||
| 1344 | /* Safety check: are we in the correct operating mode? */ | ||
| 1345 | if ((twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) & | ||
| 1346 | TWL4030_OPTION_1)) | ||
| 1347 | twl4030_tdm_enable(codec, substream->stream, 1); | ||
| 1348 | else | ||
| 1349 | return -EINVAL; | ||
| 1350 | } | ||
| 1351 | |||
| 1307 | if (twl4030->configured) | 1352 | if (twl4030->configured) |
| 1308 | /* Ignoring hw_params for already configured DAI */ | 1353 | /* Ignoring hw_params for already configured DAI */ |
| 1309 | return 0; | 1354 | return 0; |
| @@ -1461,6 +1506,9 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | |||
| 1461 | case SND_SOC_DAIFMT_I2S: | 1506 | case SND_SOC_DAIFMT_I2S: |
| 1462 | format |= TWL4030_AIF_FORMAT_CODEC; | 1507 | format |= TWL4030_AIF_FORMAT_CODEC; |
| 1463 | break; | 1508 | break; |
| 1509 | case SND_SOC_DAIFMT_DSP_A: | ||
| 1510 | format |= TWL4030_AIF_FORMAT_TDM; | ||
| 1511 | break; | ||
| 1464 | default: | 1512 | default: |
| 1465 | return -EINVAL; | 1513 | return -EINVAL; |
| 1466 | } | 1514 | } |
| @@ -1642,13 +1690,13 @@ struct snd_soc_dai twl4030_dai[] = { | |||
| 1642 | .playback = { | 1690 | .playback = { |
| 1643 | .stream_name = "Playback", | 1691 | .stream_name = "Playback", |
| 1644 | .channels_min = 2, | 1692 | .channels_min = 2, |
| 1645 | .channels_max = 2, | 1693 | .channels_max = 4, |
| 1646 | .rates = TWL4030_RATES | SNDRV_PCM_RATE_96000, | 1694 | .rates = TWL4030_RATES | SNDRV_PCM_RATE_96000, |
| 1647 | .formats = TWL4030_FORMATS,}, | 1695 | .formats = TWL4030_FORMATS,}, |
| 1648 | .capture = { | 1696 | .capture = { |
| 1649 | .stream_name = "Capture", | 1697 | .stream_name = "Capture", |
| 1650 | .channels_min = 2, | 1698 | .channels_min = 2, |
| 1651 | .channels_max = 2, | 1699 | .channels_max = 4, |
| 1652 | .rates = TWL4030_RATES, | 1700 | .rates = TWL4030_RATES, |
| 1653 | .formats = TWL4030_FORMATS,}, | 1701 | .formats = TWL4030_FORMATS,}, |
| 1654 | .ops = &twl4030_dai_ops, | 1702 | .ops = &twl4030_dai_ops, |
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h index 981ec609495b..3441115136f6 100644 --- a/sound/soc/codecs/twl4030.h +++ b/sound/soc/codecs/twl4030.h | |||
| @@ -116,6 +116,17 @@ | |||
| 116 | #define TWL4030_OPTION_1 (1 << 0) | 116 | #define TWL4030_OPTION_1 (1 << 0) |
| 117 | #define TWL4030_OPTION_2 (0 << 0) | 117 | #define TWL4030_OPTION_2 (0 << 0) |
| 118 | 118 | ||
| 119 | /* TWL4030_OPTION (0x02) Fields */ | ||
| 120 | |||
| 121 | #define TWL4030_ATXL1_EN (1 << 0) | ||
| 122 | #define TWL4030_ATXR1_EN (1 << 1) | ||
| 123 | #define TWL4030_ATXL2_VTXL_EN (1 << 2) | ||
| 124 | #define TWL4030_ATXR2_VTXR_EN (1 << 3) | ||
| 125 | #define TWL4030_ARXL1_VRX_EN (1 << 4) | ||
| 126 | #define TWL4030_ARXR1_EN (1 << 5) | ||
| 127 | #define TWL4030_ARXL2_EN (1 << 6) | ||
| 128 | #define TWL4030_ARXR2_EN (1 << 7) | ||
| 129 | |||
| 119 | /* TWL4030_REG_MICBIAS_CTL (0x04) Fields */ | 130 | /* TWL4030_REG_MICBIAS_CTL (0x04) Fields */ |
| 120 | 131 | ||
| 121 | #define TWL4030_MICBIAS2_CTL 0x40 | 132 | #define TWL4030_MICBIAS2_CTL 0x40 |
