Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a1f936a authored by Peter Ujfalusi's avatar Peter Ujfalusi Committed by Mark Brown
Browse files

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: default avatarPeter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 31a00c6b
Loading
Loading
Loading
Loading
+50 −2
Original line number Diff line number Diff line
@@ -1251,6 +1251,28 @@ static void twl4030_constraints(struct twl4030_priv *twl4030,
				twl4030->channels);
}

/* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for
 * capture has to be enabled/disabled. */
static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction,
				int enable)
{
	u8 reg, mask;

	reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION);

	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
		mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN;
	else
		mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN;

	if (enable)
		reg |= mask;
	else
		reg &= ~mask;

	twl4030_write(codec, TWL4030_REG_OPTION, reg);
}

static int twl4030_startup(struct snd_pcm_substream *substream,
			   struct snd_soc_dai *dai)
{
@@ -1267,6 +1289,15 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
		if (twl4030->configured)
			twl4030_constraints(twl4030, twl4030->master_substream);
	} else {
		if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) &
			TWL4030_OPTION_1)) {
			/* In option2 4 channel is not supported, set the
			 * constraint for the first stream for channels, the
			 * second stream will 'inherit' this cosntraint */
			snd_pcm_hw_constraint_minmax(substream->runtime,
						SNDRV_PCM_HW_PARAM_CHANNELS,
						2, 2);
		}
		twl4030->master_substream = substream;
	}

@@ -1292,6 +1323,10 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream,
		twl4030->configured = 0;
	 else if (!twl4030->master_substream->runtime->channels)
		twl4030->configured = 0;

	 /* If the closing substream had 4 channel, do the necessary cleanup */
	if (substream->runtime->channels == 4)
		twl4030_tdm_enable(codec, substream->stream, 0);
}

static int twl4030_hw_params(struct snd_pcm_substream *substream,
@@ -1304,6 +1339,16 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
	struct twl4030_priv *twl4030 = codec->private_data;
	u8 mode, old_mode, format, old_format;

	 /* If the substream has 4 channel, do the necessary setup */
	if (params_channels(params) == 4) {
		/* Safety check: are we in the correct operating mode? */
		if ((twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) &
			TWL4030_OPTION_1))
			twl4030_tdm_enable(codec, substream->stream, 1);
		else
			return -EINVAL;
	}

	if (twl4030->configured)
		/* Ignoring hw_params for already configured DAI */
		return 0;
@@ -1461,6 +1506,9 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
	case SND_SOC_DAIFMT_I2S:
		format |= TWL4030_AIF_FORMAT_CODEC;
		break;
	case SND_SOC_DAIFMT_DSP_A:
		format |= TWL4030_AIF_FORMAT_TDM;
		break;
	default:
		return -EINVAL;
	}
@@ -1642,13 +1690,13 @@ struct snd_soc_dai twl4030_dai[] = {
	.playback = {
		.stream_name = "Playback",
		.channels_min = 2,
		.channels_max = 2,
		.channels_max = 4,
		.rates = TWL4030_RATES | SNDRV_PCM_RATE_96000,
		.formats = TWL4030_FORMATS,},
	.capture = {
		.stream_name = "Capture",
		.channels_min = 2,
		.channels_max = 2,
		.channels_max = 4,
		.rates = TWL4030_RATES,
		.formats = TWL4030_FORMATS,},
	.ops = &twl4030_dai_ops,
+11 −0
Original line number Diff line number Diff line
@@ -116,6 +116,17 @@
#define TWL4030_OPTION_1		(1 << 0)
#define TWL4030_OPTION_2		(0 << 0)

/* TWL4030_OPTION (0x02) Fields */

#define TWL4030_ATXL1_EN		(1 << 0)
#define TWL4030_ATXR1_EN		(1 << 1)
#define TWL4030_ATXL2_VTXL_EN		(1 << 2)
#define TWL4030_ATXR2_VTXR_EN		(1 << 3)
#define TWL4030_ARXL1_VRX_EN		(1 << 4)
#define TWL4030_ARXR1_EN		(1 << 5)
#define TWL4030_ARXL2_EN		(1 << 6)
#define TWL4030_ARXR2_EN		(1 << 7)

/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */

#define TWL4030_MICBIAS2_CTL		0x40