From d8764646e1cc0ad209af29396e59f836b7d8f164 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 20 Nov 2013 10:04:15 +0100 Subject: ASoC: fsl-ssi: Move ac97 specific setup to seperate function This is a pure cleanup patch to increase code readability. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 57 +++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 25 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 35e277379b86..fb8f52a0e7b7 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -321,6 +321,36 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) return ret; } +static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) +{ + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + + /* + * Setup the clock control register + */ + write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), + &ssi->stccr); + write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), + &ssi->srccr); + + /* + * Enable AC97 mode and startup the SSI + */ + write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV, + &ssi->sacnt); + write_ssi(0xff, &ssi->saccdis); + write_ssi(0x300, &ssi->saccen); + + /* + * Enable SSI, Transmit and Receive. AC97 has to communicate with the + * codec before a stream is started. + */ + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN | + CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); + + write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); +} + static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) { struct ccsr_ssi __iomem *ssi = ssi_private->ssi; @@ -387,31 +417,8 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) * because it is also running without an active substream. Normally SSI * is only enabled when there is a substream. */ - if (ssi_private->imx_ac97) { - /* - * Setup the clock control register - */ - write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), - &ssi->stccr); - write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), - &ssi->srccr); - - /* - * Enable AC97 mode and startup the SSI - */ - write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV, - &ssi->sacnt); - write_ssi(0xff, &ssi->saccdis); - write_ssi(0x300, &ssi->saccen); - - /* - * Enable SSI, Transmit and Receive - */ - write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN | - CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); - - write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); - } + if (ssi_private->imx_ac97) + fsl_ssi_setup_ac97(ssi_private); return 0; } -- cgit v1.2.3 From 4ded61eb3ea87e9c563e09662be3ed5e942ff2a2 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 2 Dec 2013 00:41:24 -0200 Subject: ASoC: imx-spdif: Remove error message upon devm_kzalloc() failure No need to have a specific OOM message, since there is generic MM out of memory message in place. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/imx-spdif.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c index 8499d5292f08..980dd1fc9113 100644 --- a/sound/soc/fsl/imx-spdif.c +++ b/sound/soc/fsl/imx-spdif.c @@ -35,7 +35,6 @@ static int imx_spdif_audio_probe(struct platform_device *pdev) data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) { - dev_err(&pdev->dev, "failed to allocate memory\n"); ret = -ENOMEM; goto end; } -- cgit v1.2.3 From 7e6d18ac7ea1372b462778ff7c416ceaabe71b66 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 28 Nov 2013 08:50:35 +0100 Subject: ASoC: fsl: Use devm_snd_dmaengine_pcm_register() Makes the code shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 8 -------- sound/soc/fsl/fsl_ssi.c | 4 ---- sound/soc/fsl/imx-pcm-dma.c | 9 ++------- sound/soc/fsl/imx-pcm.h | 5 ----- sound/soc/fsl/imx-ssi.c | 3 --- 5 files changed, 2 insertions(+), 27 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 55193a5596ca..4d075f1abe78 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1181,13 +1181,6 @@ static int fsl_spdif_probe(struct platform_device *pdev) return ret; } -static int fsl_spdif_remove(struct platform_device *pdev) -{ - imx_pcm_dma_exit(pdev); - - return 0; -} - static const struct of_device_id fsl_spdif_dt_ids[] = { { .compatible = "fsl,imx35-spdif", }, {} @@ -1201,7 +1194,6 @@ static struct platform_driver fsl_spdif_driver = { .of_match_table = fsl_spdif_dt_ids, }, .probe = fsl_spdif_probe, - .remove = fsl_spdif_remove, }; module_platform_driver(fsl_spdif_driver); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index fb8f52a0e7b7..3df0318e71df 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1109,8 +1109,6 @@ done: return 0; error_dai: - if (ssi_private->ssi_on_imx) - imx_pcm_dma_exit(pdev); snd_soc_unregister_component(&pdev->dev); error_dev: @@ -1132,8 +1130,6 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (!ssi_private->new_binding) platform_device_unregister(ssi_private->pdev); - if (ssi_private->ssi_on_imx) - imx_pcm_dma_exit(pdev); snd_soc_unregister_component(&pdev->dev); device_remove_file(&pdev->dev, &ssi_private->dev_attr); if (ssi_private->ssi_on_imx) diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index aee23077080a..c5e47f866b4b 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -61,16 +61,11 @@ static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = { int imx_pcm_dma_init(struct platform_device *pdev) { - return snd_dmaengine_pcm_register(&pdev->dev, &imx_dmaengine_pcm_config, + return devm_snd_dmaengine_pcm_register(&pdev->dev, + &imx_dmaengine_pcm_config, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | SND_DMAENGINE_PCM_FLAG_COMPAT); } EXPORT_SYMBOL_GPL(imx_pcm_dma_init); -void imx_pcm_dma_exit(struct platform_device *pdev) -{ - snd_dmaengine_pcm_unregister(&pdev->dev); -} -EXPORT_SYMBOL_GPL(imx_pcm_dma_exit); - MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 5d5b73303e11..c79cb27473be 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -40,16 +40,11 @@ struct imx_pcm_fiq_params { #if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA) int imx_pcm_dma_init(struct platform_device *pdev); -void imx_pcm_dma_exit(struct platform_device *pdev); #else static inline int imx_pcm_dma_init(struct platform_device *pdev) { return -ENODEV; } - -static inline void imx_pcm_dma_exit(struct platform_device *pdev) -{ -} #endif #if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_FIQ) diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index f5f248c91c16..cc7376f87f88 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -624,9 +624,6 @@ static int imx_ssi_remove(struct platform_device *pdev) { struct imx_ssi *ssi = platform_get_drvdata(pdev); - if (!ssi->dma_init) - imx_pcm_dma_exit(pdev); - if (!ssi->fiq_init) imx_pcm_fiq_exit(pdev); -- cgit v1.2.3 From 3621dbbc27ff347f2e4476013054bab18ebd906c Mon Sep 17 00:00:00 2001 From: Oskar Schirmer Date: Sat, 16 Nov 2013 07:52:25 +0000 Subject: ASoC: fsl: imx-ssi: omit ssi counter to avoid harm in unbalanced situation Unbalanced calls to imx_ssi_trigger() may result in endless SSI activity and thus provoke eternal sound. While on the first glance, the switch statement looks pretty symmetric, the SUSPEND/RESUME pair is not: the suspend case comes along snd_pcm_suspend_all(), which for fsl/imx-pcm-fiq is called only at snd_soc_suspend(), but the resume case originates straight from the SNDRV_PCM_IOCTL_RESUME. This way userland may provoke an unbalanced resume, which might cause the ssi->enabled counter to increase and never return to zero again, so eventually SSI_SCR_SSIEN is never disabled. As the information on whether to enable the SSI or not is contained in the two bits for TE/RE, we save all the software mirroring of hardware state here and simply use the hardware register itself to keep the state of whether someone is currently playing or capturing. This is essentially the same stuff as in sound/soc/fsl/imx-pcm-fiq.c which I send a patch for three days ago. Astonishing enough this highly fragile scheme is used twice in parallel to serve the very same control function, synchronously: Once out of sync you are lost until reboot. Note, that these fixes wont prevent state machine distortion on alsa level to cut sound or the like. It just makes sure we have a chance to synchronise again later on. Signed-off-by: Oskar Schirmer Signed-off-by: Mark Brown --- sound/soc/fsl/imx-ssi.c | 5 ++--- sound/soc/fsl/imx-ssi.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index cc7376f87f88..6336757e967a 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -304,8 +304,7 @@ static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, scr |= SSI_SCR_RE; sier |= sier_bits; - if (++ssi->enabled == 1) - scr |= SSI_SCR_SSIEN; + scr |= SSI_SCR_SSIEN; break; @@ -318,7 +317,7 @@ static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, scr &= ~SSI_SCR_RE; sier &= ~sier_bits; - if (--ssi->enabled == 0) + if (!(scr & (SSI_SCR_TE | SSI_SCR_RE))) scr &= ~SSI_SCR_SSIEN; break; diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h index 560c40fc9ebb..be6562365b6a 100644 --- a/sound/soc/fsl/imx-ssi.h +++ b/sound/soc/fsl/imx-ssi.h @@ -213,7 +213,6 @@ struct imx_ssi { int fiq_init; int dma_init; - int enabled; }; #endif /* _IMX_SSI_H */ -- cgit v1.2.3 From 2924a9981006ad01efb46c754689fa7d03e3eb4f Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 2 Dec 2013 23:29:03 +0800 Subject: ASoC: fsl_ssi: Add monaural audio support for non-ac97 interface The normal mode of SSI allows it to send/receive data to/from the first slot of each period. So we can use this normal mode to trick I2S signal by puting/getting data to/from the first slot only (the left channel) so as to support monaural audio playback and recording. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 19 ++++++++++++------- 1 file changed, 12 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 3df0318e71df..90ff1071e29c 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -143,6 +143,7 @@ struct fsl_ssi_private { bool ssi_on_imx; bool imx_ac97; bool use_dma; + u8 i2s_mode; struct clk *clk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; @@ -354,14 +355,13 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) { struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - u8 i2s_mode; u8 wm; int synchronous = ssi_private->cpu_dai_drv.symmetric_rates; if (ssi_private->imx_ac97) - i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET; + ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET; else - i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE; + ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE; /* * Section 16.5 of the MPC8610 reference manual says that the SSI needs @@ -378,7 +378,7 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, CCSR_SSI_SCR_TFR_CLK_DIS | - i2s_mode | + ssi_private->i2s_mode | (synchronous ? CCSR_SSI_SCR_SYN : 0)); write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | @@ -508,6 +508,7 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, { struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + unsigned int channels = params_channels(hw_params); unsigned int sample_size = snd_pcm_format_width(params_format(hw_params)); u32 wl = CCSR_SSI_SxCCR_WL(sample_size); @@ -537,6 +538,11 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, else write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl); + if (!ssi_private->imx_ac97) + write_ssi_mask(&ssi->scr, + CCSR_SSI_SCR_NET | CCSR_SSI_SCR_I2S_MODE_MASK, + channels == 1 ? 0 : ssi_private->i2s_mode); + return 0; } @@ -649,14 +655,13 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { static struct snd_soc_dai_driver fsl_ssi_dai_template = { .probe = fsl_ssi_dai_probe, .playback = { - /* The SSI does not support monaural audio. */ - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = FSLSSI_I2S_RATES, .formats = FSLSSI_I2S_FORMATS, }, .capture = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = FSLSSI_I2S_RATES, .formats = FSLSSI_I2S_FORMATS, -- cgit v1.2.3 From 07a9483aaca5d3b5de8ee824ee576321d3f8b4c6 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 3 Dec 2013 18:38:07 +0800 Subject: ASoC: fsl_ssi: Implement symmetric_channels and symmetric_samplebits Since we introduced symmetric_channels and symmetric_samplebits, we implement these two features to fsl_ssi so as to drop some no-more-needed code and make the driver neat and clean. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 79 ++++++------------------------------------------- 1 file changed, 9 insertions(+), 70 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 90ff1071e29c..f9f4569417ed 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -119,8 +119,6 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set) * @ssi: pointer to the SSI's registers * @ssi_phys: physical address of the SSI registers * @irq: IRQ of this SSI - * @first_stream: pointer to the stream that was opened first - * @second_stream: pointer to second stream * @playback: the number of playback streams opened * @capture: the number of capture streams opened * @cpu_dai: the CPU DAI for this device @@ -132,8 +130,6 @@ struct fsl_ssi_private { struct ccsr_ssi __iomem *ssi; dma_addr_t ssi_phys; unsigned int irq; - struct snd_pcm_substream *first_stream; - struct snd_pcm_substream *second_stream; unsigned int fifo_depth; struct snd_soc_dai_driver cpu_dai_drv; struct device_attribute dev_attr; @@ -438,54 +434,13 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); - int synchronous = ssi_private->cpu_dai_drv.symmetric_rates; - /* - * If this is the first stream opened, then request the IRQ - * and initialize the SSI registers. + /* First, we only do fsl_ssi_setup() when SSI is going to be active. + * Second, fsl_ssi_setup was already called by ac97_init earlier if + * the driver is in ac97 mode. */ - if (!ssi_private->first_stream) { - ssi_private->first_stream = substream; - - /* - * fsl_ssi_setup was already called by ac97_init earlier if - * the driver is in ac97 mode. - */ - if (!ssi_private->imx_ac97) - fsl_ssi_setup(ssi_private); - } else { - if (synchronous) { - struct snd_pcm_runtime *first_runtime = - ssi_private->first_stream->runtime; - /* - * This is the second stream open, and we're in - * synchronous mode, so we need to impose sample - * sample size constraints. This is because STCCR is - * used for playback and capture in synchronous mode, - * so there's no way to specify different word - * lengths. - * - * Note that this can cause a race condition if the - * second stream is opened before the first stream is - * fully initialized. We provide some protection by - * checking to make sure the first stream is - * initialized, but it's not perfect. ALSA sometimes - * re-initializes the driver with a different sample - * rate or size. If the second stream is opened - * before the first stream has received its final - * parameters, then the second stream may be - * constrained to the wrong sample rate or size. - */ - if (first_runtime->sample_bits) { - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - first_runtime->sample_bits, - first_runtime->sample_bits); - } - } - - ssi_private->second_stream = substream; - } + if (!dai->active && !ssi_private->imx_ac97) + fsl_ssi_setup(ssi_private); return 0; } @@ -615,23 +570,6 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -/** - * fsl_ssi_shutdown: shutdown the SSI - * - * Shutdown the SSI if there are no other substreams open. - */ -static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); - - if (ssi_private->first_stream == substream) - ssi_private->first_stream = ssi_private->second_stream; - - ssi_private->second_stream = NULL; -} - static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) { struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai); @@ -647,7 +585,6 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { .startup = fsl_ssi_startup, .hw_params = fsl_ssi_hw_params, - .shutdown = fsl_ssi_shutdown, .trigger = fsl_ssi_trigger, }; @@ -722,7 +659,6 @@ static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd, static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = { .startup = fsl_ssi_startup, - .shutdown = fsl_ssi_shutdown, .trigger = fsl_ssi_ac97_trigger, }; @@ -947,8 +883,11 @@ static int fsl_ssi_probe(struct platform_device *pdev) } /* Are the RX and the TX clocks locked? */ - if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) + if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { ssi_private->cpu_dai_drv.symmetric_rates = 1; + ssi_private->cpu_dai_drv.symmetric_channels = 1; + ssi_private->cpu_dai_drv.symmetric_samplebits = 1; + } /* Determine the FIFO depth. */ iprop = of_get_property(np, "fsl,fifo-depth", NULL); -- cgit v1.2.3 From 24f4bd57a7b24d10e52a3807f88adec79824e5d8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 4 Dec 2013 20:27:44 -0200 Subject: ASoC: imx-ssi: Check the return value from clk_prepare_enable() clk_prepare_enable() may fail, so let's check its return value and propagate it in the case of error. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/imx-ssi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 6336757e967a..df552fa1aa65 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -535,7 +535,9 @@ static int imx_ssi_probe(struct platform_device *pdev) ret); goto failed_clk; } - clk_prepare_enable(ssi->clk); + ret = clk_prepare_enable(ssi->clk); + if (ret) + goto failed_clk; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ssi->base = devm_ioremap_resource(&pdev->dev, res); -- cgit v1.2.3 From dd407a3243234c6a17ba624d698e6824067003c9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 18 Dec 2013 13:50:10 +0000 Subject: ASoC: fsl/mxs: Remove unnecessarily gendered language The kernel as a number of cases of gendered language. The majority of these refer to objects that don't have gender in English, and so I've replaced them with "it" and "its". Some refer to people (developers or users), and I've replaced these with the singular "they" variant. Some are simply typos that I've fixed up. I've left cases where gendered language was used to refer to specific individuals, was a quote or is part of license text. Signed-off-by: Matthew Garrett Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index fb9bb9eb5ca3..d570f8c81dc6 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -852,7 +852,7 @@ static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm) } /** - * find_ssi_node -- returns the SSI node that points to his DMA channel node + * find_ssi_node -- returns the SSI node that points to its DMA channel node * * Although this DMA driver attempts to operate independently of the other * devices, it still needs to determine some information about the SSI device -- cgit v1.2.3 From 4355082149429d1f87b6fbfc3ebc6305a5372ce2 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 17 Dec 2013 11:24:38 +0800 Subject: ASoC: Add SAI SoC Digital Audio Interface driver. This adds Freescale SAI ASoC Audio support. This implementation is only compatible with device tree definition. Features: o Supports playback/capture o Supports 16/20/24 bit PCM o Supports 8k - 96k sample rates o Supports master and slave mode. Signed-off-by: Alison Wang Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 4 + sound/soc/fsl/Makefile | 4 +- sound/soc/fsl/fsl_sai.c | 492 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/fsl/fsl_sai.h | 114 +++++++++++ 4 files changed, 613 insertions(+), 1 deletion(-) create mode 100644 sound/soc/fsl/fsl_sai.c create mode 100644 sound/soc/fsl/fsl_sai.h (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index b7ab71f2ccc1..ac4fe4ea15a9 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,3 +1,7 @@ +config SND_SOC_FSL_SAI + tristate + select SND_SOC_GENERIC_DMAENGINE_PCM + config SND_SOC_FSL_SSI tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 8db705b0fdf9..aaccbee17006 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -10,11 +10,13 @@ obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o snd-soc-p1022-rdk-objs := p1022_rdk.o obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o -# Freescale PowerPC SSI/DMA Platform Support +# Freescale SSI/DMA/SAI/SPDIF Support +snd-soc-fsl-sai-objs := fsl_sai.o snd-soc-fsl-ssi-objs := fsl_ssi.o snd-soc-fsl-spdif-objs := fsl_spdif.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o +obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c new file mode 100644 index 000000000000..50a797e65781 --- /dev/null +++ b/sound/soc/fsl/fsl_sai.c @@ -0,0 +1,492 @@ +/* + * Freescale ALSA SoC Digital Audio Interface (SAI) driver. + * + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * This program is free software, you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or(at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_sai.h" + +static inline u32 sai_readl(struct fsl_sai *sai, + const void __iomem *addr) +{ + u32 val; + + val = __raw_readl(addr); + + if (likely(sai->big_endian_regs)) + val = be32_to_cpu(val); + else + val = le32_to_cpu(val); + rmb(); + + return val; +} + +static inline void sai_writel(struct fsl_sai *sai, + u32 val, void __iomem *addr) +{ + wmb(); + if (likely(sai->big_endian_regs)) + val = cpu_to_be32(val); + else + val = cpu_to_le32(val); + + __raw_writel(val, addr); +} + +static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int fsl_dir) +{ + u32 val_cr2, reg_cr2; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + if (fsl_dir == FSL_FMT_TRANSMITTER) + reg_cr2 = FSL_SAI_TCR2; + else + reg_cr2 = FSL_SAI_RCR2; + + val_cr2 = sai_readl(sai, sai->base + reg_cr2); + switch (clk_id) { + case FSL_SAI_CLK_BUS: + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + val_cr2 |= FSL_SAI_CR2_MSEL_BUS; + break; + case FSL_SAI_CLK_MAST1: + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1; + break; + case FSL_SAI_CLK_MAST2: + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2; + break; + case FSL_SAI_CLK_MAST3: + val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK; + val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3; + break; + default: + return -EINVAL; + } + sai_writel(sai, val_cr2, sai->base + reg_cr2); + + return 0; +} + +static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + int ret; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + if (dir == SND_SOC_CLOCK_IN) + return 0; + + ret = clk_prepare_enable(sai->clk); + if (ret) + return ret; + + sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR); + sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR); + sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1); + sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1); + + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, + FSL_FMT_TRANSMITTER); + if (ret) { + dev_err(cpu_dai->dev, + "Cannot set SAI's transmitter sysclk: %d\n", + ret); + return ret; + } + + ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, + FSL_FMT_RECEIVER); + if (ret) { + dev_err(cpu_dai->dev, + "Cannot set SAI's receiver sysclk: %d\n", + ret); + return ret; + } + + clk_disable_unprepare(sai->clk); + + return 0; +} + +static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, + unsigned int fmt, int fsl_dir) +{ + u32 val_cr2, val_cr3, val_cr4, reg_cr2, reg_cr3, reg_cr4; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + if (fsl_dir == FSL_FMT_TRANSMITTER) { + reg_cr2 = FSL_SAI_TCR2; + reg_cr3 = FSL_SAI_TCR3; + reg_cr4 = FSL_SAI_TCR4; + } else { + reg_cr2 = FSL_SAI_RCR2; + reg_cr3 = FSL_SAI_RCR3; + reg_cr4 = FSL_SAI_RCR4; + } + + val_cr2 = sai_readl(sai, sai->base + reg_cr2); + val_cr3 = sai_readl(sai, sai->base + reg_cr3); + val_cr4 = sai_readl(sai, sai->base + reg_cr4); + + if (sai->big_endian_data) + val_cr4 |= FSL_SAI_CR4_MF; + else + val_cr4 &= ~FSL_SAI_CR4_MF; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val_cr4 |= FSL_SAI_CR4_FSE; + val_cr4 |= FSL_SAI_CR4_FSP; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + val_cr4 |= FSL_SAI_CR4_FSP; + val_cr2 &= ~FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_IB_NF: + val_cr4 &= ~FSL_SAI_CR4_FSP; + val_cr2 &= ~FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_NB_IF: + val_cr4 |= FSL_SAI_CR4_FSP; + val_cr2 |= FSL_SAI_CR2_BCP; + break; + case SND_SOC_DAIFMT_NB_NF: + val_cr4 &= ~FSL_SAI_CR4_FSP; + val_cr2 |= FSL_SAI_CR2_BCP; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + val_cr2 |= FSL_SAI_CR2_BCD_MSTR; + val_cr4 |= FSL_SAI_CR4_FSD_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR; + val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR; + break; + default: + return -EINVAL; + } + + val_cr3 |= FSL_SAI_CR3_TRCE; + + if (fsl_dir == FSL_FMT_RECEIVER) + val_cr2 |= FSL_SAI_CR2_SYNC; + + sai_writel(sai, val_cr2, sai->base + reg_cr2); + sai_writel(sai, val_cr3, sai->base + reg_cr3); + sai_writel(sai, val_cr4, sai->base + reg_cr4); + + return 0; +} + +static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + int ret; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + ret = clk_prepare_enable(sai->clk); + if (ret) + return ret; + + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); + if (ret) { + dev_err(cpu_dai->dev, + "Cannot set SAI's transmitter format: %d\n", + ret); + return ret; + } + + ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); + if (ret) { + dev_err(cpu_dai->dev, + "Cannot set SAI's receiver format: %d\n", + ret); + return ret; + } + + clk_disable_unprepare(sai->clk); + + return 0; +} + +static int fsl_sai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr, word_width; + unsigned int channels = params_channels(params); + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + reg_cr4 = FSL_SAI_TCR4; + reg_cr5 = FSL_SAI_TCR5; + reg_mr = FSL_SAI_TMR; + } else { + reg_cr4 = FSL_SAI_RCR4; + reg_cr5 = FSL_SAI_RCR5; + reg_mr = FSL_SAI_RMR; + } + + val_cr4 = sai_readl(sai, sai->base + reg_cr4); + val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK; + val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK; + + val_cr5 = sai_readl(sai, sai->base + reg_cr5); + val_cr5 &= ~FSL_SAI_CR5_WNW_MASK; + val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; + val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + word_width = 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + word_width = 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + word_width = 24; + break; + default: + return -EINVAL; + } + + val_cr4 |= FSL_SAI_CR4_SYWD(word_width); + val_cr5 |= FSL_SAI_CR5_WNW(word_width); + val_cr5 |= FSL_SAI_CR5_W0W(word_width); + + if (sai->big_endian_data) + val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); + else + val_cr5 |= FSL_SAI_CR5_FBT(0); + + val_cr4 |= FSL_SAI_CR4_FRSZ(channels); + if (channels == 2 || channels == 1) + val_mr = ~0UL - ((1 << channels) - 1); + else + return -EINVAL; + + sai_writel(sai, val_cr4, sai->base + reg_cr4); + sai_writel(sai, val_cr5, sai->base + reg_cr5); + sai_writel(sai, val_mr, sai->base + reg_mr); + + return 0; +} + +static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int tcsr, rcsr; + + tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR); + rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tcsr |= FSL_SAI_CSR_FRDE; + rcsr &= ~FSL_SAI_CSR_FRDE; + } else { + rcsr |= FSL_SAI_CSR_FRDE; + tcsr &= ~FSL_SAI_CSR_FRDE; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + tcsr |= FSL_SAI_CSR_TERE; + rcsr |= FSL_SAI_CSR_TERE; + sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); + sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (!(cpu_dai->playback_active || cpu_dai->capture_active)) { + tcsr &= ~FSL_SAI_CSR_TERE; + rcsr &= ~FSL_SAI_CSR_TERE; + } + sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); + sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fsl_sai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + int ret; + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + ret = clk_prepare_enable(sai->clk); + + return ret; +} + +static void fsl_sai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + + clk_disable_unprepare(sai->clk); +} + +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, + .hw_params = fsl_sai_hw_params, + .trigger = fsl_sai_trigger, + .startup = fsl_sai_startup, + .shutdown = fsl_sai_shutdown, +}; + +static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); + + cpu_dai->playback_dma_data = &sai->dma_params_tx; + cpu_dai->capture_dma_data = &sai->dma_params_rx; + + snd_soc_dai_set_drvdata(cpu_dai, sai); + + return 0; +} + +static int fsl_sai_dai_remove(struct snd_soc_dai *cpu_dai) +{ + cpu_dai->playback_dma_data = NULL; + cpu_dai->capture_dma_data = NULL; + + snd_soc_dai_set_drvdata(cpu_dai, NULL); + + return 0; +} + +static struct snd_soc_dai_driver fsl_sai_dai = { + .probe = fsl_sai_dai_probe, + .remove = fsl_sai_dai_remove, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = FSL_SAI_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = FSL_SAI_FORMATS, + }, + .ops = &fsl_sai_pcm_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_component = { + .name = "fsl-sai", +}; + +static int fsl_sai_probe(struct platform_device *pdev) +{ + int ret; + struct fsl_sai *sai; + struct resource *res; + struct device_node *np = pdev->dev.of_node; + + sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); + if (!sai) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sai->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sai->base)) + return PTR_ERR(sai->base); + + sai->clk = devm_clk_get(&pdev->dev, "sai"); + if (IS_ERR(sai->clk)) { + dev_err(&pdev->dev, "Cannot get SAI's clock\n"); + return PTR_ERR(sai->clk); + } + + 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; + sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; + + sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs"); + sai->big_endian_data = of_property_read_bool(np, "big-endian-data"); + + platform_set_drvdata(pdev, sai); + + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component, + &fsl_sai_dai, 1); + if (ret) + return ret; + + ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); + if (ret) + return ret; + + return 0; +} + +static int fsl_sai_remove(struct platform_device *pdev) +{ + snd_dmaengine_pcm_unregister(&pdev->dev); + + return 0; +} + +static const struct of_device_id fsl_sai_ids[] = { + { .compatible = "fsl,vf610-sai", }, + { /* sentinel */ } +}; + +static struct platform_driver fsl_sai_driver = { + .probe = fsl_sai_probe, + .remove = fsl_sai_remove, + + .driver = { + .name = "fsl-sai", + .owner = THIS_MODULE, + .of_match_table = fsl_sai_ids, + }, +}; +module_platform_driver(fsl_sai_driver); + +MODULE_DESCRIPTION("Freescale Soc SAI Interface"); +MODULE_AUTHOR("Xiubo Li, "); +MODULE_ALIAS("platform:fsl-sai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h new file mode 100644 index 000000000000..41bb62e69361 --- /dev/null +++ b/sound/soc/fsl/fsl_sai.h @@ -0,0 +1,114 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __FSL_SAI_H +#define __FSL_SAI_H + +#include + +#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +/* SAI Transmit/Recieve Control Register */ +#define FSL_SAI_TCSR 0x00 +#define FSL_SAI_RCSR 0x80 +#define FSL_SAI_CSR_TERE BIT(31) +#define FSL_SAI_CSR_FWF BIT(17) +#define FSL_SAI_CSR_FRIE BIT(8) +#define FSL_SAI_CSR_FRDE BIT(0) + +/* SAI Transmit Data/FIFO/MASK Register */ +#define FSL_SAI_TDR 0x20 +#define FSL_SAI_TFR 0x40 +#define FSL_SAI_TMR 0x60 + +/* SAI Recieve Data/FIFO/MASK Register */ +#define FSL_SAI_RDR 0xa0 +#define FSL_SAI_RFR 0xc0 +#define FSL_SAI_RMR 0xe0 + +/* SAI Transmit and Recieve Configuration 1 Register */ +#define FSL_SAI_TCR1 0x04 +#define FSL_SAI_RCR1 0x84 + +/* SAI Transmit and Recieve Configuration 2 Register */ +#define FSL_SAI_TCR2 0x08 +#define FSL_SAI_RCR2 0x88 +#define FSL_SAI_CR2_SYNC BIT(30) +#define FSL_SAI_CR2_MSEL_MASK (0xff << 26) +#define FSL_SAI_CR2_MSEL_BUS 0 +#define FSL_SAI_CR2_MSEL_MCLK1 BIT(26) +#define FSL_SAI_CR2_MSEL_MCLK2 BIT(27) +#define FSL_SAI_CR2_MSEL_MCLK3 (BIT(26) | BIT(27)) +#define FSL_SAI_CR2_BCP BIT(25) +#define FSL_SAI_CR2_BCD_MSTR BIT(24) + +/* SAI Transmit and Recieve Configuration 3 Register */ +#define FSL_SAI_TCR3 0x0c +#define FSL_SAI_RCR3 0x8c +#define FSL_SAI_CR3_TRCE BIT(16) +#define FSL_SAI_CR3_WDFL(x) (x) +#define FSL_SAI_CR3_WDFL_MASK 0x1f + +/* SAI Transmit and Recieve Configuration 4 Register */ +#define FSL_SAI_TCR4 0x10 +#define FSL_SAI_RCR4 0x90 +#define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16) +#define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16) +#define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8) +#define FSL_SAI_CR4_SYWD_MASK (0x1f << 8) +#define FSL_SAI_CR4_MF BIT(4) +#define FSL_SAI_CR4_FSE BIT(3) +#define FSL_SAI_CR4_FSP BIT(1) +#define FSL_SAI_CR4_FSD_MSTR BIT(0) + +/* SAI Transmit and Recieve Configuration 5 Register */ +#define FSL_SAI_TCR5 0x14 +#define FSL_SAI_RCR5 0x94 +#define FSL_SAI_CR5_WNW(x) (((x) - 1) << 24) +#define FSL_SAI_CR5_WNW_MASK (0x1f << 24) +#define FSL_SAI_CR5_W0W(x) (((x) - 1) << 16) +#define FSL_SAI_CR5_W0W_MASK (0x1f << 16) +#define FSL_SAI_CR5_FBT(x) ((x) << 8) +#define FSL_SAI_CR5_FBT_MASK (0x1f << 8) + +/* SAI type */ +#define FSL_SAI_DMA BIT(0) +#define FSL_SAI_USE_AC97 BIT(1) +#define FSL_SAI_NET BIT(2) +#define FSL_SAI_TRA_SYN BIT(3) +#define FSL_SAI_REC_SYN BIT(4) +#define FSL_SAI_USE_I2S_SLAVE BIT(5) + +#define FSL_FMT_TRANSMITTER 0 +#define FSL_FMT_RECEIVER 1 + +/* SAI clock sources */ +#define FSL_SAI_CLK_BUS 0 +#define FSL_SAI_CLK_MAST1 1 +#define FSL_SAI_CLK_MAST2 2 +#define FSL_SAI_CLK_MAST3 3 + +/* SAI data transfer numbers per DMA request */ +#define FSL_SAI_MAXBURST_TX 6 +#define FSL_SAI_MAXBURST_RX 6 + +struct fsl_sai { + struct clk *clk; + + void __iomem *base; + + bool big_endian_regs; + bool big_endian_data; + + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; +}; + +#endif /* __FSL_SAI_H */ -- cgit v1.2.3 From 05004cb4cd06127bb8ff70d5ab5a915103828e9d Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 18 Dec 2013 13:50:10 +0000 Subject: ASoC: fsl/mxs: Remove unnecessarily gendered language The kernel as a number of cases of gendered language. The majority of these refer to objects that don't have gender in English, and so I've replaced them with "it" and "its". Some refer to people (developers or users), and I've replaced these with the singular "they" variant. Some are simply typos that I've fixed up. I've left cases where gendered language was used to refer to specific individuals, was a quote or is part of license text. Signed-off-by: Matthew Garrett Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index fb9bb9eb5ca3..d570f8c81dc6 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -852,7 +852,7 @@ static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm) } /** - * find_ssi_node -- returns the SSI node that points to his DMA channel node + * find_ssi_node -- returns the SSI node that points to its DMA channel node * * Although this DMA driver attempts to operate independently of the other * devices, it still needs to determine some information about the SSI device -- cgit v1.2.3 From aafa85e71a75fdea9076c5e0f7fe09e12568c9a4 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 12 Dec 2013 18:44:45 +0800 Subject: ASoC: fsl_ssi: Add DAI master mode support for SSI on i.MX series This patch adds three main functions for DAI master mode: set_dai_fmt(), set_dai_sysclk() and set_dai_tdm_slot(), and one essential baud clock accordingly. After appending this patch, the fsl_ssi driver on i.MX series has the ability to derive LRCLK and BCLK from baud clock source so as to support some audio Codecs which can only be used in slave mode. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++- sound/soc/fsl/fsl_ssi.h | 2 + 2 files changed, 278 insertions(+), 4 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index f9f4569417ed..b2ebaf811599 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -139,7 +140,10 @@ struct fsl_ssi_private { bool ssi_on_imx; bool imx_ac97; bool use_dma; + bool baudclk_locked; u8 i2s_mode; + spinlock_t baudclk_lock; + struct clk *baudclk; struct clk *clk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; @@ -434,13 +438,18 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); + unsigned long flags; /* First, we only do fsl_ssi_setup() when SSI is going to be active. * Second, fsl_ssi_setup was already called by ac97_init earlier if * the driver is in ac97 mode. */ - if (!dai->active && !ssi_private->imx_ac97) + if (!dai->active && !ssi_private->imx_ac97) { fsl_ssi_setup(ssi_private); + spin_lock_irqsave(&ssi_private->baudclk_lock, flags); + ssi_private->baudclk_locked = false; + spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); + } return 0; } @@ -501,6 +510,243 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, return 0; } +/** + * fsl_ssi_set_dai_fmt - configure Digital Audio Interface Format. + */ +static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + u32 strcr = 0, stcr, srcr, scr, mask; + + scr = read_ssi(&ssi->scr) & ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_I2S_MODE_MASK); + scr |= CCSR_SSI_SCR_NET; + + mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR | + CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL | + CCSR_SSI_STCR_TEFS; + stcr = read_ssi(&ssi->stcr) & ~mask; + srcr = read_ssi(&ssi->srcr) & ~mask; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_MASTER; + break; + case SND_SOC_DAIFMT_CBM_CFM: + ssi_private->i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE; + break; + default: + return -EINVAL; + } + scr |= ssi_private->i2s_mode; + + /* Data on rising edge of bclk, frame low, 1clk before data */ + strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP | + CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS; + break; + case SND_SOC_DAIFMT_LEFT_J: + /* Data on rising edge of bclk, frame high */ + strcr |= CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TSCKP; + break; + case SND_SOC_DAIFMT_DSP_A: + /* Data on rising edge of bclk, frame high, 1clk before data */ + strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP | + CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS; + break; + case SND_SOC_DAIFMT_DSP_B: + /* Data on rising edge of bclk, frame high */ + strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP | + CCSR_SSI_STCR_TXBIT0; + break; + default: + return -EINVAL; + } + + /* DAI clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + /* Nothing to do for both normal cases */ + break; + case SND_SOC_DAIFMT_IB_NF: + /* Invert bit clock */ + strcr ^= CCSR_SSI_STCR_TSCKP; + break; + case SND_SOC_DAIFMT_NB_IF: + /* Invert frame clock */ + strcr ^= CCSR_SSI_STCR_TFSI; + break; + case SND_SOC_DAIFMT_IB_IF: + /* Invert both clocks */ + strcr ^= CCSR_SSI_STCR_TSCKP; + strcr ^= CCSR_SSI_STCR_TFSI; + break; + default: + return -EINVAL; + } + + /* DAI clock master masks */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + strcr |= CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR; + scr |= CCSR_SSI_SCR_SYS_CLK_EN; + break; + case SND_SOC_DAIFMT_CBM_CFM: + scr &= ~CCSR_SSI_SCR_SYS_CLK_EN; + break; + default: + return -EINVAL; + } + + stcr |= strcr; + srcr |= strcr; + + if (ssi_private->cpu_dai_drv.symmetric_rates) { + /* Need to clear RXDIR when using SYNC mode */ + srcr &= ~CCSR_SSI_SRCR_RXDIR; + scr |= CCSR_SSI_SCR_SYN; + } + + write_ssi(stcr, &ssi->stcr); + write_ssi(srcr, &ssi->srcr); + write_ssi(scr, &ssi->scr); + + return 0; +} + +/** + * fsl_ssi_set_dai_sysclk - configure Digital Audio Interface bit clock + * + * Note: This function can be only called when using SSI as DAI master + * + * Quick instruction for parameters: + * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels + * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK. + */ +static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret; + u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; + unsigned long flags, clkrate, baudrate, tmprate; + u64 sub, savesub = 100000; + + /* Don't apply it to any non-baudclk circumstance */ + if (IS_ERR(ssi_private->baudclk)) + return -EINVAL; + + /* It should be already enough to divide clock by setting pm alone */ + psr = 0; + div2 = 0; + + factor = (div2 + 1) * (7 * psr + 1) * 2; + + for (i = 0; i < 255; i++) { + /* The bclk rate must be smaller than 1/5 sysclk rate */ + if (factor * (i + 1) < 5) + continue; + + tmprate = freq * factor * (i + 2); + clkrate = clk_round_rate(ssi_private->baudclk, tmprate); + + do_div(clkrate, factor); + afreq = (u32)clkrate / (i + 1); + + if (freq == afreq) + sub = 0; + else if (freq / afreq == 1) + sub = freq - afreq; + else if (afreq / freq == 1) + sub = afreq - freq; + else + continue; + + /* Calculate the fraction */ + sub *= 100000; + do_div(sub, freq); + + if (sub < savesub) { + baudrate = tmprate; + savesub = sub; + pm = i; + } + + /* We are lucky */ + if (savesub == 0) + break; + } + + /* No proper pm found if it is still remaining the initial value */ + if (pm == 999) { + dev_err(cpu_dai->dev, "failed to handle the required sysclk\n"); + return -EINVAL; + } + + stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0) | + (psr ? CCSR_SSI_SxCCR_PSR : 0); + mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2 | CCSR_SSI_SxCCR_PSR; + + if (dir == SND_SOC_CLOCK_OUT || synchronous) + write_ssi_mask(&ssi->stccr, mask, stccr); + else + write_ssi_mask(&ssi->srccr, mask, stccr); + + spin_lock_irqsave(&ssi_private->baudclk_lock, flags); + if (!ssi_private->baudclk_locked) { + ret = clk_set_rate(ssi_private->baudclk, baudrate); + if (ret) { + spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); + dev_err(cpu_dai->dev, "failed to set baudclk rate\n"); + return -EINVAL; + } + ssi_private->baudclk_locked = true; + } + spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); + + return 0; +} + +/** + * fsl_ssi_set_dai_tdm_slot - set TDM slot number + * + * Note: This function can be only called when using SSI as DAI master + */ +static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, + u32 rx_mask, int slots, int slot_width) +{ + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + u32 val; + + /* The slot number should be >= 2 if using Network mode or I2S mode */ + val = read_ssi(&ssi->scr) & (CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_NET); + if (val && slots < 2) { + dev_err(cpu_dai->dev, "slot number should be >= 2 in I2S or NET\n"); + return -EINVAL; + } + + write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK, + CCSR_SSI_SxCCR_DC(slots)); + write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_DC_MASK, + CCSR_SSI_SxCCR_DC(slots)); + + /* The register SxMSKs needs SSI to provide essential clock due to + * hardware design. So we here temporarily enable SSI to set them. + */ + val = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN; + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN); + + write_ssi(tx_mask, &ssi->stmsk); + write_ssi(rx_mask, &ssi->srmsk); + + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, val); + + return 0; +} + /** * fsl_ssi_trigger: start and stop the DMA transfer. * @@ -517,6 +763,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct ccsr_ssi __iomem *ssi = ssi_private->ssi; unsigned int sier_bits; + unsigned long flags; /* * Enable only the interrupts and DMA requests @@ -557,8 +804,12 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0); if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) & - (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) + (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) { write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); + spin_lock_irqsave(&ssi_private->baudclk_lock, flags); + ssi_private->baudclk_locked = false; + spin_unlock_irqrestore(&ssi_private->baudclk_lock, flags); + } break; default: @@ -585,6 +836,9 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { .startup = fsl_ssi_startup, .hw_params = fsl_ssi_hw_params, + .set_fmt = fsl_ssi_set_dai_fmt, + .set_sysclk = fsl_ssi_set_dai_sysclk, + .set_tdm_slot = fsl_ssi_set_dai_tdm_slot, .trigger = fsl_ssi_trigger, }; @@ -897,6 +1151,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) /* Older 8610 DTs didn't have the fifo-depth property */ ssi_private->fifo_depth = 8; + ssi_private->baudclk_locked = false; + spin_lock_init(&ssi_private->baudclk_lock); + if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) { u32 dma_events[2]; ssi_private->ssi_on_imx = true; @@ -914,6 +1171,15 @@ static int fsl_ssi_probe(struct platform_device *pdev) goto error_irqmap; } + /* For those SLAVE implementations, we ingore non-baudclk cases + * and, instead, abandon MASTER mode that needs baud clock. + */ + ssi_private->baudclk = devm_clk_get(&pdev->dev, "baud"); + if (IS_ERR(ssi_private->baudclk)) + dev_warn(&pdev->dev, "could not get baud clock: %d\n", ret); + else + clk_prepare_enable(ssi_private->baudclk); + /* * We have burstsize be "fifo_depth - 2" to match the SSI * watermark setting in fsl_ssi_startup(). @@ -1059,8 +1325,11 @@ error_dev: device_remove_file(&pdev->dev, dev_attr); error_clk: - if (ssi_private->ssi_on_imx) + if (ssi_private->ssi_on_imx) { + if (!IS_ERR(ssi_private->baudclk)) + clk_disable_unprepare(ssi_private->baudclk); clk_disable_unprepare(ssi_private->clk); + } error_irqmap: irq_dispose_mapping(ssi_private->irq); @@ -1076,8 +1345,11 @@ static int fsl_ssi_remove(struct platform_device *pdev) platform_device_unregister(ssi_private->pdev); snd_soc_unregister_component(&pdev->dev); device_remove_file(&pdev->dev, &ssi_private->dev_attr); - if (ssi_private->ssi_on_imx) + if (ssi_private->ssi_on_imx) { + if (!IS_ERR(ssi_private->baudclk)) + clk_disable_unprepare(ssi_private->baudclk); clk_disable_unprepare(ssi_private->clk); + } irq_dispose_mapping(ssi_private->irq); return 0; diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h index e6b9a69e2a68..e6b63240a3d7 100644 --- a/sound/soc/fsl/fsl_ssi.h +++ b/sound/soc/fsl/fsl_ssi.h @@ -125,7 +125,9 @@ struct ccsr_ssi { #define CCSR_SSI_SRCR_REFS 0x00000001 /* STCCR and SRCCR */ +#define CCSR_SSI_SxCCR_DIV2_SHIFT 18 #define CCSR_SSI_SxCCR_DIV2 0x00040000 +#define CCSR_SSI_SxCCR_PSR_SHIFT 17 #define CCSR_SSI_SxCCR_PSR 0x00020000 #define CCSR_SSI_SxCCR_WL_SHIFT 13 #define CCSR_SSI_SxCCR_WL_MASK 0x0001E000 -- cgit v1.2.3 From a6af47ae5399baf4f5a2426b2121c1bcb9da4019 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 20 Dec 2013 12:17:38 +0800 Subject: ASoC: fsl-sai: Remove fsl_sai_remove() There is no need of this function and makes the code slightly shorter Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 50a797e65781..1868ec34be10 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -385,19 +385,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } -static int fsl_sai_dai_remove(struct snd_soc_dai *cpu_dai) -{ - cpu_dai->playback_dma_data = NULL; - cpu_dai->capture_dma_data = NULL; - - snd_soc_dai_set_drvdata(cpu_dai, NULL); - - return 0; -} - static struct snd_soc_dai_driver fsl_sai_dai = { .probe = fsl_sai_dai_probe, - .remove = fsl_sai_dai_remove, .playback = { .channels_min = 1, .channels_max = 2, -- cgit v1.2.3 From e5180df3960b6130f17f3c5ab50d23674cdb2b5a Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 20 Dec 2013 12:30:26 +0800 Subject: ASoC: fsl-sai: Use devm_snd_dmaengine_pcm_register() Makes the code slightly shorter Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 1868ec34be10..262d3107892e 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -443,19 +443,8 @@ static int fsl_sai_probe(struct platform_device *pdev) if (ret) return ret; - ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, + return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); - if (ret) - return ret; - - return 0; -} - -static int fsl_sai_remove(struct platform_device *pdev) -{ - snd_dmaengine_pcm_unregister(&pdev->dev); - - return 0; } static const struct of_device_id fsl_sai_ids[] = { @@ -465,8 +454,6 @@ static const struct of_device_id fsl_sai_ids[] = { static struct platform_driver fsl_sai_driver = { .probe = fsl_sai_probe, - .remove = fsl_sai_remove, - .driver = { .name = "fsl-sai", .owner = THIS_MODULE, -- cgit v1.2.3 From dd9f40602e96353c210805a99abd9af6abd28473 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 20 Dec 2013 12:35:33 +0800 Subject: ASoC: fsl-sai: Use snd_soc_dai_init_dma_data() Makes the code slightly shorter Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 262d3107892e..b8cdbf8660fe 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -377,8 +377,8 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); - cpu_dai->playback_dma_data = &sai->dma_params_tx; - cpu_dai->capture_dma_data = &sai->dma_params_rx; + snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx, + &sai->dma_params_rx); snd_soc_dai_set_drvdata(cpu_dai, sai); -- cgit v1.2.3 From 1fb2d9d7465bcbb519c582fa4a3bd04ff4fce2d2 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:00 +0800 Subject: ASoC: fsl_sai: Keep symmetry for clk_enable() and clk_disable() There are two functions haven't clk_disable_unprepare() if having error. Thus fix them. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index b8cdbf8660fe..69a375f48efe 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -111,7 +111,7 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, dev_err(cpu_dai->dev, "Cannot set SAI's transmitter sysclk: %d\n", ret); - return ret; + goto err_clk; } ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, @@ -120,12 +120,13 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, dev_err(cpu_dai->dev, "Cannot set SAI's receiver sysclk: %d\n", ret); - return ret; + goto err_clk; } +err_clk: clk_disable_unprepare(sai->clk); - return 0; + return ret; } static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, @@ -222,7 +223,7 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) dev_err(cpu_dai->dev, "Cannot set SAI's transmitter format: %d\n", ret); - return ret; + goto err_clk; } ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); @@ -230,12 +231,13 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) dev_err(cpu_dai->dev, "Cannot set SAI's receiver format: %d\n", ret); - return ret; + goto err_clk; } +err_clk: clk_disable_unprepare(sai->clk); - return 0; + return ret; } static int fsl_sai_hw_params(struct snd_pcm_substream *substream, -- cgit v1.2.3 From 1d7003092771bd2feec30e2f3e5a06aa33479e08 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:01 +0800 Subject: ASoC: fsl_sai: Use snd_pcm_format_width() Use common helper function snd_pcm_format_width() to make code neater. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 69a375f48efe..e68102e63521 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -244,9 +244,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr, word_width; + u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr; unsigned int channels = params_channels(params); struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 word_width = snd_pcm_format_width(params_format(params)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { reg_cr4 = FSL_SAI_TCR4; @@ -267,20 +268,6 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 &= ~FSL_SAI_CR5_W0W_MASK; val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - word_width = 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - word_width = 20; - break; - case SNDRV_PCM_FORMAT_S24_LE: - word_width = 24; - break; - default: - return -EINVAL; - } - val_cr4 |= FSL_SAI_CR4_SYWD(word_width); val_cr5 |= FSL_SAI_CR5_WNW(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); -- cgit v1.2.3 From d22e28cce80a93578787d273bf1fa26a2be2636b Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:02 +0800 Subject: ASoC: fsl_sai: Drop useless channels check in hw_params() SAi only supports two data channels on hardware level and the driver also does register the min->1 and max->2, so no need to check channels. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index e68102e63521..8450bff6fb13 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -278,10 +278,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 |= FSL_SAI_CR5_FBT(0); val_cr4 |= FSL_SAI_CR4_FRSZ(channels); - if (channels == 2 || channels == 1) - val_mr = ~0UL - ((1 << channels) - 1); - else - return -EINVAL; + val_mr = ~0UL - ((1 << channels) - 1); sai_writel(sai, val_cr4, sai->base + reg_cr4); sai_writel(sai, val_cr5, sai->base + reg_cr5); -- cgit v1.2.3 From 15b29dae6604d2d2daf586429ff12f26272a868a Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:03 +0800 Subject: ASoC: fsl_sai: Drop useless ret in startup() We can save this ret to make the code neater. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 8450bff6fb13..fc4cd95ad0a9 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -334,12 +334,9 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, static int fsl_sai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { - int ret; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - ret = clk_prepare_enable(sai->clk); - - return ret; + return clk_prepare_enable(sai->clk); } static void fsl_sai_shutdown(struct snd_pcm_substream *substream, -- cgit v1.2.3 From 190af12dad975f2ea7d69d1c5c9d36fec64da767 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:04 +0800 Subject: ASoC: fsl_sai: Make dev_err information neater Since using dev_err() there's no need to mention SAI any more, it will print the full name of the driver -- fsl_sai. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index fc4cd95ad0a9..68d666b491de 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -108,18 +108,14 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_TRANSMITTER); if (ret) { - dev_err(cpu_dai->dev, - "Cannot set SAI's transmitter sysclk: %d\n", - ret); + dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret); goto err_clk; } ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_RECEIVER); if (ret) { - dev_err(cpu_dai->dev, - "Cannot set SAI's receiver sysclk: %d\n", - ret); + dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret); goto err_clk; } @@ -220,17 +216,13 @@ static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER); if (ret) { - dev_err(cpu_dai->dev, - "Cannot set SAI's transmitter format: %d\n", - ret); + dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret); goto err_clk; } ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER); if (ret) { - dev_err(cpu_dai->dev, - "Cannot set SAI's receiver format: %d\n", - ret); + dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret); goto err_clk; } -- cgit v1.2.3 From 4e3a99f5b004b30bc604d82e5498700649148e0d Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 20 Dec 2013 16:41:05 +0800 Subject: ASoC: fsl_sai: Sort local variable in general way Generally we would write code for local variable like: static new_func() { struct xxx *yyy; ... int ret; } But this driver only follows this pattern for some functions, not all. Thus this patch sorts the local variable in the general way. Signed-off-by: Nicolin Chen Reviewed-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 68d666b491de..b72132fa70de 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -53,8 +53,8 @@ static inline void sai_writel(struct fsl_sai *sai, static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int fsl_dir) { - u32 val_cr2, reg_cr2; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr2, reg_cr2; if (fsl_dir == FSL_FMT_TRANSMITTER) reg_cr2 = FSL_SAI_TCR2; @@ -90,8 +90,8 @@ static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai, static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, unsigned int freq, int dir) { - int ret; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + int ret; if (dir == SND_SOC_CLOCK_IN) return 0; @@ -128,8 +128,8 @@ err_clk: static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, unsigned int fmt, int fsl_dir) { - u32 val_cr2, val_cr3, val_cr4, reg_cr2, reg_cr3, reg_cr4; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + u32 val_cr2, val_cr3, val_cr4, reg_cr2, reg_cr3, reg_cr4; if (fsl_dir == FSL_FMT_TRANSMITTER) { reg_cr2 = FSL_SAI_TCR2; @@ -207,8 +207,8 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { - int ret; struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); + int ret; ret = clk_prepare_enable(sai->clk); if (ret) @@ -236,9 +236,9 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { + struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr; unsigned int channels = params_channels(params); - struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); u32 word_width = snd_pcm_format_width(params_format(params)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -383,10 +383,10 @@ static const struct snd_soc_component_driver fsl_component = { static int fsl_sai_probe(struct platform_device *pdev) { - int ret; + struct device_node *np = pdev->dev.of_node; struct fsl_sai *sai; struct resource *res; - struct device_node *np = pdev->dev.of_node; + int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) -- cgit v1.2.3 From e6dc12d7198eddba2e3e7a13feab5c7edde7ba1d Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 25 Dec 2013 11:20:14 +0800 Subject: ASoC: fsl_sai: Move the global registers setting to _dai_probe() Because we cannot make sure which one of _dai_fmt() and _dai_sysclk() will be firstly called. So move the RCSR/TCSR and TCR1/RCR1's initialization to _dai_probe(), and this can make sure that before any of {T,R}CR{1~5} register to be set the RCSR/TCSR's RE/TE bit has been cleared for the hareware limitation. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index b72132fa70de..596aabbf9037 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -100,11 +100,6 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, if (ret) return ret; - sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR); - sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR); - sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1); - sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1); - ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, FSL_FMT_TRANSMITTER); if (ret) { @@ -351,6 +346,18 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev); + int ret; + + ret = clk_prepare_enable(sai->clk); + if (ret) + return ret; + + sai_writel(sai, 0x0, sai->base + FSL_SAI_RCSR); + sai_writel(sai, 0x0, sai->base + FSL_SAI_TCSR); + sai_writel(sai, FSL_SAI_MAXBURST_TX * 2, sai->base + FSL_SAI_TCR1); + sai_writel(sai, FSL_SAI_MAXBURST_RX - 1, sai->base + FSL_SAI_RCR1); + + clk_disable_unprepare(sai->clk); snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx, &sai->dma_params_rx); -- cgit v1.2.3 From e5d0fa9c3ec59a40e0285d96b65b7f62875acd42 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 25 Dec 2013 12:40:04 +0800 Subject: ASoC: fsl_sai: Add disable operation for the corresponding data channel. Enables/Disables the corresponding data channel for tx/rx operation. A channel must be enabled before its FIFO is accessed, and then disable it when tx/rx is stopped or idle. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 596aabbf9037..af802465456e 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -124,20 +124,17 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, unsigned int fmt, int fsl_dir) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - u32 val_cr2, val_cr3, val_cr4, reg_cr2, reg_cr3, reg_cr4; + u32 val_cr2, val_cr4, reg_cr2, reg_cr4; if (fsl_dir == FSL_FMT_TRANSMITTER) { reg_cr2 = FSL_SAI_TCR2; - reg_cr3 = FSL_SAI_TCR3; reg_cr4 = FSL_SAI_TCR4; } else { reg_cr2 = FSL_SAI_RCR2; - reg_cr3 = FSL_SAI_RCR3; reg_cr4 = FSL_SAI_RCR4; } val_cr2 = sai_readl(sai, sai->base + reg_cr2); - val_cr3 = sai_readl(sai, sai->base + reg_cr3); val_cr4 = sai_readl(sai, sai->base + reg_cr4); if (sai->big_endian_data) @@ -188,13 +185,10 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; } - val_cr3 |= FSL_SAI_CR3_TRCE; - if (fsl_dir == FSL_FMT_RECEIVER) val_cr2 |= FSL_SAI_CR2_SYNC; sai_writel(sai, val_cr2, sai->base + reg_cr2); - sai_writel(sai, val_cr3, sai->base + reg_cr3); sai_writel(sai, val_cr4, sai->base + reg_cr4); return 0; @@ -278,7 +272,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - unsigned int tcsr, rcsr; + u32 tcsr, rcsr, val_cr3, reg_cr3; tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR); rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR); @@ -286,17 +280,24 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { tcsr |= FSL_SAI_CSR_FRDE; rcsr &= ~FSL_SAI_CSR_FRDE; + reg_cr3 = FSL_SAI_TCR3; } else { rcsr |= FSL_SAI_CSR_FRDE; tcsr &= ~FSL_SAI_CSR_FRDE; + reg_cr3 = FSL_SAI_RCR3; } + val_cr3 = sai_readl(sai, sai->base + reg_cr3); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: tcsr |= FSL_SAI_CSR_TERE; rcsr |= FSL_SAI_CSR_TERE; + val_cr3 |= FSL_SAI_CR3_TRCE; + + sai_writel(sai, val_cr3, sai->base + reg_cr3); sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); break; @@ -308,8 +309,12 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, tcsr &= ~FSL_SAI_CSR_TERE; rcsr &= ~FSL_SAI_CSR_TERE; } + + val_cr3 &= ~FSL_SAI_CR3_TRCE; + sai_writel(sai, tcsr, sai->base + FSL_SAI_TCSR); sai_writel(sai, rcsr, sai->base + FSL_SAI_RCSR); + sai_writel(sai, val_cr3, sai->base + reg_cr3); break; default: return -EINVAL; -- cgit v1.2.3 From 496a39d9ec238569fac6daceac8f5420c5edc2f1 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 31 Dec 2013 15:33:21 +0800 Subject: ASoC: fsl_sai: Fix one bug for hardware limitation. This is maybe one bug or a limitation of the hardware that the {T,R}CR2's Synchronous Mode bits must be set as late as possible, or the SAI device maybe hanged up, and there has not any explaination about this limitation in the SAI Data Sheet. And the {T,R}CR2's Synchronous Mode bits must be set at the same time whether for Tx or Rx stream. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index af802465456e..2ece14716c67 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -145,7 +145,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: val_cr4 |= FSL_SAI_CR4_FSE; - val_cr4 |= FSL_SAI_CR4_FSP; break; default: return -EINVAL; @@ -185,9 +184,6 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, return -EINVAL; } - if (fsl_dir == FSL_FMT_RECEIVER) - val_cr2 |= FSL_SAI_CR2_SYNC; - sai_writel(sai, val_cr2, sai->base + reg_cr2); sai_writel(sai, val_cr4, sai->base + reg_cr4); @@ -253,6 +249,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 |= FSL_SAI_CR5_WNW(word_width); val_cr5 |= FSL_SAI_CR5_W0W(word_width); + val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; if (sai->big_endian_data) val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); else @@ -272,7 +269,15 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - u32 tcsr, rcsr, val_cr3, reg_cr3; + u32 tcsr, rcsr, val_cr2, val_cr3, reg_cr3; + + val_cr2 = sai_readl(sai, sai->base + FSL_SAI_TCR2); + val_cr2 &= ~FSL_SAI_CR2_SYNC; + sai_writel(sai, val_cr2, sai->base + FSL_SAI_TCR2); + + val_cr2 = sai_readl(sai, sai->base + FSL_SAI_RCR2); + val_cr2 |= FSL_SAI_CR2_SYNC; + sai_writel(sai, val_cr2, sai->base + FSL_SAI_RCR2); tcsr = sai_readl(sai, sai->base + FSL_SAI_TCSR); rcsr = sai_readl(sai, sai->base + FSL_SAI_RCSR); -- cgit v1.2.3 From 72aa62bed3ea30635156fad95f123a0b665072bf Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Tue, 31 Dec 2013 15:33:22 +0800 Subject: ASoC: fsl_sai: fix the endianess for SAI fifo data. Revert the SAI's endianess for fifo data to/from DMA engine. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 2ece14716c67..5d38a6749b9f 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -138,9 +138,9 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai, val_cr4 = sai_readl(sai, sai->base + reg_cr4); if (sai->big_endian_data) - val_cr4 |= FSL_SAI_CR4_MF; - else val_cr4 &= ~FSL_SAI_CR4_MF; + else + val_cr4 |= FSL_SAI_CR4_MF; switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: @@ -251,9 +251,9 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr5 &= ~FSL_SAI_CR5_FBT_MASK; if (sai->big_endian_data) - val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1); - else 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_mr = ~0UL - ((1 << channels) - 1); -- cgit v1.2.3