aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/fsl/fsl_sai.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/fsl/fsl_sai.c')
-rw-r--r--sound/soc/fsl/fsl_sai.c98
1 files changed, 87 insertions, 11 deletions
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 08b460ba06ef..fef264d27fd3 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -126,6 +126,17 @@ out:
return IRQ_HANDLED;
}
+static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+ sai->slots = slots;
+ sai->slot_width = slot_width;
+
+ return 0;
+}
+
static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int fsl_dir)
{
@@ -354,13 +365,25 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq)
return -EINVAL;
}
- if ((tx && sai->synchronous[TX]) || (!tx && !sai->synchronous[RX])) {
+ /*
+ * 1) For Asynchronous mode, we must set RCR2 register for capture, and
+ * set TCR2 register for playback.
+ * 2) For Tx sync with Rx clock, we must set RCR2 register for playback
+ * and capture.
+ * 3) For Rx sync with Tx clock, we must set TCR2 register for playback
+ * and capture.
+ * 4) For Tx and Rx are both Synchronous with another SAI, we just
+ * ignore it.
+ */
+ if ((sai->synchronous[TX] && !sai->synchronous[RX]) ||
+ (!tx && !sai->synchronous[RX])) {
regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
regmap_update_bits(sai->regmap, FSL_SAI_RCR2,
FSL_SAI_CR2_DIV_MASK, savediv - 1);
- } else {
+ } else if ((sai->synchronous[RX] && !sai->synchronous[TX]) ||
+ (tx && !sai->synchronous[TX])) {
regmap_update_bits(sai->regmap, FSL_SAI_TCR2,
FSL_SAI_CR2_MSEL_MASK,
FSL_SAI_CR2_MSEL(sai->mclk_id[tx]));
@@ -381,13 +404,21 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int channels = params_channels(params);
- u32 word_width = snd_pcm_format_width(params_format(params));
+ u32 word_width = params_width(params);
u32 val_cr4 = 0, val_cr5 = 0;
+ u32 slots = (channels == 1) ? 2 : channels;
+ u32 slot_width = word_width;
int ret;
+ if (sai->slots)
+ slots = sai->slots;
+
+ if (sai->slot_width)
+ slot_width = sai->slot_width;
+
if (!sai->is_slave_mode) {
ret = fsl_sai_set_bclk(cpu_dai, tx,
- 2 * word_width * params_rate(params));
+ slots * slot_width * params_rate(params));
if (ret)
return ret;
@@ -399,21 +430,49 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
sai->mclk_streams |= BIT(substream->stream);
}
-
}
if (!sai->is_dsp_mode)
- val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
+ val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
- val_cr5 |= FSL_SAI_CR5_WNW(word_width);
- val_cr5 |= FSL_SAI_CR5_W0W(word_width);
+ val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
+ val_cr5 |= FSL_SAI_CR5_W0W(slot_width);
if (sai->is_lsb_first)
val_cr5 |= FSL_SAI_CR5_FBT(0);
else
val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
- val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
+ val_cr4 |= FSL_SAI_CR4_FRSZ(slots);
+
+ /*
+ * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will
+ * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4),
+ * RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync
+ * error.
+ */
+
+ if (!sai->is_slave_mode) {
+ if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) {
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR4,
+ FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+ val_cr4);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCR5,
+ FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+ FSL_SAI_CR5_FBT_MASK, val_cr5);
+ regmap_write(sai->regmap, FSL_SAI_TMR,
+ ~0UL - ((1 << channels) - 1));
+ } else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) {
+ regmap_update_bits(sai->regmap, FSL_SAI_RCR4,
+ FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
+ val_cr4);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCR5,
+ FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK |
+ FSL_SAI_CR5_FBT_MASK, val_cr5);
+ regmap_write(sai->regmap, FSL_SAI_RMR,
+ ~0UL - ((1 << channels) - 1));
+ }
+ }
regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx),
FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK,
@@ -569,6 +628,7 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt,
+ .set_tdm_slot = fsl_sai_set_dai_tdm_slot,
.hw_params = fsl_sai_hw_params,
.hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
@@ -627,6 +687,22 @@ static const struct snd_soc_component_driver fsl_component = {
.name = "fsl-sai",
};
+static struct reg_default fsl_sai_reg_defaults[] = {
+ {FSL_SAI_TCR1, 0},
+ {FSL_SAI_TCR2, 0},
+ {FSL_SAI_TCR3, 0},
+ {FSL_SAI_TCR4, 0},
+ {FSL_SAI_TCR5, 0},
+ {FSL_SAI_TDR, 0},
+ {FSL_SAI_TMR, 0},
+ {FSL_SAI_RCR1, 0},
+ {FSL_SAI_RCR2, 0},
+ {FSL_SAI_RCR3, 0},
+ {FSL_SAI_RCR4, 0},
+ {FSL_SAI_RCR5, 0},
+ {FSL_SAI_RMR, 0},
+};
+
static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -660,13 +736,11 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
case FSL_SAI_RCSR:
case FSL_SAI_TFR:
case FSL_SAI_RFR:
- case FSL_SAI_TDR:
case FSL_SAI_RDR:
return true;
default:
return false;
}
-
}
static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
@@ -699,6 +773,8 @@ static const struct regmap_config fsl_sai_regmap_config = {
.val_bits = 32,
.max_register = FSL_SAI_RMR,
+ .reg_defaults = fsl_sai_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults),
.readable_reg = fsl_sai_readable_reg,
.volatile_reg = fsl_sai_volatile_reg,
.writeable_reg = fsl_sai_writeable_reg,