Loading sound/soc/fsl/fsl_sai.c +68 −8 Original line number Original line Diff line number Diff line Loading @@ -126,6 +126,17 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) return IRQ_HANDLED; 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, static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) int clk_id, unsigned int freq, int fsl_dir) { { Loading Loading @@ -354,13 +365,25 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) return -EINVAL; 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, regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); regmap_update_bits(sai->regmap, FSL_SAI_RCR2, regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_DIV_MASK, savediv - 1); 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, regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); Loading @@ -383,11 +406,19 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, unsigned int channels = params_channels(params); unsigned int channels = params_channels(params); u32 word_width = snd_pcm_format_width(params_format(params)); u32 word_width = snd_pcm_format_width(params_format(params)); u32 val_cr4 = 0, val_cr5 = 0; u32 val_cr4 = 0, val_cr5 = 0; u32 slots = (channels == 1) ? 2 : channels; u32 slot_width = word_width; int ret; int ret; if (sai->slots) slots = sai->slots; if (sai->slot_width) slot_width = sai->slot_width; if (!sai->is_slave_mode) { if (!sai->is_slave_mode) { ret = fsl_sai_set_bclk(cpu_dai, tx, ret = fsl_sai_set_bclk(cpu_dai, tx, 2 * word_width * params_rate(params)); slots * slot_width * params_rate(params)); if (ret) if (ret) return ret; return ret; Loading @@ -399,21 +430,49 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, sai->mclk_streams |= BIT(substream->stream); sai->mclk_streams |= BIT(substream->stream); } } } } if (!sai->is_dsp_mode) 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_WNW(slot_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); val_cr5 |= FSL_SAI_CR5_W0W(slot_width); if (sai->is_lsb_first) if (sai->is_lsb_first) val_cr5 |= FSL_SAI_CR5_FBT(0); val_cr5 |= FSL_SAI_CR5_FBT(0); else else val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); 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), regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx), FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, Loading Loading @@ -550,6 +609,7 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { .set_sysclk = fsl_sai_set_dai_sysclk, .set_sysclk = fsl_sai_set_dai_sysclk, .set_fmt = fsl_sai_set_dai_fmt, .set_fmt = fsl_sai_set_dai_fmt, .set_tdm_slot = fsl_sai_set_dai_tdm_slot, .hw_params = fsl_sai_hw_params, .hw_params = fsl_sai_hw_params, .hw_free = fsl_sai_hw_free, .hw_free = fsl_sai_hw_free, .trigger = fsl_sai_trigger, .trigger = fsl_sai_trigger, Loading sound/soc/fsl/fsl_sai.h +3 −0 Original line number Original line Diff line number Diff line Loading @@ -143,6 +143,9 @@ struct fsl_sai { unsigned int mclk_id[2]; unsigned int mclk_id[2]; unsigned int mclk_streams; unsigned int mclk_streams; unsigned int slots; unsigned int slot_width; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_tx; }; }; Loading Loading
sound/soc/fsl/fsl_sai.c +68 −8 Original line number Original line Diff line number Diff line Loading @@ -126,6 +126,17 @@ static irqreturn_t fsl_sai_isr(int irq, void *devid) return IRQ_HANDLED; 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, static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) int clk_id, unsigned int freq, int fsl_dir) { { Loading Loading @@ -354,13 +365,25 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) return -EINVAL; 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, regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); regmap_update_bits(sai->regmap, FSL_SAI_RCR2, regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_DIV_MASK, savediv - 1); 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, regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); Loading @@ -383,11 +406,19 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, unsigned int channels = params_channels(params); unsigned int channels = params_channels(params); u32 word_width = snd_pcm_format_width(params_format(params)); u32 word_width = snd_pcm_format_width(params_format(params)); u32 val_cr4 = 0, val_cr5 = 0; u32 val_cr4 = 0, val_cr5 = 0; u32 slots = (channels == 1) ? 2 : channels; u32 slot_width = word_width; int ret; int ret; if (sai->slots) slots = sai->slots; if (sai->slot_width) slot_width = sai->slot_width; if (!sai->is_slave_mode) { if (!sai->is_slave_mode) { ret = fsl_sai_set_bclk(cpu_dai, tx, ret = fsl_sai_set_bclk(cpu_dai, tx, 2 * word_width * params_rate(params)); slots * slot_width * params_rate(params)); if (ret) if (ret) return ret; return ret; Loading @@ -399,21 +430,49 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, sai->mclk_streams |= BIT(substream->stream); sai->mclk_streams |= BIT(substream->stream); } } } } if (!sai->is_dsp_mode) 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_WNW(slot_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); val_cr5 |= FSL_SAI_CR5_W0W(slot_width); if (sai->is_lsb_first) if (sai->is_lsb_first) val_cr5 |= FSL_SAI_CR5_FBT(0); val_cr5 |= FSL_SAI_CR5_FBT(0); else else val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); 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), regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx), FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, Loading Loading @@ -550,6 +609,7 @@ static void fsl_sai_shutdown(struct snd_pcm_substream *substream, static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { .set_sysclk = fsl_sai_set_dai_sysclk, .set_sysclk = fsl_sai_set_dai_sysclk, .set_fmt = fsl_sai_set_dai_fmt, .set_fmt = fsl_sai_set_dai_fmt, .set_tdm_slot = fsl_sai_set_dai_tdm_slot, .hw_params = fsl_sai_hw_params, .hw_params = fsl_sai_hw_params, .hw_free = fsl_sai_hw_free, .hw_free = fsl_sai_hw_free, .trigger = fsl_sai_trigger, .trigger = fsl_sai_trigger, Loading
sound/soc/fsl/fsl_sai.h +3 −0 Original line number Original line Diff line number Diff line Loading @@ -143,6 +143,9 @@ struct fsl_sai { unsigned int mclk_id[2]; unsigned int mclk_id[2]; unsigned int mclk_streams; unsigned int mclk_streams; unsigned int slots; unsigned int slot_width; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_tx; }; }; Loading