Merge branch 'topic/fsl-sai' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-fsl

This commit is contained in:
Mark Brown 2015-11-25 12:12:48 +00:00
commit 3d8d0bd078
2 changed files with 71 additions and 8 deletions

View File

@ -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)
{ {
@ -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]));
@ -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;
@ -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,
@ -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,

View File

@ -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;
}; };