diff options
Diffstat (limited to 'sound/soc/fsl/fsl_ssi.c')
-rw-r--r-- | sound/soc/fsl/fsl_ssi.c | 80 |
1 files changed, 69 insertions, 11 deletions
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 08dcbbf60adb..632ecc0e3956 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -262,6 +262,7 @@ struct fsl_ssi_private { struct fsl_ssi_dbg dbg_stats; const struct fsl_ssi_soc_data *soc; + struct device *dev; }; /* @@ -401,6 +402,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private, } /* + * Clear RX or TX FIFO to remove samples from the previous + * stream session which may be still present in the FIFO and + * may introduce bad samples and/or channel slipping. + * + * Note: The SOR is not documented in recent IMX datasheet, but + * is described in IMX51 reference manual at section 56.3.3.15. + */ +static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private, + bool is_rx) +{ + if (is_rx) { + regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR, + CCSR_SSI_SOR_RX_CLR, CCSR_SSI_SOR_RX_CLR); + } else { + regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR, + CCSR_SSI_SOR_TX_CLR, CCSR_SSI_SOR_TX_CLR); + } +} + +/* * Calculate the bits that have to be disabled for the current stream that is * getting disabled. This keeps the bits enabled that are necessary for the * second stream to work if 'stream_active' is true. @@ -475,9 +496,11 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, * (online configuration) */ if (enable) { - regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier); + fsl_ssi_fifo_clear(ssi_private, vals->scr & CCSR_SSI_SCR_RE); + regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr); regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr); + regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier); } else { u32 sier; u32 srcr; @@ -507,8 +530,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, config_done: /* Enabling of subunits is done after configuration */ - if (enable) + if (enable) { + if (ssi_private->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) { + /* + * Be sure the Tx FIFO is filled when TE is set. + * Otherwise, there are some chances to start the + * playback with some void samples inserted first, + * generating a channel slip. + * + * First, SSIEN must be set, to let the FIFO be filled. + * + * Notes: + * - Limit this fix to the DMA case until FIQ cases can + * be tested. + * - Limit the length of the busy loop to not lock the + * system too long, even if 1-2 loops are sufficient + * in general. + */ + int i; + int max_loop = 100; + regmap_update_bits(regs, CCSR_SSI_SCR, + CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN); + for (i = 0; i < max_loop; i++) { + u32 sfcsr; + regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr); + if (CCSR_SSI_SFCSR_TFCNT0(sfcsr)) + break; + } + if (i == max_loop) { + dev_err(ssi_private->dev, + "Timeout waiting TX FIFO filling\n"); + } + } regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr); + } } @@ -671,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, if (IS_ERR(ssi_private->baudclk)) return -EINVAL; + /* + * Hardware limitation: The bclk rate must be + * never greater than 1/5 IPG clock rate + */ + if (freq * 5 > clk_get_rate(ssi_private->clk)) { + dev_err(cpu_dai->dev, "bitclk > ipgclk/5\n"); + return -EINVAL; + } + baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream)); /* It should be already enough to divide clock by setting pm alone */ @@ -687,13 +751,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, else clkrate = clk_round_rate(ssi_private->baudclk, tmprate); - /* - * Hardware limitation: The bclk rate must be - * never greater than 1/5 IPG clock rate - */ - if (clkrate * 5 > clk_get_rate(ssi_private->clk)) - continue; - clkrate /= factor; afreq = clkrate / (i + 1); @@ -1159,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = { .playback = { .stream_name = "CPU-Playback", .channels_min = 1, - .channels_max = 2, + .channels_max = 32, .rates = FSLSSI_I2S_RATES, .formats = FSLSSI_I2S_FORMATS, }, .capture = { .stream_name = "CPU-Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 32, .rates = FSLSSI_I2S_RATES, .formats = FSLSSI_I2S_FORMATS, }, @@ -1403,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) } ssi_private->soc = of_id->data; + ssi_private->dev = &pdev->dev; sprop = of_get_property(np, "fsl,mode", NULL); if (sprop) { |