From 1593af62b694b3638edf577e3b763fa1a4ca3d76 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 4 May 2016 19:33:58 -0300 Subject: ASoC: fsl_sai: Introduce a compatible string for MX6UL MX6UL may need to configure the General Purpose Register 1 (GPR1), so it is better to add a new compatible string to differentiate. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 0754df771e3b..d8b673f7c577 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -797,7 +797,8 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->pdev = pdev; - if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai")) + if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai") || + of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) sai->sai_on_imx = true; sai->is_lsb_first = of_property_read_bool(np, "lsb-first"); @@ -898,6 +899,7 @@ static int fsl_sai_probe(struct platform_device *pdev) static const struct of_device_id fsl_sai_ids[] = { { .compatible = "fsl,vf610-sai", }, { .compatible = "fsl,imx6sx-sai", }, + { .compatible = "fsl,imx6ul-sai", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_sai_ids); -- cgit v1.2.3 From 4d2458507d0b465c62ae80f3e81b8c008ec96b05 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 4 May 2016 19:33:59 -0300 Subject: ASoC: fsl_sai: Allow setting the SAI MCLK direction On mx6ul the General Purpose Register 1 (GPR1) contains the following bits for configuring the direction of the SAI MCLKs: SAI1_MCLK_DIR, SAI2_MCLK_DIR, SAI3_MCLK_DIR Introduce the "fsl,sai-mclk-direction-output" optional property to allow configuring the SAI_MCLK outputs. Tested on a imx6ul-evk board. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/fsl-sai.txt | 5 +++++ include/linux/mfd/syscon/imx6q-iomuxc-gpr.h | 6 ++++++ sound/soc/fsl/fsl_sai.c | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+) (limited to 'sound/soc/fsl') diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt index 777b941d6cbe..740b467adf7d 100644 --- a/Documentation/devicetree/bindings/sound/fsl-sai.txt +++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt @@ -48,6 +48,11 @@ Required properties: receive data by following their own bit clocks and frame sync clocks separately. +Optional properties (for mx6ul): + + - fsl,sai-mclk-direction-output: This is a boolean property. If present, + indicates that SAI will output the SAI MCLK clock. + Note: - If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the default synchronous mode (sync Rx with Tx) will be used, which means both diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h index 238c8db953eb..68353822afce 100644 --- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h +++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h @@ -447,5 +447,11 @@ #define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18) #define IMX6UL_GPR1_ENET_CLK_DIR (0x3 << 17) #define IMX6UL_GPR1_ENET_CLK_OUTPUT (0x3 << 17) +#define IMX6UL_GPR1_SAI1_MCLK_DIR (0x1 << 19) +#define IMX6UL_GPR1_SAI2_MCLK_DIR (0x1 << 20) +#define IMX6UL_GPR1_SAI3_MCLK_DIR (0x1 << 21) +#define IMX6UL_GPR1_SAI_MCLK_MASK (0x7 << 19) +#define MCLK_DIR(x) (x == 1 ? IMX6UL_GPR1_SAI1_MCLK_DIR : x == 2 ? \ + IMX6UL_GPR1_SAI2_MCLK_DIR : IMX6UL_GPR1_SAI3_MCLK_DIR) #endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */ diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index d8b673f7c577..2147994ab46f 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "fsl_sai.h" #include "imx-pcm.h" @@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct fsl_sai *sai; + struct regmap *gpr; struct resource *res; void __iomem *base; char tmp[8]; int irq, ret, i; + int index; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) @@ -878,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev) fsl_sai_dai.symmetric_samplebits = 0; } + if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && + of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) { + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr"); + if (IS_ERR(gpr)) { + dev_err(&pdev->dev, "cannot find iomuxc registers\n"); + return PTR_ERR(gpr); + } + + index = of_alias_get_id(np, "sai"); + if (index < 0) + return index; + + regmap_update_bits(gpr, IOMUXC_GPR1, MCLK_DIR(index), + MCLK_DIR(index)); + } + sai->dma_params_rx.addr = res->start + FSL_SAI_RDR; sai->dma_params_tx.addr = res->start + FSL_SAI_TDR; sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX; -- cgit v1.2.3 From 48a260eec301fd7a112d1737ca2755d91558a349 Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:55 +0200 Subject: ASoC: fsl_ssi: Real hardware channels max number is 32 The max number of slots in TDM mode is 32: - Frame Rate Divider Control is a 5bit value - Time slot mask registers control 32 slots. Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index ed8de1035cda..8d5f3c192de2 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1158,14 +1158,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, }, -- cgit v1.2.3 From e09745f2e6a1f692fc63db01850aacf025475aad Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:56 +0200 Subject: ASoC: fsl_ssi: The IPG/5 limitation concerns the bitclk, not the sysclk. im6sl reference manual 47.7.4: " Bit clock - Used to serially clock the data bits in and out of the SSI port. This clock is either generated internally (from SSI's sys clock) or taken from external clock source (through the Tx/Rx clock ports). [...] Care should be taken to ensure that the bit clock frequency (either internally generated by dividing the SSI's sys clock or sourced from external device through Tx/Rx clock ports) is never greater than 1/5 of the ipg_clk (from CCM) frequency. " Since, in master mode, the sysclk is a multiple of bitclk, we can easily reach a high sysclk value, whereas keeping a reasonable bitclk. ex: 8ch x 16bit x 48kHz = 6144000, requires a 24576000 sysclk (PM=1) yet ipg_clk/5 = 66Mhz/5 = 13.2 Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8d5f3c192de2..86229c8902d2 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -670,6 +670,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 */ @@ -686,13 +695,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); -- cgit v1.2.3 From 0096b693962d3abde4f41b13cf03c765f33e9d8d Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:57 +0200 Subject: ASoC: fsl_ssi: Save a dev reference for dev_err() purpose. Most of functions only receive the ssi_private reference and don't have a knowledge of 'dev' pointer, even for debug purpose. Signed-off-by: Arnaud Mouiche Tested-by: Caleb Crome Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 86229c8902d2..149df3ca4f5e 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -261,6 +261,7 @@ struct fsl_ssi_private { struct fsl_ssi_dbg dbg_stats; const struct fsl_ssi_soc_data *soc; + struct device *dev; }; /* @@ -1404,6 +1405,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) { -- cgit v1.2.3 From d9f2a202877c15818d98268f47d6b4bcfcb84437 Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:58 +0200 Subject: ASoC: fsl_ssi: Fix samples being dropped at Playback startup If the capture is already running while playback is started, it is highly probable (>80% in a 8 channels scenario) that samples are lost between the DMA and TX fifo. The reason is that SIER.TDMAE is set before STCR.TFEN0, leaving a time window where the FIFO doesn't receive the samples written by the DMA. This particular case happened only if capture is already enabled as SCR.SSIEN is already set at the playback startup instant. Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 149df3ca4f5e..47ebb835f3f5 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -475,9 +475,9 @@ 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); 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; -- cgit v1.2.3 From 61fcf10a0ee44763e0347b297a377137f8950772 Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:13:59 +0200 Subject: ASoC: fsl_ssi: Fix channel slipping in Playback at startup Previously, SCR.SSIEN and SCR.TE were enabled at once if no capture stream was also running. This may not give a chance for the DMA to write the first sample in TX FIFO before the streaming starts on the PCM bus, inserting void samples first. Those void samples are then responsible for slipping the channels. Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 47ebb835f3f5..8944af542b4f 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -507,8 +507,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); + } } -- cgit v1.2.3 From 027db2e122db81b055a2b569d72f2f1d29c4d007 Mon Sep 17 00:00:00 2001 From: Arnaud Mouiche Date: Tue, 3 May 2016 14:14:00 +0200 Subject: ASoC: fsl_ssi: Fix channel slipping on capture (or playback) restart in full duplex. Happened when the Playback (or Capture) is running continuously and Capture (or Playback) is restarted (xrun, manual stop/start...) Since the RX (or TX) FIFO are only reset when the whole SSI is disabled, pending samples from previous capture (or playback) session may still be present. They must be erased to not introduce channel slipping. FIFO Clear register fields are documented in IMX51, IMX35 reference manual. They are not documented in IMX50 or IMX6 RM, despite they are working as expected on IMX6SL and IMX6solo. Signed-off-by: Arnaud Mouiche Reviewed-by: Fabio Estevam Tested-by: Caleb Crome Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8944af542b4f..d2dd47d2d500 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -400,6 +400,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 @@ -475,6 +495,8 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable, * (online configuration) */ if (enable) { + 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); -- cgit v1.2.3